Skip to content

Commit b443b2d

Browse files
samejrclaude
andcommitted
feat(webapp): cover the remaining components in the storybook
New stories, all with static data and every variant/size/state in grid form: theme tokens, slider, pagination, sheet, copy & clipboard, dates & timers, run statuses (status combos, run icons, tags), log levels, indicators (pulsing dot, step number, animated number/arrow), settings rows, accordion + alert dialog, and the usage sparkline (the old sidebar's dead "usage" link). Extends input fields with SearchInput and InputOTP, date fields with the date-time and duration pickers, and cells with PropertyTable, LabelValueStack and MiddleTruncate. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent b9a30e7 commit b443b2d

16 files changed

Lines changed: 1372 additions & 81 deletions

File tree

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { BookOpenIcon, Cog8ToothIcon } from "@heroicons/react/20/solid";
2+
import {
3+
Accordion,
4+
AccordionContent,
5+
AccordionItem,
6+
AccordionTrigger,
7+
} from "~/components/primitives/Accordion";
8+
import { Paragraph } from "~/components/primitives/Paragraph";
9+
import { StoryPage, StorySection } from "../storybook/StoryKit";
10+
11+
export default function Story_() {
12+
return (
13+
<StoryPage title="Accordion" description="Collapsible sections built on Radix Accordion.">
14+
<StorySection title="Single, collapsible">
15+
<div className="max-w-xl rounded-sm border border-grid-dimmed p-2">
16+
<Accordion type="single" collapsible>
17+
<AccordionItem value="what">
18+
<AccordionTrigger>What counts as a run?</AccordionTrigger>
19+
<AccordionContent>
20+
<Paragraph variant="small">
21+
Every task execution is a run, including retries and child tasks triggered from a
22+
parent.
23+
</Paragraph>
24+
</AccordionContent>
25+
</AccordionItem>
26+
<AccordionItem value="limits">
27+
<AccordionTrigger>How do concurrency limits work?</AccordionTrigger>
28+
<AccordionContent>
29+
<Paragraph variant="small">
30+
Each environment has a concurrency limit; runs beyond it queue until a slot frees
31+
up.
32+
</Paragraph>
33+
</AccordionContent>
34+
</AccordionItem>
35+
</Accordion>
36+
</div>
37+
</StorySection>
38+
39+
<StorySection title="Multiple open, with leading icons">
40+
<div className="max-w-xl rounded-sm border border-grid-dimmed p-2">
41+
<Accordion type="multiple" defaultValue={["docs"]}>
42+
<AccordionItem value="docs">
43+
<AccordionTrigger leadingIcon={BookOpenIcon} leadingIconClassName="text-blue-500">
44+
Documentation
45+
</AccordionTrigger>
46+
<AccordionContent>
47+
<Paragraph variant="small">Open by default via defaultValue.</Paragraph>
48+
</AccordionContent>
49+
</AccordionItem>
50+
<AccordionItem value="settings">
51+
<AccordionTrigger leadingIcon={Cog8ToothIcon} leadingIconClassName="text-text-dimmed">
52+
Advanced settings
53+
</AccordionTrigger>
54+
<AccordionContent>
55+
<Paragraph variant="small">
56+
Both items can be open at once with type="multiple".
57+
</Paragraph>
58+
</AccordionContent>
59+
</AccordionItem>
60+
</Accordion>
61+
</div>
62+
</StorySection>
63+
</StoryPage>
64+
);
65+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { CopyButton } from "~/components/primitives/CopyButton";
2+
import { CopyTextLink } from "~/components/primitives/CopyTextLink";
3+
import { CopyableText } from "~/components/primitives/CopyableText";
4+
import { TruncatedCopyableValue } from "~/components/primitives/TruncatedCopyableValue";
5+
import { Story, StoryGrid, StoryPage, StorySection } from "../storybook/StoryKit";
6+
7+
const RUN_ID = "run_cmsq1zzim1g2y2k8q1az3sfqf";
8+
9+
export default function Story_() {
10+
return (
11+
<StoryPage
12+
title="Copy & clipboard"
13+
description="The copy affordances: buttons, links, inline text and truncated IDs. Click any of them."
14+
>
15+
<StorySection title="CopyButton — button variant" description="Sizes with a label child.">
16+
<StoryGrid min="13rem">
17+
<Story label="extra-small">
18+
<CopyButton value={RUN_ID} variant="button" size="extra-small">
19+
Copy
20+
</CopyButton>
21+
</Story>
22+
<Story label="small">
23+
<CopyButton value={RUN_ID} variant="button" size="small">
24+
Copy
25+
</CopyButton>
26+
</Story>
27+
<Story label="medium">
28+
<CopyButton value={RUN_ID} variant="button" size="medium">
29+
Copy
30+
</CopyButton>
31+
</Story>
32+
</StoryGrid>
33+
</StorySection>
34+
35+
<StorySection title="CopyButton — icon variant">
36+
<StoryGrid min="13rem">
37+
<Story label="extra-small">
38+
<CopyButton value={RUN_ID} variant="icon" size="extra-small" />
39+
</Story>
40+
<Story label="small">
41+
<CopyButton value={RUN_ID} variant="icon" size="small" />
42+
</Story>
43+
<Story label="medium">
44+
<CopyButton value={RUN_ID} variant="icon" size="medium" />
45+
</Story>
46+
</StoryGrid>
47+
</StorySection>
48+
49+
<StorySection title="CopyableText" description="Inline text that copies on click.">
50+
<StoryGrid min="16rem">
51+
<Story label="Default (icon on hover)">
52+
<CopyableText value={RUN_ID} className="font-mono text-xs" />
53+
</Story>
54+
<Story label="icon-right">
55+
<CopyableText value="hello-world" variant="icon-right" className="text-sm" />
56+
</Story>
57+
<Story label="Copy a different value">
58+
<CopyableText value="Display text" copyValue={RUN_ID} className="text-sm" />
59+
</Story>
60+
</StoryGrid>
61+
</StorySection>
62+
63+
<StorySection
64+
title="TruncatedCopyableValue"
65+
description="Shows the tail of a long ID; the tooltip carries the whole value."
66+
>
67+
<StoryGrid min="16rem">
68+
<Story label="Default (last 8)">
69+
<TruncatedCopyableValue value={RUN_ID} />
70+
</Story>
71+
<Story label="length={12}">
72+
<TruncatedCopyableValue value={RUN_ID} length={12} />
73+
</Story>
74+
</StoryGrid>
75+
</StorySection>
76+
77+
<StorySection title="CopyTextLink">
78+
<StoryGrid min="16rem">
79+
<Story label="Default">
80+
<CopyTextLink value={RUN_ID} />
81+
</Story>
82+
</StoryGrid>
83+
</StorySection>
84+
</StoryPage>
85+
);
86+
}
Lines changed: 88 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,94 @@
1+
import { useState } from "react";
12
import { DateField } from "~/components/primitives/DateField";
2-
import { Header2 } from "~/components/primitives/Headers";
3+
import { DateTimePicker } from "~/components/primitives/DateTimePicker";
4+
import { DurationPicker } from "~/components/primitives/DurationPicker";
5+
import { Paragraph } from "~/components/primitives/Paragraph";
6+
import { Story, StoryGrid, StoryPage, StorySection } from "../storybook/StoryKit";
37

4-
export default function Story() {
8+
export default function Story_() {
59
return (
6-
<div className="m-8 space-y-8">
7-
<div className="flex flex-col gap-4">
8-
<Header2>Size = small</Header2>
9-
<DateField label="From (UTC)" granularity="second" showNowButton showClearButton />
10-
<DateField
11-
label="From (UTC)"
12-
defaultValue={new Date()}
13-
granularity="second"
14-
showNowButton
15-
showClearButton
16-
/>
17-
</div>
18-
<div className="flex flex-col gap-4">
19-
<Header2>Size = medium</Header2>
20-
<DateField
21-
label="From (UTC)"
22-
granularity="second"
23-
showNowButton
24-
showClearButton
25-
variant="medium"
26-
/>
27-
<DateField
28-
label="From (UTC)"
29-
defaultValue={new Date()}
30-
granularity="second"
31-
showNowButton
32-
showClearButton
33-
variant="medium"
34-
/>
35-
</div>
10+
<StoryPage
11+
title="Date fields"
12+
description="Segmented date entry, the combined date-time picker and the duration picker."
13+
>
14+
<StorySection title="DateField — small">
15+
<div className="flex flex-col gap-4">
16+
<DateField label="From (UTC)" granularity="second" showNowButton showClearButton />
17+
<DateField
18+
label="From (UTC)"
19+
defaultValue={new Date("2026-08-12T09:24:03.000Z")}
20+
granularity="second"
21+
showNowButton
22+
showClearButton
23+
/>
24+
</div>
25+
</StorySection>
26+
27+
<StorySection title="DateField — medium">
28+
<div className="flex flex-col gap-4">
29+
<DateField
30+
label="From (UTC)"
31+
granularity="second"
32+
showNowButton
33+
showClearButton
34+
variant="medium"
35+
/>
36+
<DateField
37+
label="From (UTC)"
38+
defaultValue={new Date("2026-08-12T09:24:03.000Z")}
39+
granularity="second"
40+
showNowButton
41+
showClearButton
42+
variant="medium"
43+
/>
44+
</div>
45+
</StorySection>
46+
47+
<StorySection title="DateTimePicker">
48+
<StoryGrid min="20rem">
49+
<Story label="Controlled">
50+
<ControlledDateTimePicker />
51+
</Story>
52+
</StoryGrid>
53+
</StorySection>
54+
55+
<StorySection title="DurationPicker">
56+
<StoryGrid min="20rem">
57+
<Story label="Controlled (starts at 90s)">
58+
<ControlledDurationPicker />
59+
</Story>
60+
</StoryGrid>
61+
</StorySection>
62+
</StoryPage>
63+
);
64+
}
65+
66+
function ControlledDateTimePicker() {
67+
const [value, setValue] = useState<Date | undefined>(new Date("2026-08-12T09:24:00.000Z"));
68+
return (
69+
<div className="flex flex-col gap-2">
70+
<DateTimePicker
71+
label="Run at"
72+
value={value}
73+
onChange={setValue}
74+
showNowButton
75+
showClearButton
76+
/>
77+
<Paragraph variant="extra-small" className="text-text-dimmed">
78+
{value ? value.toISOString() : "No date selected"}
79+
</Paragraph>
80+
</div>
81+
);
82+
}
83+
84+
function ControlledDurationPicker() {
85+
const [seconds, setSeconds] = useState(90);
86+
return (
87+
<div className="flex flex-col gap-2">
88+
<DurationPicker value={seconds} onChange={setSeconds} />
89+
<Paragraph variant="extra-small" className="text-text-dimmed">
90+
{seconds} seconds
91+
</Paragraph>
3692
</div>
3793
);
3894
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { useState } from "react";
2+
import {
3+
DateTime,
4+
DateTimeAccurate,
5+
DateTimeShort,
6+
RelativeDateTime,
7+
SmartDateTime,
8+
} from "~/components/primitives/DateTime";
9+
import { PrettyDuration } from "~/components/primitives/PrettyDuration";
10+
import { LiveCountdown, LiveCountUp, LiveTimer } from "~/components/runs/v3/LiveTimer";
11+
import { Story, StoryGrid, StoryPage, StorySection } from "../storybook/StoryKit";
12+
13+
/* A fixed moment so the formatting variants are comparable at a glance. */
14+
const SAMPLE = new Date("2026-08-12T09:24:03.256Z");
15+
const SAMPLE_EARLIER = new Date("2026-08-12T09:23:41.881Z");
16+
const SAMPLE_PREVIOUS_DAY = new Date("2026-08-11T22:10:00.000Z");
17+
18+
export default function Story_() {
19+
// Anchored on mount so the live components have something to count against.
20+
const [mountedAt] = useState(() => new Date(Date.now() - 83_000));
21+
const [countdownEnd] = useState(() => new Date(Date.now() + 95_000));
22+
23+
return (
24+
<StoryPage
25+
title="Dates & timers"
26+
description="Every date formatting component, plus the live-updating timers used on run rows."
27+
>
28+
<StorySection title="DateTime" description="The standard formatter; hover for the tooltip.">
29+
<StoryGrid min="16rem">
30+
<Story label="Default">
31+
<DateTime date={SAMPLE} />
32+
</Story>
33+
<Story label="includeSeconds">
34+
<DateTime date={SAMPLE} includeSeconds />
35+
</Story>
36+
<Story label="includeTime={false}">
37+
<DateTime date={SAMPLE} includeTime={false} />
38+
</Story>
39+
<Story label="showTimezone">
40+
<DateTime date={SAMPLE} showTimezone />
41+
</Story>
42+
<Story label="hour12={false}">
43+
<DateTime date={SAMPLE} hour12={false} />
44+
</Story>
45+
</StoryGrid>
46+
</StorySection>
47+
48+
<StorySection title="Variants">
49+
<StoryGrid min="16rem">
50+
<Story label="DateTimeAccurate (ms)">
51+
<DateTimeAccurate date={SAMPLE} />
52+
</Story>
53+
<Story label="DateTimeShort">
54+
<DateTimeShort date={SAMPLE} />
55+
</Story>
56+
<Story label="SmartDateTime (same day as previous)">
57+
<SmartDateTime date={SAMPLE} previousDate={SAMPLE_EARLIER} />
58+
</Story>
59+
<Story label="SmartDateTime (new day)">
60+
<SmartDateTime date={SAMPLE} previousDate={SAMPLE_PREVIOUS_DAY} />
61+
</Story>
62+
<Story label="RelativeDateTime">
63+
<RelativeDateTime date={SAMPLE_PREVIOUS_DAY} />
64+
</Story>
65+
</StoryGrid>
66+
</StorySection>
67+
68+
<StorySection title="PrettyDuration">
69+
<StoryGrid min="16rem">
70+
<Story label="22.4s">
71+
<PrettyDuration startAt={SAMPLE_EARLIER} endAt={SAMPLE} />
72+
</Story>
73+
<Story label="11h 14m">
74+
<PrettyDuration startAt={SAMPLE_PREVIOUS_DAY} endAt={SAMPLE} />
75+
</Story>
76+
<Story label="Missing dates → fallback">
77+
<PrettyDuration startAt={null} endAt={null} fallback="Not started" />
78+
</Story>
79+
</StoryGrid>
80+
</StorySection>
81+
82+
<StorySection
83+
title="Live timers"
84+
description="Update in place — used while a run is executing."
85+
>
86+
<StoryGrid min="16rem">
87+
<Story label="LiveTimer (running)">
88+
<LiveTimer startTime={mountedAt} />
89+
</Story>
90+
<Story label="LiveTimer (ended)">
91+
<LiveTimer startTime={SAMPLE_EARLIER} endTime={SAMPLE} />
92+
</Story>
93+
<Story label="LiveCountUp">
94+
<LiveCountUp lastUpdated={mountedAt} />
95+
</Story>
96+
<Story label="LiveCountdown">
97+
<LiveCountdown endTime={countdownEnd} />
98+
</Story>
99+
</StoryGrid>
100+
</StorySection>
101+
</StoryPage>
102+
);
103+
}

0 commit comments

Comments
 (0)