@checkstack/ui 1.8.0 → 1.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/.storybook/main.ts +22 -0
  2. package/.storybook/manager-head.html +2 -0
  3. package/.storybook/mock-access-api.ts +5 -0
  4. package/.storybook/preview.css +5 -0
  5. package/.storybook/preview.tsx +73 -0
  6. package/CHANGELOG.md +51 -0
  7. package/package.json +22 -8
  8. package/postcss.config.js +6 -0
  9. package/stories/AccessDenied.stories.tsx +17 -0
  10. package/stories/AccessGate.stories.tsx +49 -0
  11. package/stories/Accordion.stories.tsx +56 -0
  12. package/stories/Alert.stories.tsx +88 -0
  13. package/stories/AmbientBackground.stories.tsx +28 -0
  14. package/stories/AnimatedCounter.stories.tsx +41 -0
  15. package/stories/AnimatedNumber.stories.tsx +39 -0
  16. package/stories/BackLink.stories.tsx +32 -0
  17. package/stories/Badge.stories.tsx +37 -0
  18. package/stories/Button.stories.tsx +57 -0
  19. package/stories/Card.stories.tsx +39 -0
  20. package/stories/Checkbox.stories.tsx +45 -0
  21. package/stories/CodeEditor.stories.tsx +67 -0
  22. package/stories/ColorPicker.stories.tsx +24 -0
  23. package/stories/CommandPalette.stories.tsx +36 -0
  24. package/stories/ConfirmationModal.stories.tsx +40 -0
  25. package/stories/DateRangeFilter.stories.tsx +30 -0
  26. package/stories/DateTimePicker.stories.tsx +26 -0
  27. package/stories/Dialog.stories.tsx +62 -0
  28. package/stories/DynamicForm.stories.tsx +83 -0
  29. package/stories/DynamicIcon.stories.tsx +52 -0
  30. package/stories/EditableText.stories.tsx +29 -0
  31. package/stories/Feedback.stories.tsx +45 -0
  32. package/stories/IDELayout.stories.tsx +70 -0
  33. package/stories/InfoBanner.stories.tsx +41 -0
  34. package/stories/Input.stories.tsx +29 -0
  35. package/stories/InstanceScopeBanner.stories.tsx +35 -0
  36. package/stories/Introduction.mdx +50 -0
  37. package/stories/Label.stories.tsx +21 -0
  38. package/stories/LinksEditor.stories.tsx +37 -0
  39. package/stories/Markdown.stories.tsx +35 -0
  40. package/stories/Menu.stories.tsx +35 -0
  41. package/stories/MetricTile.stories.tsx +31 -0
  42. package/stories/NavItem.stories.tsx +29 -0
  43. package/stories/NotFound.stories.tsx +17 -0
  44. package/stories/Page.stories.tsx +29 -0
  45. package/stories/PageLayout.stories.tsx +59 -0
  46. package/stories/PaginatedList.stories.tsx +44 -0
  47. package/stories/Pagination.stories.tsx +31 -0
  48. package/stories/PerformanceProvider.stories.tsx +33 -0
  49. package/stories/PluginConfigForm.stories.tsx +64 -0
  50. package/stories/Popover.stories.tsx +30 -0
  51. package/stories/SectionHeader.stories.tsx +20 -0
  52. package/stories/Select.stories.tsx +36 -0
  53. package/stories/Sheet.stories.tsx +59 -0
  54. package/stories/Slider.stories.tsx +24 -0
  55. package/stories/StatusCard.stories.tsx +30 -0
  56. package/stories/StatusUpdateTimeline.stories.tsx +77 -0
  57. package/stories/StrategyConfigCard.stories.tsx +57 -0
  58. package/stories/SubscribeButton.stories.tsx +27 -0
  59. package/stories/Table.stories.tsx +56 -0
  60. package/stories/Tabs.stories.tsx +40 -0
  61. package/stories/TerminalFeed.stories.tsx +47 -0
  62. package/stories/Textarea.stories.tsx +25 -0
  63. package/stories/ThemeProvider.stories.tsx +38 -0
  64. package/stories/Toast.stories.tsx +32 -0
  65. package/stories/Toggle.stories.tsx +43 -0
  66. package/stories/Tooltip.stories.tsx +20 -0
  67. package/stories/UserMenu.stories.tsx +35 -0
  68. package/tailwind.config.js +74 -0
@@ -0,0 +1,22 @@
1
+ import type { StorybookConfig } from "@storybook/react-vite";
2
+
3
+ const config: StorybookConfig = {
4
+ stories: [
5
+ "../stories/**/*.mdx",
6
+ "../stories/**/*.stories.@(ts|tsx)",
7
+ ],
8
+ addons: [
9
+ "@storybook/addon-docs",
10
+ "@storybook/addon-themes",
11
+ "@storybook/addon-a11y",
12
+ ],
13
+ framework: {
14
+ name: "@storybook/react-vite",
15
+ options: {},
16
+ },
17
+ typescript: {
18
+ reactDocgen: "react-docgen",
19
+ },
20
+ };
21
+
22
+ export default config;
@@ -0,0 +1,2 @@
1
+ <title>Checkstack UI — Component Library</title>
2
+ <meta name="description" content="Component library for Checkstack plugin developers." />
@@ -0,0 +1,5 @@
1
+ import type { AccessApi } from "@checkstack/frontend-api";
2
+
3
+ export const mockAccessApi: AccessApi = {
4
+ useAccess: () => ({ loading: false, allowed: true }),
5
+ };
@@ -0,0 +1,5 @@
1
+ @import "../src/themes.css";
2
+
3
+ @tailwind base;
4
+ @tailwind components;
5
+ @tailwind utilities;
@@ -0,0 +1,73 @@
1
+ import type { Preview } from "@storybook/react";
2
+ import { withThemeByClassName } from "@storybook/addon-themes";
3
+ import React from "react";
4
+ import { MemoryRouter } from "react-router-dom";
5
+ import {
6
+ ApiRegistryBuilder,
7
+ ApiProvider,
8
+ accessApiRef,
9
+ } from "@checkstack/frontend-api";
10
+ import { ThemeProvider } from "../src/components/ThemeProvider";
11
+ import { ToastProvider } from "../src/components/ToastProvider";
12
+ import { PerformanceProvider } from "../src/components/PerformanceProvider";
13
+ import { mockAccessApi } from "./mock-access-api";
14
+ import "./preview.css";
15
+
16
+ const apiRegistry = new ApiRegistryBuilder()
17
+ .register(accessApiRef, mockAccessApi)
18
+ .build();
19
+
20
+ const preview: Preview = {
21
+ parameters: {
22
+ controls: {
23
+ matchers: {
24
+ color: /(background|color)$/i,
25
+ date: /Date$/i,
26
+ },
27
+ },
28
+ backgrounds: {
29
+ default: "app",
30
+ values: [
31
+ { name: "app", value: "hsl(0 0% 100%)" },
32
+ { name: "app-dark", value: "hsl(240 10% 4%)" },
33
+ ],
34
+ },
35
+ layout: "padded",
36
+ options: {
37
+ storySort: {
38
+ order: [
39
+ "Introduction",
40
+ "Foundations",
41
+ "Components",
42
+ ["Inputs", "Display", "Feedback", "Navigation", "Overlays", "Forms", "Layout", "Data"],
43
+ ],
44
+ },
45
+ },
46
+ },
47
+ decorators: [
48
+ withThemeByClassName({
49
+ themes: {
50
+ light: "light",
51
+ dark: "dark",
52
+ },
53
+ defaultTheme: "light",
54
+ }),
55
+ (Story) => (
56
+ <MemoryRouter>
57
+ <ApiProvider registry={apiRegistry}>
58
+ <ThemeProvider defaultTheme="light" storageKey="checkstack-storybook-theme">
59
+ <ToastProvider>
60
+ <PerformanceProvider>
61
+ <div className="bg-background text-foreground p-4">
62
+ <Story />
63
+ </div>
64
+ </PerformanceProvider>
65
+ </ToastProvider>
66
+ </ThemeProvider>
67
+ </ApiProvider>
68
+ </MemoryRouter>
69
+ ),
70
+ ],
71
+ };
72
+
73
+ export default preview;
package/CHANGELOG.md CHANGED
@@ -1,5 +1,56 @@
1
1
  # @checkstack/ui
2
2
 
3
+ ## 1.8.2
4
+
5
+ ### Patch Changes
6
+
7
+ - b627562: Bump direct and transitive dependencies to clear MEDIUM-severity advisories
8
+ that Trivy now surfaces alongside CRITICAL/HIGH.
9
+
10
+ Direct version bumps in package.json:
11
+
12
+ - `@checkstack/catalog-backend`, `@checkstack/gitops-backend`,
13
+ `@checkstack/healthcheck-frontend`: `uuid` `^13.0.0` → `^14.0.0`
14
+ (GHSA-w5hq-g745-h8pq, missing buffer bounds check in v3/v5/v6). Also
15
+ dropped the now-redundant `@types/uuid` devDependency — uuid 14 ships
16
+ its own types and the npm `@types/uuid` package is a stub.
17
+ - `@checkstack/gitops-backend`: `yaml` `^2.7.0` → `^2.8.3`
18
+ (GHSA-48c2-rrv3-qjmp, stack overflow on deeply nested collections).
19
+ - `@checkstack/dev-server`: `vite` `^5.4.0` → `^8.0.12`
20
+ (GHSA-4w7w-66w2-5vf9, path traversal in optimized-deps `.map` handling)
21
+ and `@vitejs/plugin-react` `^4.3.4` → `^6.0.1` to stay inside the new
22
+ vite peer range.
23
+
24
+ Root `overrides` / `resolutions` to bypass transitive pins that block the
25
+ walk:
26
+
27
+ - `dompurify` `^3.4.3` — `monaco-editor@0.55.1` pins `dompurify@3.2.7`
28
+ exactly, so the only way to pick up the eight DOMPurify XSS / prototype
29
+ pollution advisories (GHSA-v2wj-7wpq-c8vv et al.) is an override.
30
+ Affects `@checkstack/ui`, which is the only consumer of monaco.
31
+ - `uuid` `^14.0.0` — also forces `bullmq`'s nested `uuid@11.1.0`
32
+ (vulnerable per GHSA-w5hq-g745-h8pq) to the patched line. Affects
33
+ `@checkstack/queue-bullmq-backend`.
34
+ - `yaml` `^2.9.0` — covers transitive resolutions that would otherwise
35
+ pin pre-2.8.3 yaml.
36
+
37
+ The CI image scan (`.github/workflows/pr-checks.yml`) and the local
38
+ `bun run audit:*` helper now include `MEDIUM` alongside `CRITICAL,HIGH`,
39
+ so future MEDIUM regressions fail the pipeline. The production Dockerfile
40
+ also strips vendored `test/`, `tests/`, `__tests__/`, `benchmark/`,
41
+ `benchmarks/`, `example/` and `examples/` folders from `node_modules`
42
+ before the runtime stage — those tarball artefacts ship their own
43
+ nested `package.json` (`benchmark`, `tedious-benchmarks`, etc.) which
44
+ Trivy was scanning as if they were real packages.
45
+
46
+ ## 1.8.1
47
+
48
+ ### Patch Changes
49
+
50
+ - Updated dependencies [9016526]
51
+ - @checkstack/common@0.10.0
52
+ - @checkstack/frontend-api@0.5.1
53
+
3
54
  ## 1.8.0
4
55
 
5
56
  ### Minor Changes
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@checkstack/ui",
3
- "version": "1.8.0",
3
+ "version": "1.8.2",
4
4
  "license": "Elastic-2.0",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
7
7
  "dependencies": {
8
- "@checkstack/common": "0.8.0",
9
- "@checkstack/frontend-api": "0.4.2",
8
+ "@checkstack/common": "0.10.0",
9
+ "@checkstack/frontend-api": "0.5.1",
10
10
  "@monaco-editor/react": "^4.7.0",
11
11
  "@radix-ui/react-accordion": "^1.2.12",
12
12
  "@radix-ui/react-dialog": "^1.1.15",
@@ -23,26 +23,40 @@
23
23
  "monaco-editor": "^0.55.1",
24
24
  "react": "^18.2.0",
25
25
  "react-day-picker": "^9.13.0",
26
- "react-dom": "^19.2.5",
26
+ "react-dom": "^18.2.0",
27
27
  "react-markdown": "^10.1.0",
28
28
  "react-router-dom": "^6.20.0",
29
29
  "recharts": "^3.6.0",
30
30
  "tailwind-merge": "^2.2.0"
31
31
  },
32
32
  "devDependencies": {
33
- "@checkstack/scripts": "0.3.0",
33
+ "@checkstack/scripts": "0.3.2",
34
34
  "@checkstack/test-utils-frontend": "0.0.5",
35
35
  "@checkstack/tsconfig": "0.0.7",
36
+ "@storybook/addon-a11y": "^10.3.6",
37
+ "@storybook/addon-docs": "^10.3.6",
38
+ "@storybook/addon-themes": "^10.3.6",
39
+ "@storybook/react": "^10.3.6",
40
+ "@storybook/react-vite": "^10.3.6",
36
41
  "@testing-library/react": "^16.0.0",
37
42
  "@types/react": "^18.2.0",
38
- "@types/react-dom": "^19.2.3",
39
- "typescript": "^5.0.0"
43
+ "@types/react-dom": "^18.2.0",
44
+ "@vitejs/plugin-react": "^6.0.1",
45
+ "autoprefixer": "^10.4.18",
46
+ "postcss": "^8.4.35",
47
+ "storybook": "^10.3.6",
48
+ "tailwindcss": "^3.4.1",
49
+ "tailwindcss-animate": "^1.0.7",
50
+ "typescript": "^5.0.0",
51
+ "vite": "^8.0.8"
40
52
  },
41
53
  "scripts": {
42
54
  "typecheck": "tsgo -b",
43
55
  "lint": "bun run lint:code",
44
56
  "lint:code": "eslint . --max-warnings 0",
45
- "test": "bun test"
57
+ "test": "bun test",
58
+ "storybook": "storybook dev -p 6006",
59
+ "storybook:build": "storybook build -o storybook-static"
46
60
  },
47
61
  "checkstack": {
48
62
  "type": "tooling"
@@ -0,0 +1,6 @@
1
+ export default {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ };
@@ -0,0 +1,17 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { AccessDenied } from "../src/components/AccessDenied";
3
+
4
+ const meta: Meta<typeof AccessDenied> = {
5
+ title: "Components/Feedback/AccessDenied",
6
+ component: AccessDenied,
7
+ tags: ["autodocs"],
8
+ };
9
+
10
+ export default meta;
11
+ type Story = StoryObj<typeof AccessDenied>;
12
+
13
+ export const Default: Story = {};
14
+
15
+ export const CustomMessage: Story = {
16
+ args: { message: "You need 'catalog.manage' to view this page." },
17
+ };
@@ -0,0 +1,49 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { AccessGate } from "../src/components/AccessGate";
3
+
4
+ const allowed = () => ({ loading: false, allowed: true });
5
+ const denied = () => ({ loading: false, allowed: false });
6
+ const loading = () => ({ loading: true, allowed: false });
7
+
8
+ const meta: Meta<typeof AccessGate> = {
9
+ title: "Components/Feedback/AccessGate",
10
+ component: AccessGate,
11
+ tags: ["autodocs"],
12
+ };
13
+
14
+ export default meta;
15
+ type Story = StoryObj<typeof AccessGate>;
16
+
17
+ export const Allowed: Story = {
18
+ args: {
19
+ accessRuleId: "demo.read",
20
+ useAccess: allowed,
21
+ children: <p className="text-sm">Gated content visible.</p>,
22
+ },
23
+ };
24
+
25
+ export const Hidden: Story = {
26
+ args: {
27
+ accessRuleId: "demo.read",
28
+ useAccess: denied,
29
+ children: <p className="text-sm">You should not see this.</p>,
30
+ },
31
+ };
32
+
33
+ export const ShowDenied: Story = {
34
+ args: {
35
+ accessRuleId: "demo.read",
36
+ useAccess: denied,
37
+ showDenied: true,
38
+ deniedMessage: "Demo access denied.",
39
+ children: <p>Hidden</p>,
40
+ },
41
+ };
42
+
43
+ export const Loading: Story = {
44
+ args: {
45
+ accessRuleId: "demo.read",
46
+ useAccess: loading,
47
+ children: <p>Resolves once access loads.</p>,
48
+ },
49
+ };
@@ -0,0 +1,56 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import {
3
+ Accordion,
4
+ AccordionContent,
5
+ AccordionItem,
6
+ AccordionTrigger,
7
+ } from "../src/components/Accordion";
8
+
9
+ const meta: Meta<typeof Accordion> = {
10
+ title: "Components/Display/Accordion",
11
+ component: Accordion,
12
+ tags: ["autodocs"],
13
+ };
14
+
15
+ export default meta;
16
+ type Story = StoryObj<typeof Accordion>;
17
+
18
+ export const Single: Story = {
19
+ render: () => (
20
+ <Accordion type="single" collapsible className="max-w-md">
21
+ <AccordionItem value="a">
22
+ <AccordionTrigger>What is a health check?</AccordionTrigger>
23
+ <AccordionContent>
24
+ A periodic probe that asserts a system property and reports a status.
25
+ </AccordionContent>
26
+ </AccordionItem>
27
+ <AccordionItem value="b">
28
+ <AccordionTrigger>What is a strategy?</AccordionTrigger>
29
+ <AccordionContent>
30
+ A pluggable provider that defines how a check is executed.
31
+ </AccordionContent>
32
+ </AccordionItem>
33
+ <AccordionItem value="c">
34
+ <AccordionTrigger>What is a satellite?</AccordionTrigger>
35
+ <AccordionContent>
36
+ A standalone agent that runs checks from a remote network location.
37
+ </AccordionContent>
38
+ </AccordionItem>
39
+ </Accordion>
40
+ ),
41
+ };
42
+
43
+ export const Multiple: Story = {
44
+ render: () => (
45
+ <Accordion type="multiple" className="max-w-md">
46
+ <AccordionItem value="a">
47
+ <AccordionTrigger>Section one</AccordionTrigger>
48
+ <AccordionContent>Content one.</AccordionContent>
49
+ </AccordionItem>
50
+ <AccordionItem value="b">
51
+ <AccordionTrigger>Section two</AccordionTrigger>
52
+ <AccordionContent>Content two.</AccordionContent>
53
+ </AccordionItem>
54
+ </Accordion>
55
+ ),
56
+ };
@@ -0,0 +1,88 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { CircleAlert, CircleCheck, Info, TriangleAlert } from "lucide-react";
3
+ import {
4
+ Alert,
5
+ AlertContent,
6
+ AlertDescription,
7
+ AlertIcon,
8
+ AlertTitle,
9
+ } from "../src/components/Alert";
10
+
11
+ const meta: Meta<typeof Alert> = {
12
+ title: "Components/Feedback/Alert",
13
+ component: Alert,
14
+ tags: ["autodocs"],
15
+ argTypes: {
16
+ variant: {
17
+ control: "select",
18
+ options: ["default", "success", "warning", "error", "info"],
19
+ },
20
+ },
21
+ };
22
+
23
+ export default meta;
24
+ type Story = StoryObj<typeof Alert>;
25
+
26
+ export const Default: Story = {
27
+ args: { variant: "default" },
28
+ render: (args) => (
29
+ <Alert {...args}>
30
+ <AlertIcon>
31
+ <Info className="h-4 w-4" />
32
+ </AlertIcon>
33
+ <AlertContent>
34
+ <AlertTitle>Heads up</AlertTitle>
35
+ <AlertDescription>
36
+ This is an informational alert for plugin developers.
37
+ </AlertDescription>
38
+ </AlertContent>
39
+ </Alert>
40
+ ),
41
+ };
42
+
43
+ export const Success: Story = {
44
+ args: { variant: "success" },
45
+ render: (args) => (
46
+ <Alert {...args}>
47
+ <AlertIcon>
48
+ <CircleCheck className="h-4 w-4" />
49
+ </AlertIcon>
50
+ <AlertContent>
51
+ <AlertTitle>Saved</AlertTitle>
52
+ <AlertDescription>Plugin configuration was saved.</AlertDescription>
53
+ </AlertContent>
54
+ </Alert>
55
+ ),
56
+ };
57
+
58
+ export const Warning: Story = {
59
+ args: { variant: "warning" },
60
+ render: (args) => (
61
+ <Alert {...args}>
62
+ <AlertIcon>
63
+ <TriangleAlert className="h-4 w-4" />
64
+ </AlertIcon>
65
+ <AlertContent>
66
+ <AlertTitle>Heads up</AlertTitle>
67
+ <AlertDescription>
68
+ The satellite token will expire in 24 hours.
69
+ </AlertDescription>
70
+ </AlertContent>
71
+ </Alert>
72
+ ),
73
+ };
74
+
75
+ export const Error: Story = {
76
+ args: { variant: "error" },
77
+ render: (args) => (
78
+ <Alert {...args}>
79
+ <AlertIcon>
80
+ <CircleAlert className="h-4 w-4" />
81
+ </AlertIcon>
82
+ <AlertContent>
83
+ <AlertTitle>Something went wrong</AlertTitle>
84
+ <AlertDescription>The health check failed to start.</AlertDescription>
85
+ </AlertContent>
86
+ </Alert>
87
+ ),
88
+ };
@@ -0,0 +1,28 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { AmbientBackground } from "../src/components/AmbientBackground";
3
+
4
+ const meta: Meta<typeof AmbientBackground> = {
5
+ title: "Components/Layout/AmbientBackground",
6
+ component: AmbientBackground,
7
+ tags: ["autodocs"],
8
+ parameters: {
9
+ layout: "fullscreen",
10
+ },
11
+ };
12
+
13
+ export default meta;
14
+ type Story = StoryObj<typeof AmbientBackground>;
15
+
16
+ export const Default: Story = {
17
+ render: () => (
18
+ <AmbientBackground>
19
+ <div className="max-w-3xl mx-auto py-24 px-6">
20
+ <h1 className="text-4xl font-bold tracking-tight">Inverse glow grid</h1>
21
+ <p className="mt-4 text-muted-foreground">
22
+ Aurora blobs and a grid mask, with automatic graceful degradation when{" "}
23
+ <code>usePerformance().isLowPower</code> is true.
24
+ </p>
25
+ </div>
26
+ </AmbientBackground>
27
+ ),
28
+ };
@@ -0,0 +1,41 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { useEffect, useState } from "react";
3
+ import { AnimatedCounter } from "../src/components/AnimatedCounter";
4
+
5
+ const meta: Meta<typeof AnimatedCounter> = {
6
+ title: "Components/Display/AnimatedCounter",
7
+ component: AnimatedCounter,
8
+ tags: ["autodocs"],
9
+ };
10
+
11
+ export default meta;
12
+ type Story = StoryObj<typeof AnimatedCounter>;
13
+
14
+ const Demo = () => {
15
+ const [value, setValue] = useState(0);
16
+ useEffect(() => {
17
+ const id = setInterval(
18
+ () => setValue((v) => (v >= 1000 ? 0 : v + Math.floor(Math.random() * 200))),
19
+ 1500,
20
+ );
21
+ return () => clearInterval(id);
22
+ }, []);
23
+ return (
24
+ <div className="text-4xl font-bold">
25
+ <AnimatedCounter value={value} />
26
+ </div>
27
+ );
28
+ };
29
+
30
+ export const TickingUp: Story = {
31
+ render: () => <Demo />,
32
+ };
33
+
34
+ export const StaticValue: Story = {
35
+ args: { value: 1042 },
36
+ render: (args) => (
37
+ <span className="text-3xl font-bold">
38
+ <AnimatedCounter {...args} />
39
+ </span>
40
+ ),
41
+ };
@@ -0,0 +1,39 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { useEffect, useState } from "react";
3
+ import { AnimatedNumber } from "../src/components/AnimatedNumber";
4
+
5
+ const meta: Meta<typeof AnimatedNumber> = {
6
+ title: "Components/Display/AnimatedNumber",
7
+ component: AnimatedNumber,
8
+ tags: ["autodocs"],
9
+ };
10
+
11
+ export default meta;
12
+ type Story = StoryObj<typeof AnimatedNumber>;
13
+
14
+ const Demo = () => {
15
+ const [value, setValue] = useState(99.95);
16
+ useEffect(() => {
17
+ const id = setInterval(
18
+ () => setValue(95 + Math.random() * 5),
19
+ 1500,
20
+ );
21
+ return () => clearInterval(id);
22
+ }, []);
23
+ return (
24
+ <div className="text-3xl font-bold text-success tabular-nums">
25
+ <AnimatedNumber value={value} suffix="%" decimals={2} />
26
+ </div>
27
+ );
28
+ };
29
+
30
+ export const Uptime: Story = { render: () => <Demo /> };
31
+
32
+ export const NA: Story = {
33
+ args: { value: undefined },
34
+ render: (args) => (
35
+ <span className="text-2xl font-bold">
36
+ <AnimatedNumber {...args} />
37
+ </span>
38
+ ),
39
+ };
@@ -0,0 +1,32 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { BackLink } from "../src/components/BackLink";
3
+
4
+ const meta: Meta<typeof BackLink> = {
5
+ title: "Components/Navigation/BackLink",
6
+ component: BackLink,
7
+ tags: ["autodocs"],
8
+ };
9
+
10
+ export default meta;
11
+ type Story = StoryObj<typeof BackLink>;
12
+
13
+ export const Default: Story = {
14
+ args: {
15
+ onClick: () => {
16
+ /* navigate back */
17
+ },
18
+ },
19
+ };
20
+
21
+ export const CustomLabel: Story = {
22
+ args: {
23
+ children: "Back to systems",
24
+ onClick: () => {
25
+ /* navigate back */
26
+ },
27
+ },
28
+ };
29
+
30
+ export const AsLink: Story = {
31
+ args: { to: "/catalog" },
32
+ };
@@ -0,0 +1,37 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Badge } from "../src/components/Badge";
3
+
4
+ const meta: Meta<typeof Badge> = {
5
+ title: "Components/Display/Badge",
6
+ component: Badge,
7
+ tags: ["autodocs"],
8
+ argTypes: {
9
+ variant: {
10
+ control: "select",
11
+ options: ["default", "secondary", "destructive", "outline", "success", "warning", "info"],
12
+ },
13
+ },
14
+ args: {
15
+ children: "Badge",
16
+ variant: "default",
17
+ },
18
+ };
19
+
20
+ export default meta;
21
+ type Story = StoryObj<typeof Badge>;
22
+
23
+ export const Default: Story = {};
24
+
25
+ export const Variants: Story = {
26
+ render: () => (
27
+ <div className="flex flex-wrap gap-2">
28
+ <Badge variant="default">Default</Badge>
29
+ <Badge variant="secondary">Secondary</Badge>
30
+ <Badge variant="destructive">Destructive</Badge>
31
+ <Badge variant="outline">Outline</Badge>
32
+ <Badge variant="success">Success</Badge>
33
+ <Badge variant="warning">Warning</Badge>
34
+ <Badge variant="info">Info</Badge>
35
+ </div>
36
+ ),
37
+ };