From 32ad2cebe474535a44841f128398b87a4a4b2e44 Mon Sep 17 00:00:00 2001 From: vince Date: Sat, 20 Jun 2026 06:43:09 +0200 Subject: [PATCH] refactor(ui): organize component source tree --- README.md | 3 +- .../src/components/{ => foundation}/Badge.tsx | 2 +- .../components/{ => foundation}/Button.tsx | 0 .../{ => foundation}/IconButton.tsx | 0 .../components/{ => foundation}/IconGlyph.tsx | 0 .../{ => foundation}/ProgressMeter.tsx | 4 +- .../components/{ => foundation}/Separator.tsx | 0 .../{ => foundation}/StatusBadge.tsx | 2 +- .../{ => foundation}/ThemeToggle.tsx | 4 +- .../{ => frames}/CornerBracketFrame.tsx | 0 .../{ => frames}/DashboardFrame.tsx | 8 +- .../{ => frames}/DashboardHeader.tsx | 2 +- .../{ => frames}/DiagonalStripeField.tsx | 0 .../components/{ => frames}/FooterCell.tsx | 2 +- .../{ => frames}/FooterStatusCell.tsx | 2 +- .../src/components/{ => frames}/GridFrame.tsx | 0 .../components/{ => frames}/ModuleCard.tsx | 4 +- .../ui/src/components/{ => frames}/Panel.tsx | 0 .../components/{ => frames}/ScanlineField.tsx | 0 .../{ => operations}/ServiceGroupPanel.tsx | 2 +- .../{ => operations}/ServicePanel.tsx | 4 +- .../{ => operations}/ServiceRow.tsx | 6 +- .../{ => operations}/StatusStrip.tsx | 4 +- .../{ => operations}/SystemState.tsx | 4 +- .../{ => operations}/WeatherModule.tsx | 4 +- packages/ui/src/components/render.test.tsx | 20 ++--- .../components/{ => telemetry}/LineChart.tsx | 2 +- .../{ => telemetry}/SignalTrace.tsx | 0 .../components/{ => telemetry}/Sparkline.tsx | 2 +- .../{ => telemetry}/TelemetryCard.tsx | 6 +- .../{ => telemetry}/TelemetryGrid.tsx | 2 +- .../{ => telemetry}/TelemetryStrip.tsx | 2 +- packages/ui/src/content-boundary.test.ts | 25 ++++-- packages/ui/src/index.ts | 60 +++++++------- packages/ui/src/stories/Badge.stories.tsx | 2 +- packages/ui/src/stories/Button.stories.tsx | 2 +- .../stories/CornerBracketFrame.stories.tsx | 2 +- .../ui/src/stories/DashboardFrame.stories.tsx | 2 +- .../src/stories/DashboardHeader.stories.tsx | 2 +- .../src/stories/DashboardOnePager.stories.tsx | 2 +- .../stories/DiagonalStripeField.stories.tsx | 2 +- .../ui/src/stories/FooterCell.stories.tsx | 2 +- .../src/stories/FooterStatusCell.stories.tsx | 2 +- packages/ui/src/stories/GridFrame.stories.tsx | 3 +- .../ui/src/stories/IconButton.stories.tsx | 2 +- packages/ui/src/stories/IconGlyph.stories.tsx | 2 +- packages/ui/src/stories/LineChart.stories.tsx | 2 +- .../ui/src/stories/ModuleCard.stories.tsx | 2 +- packages/ui/src/stories/Panel.stories.tsx | 3 +- .../ui/src/stories/ProgressMeter.stories.tsx | 2 +- .../ui/src/stories/ScanlineField.stories.tsx | 2 +- packages/ui/src/stories/Separator.stories.tsx | 2 +- .../src/stories/ServiceGroupPanel.stories.tsx | 2 +- .../ui/src/stories/ServicePanel.stories.tsx | 2 +- .../ui/src/stories/ServiceRow.stories.tsx | 2 +- .../ui/src/stories/SignalTrace.stories.tsx | 2 +- packages/ui/src/stories/Sparkline.stories.tsx | 2 +- .../ui/src/stories/StatusBadge.stories.tsx | 2 +- .../ui/src/stories/StatusStrip.stories.tsx | 2 +- .../ui/src/stories/SystemState.stories.tsx | 2 +- .../ui/src/stories/TelemetryCard.stories.tsx | 2 +- .../ui/src/stories/TelemetryGrid.stories.tsx | 2 +- .../ui/src/stories/TelemetryStrip.stories.tsx | 2 +- .../ui/src/stories/ThemeToggle.stories.tsx | 2 +- .../ui/src/stories/WeatherModule.stories.tsx | 2 +- packages/ui/src/storybook.test.ts | 78 ++++++++++++++++--- 66 files changed, 194 insertions(+), 124 deletions(-) rename packages/ui/src/components/{ => foundation}/Badge.tsx (84%) rename packages/ui/src/components/{ => foundation}/Button.tsx (100%) rename packages/ui/src/components/{ => foundation}/IconButton.tsx (100%) rename packages/ui/src/components/{ => foundation}/IconGlyph.tsx (100%) rename packages/ui/src/components/{ => foundation}/ProgressMeter.tsx (88%) rename packages/ui/src/components/{ => foundation}/Separator.tsx (100%) rename packages/ui/src/components/{ => foundation}/StatusBadge.tsx (85%) rename packages/ui/src/components/{ => foundation}/ThemeToggle.tsx (92%) rename packages/ui/src/components/{ => frames}/CornerBracketFrame.tsx (100%) rename packages/ui/src/components/{ => frames}/DashboardFrame.tsx (88%) rename packages/ui/src/components/{ => frames}/DashboardHeader.tsx (92%) rename packages/ui/src/components/{ => frames}/DiagonalStripeField.tsx (100%) rename packages/ui/src/components/{ => frames}/FooterCell.tsx (94%) rename packages/ui/src/components/{ => frames}/FooterStatusCell.tsx (75%) rename packages/ui/src/components/{ => frames}/GridFrame.tsx (100%) rename packages/ui/src/components/{ => frames}/ModuleCard.tsx (89%) rename packages/ui/src/components/{ => frames}/Panel.tsx (100%) rename packages/ui/src/components/{ => frames}/ScanlineField.tsx (100%) rename packages/ui/src/components/{ => operations}/ServiceGroupPanel.tsx (76%) rename packages/ui/src/components/{ => operations}/ServicePanel.tsx (87%) rename packages/ui/src/components/{ => operations}/ServiceRow.tsx (89%) rename packages/ui/src/components/{ => operations}/StatusStrip.tsx (82%) rename packages/ui/src/components/{ => operations}/SystemState.tsx (82%) rename packages/ui/src/components/{ => operations}/WeatherModule.tsx (53%) rename packages/ui/src/components/{ => telemetry}/LineChart.tsx (98%) rename packages/ui/src/components/{ => telemetry}/SignalTrace.tsx (100%) rename packages/ui/src/components/{ => telemetry}/Sparkline.tsx (94%) rename packages/ui/src/components/{ => telemetry}/TelemetryCard.tsx (91%) rename packages/ui/src/components/{ => telemetry}/TelemetryGrid.tsx (94%) rename packages/ui/src/components/{ => telemetry}/TelemetryStrip.tsx (76%) diff --git a/README.md b/README.md index 98dc8ec..572f9bc 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,8 @@ validated dashboard model state inside the web app. - `packages/dashboard-model`: reusable dashboard schema, validation, and generic model fixtures shared by apps and tooling. - `packages/ui`: reusable dashboard React components, design tokens, - shadcn/radix primitives, generic fixtures, and Storybook. + shadcn/radix primitives, generic fixtures, and Storybook. Component source is + grouped under `foundation`, `frames`, `operations`, and `telemetry` domains. - `docs/superpowers`: migration specs and execution plans used for this repo. ## Development diff --git a/packages/ui/src/components/Badge.tsx b/packages/ui/src/components/foundation/Badge.tsx similarity index 84% rename from packages/ui/src/components/Badge.tsx rename to packages/ui/src/components/foundation/Badge.tsx index a21ade8..5bdee48 100644 --- a/packages/ui/src/components/Badge.tsx +++ b/packages/ui/src/components/foundation/Badge.tsx @@ -1,4 +1,4 @@ -import type { UiSeverity } from "../types"; +import type { UiSeverity } from "../../types"; import { StatusBadge } from "./StatusBadge"; export interface BadgeProps { diff --git a/packages/ui/src/components/Button.tsx b/packages/ui/src/components/foundation/Button.tsx similarity index 100% rename from packages/ui/src/components/Button.tsx rename to packages/ui/src/components/foundation/Button.tsx diff --git a/packages/ui/src/components/IconButton.tsx b/packages/ui/src/components/foundation/IconButton.tsx similarity index 100% rename from packages/ui/src/components/IconButton.tsx rename to packages/ui/src/components/foundation/IconButton.tsx diff --git a/packages/ui/src/components/IconGlyph.tsx b/packages/ui/src/components/foundation/IconGlyph.tsx similarity index 100% rename from packages/ui/src/components/IconGlyph.tsx rename to packages/ui/src/components/foundation/IconGlyph.tsx diff --git a/packages/ui/src/components/ProgressMeter.tsx b/packages/ui/src/components/foundation/ProgressMeter.tsx similarity index 88% rename from packages/ui/src/components/ProgressMeter.tsx rename to packages/ui/src/components/foundation/ProgressMeter.tsx index c7278bf..c3f3aad 100644 --- a/packages/ui/src/components/ProgressMeter.tsx +++ b/packages/ui/src/components/foundation/ProgressMeter.tsx @@ -1,6 +1,6 @@ import type { CSSProperties } from "react"; -import { clampPercent } from "../format"; -import type { UiSeverity } from "../types"; +import { clampPercent } from "../../format"; +import type { UiSeverity } from "../../types"; export interface ProgressMeterProps { value?: number; diff --git a/packages/ui/src/components/Separator.tsx b/packages/ui/src/components/foundation/Separator.tsx similarity index 100% rename from packages/ui/src/components/Separator.tsx rename to packages/ui/src/components/foundation/Separator.tsx diff --git a/packages/ui/src/components/StatusBadge.tsx b/packages/ui/src/components/foundation/StatusBadge.tsx similarity index 85% rename from packages/ui/src/components/StatusBadge.tsx rename to packages/ui/src/components/foundation/StatusBadge.tsx index fb63a04..d2b01b3 100644 --- a/packages/ui/src/components/StatusBadge.tsx +++ b/packages/ui/src/components/foundation/StatusBadge.tsx @@ -1,4 +1,4 @@ -import type { UiSeverity } from "../types"; +import type { UiSeverity } from "../../types"; export interface StatusBadgeProps { label: string; diff --git a/packages/ui/src/components/ThemeToggle.tsx b/packages/ui/src/components/foundation/ThemeToggle.tsx similarity index 92% rename from packages/ui/src/components/ThemeToggle.tsx rename to packages/ui/src/components/foundation/ThemeToggle.tsx index 9e883af..5d83031 100644 --- a/packages/ui/src/components/ThemeToggle.tsx +++ b/packages/ui/src/components/foundation/ThemeToggle.tsx @@ -1,5 +1,5 @@ -import type { UiTheme } from "../theme"; -import { getNextUiTheme } from "../theme"; +import type { UiTheme } from "../../theme"; +import { getNextUiTheme } from "../../theme"; import { IconGlyph } from "./IconGlyph"; export interface ThemeToggleProps { diff --git a/packages/ui/src/components/CornerBracketFrame.tsx b/packages/ui/src/components/frames/CornerBracketFrame.tsx similarity index 100% rename from packages/ui/src/components/CornerBracketFrame.tsx rename to packages/ui/src/components/frames/CornerBracketFrame.tsx diff --git a/packages/ui/src/components/DashboardFrame.tsx b/packages/ui/src/components/frames/DashboardFrame.tsx similarity index 88% rename from packages/ui/src/components/DashboardFrame.tsx rename to packages/ui/src/components/frames/DashboardFrame.tsx index cdf312a..08029cf 100644 --- a/packages/ui/src/components/DashboardFrame.tsx +++ b/packages/ui/src/components/frames/DashboardFrame.tsx @@ -1,10 +1,10 @@ import { useId } from "react"; import type { ReactNode } from "react"; -import type { UiDashboardPreview } from "../types"; +import type { UiDashboardPreview } from "../../types"; import { ModuleCard } from "./ModuleCard"; -import { ServicePanel } from "./ServicePanel"; -import { StatusStrip } from "./StatusStrip"; -import { TelemetryGrid } from "./TelemetryGrid"; +import { ServicePanel } from "../operations/ServicePanel"; +import { StatusStrip } from "../operations/StatusStrip"; +import { TelemetryGrid } from "../telemetry/TelemetryGrid"; export interface DashboardFrameProps { actions?: ReactNode; diff --git a/packages/ui/src/components/DashboardHeader.tsx b/packages/ui/src/components/frames/DashboardHeader.tsx similarity index 92% rename from packages/ui/src/components/DashboardHeader.tsx rename to packages/ui/src/components/frames/DashboardHeader.tsx index 68b4e11..3f418fa 100644 --- a/packages/ui/src/components/DashboardHeader.tsx +++ b/packages/ui/src/components/frames/DashboardHeader.tsx @@ -1,5 +1,5 @@ import { useId } from "react"; -import type { UiModuleBlock } from "../types"; +import type { UiModuleBlock } from "../../types"; import { ModuleCard } from "./ModuleCard"; export interface DashboardHeaderProps { diff --git a/packages/ui/src/components/DiagonalStripeField.tsx b/packages/ui/src/components/frames/DiagonalStripeField.tsx similarity index 100% rename from packages/ui/src/components/DiagonalStripeField.tsx rename to packages/ui/src/components/frames/DiagonalStripeField.tsx diff --git a/packages/ui/src/components/FooterCell.tsx b/packages/ui/src/components/frames/FooterCell.tsx similarity index 94% rename from packages/ui/src/components/FooterCell.tsx rename to packages/ui/src/components/frames/FooterCell.tsx index 760e853..8b9a86d 100644 --- a/packages/ui/src/components/FooterCell.tsx +++ b/packages/ui/src/components/frames/FooterCell.tsx @@ -1,4 +1,4 @@ -import type { UiStatusItem } from "../types"; +import type { UiStatusItem } from "../../types"; export interface FooterCellProps { item: UiStatusItem; diff --git a/packages/ui/src/components/FooterStatusCell.tsx b/packages/ui/src/components/frames/FooterStatusCell.tsx similarity index 75% rename from packages/ui/src/components/FooterStatusCell.tsx rename to packages/ui/src/components/frames/FooterStatusCell.tsx index dcfb8b4..b5b339e 100644 --- a/packages/ui/src/components/FooterStatusCell.tsx +++ b/packages/ui/src/components/frames/FooterStatusCell.tsx @@ -1,4 +1,4 @@ -import type { UiStatusItem } from "../types"; +import type { UiStatusItem } from "../../types"; import { FooterCell } from "./FooterCell"; export function FooterStatusCell({ item }: { item: UiStatusItem }) { diff --git a/packages/ui/src/components/GridFrame.tsx b/packages/ui/src/components/frames/GridFrame.tsx similarity index 100% rename from packages/ui/src/components/GridFrame.tsx rename to packages/ui/src/components/frames/GridFrame.tsx diff --git a/packages/ui/src/components/ModuleCard.tsx b/packages/ui/src/components/frames/ModuleCard.tsx similarity index 89% rename from packages/ui/src/components/ModuleCard.tsx rename to packages/ui/src/components/frames/ModuleCard.tsx index d05bee0..9b96df9 100644 --- a/packages/ui/src/components/ModuleCard.tsx +++ b/packages/ui/src/components/frames/ModuleCard.tsx @@ -1,6 +1,6 @@ import { useId } from "react"; -import type { UiModuleBlock } from "../types"; -import { IconGlyph } from "./IconGlyph"; +import type { UiModuleBlock } from "../../types"; +import { IconGlyph } from "../foundation/IconGlyph"; export interface ModuleCardProps { module: UiModuleBlock; diff --git a/packages/ui/src/components/Panel.tsx b/packages/ui/src/components/frames/Panel.tsx similarity index 100% rename from packages/ui/src/components/Panel.tsx rename to packages/ui/src/components/frames/Panel.tsx diff --git a/packages/ui/src/components/ScanlineField.tsx b/packages/ui/src/components/frames/ScanlineField.tsx similarity index 100% rename from packages/ui/src/components/ScanlineField.tsx rename to packages/ui/src/components/frames/ScanlineField.tsx diff --git a/packages/ui/src/components/ServiceGroupPanel.tsx b/packages/ui/src/components/operations/ServiceGroupPanel.tsx similarity index 76% rename from packages/ui/src/components/ServiceGroupPanel.tsx rename to packages/ui/src/components/operations/ServiceGroupPanel.tsx index 4252cc9..9b53340 100644 --- a/packages/ui/src/components/ServiceGroupPanel.tsx +++ b/packages/ui/src/components/operations/ServiceGroupPanel.tsx @@ -1,4 +1,4 @@ -import type { UiServiceGroup } from "../types"; +import type { UiServiceGroup } from "../../types"; import { ServicePanel } from "./ServicePanel"; export function ServiceGroupPanel({ group }: { group: UiServiceGroup }) { diff --git a/packages/ui/src/components/ServicePanel.tsx b/packages/ui/src/components/operations/ServicePanel.tsx similarity index 87% rename from packages/ui/src/components/ServicePanel.tsx rename to packages/ui/src/components/operations/ServicePanel.tsx index 12a2e74..c8b85af 100644 --- a/packages/ui/src/components/ServicePanel.tsx +++ b/packages/ui/src/components/operations/ServicePanel.tsx @@ -1,5 +1,5 @@ -import type { UiServiceGroup } from "../types"; -import { Panel } from "./Panel"; +import type { UiServiceGroup } from "../../types"; +import { Panel } from "../frames/Panel"; import { ServiceRow } from "./ServiceRow"; import { StatusStrip } from "./StatusStrip"; diff --git a/packages/ui/src/components/ServiceRow.tsx b/packages/ui/src/components/operations/ServiceRow.tsx similarity index 89% rename from packages/ui/src/components/ServiceRow.tsx rename to packages/ui/src/components/operations/ServiceRow.tsx index cf065f4..e6e5aa9 100644 --- a/packages/ui/src/components/ServiceRow.tsx +++ b/packages/ui/src/components/operations/ServiceRow.tsx @@ -1,6 +1,6 @@ -import type { UiServiceRow } from "../types"; -import { IconGlyph } from "./IconGlyph"; -import { StatusBadge } from "./StatusBadge"; +import type { UiServiceRow } from "../../types"; +import { IconGlyph } from "../foundation/IconGlyph"; +import { StatusBadge } from "../foundation/StatusBadge"; export interface ServiceRowProps { service: UiServiceRow; diff --git a/packages/ui/src/components/StatusStrip.tsx b/packages/ui/src/components/operations/StatusStrip.tsx similarity index 82% rename from packages/ui/src/components/StatusStrip.tsx rename to packages/ui/src/components/operations/StatusStrip.tsx index 66c6451..3f2bca2 100644 --- a/packages/ui/src/components/StatusStrip.tsx +++ b/packages/ui/src/components/operations/StatusStrip.tsx @@ -1,5 +1,5 @@ -import type { UiStatusItem } from "../types"; -import { FooterCell } from "./FooterCell"; +import type { UiStatusItem } from "../../types"; +import { FooterCell } from "../frames/FooterCell"; export interface StatusStripProps { id?: string; diff --git a/packages/ui/src/components/SystemState.tsx b/packages/ui/src/components/operations/SystemState.tsx similarity index 82% rename from packages/ui/src/components/SystemState.tsx rename to packages/ui/src/components/operations/SystemState.tsx index e817e2b..438fa52 100644 --- a/packages/ui/src/components/SystemState.tsx +++ b/packages/ui/src/components/operations/SystemState.tsx @@ -1,5 +1,5 @@ -import type { UiSeverity } from "../types"; -import { IconGlyph } from "./IconGlyph"; +import type { UiSeverity } from "../../types"; +import { IconGlyph } from "../foundation/IconGlyph"; export interface SystemStateProps { title: string; diff --git a/packages/ui/src/components/WeatherModule.tsx b/packages/ui/src/components/operations/WeatherModule.tsx similarity index 53% rename from packages/ui/src/components/WeatherModule.tsx rename to packages/ui/src/components/operations/WeatherModule.tsx index 0967cfb..c7e55db 100644 --- a/packages/ui/src/components/WeatherModule.tsx +++ b/packages/ui/src/components/operations/WeatherModule.tsx @@ -1,5 +1,5 @@ -import type { UiModuleBlock } from "../types"; -import { ModuleCard } from "./ModuleCard"; +import type { UiModuleBlock } from "../../types"; +import { ModuleCard } from "../frames/ModuleCard"; export function WeatherModule({ module }: { module: UiModuleBlock }) { return ; diff --git a/packages/ui/src/components/render.test.tsx b/packages/ui/src/components/render.test.tsx index 1cf40fe..010f672 100644 --- a/packages/ui/src/components/render.test.tsx +++ b/packages/ui/src/components/render.test.tsx @@ -1,14 +1,16 @@ import { renderToString } from "react-dom/server"; import { describe, expect, test } from "vitest"; -import { Button } from "./Button"; -import { DashboardFrame } from "./DashboardFrame"; -import { FooterCell } from "./FooterCell"; -import { IconButton } from "./IconButton"; -import { ServiceRow } from "./ServiceRow"; -import { StatusStrip } from "./StatusStrip"; -import { TelemetryCard } from "./TelemetryCard"; -import { TelemetryGrid } from "./TelemetryGrid"; -import { ThemeToggle } from "./ThemeToggle"; +import { + Button, + DashboardFrame, + FooterCell, + IconButton, + ServiceRow, + StatusStrip, + TelemetryCard, + TelemetryGrid, + ThemeToggle, +} from "../index"; import { dashboardPreviewFixtures } from "../fixtures"; describe("dashboard UI components", () => { diff --git a/packages/ui/src/components/LineChart.tsx b/packages/ui/src/components/telemetry/LineChart.tsx similarity index 98% rename from packages/ui/src/components/LineChart.tsx rename to packages/ui/src/components/telemetry/LineChart.tsx index 386b515..0accdf7 100644 --- a/packages/ui/src/components/LineChart.tsx +++ b/packages/ui/src/components/telemetry/LineChart.tsx @@ -1,6 +1,6 @@ import "uplot/dist/uPlot.min.css"; import { useEffect, useRef } from "react"; -import type { UiSeverity } from "../types"; +import type { UiSeverity } from "../../types"; export interface LineChartProps { values?: number[]; diff --git a/packages/ui/src/components/SignalTrace.tsx b/packages/ui/src/components/telemetry/SignalTrace.tsx similarity index 100% rename from packages/ui/src/components/SignalTrace.tsx rename to packages/ui/src/components/telemetry/SignalTrace.tsx diff --git a/packages/ui/src/components/Sparkline.tsx b/packages/ui/src/components/telemetry/Sparkline.tsx similarity index 94% rename from packages/ui/src/components/Sparkline.tsx rename to packages/ui/src/components/telemetry/Sparkline.tsx index a79f532..d03fa13 100644 --- a/packages/ui/src/components/Sparkline.tsx +++ b/packages/ui/src/components/telemetry/Sparkline.tsx @@ -1,4 +1,4 @@ -import type { UiSeverity } from "../types"; +import type { UiSeverity } from "../../types"; export interface SparklineProps { values?: number[]; diff --git a/packages/ui/src/components/TelemetryCard.tsx b/packages/ui/src/components/telemetry/TelemetryCard.tsx similarity index 91% rename from packages/ui/src/components/TelemetryCard.tsx rename to packages/ui/src/components/telemetry/TelemetryCard.tsx index 97033c9..15481ee 100644 --- a/packages/ui/src/components/TelemetryCard.tsx +++ b/packages/ui/src/components/telemetry/TelemetryCard.tsx @@ -1,7 +1,7 @@ import type { CSSProperties } from "react"; -import { clampPercent, formatMetricValue } from "../format"; -import type { UiTelemetryCard } from "../types"; -import { IconGlyph } from "./IconGlyph"; +import { clampPercent, formatMetricValue } from "../../format"; +import type { UiTelemetryCard } from "../../types"; +import { IconGlyph } from "../foundation/IconGlyph"; import { LineChart } from "./LineChart"; export interface TelemetryCardProps { diff --git a/packages/ui/src/components/TelemetryGrid.tsx b/packages/ui/src/components/telemetry/TelemetryGrid.tsx similarity index 94% rename from packages/ui/src/components/TelemetryGrid.tsx rename to packages/ui/src/components/telemetry/TelemetryGrid.tsx index 8afb9a2..9f4d497 100644 --- a/packages/ui/src/components/TelemetryGrid.tsx +++ b/packages/ui/src/components/telemetry/TelemetryGrid.tsx @@ -1,5 +1,5 @@ import { useId } from "react"; -import type { UiTelemetryCard } from "../types"; +import type { UiTelemetryCard } from "../../types"; import { TelemetryCard } from "./TelemetryCard"; export interface TelemetryGridProps { diff --git a/packages/ui/src/components/TelemetryStrip.tsx b/packages/ui/src/components/telemetry/TelemetryStrip.tsx similarity index 76% rename from packages/ui/src/components/TelemetryStrip.tsx rename to packages/ui/src/components/telemetry/TelemetryStrip.tsx index 765c1e2..35073bb 100644 --- a/packages/ui/src/components/TelemetryStrip.tsx +++ b/packages/ui/src/components/telemetry/TelemetryStrip.tsx @@ -1,4 +1,4 @@ -import type { UiTelemetryCard } from "../types"; +import type { UiTelemetryCard } from "../../types"; import { TelemetryGrid } from "./TelemetryGrid"; export function TelemetryStrip({ cards }: { cards: UiTelemetryCard[] }) { diff --git a/packages/ui/src/content-boundary.test.ts b/packages/ui/src/content-boundary.test.ts index 80b85ce..f7188a1 100644 --- a/packages/ui/src/content-boundary.test.ts +++ b/packages/ui/src/content-boundary.test.ts @@ -26,15 +26,28 @@ const forbiddenTerms = [ describe("UI package content boundary", () => { test("contains the reusable dashboard component inventory", () => { expect(existsSync(join(uiSourceRoot, "index.ts"))).toBe(true); - expect(existsSync(join(uiSourceRoot, "components/DashboardFrame.tsx"))).toBe( - true, - ); - expect(existsSync(join(uiSourceRoot, "components/ThemeToggle.tsx"))).toBe( - true, - ); + expect( + existsSync(join(uiSourceRoot, "components/frames/DashboardFrame.tsx")), + ).toBe(true); + expect( + existsSync(join(uiSourceRoot, "components/foundation/ThemeToggle.tsx")), + ).toBe(true); expect(existsSync(join(uiSourceRoot, "styles.css"))).toBe(true); }); + test("keeps UI components grouped by domain instead of a flat bucket", () => { + const domainFolders = ["foundation", "frames", "operations", "telemetry"]; + + for (const folder of domainFolders) { + expect(existsSync(join(uiSourceRoot, "components", folder))).toBe(true); + } + + const flatComponentFiles = readdirSync(join(uiSourceRoot, "components")) + .filter((entry) => entry.endsWith(".tsx") && !entry.endsWith(".test.tsx")) + .sort(); + expect(flatComponentFiles).toEqual([]); + }); + test("keeps environment-specific content out of reusable UI source", () => { const source = readUiSource(uiSourceRoot).toLowerCase(); diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index a9a83df..bc506c0 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -1,33 +1,33 @@ -export { Badge } from "./components/Badge"; -export { Button } from "./components/Button"; -export { CornerBracketFrame } from "./components/CornerBracketFrame"; -export { DashboardHeader } from "./components/DashboardHeader"; -export { DashboardFrame } from "./components/DashboardFrame"; -export { DiagonalStripeField } from "./components/DiagonalStripeField"; -export { FooterCell } from "./components/FooterCell"; -export { FooterStatusCell } from "./components/FooterStatusCell"; -export { GridFrame } from "./components/GridFrame"; -export { IconGlyph } from "./components/IconGlyph"; -export { IconButton } from "./components/IconButton"; -export { LineChart } from "./components/LineChart"; -export { ModuleCard } from "./components/ModuleCard"; -export { Panel } from "./components/Panel"; -export { ProgressMeter } from "./components/ProgressMeter"; -export { Separator } from "./components/Separator"; -export { ServiceGroupPanel } from "./components/ServiceGroupPanel"; -export { ServicePanel } from "./components/ServicePanel"; -export { ServiceRow } from "./components/ServiceRow"; -export { ScanlineField } from "./components/ScanlineField"; -export { SignalTrace } from "./components/SignalTrace"; -export { Sparkline } from "./components/Sparkline"; -export { StatusBadge } from "./components/StatusBadge"; -export { StatusStrip } from "./components/StatusStrip"; -export { SystemState } from "./components/SystemState"; -export { TelemetryCard } from "./components/TelemetryCard"; -export { TelemetryGrid } from "./components/TelemetryGrid"; -export { TelemetryStrip } from "./components/TelemetryStrip"; -export { ThemeToggle } from "./components/ThemeToggle"; -export { WeatherModule } from "./components/WeatherModule"; +export { Badge } from "./components/foundation/Badge"; +export { Button } from "./components/foundation/Button"; +export { IconGlyph } from "./components/foundation/IconGlyph"; +export { IconButton } from "./components/foundation/IconButton"; +export { ProgressMeter } from "./components/foundation/ProgressMeter"; +export { Separator } from "./components/foundation/Separator"; +export { StatusBadge } from "./components/foundation/StatusBadge"; +export { ThemeToggle } from "./components/foundation/ThemeToggle"; +export { CornerBracketFrame } from "./components/frames/CornerBracketFrame"; +export { DashboardHeader } from "./components/frames/DashboardHeader"; +export { DashboardFrame } from "./components/frames/DashboardFrame"; +export { DiagonalStripeField } from "./components/frames/DiagonalStripeField"; +export { FooterCell } from "./components/frames/FooterCell"; +export { FooterStatusCell } from "./components/frames/FooterStatusCell"; +export { GridFrame } from "./components/frames/GridFrame"; +export { ModuleCard } from "./components/frames/ModuleCard"; +export { Panel } from "./components/frames/Panel"; +export { ScanlineField } from "./components/frames/ScanlineField"; +export { ServiceGroupPanel } from "./components/operations/ServiceGroupPanel"; +export { ServicePanel } from "./components/operations/ServicePanel"; +export { ServiceRow } from "./components/operations/ServiceRow"; +export { StatusStrip } from "./components/operations/StatusStrip"; +export { SystemState } from "./components/operations/SystemState"; +export { WeatherModule } from "./components/operations/WeatherModule"; +export { LineChart } from "./components/telemetry/LineChart"; +export { SignalTrace } from "./components/telemetry/SignalTrace"; +export { Sparkline } from "./components/telemetry/Sparkline"; +export { TelemetryCard } from "./components/telemetry/TelemetryCard"; +export { TelemetryGrid } from "./components/telemetry/TelemetryGrid"; +export { TelemetryStrip } from "./components/telemetry/TelemetryStrip"; export { dashboardPreviewFixtures } from "./fixtures"; export { getNextUiTheme, diff --git a/packages/ui/src/stories/Badge.stories.tsx b/packages/ui/src/stories/Badge.stories.tsx index d33394d..26e3ea8 100644 --- a/packages/ui/src/stories/Badge.stories.tsx +++ b/packages/ui/src/stories/Badge.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { Badge } from "../components/Badge"; +import { Badge } from "../index"; const meta = { title: "UI/Badge", diff --git a/packages/ui/src/stories/Button.stories.tsx b/packages/ui/src/stories/Button.stories.tsx index c207233..084caa8 100644 --- a/packages/ui/src/stories/Button.stories.tsx +++ b/packages/ui/src/stories/Button.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { Button } from "../components/Button"; +import { Button } from "../index"; const meta = { title: "UI/Button", diff --git a/packages/ui/src/stories/CornerBracketFrame.stories.tsx b/packages/ui/src/stories/CornerBracketFrame.stories.tsx index dd7efbf..d5c0110 100644 --- a/packages/ui/src/stories/CornerBracketFrame.stories.tsx +++ b/packages/ui/src/stories/CornerBracketFrame.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { CornerBracketFrame } from "../components/CornerBracketFrame"; +import { CornerBracketFrame } from "../index"; const meta = { title: "UI/CornerBracketFrame", diff --git a/packages/ui/src/stories/DashboardFrame.stories.tsx b/packages/ui/src/stories/DashboardFrame.stories.tsx index e1963af..894219e 100644 --- a/packages/ui/src/stories/DashboardFrame.stories.tsx +++ b/packages/ui/src/stories/DashboardFrame.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { DashboardFrame } from "../components/DashboardFrame"; +import { DashboardFrame } from "../index"; import { fullCompositionDashboard, secondaryDashboard } from "./story-data"; const meta = { diff --git a/packages/ui/src/stories/DashboardHeader.stories.tsx b/packages/ui/src/stories/DashboardHeader.stories.tsx index 8263dd1..c337158 100644 --- a/packages/ui/src/stories/DashboardHeader.stories.tsx +++ b/packages/ui/src/stories/DashboardHeader.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { DashboardHeader } from "../components/DashboardHeader"; +import { DashboardHeader } from "../index"; import { moduleBlocks } from "./story-data"; const meta = { diff --git a/packages/ui/src/stories/DashboardOnePager.stories.tsx b/packages/ui/src/stories/DashboardOnePager.stories.tsx index c44364b..3bd800f 100644 --- a/packages/ui/src/stories/DashboardOnePager.stories.tsx +++ b/packages/ui/src/stories/DashboardOnePager.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { DashboardFrame } from "../components/DashboardFrame"; +import { DashboardFrame } from "../index"; import { fullCompositionDashboard } from "./story-data"; const meta = { diff --git a/packages/ui/src/stories/DiagonalStripeField.stories.tsx b/packages/ui/src/stories/DiagonalStripeField.stories.tsx index 4ca01f2..1f094ee 100644 --- a/packages/ui/src/stories/DiagonalStripeField.stories.tsx +++ b/packages/ui/src/stories/DiagonalStripeField.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { DiagonalStripeField } from "../components/DiagonalStripeField"; +import { DiagonalStripeField } from "../index"; const meta = { title: "UI/DiagonalStripeField", diff --git a/packages/ui/src/stories/FooterCell.stories.tsx b/packages/ui/src/stories/FooterCell.stories.tsx index 3ba26c7..7aa06d8 100644 --- a/packages/ui/src/stories/FooterCell.stories.tsx +++ b/packages/ui/src/stories/FooterCell.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { FooterCell } from "../components/FooterCell"; +import { FooterCell } from "../index"; import { statusItems } from "./story-data"; const meta = { diff --git a/packages/ui/src/stories/FooterStatusCell.stories.tsx b/packages/ui/src/stories/FooterStatusCell.stories.tsx index 8097ba3..bd985a1 100644 --- a/packages/ui/src/stories/FooterStatusCell.stories.tsx +++ b/packages/ui/src/stories/FooterStatusCell.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { FooterStatusCell } from "../components/FooterStatusCell"; +import { FooterStatusCell } from "../index"; import { statusItems } from "./story-data"; const meta = { diff --git a/packages/ui/src/stories/GridFrame.stories.tsx b/packages/ui/src/stories/GridFrame.stories.tsx index a43ad22..191bf6c 100644 --- a/packages/ui/src/stories/GridFrame.stories.tsx +++ b/packages/ui/src/stories/GridFrame.stories.tsx @@ -1,6 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { GridFrame } from "../components/GridFrame"; -import { ModuleCard } from "../components/ModuleCard"; +import { GridFrame, ModuleCard } from "../index"; import { moduleBlocks } from "./story-data"; const meta = { diff --git a/packages/ui/src/stories/IconButton.stories.tsx b/packages/ui/src/stories/IconButton.stories.tsx index 35b58b9..612bf2a 100644 --- a/packages/ui/src/stories/IconButton.stories.tsx +++ b/packages/ui/src/stories/IconButton.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { IconButton } from "../components/IconButton"; +import { IconButton } from "../index"; const meta = { title: "UI/IconButton", diff --git a/packages/ui/src/stories/IconGlyph.stories.tsx b/packages/ui/src/stories/IconGlyph.stories.tsx index 3f1715a..d832951 100644 --- a/packages/ui/src/stories/IconGlyph.stories.tsx +++ b/packages/ui/src/stories/IconGlyph.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { IconGlyph } from "../components/IconGlyph"; +import { IconGlyph } from "../index"; const meta = { title: "UI/IconGlyph", diff --git a/packages/ui/src/stories/LineChart.stories.tsx b/packages/ui/src/stories/LineChart.stories.tsx index bbf8690..ccbd502 100644 --- a/packages/ui/src/stories/LineChart.stories.tsx +++ b/packages/ui/src/stories/LineChart.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { LineChart } from "../components/LineChart"; +import { LineChart } from "../index"; const meta = { title: "UI/LineChart", diff --git a/packages/ui/src/stories/ModuleCard.stories.tsx b/packages/ui/src/stories/ModuleCard.stories.tsx index 3366a55..ea0305b 100644 --- a/packages/ui/src/stories/ModuleCard.stories.tsx +++ b/packages/ui/src/stories/ModuleCard.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { ModuleCard } from "../components/ModuleCard"; +import { ModuleCard } from "../index"; import { moduleBlocks } from "./story-data"; const meta = { diff --git a/packages/ui/src/stories/Panel.stories.tsx b/packages/ui/src/stories/Panel.stories.tsx index 730c2d9..91eb61e 100644 --- a/packages/ui/src/stories/Panel.stories.tsx +++ b/packages/ui/src/stories/Panel.stories.tsx @@ -1,6 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { Panel } from "../components/Panel"; -import { ServiceRow } from "../components/ServiceRow"; +import { Panel, ServiceRow } from "../index"; import { serviceRows } from "./story-data"; const meta = { diff --git a/packages/ui/src/stories/ProgressMeter.stories.tsx b/packages/ui/src/stories/ProgressMeter.stories.tsx index d3bfdac..eec35c2 100644 --- a/packages/ui/src/stories/ProgressMeter.stories.tsx +++ b/packages/ui/src/stories/ProgressMeter.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { ProgressMeter } from "../components/ProgressMeter"; +import { ProgressMeter } from "../index"; const meta = { title: "UI/ProgressMeter", diff --git a/packages/ui/src/stories/ScanlineField.stories.tsx b/packages/ui/src/stories/ScanlineField.stories.tsx index 89e554e..fbe53f4 100644 --- a/packages/ui/src/stories/ScanlineField.stories.tsx +++ b/packages/ui/src/stories/ScanlineField.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { ScanlineField } from "../components/ScanlineField"; +import { ScanlineField } from "../index"; const meta = { title: "UI/ScanlineField", diff --git a/packages/ui/src/stories/Separator.stories.tsx b/packages/ui/src/stories/Separator.stories.tsx index e261f52..986ed23 100644 --- a/packages/ui/src/stories/Separator.stories.tsx +++ b/packages/ui/src/stories/Separator.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { Separator } from "../components/Separator"; +import { Separator } from "../index"; const meta = { title: "UI/Separator", diff --git a/packages/ui/src/stories/ServiceGroupPanel.stories.tsx b/packages/ui/src/stories/ServiceGroupPanel.stories.tsx index 7183771..f5ebdce 100644 --- a/packages/ui/src/stories/ServiceGroupPanel.stories.tsx +++ b/packages/ui/src/stories/ServiceGroupPanel.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { ServiceGroupPanel } from "../components/ServiceGroupPanel"; +import { ServiceGroupPanel } from "../index"; import { serviceGroups } from "./story-data"; const meta = { diff --git a/packages/ui/src/stories/ServicePanel.stories.tsx b/packages/ui/src/stories/ServicePanel.stories.tsx index 750c428..d29b7ad 100644 --- a/packages/ui/src/stories/ServicePanel.stories.tsx +++ b/packages/ui/src/stories/ServicePanel.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { ServicePanel } from "../components/ServicePanel"; +import { ServicePanel } from "../index"; import { serviceGroups } from "./story-data"; const meta = { diff --git a/packages/ui/src/stories/ServiceRow.stories.tsx b/packages/ui/src/stories/ServiceRow.stories.tsx index 9513d2c..56ba076 100644 --- a/packages/ui/src/stories/ServiceRow.stories.tsx +++ b/packages/ui/src/stories/ServiceRow.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { ServiceRow } from "../components/ServiceRow"; +import { ServiceRow } from "../index"; import { serviceRows } from "./story-data"; const meta = { diff --git a/packages/ui/src/stories/SignalTrace.stories.tsx b/packages/ui/src/stories/SignalTrace.stories.tsx index ae8785b..4089df8 100644 --- a/packages/ui/src/stories/SignalTrace.stories.tsx +++ b/packages/ui/src/stories/SignalTrace.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { SignalTrace } from "../components/SignalTrace"; +import { SignalTrace } from "../index"; const meta = { title: "UI/SignalTrace", diff --git a/packages/ui/src/stories/Sparkline.stories.tsx b/packages/ui/src/stories/Sparkline.stories.tsx index efecfbd..af3e6e5 100644 --- a/packages/ui/src/stories/Sparkline.stories.tsx +++ b/packages/ui/src/stories/Sparkline.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { Sparkline } from "../components/Sparkline"; +import { Sparkline } from "../index"; const meta = { title: "UI/Sparkline", diff --git a/packages/ui/src/stories/StatusBadge.stories.tsx b/packages/ui/src/stories/StatusBadge.stories.tsx index 4513a08..92a444d 100644 --- a/packages/ui/src/stories/StatusBadge.stories.tsx +++ b/packages/ui/src/stories/StatusBadge.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { StatusBadge } from "../components/StatusBadge"; +import { StatusBadge } from "../index"; const meta = { title: "UI/StatusBadge", diff --git a/packages/ui/src/stories/StatusStrip.stories.tsx b/packages/ui/src/stories/StatusStrip.stories.tsx index 7b808fa..2fc236d 100644 --- a/packages/ui/src/stories/StatusStrip.stories.tsx +++ b/packages/ui/src/stories/StatusStrip.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { StatusStrip } from "../components/StatusStrip"; +import { StatusStrip } from "../index"; import { mixedStatusStrip } from "./story-data"; const meta = { diff --git a/packages/ui/src/stories/SystemState.stories.tsx b/packages/ui/src/stories/SystemState.stories.tsx index 585540e..9b3f6e5 100644 --- a/packages/ui/src/stories/SystemState.stories.tsx +++ b/packages/ui/src/stories/SystemState.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { SystemState } from "../components/SystemState"; +import { SystemState } from "../index"; const meta = { title: "UI/SystemState", diff --git a/packages/ui/src/stories/TelemetryCard.stories.tsx b/packages/ui/src/stories/TelemetryCard.stories.tsx index dbdf301..3ac0e1e 100644 --- a/packages/ui/src/stories/TelemetryCard.stories.tsx +++ b/packages/ui/src/stories/TelemetryCard.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { TelemetryCard } from "../components/TelemetryCard"; +import { TelemetryCard } from "../index"; import { telemetryCards } from "./story-data"; const meta = { diff --git a/packages/ui/src/stories/TelemetryGrid.stories.tsx b/packages/ui/src/stories/TelemetryGrid.stories.tsx index 6b0166e..34493c5 100644 --- a/packages/ui/src/stories/TelemetryGrid.stories.tsx +++ b/packages/ui/src/stories/TelemetryGrid.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { TelemetryGrid } from "../components/TelemetryGrid"; +import { TelemetryGrid } from "../index"; import { eightTelemetryCards, sixteenTelemetryCards } from "./story-data"; const meta = { diff --git a/packages/ui/src/stories/TelemetryStrip.stories.tsx b/packages/ui/src/stories/TelemetryStrip.stories.tsx index 76fc983..f7c9d13 100644 --- a/packages/ui/src/stories/TelemetryStrip.stories.tsx +++ b/packages/ui/src/stories/TelemetryStrip.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { TelemetryStrip } from "../components/TelemetryStrip"; +import { TelemetryStrip } from "../index"; import { eightTelemetryCards } from "./story-data"; const meta = { diff --git a/packages/ui/src/stories/ThemeToggle.stories.tsx b/packages/ui/src/stories/ThemeToggle.stories.tsx index 33e494a..f2edeea 100644 --- a/packages/ui/src/stories/ThemeToggle.stories.tsx +++ b/packages/ui/src/stories/ThemeToggle.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { ThemeToggle } from "../components/ThemeToggle"; +import { ThemeToggle } from "../index"; const meta = { title: "UI/ThemeToggle", diff --git a/packages/ui/src/stories/WeatherModule.stories.tsx b/packages/ui/src/stories/WeatherModule.stories.tsx index 6429de2..a376448 100644 --- a/packages/ui/src/stories/WeatherModule.stories.tsx +++ b/packages/ui/src/stories/WeatherModule.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { WeatherModule } from "../components/WeatherModule"; +import { WeatherModule } from "../index"; import { moduleBlocks } from "./story-data"; const meta = { diff --git a/packages/ui/src/storybook.test.ts b/packages/ui/src/storybook.test.ts index 4ef14ba..49a9e83 100644 --- a/packages/ui/src/storybook.test.ts +++ b/packages/ui/src/storybook.test.ts @@ -9,6 +9,56 @@ const packageRoot = existsSync(join(root, "packages/ui/package.json")) const componentsDir = join(packageRoot, "src/components"); const storiesDir = join(packageRoot, "src/stories"); +const componentDomains = { + foundation: [ + "Badge", + "Button", + "IconButton", + "IconGlyph", + "ProgressMeter", + "Separator", + "StatusBadge", + "ThemeToggle", + ], + frames: [ + "CornerBracketFrame", + "DashboardFrame", + "DashboardHeader", + "DiagonalStripeField", + "FooterCell", + "FooterStatusCell", + "GridFrame", + "ModuleCard", + "Panel", + "ScanlineField", + ], + operations: [ + "ServiceGroupPanel", + "ServicePanel", + "ServiceRow", + "StatusStrip", + "SystemState", + "WeatherModule", + ], + telemetry: [ + "LineChart", + "SignalTrace", + "Sparkline", + "TelemetryCard", + "TelemetryGrid", + "TelemetryStrip", + ], +} as const; + +const componentFiles: ReadonlyMap = new Map( + Object.entries(componentDomains).flatMap(([domain, components]) => + components.map((component) => [ + component, + join(componentsDir, domain, `${component}.tsx`), + ]), + ), +); + const requiredStoryFiles = [ "Badge.stories.tsx", "Button.stories.tsx", @@ -69,10 +119,7 @@ describe("Storybook inventory", () => { test("loads dashboard component styles through the global app stylesheet", () => { const packageStyles = readFileSync(join(packageRoot, "src/styles.css"), "utf8"); - const dashboardFrame = readFileSync( - join(componentsDir, "DashboardFrame.tsx"), - "utf8", - ); + const dashboardFrame = readFileSync(requiredComponentPath("DashboardFrame"), "utf8"); expect(packageStyles).toContain('./components/styles.css'); expect(dashboardFrame).not.toContain('./styles.css'); @@ -87,9 +134,9 @@ describe("Storybook inventory", () => { }); test("keeps component and story files paired as the UI inventory changes", () => { - const componentStoryFiles = readdirSync(componentsDir) - .filter((filename) => filename.endsWith(".tsx") && !filename.endsWith(".test.tsx")) - .map((filename) => filename.replace(".tsx", ".stories.tsx")); + const componentStoryFiles = [...componentFiles.keys()].map( + (component) => `${component}.stories.tsx`, + ); for (const filename of componentStoryFiles) { expect(existsSync(join(storiesDir, filename)), `${filename} is missing`).toBe(true); @@ -114,9 +161,7 @@ describe("Storybook inventory", () => { "ScanlineField", "SignalTrace", ]) { - expect(existsSync(join(componentsDir, `${component}.tsx`)), `${component} is missing`).toBe( - true, - ); + expect(existsSync(requiredComponentPath(component)), `${component} is missing`).toBe(true); expect( existsSync(join(storiesDir, `${component}.stories.tsx`)), `${component}.stories.tsx is missing`, @@ -131,7 +176,7 @@ describe("Storybook inventory", () => { "ScanlineField", "SignalTrace", ]) { - const source = readFileSync(join(componentsDir, `${component}.tsx`), "utf8"); + const source = readFileSync(requiredComponentPath(component), "utf8"); expect(source).toContain('aria-hidden="true"'); } @@ -139,6 +184,7 @@ describe("Storybook inventory", () => { test("does not add deferred form/navigation primitives", () => { for (const component of ["Input", "ToggleGroup", "ScrollArea"]) { + expect(componentFiles.has(component)).toBe(false); expect(existsSync(join(componentsDir, `${component}.tsx`))).toBe(false); expect(existsSync(join(storiesDir, `${component}.stories.tsx`))).toBe(false); } @@ -153,3 +199,13 @@ describe("Storybook inventory", () => { expect(legacyStories).toEqual([]); }); }); + +function requiredComponentPath(component: string): string { + const componentPath = componentFiles.get(component); + + expect(componentPath, `${component} is missing from the UI component map`).toBeTypeOf( + "string", + ); + + return componentPath as string; +}