Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui): Persist variables control bar preferences #12901

Merged
merged 1 commit into from
Mar 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
1. [12876](https://github.com/influxdata/influxdb/pull/12876): Add the ability to update token's status in Token list
1. [12821](https://github.com/influxdata/influxdb/pull/12821): Allow variables to be re-ordered within control bar on a dashboard.
1. [12888](https://github.com/influxdata/influxdb/pull/12888): Add the ability to delete a template
1. [12901](https://github.com/influxdata/influxdb/pull/12901): Save user preference for variable control bar visibility and default to visible

### Bug Fixes

Expand Down
10 changes: 9 additions & 1 deletion ui/src/dashboards/actions/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {RemoteDataState} from 'src/types'
import {Dispatch} from 'redux'
import {View} from 'src/types'

export type Action = SetViewAction | SetViewsAction
export type Action = SetViewAction | SetViewsAction | ResetViewsAction

export interface SetViewsAction {
type: 'SET_VIEWS'
Expand Down Expand Up @@ -45,6 +45,14 @@ export const setView = (
payload: {id, view, status},
})

export interface ResetViewsAction {
type: 'RESET_VIEWS'
}

export const resetViews = (): ResetViewsAction => ({
type: 'RESET_VIEWS',
})

export const getView = (dashboardID: string, cellID: string) => async (
dispatch: Dispatch<Action>
): Promise<void> => {
Expand Down
1 change: 0 additions & 1 deletion ui/src/dashboards/components/DashboardHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ interface Props extends DefaultProps {
onManualRefresh: () => void
handleClickPresentationButton: AppActions.DelayEnablePresentationModeDispatcher
onAddCell: () => void
showTemplateControlBar: boolean
onRenameDashboard: (name: string) => Promise<void>
toggleVariablesControlBar: () => void
isShowingVariablesControlBar: boolean
Expand Down
31 changes: 12 additions & 19 deletions ui/src/dashboards/components/DashboardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {Location} from 'history'
import * as AppActions from 'src/types/actions/app'
import * as ColorsModels from 'src/types/colors'
import * as NotificationsActions from 'src/types/actions/notifications'
import {toggleShowVariablesControls} from 'src/userSettings/actions'

interface StateProps {
links: Links
Expand All @@ -45,7 +46,7 @@ interface StateProps {
dashboard: Dashboard
autoRefresh: number
inPresentationMode: boolean
showTemplateControlBar: boolean
showVariablesControls: boolean
views: {[cellID: string]: {view: View; status: RemoteDataState}}
}

Expand All @@ -64,6 +65,7 @@ interface DispatchProps {
onCreateCellWithView: typeof dashboardActions.createCellWithView
onUpdateView: typeof dashboardActions.updateView
onSetActiveTimeMachine: typeof setActiveTimeMachine
onToggleShowVariablesControls: typeof toggleShowVariablesControls
}

interface PassedProps {
Expand All @@ -90,8 +92,6 @@ type Props = PassedProps &
interface State {
scrollTop: number
windowHeight: number
isShowingVEO: boolean
isShowingVariablesControlBar: boolean
}

@ErrorHandling
Expand All @@ -102,8 +102,6 @@ class DashboardPage extends Component<Props, State> {
this.state = {
scrollTop: 0,
windowHeight: window.innerHeight,
isShowingVEO: false,
isShowingVariablesControlBar: false,
}
}

Expand Down Expand Up @@ -142,17 +140,17 @@ class DashboardPage extends Component<Props, State> {
const {
timeRange,
zoomedTimeRange,
showTemplateControlBar,
dashboard,
autoRefresh,
manualRefresh,
onManualRefresh,
inPresentationMode,
showVariablesControls,
handleChooseAutoRefresh,
handleClickPresentationButton,
onToggleShowVariablesControls,
children,
} = this.props
const {isShowingVariablesControlBar} = this.state

return (
<Page titleTag={this.pageTitle}>
Expand All @@ -168,14 +166,13 @@ class DashboardPage extends Component<Props, State> {
zoomedTimeRange={zoomedTimeRange}
onRenameDashboard={this.handleRenameDashboard}
activeDashboard={dashboard ? dashboard.name : ''}
showTemplateControlBar={showTemplateControlBar}
handleChooseAutoRefresh={handleChooseAutoRefresh}
handleChooseTimeRange={this.handleChooseTimeRange}
handleClickPresentationButton={handleClickPresentationButton}
toggleVariablesControlBar={this.toggleVariablesControlBar}
isShowingVariablesControlBar={isShowingVariablesControlBar}
toggleVariablesControlBar={onToggleShowVariablesControls}
isShowingVariablesControlBar={showVariablesControls}
/>
{isShowingVariablesControlBar && (
{showVariablesControls && !!dashboard && (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

<VariablesControlBar dashboardID={dashboard.id} />
)}
{!!dashboard && (
Expand Down Expand Up @@ -292,12 +289,6 @@ class DashboardPage extends Component<Props, State> {
this.setState({windowHeight: window.innerHeight})
}

private toggleVariablesControlBar = (): void => {
this.setState({
isShowingVariablesControlBar: !this.state.isShowingVariablesControlBar,
})
}

private get pageTitle(): string {
const {dashboard} = this.props

Expand All @@ -310,11 +301,12 @@ const mstp = (state: AppState, {params: {dashboardID}}): StateProps => {
links,
app: {
ephemeral: {inPresentationMode},
persisted: {autoRefresh, showTemplateControlBar},
persisted: {autoRefresh},
},
ranges,
dashboards,
views: {views},
userSettings: {showVariablesControls},
} = state

const timeRange =
Expand All @@ -330,7 +322,7 @@ const mstp = (state: AppState, {params: {dashboardID}}): StateProps => {
dashboard,
autoRefresh,
inPresentationMode,
showTemplateControlBar,
showVariablesControls,
}
}

Expand All @@ -349,6 +341,7 @@ const mdtp: DispatchProps = {
onCreateCellWithView: dashboardActions.createCellWithView,
onUpdateView: dashboardActions.updateView,
onSetActiveTimeMachine: setActiveTimeMachine,
onToggleShowVariablesControls: toggleShowVariablesControls,
}

export default connect(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {AppState, Dashboard} from 'src/types'

// Constants
import {DEFAULT_DASHBOARD_NAME} from 'src/dashboards/constants'
import {resetViews} from 'src/dashboards/actions/views'

interface PassedProps {
dashboard: Dashboard
Expand All @@ -44,6 +45,7 @@ interface DispatchProps {
onAddDashboardLabels: typeof addDashboardLabelsAsync
onRemoveDashboardLabels: typeof removeDashboardLabelsAsync
onCreateLabel: typeof createLabelAsync
onResetViews: typeof resetViews
}

type Props = PassedProps & DispatchProps & StateProps & WithRouterProps
Expand Down Expand Up @@ -145,7 +147,9 @@ class DashboardCard extends PureComponent<Props> {
}

private handleClickDashboard = () => {
const {router, dashboard} = this.props
const {router, dashboard, onResetViews} = this.props

onResetViews()

router.push(`/dashboards/${dashboard.id}`)
}
Expand Down Expand Up @@ -203,6 +207,7 @@ const mdtp: DispatchProps = {
onCreateLabel: createLabelAsync,
onAddDashboardLabels: addDashboardLabelsAsync,
onRemoveDashboardLabels: removeDashboardLabelsAsync,
onResetViews: resetViews,
}

export default connect<StateProps, DispatchProps, PassedProps>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,13 @@ $variables-control-bar--gutter: $ix-marg-a;
.variable-dropdown {
margin: 0 $variables-control-bar--gutter / 2;
}

.variables-spinner-container {
height: $variables-control-bar--height;
}
}


.variables-control-bar--empty {
background-color: $g3-castle;
border-radius: $radius;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import HTML5Backend from 'react-dnd-html5-backend'

// Components
import {EmptyState, ComponentSize} from 'src/clockface'
import {TechnoSpinner} from '@influxdata/clockface'
import {TechnoSpinner, SpinnerContainer} from '@influxdata/clockface'

// Utils
import {
getVariablesForDashboard,
getDashboardValuesStatus,
getDashboardVariablesStatus,
} from 'src/variables/selectors'

// Styles
Expand All @@ -37,34 +38,62 @@ interface OwnProps {
interface StateProps {
variables: Variable[]
valuesStatus: RemoteDataState
variablesStatus: RemoteDataState
}

interface DispatchProps {
moveVariable: typeof moveVariable
}

interface State {
initialLoading: RemoteDataState
}

type Props = StateProps & DispatchProps & OwnProps

@ErrorHandling
class VariablesControlBar extends PureComponent<Props> {
render() {
const {dashboardID, variables, valuesStatus} = this.props

if (isEmpty(variables)) {
return (
<div className="variables-control-bar">
<EmptyState
size={ComponentSize.ExtraSmall}
customClass="variables-control-bar--empty"
>
<EmptyState.Text text="To see variable controls here, use a variable in a cell query" />
</EmptyState>
</div>
)
class VariablesControlBar extends PureComponent<Props, State> {
public state: State = {initialLoading: RemoteDataState.Loading}

static getDerivedStateFromProps(props, state) {
if (
props.valuesStatus === RemoteDataState.Done &&
props.variablesStatus === RemoteDataState.Done &&
state.initialLoading !== RemoteDataState.Done
) {
return {initialLoading: RemoteDataState.Done}
}
}

render() {
return (
<div className="variables-control-bar">
<SpinnerContainer
loading={this.state.initialLoading}
spinnerComponent={<TechnoSpinner diameterPixels={50} />}
className="variables-spinner-container"
>
{this.bar}
</SpinnerContainer>
</div>
)
}

private get emptyBar(): JSX.Element {
return (
<EmptyState
size={ComponentSize.ExtraSmall}
customClass="variables-control-bar--empty"
>
<EmptyState.Text text="To see variable controls here, use a variable in a cell query" />
</EmptyState>
)
}

private get barContents(): JSX.Element {
const {dashboardID, variables, valuesStatus} = this.props
return (
<>
{variables.map((v, i) => (
<DraggableDropdown
key={v.id}
Expand All @@ -78,10 +107,20 @@ class VariablesControlBar extends PureComponent<Props> {
{valuesStatus === RemoteDataState.Loading && (
<TechnoSpinner diameterPixels={18} />
)}
</div>
</>
)
}

private get bar(): JSX.Element {
const {variables} = this.props

if (isEmpty(variables)) {
return this.emptyBar
}

return this.barContents
}

private handleMoveDropdown = (
originalIndex: number,
newIndex: number
Expand All @@ -98,8 +137,9 @@ const mdtp = {
const mstp = (state: AppState, props: OwnProps): StateProps => {
const variables = getVariablesForDashboard(state, props.dashboardID)
const valuesStatus = getDashboardValuesStatus(state, props.dashboardID)
const variablesStatus = getDashboardVariablesStatus(state)

return {variables, valuesStatus}
return {variables, valuesStatus, variablesStatus}
}

export default DragDropContext(HTML5Backend)(
Expand Down
4 changes: 4 additions & 0 deletions ui/src/dashboards/reducers/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ const viewsReducer = (
},
}
}

case 'RESET_VIEWS': {
return initialState()
}
}

return state
Expand Down
2 changes: 2 additions & 0 deletions ui/src/localStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const saveToLocalStorage = ({
app: {persisted},
ranges,
variables,
userSettings,
}: LocalStorage): void => {
try {
const appPersisted = {app: {persisted}}
Expand All @@ -41,6 +42,7 @@ export const saveToLocalStorage = ({
VERSION,
ranges: normalizer(ranges),
variables,
userSettings,
})
)
} catch (err) {
Expand Down
6 changes: 4 additions & 2 deletions ui/src/mockState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import {Provider} from 'react-redux'
import {Router, createMemoryHistory} from 'react-router'

import {render} from 'react-testing-library'
import {initialState} from 'src/variables/reducers'
import {initialState as initialVariablesState} from 'src/variables/reducers'
import {initialState as initialUserSettingsState} from 'src/userSettings/reducers'
import configureStore from 'src/store/configureStore'

const localState = {
Expand All @@ -25,7 +26,8 @@ const localState = {
duration: '15m',
},
],
variables: initialState(),
variables: initialVariablesState(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would we need to worry about sensitive data in variables?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the initial state of variables doesnt have much in it. plus this is just mocks

userSettings: initialUserSettingsState(),
}

const history = createMemoryHistory({entries: ['/']})
Expand Down
Loading