fix(ui): unify heading help, table sizing and navigation contracts

Verified with the coordinated workspace changes by devkit full run
2026-09-08T225814-186389-0000-3e3ed7cd (all seven phases passed).
This shared UI pass does not mark the individual module reviews complete.
This commit is contained in:
2026-09-09 02:03:16 +02:00
parent 6591aaa3fd
commit 6d37aa527f
54 changed files with 1728 additions and 117 deletions
+112
View File
@@ -0,0 +1,112 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { createRequire } from "node:module";
import test from "node:test";
import vm from "node:vm";
const require = createRequire(import.meta.url);
const { transformSync } = require("esbuild");
const React = require("react");
const { renderToStaticMarkup } = require("react-dom/server");
const { MemoryRouter } = require("react-router");
function loadSource(path, bindings = {}) {
const context = vm.createContext({ module: { exports: {} }, require, ...bindings });
context.exports = context.module.exports;
vm.runInContext(transformSync(readFileSync(new URL(path, import.meta.url), "utf8"), {
loader: path.endsWith(".tsx") ? "tsx" : "ts", format: "cjs", jsx: "automatic"
}).code, context);
return { exports: context.module.exports, context };
}
const { generatedTranslations } = loadSource("../src/i18n/generatedTranslations.ts").exports;
const launch = loadSource("../src/platform/launchContext.ts").exports;
function harness(locale = "en") {
const navigation = [];
const translateText = (value) => {
if (typeof value === "string") return generatedTranslations[locale][value] ?? value;
let text = generatedTranslations[locale][value.key] ?? value.key;
for (const [key, replacement] of Object.entries(value.values)) {
text = text.replaceAll(`{${key}}`, replacement);
}
return text;
};
const loaded = loadSource("../src/layout/BreadcrumbBar.tsx", {
window: { history: { state: { idx: 7 } } },
require: (name) => {
if (name === "../i18n/LanguageContext") return {
usePlatformLanguage: () => ({ translateText }),
i18nMessage: (key, values) => ({ key, values })
};
if (name === "../components/UnsavedChangesGuard") return {
useGuardedNavigate: () => (...args) => navigation.push(args)
};
if (name === "../platform/launchContext") return launch;
return require(name);
}
});
const BreadcrumbBar = loaded.exports.default;
return {
...loaded, navigation, BreadcrumbBar,
links(pathname, search = "") {
const html = renderToStaticMarkup(React.createElement(
MemoryRouter, { initialEntries: [`${pathname}${search}`] },
React.createElement(BreadcrumbBar, { pathname })
));
return [...html.matchAll(/<a\b([^>]*)>(.*?)<\/a>/g)].map(([, attributes, label]) => ({
href: /href="([^"]*)"/.exec(attributes)?.[1], label: label.replaceAll("&amp;", "&")
}));
}
};
}
test("module-global reports and operator queue have their own bilingual labels and exact links", () => {
for (const locale of ["en", "de"]) {
const h = harness(locale);
const root = generatedTranslations[locale]["i18n:govoplan-core.campaigns.01a23a28"];
for (const [route, key] of [
["reports", "i18n:govoplan-core.reports.88bc3fe3"],
["queue", "i18n:govoplan-core.operator_queue.72492fb5"]
]) {
for (const trailingSlash of ["", "/"]) {
assert.deepEqual(h.links(`/campaigns/${route}${trailingSlash}`, "?campaign=selected-campaign"), [
{ href: "/campaigns", label: root },
{ href: `/campaigns/${route}`, label: generatedTranslations[locale][key] }
]);
}
}
assert.equal(h.links("/operator")[0].label, generatedTranslations[locale]["i18n:govoplan-core.operator_queue.72492fb5"]);
}
});
test("campaign editor report aliases and attachment deep links retain their singular campaign context", () => {
const h = harness();
const campaignId = "8ef65230-94d1-49a5-93a9-c5ef7c87ad45";
for (const suffix of ["report", "reports", "attachments"]) {
const links = h.links(`/campaigns/${campaignId}/${suffix}`);
assert.deepEqual(links.map((item) => item.href), ["/campaigns", `/campaigns/${campaignId}`, `/campaigns/${campaignId}/${suffix}`]);
assert.equal(links[1].label, generatedTranslations.en["i18n:govoplan-core.campaign.69390e16"]);
assert.equal(links[2].label, generatedTranslations.en[suffix === "attachments"
? "i18n:govoplan-core.attachments.6771ade6" : "i18n:govoplan-core.report.ee45c303"]);
}
});
test("quick-access return preserves guarded history back or the exact origin deep link", () => {
const origin = launch.createQuickAccessLaunchContext({
pathname: "/cases/case-fixture", search: "?tab=work", hash: "#campaigns", historyIndex: 6,
auth: { user: { account_id: "account-fixture" }, tenant: { id: "tenant-fixture" } },
temporalContext: { validityMode: "current" }
});
const h = harness();
for (const historyIndex of [7, 20]) {
h.context.window.history.state.idx = historyIndex;
const tree = h.BreadcrumbBar({ pathname: "/campaigns/reports", locationState: launch.quickAccessLaunchState(origin) });
const button = React.Children.toArray(tree.props.children).find((item) => item.type === "button");
assert.ok(button, "the launch return action remains visible");
button.props.onClick();
}
assert.deepEqual(h.navigation[0], [-1]);
assert.equal(h.navigation[1][0], "/cases/case-fixture?tab=work#campaigns");
assert.equal(h.navigation[1][1].replace, true);
});
+57
View File
@@ -0,0 +1,57 @@
import assert from "node:assert/strict";
import { mkdtempSync, readFileSync, readdirSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { test } from "node:test";
import { componentSuites, runComponentTests, selectSuites } from "../scripts/run-component-tests.mjs";
test("component aliases select the existing contract and reject unknown input", () => {
assert.deepEqual(selectSuites([]), Object.keys(componentSuites));
assert.deepEqual(selectSuites(["page-layout", "page-layout"]), ["page-layout"]);
assert.deepEqual(componentSuites["data-grid-actions"], ["data-grid-actions", "data-grid-sizing"]);
assert.throws(() => selectSuites(["../../unowned"]), /Unknown component suite/);
});
test("every configured component test remains covered by the batch and standalone aliases", () => {
const config = JSON.parse(readFileSync(new URL("../tsconfig.component-tests.json", import.meta.url), "utf8"));
const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
const configured = config.include.filter((file) => file.startsWith("tests/")).map((file) => file.replace(/^tests\//, "").replace(/\.test\.tsx?$/, "")).sort();
assert.deepEqual(Object.values(componentSuites).flat().sort(), configured);
for (const name of Object.keys(componentSuites)) {
assert.equal(packageJson.scripts[`test:${name}`], `node scripts/run-component-tests.mjs ${name}`);
}
assert.equal(packageJson.scripts["test:components"], "node scripts/run-component-tests.mjs");
});
test("one compile serves a batch and preserves structural follow-ups", async () => {
const root = mkdtempSync(join(tmpdir(), "govoplan-component-runner-"));
const commands = [];
try {
const result = await runComponentTests({ webuiRoot: root, compiler: "fixture-tsc", names: ["data-grid-actions", "dialog-focus"], run: async (argv) => commands.push(argv) });
assert.equal(result.compiled, 1);
assert.equal(commands.filter((argv) => argv[1] === "fixture-tsc").length, 1);
assert.equal(commands.length, 5);
assert(commands.at(-1)[1].endsWith("test-dialog-focus-structure.mjs"));
assert.deepEqual(readdirSync(root), []);
} finally { rmSync(root, { recursive: true, force: true }); }
});
test("overlapping runs have separate outputs and failures clean only owned output", async () => {
const root = mkdtempSync(join(tmpdir(), "govoplan-component-runner-"));
const outputs = [];
let unblock;
const bothStarted = new Promise((resolve) => { unblock = resolve; });
const run = async (argv) => {
if (argv[1] !== "fixture-tsc") return;
outputs.push(argv.at(-1));
if (outputs.length === 2) unblock();
await bothStarted;
throw new Error("fixture compilation failed");
};
try {
const results = await Promise.allSettled([1, 2].map(() => runComponentTests({ webuiRoot: root, compiler: "fixture-tsc", names: ["page-layout"], run })));
assert(results.every((result) => result.status === "rejected"));
assert.equal(new Set(outputs).size, 2);
assert.deepEqual(readdirSync(root), []);
} finally { rmSync(root, { recursive: true, force: true }); }
});
+71
View File
@@ -342,6 +342,77 @@ assertWidths(
"growth does not resize a fixed peer merely to avoid overflow"
);
const mixedPeerColumns: DataGridSizingColumn[] = [
{ id: "first", width: 200, minWidth: 100, maxWidth: 500, resizable: true },
{ id: "fixed", width: 120, minWidth: 120, maxWidth: 120 },
{ id: "last", width: 300, minWidth: 100, maxWidth: 500, resizable: true },
{ id: "actions", width: 100, sticky: "end" }
];
const mixedPeerBase = { first: 200, fixed: 120, last: 300, actions: 100 };
for (const mode of ["cover", "free", "constrained"] as const) {
const grown = resizeDataGridColumn(mixedPeerColumns, mixedPeerBase, "first", 80, mode);
assertWidths(grown.widths,
{ first: 280, fixed: 120, last: mode === "constrained" ? 220 : 300, actions: 100 },
`${mode} growth crosses a fixed intermediate column without changing it`);
const shrunk = resizeDataGridColumn(mixedPeerColumns, mixedPeerBase, "first", -80, mode);
assertWidths(shrunk.widths,
{ first: 120, fixed: 120, last: mode === "free" ? 300 : 380, actions: 100 },
`${mode} shrink uses only the appropriate resizable compensation peers`);
const limited = resizeDataGridColumn(mixedPeerColumns, mixedPeerBase, "first", 800, mode);
assertEqual(limited.widths.fixed, 120, `${mode} exhaustion never changes the fixed intermediate track`);
assertEqual(limited.widths.first, mode === "constrained" ? 400 : 500, `${mode} retains declared growth limits`);
}
const preferredLimitColumns: DataGridSizingColumn[] = [
{ id: "recipients", width: "minmax(320px, 1.4fr)", preferredMaxWidth: 640, resizable: true },
{ id: "active", width: 130 },
{ id: "delivery", width: "minmax(260px, 0.9fr)", preferredMaxWidth: 480, resizable: true },
{ id: "attachments", width: 180 },
...["first", "second", "third"].map((id) => ({ id, width: 190, minWidth: 160, preferredMaxWidth: 360, resizable: true })),
{ id: "actions", width: 180, sticky: "end" }
];
const hardLimitColumns = preferredLimitColumns.map(({ preferredMaxWidth, ...column }) => ({ ...column, maxWidth: preferredMaxWidth }));
for (const container of [900, 1600, 2400, 3085]) {
assertWidths(fitDataGridColumns(preferredLimitColumns, container).widths,
fitDataGridColumns(hardLimitColumns, container).widths,
`preferred caps preserve the previous automatic fit at ${container}px without imposing manual caps`);
}
const rightPeersAtOldCaps: DataGridSizingColumn[] = [
{ id: "recipients", width: "minmax(320px, 1.4fr)", preferredMaxWidth: 640, resizable: true },
{ id: "fixed", width: 130, minWidth: 130, maxWidth: 130 },
{ id: "first", width: 190, preferredMaxWidth: 360, resizable: true },
{ id: "second", width: 190, preferredMaxWidth: 360, resizable: true },
{ id: "actions", width: 180, sticky: "end" }
];
const oversizedRecipient = { recipients: 1000, fixed: 130, first: 360, second: 360, actions: 180 };
const oldManualCaps = rightPeersAtOldCaps.map(({ preferredMaxWidth, ...column }) => ({
...column, maxWidth: preferredMaxWidth ?? column.maxWidth
}));
assertEqual(resizeDataGridColumn(oldManualCaps, oversizedRecipient, "recipients", -120, "cover", 0).appliedDelta, 0,
"regression: at the scrolled-right boundary, old field caps block shrinking an oversized recipient column");
const shrunkAcrossPreferredCaps = resizeDataGridColumn(rightPeersAtOldCaps, oversizedRecipient, "recipients", -120, "cover", 0);
assertWidths(shrunkAcrossPreferredCaps.widths,
{ recipients: 880, fixed: 130, first: 420, second: 420, actions: 180 },
"preferred field caps allow cover compensation at the right scroll boundary without moving fixed neighbors");
assertWidths(fitDataGridColumns(rightPeersAtOldCaps, 2030, {}, shrunkAcrossPreferredCaps.widths, "cover", 2030).widths,
shrunkAcrossPreferredCaps.widths, "committing a compensated shrink never refills the wide recipient column");
for (const mode of ["cover", "free", "constrained"] as const) {
const expandedField = resizeDataGridColumn(rightPeersAtOldCaps, oversizedRecipient, "first", 160, mode);
assertEqual(expandedField.widths.first, 520, `${mode} direct resizing can exceed a presentation-only field cap`);
assertEqual(expandedField.widths.fixed, 130, `${mode} preferred caps do not change a fixed neighbor`);
}
const preferredWithHardLimit: DataGridSizingColumn[] = [{ id: "text", width: 300, preferredMaxWidth: 360, maxWidth: 500, resizable: true }];
assertEqual(resizeDataGridColumn(preferredWithHardLimit, { text: 360 }, "text", 200, "cover").widths.text, 500,
"an explicitly declared hard maximum still bounds direct resizing beyond a preferred cap");
assertEqual(fitDataGridColumns([{ id: "text", width: 500, preferredMaxWidth: 400, maxWidth: 300 }], 300, {}, {}, "free").widths.text, 300,
"a preferred cap never overrides a smaller hard maximum");
assertEqual(fitDataGridColumns([{ id: "text", width: 300, minWidth: 160, preferredMaxWidth: 100 }], 160, {}, {}, "free").widths.text, 160,
"preferred caps never reduce a column below its hard minimum");
assertEqual(dataGridLayoutSignature([{ id: "text", width: 190, resizable: true }], "container", "cover"),
"v3::text:190:::r::::default:::container::cover", "omitting preferred caps preserves the existing persisted signature format");
assertEqual(dataGridLayoutSignature(preferredLimitColumns, "container", "cover") === dataGridLayoutSignature(hardLimitColumns, "container", "cover"), false,
"new preferred sizing declarations invalidate only their own obsolete manual-width contract");
const coverBeyondPassiveMax = resizeDataGridColumn([
{ id: "first", width: 200, minWidth: 100, maxWidth: 200, resizable: true },
{ id: "second", width: 300, minWidth: 100, maxWidth: 300, resizable: true },
@@ -6,6 +6,14 @@ import { renderToStaticMarkup } from "react-dom/server";
import DocumentationHelpLink, { DocumentationHelpProvider } from "../src/components/help/DocumentationHelpLink";
import FieldLabel from "../src/components/help/FieldLabel";
import { documentationHelpHref } from "../src/components/help/documentationHelp";
import PageLayout from "../src/components/PageLayout";
import PageTitle from "../src/components/PageTitle";
import Card from "../src/components/Card";
import Dialog from "../src/components/Dialog";
import AdminPageLayout from "../src/components/admin/AdminPageLayout";
import WorkspaceActionBar from "../src/components/WorkspaceActionBar";
import TextWithHelp from "../src/components/help/TextWithHelp";
import { PlatformLanguageProvider } from "../src/i18n/LanguageContext";
assert(
documentationHelpHref({ topicId: "campaigns.workflow.complete-review" }) ===
@@ -49,3 +57,30 @@ const fieldMarkup = renderToStaticMarkup(
</DocumentationHelpProvider>
);
assert(fieldMarkup.includes("topic=access.reference.admin-access-fields"), "field labels can link to stable reference topics");
for (const language of ["en", "de"]) {
const helpLabel = language === "de" ? "Benutzerdokumentation öffnen" : "Open user documentation";
const help = <DocumentationHelpLink reference={{ contextId: "dashboard" }} />;
for (const [name, element] of [
["page", <PageLayout archetype="overview" title="Dashboard" titleHelp={help} headerLoading actions={<button>Configure</button>}>Data</PageLayout>],
["title", <PageTitle loading titleHelp={help}>Dashboard</PageTitle>],
["administration", <AdminPageLayout title="Dashboard" description="Summary" titleHelp={help}>Data</AdminPageLayout>],
["card", <Card title="Dashboard" titleHelp={help} collapsible>Data</Card>],
["dialog", <Dialog open title="Dashboard" titleHelp={help} onClose={() => undefined}>Data</Dialog>],
["workspace", <WorkspaceActionBar variant="workspace" title="Dashboard" titleHelp={help} titleLevel={1} primaryActions={<button>Edit</button>} />],
["section", <TextWithHelp as="div" help={help}><h3>Dashboard</h3></TextWithHelp>]
] as const) {
const markup = renderToStaticMarkup(<PlatformLanguageProvider preferredLanguageCode={language}>{element}</PlatformLanguageProvider>);
const heading = markup.match(/<h[123]\b[^>]*>[\s\S]*?<\/h[123]>/)?.[0];
assert(heading?.includes("Dashboard"), `${name} retains a visible semantic heading`);
assert(!heading?.includes("documentation-help-link") && !heading?.includes("loading-indicator"), `${name} heading name excludes help and progress controls`);
assert(markup.includes(`aria-label="${helpLabel}"`), `${name} keeps translated documentation access in ${language}`);
assert(markup.indexOf('class="documentation-help-link"') > markup.indexOf(heading!), `${name} help follows its visible heading`);
assert(!/<button\b[^>]*>[\s\S]*?documentation-help-link[\s\S]*?<\/button>/.test(markup), `${name} never nests documentation inside an action`);
}
}
const titleWithoutText = renderToStaticMarkup(<WorkspaceActionBar variant="workspace" titleHelp={<DocumentationHelpLink reference={{ contextId: "dashboard" }} />} />);
assert(!titleWithoutText.includes("documentation-help-link"), "workspace help cannot appear without a visible context heading");
const unheadedCard = renderToStaticMarkup(<Card titleHelp={<DocumentationHelpLink reference={{ contextId: "dashboard" }} />}>Data</Card>);
assert(!unheadedCard.includes("documentation-help-link"), "card help cannot appear without a visible title");
+22
View File
@@ -346,6 +346,28 @@ const aliasOrdered = groupNavigationItems(reorderedProductNavigation.primaryItem
assert(aliasOrdered[0]?.items[0]?.to === "/files" && aliasOrdered[1]?.items[0]?.to === "/messages" && aliasOrdered[1]?.label === "View group", "View ordering recognizes non-placement owner aliases and assigns the requested separator exactly once");
const unauthorizedLockedOwner = projectProductNavigation(reorderedOwners, messageSurfaceModules, { scopes: ["mail:mailbox:read"] } as AuthInfo).primaryItems.find((item) => item.to === "/messages");
assert(unauthorizedLockedOwner?.navigationLocked === false && !unauthorizedLockedOwner.navigationAliases?.includes("postbox.navigation.postbox"), "unavailable optional owners contribute neither aliases nor lock metadata");
const composedCommunicationArea = [{
id: "communication", moduleId: "mail", label: "Communication", iconName: "mail" as const,
surfaceIds: ["mail.navigation.mail"], order: 40
}];
const defaultComposedNavigation = reorderedProductNavigation.primaryItems.map((item) => ({
...item, navigationCustomLayout: false, navigationSection: null
}));
assert(groupNavigationItems(defaultComposedNavigation, composedCommunicationArea)[0]?.items[0]?.to === "/messages", "standard product-area grouping recognizes authorized composed-owner aliases even when another owner supplies the rail placement");
assert(groupNavigationItems(defaultComposedNavigation, composedCommunicationArea)[0]?.label === "Communication", "composed entries retain their standard section heading without a saved layout");
const competingOwnerAreas = [
{ ...composedCommunicationArea[0], order: 10 },
{ ...composedCommunicationArea[0], id: "direct-owner", label: "Direct owner area", surfaceIds: ["postbox.navigation.postbox"], order: 70 }
];
const directOwnerGroups = groupNavigationItems(defaultComposedNavigation, competingOwnerAreas);
assert(directOwnerGroups[0]?.id === "product-area:direct-owner" && directOwnerGroups[0]?.items[0]?.to === "/messages", "an authorized composed entry keeps its directly declared area even when another owner's alias belongs to an earlier area");
assert(directOwnerGroups.flatMap((group) => group.items).filter((item) => item.to === "/messages").length === 1, "competing owner areas never duplicate the composed destination");
const personalFlatNavigation = defaultComposedNavigation.map((item) => ({
...item, navigationCustomLayout: true, navigationLayoutSource: "user"
}));
assert(groupNavigationItems(personalFlatNavigation, composedCommunicationArea).every((group) => group.label === undefined), "standard sections never replace an explicitly flat personal layout");
const mailOnlyDefaultNavigation = projectProductNavigation(reorderedOwners, messageSurfaceModules, { scopes: ["mail:mailbox:read"] } as AuthInfo).primaryItems;
assert(groupNavigationItems(mailOnlyDefaultNavigation, [{ ...composedCommunicationArea[0], surfaceIds: ["postbox.navigation.postbox"] }])[0]?.id === "more-tools", "an unauthorized optional owner's area alias cannot affect default navigation grouping");
assert(
mailOnlyNavigation.primaryItems.map((item) => item.to).join(",") === "/messages",
"composition should remain stable when only one optional contributor is authorized"
+31
View File
@@ -91,3 +91,34 @@ const delegatedHeaderMarkup = renderToStaticMarkup(
assert(!delegatedHeaderMarkup.includes("page-layout-header"), "composite workspaces can delegate their visible heading to contributed content");
assert(delegatedHeaderMarkup.includes("page-layout-workspace content-pad workspace-data-page"), "headerless composite pages retain the central content frame");
for (const state of ["clean", "dirty", "invalid", "save-failed", "conflict", "saving"] as const) {
for (const behavior of ["reset", "exit"] as const) {
const markup = renderToStaticMarkup(
<PlatformLanguageProvider>
<PageActionBar variant="editor" state={state}
discardAction={{ label: "Cancel", behavior, "aria-label": "test-cancel" }}
saveAction={{ label: "Save", "aria-label": "test-save" }} />
</PlatformLanguageProvider>
);
const cancelButton = markup.match(/<button\b[^>]*aria-label="test-cancel"[^>]*>/)?.[0];
const saveButton = markup.match(/<button\b[^>]*aria-label="test-save"[^>]*>/)?.[0];
assert(cancelButton && saveButton, "editor actions remain visible in every draft state");
const disabled = (button: string | undefined) => /\bdisabled=""/.test(button ?? "");
assert(disabled(cancelButton) === (state === "saving" || (state === "clean" && behavior === "reset")),
`${behavior} action has the correct disabled state when ${state}`);
assert(disabled(saveButton) === ["clean", "invalid", "saving"].includes(state),
`cancel availability must not enable invalid/no-op/concurrent saving when ${state}`);
assert(!markup.includes('behavior="'), "the action contract must not leak non-HTML attributes");
}
}
const permissionBlockedExit = renderToStaticMarkup(
<PlatformLanguageProvider>
<PageActionBar variant="editor" state="clean"
discardAction={{ label: "Cancel", behavior: "exit", disabled: true, disabledReason: "Explicitly blocked", "aria-label": "test-blocked-cancel" }}
saveAction={{ label: "Save" }} />
</PlatformLanguageProvider>
);
assert(/\bdisabled=""/.test(permissionBlockedExit.match(/<button\b[^>]*aria-label="test-blocked-cancel"[^>]*>/)?.[0] ?? ""),
"exit semantics preserve an explicit owning-page blocker");
+17 -1
View File
@@ -38,7 +38,10 @@ const markup = renderToStaticMarkup(
);
assert(markup.includes('role="listbox"'), "the root exposes listbox semantics");
assert(markup.includes('class="selection-list domain-list"'), "root caller classes are preserved");
const rootClasses = markup.match(/\bclass="([^"]+)"/)?.[1].split(/\s+/) ?? [];
assert(rootClasses.includes("selection-list"), "the root retains its shared component class");
assert(rootClasses.includes("selection-list-plain"), "the root defaults to the plain variant");
assert(rootClasses.includes("domain-list"), "root caller classes are preserved");
assert(markup.includes('data-source="inbox"'), "root native properties pass through");
assert(markup.includes('aria-label="Notifications"'), "the accessible label is translated");
assert(markup.includes('role="option"'), "items expose option semantics");
@@ -59,6 +62,19 @@ const nativeLabelMarkup = renderToStaticMarkup(
);
assert(nativeLabelMarkup.includes('aria-label="Already translated"'), "native accessible labels pass through unchanged");
const navigationMarkup = renderToStaticMarkup(
<SelectionList variant="navigation" className="domain-navigation" aria-label="Navigation">
<SelectionListItem selected>Current section</SelectionListItem>
</SelectionList>
);
const navigationClasses = navigationMarkup.match(/\bclass="([^"]+)"/)?.[1].split(/\s+/) ?? [];
assert(navigationClasses.includes("selection-list"), "navigation retains the shared root class");
assert(navigationClasses.includes("selection-list-navigation"), "the navigation variant is explicit");
assert(!navigationClasses.includes("selection-list-plain"), "navigation does not inherit the plain variant class");
assert(navigationClasses.includes("domain-navigation"), "navigation preserves caller classes");
assert(navigationMarkup.includes('role="listbox"'), "navigation retains listbox semantics");
assert(navigationMarkup.includes('aria-label="Navigation"'), "navigation retains its accessible label");
const directoryOptions = [
{
value: "account-1",