@checkstack/ui 1.8.0 → 1.8.1

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 +8 -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,20 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Activity } from "lucide-react";
3
+ import { SectionHeader } from "../src/components/SectionHeader";
4
+
5
+ const meta: Meta<typeof SectionHeader> = {
6
+ title: "Components/Layout/SectionHeader",
7
+ component: SectionHeader,
8
+ tags: ["autodocs"],
9
+ };
10
+
11
+ export default meta;
12
+ type Story = StoryObj<typeof SectionHeader>;
13
+
14
+ export const Default: Story = {
15
+ args: {
16
+ title: "Recent activity",
17
+ description: "Last 24 hours of health-check transitions.",
18
+ icon: <Activity className="h-5 w-5" />,
19
+ },
20
+ };
@@ -0,0 +1,36 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { useState } from "react";
3
+ import {
4
+ Select,
5
+ SelectContent,
6
+ SelectItem,
7
+ SelectTrigger,
8
+ SelectValue,
9
+ } from "../src/components/Select";
10
+
11
+ const meta: Meta = {
12
+ title: "Components/Inputs/Select",
13
+ };
14
+
15
+ export default meta;
16
+ type Story = StoryObj;
17
+
18
+ const Demo = () => {
19
+ const [value, setValue] = useState("");
20
+ return (
21
+ <div className="max-w-xs">
22
+ <Select value={value} onValueChange={setValue}>
23
+ <SelectTrigger>
24
+ <SelectValue placeholder="Pick an environment" />
25
+ </SelectTrigger>
26
+ <SelectContent>
27
+ <SelectItem value="prod">Production</SelectItem>
28
+ <SelectItem value="staging">Staging</SelectItem>
29
+ <SelectItem value="dev">Development</SelectItem>
30
+ </SelectContent>
31
+ </Select>
32
+ </div>
33
+ );
34
+ };
35
+
36
+ export const Default: Story = { render: () => <Demo /> };
@@ -0,0 +1,59 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Button } from "../src/components/Button";
3
+ import {
4
+ Sheet,
5
+ SheetBody,
6
+ SheetContent,
7
+ SheetDescription,
8
+ SheetHeader,
9
+ SheetTitle,
10
+ SheetTrigger,
11
+ } from "../src/components/Sheet";
12
+
13
+ const meta: Meta = {
14
+ title: "Components/Overlays/Sheet",
15
+ };
16
+
17
+ export default meta;
18
+ type Story = StoryObj;
19
+
20
+ export const Default: Story = {
21
+ render: () => (
22
+ <Sheet>
23
+ <SheetTrigger asChild>
24
+ <Button variant="outline">Open sheet</Button>
25
+ </SheetTrigger>
26
+ <SheetContent>
27
+ <SheetHeader>
28
+ <SheetTitle>Edit system</SheetTitle>
29
+ <SheetDescription>
30
+ Slide-in side panel for secondary tasks.
31
+ </SheetDescription>
32
+ </SheetHeader>
33
+ <SheetBody>
34
+ <p className="text-sm text-muted-foreground">Form contents…</p>
35
+ </SheetBody>
36
+ </SheetContent>
37
+ </Sheet>
38
+ ),
39
+ };
40
+
41
+ export const LargeSize: Story = {
42
+ render: () => (
43
+ <Sheet>
44
+ <SheetTrigger asChild>
45
+ <Button>Open large sheet</Button>
46
+ </SheetTrigger>
47
+ <SheetContent size="lg">
48
+ <SheetHeader>
49
+ <SheetTitle>Large sheet</SheetTitle>
50
+ </SheetHeader>
51
+ <SheetBody>
52
+ <p className="text-sm text-muted-foreground">
53
+ Use <code>size=&quot;lg&quot;</code> for richer side panels.
54
+ </p>
55
+ </SheetBody>
56
+ </SheetContent>
57
+ </Sheet>
58
+ ),
59
+ };
@@ -0,0 +1,24 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { useState } from "react";
3
+ import { Slider } from "../src/components/Slider";
4
+
5
+ const meta: Meta<typeof Slider> = {
6
+ title: "Components/Inputs/Slider",
7
+ component: Slider,
8
+ tags: ["autodocs"],
9
+ };
10
+
11
+ export default meta;
12
+ type Story = StoryObj<typeof Slider>;
13
+
14
+ const Demo = () => {
15
+ const [value, setValue] = useState([60]);
16
+ return (
17
+ <div className="max-w-md space-y-3">
18
+ <Slider value={value} onValueChange={setValue} min={0} max={100} step={1} />
19
+ <p className="text-sm text-muted-foreground">Value: {value[0]}</p>
20
+ </div>
21
+ );
22
+ };
23
+
24
+ export const Default: Story = { render: () => <Demo /> };
@@ -0,0 +1,30 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { TrendingUp } from "lucide-react";
3
+ import { StatusCard } from "../src/components/StatusCard";
4
+
5
+ const meta: Meta<typeof StatusCard> = {
6
+ title: "Components/Display/StatusCard",
7
+ component: StatusCard,
8
+ tags: ["autodocs"],
9
+ };
10
+
11
+ export default meta;
12
+ type Story = StoryObj<typeof StatusCard>;
13
+
14
+ export const Default: Story = {
15
+ args: {
16
+ title: "Uptime",
17
+ value: "99.95%",
18
+ description: "Last 30 days",
19
+ icon: <TrendingUp className="h-5 w-5" />,
20
+ },
21
+ };
22
+
23
+ export const Gradient: Story = {
24
+ args: {
25
+ title: "Throughput",
26
+ value: "1.2k req/s",
27
+ icon: <TrendingUp className="h-5 w-5" />,
28
+ variant: "gradient",
29
+ },
30
+ };
@@ -0,0 +1,77 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Badge } from "../src/components/Badge";
3
+ import {
4
+ StatusUpdateTimeline,
5
+ Timeline,
6
+ type StatusUpdate,
7
+ } from "../src/components/StatusUpdateTimeline";
8
+
9
+ const meta: Meta = {
10
+ title: "Components/Display/StatusUpdateTimeline",
11
+ };
12
+
13
+ export default meta;
14
+ type Story = StoryObj;
15
+
16
+ type IncidentStatus = "investigating" | "identified" | "monitoring" | "resolved";
17
+
18
+ const updates: StatusUpdate<IncidentStatus>[] = [
19
+ {
20
+ id: "1",
21
+ message: "We are investigating reports of elevated 5xx on /api.",
22
+ statusChange: "investigating",
23
+ createdAt: new Date(Date.now() - 1000 * 60 * 60 * 3),
24
+ createdByName: "On-call",
25
+ },
26
+ {
27
+ id: "2",
28
+ message: "Identified a misconfigured rate limiter; rolling back.",
29
+ statusChange: "identified",
30
+ createdAt: new Date(Date.now() - 1000 * 60 * 60 * 2),
31
+ createdByName: "On-call",
32
+ },
33
+ {
34
+ id: "3",
35
+ message: "Rollback completed. Monitoring error rate.",
36
+ statusChange: "monitoring",
37
+ createdAt: new Date(Date.now() - 1000 * 60 * 60),
38
+ createdByName: "On-call",
39
+ },
40
+ {
41
+ id: "4",
42
+ message: "Resolved.",
43
+ statusChange: "resolved",
44
+ createdAt: new Date(Date.now() - 1000 * 60 * 30),
45
+ createdByName: "On-call",
46
+ },
47
+ ];
48
+
49
+ export const Updates: Story = {
50
+ render: () => (
51
+ <div className="max-w-xl">
52
+ <StatusUpdateTimeline
53
+ updates={updates}
54
+ renderStatusBadge={(status) => (
55
+ <Badge variant={status === "resolved" ? "success" : "secondary"}>
56
+ {status}
57
+ </Badge>
58
+ )}
59
+ />
60
+ </div>
61
+ ),
62
+ };
63
+
64
+ export const GenericTimeline: Story = {
65
+ render: () => (
66
+ <div className="max-w-xl">
67
+ <Timeline
68
+ items={updates.map((u) => ({ id: u.id, date: u.createdAt, msg: u.message }))}
69
+ renderItem={(item) => (
70
+ <div className="rounded-md border border-border p-3">
71
+ <p className="text-sm">{item.msg}</p>
72
+ </div>
73
+ )}
74
+ />
75
+ </div>
76
+ ),
77
+ };
@@ -0,0 +1,57 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { useState } from "react";
3
+ import {
4
+ StrategyConfigCard,
5
+ type StrategyConfigCardData,
6
+ } from "../src/components/StrategyConfigCard";
7
+
8
+ const meta: Meta<typeof StrategyConfigCard> = {
9
+ title: "Components/Forms/StrategyConfigCard",
10
+ component: StrategyConfigCard,
11
+ tags: ["autodocs"],
12
+ };
13
+
14
+ export default meta;
15
+ type Story = StoryObj<typeof StrategyConfigCard>;
16
+
17
+ const strategy: StrategyConfigCardData = {
18
+ id: "http-probe",
19
+ displayName: "HTTP Probe",
20
+ description: "Probe a public HTTP endpoint and assert a 2xx response.",
21
+ icon: "Globe",
22
+ enabled: true,
23
+ };
24
+
25
+ const Demo = () => {
26
+ const [enabled, setEnabled] = useState(true);
27
+ const [config, setConfig] = useState<Record<string, unknown>>({
28
+ url: "https://example.com/health",
29
+ timeoutMs: 5000,
30
+ });
31
+ return (
32
+ <div className="max-w-2xl">
33
+ <StrategyConfigCard
34
+ strategy={{ ...strategy, enabled }}
35
+ onToggle={async (_, next) => setEnabled(next)}
36
+ configSections={[
37
+ {
38
+ id: "main",
39
+ title: "Configuration",
40
+ value: config,
41
+ schema: {
42
+ type: "object",
43
+ required: ["url"],
44
+ properties: {
45
+ url: { type: "string", title: "URL", format: "uri" },
46
+ timeoutMs: { type: "number", title: "Timeout (ms)", default: 5000 },
47
+ },
48
+ },
49
+ onSave: async (next) => setConfig(next),
50
+ },
51
+ ]}
52
+ />
53
+ </div>
54
+ );
55
+ };
56
+
57
+ export const Default: Story = { render: () => <Demo /> };
@@ -0,0 +1,27 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { useState } from "react";
3
+ import { SubscribeButton } from "../src/components/SubscribeButton";
4
+
5
+ const meta: Meta<typeof SubscribeButton> = {
6
+ title: "Components/Inputs/SubscribeButton",
7
+ component: SubscribeButton,
8
+ tags: ["autodocs"],
9
+ };
10
+
11
+ export default meta;
12
+ type Story = StoryObj<typeof SubscribeButton>;
13
+
14
+ const Demo = ({ size }: { size: "default" | "sm" | "lg" | "icon" }) => {
15
+ const [subscribed, setSubscribed] = useState(false);
16
+ return (
17
+ <SubscribeButton
18
+ isSubscribed={subscribed}
19
+ size={size}
20
+ onSubscribe={() => setSubscribed(true)}
21
+ onUnsubscribe={() => setSubscribed(false)}
22
+ />
23
+ );
24
+ };
25
+
26
+ export const IconButton: Story = { render: () => <Demo size="icon" /> };
27
+ export const Default: Story = { render: () => <Demo size="default" /> };
@@ -0,0 +1,56 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Badge } from "../src/components/Badge";
3
+ import {
4
+ Table,
5
+ TableBody,
6
+ TableCaption,
7
+ TableCell,
8
+ TableHead,
9
+ TableHeader,
10
+ TableRow,
11
+ } from "../src/components/Table";
12
+
13
+ const meta: Meta = {
14
+ title: "Components/Data/Table",
15
+ };
16
+
17
+ export default meta;
18
+ type Story = StoryObj;
19
+
20
+ export const Default: Story = {
21
+ render: () => (
22
+ <div className="max-w-2xl">
23
+ <Table>
24
+ <TableCaption>Recent health checks</TableCaption>
25
+ <TableHeader>
26
+ <TableRow>
27
+ <TableHead>Name</TableHead>
28
+ <TableHead>Strategy</TableHead>
29
+ <TableHead>Status</TableHead>
30
+ <TableHead className="text-right">Latency</TableHead>
31
+ </TableRow>
32
+ </TableHeader>
33
+ <TableBody>
34
+ <TableRow>
35
+ <TableCell>api.checkstack.io</TableCell>
36
+ <TableCell>HTTP probe</TableCell>
37
+ <TableCell><Badge variant="success">Healthy</Badge></TableCell>
38
+ <TableCell className="text-right">42 ms</TableCell>
39
+ </TableRow>
40
+ <TableRow>
41
+ <TableCell>db-primary</TableCell>
42
+ <TableCell>TCP probe</TableCell>
43
+ <TableCell><Badge variant="warning">Degraded</Badge></TableCell>
44
+ <TableCell className="text-right">340 ms</TableCell>
45
+ </TableRow>
46
+ <TableRow>
47
+ <TableCell>cache</TableCell>
48
+ <TableCell>HTTP probe</TableCell>
49
+ <TableCell><Badge variant="destructive">Down</Badge></TableCell>
50
+ <TableCell className="text-right">—</TableCell>
51
+ </TableRow>
52
+ </TableBody>
53
+ </Table>
54
+ </div>
55
+ ),
56
+ };
@@ -0,0 +1,40 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { useState } from "react";
3
+ import { TabPanel, Tabs, type TabItem } from "../src/components/Tabs";
4
+
5
+ const meta: Meta<typeof Tabs> = {
6
+ title: "Components/Navigation/Tabs",
7
+ component: Tabs,
8
+ tags: ["autodocs"],
9
+ };
10
+
11
+ export default meta;
12
+ type Story = StoryObj<typeof Tabs>;
13
+
14
+ const items: TabItem[] = [
15
+ { id: "overview", label: "Overview" },
16
+ { id: "history", label: "History" },
17
+ { id: "config", label: "Configuration" },
18
+ ];
19
+
20
+ const InteractiveTabs = () => {
21
+ const [active, setActive] = useState("overview");
22
+ return (
23
+ <div className="space-y-4">
24
+ <Tabs items={items} activeTab={active} onTabChange={setActive} />
25
+ <TabPanel tabId="overview" activeTab={active}>
26
+ <p className="text-sm text-muted-foreground">Overview content.</p>
27
+ </TabPanel>
28
+ <TabPanel tabId="history" activeTab={active}>
29
+ <p className="text-sm text-muted-foreground">History content.</p>
30
+ </TabPanel>
31
+ <TabPanel tabId="config" activeTab={active}>
32
+ <p className="text-sm text-muted-foreground">Configuration content.</p>
33
+ </TabPanel>
34
+ </div>
35
+ );
36
+ };
37
+
38
+ export const Basic: Story = {
39
+ render: () => <InteractiveTabs />,
40
+ };
@@ -0,0 +1,47 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { TerminalFeed, type TerminalEntry } from "../src/components/TerminalFeed";
3
+
4
+ const meta: Meta<typeof TerminalFeed> = {
5
+ title: "Components/Display/TerminalFeed",
6
+ component: TerminalFeed,
7
+ tags: ["autodocs"],
8
+ };
9
+
10
+ export default meta;
11
+ type Story = StoryObj<typeof TerminalFeed>;
12
+
13
+ const entries: TerminalEntry[] = [
14
+ {
15
+ id: "1",
16
+ timestamp: new Date(),
17
+ content: "Probe started for api.checkstack.io",
18
+ variant: "info",
19
+ },
20
+ {
21
+ id: "2",
22
+ timestamp: new Date(),
23
+ content: "200 OK in 42ms",
24
+ variant: "success",
25
+ },
26
+ {
27
+ id: "3",
28
+ timestamp: new Date(),
29
+ content: "Latency exceeds SLO threshold",
30
+ variant: "warning",
31
+ suffix: "p99 > 300ms",
32
+ },
33
+ {
34
+ id: "4",
35
+ timestamp: new Date(),
36
+ content: "Probe failed: connection refused",
37
+ variant: "error",
38
+ },
39
+ ];
40
+
41
+ export const Default: Story = {
42
+ args: {
43
+ entries,
44
+ title: "Probe activity",
45
+ maxHeight: "300px",
46
+ },
47
+ };
@@ -0,0 +1,25 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Label } from "../src/components/Label";
3
+ import { Textarea } from "../src/components/Textarea";
4
+
5
+ const meta: Meta<typeof Textarea> = {
6
+ title: "Components/Inputs/Textarea",
7
+ component: Textarea,
8
+ tags: ["autodocs"],
9
+ };
10
+
11
+ export default meta;
12
+ type Story = StoryObj<typeof Textarea>;
13
+
14
+ export const Default: Story = {
15
+ args: { placeholder: "Describe what changed…", rows: 4 },
16
+ };
17
+
18
+ export const WithLabel: Story = {
19
+ render: () => (
20
+ <div className="space-y-2 max-w-md">
21
+ <Label htmlFor="notes">Notes</Label>
22
+ <Textarea id="notes" rows={5} placeholder="Optional context…" />
23
+ </div>
24
+ ),
25
+ };
@@ -0,0 +1,38 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Button } from "../src/components/Button";
3
+ import { useTheme } from "../src/components/ThemeProvider";
4
+
5
+ const meta: Meta = {
6
+ title: "Foundations/ThemeProvider",
7
+ };
8
+
9
+ export default meta;
10
+ type Story = StoryObj;
11
+
12
+ const Demo = () => {
13
+ const { theme, resolvedTheme, setTheme } = useTheme();
14
+ return (
15
+ <div className="space-y-3">
16
+ <p className="text-sm">
17
+ Theme: <strong>{theme}</strong> (resolved: {resolvedTheme})
18
+ </p>
19
+ <div className="flex gap-2">
20
+ <Button variant={theme === "light" ? "primary" : "outline"} onClick={() => setTheme("light")}>
21
+ Light
22
+ </Button>
23
+ <Button variant={theme === "dark" ? "primary" : "outline"} onClick={() => setTheme("dark")}>
24
+ Dark
25
+ </Button>
26
+ <Button variant={theme === "system" ? "primary" : "outline"} onClick={() => setTheme("system")}>
27
+ System
28
+ </Button>
29
+ </div>
30
+ <p className="text-xs text-muted-foreground">
31
+ The host frontend wraps the app in <code>ThemeProvider</code>; plugins
32
+ inherit it for free.
33
+ </p>
34
+ </div>
35
+ );
36
+ };
37
+
38
+ export const Switcher: Story = { render: () => <Demo /> };
@@ -0,0 +1,32 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Button } from "../src/components/Button";
3
+ import { useToast } from "../src/components/ToastProvider";
4
+
5
+ const meta: Meta = {
6
+ title: "Components/Feedback/Toast",
7
+ };
8
+
9
+ export default meta;
10
+ type Story = StoryObj;
11
+
12
+ const Demo = () => {
13
+ const toast = useToast();
14
+ return (
15
+ <div className="flex flex-wrap gap-2">
16
+ <Button onClick={() => toast.success("Saved successfully.")}>
17
+ Success
18
+ </Button>
19
+ <Button variant="outline" onClick={() => toast.info("Reload to see updates.")}>
20
+ Info
21
+ </Button>
22
+ <Button variant="outline" onClick={() => toast.warning("Token expires in 24h.")}>
23
+ Warning
24
+ </Button>
25
+ <Button variant="destructive" onClick={() => toast.error("Probe failed.")}>
26
+ Error
27
+ </Button>
28
+ </div>
29
+ );
30
+ };
31
+
32
+ export const Triggers: Story = { render: () => <Demo /> };
@@ -0,0 +1,43 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { useState } from "react";
3
+ import { Toggle } from "../src/components/Toggle";
4
+
5
+ const meta: Meta<typeof Toggle> = {
6
+ title: "Components/Inputs/Toggle",
7
+ component: Toggle,
8
+ tags: ["autodocs"],
9
+ };
10
+
11
+ export default meta;
12
+ type Story = StoryObj<typeof Toggle>;
13
+
14
+ const InteractiveToggle = () => {
15
+ const [checked, setChecked] = useState(false);
16
+ return (
17
+ <div className="flex items-center gap-3">
18
+ <Toggle
19
+ checked={checked}
20
+ onCheckedChange={setChecked}
21
+ aria-label="Enable feature"
22
+ />
23
+ <span className="text-sm">{checked ? "Enabled" : "Disabled"}</span>
24
+ </div>
25
+ );
26
+ };
27
+
28
+ export const Interactive: Story = {
29
+ render: () => <InteractiveToggle />,
30
+ };
31
+
32
+ export const Disabled: Story = {
33
+ render: () => (
34
+ <Toggle
35
+ checked
36
+ disabled
37
+ onCheckedChange={() => {
38
+ /* readonly */
39
+ }}
40
+ aria-label="Read-only toggle"
41
+ />
42
+ ),
43
+ };
@@ -0,0 +1,20 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Tooltip } from "../src/components/Tooltip";
3
+
4
+ const meta: Meta<typeof Tooltip> = {
5
+ title: "Components/Overlays/Tooltip",
6
+ component: Tooltip,
7
+ tags: ["autodocs"],
8
+ };
9
+
10
+ export default meta;
11
+ type Story = StoryObj<typeof Tooltip>;
12
+
13
+ export const Hover: Story = {
14
+ render: () => (
15
+ <div className="flex items-center gap-2">
16
+ <span className="text-sm">Probe interval</span>
17
+ <Tooltip content="How frequently this check runs against its target." />
18
+ </div>
19
+ ),
20
+ };
@@ -0,0 +1,35 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { LogOut, Settings } from "lucide-react";
3
+ import {
4
+ DropdownMenuItem,
5
+ DropdownMenuLabel,
6
+ DropdownMenuSeparator,
7
+ } from "../src/components/Menu";
8
+ import { UserMenu } from "../src/components/UserMenu";
9
+
10
+ const meta: Meta<typeof UserMenu> = {
11
+ title: "Components/Navigation/UserMenu",
12
+ component: UserMenu,
13
+ tags: ["autodocs"],
14
+ };
15
+
16
+ export default meta;
17
+ type Story = StoryObj<typeof UserMenu>;
18
+
19
+ export const Default: Story = {
20
+ args: {
21
+ user: { name: "Nico", email: "nico@example.com" },
22
+ },
23
+ render: (args) => (
24
+ <UserMenu {...args}>
25
+ <DropdownMenuLabel>{args.user.email}</DropdownMenuLabel>
26
+ <DropdownMenuItem icon={<Settings className="h-4 w-4" />}>
27
+ Settings
28
+ </DropdownMenuItem>
29
+ <DropdownMenuSeparator />
30
+ <DropdownMenuItem icon={<LogOut className="h-4 w-4" />}>
31
+ Sign out
32
+ </DropdownMenuItem>
33
+ </UserMenu>
34
+ ),
35
+ };