@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.
- package/.storybook/main.ts +22 -0
- package/.storybook/manager-head.html +2 -0
- package/.storybook/mock-access-api.ts +5 -0
- package/.storybook/preview.css +5 -0
- package/.storybook/preview.tsx +73 -0
- package/CHANGELOG.md +51 -0
- package/package.json +22 -8
- package/postcss.config.js +6 -0
- package/stories/AccessDenied.stories.tsx +17 -0
- package/stories/AccessGate.stories.tsx +49 -0
- package/stories/Accordion.stories.tsx +56 -0
- package/stories/Alert.stories.tsx +88 -0
- package/stories/AmbientBackground.stories.tsx +28 -0
- package/stories/AnimatedCounter.stories.tsx +41 -0
- package/stories/AnimatedNumber.stories.tsx +39 -0
- package/stories/BackLink.stories.tsx +32 -0
- package/stories/Badge.stories.tsx +37 -0
- package/stories/Button.stories.tsx +57 -0
- package/stories/Card.stories.tsx +39 -0
- package/stories/Checkbox.stories.tsx +45 -0
- package/stories/CodeEditor.stories.tsx +67 -0
- package/stories/ColorPicker.stories.tsx +24 -0
- package/stories/CommandPalette.stories.tsx +36 -0
- package/stories/ConfirmationModal.stories.tsx +40 -0
- package/stories/DateRangeFilter.stories.tsx +30 -0
- package/stories/DateTimePicker.stories.tsx +26 -0
- package/stories/Dialog.stories.tsx +62 -0
- package/stories/DynamicForm.stories.tsx +83 -0
- package/stories/DynamicIcon.stories.tsx +52 -0
- package/stories/EditableText.stories.tsx +29 -0
- package/stories/Feedback.stories.tsx +45 -0
- package/stories/IDELayout.stories.tsx +70 -0
- package/stories/InfoBanner.stories.tsx +41 -0
- package/stories/Input.stories.tsx +29 -0
- package/stories/InstanceScopeBanner.stories.tsx +35 -0
- package/stories/Introduction.mdx +50 -0
- package/stories/Label.stories.tsx +21 -0
- package/stories/LinksEditor.stories.tsx +37 -0
- package/stories/Markdown.stories.tsx +35 -0
- package/stories/Menu.stories.tsx +35 -0
- package/stories/MetricTile.stories.tsx +31 -0
- package/stories/NavItem.stories.tsx +29 -0
- package/stories/NotFound.stories.tsx +17 -0
- package/stories/Page.stories.tsx +29 -0
- package/stories/PageLayout.stories.tsx +59 -0
- package/stories/PaginatedList.stories.tsx +44 -0
- package/stories/Pagination.stories.tsx +31 -0
- package/stories/PerformanceProvider.stories.tsx +33 -0
- package/stories/PluginConfigForm.stories.tsx +64 -0
- package/stories/Popover.stories.tsx +30 -0
- package/stories/SectionHeader.stories.tsx +20 -0
- package/stories/Select.stories.tsx +36 -0
- package/stories/Sheet.stories.tsx +59 -0
- package/stories/Slider.stories.tsx +24 -0
- package/stories/StatusCard.stories.tsx +30 -0
- package/stories/StatusUpdateTimeline.stories.tsx +77 -0
- package/stories/StrategyConfigCard.stories.tsx +57 -0
- package/stories/SubscribeButton.stories.tsx +27 -0
- package/stories/Table.stories.tsx +56 -0
- package/stories/Tabs.stories.tsx +40 -0
- package/stories/TerminalFeed.stories.tsx +47 -0
- package/stories/Textarea.stories.tsx +25 -0
- package/stories/ThemeProvider.stories.tsx +38 -0
- package/stories/Toast.stories.tsx +32 -0
- package/stories/Toggle.stories.tsx +43 -0
- package/stories/Tooltip.stories.tsx +20 -0
- package/stories/UserMenu.stories.tsx +35 -0
- package/tailwind.config.js +74 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import {
|
|
4
|
+
PluginConfigForm,
|
|
5
|
+
type PluginOption,
|
|
6
|
+
} from "../src/components/PluginConfigForm";
|
|
7
|
+
|
|
8
|
+
const meta: Meta = {
|
|
9
|
+
title: "Components/Forms/PluginConfigForm",
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default meta;
|
|
13
|
+
type Story = StoryObj;
|
|
14
|
+
|
|
15
|
+
const plugins: PluginOption[] = [
|
|
16
|
+
{
|
|
17
|
+
id: "http-probe",
|
|
18
|
+
displayName: "HTTP Probe",
|
|
19
|
+
description: "Probes a public HTTP endpoint and asserts a 2xx response.",
|
|
20
|
+
configSchema: {
|
|
21
|
+
type: "object",
|
|
22
|
+
required: ["url"],
|
|
23
|
+
properties: {
|
|
24
|
+
url: { type: "string", title: "URL", format: "uri" },
|
|
25
|
+
timeoutMs: { type: "number", title: "Timeout (ms)", default: 5000 },
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "tcp-probe",
|
|
31
|
+
displayName: "TCP Probe",
|
|
32
|
+
description: "Opens a TCP connection to verify the port is reachable.",
|
|
33
|
+
configSchema: {
|
|
34
|
+
type: "object",
|
|
35
|
+
required: ["host", "port"],
|
|
36
|
+
properties: {
|
|
37
|
+
host: { type: "string", title: "Host" },
|
|
38
|
+
port: { type: "number", title: "Port" },
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
const Demo = () => {
|
|
45
|
+
const [pluginId, setPluginId] = useState("http-probe");
|
|
46
|
+
const [config, setConfig] = useState<Record<string, unknown>>({});
|
|
47
|
+
return (
|
|
48
|
+
<div className="max-w-xl">
|
|
49
|
+
<PluginConfigForm
|
|
50
|
+
label="Strategy"
|
|
51
|
+
plugins={plugins}
|
|
52
|
+
selectedPluginId={pluginId}
|
|
53
|
+
onPluginChange={(id) => {
|
|
54
|
+
setPluginId(id);
|
|
55
|
+
setConfig({});
|
|
56
|
+
}}
|
|
57
|
+
config={config}
|
|
58
|
+
onConfigChange={setConfig}
|
|
59
|
+
/>
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const Default: Story = { render: () => <Demo /> };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { Button } from "../src/components/Button";
|
|
3
|
+
import {
|
|
4
|
+
Popover,
|
|
5
|
+
PopoverContent,
|
|
6
|
+
PopoverTrigger,
|
|
7
|
+
} from "../src/components/Popover";
|
|
8
|
+
|
|
9
|
+
const meta: Meta = {
|
|
10
|
+
title: "Components/Overlays/Popover",
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default meta;
|
|
14
|
+
type Story = StoryObj;
|
|
15
|
+
|
|
16
|
+
export const Default: Story = {
|
|
17
|
+
render: () => (
|
|
18
|
+
<Popover>
|
|
19
|
+
<PopoverTrigger asChild>
|
|
20
|
+
<Button variant="outline">Open popover</Button>
|
|
21
|
+
</PopoverTrigger>
|
|
22
|
+
<PopoverContent className="w-80 p-4">
|
|
23
|
+
<h4 className="font-medium leading-none mb-2">Filter</h4>
|
|
24
|
+
<p className="text-sm text-muted-foreground">
|
|
25
|
+
Use this for filter chips, info hovers, or quick edits.
|
|
26
|
+
</p>
|
|
27
|
+
</PopoverContent>
|
|
28
|
+
</Popover>
|
|
29
|
+
),
|
|
30
|
+
};
|
|
@@ -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="lg"</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 /> };
|