import { useState } from 'react';
import { Meta, StoryObj } from '@storybook/react-vite';
import { Button } from '../_base/Button/Button';
import { LabeledDivider } from './LabeledDivider';
import './labeled-divider-story.css';
const timeMenuItems = [
{ label: 'Jump to...', isHeading: true },
{ label: 'Today', callback: () => {} },
{ label: 'Yesterday', callback: () => {} },
{ label: 'Last week', callback: () => {} },
{ label: 'Last month', callback: () => {} },
{ label: 'The very beginning', callback: () => {} },
];
const datePicker = {
maxDate: new Date(),
menuLabel: 'Jump to a specific date',
modalTitle: 'Jump to a date',
onChange: () => {},
pickerLabel: 'Date',
};
const meta: Meta<typeof LabeledDivider> = {
tags: ['new-pattern', 'ready-for-dev'],
title: 'Component Proposals/Labeled Divider',
component: LabeledDivider,
parameters: {
docs: {
description: {
component:
'**New pattern:** not in PDS v2 today. A horizontal rule with a pill label centered on it, to mark a point in an event log, a thread or a timeline: a date, a jump in time or a state change.',
},
},
},
argTypes: {
className: { table: { category: 'Base attributes' } },
datePicker: { control: false, table: { category: 'Content' } },
id: { table: { category: 'Base attributes' } },
label: { control: 'text', table: { category: 'Content' } },
menuItems: { control: false, table: { category: 'Content' } },
prefixIcon: {
control: 'select',
options: [undefined, 'circle', 'zappy', 'sparklesSolid'],
table: { category: 'Content' },
},
size: {
control: 'select',
options: ['xs', 's'],
table: { category: 'Component configuration' },
},
suffixIcon: {
control: 'select',
options: [undefined, 'circle', 'zappy', 'sparklesSolid'],
table: { category: 'Content' },
},
tone: {
control: 'select',
options: ['neutral', 'agentic'],
table: { category: 'Component configuration' },
},
},
args: {
label: 'Text',
prefixIcon: 'circle',
suffixIcon: 'circle',
size: 's',
tone: 'neutral',
},
};
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {};
export const WithTimeSelector: Story = {
args: {
id: 'labeled-divider-time',
label: 'Yesterday',
datePicker,
menuItems: timeMenuItems,
prefixIcon: undefined,
suffixIcon: undefined,
},
parameters: {
docs: {
description: {
story:
'Pass `menuItems` (and an `id`) to turn the label into a selector with a chevron, for jumping to another point in time. The consumer supplies the options and what each one does.',
},
},
},
};
export const WithAction: Story = {
args: {
label: 'Show more',
},
parameters: {
docs: {
description: {
story:
'Pass `action` to swap the label for any control, centered on the line. Here it is a "Show more" Button that unfolds more content under the divider. The screen owns the control, `aria-expanded` and the content it reveals; the divider only places it. `tone` still colors the lines.',
},
},
},
render: (args) => {
const [isOpen, setIsOpen] = useState(false);
const events = [
'Updated the build cache',
'Ran a dry run of the deployment',
'Checked the dependency versions',
];
return (
<div className='labeled-divider-story'>
<LabeledDivider
{...args}
action={
<Button
aria-controls='labeled-divider-more'
aria-expanded={isOpen}
displayType='icon-end'
iconName={isOpen ? 'angleUp' : 'angleDown'}
label={isOpen ? 'Show less' : 'Show 3 more'}
onClick={() => setIsOpen((open) => !open)}
size='xs'
variant='secondary'
/>
}
/>
<ul
className='labeled-divider-story__more'
hidden={!isOpen}
id='labeled-divider-more'
>
{events.map((event) => (
<li key={event}>{event}</li>
))}
</ul>
</div>
);
},
};
export const Agentic: Story = {
render: (args) => (
<div className='labeled-divider-story'>
<LabeledDivider
{...args}
label='Zappy engaged'
prefixIcon='zappy'
suffixIcon={undefined}
tone='agentic'
/>
</div>
),
parameters: {
docs: {
description: {
story:
'The `agentic` tone: the AI gradient as the label border and on the lines, with the shimmer, and the Zappy bolt in the plain foreground color. Use for a state change in the stream, such as an agent joining.',
},
},
},
};
export const AllSizesAndVariants: Story = {
name: 'All sizes and variants',
render: (args) => (
<div className='labeled-divider-story'>
{(['s', 'xs'] as const).flatMap((size) => [
<LabeledDivider
key={`${size}-neutral`}
{...args}
label={`Neutral (${size})`}
size={size}
/>,
<LabeledDivider
key={`${size}-selector`}
{...args}
datePicker={datePicker}
id={`labeled-divider-sizes-${size}`}
label={`Time selector (${size})`}
menuItems={timeMenuItems}
prefixIcon={undefined}
size={size}
suffixIcon={undefined}
/>,
<LabeledDivider
key={`${size}-agentic`}
{...args}
label={`Agentic (${size})`}
prefixIcon='zappy'
size={size}
suffixIcon={undefined}
tone='agentic'
/>,
])}
</div>
),
};
LabeledDivider
Net-new LabeledDivider: a horizontal rule with a pill-shaped label centered on it.
Net-new componentOpen in Storybook