mirror of https://github.com/grafana/grafana
Variables: Explicitly type variable editor extended state (#44749)
* Narrow Variable editor state using selector functions - Explicitly type "extended" editor state in editor/reducter.ts using a union - Create selectors to narrow the types, using unique properties from each extended state to discriminate the union - Update DataSourceVariableEditor to use new style of redux connector - Update variable editor components to use new selectors * fix tests * Make adhoc variable infoText optional, because it is! * Add AdHocVariableEditor tests * DataSourceVariableEditor tests * comment * reset * Wrote tests for selectors \(that actually caught a bug, whodathunkit) * fix stray types and lint issues * Rename selector functionspull/45033/head
parent
ed7b9905c2
commit
1c6eca09bb
@ -0,0 +1,53 @@ |
||||
import React from 'react'; |
||||
import { render, screen } from '@testing-library/react'; |
||||
|
||||
import { AdHocVariableEditorUnConnected as AdHocVariableEditor } from './AdHocVariableEditor'; |
||||
import { initialAdHocVariableModelState } from './reducer'; |
||||
import { selectOptionInTest } from '@grafana/ui'; |
||||
import { getSelectParent } from '@grafana/ui/src/components/Select/test-utils'; |
||||
|
||||
const props = { |
||||
extended: { |
||||
dataSources: [ |
||||
{ text: 'Prometheus', value: null }, // default datasource
|
||||
{ text: 'Loki', value: { type: 'loki-ds', uid: 'abc' } }, |
||||
], |
||||
}, |
||||
variable: { ...initialAdHocVariableModelState }, |
||||
onPropChange: jest.fn(), |
||||
|
||||
// connected actions
|
||||
initAdHocVariableEditor: jest.fn(), |
||||
changeVariableDatasource: jest.fn(), |
||||
}; |
||||
|
||||
describe('AdHocVariableEditor', () => { |
||||
beforeEach(() => { |
||||
props.changeVariableDatasource.mockReset(); |
||||
}); |
||||
|
||||
it('has a datasource select menu', async () => { |
||||
render(<AdHocVariableEditor {...props} />); |
||||
|
||||
const selectContainer = getSelectParent(screen.getByLabelText('Data source')); |
||||
expect(selectContainer).toHaveTextContent('Prometheus'); |
||||
}); |
||||
|
||||
it('calls the callback when changing the datasource', async () => { |
||||
render(<AdHocVariableEditor {...props} />); |
||||
await selectOptionInTest(screen.getByLabelText('Data source'), 'Loki'); |
||||
|
||||
expect(props.changeVariableDatasource).toBeCalledWith({ type: 'loki-ds', uid: 'abc' }); |
||||
}); |
||||
|
||||
it('renders informational text', () => { |
||||
const extended = { |
||||
...props.extended, |
||||
infoText: "Here's a message that should help you", |
||||
}; |
||||
render(<AdHocVariableEditor {...props} extended={extended} />); |
||||
|
||||
const alert = screen.getByText("Here's a message that should help you"); |
||||
expect(alert).toBeInTheDocument(); |
||||
}); |
||||
}); |
@ -0,0 +1,55 @@ |
||||
import React from 'react'; |
||||
import { render, screen, fireEvent } from '@testing-library/react'; |
||||
|
||||
import { DataSourceVariableEditorUnConnected as DataSourceVariableEditor } from './DataSourceVariableEditor'; |
||||
import { initialDataSourceVariableModelState } from './reducer'; |
||||
import { selectOptionInTest } from '@grafana/ui'; |
||||
import { getSelectParent } from '@grafana/ui/src/components/Select/test-utils'; |
||||
|
||||
const props = { |
||||
extended: { |
||||
dataSourceTypes: [ |
||||
{ text: 'Prometheus', value: 'ds-prom' }, |
||||
{ text: 'Loki', value: 'ds-loki' }, |
||||
], |
||||
}, |
||||
variable: { ...initialDataSourceVariableModelState }, |
||||
onPropChange: jest.fn(), |
||||
|
||||
// connected actions
|
||||
initDataSourceVariableEditor: jest.fn(), |
||||
changeVariableMultiValue: jest.fn(), |
||||
}; |
||||
|
||||
describe('DataSourceVariableEditor', () => { |
||||
beforeEach(() => { |
||||
props.onPropChange.mockReset(); |
||||
}); |
||||
|
||||
it('has a data source select menu', () => { |
||||
render(<DataSourceVariableEditor {...props} />); |
||||
|
||||
const selectContainer = getSelectParent(screen.getByLabelText('Type')); |
||||
expect(selectContainer).toHaveTextContent('Prometheus'); |
||||
}); |
||||
|
||||
it('calls the handler when the data source is changed', async () => { |
||||
render(<DataSourceVariableEditor {...props} />); |
||||
await selectOptionInTest(screen.getByLabelText('Type'), 'Loki'); |
||||
|
||||
expect(props.onPropChange).toBeCalledWith({ propName: 'query', propValue: 'ds-loki', updateOptions: true }); |
||||
}); |
||||
|
||||
it('has a regex filter field', () => { |
||||
render(<DataSourceVariableEditor {...props} />); |
||||
const field = screen.getByLabelText('Instance name filter'); |
||||
expect(field).toBeInTheDocument(); |
||||
}); |
||||
|
||||
it('calls the handler when the regex filter is changed', () => { |
||||
render(<DataSourceVariableEditor {...props} />); |
||||
const field = screen.getByLabelText('Instance name filter'); |
||||
fireEvent.change(field, { target: { value: '/prod/' } }); |
||||
expect(props.onPropChange).toBeCalledWith({ propName: 'regex', propValue: '/prod/' }); |
||||
}); |
||||
}); |
@ -0,0 +1,89 @@ |
||||
import { |
||||
getAdhocVariableEditorState, |
||||
getDatasourceVariableEditorState, |
||||
getQueryVariableEditorState, |
||||
} from './selectors'; |
||||
import { |
||||
AdHocVariableEditorState, |
||||
DataSourceVariableEditorState, |
||||
initialVariableEditorState, |
||||
QueryVariableEditorState, |
||||
} from './reducer'; |
||||
import { LegacyVariableQueryEditor } from './LegacyVariableQueryEditor'; |
||||
import { DataSourceApi } from '@grafana/data'; |
||||
|
||||
const adhocExtended: AdHocVariableEditorState = { |
||||
dataSources: [ |
||||
{ text: 'Prometheus', value: null }, // default datasource
|
||||
{ text: 'Loki', value: { type: 'loki-ds', uid: 'abc' } }, |
||||
], |
||||
}; |
||||
|
||||
const datasourceExtended: DataSourceVariableEditorState = { |
||||
dataSourceTypes: [ |
||||
{ text: 'Prometheus', value: 'ds-prom' }, |
||||
{ text: 'Loki', value: 'ds-loki' }, |
||||
], |
||||
}; |
||||
|
||||
const queryExtended: QueryVariableEditorState = { |
||||
VariableQueryEditor: LegacyVariableQueryEditor, |
||||
dataSource: {} as unknown as DataSourceApi, |
||||
}; |
||||
|
||||
const adhocVariableState = { |
||||
...initialVariableEditorState, |
||||
extended: adhocExtended, |
||||
}; |
||||
|
||||
const datasourceVariableState = { |
||||
...initialVariableEditorState, |
||||
extended: datasourceExtended, |
||||
}; |
||||
|
||||
const queryVariableState = { |
||||
...initialVariableEditorState, |
||||
extended: queryExtended, |
||||
}; |
||||
|
||||
describe('getAdhocVariableEditorState', () => { |
||||
it('returns the extended properties for adhoc variable state', () => { |
||||
expect(getAdhocVariableEditorState(adhocVariableState)).toBe(adhocExtended); |
||||
}); |
||||
|
||||
it('returns null for datasource variable state', () => { |
||||
expect(getAdhocVariableEditorState(datasourceVariableState)).toBeNull(); |
||||
}); |
||||
|
||||
it('returns null for query variable state', () => { |
||||
expect(getAdhocVariableEditorState(queryVariableState)).toBeNull(); |
||||
}); |
||||
}); |
||||
|
||||
describe('getDatasourceVariableEditorState', () => { |
||||
it('returns the extended properties for datasource variable state', () => { |
||||
expect(getDatasourceVariableEditorState(datasourceVariableState)).toBe(datasourceExtended); |
||||
}); |
||||
|
||||
it('returns null for adhoc variable state', () => { |
||||
expect(getDatasourceVariableEditorState(adhocVariableState)).toBeNull(); |
||||
}); |
||||
|
||||
it('returns null for query variable state', () => { |
||||
expect(getDatasourceVariableEditorState(queryVariableState)).toBeNull(); |
||||
}); |
||||
}); |
||||
|
||||
describe('getQueryVariableEditorState', () => { |
||||
it('returns the extended properties for query variable state', () => { |
||||
expect(getQueryVariableEditorState(queryVariableState)).toBe(queryExtended); |
||||
}); |
||||
|
||||
it('returns null for adhoc variable state', () => { |
||||
expect(getQueryVariableEditorState(adhocVariableState)).toBeNull(); |
||||
}); |
||||
|
||||
it('returns null for datasource variable state', () => { |
||||
expect(getQueryVariableEditorState(datasourceVariableState)).toBeNull(); |
||||
}); |
||||
}); |
@ -0,0 +1,41 @@ |
||||
import { |
||||
AdHocVariableEditorState, |
||||
DataSourceVariableEditorState, |
||||
QueryVariableEditorState, |
||||
VariableEditorState, |
||||
} from './reducer'; |
||||
|
||||
/** |
||||
* Narrows generic variable editor state down to specific Adhoc variable extended editor state |
||||
*/ |
||||
export function getAdhocVariableEditorState(editorState: VariableEditorState): AdHocVariableEditorState | null { |
||||
if (editorState.extended && 'dataSources' in editorState.extended) { |
||||
return editorState.extended; |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* Narrows generic variable editor state down to specific Datasource variable extended editor state |
||||
*/ |
||||
export function getDatasourceVariableEditorState( |
||||
editorState: VariableEditorState |
||||
): DataSourceVariableEditorState | null { |
||||
if (editorState.extended && 'dataSourceTypes' in editorState.extended) { |
||||
return editorState.extended; |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* Narrows generic variable editor state down to specific Query variable extended editor state |
||||
*/ |
||||
export function getQueryVariableEditorState(editorState: VariableEditorState): QueryVariableEditorState | null { |
||||
if (editorState.extended && 'dataSource' in editorState.extended) { |
||||
return editorState.extended; |
||||
} |
||||
|
||||
return null; |
||||
} |
Loading…
Reference in new issue