refactor(ui): organize component source tree

This commit is contained in:
vince 2026-06-20 06:43:09 +02:00
parent 8927f2ab8a
commit 32ad2cebe4
66 changed files with 194 additions and 124 deletions

View file

@ -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<string, string> = 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;
}