test(ui): add primitive story coverage
This commit is contained in:
parent
5b720fda33
commit
5af6154e80
@ -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' },
|
||||
},
|
||||
|
||||
@ -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 = {
|
||||
|
||||
@ -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>
|
||||
),
|
||||
};
|
||||
|
||||
92
packages/ui/src/components/Controls.stories.tsx
Normal file
92
packages/ui/src/components/Controls.stories.tsx
Normal 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>
|
||||
),
|
||||
};
|
||||
120
packages/ui/src/components/OperationalPrimitives.stories.tsx
Normal file
120
packages/ui/src/components/OperationalPrimitives.stories.tsx
Normal 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>
|
||||
),
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user