blog.kimizuka.org
storybook.js.org
以前、Next.js + styled-componentsの構成にStorybookを入れたときはnpxを使っていましたが、今回はyarnを使います。
導入手順
❶ Storybookのセットアップ
公式のドキュメントにNext.jsへの導入方法が書いてあるので、そのままです。
yarn create storybook
で、OKです。
フルバージョンを入れるか、ミニマルバージョンを入れるかの選択を迫られるのですが、僕はミニマルバージョンにしました。
セットアップが終わると、 http://localhost:6006/?path=/story/example-button--primary が立ち上がるのですが、落としてしまって大丈夫です。
これだけで、もろもろ設定が完了します。
例えば、package.jsonに、
"scripts": { ... "storybook": "storybook dev -p 6006", "build-storybook": "storybook build" },
が追加されるので、
yarn storybook
で、Storybookが起動できるようになってます。
ただ、余計なお世話といえるものも作られていて、例えば src/stories 以下に色々作成されるのですが、僕はまるっと削除してます。
❷ globals.css読み込み
.storybook/preview.ts
import '@/app/globals.css'; // 追加 import type { Preview } from '@storybook/nextjs'; const preview: Preview = { parameters: { controls: { matchers: { color: /(background|color)$/i, date: /Date$/i, }, }, }, }; export default preview;
.storybook/preview.tsにてglobals.cssを読み込みます。
以上です。
src/stories 以下をまるっと削除すると、
yarn storybook
でブラウザを起動しても表示するコンポーネントがなくなってしまうので、
ついでにsrc/stories/Button.tsxをcomponents/Button/index.tsxに移植してみます。
src/components/Button/index.tsx
import styles from './index.module.css'; export interface ButtonProps { primary?: boolean; backgroundColor?: string; size?: 'small' | 'medium' | 'large'; label: string; onClick?: () => void; } export const Button = ({ primary = false, size = 'medium', backgroundColor, label, ...props }: ButtonProps) => { const buttonSize = (() => { switch (size) { case 'small': return styles.buttonSmall; case 'large': return styles.buttonLarge; default: return styles.buttonMedium; } })(); const buttonMode = primary ? styles.buttonPrimary : styles.buttonSecondary; return ( <button type="button" className={[styles.button, buttonSize, buttonMode].join(' ')} {...props} > {label} <style jsx>{` button { background-color: ${backgroundColor}; } `}</style> </button> ); };
src/components/Button/index.module.css
.button { display: inline-block; cursor: pointer; border: 0; border-radius: 3em; font-weight: 700; line-height: 1; font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; } .buttonPrimary { background-color: #555ab9; color: white; } .buttonSecondary { border: solid 1px white; box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset; background-color: transparent; color: white; } .buttonSmall { padding: 10px 16px; font-size: 12px; } .buttonMedium { padding: 11px 20px; font-size: 14px; } .buttonLarge { padding: 12px 24px; font-size: 16px; }
src/components/Button/index.stories.ts
import type { Meta, StoryObj } from '@storybook/nextjs'; import { fn } from 'storybook/test'; import { Button } from './index'; // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export const meta = { title: 'Example/Button', component: Button, parameters: { // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout layout: 'centered', }, // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs tags: ['autodocs'], // More on argTypes: https://storybook.js.org/docs/api/argtypes argTypes: { backgroundColor: { control: 'color' }, }, // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args args: { onClick: fn() }, } satisfies Meta<typeof Button>; export default meta; type Story = StoryObj<typeof meta>; // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args export const Primary: Story = { args: { primary: true, label: 'Button', }, }; export const Secondary: Story = { args: { label: 'Button', }, }; export const Large: Story = { args: { size: 'large', label: 'Button', }, }; export const Small: Story = { args: { size: 'small', label: 'Button', }, };
これで、ボタンコンポーネントが表示されるようになります。
3回に分けましたが、これでNext.js(TypeScript + CSSModules)+ biome + happy-css-modules + StorybookをVSCodeで開発する体制が整いました。
しばらくはこの体制で開発していこうと思います。