From 340334994fade86998a88a18f5b95c848dfcd32e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 29 Mar 2021 08:14:43 +0200 Subject: [PATCH] Variables: Fixes error when manually non-matching entering custom value in variable input/picker (#32390) --- .../pickers/OptionsPicker/reducer.test.ts | 23 +++++++++++++ .../pickers/OptionsPicker/reducer.ts | 33 +++++++++++-------- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/public/app/features/variables/pickers/OptionsPicker/reducer.test.ts b/public/app/features/variables/pickers/OptionsPicker/reducer.test.ts index 970a8e43d5c..d11e1baa1be 100644 --- a/public/app/features/variables/pickers/OptionsPicker/reducer.test.ts +++ b/public/app/features/variables/pickers/OptionsPicker/reducer.test.ts @@ -81,6 +81,28 @@ describe('optionsPickerReducer', () => { }); }; + describe('When toggleOption with undefined option is dispatched', () => { + it('should update selected values', () => { + const { initialState } = getVariableTestContext({ + options: [], + selectedValues: [], + }); + const payload = { + forceSelect: false, + clearOthers: true, + option: undefined, + }; + reducerTester() + .givenReducer(optionsPickerReducer, cloneDeep(initialState)) + .whenActionIsDispatched(toggleOption(payload)) + .thenStateShouldEqual({ + ...initialState, + selectedValues: [], + options: [], + }); + }); + }); + describe('toggleOption for multi value variable', () => { const multi = true; describe('and value All is selected in options', () => { @@ -112,6 +134,7 @@ describe('optionsPickerReducer', () => { }) ); }); + describe('and value A is selected in options', () => { const options = opsA; it.each` diff --git a/public/app/features/variables/pickers/OptionsPicker/reducer.ts b/public/app/features/variables/pickers/OptionsPicker/reducer.ts index 836e3b57928..464fa70b6cc 100644 --- a/public/app/features/variables/pickers/OptionsPicker/reducer.ts +++ b/public/app/features/variables/pickers/OptionsPicker/reducer.ts @@ -7,7 +7,7 @@ import { applyStateChanges } from '../../../../core/utils/applyStateChanges'; import { containsSearchFilter } from '../../utils'; export interface ToggleOption { - option: VariableOption; + option?: VariableOption; forceSelect: boolean; clearOthers: boolean; } @@ -148,23 +148,28 @@ const optionsPickerSlice = createSlice({ toggleOption: (state, action: PayloadAction): OptionsPickerState => { const { option, clearOthers, forceSelect } = action.payload; const { multi, selectedValues } = state; - const selected = !selectedValues.find((o) => o.value === option.value); - if (option.value === ALL_VARIABLE_VALUE || !multi || clearOthers) { - if (selected || forceSelect) { - state.selectedValues = [{ ...option, selected: true }]; - } else { - state.selectedValues = []; + if (option) { + const selected = !selectedValues.find((o) => o.value === option.value); + + if (option.value === ALL_VARIABLE_VALUE || !multi || clearOthers) { + if (selected || forceSelect) { + state.selectedValues = [{ ...option, selected: true }]; + } else { + state.selectedValues = []; + } + return applyStateChanges(state, updateDefaultSelection, updateAllSelection, updateOptions); } - return applyStateChanges(state, updateDefaultSelection, updateAllSelection, updateOptions); + if (forceSelect || selected) { + state.selectedValues.push({ ...option, selected: true }); + return applyStateChanges(state, updateDefaultSelection, updateAllSelection, updateOptions); + } + + state.selectedValues = selectedValues.filter((o) => o.value !== option.value); + } else { + state.selectedValues = []; } - if (forceSelect || selected) { - state.selectedValues.push({ ...option, selected: true }); - return applyStateChanges(state, updateDefaultSelection, updateAllSelection, updateOptions); - } - - state.selectedValues = selectedValues.filter((o) => o.value !== option.value); return applyStateChanges(state, updateDefaultSelection, updateAllSelection, updateOptions); }, toggleTag: (state, action: PayloadAction): OptionsPickerState => {