Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 7 additions & 32 deletions docs/src/components/SiteNav.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
/**
* SiteNav.tsx
*
* Unified site navigation with parent/sub-menu state switching.
* Unified site navigation. Every top-level section links directly to its index
* page; which menu renders (parent vs. a submenu) is derived from the current
* URL, not from in-menu click state.
*
* Navigation behavior:
* - Components, Get started, Foundations: has submenu (many pages)
* - Tokens: Direct link, stays on parent menu, highlights "Tokens"
* - Examples: Direct link, stays on parent menu, highlights "Examples"
*
* Menu level is managed via React state:
* - Parent view: Shows all sections
* - Sub-menu view: Shows section-specific navigation (components, get-started, foundations)
* - Components, Get started, Foundations: land on their index page, which shows
* the matching submenu.
* - Tokens, Examples: single pages, stay on the parent menu and highlight.
* - A submenu's "All" item navigates home and opens global search.
*/

import { useState, useEffect, useCallback } from "react";
Expand Down Expand Up @@ -176,25 +174,6 @@ export function SiteNav({
});
}, [menuLevel]);

const handleSelectSection = useCallback(
(section: MenuSection) => {
// Submenu sections switch the menu into their dedicated view.
if (SUBMENU_SECTIONS.includes(section)) {
setMenuLevel(section);
// Auto-expand when entering submenu while collapsed (icons aren't descriptive enough)
if (!isOpen) {
setIsOpen(true);
}
}
// Other sections navigate directly via url prop, no state change needed
},
[isOpen],
);

const handleBack = useCallback(() => {
setMenuLevel("parent");
}, []);

const handleExpandMenu = useCallback(() => {
setIsOpen(true);
}, []);
Expand All @@ -209,7 +188,6 @@ export function SiteNav({
<ComponentsSubMenu
isOpen={isOpen}
onToggle={handleToggle}
onBack={handleBack}
onExpandMenu={handleExpandMenu}
currentSlug={currentSlug}
categories={categories}
Expand All @@ -221,7 +199,6 @@ export function SiteNav({
<GetStartedSubMenu
isOpen={isOpen}
onToggle={handleToggle}
onBack={handleBack}
onExpandMenu={handleExpandMenu}
currentUrl={currentUrl}
items={getStartedNav}
Expand All @@ -233,7 +210,6 @@ export function SiteNav({
<FoundationsSubMenu
isOpen={isOpen}
onToggle={handleToggle}
onBack={handleBack}
onExpandMenu={handleExpandMenu}
currentUrl={currentUrl}
/>
Expand All @@ -246,7 +222,6 @@ export function SiteNav({
<ParentMenu
isOpen={isOpen}
onToggle={handleToggle}
onSelectSection={handleSelectSection}
currentSection={currentSection}
/>
);
Expand Down
31 changes: 31 additions & 0 deletions docs/src/components/nav/AllHomeItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* AllHomeItem.tsx
*
* The "All" item rendered at the top of every submenu (Components, Get started,
* Foundations). It navigates back to the home page, where the hero search field
* is front and center, rather than just collapsing the menu.
*
* Wrapped-onClick + sentinel `url` mirrors the existing pattern in the submenus
* (e.g. handleAllComponentsClick): the menu's onNavigate ignores `/__` paths, so
* the onClick owns navigation.
*/

import { type MouseEvent } from "react";
import { GoabWorkSideMenuItem } from "@abgov/react-components";
import { withBase } from "@/lib/base-url";

export function AllHomeItem() {
const handleClick = (e: MouseEvent) => {
e.preventDefault();
e.stopPropagation();
window.location.href = withBase("/");
};

return (
<div onClick={handleClick} style={{ cursor: "pointer" }}>
<GoabWorkSideMenuItem label="All" icon="arrow-back" url="/__all_home__" />
</div>
);
}

export default AllHomeItem;
18 changes: 4 additions & 14 deletions docs/src/components/nav/ComponentsSubMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ import {
GoabWorkSideMenuGroup,
} from "@abgov/react-components";
import { MenuSecondaryContent } from "./MenuSecondaryContent";
import { AllHomeItem } from "./AllHomeItem";
import type { NavCategory } from "../../lib/nav-categories";
import { withBase } from "@/lib/base-url";

interface ComponentsSubMenuProps {
isOpen: boolean;
onToggle: () => void;
onBack: () => void;
onExpandMenu?: () => void;
currentSlug?: string;
categories?: NavCategory[];
Expand All @@ -27,35 +27,25 @@ interface ComponentsSubMenuProps {
export function ComponentsSubMenu({
isOpen,
onToggle,
onBack,
onExpandMenu,
currentSlug,
categories = [],
}: ComponentsSubMenuProps) {
// We're on the All Components page if there's no currentSlug
const isAllComponentsPage = !currentSlug;

// Handle back button click - wrap prevents navigation, triggers state change
const handleBackClick = (e: MouseEvent) => {
e.preventDefault();
e.stopPropagation();
onBack();
};

// Handle All Components click on detail pages - navigate without URL auto-matching
const handleAllComponentsClick = (e: MouseEvent) => {
e.preventDefault();
e.stopPropagation();
window.location.href = withBase("/components");
};

// Primary content: Back button + All Components link + component categories
// Primary content: All (home + global search) + All Components link + component categories
const primaryContent = (
<>
{/* Back to parent menu - wrapped div captures click since component doesn't expose onClick */}
<div onClick={handleBackClick} style={{ cursor: "pointer" }}>
<GoabWorkSideMenuItem label="All" icon="arrow-back" url="/__back__" />
</div>
{/* "All" navigates home and opens global search */}
<AllHomeItem />

{/* All Components page link
- On All Components page: use url so auto-matching highlights it
Expand Down
16 changes: 3 additions & 13 deletions docs/src/components/nav/FoundationsSubMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
* Uses GoabWorkSideMenuGroup for expandable Style guide sections.
*/

import { type MouseEvent } from "react";
import {
GoabWorkSideMenu,
GoabWorkSideMenuItem,
GoabWorkSideMenuGroup,
} from "@abgov/react-components";
import { MenuSecondaryContent } from "./MenuSecondaryContent";
import { AllHomeItem } from "./AllHomeItem";
import { withBase } from "@/lib/base-url";

// Top-level pages (not in a group)
Expand Down Expand Up @@ -77,30 +77,20 @@ const ALL_URLS = [
interface FoundationsSubMenuProps {
isOpen: boolean;
onToggle: () => void;
onBack: () => void;
onExpandMenu?: () => void;
currentUrl?: string;
}

export function FoundationsSubMenu({
isOpen,
onToggle,
onBack,
onExpandMenu,
currentUrl,
}: FoundationsSubMenuProps) {
const handleBackClick = (e: MouseEvent) => {
e.preventDefault();
e.stopPropagation();
onBack();
};

const primaryContent = (
<>
{/* Back to parent menu */}
<div onClick={handleBackClick} style={{ cursor: "pointer" }}>
<GoabWorkSideMenuItem label="All" icon="arrow-back" url="/__back__" />
</div>
{/* "All" navigates home and opens global search */}
<AllHomeItem />

{/* Top-level pages */}
{TOP_PAGES.map((page) => (
Expand Down
17 changes: 4 additions & 13 deletions docs/src/components/nav/GetStartedSubMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@
* `getGetStartedNav()` in lib/get-started-nav.ts.
*/

import { Fragment, type MouseEvent } from "react";
import { Fragment } from "react";
import {
GoabWorkSideMenu,
GoabWorkSideMenuItem,
GoabWorkSideMenuGroup,
} from "@abgov/react-components";
import { MenuSecondaryContent } from "./MenuSecondaryContent";
import { AllHomeItem } from "./AllHomeItem";
import { withBase } from "@/lib/base-url";
import type { GetStartedNav, GetStartedNavSection } from "@/lib/get-started-nav";

interface GetStartedSubMenuProps {
isOpen: boolean;
onToggle: () => void;
onBack: () => void;
onExpandMenu?: () => void;
currentUrl?: string;
items: GetStartedNav;
Expand All @@ -29,17 +29,10 @@ interface GetStartedSubMenuProps {
export function GetStartedSubMenu({
isOpen,
onToggle,
onBack,
onExpandMenu,
currentUrl,
items,
}: GetStartedSubMenuProps) {
const handleBackClick = (e: MouseEvent) => {
e.preventDefault();
e.stopPropagation();
onBack();
};

const renderSection = (section: GetStartedNavSection) => {
if (section.type === "flat") {
return (
Expand Down Expand Up @@ -72,10 +65,8 @@ export function GetStartedSubMenu({

const primaryContent = (
<>
{/* Back to parent menu */}
<div onClick={handleBackClick} style={{ cursor: "pointer" }}>
<GoabWorkSideMenuItem label="All" icon="arrow-back" url="/__back__" />
</div>
{/* "All" navigates home and opens global search */}
<AllHomeItem />

{items.sections.map(renderSection)}
</>
Expand Down
87 changes: 14 additions & 73 deletions docs/src/components/nav/ParentMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/**
* ParentMenu.tsx
*
* Parent-level navigation showing all main sections.
* - Components, Get started, Foundations: opens submenu (has many pages)
* - Tokens, Examples: direct navigation (single page each)
* Parent-level navigation showing all main sections. Every section links
* directly to its index page. The destination page's SiteNav then shows the
* relevant submenu (Components, Get started, Foundations) based on the URL;
* Tokens and Examples are single pages.
*/

import React from "react";
import { GoabWorkSideMenu, GoabWorkSideMenuItem } from "@abgov/react-components";
import type { GoabIconType } from "@abgov/ui-components-common";
import { MenuSecondaryContent } from "./MenuSecondaryContent";
Expand All @@ -23,7 +23,6 @@ export type MenuSection =
interface ParentMenuProps {
isOpen: boolean;
onToggle: () => void;
onSelectSection: (section: MenuSection) => void;
/** Currently active section (for highlighting) */
currentSection?: MenuSection;
}
Expand All @@ -34,15 +33,6 @@ interface TopLevelSection {
icon: GoabIconType;
}

// Sections that navigate directly to a page (no submenu)
const DIRECT_NAV_SECTIONS: Partial<Record<MenuSection, string>> = {
tokens: withBase("/tokens"),
examples: withBase("/examples"),
};

// Sections that open a submenu
const SUBMENU_SECTIONS = ["components", "get-started", "foundations"] as const;

// Main navigation sections
const SECTIONS: TopLevelSection[] = [
{ id: "get-started", label: "Get started", icon: "document-text" },
Expand All @@ -55,69 +45,20 @@ const SECTIONS: TopLevelSection[] = [
export function ParentMenu({
isOpen,
onToggle,
onSelectSection,
currentSection,
}: ParentMenuProps) {
// Handle click on submenu item - prevent navigation, open submenu instead
const handleSubmenuClick = (sectionId: MenuSection) => (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
onSelectSection(sectionId);
};

// Primary content: Main navigation sections
// Every section links directly to its index page.
const primaryContent = (
<>
{SECTIONS.map((section) => {
const directUrl = DIRECT_NAV_SECTIONS[section.id];
const hasSubmenu = SUBMENU_SECTIONS.includes(
section.id as (typeof SUBMENU_SECTIONS)[number],
);
const isActive = currentSection === section.id;

if (directUrl) {
// Direct navigation - use url prop
return (
<GoabWorkSideMenuItem
key={section.id}
label={section.label}
icon={section.icon}
url={directUrl}
current={isActive}
/>
);
}

if (hasSubmenu) {
// Opens submenu - use section URL when active for matching,
// otherwise use non-matching URL to prevent false positives on homepage
const submenuUrl = isActive ? withBase(`/${section.id}`) : "/__never_match__";
return (
<div
key={section.id}
onClick={handleSubmenuClick(section.id)}
style={{ cursor: "pointer" }}
>
<GoabWorkSideMenuItem
label={section.label}
icon={section.icon}
url={submenuUrl}
current={isActive}
/>
</div>
);
} else {
// Placeholder sections - disabled for now
return (
<GoabWorkSideMenuItem
key={section.id}
label={section.label}
icon={section.icon}
onClick={() => onSelectSection(section.id)}
/>
);
}
})}
{SECTIONS.map((section) => (
<GoabWorkSideMenuItem
key={section.id}
label={section.label}
icon={section.icon}
url={withBase(`/${section.id}`)}
current={currentSection === section.id}
/>
))}
</>
);

Expand Down
Loading