← All proposals

Timeline

Net-new, in two parts.

Net-new componentOpen in Storybook

proposals/Timeline/TimelineItem.stories.tsx168 lines · View on GitHub
import { ReactNode } from 'react';

import {
	CodeBlock,
	FlexContainer,
	Tag,
} from '@pantheon-systems/pds-toolkit-react';
import { Meta, StoryObj } from '@storybook/react-vite';

import { SlotPlaceholder } from '../_story-helpers/SlotPlaceholder';

import { TimelineItem } from './TimelineItem';

const meta: Meta<typeof TimelineItem> = {
	tags: ['new-pattern', 'ready-for-dev'],
	title: 'Component Proposals/Timeline/Timeline Item',
	component: TimelineItem,
	// The item inherits its text size from a Timeline; this stands in for one.
	decorators: [(Story) => <div className='pds-copy-xs'>{Story()}</div>],
	parameters: {
		docs: {
			description: {
				component:
					'**New pattern:** not in PDS v2 today. One event or piece of work: a status icon, a label, quiet secondary text, a time and optional expandable detail. Use it alone inline, in a Timeline or inside Chain of Thought.',
			},
		},
	},
	argTypes: {
		className: { table: { category: 'Base attributes' } },
		detail: { control: 'text', table: { category: 'Content' } },
		iconName: {
			control: 'select',
			options: [undefined, 'wrench', 'zappy', 'sparklesSolid', 'check'],
			table: { category: 'Content' },
		},
		isAgentic: {
			control: 'boolean',
			table: { category: 'Component configuration' },
		},
		label: { control: 'text', table: { category: 'Content' } },
		secondaryText: { control: 'text', table: { category: 'Content' } },
		size: {
			control: 'inline-radio',
			options: [undefined, 'xs', 's'],
			table: { category: 'Component configuration' },
		},
		time: { control: 'text', table: { category: 'Content' } },
		status: {
			control: 'select',
			options: [undefined, 'pending', 'active', 'done', 'error'],
			table: { category: 'State' },
		},
	},
	args: {
		isAgentic: false,
		label: 'Read the page',
		secondaryText: 'home',
		status: 'done',
		time: '01:08 AM',
	},
};

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {};

const STATUS_ROWS: { name: string; step: ReactNode }[] = [
	{
		name: 'Pending',
		step: <TimelineItem label='Saved changes' status='pending' />,
	},
	{
		name: 'Active',
		step: (
			<TimelineItem label='Reserved the page for editing' status='active' />
		),
	},
	{
		name: 'Done',
		step: <TimelineItem label='Confirmed edit permission' status='done' />,
	},
	{
		name: 'Error',
		step: (
			<TimelineItem
				label="Couldn't save changes"
				secondaryText='Permission denied'
				status='error'
			/>
		),
	},
	{
		name: 'No status',
		step: <TimelineItem label='Read the page' secondaryText='home' />,
	},
	{
		name: 'Done, with detail',
		step: (
			<TimelineItem
				detail='Updated the heading, two paragraphs and two links.'
				label='Applied changes'
				secondaryText='5 edits'
				status='done'
			/>
		),
	},
];

export const AllStatuses: Story = {
	parameters: {
		docs: {
			description: {
				story:
					'Each status, labeled. `done` with `detail` expands to show more.',
			},
		},
	},
	render: () => (
		<FlexContainer flexDirection='column' gap='m'>
			{STATUS_ROWS.map(({ name, step }) => (
				<FlexContainer alignItems='center' gap='m' key={name}>
					<span style={{ width: '10rem' }}>{name}</span>
					{step}
				</FlexContainer>
			))}
		</FlexContainer>
	),
};

export const Slot: Story = {
	parameters: {
		docs: {
			description: {
				story:
					'`children` is a slot under the step, always visible and lined up with the label. What goes in it is up to the screen; it only renders when something is passed. The first step shows the slot, the second a code block and file tags as an example.',
			},
		},
	},
	args: {
		label: 'Reading the failed build logs and error trace',
		status: 'done',
	},
	render: (args) => (
		<FlexContainer flexDirection='column' gap='l'>
			<TimelineItem {...args}>
				<SlotPlaceholder label='children' style={{ minHeight: '6rem' }} />
			</TimelineItem>
			<TimelineItem {...args}>
				<FlexContainer flexDirection='column' gap='xs'>
					<CodeBlock
						code={
							'ERROR: Your requirements could not be resolved to an installable set of packages.\n  drupal/pathauto is locked to ^1.11 and an update was not requested.'
						}
						hasCopyButton={false}
						language='bash'
					/>
					<FlexContainer gap='xs'>
						<Tag size='xs' tagColor='slate' tagLabel='composer.json' />
						<Tag size='xs' tagColor='slate' tagLabel='composer.lock' />
					</FlexContainer>
				</FlexContainer>
			</TimelineItem>
		</FlexContainer>
	),
};