feat(ui): add navigation and picker primitives
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
Badge,
|
||||
Button
|
||||
} from "@ai-ui/ui";
|
||||
|
||||
function AccordionPlayground() {
|
||||
return (
|
||||
<div className="w-full max-w-3xl">
|
||||
<Accordion collapsible>
|
||||
<AccordionItem value="editorial">
|
||||
<AccordionTrigger>Editorial review</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
The editorial lane is ready for the final release note pass and legal cross-check.
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem value="engineering">
|
||||
<AccordionTrigger>Engineering canary</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
Canary thresholds are green and the 10% wave can begin after routing sign-off.
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem value="support">
|
||||
<AccordionTrigger>Support queue</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
Customer macros are staged and only need one more quiet-hour review.
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function AccordionFaq() {
|
||||
return (
|
||||
<div className="w-full max-w-3xl">
|
||||
<Accordion type="multiple">
|
||||
<AccordionItem value="why">
|
||||
<AccordionTrigger>Why use Accordion instead of Tabs?</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
Use Accordion when people need to compare or progressively reveal multiple sections in
|
||||
the same reading flow. Tabs hide sibling content; Accordion keeps the page narrative in
|
||||
one vertical surface.
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem value="where">
|
||||
<AccordionTrigger>Where does it fit best?</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
Settings groups, FAQ sections, filter drawers, release notes, audit explanations, and
|
||||
inspector panels all benefit from lightweight disclosure.
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem value="motion">
|
||||
<AccordionTrigger>How does motion behave?</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
The content region uses the shared motion contract for expansion and still respects the
|
||||
static motion mode when the user needs a quieter interface.
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function AccordionControlPanel() {
|
||||
return (
|
||||
<div className="w-full max-w-4xl">
|
||||
<Accordion type="multiple">
|
||||
<AccordionItem value="release-state">
|
||||
<AccordionTrigger>
|
||||
<div className="flex min-w-0 items-center gap-3">
|
||||
<Badge size="sm" variant="outline">
|
||||
Release
|
||||
</Badge>
|
||||
<span>Wave rollout controls</span>
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<div className="grid gap-3 sm:grid-cols-[minmax(0,1fr)_auto] sm:items-center">
|
||||
<p className="text-sm leading-6 text-[var(--color-muted-foreground)]">
|
||||
Stage a 10% wave, keep rollback thresholds visible, and hold broader rollout until
|
||||
the support digest is approved.
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button size="sm">Start 10% wave</Button>
|
||||
<Button size="sm" variant="secondary">
|
||||
Hold rollout
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem value="audit-context">
|
||||
<AccordionTrigger>
|
||||
<div className="flex min-w-0 items-center gap-3">
|
||||
<Badge size="sm" tone="warning" variant="outline">
|
||||
Audit
|
||||
</Badge>
|
||||
<span>Open outstanding review notes</span>
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<div className="grid gap-2 text-sm leading-6 text-[var(--color-muted-foreground)]">
|
||||
<p>Legal footnote still needs one sentence tightened before public launch.</p>
|
||||
<p>Customer support messaging is approved but waiting on the rollout window.</p>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const meta = {
|
||||
title: "Components/Accordion",
|
||||
component: AccordionPlayground,
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
"A lightweight disclosure surface for FAQ sections, filter groups, settings, and inspector panels. Use it when content should remain in one vertical reading flow instead of moving into tabs or overlays."
|
||||
}
|
||||
},
|
||||
layout: "centered"
|
||||
},
|
||||
tags: ["autodocs"]
|
||||
} satisfies Meta<typeof AccordionPlayground>;
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Playground: Story = {};
|
||||
|
||||
export const Faq: Story = {
|
||||
render: () => <AccordionFaq />
|
||||
};
|
||||
|
||||
export const ControlPanel: Story = {
|
||||
render: () => <AccordionControlPanel />
|
||||
};
|
||||
@@ -0,0 +1,128 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import {
|
||||
Badge,
|
||||
Breadcrumb,
|
||||
BreadcrumbCurrent,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbSeparator,
|
||||
Button,
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle
|
||||
} from "@ai-ui/ui";
|
||||
|
||||
function WorkspaceBreadcrumbShowcase() {
|
||||
return (
|
||||
<div className="grid gap-6 p-6">
|
||||
<Card className="max-w-4xl">
|
||||
<CardHeader>
|
||||
<CardTitle>Workflow navigation</CardTitle>
|
||||
<CardDescription>
|
||||
Breadcrumb should stabilize layered navigation across runs, threads, queues,
|
||||
and operator workspaces without competing with page headings.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="grid gap-8">
|
||||
<section className="grid gap-3">
|
||||
<p className="text-xs uppercase tracking-[var(--tracking-caps)] text-[var(--color-muted-foreground)]">
|
||||
Run detail path
|
||||
</p>
|
||||
<Breadcrumb aria-label="Run detail path">
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href="#runs">Runs</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href="#run-42">run-42</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbCurrent>Thread timeline</BreadcrumbCurrent>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
</section>
|
||||
|
||||
<section className="grid gap-3">
|
||||
<p className="text-xs uppercase tracking-[var(--tracking-caps)] text-[var(--color-muted-foreground)]">
|
||||
Environment drill-down
|
||||
</p>
|
||||
<Breadcrumb aria-label="Environment drill-down">
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href="#org">Cadence Labs</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href="#project">Agent platform</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href="#env">Production</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbCurrent>Deploy guardrails</BreadcrumbCurrent>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
</section>
|
||||
|
||||
<section className="grid gap-3">
|
||||
<p className="text-xs uppercase tracking-[var(--tracking-caps)] text-[var(--color-muted-foreground)]">
|
||||
Mixed actions
|
||||
</p>
|
||||
<Breadcrumb aria-label="Escalation trail">
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<Badge size="sm" variant="outline">
|
||||
Holding
|
||||
</Badge>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator>•</BreadcrumbSeparator>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Button size="sm" variant="ghost">
|
||||
Escalations
|
||||
</Button>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbCurrent>Review queue</BreadcrumbCurrent>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
</section>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const meta = {
|
||||
title: "Components/Breadcrumb",
|
||||
component: WorkspaceBreadcrumbShowcase,
|
||||
parameters: {
|
||||
layout: "padded",
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
"A lightweight breadcrumb family for layered operator and admin navigation. Use it to establish context across list → detail → nested object flows without overloading the page chrome."
|
||||
}
|
||||
}
|
||||
},
|
||||
tags: ["autodocs"]
|
||||
} satisfies Meta<typeof WorkspaceBreadcrumbShowcase>;
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Playground: Story = {};
|
||||
@@ -0,0 +1,229 @@
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
ContextMenu,
|
||||
ContextMenuCheckboxItem,
|
||||
ContextMenuContent,
|
||||
ContextMenuItem,
|
||||
ContextMenuLabel,
|
||||
ContextMenuRadioGroup,
|
||||
ContextMenuRadioItem,
|
||||
ContextMenuSeparator,
|
||||
ContextMenuShortcut,
|
||||
ContextMenuSub,
|
||||
ContextMenuSubContent,
|
||||
ContextMenuSubTrigger,
|
||||
ContextMenuTrigger
|
||||
} from "@ai-ui/ui";
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
function FileIcon() {
|
||||
return (
|
||||
<svg aria-hidden="true" className="size-4" fill="none" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M4 2.75h5.2l2.8 2.8v7.7H4z"
|
||||
stroke="currentColor"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="1.3"
|
||||
/>
|
||||
<path d="M9.2 2.75v2.8H12" stroke="currentColor" strokeWidth="1.3" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function EyeIcon() {
|
||||
return (
|
||||
<svg aria-hidden="true" className="size-4" fill="none" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M1.75 8s2.2-3.25 6.25-3.25S14.25 8 14.25 8 12.05 11.25 8 11.25 1.75 8 1.75 8Z"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.3"
|
||||
/>
|
||||
<circle cx="8" cy="8" r="1.85" stroke="currentColor" strokeWidth="1.3" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function FileRowContextMenu({ label = "Open file menu" }: { label?: string }) {
|
||||
return (
|
||||
<ContextMenu>
|
||||
<ContextMenuTrigger asChild>
|
||||
<div className="grid gap-2 rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-card)] p-5 shadow-[var(--shadow-sm)]">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="inline-flex size-10 items-center justify-center rounded-[var(--radius-md)] border border-[var(--color-border)] bg-[var(--color-surface)]">
|
||||
<FileIcon />
|
||||
</span>
|
||||
<div className="grid gap-1">
|
||||
<p className="text-sm font-medium text-[var(--color-foreground)]">release-plan.md</p>
|
||||
<p className="text-xs text-[var(--color-muted-foreground)]">
|
||||
Right click to open contextual actions.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Badge size="sm" variant="outline">
|
||||
docs
|
||||
</Badge>
|
||||
</div>
|
||||
<span className="sr-only">{label}</span>
|
||||
</div>
|
||||
</ContextMenuTrigger>
|
||||
<ContextMenuContent size="xl">
|
||||
<ContextMenuLabel inset>release-plan.md</ContextMenuLabel>
|
||||
<ContextMenuItem
|
||||
description="Open the document in a side-by-side preview."
|
||||
leading={<EyeIcon />}
|
||||
shortcut="P"
|
||||
>
|
||||
Preview file
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem
|
||||
description="Reveal the document inside the release workspace."
|
||||
leading={<FileIcon />}
|
||||
shortcut="R"
|
||||
>
|
||||
Reveal in workspace
|
||||
</ContextMenuItem>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuCheckboxItem
|
||||
checked
|
||||
description="Keep this file pinned in the review queue."
|
||||
shortcut="K"
|
||||
>
|
||||
Keep pinned
|
||||
</ContextMenuCheckboxItem>
|
||||
<ContextMenuRadioGroup value="write">
|
||||
<ContextMenuRadioItem
|
||||
description="Allow direct edits before the next checkpoint."
|
||||
shortcut="W"
|
||||
value="write"
|
||||
>
|
||||
Write access
|
||||
</ContextMenuRadioItem>
|
||||
<ContextMenuRadioItem
|
||||
description="Review the file without mutating it."
|
||||
shortcut="R"
|
||||
value="read"
|
||||
>
|
||||
Read-only access
|
||||
</ContextMenuRadioItem>
|
||||
</ContextMenuRadioGroup>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuSub>
|
||||
<ContextMenuSubTrigger description="Secondary actions with lower urgency.">
|
||||
More actions
|
||||
</ContextMenuSubTrigger>
|
||||
<ContextMenuSubContent>
|
||||
<ContextMenuItem description="Duplicate this file into the next release branch.">
|
||||
Duplicate file
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem
|
||||
description="Move the file out of the active release without deleting it."
|
||||
variant="destructive"
|
||||
>
|
||||
Archive file
|
||||
</ContextMenuItem>
|
||||
</ContextMenuSubContent>
|
||||
</ContextMenuSub>
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
);
|
||||
}
|
||||
|
||||
const meta = {
|
||||
title: "Components/ContextMenu",
|
||||
component: ContextMenu,
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
"ContextMenu extends the menu contract to right-click and long-press surfaces. It matches the DropdownMenu visual language while supporting richer item rows, nested submenus, toggles, and destructive actions for row-level workflows."
|
||||
}
|
||||
},
|
||||
layout: "centered"
|
||||
},
|
||||
tags: ["autodocs"]
|
||||
} satisfies Meta<typeof ContextMenu>;
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Playground: Story = {
|
||||
render: () => <FileRowContextMenu />
|
||||
};
|
||||
|
||||
export const FileRowWorkflow: Story = {
|
||||
render: () => (
|
||||
<div className="grid w-[720px] gap-4">
|
||||
<FileRowContextMenu label="Open release file context menu" />
|
||||
<div className="rounded-[var(--radius-lg)] border border-dashed border-[var(--color-border)] bg-[var(--color-background)] px-4 py-3 text-sm text-[var(--color-muted-foreground)]">
|
||||
This example is designed for real workflow rows. Right click the file card to reveal
|
||||
preview, workspace, pinning, permission, and archive actions.
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
export const DensePanels: Story = {
|
||||
render: () => (
|
||||
<div className="grid w-[960px] gap-6 lg:grid-cols-2">
|
||||
<div className="rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-card)] p-6 shadow-[var(--shadow-sm)]">
|
||||
<h3 className="text-lg font-semibold tracking-[var(--tracking-tight)] text-[var(--color-foreground)]">
|
||||
File row context menu
|
||||
</h3>
|
||||
<p className="mt-2 text-sm leading-6 text-[var(--color-muted-foreground)]">
|
||||
This menu mirrors the richer rows from DropdownMenu, but the trigger is a context
|
||||
surface instead of a button.
|
||||
</p>
|
||||
<div className="mt-6">
|
||||
<FileRowContextMenu />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-card)] p-6 shadow-[var(--shadow-sm)]">
|
||||
<h3 className="text-lg font-semibold tracking-[var(--tracking-tight)] text-[var(--color-foreground)]">
|
||||
Data table row actions
|
||||
</h3>
|
||||
<p className="mt-2 text-sm leading-6 text-[var(--color-muted-foreground)]">
|
||||
Use a context menu when table rows need denser actions than an inline action column can
|
||||
comfortably show.
|
||||
</p>
|
||||
<div className="mt-6">
|
||||
<ContextMenu>
|
||||
<ContextMenuTrigger asChild>
|
||||
<div className="grid gap-1 rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-background)] p-4">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<span className="text-sm font-medium text-[var(--color-foreground)]">
|
||||
Run 184 · release wave
|
||||
</span>
|
||||
<Badge size="sm" tone="warning" variant="outline">
|
||||
blocked
|
||||
</Badge>
|
||||
</div>
|
||||
<p className="text-sm leading-6 text-[var(--color-muted-foreground)]">
|
||||
Right click this row to route the issue, open the thread, or escalate the blocker.
|
||||
</p>
|
||||
</div>
|
||||
</ContextMenuTrigger>
|
||||
<ContextMenuContent>
|
||||
<ContextMenuItem description="Open the current run details in a side panel.">
|
||||
Open run detail
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem description="Jump directly to the latest blocked thread.">
|
||||
Open blocked thread
|
||||
</ContextMenuItem>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuItem
|
||||
description="Escalate this row into the operator blocker queue."
|
||||
variant="destructive"
|
||||
>
|
||||
Escalate blocker
|
||||
</ContextMenuItem>
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
@@ -0,0 +1,95 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import { useState } from "react";
|
||||
|
||||
import { Badge, Card, CardContent, CardDescription, CardHeader, CardTitle } from "@ai-ui/ui";
|
||||
import { DatePicker } from "../../../../packages/ui/src/components/date-picker";
|
||||
|
||||
function DatePickerPlayground() {
|
||||
const [value, setValue] = useState<Date | undefined>(new Date(2026, 3, 18));
|
||||
|
||||
return (
|
||||
<div className="grid w-full max-w-3xl gap-4">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="space-y-2">
|
||||
<CardTitle>Launch scheduling</CardTitle>
|
||||
<CardDescription>
|
||||
Pick a single launch date from a lightweight calendar surface.
|
||||
</CardDescription>
|
||||
</div>
|
||||
<Badge size="sm" variant="outline">
|
||||
single date
|
||||
</Badge>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<DatePicker
|
||||
aria-label="Launch date"
|
||||
onValueChange={setValue}
|
||||
value={value}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function DatePickerScenarios() {
|
||||
return (
|
||||
<div className="grid w-full max-w-4xl gap-4 md:grid-cols-2">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Empty state</CardTitle>
|
||||
<CardDescription>Use the field as a clean trigger for a future date choice.</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<DatePicker aria-label="Review date" placeholder="Select review date" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Guardrailed window</CardTitle>
|
||||
<CardDescription>
|
||||
Limit choices to a narrow release window without turning the API into a range picker.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<DatePicker
|
||||
aria-label="Launch window"
|
||||
defaultMonth={new Date(2026, 4, 1)}
|
||||
defaultValue={new Date(2026, 4, 14)}
|
||||
maxDate={new Date(2026, 4, 20)}
|
||||
minDate={new Date(2026, 4, 10)}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const meta = {
|
||||
title: "Components/DatePicker",
|
||||
component: DatePickerPlayground,
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
"A single-date picker for launch windows, review deadlines, and operator scheduling surfaces. This first slice stays intentionally narrow: one date, one popover calendar, no range or timezone API."
|
||||
}
|
||||
},
|
||||
layout: "centered"
|
||||
},
|
||||
tags: ["autodocs"]
|
||||
} satisfies Meta<typeof DatePickerPlayground>;
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Playground: Story = {};
|
||||
|
||||
export const Scenarios: Story = {
|
||||
render: () => <DatePickerScenarios />
|
||||
};
|
||||
Reference in New Issue
Block a user