diff --git a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap index ed97db020035e..9bb101334beb7 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap @@ -125,12 +125,14 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` "aria-label": "recent 1", "href": "recent 1", "label": "recent 1", + "onClick": undefined, "title": "recent 1", }, Object { "aria-label": "recent 2", "href": "recent 2", "label": "recent 2", + "onClick": undefined, "title": "recent 2", }, ] @@ -298,6 +300,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` >
@@ -375,6 +379,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` @@ -934,6 +939,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` >
@@ -1011,6 +1018,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` @@ -1638,6 +1646,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` className="euiFlexItem eui-yScroll" > } className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-subj="collapsibleNavGroup-recentlyViewed" id="mockId" initialIsOpen={true} onToggle={[Function]} @@ -1675,6 +1685,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` >
} className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-subj="collapsibleNavGroup-recentlyViewed" id="mockId" initialIsOpen={true} onToggle={[Function]} @@ -3311,6 +3336,7 @@ exports[`CollapsibleNav renders the default nav 2`] = ` >
} className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-subj="collapsibleNavGroup-recentlyViewed" id="mockId" initialIsOpen={true} onToggle={[Function]} @@ -4235,6 +4265,7 @@ exports[`CollapsibleNav renders the default nav 3`] = ` >
({ htmlIdGenerator: () => () => 'mockId', @@ -31,24 +31,25 @@ jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ const { kibana, observability, security, management } = DEFAULT_APP_CATEGORIES; -function mockLink(label: string, category?: AppCategory) { +function mockLink({ label = 'discover', category, onClick }: Partial) { return { key: label, label, href: label, isActive: true, - onClick: () => {}, + onClick: onClick || (() => {}), category, 'data-test-subj': label, }; } -function mockRecentNavLink(label: string) { +function mockRecentNavLink({ label = 'recent', onClick }: Partial) { return { href: label, label, title: label, 'aria-label': label, + onClick, }; } @@ -67,6 +68,20 @@ function mockProps() { }; } +function expectShownNavLinksCount(component: ReactWrapper, count: number) { + expect( + component.find('.euiAccordion-isOpen a[data-test-subj^="collapsibleNavAppLink"]').length + ).toEqual(count); +} + +function expectNavIsClosed(component: ReactWrapper) { + expectShownNavLinksCount(component, 0); +} + +function clickGroup(component: ReactWrapper, group: string) { + component.find(`[data-test-subj="collapsibleNavGroup-${group}"] button`).simulate('click'); +} + describe('CollapsibleNav', () => { // this test is mostly an "EUI works as expected" sanity check it('renders the default nav', () => { @@ -88,16 +103,19 @@ describe('CollapsibleNav', () => { it('renders links grouped by category', () => { // just a test of category functionality, categories are not accurate const navLinks = [ - mockLink('discover', kibana), - mockLink('siem', security), - mockLink('metrics', observability), - mockLink('monitoring', management), - mockLink('visualize', kibana), - mockLink('dashboard', kibana), - mockLink('canvas'), // links should be able to be rendered top level as well - mockLink('logs', observability), + mockLink({ label: 'discover', category: kibana }), + mockLink({ label: 'siem', category: security }), + mockLink({ label: 'metrics', category: observability }), + mockLink({ label: 'monitoring', category: management }), + mockLink({ label: 'visualize', category: kibana }), + mockLink({ label: 'dashboard', category: kibana }), + mockLink({ label: 'canvas' }), // links should be able to be rendered top level as well + mockLink({ label: 'logs', category: observability }), + ]; + const recentNavLinks = [ + mockRecentNavLink({ label: 'recent 1' }), + mockRecentNavLink({ label: 'recent 2' }), ]; - const recentNavLinks = [mockRecentNavLink('recent 1'), mockRecentNavLink('recent 2')]; const component = mount( { }); it('remembers collapsible section state', () => { - function expectNavLinksCount(component: ReactWrapper, count: number) { - expect( - component.find('.euiAccordion-isOpen a[data-test-subj="collapsibleNavAppLink"]').length - ).toEqual(count); - } - - const navLinks = [ - mockLink('discover', kibana), - mockLink('siem', security), - mockLink('metrics', observability), - mockLink('monitoring', management), - mockLink('visualize', kibana), - mockLink('dashboard', kibana), - mockLink('logs', observability), - ]; - const component = mount(); - expectNavLinksCount(component, 7); - component.find('[data-test-subj="collapsibleNavGroup-kibana"] button').simulate('click'); - expectNavLinksCount(component, 4); + const navLinks = [mockLink({ category: kibana }), mockLink({ category: observability })]; + const recentNavLinks = [mockRecentNavLink({})]; + const component = mount( + + ); + expectShownNavLinksCount(component, 3); + clickGroup(component, 'kibana'); + clickGroup(component, 'recentlyViewed'); + expectShownNavLinksCount(component, 1); component.setProps({ isOpen: false }); - expectNavLinksCount(component, 0); // double check the nav closed + expectNavIsClosed(component); + component.setProps({ isOpen: true }); + expectShownNavLinksCount(component, 1); + }); + + it('closes the nav after clicking a link', () => { + const onClick = sinon.spy(); + const onIsOpenUpdate = sinon.spy(); + const navLinks = [mockLink({ category: kibana, onClick })]; + const recentNavLinks = [mockRecentNavLink({ onClick })]; + const component = mount( + + ); + component.setProps({ + onIsOpenUpdate: (isOpen: boolean) => { + component.setProps({ isOpen }); + onIsOpenUpdate(); + }, + }); + + component.find('[data-test-subj="collapsibleNavGroup-recentlyViewed"] a').simulate('click'); + expect(onClick.callCount).toEqual(1); + expect(onIsOpenUpdate.callCount).toEqual(1); + expectNavIsClosed(component); component.setProps({ isOpen: true }); - expectNavLinksCount(component, 4); + component.find('[data-test-subj="collapsibleNavGroup-kibana"] a').simulate('click'); + expect(onClick.callCount).toEqual(2); + expect(onIsOpenUpdate.callCount).toEqual(2); }); }); diff --git a/src/core/public/chrome/ui/header/collapsible_nav.tsx b/src/core/public/chrome/ui/header/collapsible_nav.tsx index 9adcc19b0f0e7..81970bc4a2675 100644 --- a/src/core/public/chrome/ui/header/collapsible_nav.tsx +++ b/src/core/public/chrome/ui/header/collapsible_nav.tsx @@ -159,18 +159,23 @@ export function CollapsibleNav({ isCollapsible={true} initialIsOpen={getIsCategoryOpen('recentlyViewed', storage)} onToggle={isCategoryOpen => setIsCategoryOpen('recentlyViewed', isCategoryOpen, storage)} + data-test-subj="collapsibleNavGroup-recentlyViewed" > {recentNavLinks.length > 0 ? ( { - // TODO #64541 - // Can remove icon from recent links completely - const { iconType, ...linkWithoutIcon } = link; - return linkWithoutIcon; - })} + // TODO #64541 + // Can remove icon from recent links completely + listItems={recentNavLinks.map(({ iconType, onClick = () => {}, ...link }) => ({ + 'data-test-subj': 'collapsibleNavAppLink--recent', + onClick: (e: React.MouseEvent) => { + onIsOpenUpdate(false); + onClick(e); + }, + ...link, + }))} maxWidth="none" color="subdued" gutterSize="none" @@ -191,7 +196,7 @@ export function CollapsibleNav({ {orderedCategories.map((categoryName, i) => { const category = categoryDictionary[categoryName]!; const links = allCategorizedLinks[categoryName].map( - ({ label, href, isActive, isDisabled, onClick }: NavLink) => ({ + ({ label, href, isActive, isDisabled, onClick }) => ({ label, href, isActive, diff --git a/src/core/public/chrome/ui/header/nav_link.tsx b/src/core/public/chrome/ui/header/nav_link.tsx index 22708c796d7dc..8003c22b99a36 100644 --- a/src/core/public/chrome/ui/header/nav_link.tsx +++ b/src/core/public/chrome/ui/header/nav_link.tsx @@ -142,6 +142,7 @@ export interface RecentNavLink { title: string; 'aria-label': string; iconType?: string; + onClick?(event: React.MouseEvent): void; } /**