From b55b3307c194c5d67496ab0ece8cab07a85b2ec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Tue, 11 May 2021 14:09:19 +0200 Subject: [PATCH] Variables: Clear query when data source type changes (#33924) --- .../variables/query/QueryVariableEditor.tsx | 47 +++++++------------ .../features/variables/query/actions.test.ts | 36 ++++++++++++++ .../app/features/variables/query/actions.ts | 15 +++++- 3 files changed, 65 insertions(+), 33 deletions(-) diff --git a/public/app/features/variables/query/QueryVariableEditor.tsx b/public/app/features/variables/query/QueryVariableEditor.tsx index 1f612c7022b..2c12312effa 100644 --- a/public/app/features/variables/query/QueryVariableEditor.tsx +++ b/public/app/features/variables/query/QueryVariableEditor.tsx @@ -1,6 +1,6 @@ import React, { FormEvent, PureComponent } from 'react'; +import { connect, ConnectedProps } from 'react-redux'; import { css } from '@emotion/css'; -import { MapDispatchToProps, MapStateToProps } from 'react-redux'; import { InlineField, InlineFieldRow, VerticalGroup } from '@grafana/ui'; import { selectors } from '@grafana/e2e-selectors'; import { DataSourcePicker, getTemplateSrv } from '@grafana/runtime'; @@ -13,7 +13,6 @@ import { changeQueryVariableDataSource, changeQueryVariableQuery, initQueryVaria import { VariableEditorState } from '../editor/reducer'; import { OnPropChangeArguments, VariableEditorProps } from '../editor/types'; import { StoreState } from '../../../types'; -import { connectWithStore } from '../../../core/utils/connectWithReduxStore'; import { toVariableIdentifier } from '../state/types'; import { changeVariableMultiValue } from '../state/actions'; import { getTimeSrv } from '../../dashboard/services/TimeSrv'; @@ -23,20 +22,22 @@ import { VariableTextField } from '../editor/VariableTextField'; import { QueryVariableRefreshSelect } from './QueryVariableRefreshSelect'; import { QueryVariableSortSelect } from './QueryVariableSortSelect'; +const mapStateToProps = (state: StoreState) => ({ + editor: state.templating.editor as VariableEditorState, +}); + +const mapDispatchToProps = { + initQueryVariableEditor, + changeQueryVariableDataSource, + changeQueryVariableQuery, + changeVariableMultiValue, +}; + +const connector = connect(mapStateToProps, mapDispatchToProps); + export interface OwnProps extends VariableEditorProps {} -interface ConnectedProps { - editor: VariableEditorState; -} - -interface DispatchProps { - initQueryVariableEditor: typeof initQueryVariableEditor; - changeQueryVariableDataSource: typeof changeQueryVariableDataSource; - changeQueryVariableQuery: typeof changeQueryVariableQuery; - changeVariableMultiValue: typeof changeVariableMultiValue; -} - -export type Props = OwnProps & ConnectedProps & DispatchProps; +export type Props = OwnProps & ConnectedProps; export interface State { regex: string | null; @@ -65,7 +66,6 @@ export class QueryVariableEditorUnConnected extends PureComponent } onDataSourceChange = (dsSettings: DataSourceInstanceSettings) => { - this.props.onPropChange({ propName: 'query', propValue: '' }); this.props.onPropChange({ propName: 'datasource', propValue: dsSettings.isDefault ? null : dsSettings.name, @@ -215,19 +215,4 @@ export class QueryVariableEditorUnConnected extends PureComponent } } -const mapStateToProps: MapStateToProps = (state, ownProps) => ({ - editor: state.templating.editor as VariableEditorState, -}); - -const mapDispatchToProps: MapDispatchToProps = { - initQueryVariableEditor, - changeQueryVariableDataSource, - changeQueryVariableQuery, - changeVariableMultiValue, -}; - -export const QueryVariableEditor = connectWithStore( - QueryVariableEditorUnConnected, - mapStateToProps, - mapDispatchToProps -); +export const QueryVariableEditor = connector(QueryVariableEditorUnConnected); diff --git a/public/app/features/variables/query/actions.test.ts b/public/app/features/variables/query/actions.test.ts index 4365d0ccd86..2b880f5b1de 100644 --- a/public/app/features/variables/query/actions.test.ts +++ b/public/app/features/variables/query/actions.test.ts @@ -346,6 +346,42 @@ describe('query actions', () => { return actions.length === expectedNumberOfActions; }); }); + + describe('and data source type changed', () => { + it('then correct actions are dispatched', async () => { + const variable = createVariable({ datasource: 'other' }); + const editor = {}; + const preloadedState: any = { templating: { editor: { extended: { dataSource: { type: 'previous' } } } } }; + + mocks.pluginLoader.importDataSourcePlugin = jest.fn().mockResolvedValue({ + components: { VariableQueryEditor: editor }, + }); + + const tester = await reduxTester({ preloadedState }) + .givenRootReducer(getRootReducer()) + .whenActionIsDispatched( + addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })) + ) + .whenAsyncActionIsDispatched(changeQueryVariableDataSource(toVariablePayload(variable), 'datasource'), true); + + tester.thenDispatchedActionsPredicateShouldEqual((actions) => { + const [changeVariable, updateDatasource, updateEditor] = actions; + const expectedNumberOfActions = 3; + + expect(changeVariable).toEqual( + changeVariableProp(toVariablePayload(variable, { propName: 'query', propValue: '' })) + ); + expect(updateDatasource).toEqual( + changeVariableEditorExtended({ propName: 'dataSource', propValue: mocks.datasource }) + ); + expect(updateEditor).toEqual( + changeVariableEditorExtended({ propName: 'VariableQueryEditor', propValue: editor }) + ); + + return actions.length === expectedNumberOfActions; + }); + }); + }); }); describe('when changeQueryVariableDataSource is dispatched and editor is not configured', () => { diff --git a/public/app/features/variables/query/actions.ts b/public/app/features/variables/query/actions.ts index fb2fbf96aec..c399dd82361 100644 --- a/public/app/features/variables/query/actions.ts +++ b/public/app/features/variables/query/actions.ts @@ -1,15 +1,21 @@ -import { toDataQueryError, getDataSourceSrv } from '@grafana/runtime'; +import { getDataSourceSrv, toDataQueryError } from '@grafana/runtime'; import { updateOptions } from '../state/actions'; import { QueryVariableModel } from '../types'; import { ThunkResult } from '../../../types'; import { getVariable } from '../state/selectors'; -import { addVariableEditorError, changeVariableEditorExtended, removeVariableEditorError } from '../editor/reducer'; +import { + addVariableEditorError, + changeVariableEditorExtended, + removeVariableEditorError, + VariableEditorState, +} from '../editor/reducer'; import { changeVariableProp } from '../state/sharedReducer'; import { toVariableIdentifier, toVariablePayload, VariableIdentifier } from '../state/types'; import { getVariableQueryEditor } from '../editor/getVariableQueryEditor'; import { Subscription } from 'rxjs'; import { getVariableQueryRunner } from './VariableQueryRunner'; import { variableQueryObserver } from './variableQueryObserver'; +import { QueryVariableEditorState } from './reducer'; export const updateQueryVariableOptions = ( identifier: VariableIdentifier, @@ -58,7 +64,12 @@ export const changeQueryVariableDataSource = ( ): ThunkResult => { return async (dispatch, getState) => { try { + const editorState = getState().templating.editor as VariableEditorState; + const previousDatasource = editorState.extended?.dataSource; const dataSource = await getDataSourceSrv().get(name ?? ''); + if (previousDatasource && previousDatasource.type !== dataSource?.type) { + dispatch(changeVariableProp(toVariablePayload(identifier, { propName: 'query', propValue: '' }))); + } dispatch(changeVariableEditorExtended({ propName: 'dataSource', propValue: dataSource })); const VariableQueryEditor = await getVariableQueryEditor(dataSource);