diff --git a/public/app/features/variables/pickers/OptionsPicker/reducer.test.ts b/public/app/features/variables/pickers/OptionsPicker/reducer.test.ts index 24bfeb094a9..2b6a26cd0ce 100644 --- a/public/app/features/variables/pickers/OptionsPicker/reducer.test.ts +++ b/public/app/features/variables/pickers/OptionsPicker/reducer.test.ts @@ -860,6 +860,47 @@ describe('optionsPickerReducer', () => { }); }); + describe('when searching non-latin chars', () => { + it('should skip fuzzy matching and fall back to substring', () => { + const searchQuery = '水'; + + const options: VariableOption[] = 'A水'.split(' ').map((v) => ({ + selected: false, + text: v, + value: v, + })); + + const expect: VariableOption[] = [ + { + selected: false, + text: '> ' + searchQuery, + value: searchQuery, + }, + ].concat( + 'A水'.split(' ').map((v) => ({ + selected: false, + text: v, + value: v, + })) + ); + + const { initialState } = getVariableTestContext({ + queryValue: searchQuery, + }); + + reducerTester() + .givenReducer(optionsPickerReducer, cloneDeep(initialState)) + .whenActionIsDispatched(updateOptionsAndFilter(options)) + .thenStateShouldEqual({ + ...cloneDeep(initialState), + options: expect, + selectedValues: [], + queryValue: searchQuery, + highlightIndex: 1, + }); + }); + }); + describe('when large data for updateOptionsFromSearch is dispatched and variable has searchFilter', () => { it('then state should be correct', () => { const searchQuery = '__searchFilter'; diff --git a/public/app/features/variables/pickers/OptionsPicker/reducer.ts b/public/app/features/variables/pickers/OptionsPicker/reducer.ts index 090517d800b..5525babc55b 100644 --- a/public/app/features/variables/pickers/OptionsPicker/reducer.ts +++ b/public/app/features/variables/pickers/OptionsPicker/reducer.ts @@ -8,6 +8,9 @@ import { applyStateChanges } from '../../../../core/utils/applyStateChanges'; import { ALL_VARIABLE_VALUE } from '../../constants'; import { isMulti, isQuery } from '../../guard'; +// https://catonmat.net/my-favorite-regex :) +const REGEXP_NON_ASCII = /[^ -~]/gm; + export interface ToggleOption { option?: VariableOption; forceSelect: boolean; @@ -251,6 +254,8 @@ const optionsPickerSlice = createSlice({ if (needle === '') { opts = action.payload; + } else if (REGEXP_NON_ASCII.test(needle)) { + opts = action.payload.filter((o) => o.text.includes(needle)); } else { // with current API, not seeing a way to cache this on state using action.payload's uniqueness // since it's recreated and includes selected state on each item :(