test(ui): add primitive story coverage

This commit is contained in:
Saravana Achu Mac 2026-05-06 11:30:51 -07:00
parent 5b720fda33
commit 5af6154e80
5 changed files with 222 additions and 4 deletions

View File

@ -5,7 +5,10 @@ const meta: Meta<typeof Badge> = {
title: 'Components/Badge',
component: Badge,
argTypes: {
variant: { control: 'select', options: ['success', 'warning', 'error', 'info', 'neutral'] },
variant: {
control: 'select',
options: ['success', 'warning', 'error', 'danger', 'info', 'neutral', 'accent'],
},
size: { control: 'select', options: ['sm', 'md'] },
dot: { control: 'boolean' },
},

View File

@ -5,7 +5,10 @@ const meta: Meta<typeof Button> = {
title: 'Components/Button',
component: Button,
argTypes: {
variant: { control: 'select', options: ['primary', 'secondary', 'ghost', 'danger'] },
variant: {
control: 'select',
options: ['primary', 'secondary', 'ghost', 'destructive', 'outline', 'subtle', 'link'],
},
size: { control: 'select', options: ['sm', 'md', 'lg'] },
loading: { control: 'boolean' },
disabled: { control: 'boolean' },
@ -28,7 +31,7 @@ export const Ghost: Story = {
};
export const Danger: Story = {
args: { children: 'Danger Button', variant: 'danger' },
args: { children: 'Danger Button', variant: 'destructive' },
};
export const Loading: Story = {

View File

@ -24,7 +24,7 @@ export const WithHeader: Story = {
<CardTitle>Card Title</CardTitle>
<CardDescription>A short description of this card.</CardDescription>
</CardHeader>
<p style={{ color: 'var(--bl-text-secondary, #a0a0b0)', fontSize: 14 }}>Body content</p>
<p className="text-sm text-[var(--bl-text-secondary)]">Body content</p>
</Card>
),
};

View File

@ -0,0 +1,92 @@
import * as React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { MoreHorizontal } from 'lucide-react';
import { Checkbox } from './Checkbox.js';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from './DropdownMenu.js';
import { IconButton } from './IconButton.js';
import { RadioGroup, RadioGroupItem } from './RadioGroup.js';
import { SegmentedControl } from './SegmentedControl.js';
import { Switch } from './Switch.js';
import { Tabs, TabsContent, TabsList, TabsTrigger } from './Tabs.js';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './Tooltip.js';
const meta: Meta = {
title: 'Components/Controls',
};
export default meta;
type Story = StoryObj;
export const ChoiceControls: Story = {
render: () => {
const [mode, setMode] = React.useState('review');
return (
<div className="grid max-w-xl gap-4">
<SegmentedControl
aria-label="Queue mode"
value={mode}
onValueChange={setMode}
options={[
{ value: 'review', label: 'Review' },
{ value: 'approved', label: 'Approved' },
{ value: 'rejected', label: 'Rejected' },
]}
/>
<Checkbox label="Include archived notes" />
<Switch label="Auto-refresh queue" />
<RadioGroup defaultValue="compact" aria-label="Density">
<RadioGroupItem value="compact" label="Compact" />
<RadioGroupItem value="comfortable" label="Comfortable" />
</RadioGroup>
</div>
);
},
};
export const OverlaysAndTabs: Story = {
render: () => (
<TooltipProvider>
<div className="grid max-w-xl gap-4">
<Tabs defaultValue="queue">
<TabsList aria-label="Review sections">
<TabsTrigger value="queue">Queue</TabsTrigger>
<TabsTrigger value="timeline">Timeline</TabsTrigger>
</TabsList>
<TabsContent value="queue" className="pt-3">
Queue content
</TabsContent>
<TabsContent value="timeline" className="pt-3">
Timeline content
</TabsContent>
</Tabs>
<div className="flex gap-2">
<Tooltip>
<TooltipTrigger asChild>
<IconButton icon={<MoreHorizontal className="h-4 w-4" />} label="More actions" />
</TooltipTrigger>
<TooltipContent>More actions</TooltipContent>
</Tooltip>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<IconButton icon={<MoreHorizontal className="h-4 w-4" />} label="Open menu" />
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>Approve</DropdownMenuItem>
<DropdownMenuItem>Reject</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
</TooltipProvider>
),
};

View File

@ -0,0 +1,120 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Check, Clock } from 'lucide-react';
import { Button } from './Button.js';
import { DataList, DataListItem, DataListMeta } from './DataList.js';
import {
DataTable,
DataTableBody,
DataTableCell,
DataTableHead,
DataTableHeader,
DataTableRow,
} from './DataTable.js';
import { DiffCard } from './DiffCard.js';
import { IconButton } from './IconButton.js';
import { ListItemButton } from './ListItemButton.js';
import { Panel, PanelBody, PanelDescription, PanelHeader, PanelTitle } from './Panel.js';
import { StatusBadge } from './StatusBadge.js';
import { Surface, SurfaceList, SurfaceListItem } from './Surface.js';
import { Timeline } from './Timeline.js';
const meta: Meta = {
title: 'Components/Operational Primitives',
};
export default meta;
type Story = StoryObj;
export const SurfacesAndLists: Story = {
render: () => (
<div className="grid max-w-2xl gap-4">
<Panel>
<PanelHeader>
<div>
<PanelTitle>Review queue</PanelTitle>
<PanelDescription>Dense operator work surface</PanelDescription>
</div>
<IconButton icon={<Check className="h-4 w-4" />} label="Approve selected" />
</PanelHeader>
<PanelBody>
<ListItemButton selected>
<div className="font-medium">Merge duplicate research notes</div>
<div className="text-sm text-[var(--bl-text-secondary)]">2 linked notes affected</div>
</ListItemButton>
<SurfaceList>
<SurfaceListItem selected>Selected surface row</SurfaceListItem>
<SurfaceListItem>Normal surface row</SurfaceListItem>
</SurfaceList>
</PanelBody>
</Panel>
<Surface variant="muted">Reusable muted surface</Surface>
</div>
),
};
export const StatusTimelineAndDiff: Story = {
render: () => (
<div className="grid max-w-2xl gap-4">
<div className="flex gap-2">
<StatusBadge tone="success" dot>
Approved
</StatusBadge>
<StatusBadge tone="warning" dot>
Needs review
</StatusBadge>
</div>
<Timeline
items={[
{
id: '1',
title: 'Draft created',
description: 'Agent proposed a note update',
status: 'Draft',
tone: 'info',
},
{
id: '2',
title: 'Human review',
description: 'Awaiting decision',
status: 'Pending',
tone: 'warning',
},
]}
/>
<DiffCard before="Original note body" after="Improved note body with extracted actions" />
</div>
),
};
export const DataDisplay: Story = {
render: () => (
<div className="grid max-w-3xl gap-4">
<DataList>
<DataListItem selected>
<div className="font-medium">Workspace intelligence</div>
<DataListMeta>
<Clock className="h-3.5 w-3.5" /> Updated 4 minutes ago
</DataListMeta>
</DataListItem>
<DataListItem>Inbox processing</DataListItem>
</DataList>
<DataTable>
<DataTableHeader>
<DataTableRow>
<DataTableHead>Name</DataTableHead>
<DataTableHead>Status</DataTableHead>
</DataTableRow>
</DataTableHeader>
<DataTableBody>
<DataTableRow>
<DataTableCell>Research notes</DataTableCell>
<DataTableCell>
<StatusBadge tone="success">Healthy</StatusBadge>
</DataTableCell>
</DataTableRow>
</DataTableBody>
</DataTable>
<Button variant="subtle">Open details</Button>
</div>
),
};