diff --git a/web/packages/teleport/src/Navigation/ResourcesSection.tsx b/web/packages/teleport/src/Navigation/ResourcesSection.tsx index f0da25f25b4a6..77145168ae694 100644 --- a/web/packages/teleport/src/Navigation/ResourcesSection.tsx +++ b/web/packages/teleport/src/Navigation/ResourcesSection.tsx @@ -48,6 +48,7 @@ import { SubsectionItem, verticalPadding, } from './Section'; +import { useDefaultNavigation } from './useDefaultNavigation'; /** * getResourcesSection returns a NavigationSection for resources, @@ -311,7 +312,7 @@ export function ResourcesSection({ const isExpanded = expandedSection?.category === NavigationCategory.Resources; - const subsections = getResourcesSubsections({ + section.subsections = getResourcesSubsections({ clusterId, preferences, updatePreferences, @@ -337,6 +338,7 @@ export function ResourcesSection({ isExpanded={isExpanded} showPoweredByLogo={showPoweredByLogo} {...getReferenceProps()} + {...useDefaultNavigation(section)} > - {subsections + {section.subsections .filter(section => !section.subCategory) .map(section => ( - {subsections + {section.subsections .filter( section => section.subCategory === diff --git a/web/packages/teleport/src/Navigation/Section.tsx b/web/packages/teleport/src/Navigation/Section.tsx index f3b72956d846d..01ad624e365ca 100644 --- a/web/packages/teleport/src/Navigation/Section.tsx +++ b/web/packages/teleport/src/Navigation/Section.tsx @@ -41,6 +41,7 @@ import { NavigationSubsection, useFloatingUiWithRestMs, } from './Navigation'; +import { useDefaultNavigation } from './useDefaultNavigation'; import { zIndexMap } from './zIndexMap'; type SharedSectionProps = { @@ -90,6 +91,7 @@ export function DefaultSection({ isExpanded={isExpanded} tabIndex={section.standalone ? 0 : -1} {...getReferenceProps()} + {...useDefaultNavigation(section)} > {section.category} diff --git a/web/packages/teleport/src/Navigation/useDefaultNavigation.test.ts b/web/packages/teleport/src/Navigation/useDefaultNavigation.test.ts new file mode 100644 index 0000000000000..baf95f5c6c676 --- /dev/null +++ b/web/packages/teleport/src/Navigation/useDefaultNavigation.test.ts @@ -0,0 +1,69 @@ +/** + * Teleport + * Copyright (C) 2025 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { act, renderHook } from '@testing-library/react'; +import { useHistory } from 'react-router-dom'; + +import { NavigationSection, NavigationSubsection } from './Navigation'; +import { useDefaultNavigation } from './useDefaultNavigation'; + +jest.mock('react-router-dom', () => ({ + useHistory: jest.fn(), +})); + +const pushMock = jest.fn(); + +describe('useDefaultNavigation', () => { + beforeEach(() => { + (useHistory as jest.Mock).mockReturnValue({ + push: pushMock, + }); + }); + + it('returns an onClick function that calls the first section onclick and navigates to its route', () => { + const sectionOnclickMock = jest.fn(); + const testRoute = '/test'; + + const section = { + subsections: [ + { + title: 'test', + exact: true, + icon: () => null, + route: testRoute, + onClick: sectionOnclickMock, + }, + { + title: 'test2', + exact: true, + icon: () => null, + route: testRoute + '2', + }, + ] as NavigationSubsection[], + } as NavigationSection; + + const { result } = renderHook(() => useDefaultNavigation(section)); + + act(() => { + result.current.onClick?.(); + }); + + expect(sectionOnclickMock).toHaveBeenCalled(); + expect(pushMock).toHaveBeenCalledWith(testRoute); + }); +}); diff --git a/web/packages/teleport/src/Navigation/useDefaultNavigation.ts b/web/packages/teleport/src/Navigation/useDefaultNavigation.ts new file mode 100644 index 0000000000000..1959ba5db37aa --- /dev/null +++ b/web/packages/teleport/src/Navigation/useDefaultNavigation.ts @@ -0,0 +1,38 @@ +/** + * Teleport + * Copyright (C) 2025 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { useHistory } from 'react-router-dom'; + +import { NavigationSection } from './Navigation'; + +export function useDefaultNavigation(section: NavigationSection) { + const history = useHistory(); + + if (section.subsections?.length === 0) { + return {}; + } + + const first = section.subsections[0]; + + return { + onClick: () => { + first.onClick?.(); + history.push(first.route); + }, + }; +}