From deae04bf8bba9625f2f3799af42e7bcb9693521b Mon Sep 17 00:00:00 2001 From: Leon Sorokin Date: Mon, 30 Dec 2024 17:35:08 -0600 Subject: [PATCH 1/2] AdHocFilters: Fix matching non-latin template vars in filter --- .../AdHocFiltersCombobox.tsx | 4 ++-- .../adhoc/AdHocFiltersCombobox/utils.ts | 10 +++++++- packages/scenes/src/variables/utils.test.ts | 23 +++++++++++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/AdHocFiltersCombobox.tsx b/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/AdHocFiltersCombobox.tsx index 9e73e6b7a..db22ebd9c 100644 --- a/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/AdHocFiltersCombobox.tsx +++ b/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/AdHocFiltersCombobox.tsx @@ -25,7 +25,7 @@ import { import { ERROR_STATE_DROPDOWN_WIDTH, flattenOptionGroups, - fuzzySearchOptions, + searchOptions, generateFilterUpdatePayload, generatePlaceholder, populateInputValueOnInputTypeSwitch, @@ -81,7 +81,7 @@ export const AdHocCombobox = forwardRef(function AdHocCombobox( const disabledIndicesRef = useRef([]); const filterInputTypeRef = useRef(!isAlwaysWip ? 'value' : 'key'); - const optionsSearcher = useMemo(() => fuzzySearchOptions(options), [options]); + const optionsSearcher = useMemo(() => searchOptions(options), [options]); const isLastFilter = useMemo(() => { if (isAlwaysWip) { diff --git a/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/utils.ts b/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/utils.ts index 78f0078d9..643a45843 100644 --- a/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/utils.ts +++ b/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/utils.ts @@ -11,11 +11,19 @@ export const VIRTUAL_LIST_ITEM_HEIGHT = 38; export const VIRTUAL_LIST_ITEM_HEIGHT_WITH_DESCRIPTION = 60; export const ERROR_STATE_DROPDOWN_WIDTH = 366; -export function fuzzySearchOptions(options: Array>) { +// https://catonmat.net/my-favorite-regex :) +const REGEXP_NON_ASCII = /[^ -~]/m; + +export function searchOptions(options: Array>) { const haystack = options.map((o) => o.label ?? o.value!); const fuzzySearch = getFuzzySearcher(haystack); return (search: string, filterInputType: AdHocInputType) => { + // fall back to substring matching for non-latin chars + if (REGEXP_NON_ASCII.test(search)) { + return options.filter((o) => o.label?.includes(search) || o.value?.includes(search) || false); + } + if (filterInputType === 'operator' && search !== '') { // uFuzzy can only match non-alphanum chars if quoted search = `"${search}"`; diff --git a/packages/scenes/src/variables/utils.test.ts b/packages/scenes/src/variables/utils.test.ts index dab074e39..359e468dd 100644 --- a/packages/scenes/src/variables/utils.test.ts +++ b/packages/scenes/src/variables/utils.test.ts @@ -8,6 +8,8 @@ import { getFuzzySearcher, getQueriesForVariables } from './utils'; import { SceneVariableSet } from './sets/SceneVariableSet'; import { DataSourceVariable } from './variants/DataSourceVariable'; import { GetDataSourceListFilters } from '@grafana/runtime'; +import { searchOptions } from './adhoc/AdHocFiltersCombobox/utils'; +import { SelectableValue } from '@grafana/data'; describe('getQueriesForVariables', () => { it('should resolve queries', () => { @@ -289,6 +291,27 @@ describe('getFuzzySearcher orders by match quality with case-sensitivity', () => }); }); +describe('searchOptions falls back to substring matching for non-latin needles', () => { + it('Can filter options by search query', async () => { + const options: SelectableValue[] = [ + '台灣省', + '台中市', + '台北市', + '台南市', + '南投縣', + '高雄市', + '台中第一高級中學', + ].map(v => ({label: v, value: v})); + + const searcher = searchOptions(options); + + expect(searcher('南', 'key').map((o) => o.label!)).toEqual([ + '台南市', + '南投縣', + ]); + }); +}); + interface TestObjectState extends SceneObjectState { datasource: DataSourceRef | null; } From 76af88dea223342b9fb94d50813666ed36b12dbb Mon Sep 17 00:00:00 2001 From: Leon Sorokin Date: Tue, 31 Dec 2024 06:54:12 -0600 Subject: [PATCH 2/2] uglier --- packages/scenes/src/variables/utils.test.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/scenes/src/variables/utils.test.ts b/packages/scenes/src/variables/utils.test.ts index 359e468dd..e6566d6e8 100644 --- a/packages/scenes/src/variables/utils.test.ts +++ b/packages/scenes/src/variables/utils.test.ts @@ -301,14 +301,11 @@ describe('searchOptions falls back to substring matching for non-latin needles', '南投縣', '高雄市', '台中第一高級中學', - ].map(v => ({label: v, value: v})); + ].map((v) => ({ label: v, value: v })); const searcher = searchOptions(options); - expect(searcher('南', 'key').map((o) => o.label!)).toEqual([ - '台南市', - '南投縣', - ]); + expect(searcher('南', 'key').map((o) => o.label!)).toEqual(['台南市', '南投縣']); }); });