テスト入門 〜Storybook〜
UIコンポーネントテストのためにStoryBookを導入する
Storybook@7
実行環境
- 言語・フレームワーク
"typescript": "^5"
"next": "13.5.4", - スタイル
"sass": "^1.69.0",
"tailwindcss": "^3", - リンター・フォーマッター
"eslint": "^8",
"prettier": "^3.0.3",
"stylelint": "^15.10.3",
インストール
npx storybook@latest init
ESLintが入ってる場合は、plluginの導入も推奨される
• Adding Storybook support to your "Next" app
✔ Getting the correct version of 9 packages
? We have detected that you're using ESLint. Storybook provides a plugin that gives the best experience with Storybook and helps follow best practices: https://github.com/storybookjs/eslint-plugin-storybook#readme
Yesを選択
TailwindCSSが適用できるように設定を加える
import type { Preview } from '@storybook/react'
+ import '../src/styles/tailwind.css'
const preview: Preview = {
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/
}
}
}
}
export default preview
next.js13はWebpackじゃないので上記の設定だけでいいみたい?
まずはSCSSで試してみる
TailwindCSSのlinterが警告を出してくるので、offにしておく
module.exports = {
root: true,
extends: [
'plugin:@typescript-eslint/recommended',
'next/core-web-vitals',
+ // 'plugin:tailwindcss/recommended', (コメントアウト)
'prettier',
'plugin:storybook/recommended'
],
plugins: ['unused-imports'],
...
SCSSのButton
labelとprimaryを引数で受け取って、中のテキストを変えたり、真偽値によって、デザインを変えれるようにする
スタイルの拡張ライブラリとして、clsxを使う(shadcnで入ってたため)
import { FC } from 'react'
import { clsx } from 'clsx'
import styles from './style.module.scss'
interface ButtonProps {
label: string
primary?: boolean
}
export const Button: FC<ButtonProps> = ({ label, primary = false, ...args }) => {
return (
<button className={clsx(styles.button, { [styles.primary]: primary })} {...args}>
{label}
</button>
)
}
stories.tsxちょっと理解した
まずはこれ
import { StoryObj, Meta } from '@storybook/react'
import { Button } from './index'
const meta: Meta<typeof Button> = {
component: Button
}
export default meta
type Story = StoryObj<typeof Button>
export const Default: Story = {
render: () => <Button label="でふぉると" />
}
export const Primary: Story = {
render: () => <Button label="ぷらいまりぃ" primary />
}
CSFで記述される
CSF:ESMを用いてexportするオブジェクトの集合
一つのdefault export
と、1つ以上のnamed export
を用いて構成される
export defaultとexport constの役割
export default=メタデータを定義する
const meta: Meta<typeof Button> = {
component: Button
}
• 対象コンポーネントのメタデータ及び各ストーリーの共通設定を定義
描画するコンポーネントや、型情報など
export const=コンポーネントを描画するコンポーネント
export const Default: Story = {
render: () => <Button label="てきすと" />
}
• コンポーネントのストーリーを定義する
書き方メモ
1. 描画したいコンポーネントと必要なモジュールをimport
import { StoryObj, Meta } from '@storybook/react'
import { Button } from './index'
2. 描画するコンポーネントの共通の設定を行うMeta情報の定義
const meta: Meta<typeof Button> = {
component: Button // 使いたいコンポーネントを定義
}
titleはstorybook上でみられる時の名前を定義できる
3.コンポーネントをstoryに登録するための関数を作成
type Story = StoryObj<typeof Button>
export const Default: Story = {
render: () => <Button label="でふぉると" />
}
render関数を使って、コンポーネントを描画できる
登録するコンポーネントは何個でもいける
type Story = StoryObj<typeof Button>
export const Default: Story = {
render: () => <Button label="でふぉると" />
}
export const Primary: Story = {
render: () => <Button label="ぷらいまりぃ" primary />
}
StoryBookの設定
next.configの方で、styles/commnディレクトリにある共通のSassファイルを自動importできるようにailiasつくる設定をしてたので、storybookのwebpackの設定でailiasを適用できるように設定
import type { StorybookConfig } from '@storybook/nextjs'
import path from 'path'
const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-onboarding',
'@storybook/addon-interactions'
],
framework: {
name: '@storybook/nextjs',
options: {}
},
docs: {
autodocs: 'tag'
},
+ webpackFinal: async (config) => {
+ config.resolve ??= {}
+ config.resolve.alias ??= {}
+ config.resolve.alias['@'] = path.resolve(__dirname, '../src')
+ return config
+ }
}
export default config
表示!
プロパティを自在に操作できるようになる
argTypesは7.0からの最新機能みたい
title:コンポーネントのタイトル
/
で階層構造にできるみたい
@7だとデフォルトで、ワークスペースのディレクトリ階層が反映されてる
arg:デフォルトのプロパティを設定する
argTypes:コンポーネントの各プロパティ(引数)をコントロール
parameters:Storybook上の機能や外観などを設定できる
とりあえず、理解したかな、プロパティ
この記事が結構わかりやすかったかも。🙇🏻♂️
Meta情報にプロパティを書くと、コンポーネント全体に反映される
const meta: Meta<typeof Button> = {
component: Button,
argTypes: {
label: {
control: 'text',
defaultValue: 'Button'
},
primary: {
control: 'boolean',
defaultValue: false
}
}
}
これでコントローラーで操作できるようになる
Shadcn UIのコンポーネントを登録してみる
試しにButtonを登録
variant別で定義する
import { StoryObj, Meta } from '@storybook/react'
import { Button } from './button'
const meta: Meta<typeof Button> = {
component: Button,
argTypes: {
variant: {
control: {
type: 'select',
options: ['default', 'destructive', 'outline', 'secondary', 'ghost', 'link']
}
},
size: {
control: {
type: 'select',
options: ['default', 'sm', 'lg', 'icon']
}
},
className: {
control: 'text'
}
}
}
export default meta
type Story = StoryObj<typeof Button>
export const Base: Story = {
args: {
children: 'Base'
}
}
export const Destructive: Story = {
args: {
children: 'Destructive',
variant: 'destructive'
}
}
export const Outline: Story = {
args: {
children: 'Outline',
variant: 'outline'
}
}
export const Secondary: Story = {
args: {
children: 'Secondary',
variant: 'secondary'
}
}
export const Ghost: Story = {
args: {
children: 'Ghost',
variant: 'ghost'
}
}
export const Link: Story = {
args: {
children: 'Link',
variant: 'link'
}
}
export const Small: Story = {
args: {
children: 'Small',
size: 'sm'
}
}
export const Large: Story = {
args: {
children: 'Large',
size: 'lg'
}
}
export const Icon: Story = {
args: {
children: 'Icon',
size: 'icon'
}
}
export const Disabled: Story = {
args: {
children: 'Disabled',
disabled: true
}
}
export const AsChild: Story = {
args: {
children: 'AsChild',
asChild: true
}
}
export const CustomClass: Story = {
args: {
children: 'CustomClass',
className: 'text-red-500'
}
}