From cfc8f2b22c316ccc1c65a5a8fbeb1aec39fc7050 Mon Sep 17 00:00:00 2001 From: iris Date: Sat, 15 Aug 2026 12:57:33 +0200 Subject: [PATCH] swarm-ui: add a components-library page (hyperhive#3288) New /components route showing every ui primitive (Panel, StatusChip, Table) in each mode it supports: Panel with/without a title, StatusChip's four tones, Table populated and empty. Sample data only, no API calls, so it renders identically regardless of backend state. Linked from the Shell nav next to "overview". Verified with npm run build + npm run typecheck, and a headless chromium screenshot of the built dist. --- frontend/packages/swarm-ui/src/App.tsx | 2 + .../swarm-ui/src/pages/ComponentsPage.css | 44 ++++++++++ .../swarm-ui/src/pages/ComponentsPage.tsx | 87 +++++++++++++++++++ .../packages/swarm-ui/src/shell/Shell.tsx | 5 +- 4 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 frontend/packages/swarm-ui/src/pages/ComponentsPage.css create mode 100644 frontend/packages/swarm-ui/src/pages/ComponentsPage.tsx diff --git a/frontend/packages/swarm-ui/src/App.tsx b/frontend/packages/swarm-ui/src/App.tsx index 87b66170..b68f4429 100644 --- a/frontend/packages/swarm-ui/src/App.tsx +++ b/frontend/packages/swarm-ui/src/App.tsx @@ -6,6 +6,7 @@ import { useEffect, useState } from 'preact/hooks'; import { Route, Switch } from 'wouter-preact'; import { Shell } from './shell/Shell.js'; +import { ComponentsPage } from './pages/ComponentsPage.js'; import { Panel } from './ui/panel/Panel.js'; import { StatusChip } from './ui/status-chip/StatusChip.js'; import { Table, type TableColumn } from './ui/table/Table.js'; @@ -69,6 +70,7 @@ export function App() { + diff --git a/frontend/packages/swarm-ui/src/pages/ComponentsPage.css b/frontend/packages/swarm-ui/src/pages/ComponentsPage.css new file mode 100644 index 00000000..49d1e44c --- /dev/null +++ b/frontend/packages/swarm-ui/src/pages/ComponentsPage.css @@ -0,0 +1,44 @@ +/* layout — a plain vertical stack of sections, each a + labelled grid of samples. No new colour vars: reuses the same + base16 slots (../theme.css) every other component draws from, so + this page can't drift from the palette it's demonstrating. */ +.components-intro { + margin: 0 0 1.5em; + color: var(--muted); +} +.components-intro code { + color: var(--fg); +} +.components-section { + margin-bottom: 2em; +} +.components-section:last-child { + margin-bottom: 0; +} +.components-section-title { + margin: 0 0 0.75em; + font-size: 0.9em; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.04em; + color: var(--muted); +} +.components-section-body { + display: flex; + flex-direction: column; + gap: 1em; +} +.components-chip-row { + display: flex; + flex-wrap: wrap; + gap: 1em; +} +.components-sample { + display: flex; + flex-direction: column; + gap: 0.4em; +} +.components-sample-label { + font-size: 0.8em; + color: var(--muted); +} diff --git a/frontend/packages/swarm-ui/src/pages/ComponentsPage.tsx b/frontend/packages/swarm-ui/src/pages/ComponentsPage.tsx new file mode 100644 index 00000000..1159b62c --- /dev/null +++ b/frontend/packages/swarm-ui/src/pages/ComponentsPage.tsx @@ -0,0 +1,87 @@ +// — living style guide for every ui primitive this +// package ships. One section per component, each mode it supports +// rendered side by side with a caption naming the mode — so a visual +// regression or a missing variant is obvious at a glance, and a new +// primitive gets a section here the same day it's added. Sample data +// only, no network calls — this page must render the same whether +// swarm-controller's API is up or not. +import type { ComponentChildren } from 'preact'; +import { Panel } from '../ui/panel/Panel.js'; +import { StatusChip, type ChipTone } from '../ui/status-chip/StatusChip.js'; +import { Table, type TableColumn } from '../ui/table/Table.js'; +import './ComponentsPage.css'; + +function Section({ title, children }: { title: string; children: ComponentChildren }) { + return ( +
+

{title}

+
{children}
+
+ ); +} + +function Sample({ label, children }: { label: string; children: ComponentChildren }) { + return ( +
+
{label}
+
{children}
+
+ ); +} + +const CHIP_TONES: ChipTone[] = ['neutral', 'positive', 'warning', 'negative']; + +interface Row { + name: string; + detail: string; +} + +const TABLE_COLUMNS: TableColumn[] = [ + { key: 'name', header: 'name', render: (r) => r.name }, + { key: 'detail', header: 'detail', render: (r) => r.detail }, + { key: 'status', header: 'status', render: () => }, +]; + +const TABLE_ROWS: Row[] = [ + { name: 'alpha', detail: 'sample row one' }, + { name: 'beta', detail: 'sample row two' }, +]; + +export function ComponentsPage() { + return ( + +

+ Every primitive in src/ui/, shown in each mode it supports. Sample data + only — nothing here calls the API. +

+ +
+ + panel body content + + + panel body content, no title + +
+ +
+
+ {CHIP_TONES.map((tone) => ( + + + + ))} +
+
+ +
+ + r.name} /> + + +
r.name} /> + + + + ); +} diff --git a/frontend/packages/swarm-ui/src/shell/Shell.tsx b/frontend/packages/swarm-ui/src/shell/Shell.tsx index 9a25878e..efdee4be 100644 --- a/frontend/packages/swarm-ui/src/shell/Shell.tsx +++ b/frontend/packages/swarm-ui/src/shell/Shell.tsx @@ -16,7 +16,10 @@ import type { ComponentChildren } from 'preact'; import { Link, useRoute } from 'wouter-preact'; import './Shell.css'; -const NAV_ITEMS: { href: string; label: string }[] = [{ href: '/', label: 'overview' }]; +const NAV_ITEMS: { href: string; label: string }[] = [ + { href: '/', label: 'overview' }, + { href: '/components', label: 'components' }, +]; function NavLink({ href, label }: { href: string; label: string }) { const [active] = useRoute(href);