diff --git a/webpack/assets/javascripts/react_app/components/Layout/NavigationSearch.js b/webpack/assets/javascripts/react_app/components/Layout/NavigationSearch.js index 451a0bc6718..322f7fb0a64 100644 --- a/webpack/assets/javascripts/react_app/components/Layout/NavigationSearch.js +++ b/webpack/assets/javascripts/react_app/components/Layout/NavigationSearch.js @@ -11,42 +11,36 @@ import { import { translate as __ } from '../../common/I18n'; export const NavigationSearch = ({ items, clickAndNavigate }) => { - const navLinks = {}; let parent = null; + const navLinksArray = []; items.forEach(item => { item.subItems.forEach(group => { if (group.isDivider) { parent = group.title; } else { - navLinks[group.title] = { + navLinksArray.push({ ...group, parents: [item.title, parent].filter(Boolean), - }; + }); } }); parent = null; }); - const navItems = Object.keys(navLinks); - const menuNav = navItem => ( + const navItems = navLinksArray.map(item => item.title); + const menuNav = ({ id, title, href, onClick, parents }, key) => ( - clickAndNavigate( - navLinks[navItem].onClick, - navLinks[navItem].href, - event - ) - } - itemId={navItem} - key={navItem} - description={[...navLinks[navItem].parents, navItem].join(' > ')} + to={href} + onClick={event => clickAndNavigate(onClick, href, event)} + itemId={`${id}_${key}`} + key={`${id}_${key}`} + description={[...parents, title].join(' > ')} > - {navItem} + {title} ); const [autocompleteOptions, setAutocompleteOptions] = useState( - navItems.slice(0, 10).map(menuNav) + navLinksArray.slice(0, 10).map(menuNav) ); const [value, setValue] = useState(''); @@ -57,29 +51,27 @@ export const NavigationSearch = ({ items, clickAndNavigate }) => { const onClear = () => { setValue(''); + setAutocompleteOptions(navLinksArray.slice(0, 10).map(menuNav)); }; const onChange = newValue => { + // When the value of the search input changes, build a list of no more than 10 autocomplete options. + let options = navLinksArray + .filter(({ title }) => + title.toLowerCase().includes(newValue.toLowerCase()) + ) + .map(menuNav); + if (options.length > 10) { + options = options.slice(0, 10); + } + + setAutocompleteOptions(options); if ( newValue !== '' && searchInputRef?.current?.contains(document.activeElement) ) { - setIsAutocompleteOpen(true); - - // When the value of the search input changes, build a list of no more than 10 autocomplete options. - // Options which start with the search input value are listed first, followed by options which contain - // the search input value. - let options = navItems - .filter(option => option.toLowerCase().includes(newValue.toLowerCase())) - .map(menuNav); - if (options.length > 10) { - options = options.slice(0, 10); - } - // The menu is hidden if there are no options setIsAutocompleteOpen(options.length > 0); - - setAutocompleteOptions(options); } else { setIsAutocompleteOpen(false); } @@ -190,11 +182,10 @@ export const NavigationSearch = ({ items, clickAndNavigate }) => { option.toLowerCase().includes(value.toLowerCase()) ); if (firstItem) { - clickAndNavigate( - navLinks[firstItem].onClick, - navLinks[firstItem].href, - event + const navLink = navLinksArray.find( + ({ title }) => title === firstItem ); + clickAndNavigate(navLink.onClick, navLink.href, event); } }} /> diff --git a/webpack/assets/javascripts/react_app/components/Layout/__tests__/NavigationSearch.test.js b/webpack/assets/javascripts/react_app/components/Layout/__tests__/NavigationSearch.test.js index 21026c18cdc..180ecfb1d24 100644 --- a/webpack/assets/javascripts/react_app/components/Layout/__tests__/NavigationSearch.test.js +++ b/webpack/assets/javascripts/react_app/components/Layout/__tests__/NavigationSearch.test.js @@ -4,7 +4,20 @@ import { NavigationSearch } from '../NavigationSearch'; import { PFitems } from '../Layout.fixtures'; describe('NavigationSearch', () => { - const items = PFitems; + const items = [ + ...PFitems, + { + title: 'test', + initialActive: true, + iconClass: 'fa fa-tachometer', + subItems: [{ + title: 'Aa', // duplicate title to test filtering + isDivider: false, + href: '/aaa', + id: 'menu_item_aa', + }], + }, + ]; it('should display autocomplete options when input is typed', async () => { const { queryAllByRole, @@ -21,6 +34,6 @@ describe('NavigationSearch', () => { await act(async () => { await fireEvent.change(input, { target: { value: 'a' } }); }); - expect(queryAllByRole('menuitem')).toHaveLength(2); + expect(queryAllByRole('menuitem')).toHaveLength(3); }); });