feat: build housekeeping command deck shell

This commit is contained in:
Simo committed 2026-08-25 20:05:56 +02:00
1 parent cfeb0f19b8
commit addc9c7b1a
9 files changed
+528

No files matched your search

@@ -0,0 +1,174 @@
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { renderToStaticMarkup } from "react-dom/server";
import { describe, expect, it } from "vitest";
import type { HousekeepingNavigationDomain } from "../navigation";
import { HousekeepingShell } from "./housekeeping-shell";
const labels = {
skipToContent: "Skip to housekeeping content",
primaryNavigation: "Primary housekeeping navigation",
contextualNavigation: "Operations navigation",
command: "Commands are unavailable in preview",
preview: "Preview",
backToSite: "Back to site",
};
const domains: readonly HousekeepingNavigationDomain[] = [
{
id: "operations",
href: "/admin-next/operations",
iconId: "inbox",
label: "Operations",
description: "Manage operations",
items: [
{ id: "queue", href: "/admin-next/operations/queue", label: "Queue" },
],
},
{
id: "people",
href: "/admin-next/people",
iconId: "users",
label: "People",
description: "Manage people",
items: [],
},
{
id: "content",
href: "/admin-next/content",
iconId: "file-text",
label: "Content",
description: "Manage content",
items: [],
},
{
id: "economy",
href: "/admin-next/economy",
iconId: "gem",
label: "Economy",
description: "Manage economy",
items: [],
},
{
id: "hotel",
href: "/admin-next/hotel",
iconId: "hotel",
label: "Hotel",
description: "Manage hotel",
items: [],
},
{
id: "system",
href: "/admin-next/system",
iconId: "settings",
label: "System",
description: "Manage system",
items: [],
},
];
function renderShell(
activeDomainId: HousekeepingNavigationDomain["id"] = "operations",
): string {
return renderToStaticMarkup(
<HousekeepingShell
actor={{ id: 7, username: "Nora", rank: 6 }}
activeDomainId={activeDomainId}
domains={domains}
labels={labels}
>
<p>Deck body</p>
</HousekeepingShell>,
);
}
describe("HousekeepingShell", () => {
it("renders the accessible command deck landmarks and supplied shell content", () => {
const html = renderShell();
expect(html).toContain('href="#housekeeping-content"');
expect(html).toContain(">Skip to housekeeping content<");
expect(html).toContain("<aside");
expect(html).toContain("<header");
expect(html).toContain('<nav aria-label="Primary housekeeping navigation"');
expect(html).toContain('<nav aria-label="Operations navigation"');
expect(html).toContain('<main id="housekeeping-content"');
expect(html).toContain(">Deck body</p>");
expect(html).toMatch(
/<a(?=[^>]*href="\/admin-next\/operations")(?=[^>]*aria-current="page")/,
);
expect(html).toContain('href="/admin-next/operations/queue"');
expect(html).toContain(">Queue<");
expect(html).toContain(">Nora<");
expect(html).toContain(">6<");
expect(html).toContain(">Preview<");
expect(html).toContain('href="/"');
expect(html).toContain(">Back to site<");
expect(html).toMatch(
/<button type="button" disabled=""[^>]*>Commands are unavailable in preview<\/button>/,
);
});
it("maps every authorized domain icon and only marks the active domain", () => {
const html = renderShell("people");
for (const iconId of [
"inbox",
"users",
"file-text",
"gem",
"hotel",
"settings",
]) {
expect(html).toContain(`data-domain-icon="${iconId}"`);
}
expect(html).toMatch(
/<a(?=[^>]*href="\/admin-next\/people")(?=[^>]*aria-current="page")/,
);
expect(html).not.toContain(
'href="/admin-next/operations" aria-current="page"',
);
});
it("renders no contextual items when the active domain is absent", () => {
const html = renderShell("system");
expect(html).not.toContain('href="/admin-next/operations/queue"');
expect(html).not.toContain(">Queue<");
});
it("keeps Task 10 modules within the server-rendered source boundary", () => {
const taskFiles = [
"src/features/housekeeping/foundation/shell/housekeeping-shell.tsx",
"src/features/housekeeping/foundation/shell/domain-rail.tsx",
"src/features/housekeeping/foundation/shell/context-nav.tsx",
"src/features/housekeeping/foundation/shell/command-trigger.tsx",
"src/features/housekeeping/foundation/shell/operator-summary.tsx",
"src/features/housekeeping/foundation/page/housekeeping-page-shell.tsx",
"src/features/housekeeping/foundation/page/housekeeping-page-state.tsx",
"src/features/housekeeping/foundation/shell/housekeeping-shell.test.tsx",
"src/features/housekeeping/foundation/page/housekeeping-page-state.test.tsx",
];
const prohibited = [
new RegExp("use " + "client"),
new RegExp("cmd" + "k", "i"),
new RegExp("addEvent" + "Listener"),
new RegExp(
"from\\s+['\"][^'\"]*(?:@/lib/" +
"db|drizzle|/" +
"actions)[^'\"]*['\"]",
),
new RegExp("next" + "-intl"),
/(?:bg|text|border|from|to)-(?:red|blue|green|yellow|orange|purple|pink|gray|slate|zinc|neutral)-\d{2,3}/,
new RegExp("#" + "[0-9a-fA-F]{3,8}"),
new RegExp("(?:r" + "gb|h" + "sl)\\("),
];
for (const path of taskFiles) {
const source = readFileSync(resolve(process.cwd(), path), "utf8");
for (const pattern of prohibited) {
expect(source).not.toMatch(pattern);
}
}
});
});