diff --git a/x-pack/plugins/global_search_bar/public/components/search_bar.tsx b/x-pack/plugins/global_search_bar/public/components/search_bar.tsx index de9bb85f7a8a3..318b5cf2e4526 100644 --- a/x-pack/plugins/global_search_bar/public/components/search_bar.tsx +++ b/x-pack/plugins/global_search_bar/public/components/search_bar.tsx @@ -39,10 +39,6 @@ import { PopoverPlaceholder } from './popover_placeholder'; import './search_bar.scss'; import { SearchBarProps } from './types'; -const NoMatchesMessage = (props: { basePathUrl: string }) => { - return ; -}; - const SearchCharLimitExceededMessage = (props: { basePathUrl: string }) => { const charLimitMessage = ( <> @@ -90,17 +86,17 @@ export const SearchBar: FC = (opts) => { // General hooks const [initialLoad, setInitialLoad] = useState(false); const [searchValue, setSearchValue] = useState(''); - const [searchTerm, setSearchTerm] = useState(''); const [searchRef, setSearchRef] = useState(null); const [buttonRef, setButtonRef] = useState(null); const searchSubscription = useRef(null); - const [options, _setOptions] = useState([]); + const [options, setOptions] = useState([]); const [searchableTypes, setSearchableTypes] = useState([]); const [showAppend, setShowAppend] = useState(true); const UNKNOWN_TAG_ID = '__unknown__'; const [isLoading, setIsLoading] = useState(false); const [searchCharLimitExceeded, setSearchCharLimitExceeded] = useState(false); + // Initialize searchableTypes data useEffect(() => { if (initialLoad) { const fetch = async () => { @@ -111,6 +107,11 @@ export const SearchBar: FC = (opts) => { } }, [globalSearch, initialLoad]); + // Whenever searchValue changes, isLoading = true + useEffect(() => { + setIsLoading(true); + }, [searchValue]); + const loadSuggestions = useCallback( (term: string) => { return getSuggestions({ @@ -122,17 +123,13 @@ export const SearchBar: FC = (opts) => { [taggingApi, searchableTypes] ); - const setOptions = useCallback( + const setDecoratedOptions = useCallback( ( _options: GlobalSearchResult[], suggestions: SearchSuggestion[], searchTagIds: string[] = [] ) => { - if (!isMounted()) { - return; - } - - _setOptions([ + setOptions([ ...suggestions.map(suggestionToOption), ..._options.map((option) => resultToOption( @@ -143,7 +140,7 @@ export const SearchBar: FC = (opts) => { ), ]); }, - [isMounted, _setOptions, taggingApi] + [setOptions, taggingApi] ); useDebounce( @@ -163,9 +160,7 @@ export const SearchBar: FC = (opts) => { setSearchCharLimitExceeded(false); } - setIsLoading(true); const suggestions = loadSuggestions(searchValue.toLowerCase()); - setIsLoading(false); let aggregatedResults: GlobalSearchResult[] = []; @@ -187,26 +182,23 @@ export const SearchBar: FC = (opts) => { types: rawParams.filters.types, tags: tagIds, }; - // TODO technically a subtle bug here - // this term won't be set until the next time the debounce is fired - // so the SearchOption won't highlight anything if only one call is fired - // in practice, this is hard to spot, unlikely to happen, and is a negligible issue - setSearchTerm(rawParams.term ?? ''); - setIsLoading(true); + searchSubscription.current = globalSearch.find(searchParams, {}).subscribe({ next: ({ results }) => { + if (!isMounted()) { + return; + } + if (searchValue.length > 0) { aggregatedResults = [...results, ...aggregatedResults].sort(sort.byScore); - setOptions(aggregatedResults, suggestions, searchParams.tags); + setDecoratedOptions(aggregatedResults, suggestions, searchParams.tags); return; } // if searchbar is empty, filter to only applications and sort alphabetically results = results.filter(({ type }: GlobalSearchResult) => type === 'application'); - aggregatedResults = [...results, ...aggregatedResults].sort(sort.byTitle); - - setOptions(aggregatedResults, suggestions, searchParams.tags); + setDecoratedOptions(aggregatedResults, suggestions, searchParams.tags); }, error: (err) => { setIsLoading(false); @@ -370,7 +362,7 @@ export const SearchBar: FC = (opts) => { className="kbnSearchBar" popoverButtonBreakpoints={['xs', 's']} singleSelection={true} - renderOption={(option) => euiSelectableTemplateSitewideRenderOptions(option, searchTerm)} + renderOption={(option) => euiSelectableTemplateSitewideRenderOptions(option, searchValue)} listProps={{ className: 'eui-yScroll', css: css` @@ -400,7 +392,7 @@ export const SearchBar: FC = (opts) => { }} errorMessage={searchCharLimitExceeded ? : null} emptyMessage={} - noMatchesMessage={} + noMatchesMessage={} popoverProps={{ 'data-test-subj': 'nav-search-popover', panelClassName: 'navSearch__panel', diff --git a/x-pack/plugins/global_search_bar/public/telemetry/telemetry.test.tsx b/x-pack/plugins/global_search_bar/public/telemetry/telemetry.test.tsx index 2137679fcf5c3..e4302c1e64aec 100644 --- a/x-pack/plugins/global_search_bar/public/telemetry/telemetry.test.tsx +++ b/x-pack/plugins/global_search_bar/public/telemetry/telemetry.test.tsx @@ -194,9 +194,7 @@ describe('SearchBar', () => { }); it(`tracks the user's search term`, async () => { - searchService.find.mockReturnValueOnce( - of(createBatch('Discover', { id: 'My Dashboard', type: 'test' })) - ); + searchService.find.mockReturnValue(of(createBatch('Discover'))); render(