Remove explicit injection of TimeMachineContainer

pull/4538/head
Christopher Henn 2018-10-04 09:14:01 -07:00 committed by Chris Henn
parent f25d200b0d
commit 435b57507f
8 changed files with 151 additions and 140 deletions

View File

@ -11,6 +11,7 @@ import CEOHeader from 'src/dashboards/components/CEOHeader'
// Utils
import {getDeep} from 'src/utils/wrappers'
import {TimeMachineContainer} from 'src/shared/utils/TimeMachineContainer'
import {initialStateFromCell} from 'src/shared/utils/timeMachine'
// Actions
import {editCellQueryStatus} from 'src/dashboards/actions'
@ -56,6 +57,7 @@ interface ConnectedProps {
thresholdsListType: ThresholdType
gaugeColors: ColorNumber[]
lineColors: ColorString[]
onResetTimeMachine: TimeMachineContainer['reset']
}
interface PassedProps {
@ -98,6 +100,13 @@ class CellEditorOverlay extends Component<Props, State> {
}
public componentDidMount() {
const {cell, dashboardTimeRange, onResetTimeMachine} = this.props
onResetTimeMachine({
...initialStateFromCell(cell),
timeRange: dashboardTimeRange,
})
this.handleResetFocus()
}
@ -324,6 +333,7 @@ const ConnectedCellEditorOverlay = (props: PassedProps) => {
thresholdsListType={timeMachineContainer.state.thresholdsListType}
gaugeColors={timeMachineContainer.state.gaugeColors}
lineColors={timeMachineContainer.state.lineColors}
onResetTimeMachine={timeMachineContainer.reset}
/>
)}
</Subscribe>

View File

@ -3,7 +3,6 @@ import React, {Component, MouseEvent} from 'react'
import {connect} from 'react-redux'
import {withRouter} from 'react-router'
import _ from 'lodash'
import {Provider} from 'unstated'
// Components
import {ErrorHandling} from 'src/shared/decorators/errors'
@ -29,8 +28,6 @@ import * as errorActions from 'src/shared/actions/errors'
import * as notifyActions from 'src/shared/actions/notifications'
// Utils
import {TimeMachineContainer} from 'src/shared/utils/TimeMachineContainer'
import {initialStateFromCell} from 'src/shared/utils/timeMachine'
import idNormalizer, {TYPE_ID} from 'src/normalizers/id'
import {getDeep} from 'src/utils/wrappers'
import {updateDashboardLinks} from 'src/dashboards/utils/dashboardSwitcherLinks'
@ -128,8 +125,6 @@ interface State {
@ErrorHandling
class DashboardPage extends Component<Props, State> {
private timeMachineContainer: TimeMachineContainer
public constructor(props: Props) {
super(props)
@ -283,22 +278,20 @@ class DashboardPage extends Component<Props, State> {
return (
<Page>
<OverlayTechnology visible={showCellEditorOverlay}>
<Provider inject={[this.timeMachineContainer]}>
<CellEditorOverlay
source={source}
sources={sources}
notify={notify}
fluxLinks={fluxLinks}
cell={selectedCell}
dashboardID={dashboardID}
queryStatus={cellQueryStatus}
onSave={this.handleSaveEditedCell}
onCancel={this.handleHideCellEditorOverlay}
templates={templatesIncludingDashTime}
editQueryStatus={this.props.editCellQueryStatus}
dashboardTimeRange={timeRange}
/>
</Provider>
<CellEditorOverlay
source={source}
sources={sources}
notify={notify}
fluxLinks={fluxLinks}
cell={selectedCell}
dashboardID={dashboardID}
queryStatus={cellQueryStatus}
onSave={this.handleSaveEditedCell}
onCancel={this.handleHideCellEditorOverlay}
templates={templatesIncludingDashTime}
editQueryStatus={this.props.editCellQueryStatus}
dashboardTimeRange={timeRange}
/>
</OverlayTechnology>
<DashboardHeader
dashboard={dashboard}
@ -421,13 +414,6 @@ class DashboardPage extends Component<Props, State> {
}
private handleShowCellEditorOverlay = (cell: DashboardsModels.Cell): void => {
const {timeRange} = this.props
this.timeMachineContainer = new TimeMachineContainer({
...initialStateFromCell(cell),
timeRange,
})
this.setState({selectedCell: cell, showCellEditorOverlay: true})
}

View File

@ -46,6 +46,7 @@ import {
TEMP_VAR_DASHBOARD_TIME,
TEMP_VAR_UPPER_DASHBOARD_TIME,
} from 'src/shared/constants'
import {DE_LOCAL_STORAGE_KEY} from 'src/data_explorer/constants'
// Types
import {
@ -97,6 +98,7 @@ interface ConnectedProps {
script: string
onUpdateQueryDrafts: (queryDrafts: CellQuery[]) => void
onChangeScript: TimeMachineContainer['handleChangeScript']
onResetTimeMachine: TimeMachineContainer['reset']
}
type Props = PassedProps & ConnectedProps
@ -122,11 +124,14 @@ export class DataExplorer extends PureComponent<Props, State> {
}
public async componentDidMount() {
const {autoRefresh} = this.props
const {autoRefresh, onResetTimeMachine} = this.props
await this.resolveQueryParams()
onResetTimeMachine({}, DE_LOCAL_STORAGE_KEY)
GlobalAutoRefresher.poll(autoRefresh)
this.setState({isComponentMounted: true})
}
@ -440,6 +445,7 @@ const ConnectedDataExplorer = (props: PassedProps & WithRouterProps) => {
script={timeMachineContainer.state.script}
onChangeScript={timeMachineContainer.handleChangeScript}
onUpdateQueryDrafts={timeMachineContainer.handleUpdateQueryDrafts}
onResetTimeMachine={timeMachineContainer.reset}
/>
)}
</Subscribe>

View File

@ -1,11 +1,8 @@
import React, {PureComponent} from 'react'
import {Provider} from 'unstated'
import DataExplorer from './DataExplorer'
import {TimeMachineContainer} from 'src/shared/utils/TimeMachineContainer'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {DE_LOCAL_STORAGE_KEY} from 'src/data_explorer/constants'
import {Source} from 'src/types'
@ -15,23 +12,10 @@ interface Props {
@ErrorHandling
class DataExplorerPage extends PureComponent<Props> {
private timeMachineContainer: TimeMachineContainer
constructor(props: Props) {
super(props)
this.timeMachineContainer = new TimeMachineContainer(
{},
DE_LOCAL_STORAGE_KEY
)
}
public render() {
return (
<div className="page">
<Provider inject={[this.timeMachineContainer]}>
<DataExplorer source={this.props.source} />
</Provider>
<DataExplorer source={this.props.source} />
</div>
)
}

View File

@ -2,7 +2,8 @@ import 'babel-polyfill'
import React, {PureComponent} from 'react'
import {render} from 'react-dom'
import {Provider} from 'react-redux'
import {Provider as ReduxProvider} from 'react-redux'
import {Provider as UnstatedProvider} from 'unstated'
import {Router, Route, useRouterHistory} from 'react-router'
import {createHistory} from 'history'
import {syncHistoryWithStore} from 'react-router-redux'
@ -122,52 +123,69 @@ class Root extends PureComponent<{}, State> {
public render() {
return this.state.ready ? (
<Provider store={store}>
<Router history={history}>
<Route path="/" component={UserIsAuthenticated(CheckSources)} />
<Route path="/login" component={UserIsNotAuthenticated(Login)} />
<Route path="/purgatory" component={UserIsAuthenticated(Purgatory)} />
<Route component={UserIsAuthenticated(App)}>
<Route path="/logs" component={LogsPage} />
</Route>
<Route
path="/sources/new"
component={UserIsAuthenticated(OnboardingWizard)}
/>
<Route path="/sources/:sourceID" component={UserIsAuthenticated(App)}>
<Route component={CheckSources}>
<Route path="status" component={StatusPage} />
<Route path="hosts" component={HostsPage} />
<Route path="hosts/:hostID" component={HostPage} />
<Route
path="chronograf/data-explorer"
component={DataExplorerPage}
/>
<Route path="dashboards" component={DashboardsPage} />
<Route path="dashboards/:dashboardID" component={DashboardPage} />
<Route path="alerts" component={AlertsApp} />
<Route path="alert-rules" component={KapacitorRulesPage} />
<Route path="alert-rules/:ruleID" component={KapacitorRulePage} />
<Route path="alert-rules/new" component={KapacitorRulePage} />
<Route path="tickscript/new" component={TickscriptPage} />
<Route path="tickscript/:ruleID" component={TickscriptPage} />
<Route path="kapacitors/new" component={KapacitorPage} />
<Route path="kapacitors/:id/edit" component={KapacitorPage} />
<Route
path="kapacitors/:id/edit:hash"
component={KapacitorPage}
/>
<Route
path="admin-chronograf/:tab"
component={AdminChronografPage}
/>
<Route path="admin-influxdb/:tab" component={AdminInfluxDBPage} />
<Route path="manage-sources" component={ManageSources} />
<ReduxProvider store={store}>
<UnstatedProvider>
<Router history={history}>
<Route path="/" component={UserIsAuthenticated(CheckSources)} />
<Route path="/login" component={UserIsNotAuthenticated(Login)} />
<Route
path="/purgatory"
component={UserIsAuthenticated(Purgatory)}
/>
<Route component={UserIsAuthenticated(App)}>
<Route path="/logs" component={LogsPage} />
</Route>
</Route>
<Route path="*" component={NotFound} />
</Router>
</Provider>
<Route
path="/sources/new"
component={UserIsAuthenticated(OnboardingWizard)}
/>
<Route
path="/sources/:sourceID"
component={UserIsAuthenticated(App)}
>
<Route component={CheckSources}>
<Route path="status" component={StatusPage} />
<Route path="hosts" component={HostsPage} />
<Route path="hosts/:hostID" component={HostPage} />
<Route
path="chronograf/data-explorer"
component={DataExplorerPage}
/>
<Route path="dashboards" component={DashboardsPage} />
<Route
path="dashboards/:dashboardID"
component={DashboardPage}
/>
<Route path="alerts" component={AlertsApp} />
<Route path="alert-rules" component={KapacitorRulesPage} />
<Route
path="alert-rules/:ruleID"
component={KapacitorRulePage}
/>
<Route path="alert-rules/new" component={KapacitorRulePage} />
<Route path="tickscript/new" component={TickscriptPage} />
<Route path="tickscript/:ruleID" component={TickscriptPage} />
<Route path="kapacitors/new" component={KapacitorPage} />
<Route path="kapacitors/:id/edit" component={KapacitorPage} />
<Route
path="kapacitors/:id/edit:hash"
component={KapacitorPage}
/>
<Route
path="admin-chronograf/:tab"
component={AdminChronografPage}
/>
<Route
path="admin-influxdb/:tab"
component={AdminInfluxDBPage}
/>
<Route path="manage-sources" component={ManageSources} />
</Route>
</Route>
<Route path="*" component={NotFound} />
</Router>
</UnstatedProvider>
</ReduxProvider>
) : (
<div className="page-spinner" />
)

View File

@ -65,6 +65,24 @@ import {ColorString, ColorNumber} from 'src/types/colors'
const LOCAL_STORAGE_DELAY_MS = 1000
const DEFAULT_STATE = () => ({
script: '',
queryDrafts: [],
timeRange: DEFAULT_TIME_RANGE,
type: CellType.Line,
note: '',
noteVisibility: NoteVisibility.Default,
thresholdsListType: ThresholdType.Text,
thresholdsListColors: DEFAULT_THRESHOLDS_LIST_COLORS,
gaugeColors: DEFAULT_GAUGE_COLORS,
lineColors: DEFAULT_LINE_COLORS,
axes: DEFAULT_AXES,
tableOptions: DEFAULT_TABLE_OPTIONS,
timeFormat: DEFAULT_TIME_FORMAT,
decimalPlaces: DEFAULT_DECIMAL_PLACES,
fieldOptions: DEFAULT_FIELD_OPTIONS,
})
export interface TimeMachineState {
script: string
queryDrafts: CellQuery[]
@ -84,38 +102,24 @@ export interface TimeMachineState {
}
export class TimeMachineContainer extends Container<TimeMachineState> {
public state: TimeMachineState = DEFAULT_STATE()
private debouncer: Debouncer = new DefaultDebouncer()
private localStorageKey?: string
constructor(
public reset = (
initialState: Partial<TimeMachineState> = {},
localStorageKey?: string
) {
super()
const localStorageState = getLocalStorage(localStorageKey)
this.state = {
script: '',
queryDrafts: [],
timeRange: DEFAULT_TIME_RANGE,
type: CellType.Line,
note: '',
noteVisibility: NoteVisibility.Default,
thresholdsListType: ThresholdType.Text,
thresholdsListColors: DEFAULT_THRESHOLDS_LIST_COLORS,
gaugeColors: DEFAULT_GAUGE_COLORS,
lineColors: DEFAULT_LINE_COLORS,
axes: DEFAULT_AXES,
tableOptions: DEFAULT_TABLE_OPTIONS,
timeFormat: DEFAULT_TIME_FORMAT,
decimalPlaces: DEFAULT_DECIMAL_PLACES,
fieldOptions: DEFAULT_FIELD_OPTIONS,
...localStorageState,
...initialState,
}
): Promise<void> => {
const localStorageState = getLocalStorage(localStorageKey) || {}
this.localStorageKey = localStorageKey
return this.setAndPersistState({
...DEFAULT_STATE(),
...localStorageState,
...initialState,
})
}
public handleChangeScript = (script: string) => {

View File

@ -1,21 +1,21 @@
import {GIT_SHA} from 'src/shared/constants'
export function setLocalStorage(key: string, data: any): void {
export function setLocalStorage(key: string, data: object): void {
const localStorageData = {version: GIT_SHA, data}
window.localStorage.setItem(key, JSON.stringify(localStorageData))
}
export function getLocalStorage(key: string): any {
export function getLocalStorage(key: string): object | null {
try {
const {version, data} = JSON.parse(window.localStorage.getItem(key))
if (version !== GIT_SHA) {
return {}
return null
}
return data
} catch {
return {}
return null
}
}

View File

@ -11,6 +11,7 @@ import {QueryType} from 'src/types'
describe('TimeMachineContainer', () => {
let initialState: Partial<TimeMachineState>
let container: TimeMachineContainer
beforeEach(() => {
const queryDraft1 = defaultQueryDraft(QueryType.InfluxQL)
@ -22,10 +23,11 @@ describe('TimeMachineContainer', () => {
queryDraft2.queryConfig.id = '2'
initialState = {queryDrafts: [queryDraft1, queryDraft2]}
container = new TimeMachineContainer()
})
test('handleAddQuery', async () => {
const container = new TimeMachineContainer(initialState)
await container.reset(initialState)
expect(container.state.queryDrafts).toHaveLength(2)
@ -35,7 +37,7 @@ describe('TimeMachineContainer', () => {
})
test('handleChooseNamespace', async () => {
const container = new TimeMachineContainer(initialState)
await container.reset(initialState)
await container.handleChooseNamespace('1', {
database: 'foo',
@ -49,7 +51,7 @@ describe('TimeMachineContainer', () => {
})
test('handleChooseMeasurement', async () => {
const container = new TimeMachineContainer(initialState)
await container.reset(initialState)
await container.handleChooseMeasurement('1', 'foo')
@ -59,7 +61,7 @@ describe('TimeMachineContainer', () => {
})
test('handleToggleField', async () => {
const container = new TimeMachineContainer(initialState)
await container.reset(initialState)
await container.handleToggleField('1', {value: 'a', type: 'field'})
@ -93,7 +95,7 @@ describe('TimeMachineContainer', () => {
},
]
const container = new TimeMachineContainer(initialState)
await container.reset(initialState)
await container.handleApplyFuncsToField('1', {
field: {
@ -162,7 +164,7 @@ describe('TimeMachineContainer', () => {
groupBy,
})
const container = new TimeMachineContainer(initialState)
await container.reset(initialState)
await container.handleRemoveFuncs('1', fields)
@ -179,7 +181,7 @@ describe('TimeMachineContainer', () => {
k2: ['foo'],
}
const container = new TimeMachineContainer(initialState)
await container.reset(initialState)
await container.handleChooseTag('1', {
key: 'k1',
@ -195,7 +197,7 @@ describe('TimeMachineContainer', () => {
})
test("creates a new entry if it's the first key", async () => {
const container = new TimeMachineContainer(initialState)
await container.reset(initialState)
await container.handleChooseTag('1', {
key: 'k1',
@ -214,7 +216,7 @@ describe('TimeMachineContainer', () => {
k1: ['v1'],
}
const container = new TimeMachineContainer(initialState)
await container.reset(initialState)
await container.handleChooseTag('1', {
key: 'k1',
@ -240,7 +242,7 @@ describe('TimeMachineContainer', () => {
},
})
const container = new TimeMachineContainer(initialState)
await container.reset(initialState)
await container.handleGroupByTag('1', 'k1')
@ -262,7 +264,7 @@ describe('TimeMachineContainer', () => {
},
})
const container = new TimeMachineContainer(initialState)
await container.reset(initialState)
await container.handleGroupByTag('1', 'k1')
@ -274,7 +276,7 @@ describe('TimeMachineContainer', () => {
})
test('handleToggleTagAcceptance', async () => {
const container = new TimeMachineContainer(initialState)
await container.reset(initialState)
await container.handleToggleTagAcceptance('1')
@ -284,7 +286,7 @@ describe('TimeMachineContainer', () => {
})
test('handleGroupByTime', async () => {
const container = new TimeMachineContainer(initialState)
await container.reset(initialState)
await container.handleGroupByTime('1', '100y')
@ -294,7 +296,7 @@ describe('TimeMachineContainer', () => {
})
test('handleEditQueryStatus', async () => {
const container = new TimeMachineContainer(initialState)
await container.reset(initialState)
await container.handleEditQueryStatus('1', {success: 'yay'})
@ -305,7 +307,7 @@ describe('TimeMachineContainer', () => {
describe('handleFill', () => {
it('applies an explicit fill when group by time is used', async () => {
const container = new TimeMachineContainer(initialState)
await container.reset(initialState)
await container.handleGroupByTime('1', '10s')
@ -315,7 +317,7 @@ describe('TimeMachineContainer', () => {
})
it('updates fill to non-null-string non-number string value', async () => {
const container = new TimeMachineContainer(initialState)
await container.reset(initialState)
await container.handleFill('1', LINEAR)
@ -325,7 +327,7 @@ describe('TimeMachineContainer', () => {
})
it('updates fill to string integer value', async () => {
const container = new TimeMachineContainer(initialState)
await container.reset(initialState)
await container.handleFill('1', '1337')
@ -335,7 +337,7 @@ describe('TimeMachineContainer', () => {
})
it('updates fill to string float value', async () => {
const container = new TimeMachineContainer(initialState)
await container.reset(initialState)
await container.handleFill('1', '1.337')
@ -346,7 +348,8 @@ describe('TimeMachineContainer', () => {
})
test('handleTimeShift', async () => {
const container = new TimeMachineContainer(initialState)
await container.reset(initialState)
const shift = {
quantity: '1',
unit: 'd',