Storybook
View on GitHubWrite a story
Every component gets a colocated <Name>.stories.tsx.
1. Create the file
resources/js/Components/Atoms/Button/Button.stories.tsx is the
reference. The skeleton:
import type { Meta, StoryObj } from '@storybook/react-vite';
import { expect, fn, userEvent, within } from 'storybook/test';
import Button from './Button';
const meta = {
title: 'Atoms/Button',
component: Button,
args: { children: 'Save', onClick: fn() },
} satisfies Meta<typeof Button>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {};
export const Clicked: Story = {
play: async ({ args, canvasElement }) => {
await userEvent.click(within(canvasElement).getByRole('button'));
await expect(args.onClick).toHaveBeenCalledTimes(1);
},
};
titleisAtoms/…,Molecules/…orOrganisms/…; the sidebar order is set bystorySortin.storybook/preview.tsx.- Docs pages are generated for every file (
tags: ['autodocs']in the preview). - Use
fn()for callbacks and assert on them inplay.
2. Use shared data
import { issues, makeProject } from '@/stories/fixtures';
makeProject() / makeIssue() take overrides. Add new shared fixtures
to resources/js/stories/fixtures.ts; one-off data stays in the story file.
3. Layout
Wrap in a decorator when the component needs a width:
decorators: [
(Story) => (
<div className="w-[340px]">
<Story />
</div>
),
],
4. Check it
npx vitest run resources/js/stories
The story is picked up automatically by the per-layer test files (see
Stories as Vitest testsStorybookStories as Vitest testsStories are tested through Storybook's portable stories: composeStories applies the preview (decorators, providers, globals) and story.run() renders the st…). A story fails
if it throws, if its play function fails, or if React logs a
console.error.
