feat(ui): migrate dashboard runtime to React #28
64 changed files with 590 additions and 685 deletions
|
|
@ -1,15 +1,14 @@
|
|||
import type { StorybookConfig } from "@storybook/sveltekit";
|
||||
import type { StorybookConfig } from "@storybook/react-vite";
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: ["../src/**/*.stories.@(js|ts|svelte)"],
|
||||
stories: ["../src/**/*.stories.@(js|ts|tsx)"],
|
||||
staticDirs: ["../static"],
|
||||
addons: [
|
||||
"@storybook/addon-svelte-csf",
|
||||
"@storybook/addon-a11y",
|
||||
"@storybook/addon-vitest",
|
||||
],
|
||||
framework: {
|
||||
name: "@storybook/sveltekit",
|
||||
name: "@storybook/react-vite",
|
||||
options: {},
|
||||
},
|
||||
docs: {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import "../src/app.css";
|
||||
import type { Preview } from "@storybook/sveltekit";
|
||||
import type { Preview } from "@storybook/react-vite";
|
||||
import { setupWorker } from "msw/browser";
|
||||
import { externalApiHandlers } from "../src/lib/testing/external-api-mocks";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import Badge from "../components/Badge.svelte";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Base UI/Badge",
|
||||
component: Badge,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Neutral" args={{ label: "Neutral", severity: "neutral" }} />
|
||||
<Story name="Ok" args={{ label: "Operational", severity: "ok" }} />
|
||||
<Story name="Warning" args={{ label: "Warning", severity: "warning" }} />
|
||||
<Story name="Danger" args={{ label: "Incident", severity: "danger" }} />
|
||||
<Story name="Stale" args={{ label: "Stale", severity: "stale" }} />
|
||||
<Story name="Unavailable" args={{ label: "Unavailable", severity: "unavailable" }} />
|
||||
<Story
|
||||
name="Long Label"
|
||||
args={{ label: "Long generic operational label", severity: "warning" }}
|
||||
/>
|
||||
16
src/lib/ui/stories/Badge.stories.tsx
Normal file
16
src/lib/ui/stories/Badge.stories.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { Badge } from "../components/Badge";
|
||||
|
||||
const meta = {
|
||||
title: "UI/Badge",
|
||||
component: Badge,
|
||||
args: {
|
||||
label: "Ready",
|
||||
severity: "ok",
|
||||
},
|
||||
} satisfies Meta<typeof Badge>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import Button from "../components/Button.svelte";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Base UI/Button",
|
||||
component: Button,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Default" args={{ label: "Refresh" }} />
|
||||
<Story name="Secondary" args={{ label: "Preview", variant: "secondary" }} />
|
||||
<Story name="Danger" args={{ label: "Stop", variant: "danger" }} />
|
||||
<Story name="Ghost Outline" args={{ label: "Inspect", variant: "ghost" }} />
|
||||
<Story name="Disabled" args={{ label: "Publish", disabled: true }} />
|
||||
<Story name="Loading" args={{ label: "Sync", loading: true }} />
|
||||
<Story name="Icon Leading" args={{ label: "Open", icon: "mdi:open-in-new" }} />
|
||||
<Story name="Compact" args={{ label: "Apply", size: "compact" }} />
|
||||
21
src/lib/ui/stories/Button.stories.tsx
Normal file
21
src/lib/ui/stories/Button.stories.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { Button } from "../components/Button";
|
||||
|
||||
const meta = {
|
||||
title: "UI/Button",
|
||||
component: Button,
|
||||
args: {
|
||||
label: "Refresh",
|
||||
icon: "mdi:refresh",
|
||||
},
|
||||
} satisfies Meta<typeof Button>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Primary: Story = {};
|
||||
export const Secondary: Story = {
|
||||
args: {
|
||||
variant: "secondary",
|
||||
},
|
||||
};
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import CornerBracketFrame from "../components/CornerBracketFrame.svelte";
|
||||
import GridFrame from "../components/GridFrame.svelte";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Style Primitives/CornerBracketFrame",
|
||||
component: CornerBracketFrame,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Neutral Medium" args={{ tone: "neutral", size: "md" }} />
|
||||
<Story name="Accent Tight" args={{ tone: "accent", density: "tight" }} />
|
||||
<Story name="Large Danger" args={{ tone: "danger", size: "lg" }} />
|
||||
<Story name="Grid Set" asChild>
|
||||
<GridFrame density="compact">
|
||||
<CornerBracketFrame tone="neutral" size="sm" />
|
||||
<CornerBracketFrame tone="accent" size="sm" />
|
||||
<CornerBracketFrame tone="danger" size="sm" density="tight" />
|
||||
</GridFrame>
|
||||
</Story>
|
||||
16
src/lib/ui/stories/CornerBracketFrame.stories.tsx
Normal file
16
src/lib/ui/stories/CornerBracketFrame.stories.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { CornerBracketFrame } from "../components/CornerBracketFrame";
|
||||
|
||||
const meta = {
|
||||
title: "UI/CornerBracketFrame",
|
||||
component: CornerBracketFrame,
|
||||
args: {
|
||||
tone: "accent",
|
||||
size: "md",
|
||||
},
|
||||
} satisfies Meta<typeof CornerBracketFrame>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import DashboardFrame from "../components/DashboardFrame.svelte";
|
||||
import { emptyDashboard, fullCompositionDashboard, primaryDashboard } from "./story-data";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Composition/DashboardFrame",
|
||||
component: DashboardFrame,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Generic Runtime" args={{ dashboard: primaryDashboard }} />
|
||||
<Story name="Dense Generic Runtime" args={{ dashboard: fullCompositionDashboard }} />
|
||||
<Story name="Empty Regions" args={{ dashboard: emptyDashboard }} />
|
||||
21
src/lib/ui/stories/DashboardFrame.stories.tsx
Normal file
21
src/lib/ui/stories/DashboardFrame.stories.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { DashboardFrame } from "../components/DashboardFrame";
|
||||
import { fullCompositionDashboard, secondaryDashboard } from "./story-data";
|
||||
|
||||
const meta = {
|
||||
title: "UI/DashboardFrame",
|
||||
component: DashboardFrame,
|
||||
args: {
|
||||
dashboard: fullCompositionDashboard,
|
||||
},
|
||||
} satisfies Meta<typeof DashboardFrame>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const FullComposition: Story = {};
|
||||
export const Secondary: Story = {
|
||||
args: {
|
||||
dashboard: secondaryDashboard,
|
||||
},
|
||||
};
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import DashboardHeader from "../components/DashboardHeader.svelte";
|
||||
import { moduleBlocks } from "./story-data";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Composition/DashboardHeader",
|
||||
component: DashboardHeader,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story
|
||||
name="Title Subtitle Module"
|
||||
args={{
|
||||
eyebrow: "Command Surface",
|
||||
title: "Operations Console",
|
||||
subtitle: "Capacity and queue health",
|
||||
module: moduleBlocks.compact,
|
||||
}}
|
||||
/>
|
||||
<Story
|
||||
name="Long Title"
|
||||
args={{
|
||||
eyebrow: "Preview Surface",
|
||||
title: "Long Generic Dashboard Title That Must Wrap",
|
||||
subtitle: "Secondary status text wraps independently",
|
||||
module: moduleBlocks.long,
|
||||
}}
|
||||
/>
|
||||
<Story name="No Module" args={{ title: "Standalone Console", subtitle: "Header without side module" }} />
|
||||
19
src/lib/ui/stories/DashboardHeader.stories.tsx
Normal file
19
src/lib/ui/stories/DashboardHeader.stories.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { DashboardHeader } from "../components/DashboardHeader";
|
||||
import { moduleBlocks } from "./story-data";
|
||||
|
||||
const meta = {
|
||||
title: "UI/DashboardHeader",
|
||||
component: DashboardHeader,
|
||||
args: {
|
||||
eyebrow: "Preview Surface",
|
||||
title: "Operations Console",
|
||||
subtitle: "Generic dashboard header",
|
||||
module: moduleBlocks.compact,
|
||||
},
|
||||
} satisfies Meta<typeof DashboardHeader>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const WithModule: Story = {};
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import DashboardFrame from "../components/DashboardFrame.svelte";
|
||||
import { fullCompositionDashboard } from "./story-data";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Composition/DashboardOnePager",
|
||||
component: DashboardFrame,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Desktop Generic Dashboard" args={{ dashboard: fullCompositionDashboard }} />
|
||||
<Story name="Mobile Generic Dashboard" asChild>
|
||||
<div style="max-width: 390px">
|
||||
<DashboardFrame dashboard={fullCompositionDashboard} />
|
||||
</div>
|
||||
</Story>
|
||||
16
src/lib/ui/stories/DashboardOnePager.stories.tsx
Normal file
16
src/lib/ui/stories/DashboardOnePager.stories.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { DashboardFrame } from "../components/DashboardFrame";
|
||||
import { fullCompositionDashboard } from "./story-data";
|
||||
|
||||
const meta = {
|
||||
title: "UI/DashboardOnePager",
|
||||
component: DashboardFrame,
|
||||
args: {
|
||||
dashboard: fullCompositionDashboard,
|
||||
},
|
||||
} satisfies Meta<typeof DashboardFrame>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const OnePager: Story = {};
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import DiagonalStripeField from "../components/DiagonalStripeField.svelte";
|
||||
import Panel from "../components/Panel.svelte";
|
||||
import SignalTrace from "../components/SignalTrace.svelte";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Style Primitives/DiagonalStripeField",
|
||||
component: DiagonalStripeField,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Accent Regular" args={{ density: "regular", tone: "accent", size: "md" }} />
|
||||
<Story name="Tight Warning" args={{ density: "tight", tone: "warning", direction: "backward" }} />
|
||||
<Story name="Large Danger" args={{ density: "open", tone: "danger", size: "lg" }} />
|
||||
<Story name="Composed Divider" asChild>
|
||||
<Panel title="Generic Surface">
|
||||
<DiagonalStripeField density="tight" size="sm" />
|
||||
<SignalTrace tone="accent" />
|
||||
<DiagonalStripeField direction="backward" tone="neutral" />
|
||||
</Panel>
|
||||
</Story>
|
||||
16
src/lib/ui/stories/DiagonalStripeField.stories.tsx
Normal file
16
src/lib/ui/stories/DiagonalStripeField.stories.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { DiagonalStripeField } from "../components/DiagonalStripeField";
|
||||
|
||||
const meta = {
|
||||
title: "UI/DiagonalStripeField",
|
||||
component: DiagonalStripeField,
|
||||
args: {
|
||||
tone: "accent",
|
||||
density: "regular",
|
||||
},
|
||||
} satisfies Meta<typeof DiagonalStripeField>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { onMount, tick, type Snippet } from "svelte";
|
||||
|
||||
let {
|
||||
children,
|
||||
selector = "button, a[href]",
|
||||
}: {
|
||||
children?: Snippet;
|
||||
selector?: string;
|
||||
} = $props();
|
||||
|
||||
let root: HTMLDivElement;
|
||||
|
||||
onMount(async () => {
|
||||
await tick();
|
||||
root.querySelector<HTMLElement>(selector)?.focus();
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="focus-preview" bind:this={root}>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.focus-preview {
|
||||
display: inline-grid;
|
||||
padding: var(--ui-space-2);
|
||||
}
|
||||
|
||||
.focus-preview :global(:is(a, button)) {
|
||||
box-shadow: var(--ui-focus-ring);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import FooterCell from "../components/FooterCell.svelte";
|
||||
import { statusItems } from "./story-data";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Dashboard Primitives/FooterCell",
|
||||
component: FooterCell,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Label Value" args={{ item: statusItems.ok }} />
|
||||
<Story name="Action Cell" args={{ item: statusItems.action }} />
|
||||
<Story name="Stale" args={{ item: statusItems.stale }} />
|
||||
<Story name="Long Value" args={{ item: statusItems.long }} />
|
||||
21
src/lib/ui/stories/FooterCell.stories.tsx
Normal file
21
src/lib/ui/stories/FooterCell.stories.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { FooterCell } from "../components/FooterCell";
|
||||
import { statusItems } from "./story-data";
|
||||
|
||||
const meta = {
|
||||
title: "UI/FooterCell",
|
||||
component: FooterCell,
|
||||
args: {
|
||||
item: statusItems.ok,
|
||||
},
|
||||
} satisfies Meta<typeof FooterCell>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Operational: Story = {};
|
||||
export const Linked: Story = {
|
||||
args: {
|
||||
item: statusItems.action,
|
||||
},
|
||||
};
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import FooterStatusCell from "../components/FooterStatusCell.svelte";
|
||||
import { statusItems } from "./story-data";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Dashboard Primitives/FooterStatusCell",
|
||||
component: FooterStatusCell,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Label Value" args={{ item: statusItems.ok }} />
|
||||
<Story name="Action Cell" args={{ item: statusItems.action }} />
|
||||
<Story name="Stale" args={{ item: statusItems.stale }} />
|
||||
<Story name="Long Value" args={{ item: statusItems.long }} />
|
||||
16
src/lib/ui/stories/FooterStatusCell.stories.tsx
Normal file
16
src/lib/ui/stories/FooterStatusCell.stories.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { FooterStatusCell } from "../components/FooterStatusCell";
|
||||
import { statusItems } from "./story-data";
|
||||
|
||||
const meta = {
|
||||
title: "UI/FooterStatusCell",
|
||||
component: FooterStatusCell,
|
||||
args: {
|
||||
item: statusItems.degraded,
|
||||
},
|
||||
} satisfies Meta<typeof FooterStatusCell>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Warning: Story = {};
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import GridFrame from "../components/GridFrame.svelte";
|
||||
import SystemState from "../components/SystemState.svelte";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Dashboard Primitives/GridFrame",
|
||||
component: GridFrame,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Desktop Grid" asChild>
|
||||
<GridFrame>
|
||||
<SystemState title="Cell One" />
|
||||
<SystemState title="Cell Two" />
|
||||
<SystemState title="Cell Three" />
|
||||
<SystemState title="Cell Four" />
|
||||
</GridFrame>
|
||||
</Story>
|
||||
<Story name="Tablet Wrap" asChild>
|
||||
<div style="max-width: 760px">
|
||||
<GridFrame density="dense">
|
||||
<SystemState title="Cell One" />
|
||||
<SystemState title="Cell Two" />
|
||||
<SystemState title="Cell Three" />
|
||||
</GridFrame>
|
||||
</div>
|
||||
</Story>
|
||||
<Story name="Mobile Stack" asChild>
|
||||
<div style="max-width: 390px">
|
||||
<GridFrame density="compact">
|
||||
<SystemState title="Cell One" />
|
||||
<SystemState title="Cell Two" />
|
||||
</GridFrame>
|
||||
</div>
|
||||
</Story>
|
||||
25
src/lib/ui/stories/GridFrame.stories.tsx
Normal file
25
src/lib/ui/stories/GridFrame.stories.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { GridFrame } from "../components/GridFrame";
|
||||
import { ModuleCard } from "../components/ModuleCard";
|
||||
import { moduleBlocks } from "./story-data";
|
||||
|
||||
const meta = {
|
||||
title: "UI/GridFrame",
|
||||
component: GridFrame,
|
||||
render: (args) => (
|
||||
<GridFrame {...args}>
|
||||
<ModuleCard module={moduleBlocks.compact} />
|
||||
<ModuleCard module={moduleBlocks.unavailable} />
|
||||
<ModuleCard module={moduleBlocks.long} />
|
||||
</GridFrame>
|
||||
),
|
||||
} satisfies Meta<typeof GridFrame>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Dense: Story = {
|
||||
args: {
|
||||
density: "dense",
|
||||
},
|
||||
};
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import IconButton from "../components/IconButton.svelte";
|
||||
import FocusPreview from "./FocusPreview.svelte";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Base UI/IconButton",
|
||||
component: IconButton,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Refresh" args={{ icon: "mdi:refresh", label: "Refresh" }} />
|
||||
<Story name="Open" args={{ icon: "mdi:open-in-new", label: "Open" }} />
|
||||
<Story name="Inspect" args={{ icon: "mdi:magnify-scan", label: "Inspect" }} />
|
||||
<Story name="Disabled" args={{ icon: "mdi:refresh", label: "Disabled", disabled: true }} />
|
||||
<Story name="Active" args={{ icon: "mdi:refresh", label: "Active", active: true }} />
|
||||
<Story name="Focus Visible" asChild>
|
||||
<FocusPreview>
|
||||
<IconButton icon="mdi:keyboard" label="Focus visible" />
|
||||
</FocusPreview>
|
||||
</Story>
|
||||
16
src/lib/ui/stories/IconButton.stories.tsx
Normal file
16
src/lib/ui/stories/IconButton.stories.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { IconButton } from "../components/IconButton";
|
||||
|
||||
const meta = {
|
||||
title: "UI/IconButton",
|
||||
component: IconButton,
|
||||
args: {
|
||||
icon: "mdi:refresh",
|
||||
label: "Refresh status",
|
||||
},
|
||||
} satisfies Meta<typeof IconButton>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import IconGlyph from "../components/IconGlyph.svelte";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Base UI/IconGlyph",
|
||||
component: IconGlyph,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Valid Icon" args={{ name: "mdi:server-network" }} />
|
||||
<Story name="Missing Icon Fallback" args={{ name: undefined }} />
|
||||
<Story name="Small" args={{ name: "mdi:radar", size: "sm" }} />
|
||||
<Story name="Medium" args={{ name: "mdi:radar", size: "md" }} />
|
||||
<Story name="Large" args={{ name: "mdi:radar", size: "lg" }} />
|
||||
<Story name="Severity Color" asChild>
|
||||
<div style="color: var(--ui-color-warning)">
|
||||
<IconGlyph name="mdi:alert-outline" size="lg" />
|
||||
</div>
|
||||
</Story>
|
||||
17
src/lib/ui/stories/IconGlyph.stories.tsx
Normal file
17
src/lib/ui/stories/IconGlyph.stories.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { IconGlyph } from "../components/IconGlyph";
|
||||
|
||||
const meta = {
|
||||
title: "UI/IconGlyph",
|
||||
component: IconGlyph,
|
||||
args: {
|
||||
name: "mdi:server-network",
|
||||
label: "Generic service",
|
||||
size: "md",
|
||||
},
|
||||
} satisfies Meta<typeof IconGlyph>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import LineChart from "../components/LineChart.svelte";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Dashboard Primitives/LineChart",
|
||||
component: LineChart,
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Neutral Trend" args={{ values: [12, 18, 15, 28, 24, 32] }} />
|
||||
<Story name="Warning Trend" args={{ values: [22, 34, 48, 62, 74, 70], severity: "warning" }} />
|
||||
<Story name="Danger Trend" args={{ values: [20, 40, 64, 82, 90, 94], severity: "danger" }} />
|
||||
17
src/lib/ui/stories/LineChart.stories.tsx
Normal file
17
src/lib/ui/stories/LineChart.stories.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { LineChart } from "../components/LineChart";
|
||||
|
||||
const meta = {
|
||||
title: "UI/LineChart",
|
||||
component: LineChart,
|
||||
args: {
|
||||
values: [12, 18, 24, 20, 36, 44],
|
||||
severity: "ok",
|
||||
label: "Generic trend",
|
||||
},
|
||||
} satisfies Meta<typeof LineChart>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Trend: Story = {};
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import ModuleCard from "../components/ModuleCard.svelte";
|
||||
import { moduleBlocks } from "./story-data";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Dashboard Primitives/ModuleCard",
|
||||
component: ModuleCard,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Compact Summary" args={{ module: moduleBlocks.compact }} />
|
||||
<Story name="Unavailable" args={{ module: moduleBlocks.unavailable }} />
|
||||
<Story name="Long Value" args={{ module: moduleBlocks.long }} />
|
||||
21
src/lib/ui/stories/ModuleCard.stories.tsx
Normal file
21
src/lib/ui/stories/ModuleCard.stories.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { ModuleCard } from "../components/ModuleCard";
|
||||
import { moduleBlocks } from "./story-data";
|
||||
|
||||
const meta = {
|
||||
title: "UI/ModuleCard",
|
||||
component: ModuleCard,
|
||||
args: {
|
||||
module: moduleBlocks.compact,
|
||||
},
|
||||
} satisfies Meta<typeof ModuleCard>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Compact: Story = {};
|
||||
export const Long: Story = {
|
||||
args: {
|
||||
module: moduleBlocks.long,
|
||||
},
|
||||
};
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import Panel from "../components/Panel.svelte";
|
||||
import SystemState from "../components/SystemState.svelte";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Dashboard Primitives/Panel",
|
||||
component: Panel,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Default" asChild>
|
||||
<Panel title="Generic Panel">
|
||||
<p>Reusable panel content.</p>
|
||||
</Panel>
|
||||
</Story>
|
||||
<Story name="Dense" asChild>
|
||||
<Panel title="Dense Panel" density="compact">
|
||||
<p>Compact repeated content.</p>
|
||||
</Panel>
|
||||
</Story>
|
||||
<Story name="Warning" asChild>
|
||||
<Panel title="Warning Panel">
|
||||
<SystemState title="Warning" detail="Generic warning state" severity="warning" />
|
||||
</Panel>
|
||||
</Story>
|
||||
<Story name="Danger" asChild>
|
||||
<Panel title="Danger Panel">
|
||||
<SystemState title="Danger" detail="Generic danger state" severity="danger" />
|
||||
</Panel>
|
||||
</Story>
|
||||
<Story name="Empty" asChild>
|
||||
<Panel title="Empty Panel">
|
||||
<SystemState title="Empty" detail="No generic records available" severity="neutral" />
|
||||
</Panel>
|
||||
</Story>
|
||||
<Story name="Nested Content Guard" asChild>
|
||||
<Panel title="Outer Panel">
|
||||
<SystemState title="Nested content" detail="No nested card styling required" />
|
||||
</Panel>
|
||||
</Story>
|
||||
23
src/lib/ui/stories/Panel.stories.tsx
Normal file
23
src/lib/ui/stories/Panel.stories.tsx
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { Panel } from "../components/Panel";
|
||||
import { ServiceRow } from "../components/ServiceRow";
|
||||
import { serviceRows } from "./story-data";
|
||||
|
||||
const meta = {
|
||||
title: "UI/Panel",
|
||||
component: Panel,
|
||||
render: (args) => (
|
||||
<Panel {...args}>
|
||||
<ServiceRow service={serviceRows.normal} />
|
||||
<ServiceRow service={serviceRows.degraded} />
|
||||
</Panel>
|
||||
),
|
||||
args: {
|
||||
title: "Generic Panel",
|
||||
},
|
||||
} satisfies Meta<typeof Panel>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Dense: Story = {};
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import ProgressMeter from "../components/ProgressMeter.svelte";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Dashboard Primitives/ProgressMeter",
|
||||
component: ProgressMeter,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Low" args={{ value: 12, label: "Low progress" }} />
|
||||
<Story name="Normal" args={{ value: 63, severity: "ok", label: "Normal progress" }} />
|
||||
<Story name="Warn" args={{ value: 76, severity: "warning", label: "Warning progress" }} />
|
||||
<Story name="Danger" args={{ value: 94, severity: "danger", label: "Danger progress" }} />
|
||||
<Story name="Empty" args={{ value: undefined, label: "Missing progress" }} />
|
||||
17
src/lib/ui/stories/ProgressMeter.stories.tsx
Normal file
17
src/lib/ui/stories/ProgressMeter.stories.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { ProgressMeter } from "../components/ProgressMeter";
|
||||
|
||||
const meta = {
|
||||
title: "UI/ProgressMeter",
|
||||
component: ProgressMeter,
|
||||
args: {
|
||||
value: 72,
|
||||
severity: "ok",
|
||||
label: "Capacity",
|
||||
},
|
||||
} satisfies Meta<typeof ProgressMeter>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Filled: Story = {};
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import ScanlineField from "../components/ScanlineField.svelte";
|
||||
import Panel from "../components/Panel.svelte";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Style Primitives/ScanlineField",
|
||||
component: ScanlineField,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Soft Neutral" args={{ intensity: "soft", tone: "neutral", size: "md" }} />
|
||||
<Story name="Medium Accent" args={{ intensity: "medium", tone: "accent", size: "md" }} />
|
||||
<Story name="Hard Warning" args={{ intensity: "hard", tone: "warning", size: "lg" }} />
|
||||
<Story name="Panel Backdrop" asChild>
|
||||
<Panel title="Signal Surface">
|
||||
<ScanlineField intensity="hard" tone="accent" />
|
||||
</Panel>
|
||||
</Story>
|
||||
16
src/lib/ui/stories/ScanlineField.stories.tsx
Normal file
16
src/lib/ui/stories/ScanlineField.stories.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { ScanlineField } from "../components/ScanlineField";
|
||||
|
||||
const meta = {
|
||||
title: "UI/ScanlineField",
|
||||
component: ScanlineField,
|
||||
args: {
|
||||
tone: "accent",
|
||||
intensity: "medium",
|
||||
},
|
||||
} satisfies Meta<typeof ScanlineField>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import Separator from "../components/Separator.svelte";
|
||||
import Panel from "../components/Panel.svelte";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Base UI/Separator",
|
||||
component: Separator,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Horizontal" args={{ orientation: "horizontal" }} />
|
||||
<Story name="Vertical" args={{ orientation: "vertical" }} />
|
||||
<Story name="Dense Panel Use" asChild>
|
||||
<Panel title="Dense Panel">
|
||||
<p>Generic section one</p>
|
||||
<Separator dense />
|
||||
<p>Generic section two</p>
|
||||
</Panel>
|
||||
</Story>
|
||||
15
src/lib/ui/stories/Separator.stories.tsx
Normal file
15
src/lib/ui/stories/Separator.stories.tsx
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { Separator } from "../components/Separator";
|
||||
|
||||
const meta = {
|
||||
title: "UI/Separator",
|
||||
component: Separator,
|
||||
args: {
|
||||
orientation: "horizontal",
|
||||
},
|
||||
} satisfies Meta<typeof Separator>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Horizontal: Story = {};
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import ServiceGroupPanel from "../components/ServiceGroupPanel.svelte";
|
||||
import { serviceGroups } from "./story-data";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Dashboard Primitives/ServiceGroupPanel",
|
||||
component: ServiceGroupPanel,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Short List" args={{ group: serviceGroups.short }} />
|
||||
<Story name="Long List" args={{ group: serviceGroups.long }} />
|
||||
<Story name="Empty Group" args={{ group: serviceGroups.empty }} />
|
||||
<Story name="Mixed Statuses" args={{ group: serviceGroups.mixed }} />
|
||||
16
src/lib/ui/stories/ServiceGroupPanel.stories.tsx
Normal file
16
src/lib/ui/stories/ServiceGroupPanel.stories.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { ServiceGroupPanel } from "../components/ServiceGroupPanel";
|
||||
import { serviceGroups } from "./story-data";
|
||||
|
||||
const meta = {
|
||||
title: "UI/ServiceGroupPanel",
|
||||
component: ServiceGroupPanel,
|
||||
args: {
|
||||
group: serviceGroups.mixed,
|
||||
},
|
||||
} satisfies Meta<typeof ServiceGroupPanel>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Mixed: Story = {};
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import ServicePanel from "../components/ServicePanel.svelte";
|
||||
import { serviceGroups } from "./story-data";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Dashboard Primitives/ServicePanel",
|
||||
component: ServicePanel,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Short List" args={{ group: serviceGroups.short }} />
|
||||
<Story name="Long List" args={{ group: serviceGroups.long }} />
|
||||
<Story name="Empty Group" args={{ group: serviceGroups.empty }} />
|
||||
<Story name="Mixed Statuses" args={{ group: serviceGroups.mixed }} />
|
||||
16
src/lib/ui/stories/ServicePanel.stories.tsx
Normal file
16
src/lib/ui/stories/ServicePanel.stories.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { ServicePanel } from "../components/ServicePanel";
|
||||
import { serviceGroups } from "./story-data";
|
||||
|
||||
const meta = {
|
||||
title: "UI/ServicePanel",
|
||||
component: ServicePanel,
|
||||
args: {
|
||||
group: serviceGroups.long,
|
||||
},
|
||||
} satisfies Meta<typeof ServicePanel>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const LongList: Story = {};
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import ServiceRow from "../components/ServiceRow.svelte";
|
||||
import FocusPreview from "./FocusPreview.svelte";
|
||||
import { serviceRows } from "./story-data";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Dashboard Primitives/ServiceRow",
|
||||
component: ServiceRow,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Normal" args={{ service: serviceRows.normal }} />
|
||||
<Story name="Down" args={{ service: serviceRows.down }} />
|
||||
<Story name="Degraded" args={{ service: serviceRows.degraded }} />
|
||||
<Story name="Stale" args={{ service: serviceRows.stale }} />
|
||||
<Story name="No Link" args={{ service: serviceRows.noLink }} />
|
||||
<Story name="Long Title Description" args={{ service: serviceRows.long }} />
|
||||
<Story name="Keyboard Focus" asChild>
|
||||
<FocusPreview selector="a">
|
||||
<ServiceRow service={serviceRows.normal} />
|
||||
</FocusPreview>
|
||||
</Story>
|
||||
21
src/lib/ui/stories/ServiceRow.stories.tsx
Normal file
21
src/lib/ui/stories/ServiceRow.stories.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { ServiceRow } from "../components/ServiceRow";
|
||||
import { serviceRows } from "./story-data";
|
||||
|
||||
const meta = {
|
||||
title: "UI/ServiceRow",
|
||||
component: ServiceRow,
|
||||
args: {
|
||||
service: serviceRows.normal,
|
||||
},
|
||||
} satisfies Meta<typeof ServiceRow>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Normal: Story = {};
|
||||
export const Warning: Story = {
|
||||
args: {
|
||||
service: serviceRows.degraded,
|
||||
},
|
||||
};
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import SignalTrace from "../components/SignalTrace.svelte";
|
||||
import Panel from "../components/Panel.svelte";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Style Primitives/SignalTrace",
|
||||
component: SignalTrace,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Horizontal Accent" args={{ tone: "accent", orientation: "horizontal" }} />
|
||||
<Story name="Horizontal Warning Tight" args={{ tone: "warning", density: "tight" }} />
|
||||
<Story name="Vertical Neutral" args={{ tone: "neutral", orientation: "vertical" }} />
|
||||
<Story name="Panel Trace" asChild>
|
||||
<Panel title="Generic Flow">
|
||||
<SignalTrace tone="accent" />
|
||||
<SignalTrace tone="warning" density="tight" />
|
||||
</Panel>
|
||||
</Story>
|
||||
16
src/lib/ui/stories/SignalTrace.stories.tsx
Normal file
16
src/lib/ui/stories/SignalTrace.stories.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { SignalTrace } from "../components/SignalTrace";
|
||||
|
||||
const meta = {
|
||||
title: "UI/SignalTrace",
|
||||
component: SignalTrace,
|
||||
args: {
|
||||
tone: "accent",
|
||||
orientation: "horizontal",
|
||||
},
|
||||
} satisfies Meta<typeof SignalTrace>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Horizontal: Story = {};
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import Sparkline from "../components/Sparkline.svelte";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Dashboard Primitives/Sparkline",
|
||||
component: Sparkline,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Flat" args={{ values: [20, 20, 20, 20, 20, 20] }} />
|
||||
<Story name="Rising" args={{ values: [4, 12, 18, 28, 38, 54], severity: "ok" }} />
|
||||
<Story name="Spiky" args={{ values: [8, 50, 12, 64, 18, 72], severity: "warning" }} />
|
||||
<Story name="Missing Data" args={{ values: [] }} />
|
||||
<Story name="Stale" args={{ values: [32, 31, 31, 30, 30, 30], severity: "stale" }} />
|
||||
16
src/lib/ui/stories/Sparkline.stories.tsx
Normal file
16
src/lib/ui/stories/Sparkline.stories.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { Sparkline } from "../components/Sparkline";
|
||||
|
||||
const meta = {
|
||||
title: "UI/Sparkline",
|
||||
component: Sparkline,
|
||||
args: {
|
||||
values: [8, 14, 12, 24, 18, 30],
|
||||
severity: "ok",
|
||||
},
|
||||
} satisfies Meta<typeof Sparkline>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import StatusBadge from "../components/StatusBadge.svelte";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Base UI/StatusBadge",
|
||||
component: StatusBadge,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Neutral" args={{ label: "Neutral" }} />
|
||||
<Story name="Ok" args={{ label: "Operational", severity: "ok" }} />
|
||||
<Story name="Warning" args={{ label: "Warning", severity: "warning" }} />
|
||||
<Story name="Danger" args={{ label: "Danger", severity: "danger" }} />
|
||||
<Story name="Stale" args={{ label: "Stale", severity: "stale" }} />
|
||||
<Story name="Unavailable" args={{ label: "Unavailable", severity: "unavailable" }} />
|
||||
<Story name="Long Label" args={{ label: "Long Generic Badge Label", severity: "neutral" }} />
|
||||
16
src/lib/ui/stories/StatusBadge.stories.tsx
Normal file
16
src/lib/ui/stories/StatusBadge.stories.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { StatusBadge } from "../components/StatusBadge";
|
||||
|
||||
const meta = {
|
||||
title: "UI/StatusBadge",
|
||||
component: StatusBadge,
|
||||
args: {
|
||||
label: "Ready",
|
||||
severity: "ok",
|
||||
},
|
||||
} satisfies Meta<typeof StatusBadge>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Ready: Story = {};
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import StatusStrip from "../components/StatusStrip.svelte";
|
||||
import { mixedStatusStrip, statusItems } from "./story-data";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Dashboard Primitives/StatusStrip",
|
||||
component: StatusStrip,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story
|
||||
name="All Operational"
|
||||
args={{
|
||||
items: [
|
||||
{ ...statusItems.ok, id: "status-ok-a" },
|
||||
{ ...statusItems.ok, id: "status-ok-b", label: "Queue" },
|
||||
{ ...statusItems.ok, id: "status-ok-c", label: "Sync" },
|
||||
],
|
||||
}}
|
||||
/>
|
||||
<Story name="Degraded" args={{ items: [statusItems.ok, statusItems.degraded, statusItems.stale] }} />
|
||||
<Story name="Incident" args={{ items: [statusItems.ok, statusItems.incident, statusItems.degraded] }} />
|
||||
<Story name="Syncing" args={{ items: [statusItems.syncing, statusItems.ok, statusItems.action] }} />
|
||||
<Story name="Stale" args={{ items: [statusItems.stale, statusItems.long] }} />
|
||||
<Story name="Mixed Compact" args={{ items: mixedStatusStrip, compact: true }} />
|
||||
17
src/lib/ui/stories/StatusStrip.stories.tsx
Normal file
17
src/lib/ui/stories/StatusStrip.stories.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { StatusStrip } from "../components/StatusStrip";
|
||||
import { mixedStatusStrip } from "./story-data";
|
||||
|
||||
const meta = {
|
||||
title: "UI/StatusStrip",
|
||||
component: StatusStrip,
|
||||
args: {
|
||||
id: "generic-status",
|
||||
items: mixedStatusStrip,
|
||||
},
|
||||
} satisfies Meta<typeof StatusStrip>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Mixed: Story = {};
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import SystemState from "../components/SystemState.svelte";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Dashboard Primitives/SystemState",
|
||||
component: SystemState,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Loading" args={{ title: "Loading", detail: "Waiting for records", severity: "loading", icon: "mdi:progress-clock" }} />
|
||||
<Story name="Empty" args={{ title: "Empty", detail: "No generic records available", icon: "mdi:tray" }} />
|
||||
<Story name="Error" args={{ title: "Error", detail: "Generic request failed", severity: "danger", icon: "mdi:alert-octagon-outline" }} />
|
||||
<Story name="Unavailable" args={{ title: "Unavailable", detail: "External signal unavailable", severity: "unavailable", icon: "mdi:cloud-off-outline" }} />
|
||||
<Story name="Validation Failure" args={{ title: "Validation Failure", detail: "Document did not match schema", severity: "warning", icon: "mdi:file-alert-outline" }} />
|
||||
18
src/lib/ui/stories/SystemState.stories.tsx
Normal file
18
src/lib/ui/stories/SystemState.stories.tsx
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { SystemState } from "../components/SystemState";
|
||||
|
||||
const meta = {
|
||||
title: "UI/SystemState",
|
||||
component: SystemState,
|
||||
args: {
|
||||
title: "Loading Dashboard",
|
||||
detail: "Fetching active model",
|
||||
severity: "loading",
|
||||
icon: "mdi:progress-clock",
|
||||
},
|
||||
} satisfies Meta<typeof SystemState>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Loading: Story = {};
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import TelemetryCard from "../components/TelemetryCard.svelte";
|
||||
import { telemetryCards } from "./story-data";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Dashboard Primitives/TelemetryCard",
|
||||
component: TelemetryCard,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Percent" args={{ card: telemetryCards.percent }} />
|
||||
<Story name="Bytes" args={{ card: telemetryCards.bytes }} />
|
||||
<Story name="Temperature" args={{ card: telemetryCards.temperature }} />
|
||||
<Story name="Latency" args={{ card: telemetryCards.latency }} />
|
||||
<Story name="No Data" args={{ card: telemetryCards.unavailable }} />
|
||||
<Story name="Stale" args={{ card: telemetryCards.stale }} />
|
||||
<Story name="Warning" args={{ card: telemetryCards.latency }} />
|
||||
<Story name="Danger" args={{ card: telemetryCards.danger }} />
|
||||
<Story name="Long Source Label" args={{ card: telemetryCards.long }} />
|
||||
21
src/lib/ui/stories/TelemetryCard.stories.tsx
Normal file
21
src/lib/ui/stories/TelemetryCard.stories.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { TelemetryCard } from "../components/TelemetryCard";
|
||||
import { telemetryCards } from "./story-data";
|
||||
|
||||
const meta = {
|
||||
title: "UI/TelemetryCard",
|
||||
component: TelemetryCard,
|
||||
args: {
|
||||
card: telemetryCards.percent,
|
||||
},
|
||||
} satisfies Meta<typeof TelemetryCard>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Percent: Story = {};
|
||||
export const Danger: Story = {
|
||||
args: {
|
||||
card: telemetryCards.danger,
|
||||
},
|
||||
};
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import TelemetryGrid from "../components/TelemetryGrid.svelte";
|
||||
import { eightTelemetryCards, sixteenTelemetryCards } from "./story-data";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Dashboard Primitives/TelemetryGrid",
|
||||
component: TelemetryGrid,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Eight Card Density" args={{ cards: eightTelemetryCards }} />
|
||||
<Story name="Sixteen Card Density" args={{ cards: sixteenTelemetryCards }} />
|
||||
<Story name="Empty" args={{ cards: [] }} />
|
||||
21
src/lib/ui/stories/TelemetryGrid.stories.tsx
Normal file
21
src/lib/ui/stories/TelemetryGrid.stories.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { TelemetryGrid } from "../components/TelemetryGrid";
|
||||
import { eightTelemetryCards, sixteenTelemetryCards } from "./story-data";
|
||||
|
||||
const meta = {
|
||||
title: "UI/TelemetryGrid",
|
||||
component: TelemetryGrid,
|
||||
args: {
|
||||
cards: eightTelemetryCards,
|
||||
},
|
||||
} satisfies Meta<typeof TelemetryGrid>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const EightCards: Story = {};
|
||||
export const SixteenCards: Story = {
|
||||
args: {
|
||||
cards: sixteenTelemetryCards,
|
||||
},
|
||||
};
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import TelemetryStrip from "../components/TelemetryStrip.svelte";
|
||||
import { eightTelemetryCards, sixteenTelemetryCards } from "./story-data";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Composition/TelemetryStrip",
|
||||
component: TelemetryStrip,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Eight Card Density" args={{ cards: eightTelemetryCards }} />
|
||||
<Story name="Sixteen Card Density" args={{ cards: sixteenTelemetryCards }} />
|
||||
<Story name="Mixed Severity Wrap" args={{ cards: eightTelemetryCards }} />
|
||||
16
src/lib/ui/stories/TelemetryStrip.stories.tsx
Normal file
16
src/lib/ui/stories/TelemetryStrip.stories.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { TelemetryStrip } from "../components/TelemetryStrip";
|
||||
import { eightTelemetryCards } from "./story-data";
|
||||
|
||||
const meta = {
|
||||
title: "UI/TelemetryStrip",
|
||||
component: TelemetryStrip,
|
||||
args: {
|
||||
cards: eightTelemetryCards,
|
||||
},
|
||||
} satisfies Meta<typeof TelemetryStrip>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from "@storybook/addon-svelte-csf";
|
||||
import WeatherModule from "../components/WeatherModule.svelte";
|
||||
import { moduleBlocks } from "./story-data";
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: "Dashboard Primitives/WeatherModule",
|
||||
component: WeatherModule,
|
||||
tags: ["autodocs"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Compact Summary" args={{ module: moduleBlocks.compact }} />
|
||||
<Story name="Unavailable" args={{ module: moduleBlocks.unavailable }} />
|
||||
<Story name="Long Location Value" args={{ module: moduleBlocks.long }} />
|
||||
<Story name="Generic Module Variant" args={{ module: { ...moduleBlocks.compact, title: "Signal Module", icon: "mdi:radar" } }} />
|
||||
16
src/lib/ui/stories/WeatherModule.stories.tsx
Normal file
16
src/lib/ui/stories/WeatherModule.stories.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { WeatherModule } from "../components/WeatherModule";
|
||||
import { moduleBlocks } from "./story-data";
|
||||
|
||||
const meta = {
|
||||
title: "UI/WeatherModule",
|
||||
component: WeatherModule,
|
||||
args: {
|
||||
module: moduleBlocks.compact,
|
||||
},
|
||||
} satisfies Meta<typeof WeatherModule>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
|
@ -7,35 +7,36 @@ const componentsDir = join(root, "src/lib/ui/components");
|
|||
const storiesDir = join(root, "src/lib/ui/stories");
|
||||
|
||||
const requiredStoryFiles = [
|
||||
"Badge.stories.svelte",
|
||||
"Button.stories.svelte",
|
||||
"DashboardFrame.stories.svelte",
|
||||
"DashboardHeader.stories.svelte",
|
||||
"DashboardOnePager.stories.svelte",
|
||||
"CornerBracketFrame.stories.svelte",
|
||||
"DiagonalStripeField.stories.svelte",
|
||||
"FooterCell.stories.svelte",
|
||||
"FooterStatusCell.stories.svelte",
|
||||
"GridFrame.stories.svelte",
|
||||
"IconButton.stories.svelte",
|
||||
"IconGlyph.stories.svelte",
|
||||
"ModuleCard.stories.svelte",
|
||||
"Panel.stories.svelte",
|
||||
"ProgressMeter.stories.svelte",
|
||||
"Separator.stories.svelte",
|
||||
"ServiceGroupPanel.stories.svelte",
|
||||
"ServicePanel.stories.svelte",
|
||||
"ServiceRow.stories.svelte",
|
||||
"SignalTrace.stories.svelte",
|
||||
"Sparkline.stories.svelte",
|
||||
"StatusBadge.stories.svelte",
|
||||
"StatusStrip.stories.svelte",
|
||||
"SystemState.stories.svelte",
|
||||
"ScanlineField.stories.svelte",
|
||||
"TelemetryCard.stories.svelte",
|
||||
"TelemetryGrid.stories.svelte",
|
||||
"TelemetryStrip.stories.svelte",
|
||||
"WeatherModule.stories.svelte",
|
||||
"Badge.stories.tsx",
|
||||
"Button.stories.tsx",
|
||||
"DashboardFrame.stories.tsx",
|
||||
"DashboardHeader.stories.tsx",
|
||||
"DashboardOnePager.stories.tsx",
|
||||
"CornerBracketFrame.stories.tsx",
|
||||
"DiagonalStripeField.stories.tsx",
|
||||
"FooterCell.stories.tsx",
|
||||
"FooterStatusCell.stories.tsx",
|
||||
"GridFrame.stories.tsx",
|
||||
"IconButton.stories.tsx",
|
||||
"IconGlyph.stories.tsx",
|
||||
"LineChart.stories.tsx",
|
||||
"ModuleCard.stories.tsx",
|
||||
"Panel.stories.tsx",
|
||||
"ProgressMeter.stories.tsx",
|
||||
"Separator.stories.tsx",
|
||||
"ServiceGroupPanel.stories.tsx",
|
||||
"ServicePanel.stories.tsx",
|
||||
"ServiceRow.stories.tsx",
|
||||
"SignalTrace.stories.tsx",
|
||||
"Sparkline.stories.tsx",
|
||||
"StatusBadge.stories.tsx",
|
||||
"StatusStrip.stories.tsx",
|
||||
"SystemState.stories.tsx",
|
||||
"ScanlineField.stories.tsx",
|
||||
"TelemetryCard.stories.tsx",
|
||||
"TelemetryGrid.stories.tsx",
|
||||
"TelemetryStrip.stories.tsx",
|
||||
"WeatherModule.stories.tsx",
|
||||
] as const;
|
||||
|
||||
const forbiddenStoryContent = [
|
||||
|
|
@ -64,8 +65,8 @@ describe("Storybook inventory", () => {
|
|||
|
||||
test("keeps component and story files paired as the UI inventory changes", () => {
|
||||
const componentStoryFiles = readdirSync(componentsDir)
|
||||
.filter((filename) => filename.endsWith(".svelte"))
|
||||
.map((filename) => filename.replace(".svelte", ".stories.svelte"));
|
||||
.filter((filename) => filename.endsWith(".tsx") && !filename.endsWith(".test.tsx"))
|
||||
.map((filename) => filename.replace(".tsx", ".stories.tsx"));
|
||||
|
||||
for (const filename of componentStoryFiles) {
|
||||
expect(existsSync(join(storiesDir, filename)), `${filename} is missing`).toBe(true);
|
||||
|
|
@ -74,7 +75,7 @@ describe("Storybook inventory", () => {
|
|||
|
||||
test("keeps Storybook fixtures generic and content-free", () => {
|
||||
const storyText = readdirSync(storiesDir)
|
||||
.filter((filename) => filename.endsWith(".svelte") || filename.endsWith(".ts"))
|
||||
.filter((filename) => filename.endsWith(".tsx") || filename.endsWith(".ts"))
|
||||
.map((filename) => readFileSync(join(storiesDir, filename), "utf8").toLowerCase())
|
||||
.join("\n");
|
||||
|
||||
|
|
@ -90,12 +91,12 @@ describe("Storybook inventory", () => {
|
|||
"ScanlineField",
|
||||
"SignalTrace",
|
||||
]) {
|
||||
expect(existsSync(join(componentsDir, `${component}.svelte`)), `${component} is missing`).toBe(
|
||||
expect(existsSync(join(componentsDir, `${component}.tsx`)), `${component} is missing`).toBe(
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
existsSync(join(storiesDir, `${component}.stories.svelte`)),
|
||||
`${component}.stories.svelte is missing`,
|
||||
existsSync(join(storiesDir, `${component}.stories.tsx`)),
|
||||
`${component}.stories.tsx is missing`,
|
||||
).toBe(true);
|
||||
}
|
||||
});
|
||||
|
|
@ -107,7 +108,7 @@ describe("Storybook inventory", () => {
|
|||
"ScanlineField",
|
||||
"SignalTrace",
|
||||
]) {
|
||||
const source = readFileSync(join(componentsDir, `${component}.svelte`), "utf8");
|
||||
const source = readFileSync(join(componentsDir, `${component}.tsx`), "utf8");
|
||||
|
||||
expect(source).toContain('aria-hidden="true"');
|
||||
}
|
||||
|
|
@ -115,8 +116,16 @@ describe("Storybook inventory", () => {
|
|||
|
||||
test("does not add deferred form/navigation primitives", () => {
|
||||
for (const component of ["Input", "ToggleGroup", "ScrollArea"]) {
|
||||
expect(existsSync(join(root, `src/lib/ui/components/${component}.svelte`))).toBe(false);
|
||||
expect(existsSync(join(storiesDir, `${component}.stories.svelte`))).toBe(false);
|
||||
expect(existsSync(join(root, `src/lib/ui/components/${component}.tsx`))).toBe(false);
|
||||
expect(existsSync(join(storiesDir, `${component}.stories.tsx`))).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
test("does not keep Svelte stories in the React Storybook inventory", () => {
|
||||
const svelteStories = readdirSync(storiesDir).filter((filename) =>
|
||||
filename.endsWith(".stories.svelte"),
|
||||
);
|
||||
|
||||
expect(svelteStories).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue