import type { Meta, StoryObj } from '@storybook/react-vite' // Animated illustrations - import directly from individual files import AccountingAnimation from '../../generated/AccountingAnimation' import Binoculars from '../../generated/Binoculars' import BinocularsAnimation from '../../generated/BinocularsAnimation' import CardAnimation from '../../generated/CardAnimation' import FAQ from '../../generated/FAQ' import FAQWithBackground from '../../generated/FAQWithBackground' import VisuallyImpaired from '../../generated/VisuallyImpaired' import WriteBook from '../../generated/WriteBook' import { Illustration } from './Illustration' function transformIllustrationSource(code: string) { // Find all src props with name values const srcMatches = [ ...code.matchAll(/src=\{\{[^}]+name:\s*['"`]([^'"`]+)['"`][^}]+\}\}/g), ] if (srcMatches.length === 0) { return code // Return original code if no illustrations found } // Extract unique illustration names const illustrationNames = [...new Set(srcMatches.map(match => match[1]))] // Create import statements for all unique illustrations const importStatements = illustrationNames .map( name => `import ${name} from '@payfit/unity-illustrations/assets/${name}';`, ) .join('\n') // Replace all src prop objects with their corresponding imported variables let modifiedCode = code srcMatches.forEach(match => { const fullMatch = match[0] // The entire src={{...}} part const illustrationName = match[1] // The name value modifiedCode = modifiedCode.replace(fullMatch, `src={${illustrationName}}`) }) return `${importStatements}\n\n${modifiedCode}` } /** * Displays illustrations from the Unity design system with consistent styling and accessibility. * * The Illustration component handles SVG illustrations and images, providing standardized sizing options, * responsive behavior, and proper accessibility attributes. All illustration assets must be imported * individually from the `@payfit/unity-illustrations` package before use. For example, if you want to use the FAQ illustration, you can import it like this: * * ```tsx * import FAQ from '@payfit/unity-illustrations/assets/FAQ' * ``` * * See the [usage page](/docs/introduction-3-usage--docs) for complete examples and best practices. */ const meta = { title: 'Components/Illustration', component: Illustration, tags: ['autodocs'], parameters: { layout: 'centered', docs: { source: { transform: (code: string) => transformIllustrationSource(code), language: 'tsx', }, }, }, argTypes: { src: { control: 'select', description: 'The illustration asset to render. Must be imported from @payfit/unity-illustrations', table: { type: { summary: 'UnityIllustrationAsset' }, category: 'Required', }, type: { name: 'other', required: true, value: 'UnityIllustrationAsset' }, }, variant: { control: 'select', options: ['picture', 'icon'], description: 'The illustration variant. It can be a regular picture or an illustrated icon', table: { type: { summary: "'picture' | 'icon'" }, category: 'Required', }, type: { name: 'enum', required: true, value: ['picture', 'icon'] }, }, alt: { control: 'text', description: 'Accessible name for the illustration. Required when isDecorative is `true`', table: { type: { summary: 'string' }, category: 'Accessibility', }, }, description: { control: 'text', description: 'Optional description for complex illustrations. Active only when isDecorative is `true`', table: { type: { summary: 'string' }, category: 'Accessibility', }, }, isDecorative: { control: 'boolean', description: 'Whether the illustration is decorative (hidden from screen readers). When `true`, `alt` becomes optional', table: { type: { summary: 'boolean' }, defaultValue: { summary: 'false' }, category: 'Accessibility', }, }, size: { control: 'select', options: ['sm', 'md', 'lg'], description: 'The size of the illustration. Only applies to icon variant', if: { arg: 'variant', eq: 'icon' }, table: { type: { summary: "'sm' | 'md' | 'lg'" }, category: 'Icon Variant', }, }, className: { control: 'text', description: 'Additional CSS classes for styling', table: { type: { summary: 'string' }, category: 'Styling', }, }, }, } satisfies Meta export default meta type Story = StoryObj export const Primary: Story = { args: { variant: 'picture', src: Binoculars, className: 'uy:size-[150px]', alt: 'Binoculars illustration', }, } /** * You can create responsive illustrations that adapt to different screen sizes by using responsive * Tailwind classes in the `className` prop. Set the `alt` prop to provide accessible descriptions * for screen readers when using the picture variant. */ export const IllustratedPicture: Story = { args: { variant: 'picture', src: VisuallyImpaired, className: 'uy:size-[50vmin]', }, } /** * You can display illustrations as smaller icons by setting the `variant` prop to `"icon"`. * Use the `size` prop to choose from predefined sizes: `"sm"`, `"md"`, or `"lg"`. This is ideal for illustrations that are meant to be used as illustrated icons. */ export const IllustratedIcon: Story = { args: { variant: 'icon', }, render: function Render(args) { return (
) }, } /** * You can make the illustration decorative by setting the `isDecorative` prop to `true`. * When `isDecorative` is `true`, `alt` becomes optional. Screen readers will ignore the illustration and not read the `alt` or the `description` attributes. Use this when the illustration is purely decorative or visual appeal, and does not provide any information. */ export const IsDecorative: Story = { args: { variant: 'picture', src: VisuallyImpaired, className: 'uy:size-[50vmin]', isDecorative: true, }, } /** * You can create responsive layouts where illustrations adapt to different viewport sizes * by combining responsive Tailwind classes with the illustration component (with media and container queries). Use Unity theme * classes to configure how the illustration behaves across breakpoints. */ export const Responsive: Story = { render: () => { return (

Title

The image above has a different size depending on the viewport size. Use unity-themes classes to configure how the illustration behaves

) }, } /** * Unity illustrations supports animated illustrations in WEBP format. All the animated illustrations have the `Animation` suffix on their name * Animated illustrations will grow fluidly depending on its container size but are capped at their original dimensions to prevent oversizing. */ export const AnimatedIllustrations: Story = { parameters: { chromatic: { disableSnapshot: true }, }, render: () => { return (

Large Animation (Fluid)

Max dimensions: 969×852px • Scales down responsively

Medium Animation (Fluid)

Max dimensions: 750×750px • Scales down responsively

Small Animation (Fluid)

Max dimensions: 224×224px • Scales down responsively

) }, }