From 1c6eca09bb00621d00b46acd0c8ebf86323e48de Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Tue, 8 Feb 2022 09:31:42 +1100 Subject: [PATCH] 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 functions --- .../adhoc/AdHocVariableEditor.test.tsx | 53 +++++++++++ .../variables/adhoc/AdHocVariableEditor.tsx | 12 +-- .../app/features/variables/adhoc/reducer.ts | 5 -- .../DataSourceVariableEditor.test.tsx | 55 ++++++++++++ .../datasource/DataSourceVariableEditor.tsx | 63 ++++++------- .../features/variables/datasource/reducer.ts | 4 - .../features/variables/editor/reducer.test.ts | 3 +- .../app/features/variables/editor/reducer.ts | 24 ++++- .../variables/editor/selectors.test.ts | 89 +++++++++++++++++++ .../features/variables/editor/selectors.ts | 41 +++++++++ .../query/QueryVariableEditor.test.tsx | 13 ++- .../variables/query/QueryVariableEditor.tsx | 14 +-- .../app/features/variables/query/actions.ts | 13 +-- .../app/features/variables/query/reducer.ts | 16 +--- 14 files changed, 313 insertions(+), 92 deletions(-) create mode 100644 public/app/features/variables/adhoc/AdHocVariableEditor.test.tsx create mode 100644 public/app/features/variables/datasource/DataSourceVariableEditor.test.tsx create mode 100644 public/app/features/variables/editor/selectors.test.ts create mode 100644 public/app/features/variables/editor/selectors.ts diff --git a/public/app/features/variables/adhoc/AdHocVariableEditor.test.tsx b/public/app/features/variables/adhoc/AdHocVariableEditor.test.tsx new file mode 100644 index 00000000000..82944657f9e --- /dev/null +++ b/public/app/features/variables/adhoc/AdHocVariableEditor.test.tsx @@ -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(); + + const selectContainer = getSelectParent(screen.getByLabelText('Data source')); + expect(selectContainer).toHaveTextContent('Prometheus'); + }); + + it('calls the callback when changing the datasource', async () => { + render(); + 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(); + + const alert = screen.getByText("Here's a message that should help you"); + expect(alert).toBeInTheDocument(); + }); +}); diff --git a/public/app/features/variables/adhoc/AdHocVariableEditor.tsx b/public/app/features/variables/adhoc/AdHocVariableEditor.tsx index 387de6bb027..f00a074c21d 100644 --- a/public/app/features/variables/adhoc/AdHocVariableEditor.tsx +++ b/public/app/features/variables/adhoc/AdHocVariableEditor.tsx @@ -5,15 +5,14 @@ import { DataSourceRef, SelectableValue } from '@grafana/data'; import { AdHocVariableModel } from '../types'; import { VariableEditorProps } from '../editor/types'; -import { VariableEditorState } from '../editor/reducer'; -import { AdHocVariableEditorState } from './reducer'; import { changeVariableDatasource, initAdHocVariableEditor } from './actions'; import { StoreState } from 'app/types'; import { VariableSectionHeader } from '../editor/VariableSectionHeader'; import { VariableSelectField } from '../editor/VariableSelectField'; +import { getAdhocVariableEditorState } from '../editor/selectors'; const mapStateToProps = (state: StoreState) => ({ - editor: state.templating.editor as VariableEditorState, + extended: getAdhocVariableEditorState(state.templating.editor), }); const mapDispatchToProps = { @@ -37,9 +36,9 @@ export class AdHocVariableEditorUnConnected extends PureComponent { }; render() { - const { variable, editor } = this.props; - const dataSources = editor.extended?.dataSources ?? []; - const infoText = editor.extended?.infoText ?? null; + const { variable, extended } = this.props; + const dataSources = extended?.dataSources ?? []; + const infoText = extended?.infoText ?? null; const options = dataSources.map((ds) => ({ label: ds.text, value: ds.value })); const value = options.find((o) => o.value?.uid === variable.datasource?.uid) ?? options[0]; @@ -56,6 +55,7 @@ export class AdHocVariableEditorUnConnected extends PureComponent { labelWidth={10} /> + {infoText ? : null} diff --git a/public/app/features/variables/adhoc/reducer.ts b/public/app/features/variables/adhoc/reducer.ts index 50cafae936a..3f5aa9f9222 100644 --- a/public/app/features/variables/adhoc/reducer.ts +++ b/public/app/features/variables/adhoc/reducer.ts @@ -1,5 +1,4 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { DataSourceRef } from '@grafana/data'; import { AdHocVariableFilter, AdHocVariableModel, initialVariableModelState } from 'app/features/variables/types'; import { getInstanceState, initialVariablesState, VariablePayload, VariablesState } from '../state/types'; @@ -8,10 +7,6 @@ export interface AdHocVariabelFilterUpdate { index: number; filter: AdHocVariableFilter; } -export interface AdHocVariableEditorState { - infoText: string; - dataSources: Array<{ text: string; value: DataSourceRef | null }>; -} export const initialAdHocVariableModelState: AdHocVariableModel = { ...initialVariableModelState, diff --git a/public/app/features/variables/datasource/DataSourceVariableEditor.test.tsx b/public/app/features/variables/datasource/DataSourceVariableEditor.test.tsx new file mode 100644 index 00000000000..9bf77dec0c2 --- /dev/null +++ b/public/app/features/variables/datasource/DataSourceVariableEditor.test.tsx @@ -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(); + + const selectContainer = getSelectParent(screen.getByLabelText('Type')); + expect(selectContainer).toHaveTextContent('Prometheus'); + }); + + it('calls the handler when the data source is changed', async () => { + render(); + await selectOptionInTest(screen.getByLabelText('Type'), 'Loki'); + + expect(props.onPropChange).toBeCalledWith({ propName: 'query', propValue: 'ds-loki', updateOptions: true }); + }); + + it('has a regex filter field', () => { + render(); + const field = screen.getByLabelText('Instance name filter'); + expect(field).toBeInTheDocument(); + }); + + it('calls the handler when the regex filter is changed', () => { + render(); + const field = screen.getByLabelText('Instance name filter'); + fireEvent.change(field, { target: { value: '/prod/' } }); + expect(props.onPropChange).toBeCalledWith({ propName: 'regex', propValue: '/prod/' }); + }); +}); diff --git a/public/app/features/variables/datasource/DataSourceVariableEditor.tsx b/public/app/features/variables/datasource/DataSourceVariableEditor.tsx index 73af0609cea..0be82c77056 100644 --- a/public/app/features/variables/datasource/DataSourceVariableEditor.tsx +++ b/public/app/features/variables/datasource/DataSourceVariableEditor.tsx @@ -1,34 +1,34 @@ import React, { FormEvent, PureComponent } from 'react'; -import { MapDispatchToProps, MapStateToProps } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { InlineFieldRow, VerticalGroup } from '@grafana/ui'; import { DataSourceVariableModel, VariableWithMultiSupport } from '../types'; import { OnPropChangeArguments, VariableEditorProps } from '../editor/types'; import { SelectionOptionsEditor } from '../editor/SelectionOptionsEditor'; -import { VariableEditorState } from '../editor/reducer'; -import { DataSourceVariableEditorState } from './reducer'; import { initDataSourceVariableEditor } from './actions'; import { StoreState } from '../../../types'; -import { connectWithStore } from '../../../core/utils/connectWithReduxStore'; import { changeVariableMultiValue } from '../state/actions'; import { VariableSectionHeader } from '../editor/VariableSectionHeader'; import { VariableSelectField } from '../editor/VariableSelectField'; import { SelectableValue } from '@grafana/data'; import { VariableTextField } from '../editor/VariableTextField'; import { selectors } from '@grafana/e2e-selectors'; +import { getDatasourceVariableEditorState } from '../editor/selectors'; -export interface OwnProps extends VariableEditorProps {} +const mapStateToProps = (state: StoreState) => ({ + extended: getDatasourceVariableEditorState(state.templating.editor), +}); -interface ConnectedProps { - editor: VariableEditorState; -} +const mapDispatchToProps = { + initDataSourceVariableEditor, + changeVariableMultiValue, +}; -interface DispatchProps { - initDataSourceVariableEditor: typeof initDataSourceVariableEditor; - changeVariableMultiValue: typeof changeVariableMultiValue; -} +const connector = connect(mapStateToProps, mapDispatchToProps); -type Props = OwnProps & ConnectedProps & DispatchProps; +export interface OwnProps extends VariableEditorProps {} + +type Props = OwnProps & ConnectedProps; export class DataSourceVariableEditorUnConnected extends PureComponent { componentDidMount() { @@ -55,11 +55,14 @@ export class DataSourceVariableEditorUnConnected extends PureComponent { }; getSelectedDataSourceTypeValue = (): string => { - if (!this.props.editor.extended?.dataSourceTypes?.length) { + const { extended } = this.props; + + if (!extended?.dataSourceTypes.length) { return ''; } - const foundItem = this.props.editor.extended?.dataSourceTypes.find((ds) => ds.value === this.props.variable.query); - const value = foundItem ? foundItem.value : this.props.editor.extended?.dataSourceTypes[0].value; + + const foundItem = extended.dataSourceTypes.find((ds) => ds.value === this.props.variable.query); + const value = foundItem ? foundItem.value : extended.dataSourceTypes[0].value; return value ?? ''; }; @@ -68,10 +71,13 @@ export class DataSourceVariableEditorUnConnected extends PureComponent { }; render() { - const typeOptions = this.props.editor.extended?.dataSourceTypes?.length - ? this.props.editor.extended?.dataSourceTypes?.map((ds) => ({ value: ds.value ?? '', label: ds.text })) + const { variable, extended, changeVariableMultiValue } = this.props; + + const typeOptions = extended?.dataSourceTypes?.length + ? extended.dataSourceTypes?.map((ds) => ({ value: ds.value ?? '', label: ds.text })) : []; - const typeValue = typeOptions.find((o) => o.value === this.props.variable.query) ?? typeOptions[0]; + + const typeValue = typeOptions.find((o) => o.value === variable.query) ?? typeOptions[0]; return ( @@ -110,9 +116,9 @@ export class DataSourceVariableEditorUnConnected extends PureComponent { @@ -120,17 +126,4 @@ export class DataSourceVariableEditorUnConnected extends PureComponent { } } -const mapStateToProps: MapStateToProps = (state, ownProps) => ({ - editor: state.templating.editor as VariableEditorState, -}); - -const mapDispatchToProps: MapDispatchToProps = { - initDataSourceVariableEditor, - changeVariableMultiValue, -}; - -export const DataSourceVariableEditor = connectWithStore( - DataSourceVariableEditorUnConnected, - mapStateToProps, - mapDispatchToProps -); +export const DataSourceVariableEditor = connector(DataSourceVariableEditorUnConnected); diff --git a/public/app/features/variables/datasource/reducer.ts b/public/app/features/variables/datasource/reducer.ts index d67a96d0689..1a6cb0619d3 100644 --- a/public/app/features/variables/datasource/reducer.ts +++ b/public/app/features/variables/datasource/reducer.ts @@ -5,10 +5,6 @@ import { DataSourceVariableModel, initialVariableModelState, VariableOption, Var import { getInstanceState, initialVariablesState, VariablePayload, VariablesState } from '../state/types'; import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE } from '../constants'; -export interface DataSourceVariableEditorState { - dataSourceTypes: Array<{ text: string; value: string }>; -} - export const initialDataSourceVariableModelState: DataSourceVariableModel = { ...initialVariableModelState, type: 'datasource', diff --git a/public/app/features/variables/editor/reducer.test.ts b/public/app/features/variables/editor/reducer.test.ts index c43e2345039..a50a42cb5f2 100644 --- a/public/app/features/variables/editor/reducer.test.ts +++ b/public/app/features/variables/editor/reducer.test.ts @@ -62,7 +62,7 @@ describe('variableEditorReducer', () => { name: 'A name', isValid: false, errors: { update: 'Something wrong' }, - extended: { prop: 1000 }, + extended: null, }; const payload = toVariablePayload({ id: '0', type: 'textbox' }); reducerTester() @@ -188,6 +188,7 @@ describe('variableEditorReducer', () => { .thenStateShouldEqual({ ...initialVariableEditorState, extended: { + // @ts-ignore - temp ignoring this, we'll fix it soon someProp: [{}], }, }); diff --git a/public/app/features/variables/editor/reducer.ts b/public/app/features/variables/editor/reducer.ts index e3caa2fd4cc..4a93f91dc02 100644 --- a/public/app/features/variables/editor/reducer.ts +++ b/public/app/features/variables/editor/reducer.ts @@ -1,13 +1,30 @@ +import { DataSourceApi, DataSourceRef } from '@grafana/data'; import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { VariablePayload } from '../state/types'; +import { VariableQueryEditorType } from '../types'; -type VariableEditorExtension = { [P in keyof ExtendedProps]: ExtendedProps[P] }; -export interface VariableEditorState { +export interface AdHocVariableEditorState { + infoText?: string; + dataSources: Array<{ text: string; value: DataSourceRef | null }>; +} + +export interface DataSourceVariableEditorState { + dataSourceTypes: Array<{ text: string; value: string }>; +} + +export interface QueryVariableEditorState { + VariableQueryEditor: VariableQueryEditorType; + dataSource: DataSourceApi | null; +} + +type VariableEditorExtension = AdHocVariableEditorState | DataSourceVariableEditorState | QueryVariableEditorState; + +export interface VariableEditorState { id: string; name: string; errors: Record; isValid: boolean; - extended: VariableEditorExtension | null; + extended: VariableEditorExtension | null; } export const initialVariableEditorState: VariableEditorState = { @@ -65,6 +82,7 @@ const variableEditorReducerSlice = createSlice({ state: VariableEditorState, action: PayloadAction<{ propName: string; propValue: any }> ) => { + // @ts-ignore - temp ignoring the errors now the state type is more strict state.extended = { ...state.extended, [action.payload.propName]: action.payload.propValue, diff --git a/public/app/features/variables/editor/selectors.test.ts b/public/app/features/variables/editor/selectors.test.ts new file mode 100644 index 00000000000..6380d9153df --- /dev/null +++ b/public/app/features/variables/editor/selectors.test.ts @@ -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(); + }); +}); diff --git a/public/app/features/variables/editor/selectors.ts b/public/app/features/variables/editor/selectors.ts new file mode 100644 index 00000000000..2152d4ed18f --- /dev/null +++ b/public/app/features/variables/editor/selectors.ts @@ -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; +} diff --git a/public/app/features/variables/query/QueryVariableEditor.test.tsx b/public/app/features/variables/query/QueryVariableEditor.test.tsx index d61344f5b46..69c4cd08810 100644 --- a/public/app/features/variables/query/QueryVariableEditor.test.tsx +++ b/public/app/features/variables/query/QueryVariableEditor.test.tsx @@ -5,7 +5,6 @@ import { DataSourceApi } from '@grafana/data'; import { Props, QueryVariableEditorUnConnected } from './QueryVariableEditor'; import { initialQueryVariableModelState } from './reducer'; -import { initialVariableEditorState } from '../editor/reducer'; import { describe, expect } from '../../../../test/lib/common'; import { LegacyVariableQueryEditor } from '../editor/LegacyVariableQueryEditor'; import { mockDataSource } from 'app/features/alerting/unified/mocks'; @@ -13,19 +12,17 @@ import { DataSourceType } from 'app/features/alerting/unified/utils/datasource'; import { NEW_VARIABLE_ID } from '../constants'; const setupTestContext = (options: Partial) => { + const extended = { + VariableQueryEditor: LegacyVariableQueryEditor, + dataSource: {} as unknown as DataSourceApi, + }; const defaults: Props = { variable: { ...initialQueryVariableModelState }, initQueryVariableEditor: jest.fn(), changeQueryVariableDataSource: jest.fn(), changeQueryVariableQuery: jest.fn(), changeVariableMultiValue: jest.fn(), - editor: { - ...initialVariableEditorState, - extended: { - VariableQueryEditor: LegacyVariableQueryEditor, - dataSource: {} as unknown as DataSourceApi, - }, - }, + extended, onPropChange: jest.fn(), }; diff --git a/public/app/features/variables/query/QueryVariableEditor.tsx b/public/app/features/variables/query/QueryVariableEditor.tsx index df4cdfe3ccf..521748f237f 100644 --- a/public/app/features/variables/query/QueryVariableEditor.tsx +++ b/public/app/features/variables/query/QueryVariableEditor.tsx @@ -8,9 +8,7 @@ import { DataSourceInstanceSettings, getDataSourceRef, LoadingState, SelectableV import { SelectionOptionsEditor } from '../editor/SelectionOptionsEditor'; import { QueryVariableModel, VariableRefresh, VariableSort, VariableWithMultiSupport } from '../types'; -import { QueryVariableEditorState } from './reducer'; import { changeQueryVariableDataSource, changeQueryVariableQuery, initQueryVariableEditor } from './actions'; -import { VariableEditorState } from '../editor/reducer'; import { OnPropChangeArguments, VariableEditorProps } from '../editor/types'; import { StoreState } from '../../../types'; import { toVariableIdentifier } from '../state/types'; @@ -21,9 +19,10 @@ import { VariableSectionHeader } from '../editor/VariableSectionHeader'; import { VariableTextField } from '../editor/VariableTextField'; import { QueryVariableRefreshSelect } from './QueryVariableRefreshSelect'; import { QueryVariableSortSelect } from './QueryVariableSortSelect'; +import { getQueryVariableEditorState } from '../editor/selectors'; const mapStateToProps = (state: StoreState) => ({ - editor: state.templating.editor as VariableEditorState, + extended: getQueryVariableEditorState(state.templating.editor), }); const mapDispatchToProps = { @@ -114,14 +113,15 @@ export class QueryVariableEditorUnConnected extends PureComponent }; renderQueryEditor = () => { - const { editor, variable } = this.props; - if (!editor.extended || !editor.extended.dataSource || !editor.extended.VariableQueryEditor) { + const { extended, variable } = this.props; + + if (!extended || !extended.dataSource || !extended.VariableQueryEditor) { return null; } const query = variable.query; - const datasource = editor.extended.dataSource; - const VariableQueryEditor = editor.extended.VariableQueryEditor; + const datasource = extended.dataSource; + const VariableQueryEditor = extended.VariableQueryEditor; if (isLegacyQueryEditor(VariableQueryEditor, datasource)) { return ( diff --git a/public/app/features/variables/query/actions.ts b/public/app/features/variables/query/actions.ts index f88883cf6e6..58debe8d6db 100644 --- a/public/app/features/variables/query/actions.ts +++ b/public/app/features/variables/query/actions.ts @@ -6,19 +6,14 @@ import { updateOptions } from '../state/actions'; import { QueryVariableModel } from '../types'; import { ThunkResult } from '../../../types'; import { getVariable } from '../state/selectors'; -import { - addVariableEditorError, - changeVariableEditorExtended, - removeVariableEditorError, - VariableEditorState, -} from '../editor/reducer'; +import { addVariableEditorError, changeVariableEditorExtended, removeVariableEditorError } from '../editor/reducer'; import { changeVariableProp } from '../state/sharedReducer'; import { toVariableIdentifier, toVariablePayload, VariableIdentifier } from '../state/types'; import { getVariableQueryEditor } from '../editor/getVariableQueryEditor'; import { getVariableQueryRunner } from './VariableQueryRunner'; import { variableQueryObserver } from './variableQueryObserver'; -import { QueryVariableEditorState } from './reducer'; import { hasOngoingTransaction } from '../utils'; +import { getQueryVariableEditorState } from '../editor/selectors'; export const updateQueryVariableOptions = ( identifier: VariableIdentifier, @@ -71,8 +66,8 @@ export const changeQueryVariableDataSource = ( ): ThunkResult => { return async (dispatch, getState) => { try { - const editorState = getState().templating.editor as VariableEditorState; - const previousDatasource = editorState.extended?.dataSource; + const extendedEditorState = getQueryVariableEditorState(getState().templating.editor); + const previousDatasource = extendedEditorState?.dataSource; const dataSource = await getDataSourceSrv().get(name ?? ''); if (previousDatasource && previousDatasource.type !== dataSource?.type) { dispatch(changeVariableProp(toVariablePayload(identifier, { propName: 'query', propValue: '' }))); diff --git a/public/app/features/variables/query/reducer.ts b/public/app/features/variables/query/reducer.ts index 10edda1cec0..03e2e0d89e8 100644 --- a/public/app/features/variables/query/reducer.ts +++ b/public/app/features/variables/query/reducer.ts @@ -1,15 +1,8 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { isNumber, sortBy, toLower, uniqBy } from 'lodash'; -import { DataSourceApi, MetricFindValue, stringToJsRegex } from '@grafana/data'; +import { MetricFindValue, stringToJsRegex } from '@grafana/data'; -import { - initialVariableModelState, - QueryVariableModel, - VariableOption, - VariableQueryEditorType, - VariableRefresh, - VariableSort, -} from '../types'; +import { initialVariableModelState, QueryVariableModel, VariableOption, VariableRefresh, VariableSort } from '../types'; import { getInstanceState, initialVariablesState, VariablePayload, VariablesState } from '../state/types'; import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, NONE_VARIABLE_TEXT, NONE_VARIABLE_VALUE } from '../constants'; @@ -19,11 +12,6 @@ interface VariableOptionsUpdate { results: MetricFindValue[]; } -export interface QueryVariableEditorState { - VariableQueryEditor: VariableQueryEditorType; - dataSource: DataSourceApi | null; -} - export const initialQueryVariableModelState: QueryVariableModel = { ...initialVariableModelState, type: 'query',