feat(ui): redesign grouped application shell

This commit is contained in:
cesnimda
2026-09-28 17:26:02 +02:00
parent 57a6bd02c6
commit 8e56ceabf5
30 changed files with 398 additions and 92 deletions
@@ -0,0 +1,58 @@
import React, { useState } from "react";
import "@testing-library/jest-dom";
import { fireEvent, render, screen, within } from "@testing-library/react";
import { CssVarsProvider } from "@mui/material/styles";
import { I18nProvider } from "./i18n/I18nProvider";
import AppShell, { type NavItem } from "./layout/AppShell";
import { getTheme } from "./theme";
jest.mock("@mui/material/useMediaQuery", () => ({ __esModule: true, default: () => false }));
const nav: NavItem[] = [
{ to: "/dashboard", label: "Dashboard", icon: <span /> },
{ to: "/jobs", label: "Jobs", icon: <span />, section: "Job search", children: [
{ to: "/kanban", label: "Board", icon: <span /> },
{ to: "/trash", label: "Trash", icon: <span /> },
] },
{ to: "/career", label: "Career profile", icon: <span />, section: "Career" },
];
function Providers({ children }: { children: React.ReactNode }) {
return <CssVarsProvider theme={getTheme("light") as any} defaultMode="light"><I18nProvider>{children}</I18nProvider></CssVarsProvider>;
}
afterEach(() => localStorage.clear());
test("[P5-01 PRES-E07 E08] grouped navigation exposes the current child without marking the parent as the page", () => {
render(<Providers><AppShell pageTitle="Board" breadcrumbs={[]} pathname="/kanban" nav={nav} navBottom={[]} onNavigate={jest.fn()} onToggleDrawer={jest.fn()} drawerOpen={false}><div>Board content</div></AppShell></Providers>);
expect(screen.getByRole("navigation", { name: "Navigation" })).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Board" })).toHaveAttribute("aria-current", "page");
expect(screen.getByRole("button", { name: "Jobs" })).toHaveAttribute("aria-current", "location");
expect(screen.getAllByText("Job search").length).toBeGreaterThan(0);
expect(screen.getAllByText("Career").length).toBeGreaterThan(0);
});
test("[P5-01 PRES-E07 E08] collapsed secondary destinations remain keyboard-operable", () => {
localStorage.setItem("appShellDesktopSidebarCollapsed", "1");
const navigate = jest.fn();
render(<Providers><AppShell pageTitle="Jobs" breadcrumbs={[]} pathname="/jobs" nav={nav} navBottom={[]} onNavigate={navigate} onToggleDrawer={jest.fn()} drawerOpen={false}><div>Jobs content</div></AppShell></Providers>);
const jobs = screen.getByRole("button", { name: "Jobs" });
expect(jobs).toHaveAttribute("aria-haspopup", "menu");
fireEvent.keyDown(jobs, { key: "Enter" });
fireEvent.click(jobs);
const menu = screen.getByRole("menu");
fireEvent.click(within(menu).getByRole("menuitem", { name: "Trash" }));
expect(navigate).toHaveBeenCalledWith("/trash");
});
test("[P5-01 PRES-E08] successful shell navigation focuses the new route heading and exposes a skip link", () => {
function Harness() {
const [path, setPath] = useState("/dashboard");
return <AppShell pageTitle={path === "/career" ? "Career profile" : "Dashboard"} breadcrumbs={[]} pathname={path} nav={nav} navBottom={[]} onNavigate={setPath} onToggleDrawer={jest.fn()} drawerOpen={false}><div>Page content</div></AppShell>;
}
render(<Providers><Harness /></Providers>);
expect(screen.getByRole("link", { name: "Skip to main content" })).toHaveAttribute("href", "#app-main-content");
fireEvent.click(screen.getByRole("button", { name: "Career profile" }));
expect(screen.getByRole("heading", { level: 1, name: "Career profile" })).toHaveFocus();
});