← All proposals

Spinner: agentic color variant

Extend the existing PDS v2 Spinner (not a new component).

Extends a PDS componentOpen in Storybook

proposals/Spinner/Spinner.stories.tsx123 lines · View on GitHub
import { FlexContainer } from '@pantheon-systems/pds-toolkit-react';
import { Meta, StoryObj } from '@storybook/react-vite';

import { Spinner, SpinnerSize } from './Spinner';

const sizes: SpinnerSize[] = [
	's',
	'm',
	'l',
	'xl',
	'2xl',
	'3xl',
	'4xl',
	'5xl',
	'6xl',
];

const variants = [{ color: 'agentic', name: 'Agentic' }];

const meta: Meta<typeof Spinner> = {
	tags: ['ready-for-dev'],
	title: 'Component Proposals/Spinner',
	component: Spinner,
	parameters: {
		docs: {
			description: {
				component:
					'An animated loading indicator. `color="agentic"` paints it with the AI gradient.',
			},
		},
	},
	argTypes: {
		className: { table: { category: 'Base attributes' } },
		color: {
			control: 'select',
			options: ['agentic'],
			table: { category: 'Appearance' },
		},
		hasAvatar: { table: { category: 'Component configuration' } },
		isInline: { table: { category: 'Component configuration' } },
		label: { table: { category: 'Content' } },
		showLabel: { table: { category: 'Component configuration' } },
		size: { control: 'select', table: { category: 'Component configuration' } },
	},
	args: {
		color: 'agentic',
		hasAvatar: false,
		isInline: false,
		label: 'Loading',
		showLabel: false,
		size: '4xl',
	},
};

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {};

export const AllSizes: Story = {
	render: () => (
		<FlexContainer flexDirection='column' gap='xl'>
			{variants.map(({ color, name }) => (
				<FlexContainer alignItems='center' gap='xl' key={name}>
					{sizes.map((size) => (
						<Spinner color={color} key={size} label={name} size={size} />
					))}
				</FlexContainer>
			))}
		</FlexContainer>
	),
};

export const WithLabel: Story = {
	args: { showLabel: true, label: 'Thinking' },
};

export const InlineWithText: Story = {
	parameters: {
		docs: {
			description: {
				story:
					'An inline spinner sits in a line of text. `hasAvatar` is set here on purpose: the bolt never shows when `isInline` is set, whatever the size.',
			},
		},
	},
	render: () => (
		<FlexContainer flexDirection='column' gap='m'>
			{variants.map(({ color, name }) => (
				<FlexContainer alignItems='center' gap='none' key={name}>
					<Spinner
						className='pds-mar-inline-end-xs'
						color={color}
						hasAvatar
						isInline
						size='m'
					/>
					<span>Text text</span>
				</FlexContainer>
			))}
		</FlexContainer>
	),
};

export const WithIcon: Story = {
	parameters: {
		docs: {
			description: {
				story:
					'`hasAvatar` puts the Zappy bolt in the ring, in the plain foreground color (not the gradient). It shows from `2xl` up; smaller spinners ignore it, and so does an inline spinner (`isInline`), whatever its size.',
			},
		},
	},
	render: (args) => (
		<FlexContainer alignItems='center' gap='xl'>
			{sizes.slice(sizes.indexOf('2xl')).map((size) => (
				<Spinner {...args} hasAvatar key={size} size={size} />
			))}
		</FlexContainer>
	),
};