Variables: Fixes loading with a custom all value in url (#28958)

This commit is contained in:
Hugo Häggmark
2020-11-11 06:19:09 +01:00
committed by GitHub
parent b2dcf06b60
commit 8f4e50f439
4 changed files with 171 additions and 43 deletions

View File

@ -10,4 +10,8 @@ export class MultiVariableBuilder<T extends VariableWithMultiSupport> extends Op
this.variable.includeAll = includeAll;
return this;
}
withAllValue(allValue: string) {
this.variable.allValue = allValue;
return this;
}
}

View File

@ -16,7 +16,6 @@ import {
initDashboardTemplating,
initVariablesTransaction,
processVariables,
setOptionFromUrl,
validateVariableSelectionState,
} from './actions';
import {
@ -203,7 +202,6 @@ describe('shared actions', () => {
const query = { orgId: '1', 'var-stats': 'response', 'var-substats': ALL_VARIABLE_TEXT };
const tester = await reduxTester<{ templating: TemplatingState; location: { query: UrlQueryMap } }>({
preloadedState: { templating: ({} as unknown) as TemplatingState, location: { query } },
debug: true,
})
.givenRootReducer(getTemplatingAndLocationRootReducer())
.whenActionIsDispatched(variablesInitTransaction({ uid: '' }))
@ -241,46 +239,6 @@ describe('shared actions', () => {
});
});
describe('when setOptionFromUrl is dispatched with a custom variable (no refresh property)', () => {
it.each`
urlValue | isMulti | expected
${'B'} | ${false} | ${'B'}
${['B']} | ${false} | ${'B'}
${'X'} | ${false} | ${'X'}
${''} | ${false} | ${''}
${null} | ${false} | ${null}
${undefined} | ${false} | ${undefined}
${'B'} | ${true} | ${['B']}
${['B']} | ${true} | ${['B']}
${'X'} | ${true} | ${['X']}
${''} | ${true} | ${['']}
${['A', 'B']} | ${true} | ${['A', 'B']}
${null} | ${true} | ${[null]}
${undefined} | ${true} | ${[undefined]}
`('and urlValue is $urlValue then correct actions are dispatched', async ({ urlValue, expected, isMulti }) => {
const custom = customBuilder()
.withId('0')
.withMulti(isMulti)
.withOptions('A', 'B', 'C')
.withCurrent('A')
.build();
const tester = await reduxTester<{ templating: TemplatingState }>()
.givenRootReducer(getTemplatingRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(custom, { global: false, index: 0, model: custom })))
.whenAsyncActionIsDispatched(setOptionFromUrl(toVariableIdentifier(custom), urlValue), true);
await tester.thenDispatchedActionsShouldEqual(
setCurrentVariableValue(
toVariablePayload(
{ type: 'custom', id: '0' },
{ option: { text: expected, value: expected, selected: false } }
)
)
);
});
});
describe('when validateVariableSelectionState is dispatched with a custom variable (no dependencies)', () => {
describe('and not multivalue', () => {
it.each`

View File

@ -29,7 +29,13 @@ import {
variableStateFetching,
variableStateNotStarted,
} from './sharedReducer';
import { toVariableIdentifier, toVariablePayload, VariableIdentifier } from './types';
import {
ALL_VARIABLE_TEXT,
ALL_VARIABLE_VALUE,
toVariableIdentifier,
toVariablePayload,
VariableIdentifier,
} from './types';
import { contextSrv } from 'app/core/services/context_srv';
import { getTemplateSrv, TemplateSrv } from '../../templating/template_srv';
import { alignCurrentWithMulti } from '../shared/multiOptions';
@ -287,6 +293,12 @@ export const setOptionFromUrl = (
return op.text === urlValue || op.value === urlValue;
});
if (!option && isMulti(variableFromState)) {
if (variableFromState.allValue && urlValue === variableFromState.allValue) {
option = { text: ALL_VARIABLE_TEXT, value: ALL_VARIABLE_VALUE, selected: false };
}
}
if (!option) {
let defaultText = urlValue as string | string[];
const defaultValue = urlValue as string | string[];

View File

@ -0,0 +1,154 @@
import { variableAdapters } from '../adapters';
import { createCustomVariableAdapter } from '../custom/adapter';
import { customBuilder } from '../shared/testing/builders';
import { reduxTester } from '../../../../test/core/redux/reduxTester';
import { TemplatingState } from './reducers';
import { getTemplatingRootReducer } from './helpers';
import { addVariable, setCurrentVariableValue } from './sharedReducer';
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, toVariableIdentifier, toVariablePayload } from './types';
import { setOptionFromUrl } from './actions';
variableAdapters.setInit(() => [createCustomVariableAdapter()]);
describe('when setOptionFromUrl is dispatched with a custom variable (no refresh property)', () => {
it.each`
urlValue | isMulti | expected
${'B'} | ${false} | ${'B'}
${['B']} | ${false} | ${'B'}
${'X'} | ${false} | ${'X'}
${''} | ${false} | ${''}
${null} | ${false} | ${null}
${undefined} | ${false} | ${undefined}
${'B'} | ${true} | ${['B']}
${['B']} | ${true} | ${['B']}
${'X'} | ${true} | ${['X']}
${''} | ${true} | ${['']}
${['A', 'B']} | ${true} | ${['A', 'B']}
${null} | ${true} | ${[null]}
${undefined} | ${true} | ${[undefined]}
`('and urlValue is $urlValue then correct actions are dispatched', async ({ urlValue, expected, isMulti }) => {
const custom = customBuilder()
.withId('0')
.withMulti(isMulti)
.withOptions('A', 'B', 'C')
.withCurrent('A')
.build();
const tester = await reduxTester<{ templating: TemplatingState }>()
.givenRootReducer(getTemplatingRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(custom, { global: false, index: 0, model: custom })))
.whenAsyncActionIsDispatched(setOptionFromUrl(toVariableIdentifier(custom), urlValue), true);
await tester.thenDispatchedActionsShouldEqual(
setCurrentVariableValue(
toVariablePayload({ type: 'custom', id: '0' }, { option: { text: expected, value: expected, selected: false } })
)
);
});
});
describe('when setOptionFromUrl is dispatched for a variable with a custom all value', () => {
it('and urlValue contains same all value then correct actions are dispatched', async () => {
const allValue = '.*';
const urlValue = allValue;
const custom = customBuilder()
.withId('0')
.withMulti(false)
.withIncludeAll()
.withAllValue(allValue)
.withOptions('A', 'B', 'C')
.withCurrent('A')
.build();
const tester = await reduxTester<{ templating: TemplatingState }>()
.givenRootReducer(getTemplatingRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(custom, { global: false, index: 0, model: custom })))
.whenAsyncActionIsDispatched(setOptionFromUrl(toVariableIdentifier(custom), urlValue), true);
await tester.thenDispatchedActionsShouldEqual(
setCurrentVariableValue(
toVariablePayload(
{ type: 'custom', id: '0' },
{ option: { text: ALL_VARIABLE_TEXT, value: ALL_VARIABLE_VALUE, selected: false } }
)
)
);
});
it('and urlValue differs from all value then correct actions are dispatched', async () => {
const allValue = '.*';
const urlValue = 'X';
const custom = customBuilder()
.withId('0')
.withMulti(false)
.withIncludeAll()
.withAllValue(allValue)
.withOptions('A', 'B', 'C')
.withCurrent('A')
.build();
const tester = await reduxTester<{ templating: TemplatingState }>()
.givenRootReducer(getTemplatingRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(custom, { global: false, index: 0, model: custom })))
.whenAsyncActionIsDispatched(setOptionFromUrl(toVariableIdentifier(custom), urlValue), true);
await tester.thenDispatchedActionsShouldEqual(
setCurrentVariableValue(
toVariablePayload({ type: 'custom', id: '0' }, { option: { text: 'X', value: 'X', selected: false } })
)
);
});
it('and urlValue differs but matches an option then correct actions are dispatched', async () => {
const allValue = '.*';
const urlValue = 'B';
const custom = customBuilder()
.withId('0')
.withMulti(false)
.withIncludeAll()
.withAllValue(allValue)
.withOptions('A', 'B', 'C')
.withCurrent('A')
.build();
const tester = await reduxTester<{ templating: TemplatingState }>()
.givenRootReducer(getTemplatingRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(custom, { global: false, index: 0, model: custom })))
.whenAsyncActionIsDispatched(setOptionFromUrl(toVariableIdentifier(custom), urlValue), true);
await tester.thenDispatchedActionsShouldEqual(
setCurrentVariableValue(
toVariablePayload({ type: 'custom', id: '0' }, { option: { text: 'B', value: 'B', selected: false } })
)
);
});
it('and custom all value matches an option', async () => {
const allValue = '.*';
const urlValue = allValue;
const custom = customBuilder()
.withId('0')
.withMulti(false)
.withIncludeAll()
.withAllValue(allValue)
.withOptions('A', 'B', '.*')
.withCurrent('A')
.build();
custom.options[2].value = 'special value for .*';
const tester = await reduxTester<{ templating: TemplatingState }>()
.givenRootReducer(getTemplatingRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(custom, { global: false, index: 0, model: custom })))
.whenAsyncActionIsDispatched(setOptionFromUrl(toVariableIdentifier(custom), urlValue), true);
await tester.thenDispatchedActionsShouldEqual(
setCurrentVariableValue(
toVariablePayload(
{ type: 'custom', id: '0' },
{ option: { text: '.*', value: 'special value for .*', selected: false } }
)
)
);
});
});