From 622246d56de65978acf6d0a68eacd21fabfd2646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Thu, 14 May 2020 07:41:42 +0200 Subject: [PATCH] Templating: fixes so Textbox variables get value from url (#24623) --- .../variables/textbox/actions.test.ts | 63 ++++++++++--------- .../app/features/variables/textbox/actions.ts | 17 ++++- .../app/features/variables/textbox/adapter.ts | 6 +- .../app/features/variables/textbox/reducer.ts | 7 +-- 4 files changed, 57 insertions(+), 36 deletions(-) diff --git a/public/app/features/variables/textbox/actions.test.ts b/public/app/features/variables/textbox/actions.test.ts index bfa9f8a76d6..7caecd3f24d 100644 --- a/public/app/features/variables/textbox/actions.test.ts +++ b/public/app/features/variables/textbox/actions.test.ts @@ -2,13 +2,14 @@ import { variableAdapters } from '../adapters'; import { createTextBoxVariableAdapter } from './adapter'; import { reduxTester } from '../../../../test/core/redux/reduxTester'; import { TemplatingState } from 'app/features/variables/state/reducers'; -import { updateTextBoxVariableOptions } from './actions'; +import { setTextBoxVariableOptionsFromUrl, updateTextBoxVariableOptions } from './actions'; import { getRootReducer } from '../state/helpers'; -import { TextBoxVariableModel, VariableHide, VariableOption } from '../../templating/types'; +import { VariableOption } from '../../templating/types'; import { toVariablePayload } from '../state/types'; import { createTextBoxOptions } from './reducer'; -import { setCurrentVariableValue, addVariable } from '../state/sharedReducer'; +import { addVariable, changeVariableProp, setCurrentVariableValue } from '../state/sharedReducer'; import { updateLocation } from 'app/core/actions'; +import { textboxBuilder } from '../shared/testing/builders'; describe('textbox actions', () => { variableAdapters.setInit(() => [createTextBoxVariableAdapter()]); @@ -21,39 +22,45 @@ describe('textbox actions', () => { selected: false, }; - const variable: TextBoxVariableModel = { - type: 'textbox', - id: '0', - global: false, - current: { - value: '', - text: '', - selected: false, - }, - options: [], - query: 'A', - name: 'textbox', - label: '', - hide: VariableHide.dontHide, - skipUrlSync: false, - index: 0, - }; + const variable = textboxBuilder() + .withId('textbox') + .withName('textbox') + .withCurrent('A') + .withQuery('A') + .build(); const tester = await reduxTester<{ templating: TemplatingState }>() .givenRootReducer(getRootReducer()) .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable }))) .whenAsyncActionIsDispatched(updateTextBoxVariableOptions(toVariablePayload(variable)), true); - tester.thenDispatchedActionsPredicateShouldEqual(actions => { - const [createAction, setCurrentAction, locationAction] = actions; - const expectedNumberOfActions = 3; + tester.thenDispatchedActionsShouldEqual( + createTextBoxOptions(toVariablePayload(variable)), + setCurrentVariableValue(toVariablePayload(variable, { option })), + updateLocation({ query: { 'var-textbox': 'A' } }) + ); + }); + }); - expect(createAction).toEqual(createTextBoxOptions(toVariablePayload(variable))); - expect(setCurrentAction).toEqual(setCurrentVariableValue(toVariablePayload(variable, { option }))); - expect(locationAction).toEqual(updateLocation({ query: { 'var-textbox': 'A' } })); + describe('when setTextBoxVariableOptionsFromUrl is dispatched', () => { + it('then correct actions are dispatched', async () => { + const urlValue = 'bB'; + const variable = textboxBuilder() + .withId('textbox') + .withName('textbox') + .withCurrent('A') + .withQuery('A') + .build(); - return actions.length === expectedNumberOfActions; - }); + const tester = await reduxTester<{ templating: TemplatingState }>() + .givenRootReducer(getRootReducer()) + .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable }))) + .whenAsyncActionIsDispatched(setTextBoxVariableOptionsFromUrl(toVariablePayload(variable), urlValue), true); + + tester.thenDispatchedActionsShouldEqual( + changeVariableProp(toVariablePayload(variable, { propName: 'query', propValue: 'bB' })), + setCurrentVariableValue(toVariablePayload(variable, { option: { text: 'bB', value: 'bB', selected: false } })) + ); }); }); }); diff --git a/public/app/features/variables/textbox/actions.ts b/public/app/features/variables/textbox/actions.ts index 928f54f5ed3..94b5106d3e1 100644 --- a/public/app/features/variables/textbox/actions.ts +++ b/public/app/features/variables/textbox/actions.ts @@ -3,12 +3,27 @@ import { ThunkResult } from '../../../types'; import { getVariable } from '../state/selectors'; import { variableAdapters } from '../adapters'; import { createTextBoxOptions } from './reducer'; -import { toVariablePayload, VariableIdentifier } from '../state/types'; +import { toVariableIdentifier, toVariablePayload, VariableIdentifier } from '../state/types'; +import { setOptionFromUrl } from '../state/actions'; +import { UrlQueryValue } from '@grafana/data'; +import { changeVariableProp } from '../state/sharedReducer'; export const updateTextBoxVariableOptions = (identifier: VariableIdentifier): ThunkResult => { return async (dispatch, getState) => { await dispatch(createTextBoxOptions(toVariablePayload(identifier))); + const variableInState = getVariable(identifier.id!, getState()); await variableAdapters.get(identifier.type).setValue(variableInState, variableInState.options[0], true); }; }; + +export const setTextBoxVariableOptionsFromUrl = ( + identifier: VariableIdentifier, + urlValue: UrlQueryValue +): ThunkResult => async (dispatch, getState) => { + const variableInState = getVariable(identifier.id!, getState()); + + dispatch(changeVariableProp(toVariablePayload(variableInState, { propName: 'query', propValue: urlValue }))); + + await dispatch(setOptionFromUrl(toVariableIdentifier(variableInState), urlValue)); +}; diff --git a/public/app/features/variables/textbox/adapter.ts b/public/app/features/variables/textbox/adapter.ts index 2a16fdd8ada..f354e0f4ca5 100644 --- a/public/app/features/variables/textbox/adapter.ts +++ b/public/app/features/variables/textbox/adapter.ts @@ -3,11 +3,11 @@ import cloneDeep from 'lodash/cloneDeep'; import { TextBoxVariableModel } from '../../templating/types'; import { initialTextBoxVariableModelState, textBoxVariableReducer } from './reducer'; import { dispatch } from '../../../store/store'; -import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions'; +import { setOptionAsCurrent } from '../state/actions'; import { VariableAdapter } from '../adapters'; import { TextBoxVariablePicker } from './TextBoxVariablePicker'; import { TextBoxVariableEditor } from './TextBoxVariableEditor'; -import { updateTextBoxVariableOptions } from './actions'; +import { setTextBoxVariableOptionsFromUrl, updateTextBoxVariableOptions } from './actions'; import { toVariableIdentifier } from '../state/types'; export const createTextBoxVariableAdapter = (): VariableAdapter => { @@ -26,7 +26,7 @@ export const createTextBoxVariableAdapter = (): VariableAdapter { - await dispatch(setOptionFromUrl(toVariableIdentifier(variable), urlValue)); + await dispatch(setTextBoxVariableOptionsFromUrl(toVariableIdentifier(variable), urlValue)); }, updateOptions: async variable => { await dispatch(updateTextBoxVariableOptions(toVariableIdentifier(variable))); diff --git a/public/app/features/variables/textbox/reducer.ts b/public/app/features/variables/textbox/reducer.ts index d3c19ab775e..7ced3babfe8 100644 --- a/public/app/features/variables/textbox/reducer.ts +++ b/public/app/features/variables/textbox/reducer.ts @@ -25,10 +25,9 @@ export const textBoxVariableSlice = createSlice({ reducers: { createTextBoxOptions: (state: VariablesState, action: PayloadAction) => { const instanceState = getInstanceState(state, action.payload.id!); - instanceState.options = [ - { text: instanceState.query.trim(), value: instanceState.query.trim(), selected: false }, - ]; - instanceState.current = instanceState.options[0]; + const option = { text: instanceState.query.trim(), value: instanceState.query.trim(), selected: false }; + instanceState.options = [option]; + instanceState.current = option; }, }, });