From 897e7209c8dab25e549938673df51ab8412a86b1 Mon Sep 17 00:00:00 2001 From: Oliver Shi Date: Thu, 14 Apr 2022 14:12:53 -0400 Subject: [PATCH] wrap DropdownItem itemData usages with useMemo (#114) This commit updates DropdownItem itemData usages to be wrapped inside a useMemo. Originally, I tried refactoring Dropdown to not use itemData, since itemData feels a bit like an anti pattern, however this proved to be very difficult. I also tried wrapping DropdownItem usages inside a parent component, like FilterSearchDropdownItem, however recursivelyMapChildren does not work for wrapper components that internally use DropdownItem. It only works when DropdownItem is specified as a direct child. So, as a workaround, I wrapped itemData usages inside useMemo by creating a 2d matrix of item datas, itemDataMatrix, which is then referenced by component usages. J=SLAP-2016 TEST=manual see that autocomplete options appear and can be selected see that filtersearch results appear and can be selected and applied to the search resolved all eslint warnings. yay! --- .eslintrc.js | 2 +- src/components/FilterSearch.tsx | 43 +++++++++++++++++++++------------ src/components/SearchBar.tsx | 11 ++++++++- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 5899423f4..6f9b4fd4c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,7 +5,7 @@ module.exports = { ignorePatterns: ['lib', 'tests/setup/responses', 'storybook-static', '!.storybook'], overrides: [ { - files: ['**/*.test.*'], + files: ['**/*.{test,stories}.*'], rules: { 'react-perf/jsx-no-new-array-as-prop': 'off', 'react-perf/jsx-no-new-function-as-prop': 'off', diff --git a/src/components/FilterSearch.tsx b/src/components/FilterSearch.tsx index 0789fb92f..60d518e18 100644 --- a/src/components/FilterSearch.tsx +++ b/src/components/FilterSearch.tsx @@ -1,5 +1,5 @@ import { AutocompleteResult, Filter, FilterSearchResponse, SearchParameterField, useAnswersActions } from '@yext/answers-headless-react'; -import { useCallback } from 'react'; +import { useCallback, useMemo } from 'react'; import { CompositionMethod, useComposedCssClasses } from '../hooks/useComposedCssClasses'; import { useSynchronizedRequest } from '../hooks/useSynchronizedRequest'; import { executeSearch } from '../utils'; @@ -84,7 +84,10 @@ export function FilterSearch({ inputValue => answersActions.executeFilterSearch(inputValue ?? '', sectioned, searchParamFields), (e) => console.error('Error occured executing a filter search request.\n', e) ); - const sections = filterSearchResponse?.sections.filter(section => section.results.length > 0) ?? []; + const sections = useMemo(() => { + return filterSearchResponse?.sections.filter(section => section.results.length > 0) ?? []; + }, [filterSearchResponse?.sections]); + const hasResults = sections.flatMap(s => s.results).length > 0; const handleSelectDropdown = useCallback((_value, _index, itemData) => { @@ -100,6 +103,15 @@ export function FilterSearch({ const handleChangeDropdownInput = useCallback(query => executeFilterSearch(query), [executeFilterSearch]); const meetsSubmitCritera = useCallback(index => index >= 0, []); + const itemDataMatrix = useMemo(() => { + return sections.map(section => { + return section.results.map(result => ({ + filter: result.filter, + displayName: result.value + })); + }); + }, [sections]); + function renderDropdownItems() { return sections.map((section, sectionIndex) => { return ( @@ -115,7 +127,7 @@ export function FilterSearch({ key={index} focusedClassName={cssClasses.focusedOption} value={result.value} - itemData={{ filter: result.filter, displayName: result.value }} + itemData={itemDataMatrix[sectionIndex][index]} > {renderAutocompleteResult(result, cssClasses)} @@ -162,18 +174,19 @@ function getScreenReaderText(sections: { pluralForm: '0 autocomplete options found.', count: 0 }); - if (sections.length > 0) { - const screenReaderPhrases = sections.map(section => { - const optionInfo = section.label - ? `${section.results.length} ${section.label}` - : `${section.results.length}`; - return processTranslation({ - phrase: `${optionInfo} autocomplete option found.`, - pluralForm: `${optionInfo} autocomplete options found.`, - count: section.results.length - }); - }); - screenReaderText = screenReaderPhrases.join(' '); + if (sections.length === 0) { + return screenReaderText; } + const screenReaderPhrases = sections.map(section => { + const optionInfo = section.label + ? `${section.results.length} ${section.label}` + : `${section.results.length}`; + return processTranslation({ + phrase: `${optionInfo} autocomplete option found.`, + pluralForm: `${optionInfo} autocomplete options found.`, + count: section.results.length + }); + }); + screenReaderText = screenReaderPhrases.join(' '); return screenReaderText; } diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx index 4fd6148ab..f999a9c82 100644 --- a/src/components/SearchBar.tsx +++ b/src/components/SearchBar.tsx @@ -41,6 +41,7 @@ import { useSearchBarAnalytics } from '../hooks/useSearchBarAnalytics'; import { isVerticalLink, VerticalLink } from '../models/verticalLink'; import { executeAutocomplete as executeAutocompleteSearch } from '../utils/search-operations'; import { clearStaticRangeFilters } from '../utils/filterutils'; +import { useMemo } from 'react'; const builtInCssClasses: SearchBarCssClasses = { container: 'h-12 mb-6', @@ -312,6 +313,14 @@ export function SearchBar({ )); } + const itemDataMatrix = useMemo(() => { + return autocompleteResponse?.results.map(result => { + return result.verticalKeys?.map(verticalKey => ({ + verticalLink: { verticalKey, query: result.value } + })) ?? []; + }) ?? []; + }, [autocompleteResponse?.results]); + function renderQuerySuggestions() { return autocompleteResponse?.results.map((result, i) => ( @@ -334,7 +343,7 @@ export function SearchBar({ className={cssClasses.optionContainer} focusedClassName={classNames(cssClasses.optionContainer, cssClasses.focusedOption)} value={result.value} - itemData={{ verticalLink: { verticalKey, query: result.value } }} + itemData={itemDataMatrix[i][j]} onClick={handleSubmit} > {renderAutocompleteResult(