next.jsにstorybookを導入したいんじゃ
参考読んでる
- https://zenn.dev/kyo9bo/articles/bd37f814b33909
- https://zenn.dev/sum0/articles/9463d16d9d40e2
- https://zenn.dev/tomon9086/articles/a0f3e549b4e848627e3c
- https://storybook.js.org/
- https://storybook.js.org/docs/react/writing-tests/introduction
- https://zenn.dev/keitakn/articles/storybook-deploy-to-chromatic
やること
- Next.jsの既存プロジェクトにStorybookを導入する
- テストはUI、インテグレーション、a11yを予定
- Chromaticでホスティング
- ChromaticにはGiuhub Actionとかで自動デプロイ
その他気になるアドオンを試しつつ
取り敢えずinit
monorepoでもいい感じに入った
cd packages/web
npx storybook init
テンプレ通りにstories.tsxを作って確認してみる
import TagIcon from '@mui/icons-material/Tag';
import Chip from '@mui/material/Chip';
import Stack from '@mui/material/Stack';
type Props = {
tagsOnArticles: string[];
}
export default function TagChip({tagsOnArticles}: Props): JSX.Element {
return (
<>
<Stack direction="row" spacing={1}>
{
tagsOnArticles.map((tag) => (
<Chip size='small' key={tag} icon={<TagIcon />} label={tag} />
))
}
</Stack>
</>
);
}
import { ComponentMeta, ComponentStory } from '@storybook/react';
import TagChip from './tagChip';
export default {
title: 'Example/TagChip',
component: TagChip,
} as ComponentMeta<typeof TagChip>;
const Template: ComponentStory<typeof TagChip> = (args) => <TagChip {...args} />;
export const single = Template.bind({});
single.args = {
tagsOnArticles: ['tag1']
};
export const many = Template.bind({});
many.args = {
tagsOnArticles: ['tag1','tag2','tag3']
};
hygenでテンプレを作れるらしい
後でやるかも
test runnerをつかってみようと思ったけどChromaticでいい気がする
Use it locally and Chromatic on your CI.
Use Chromatic for visual and interaction tests and run other custom tests using the test runner.
- UIテスト
- インタラクションテスト
- 差分レビューあったかい
https://www.chromatic.com/docs/test
わかりやすい解説
@storybook/test-runnerがテストするのは、play関数を持たないStoryであればStoryがエラーなくレンダリングされるかどうかをテストする。
play関数を持つStoryの場合はそのStoryがエラーなくレンダリングされるかどうかに加えてplay関数でエラーが発生しないこととアサーションをパスすることのよう。
https://zenn.dev/kyo9bo/articles/9909ba89c42a77
取り敢えずchromaticに登録だけして、storiesを書いていく
yarn add --dev chromatic
npx chromatic --project-token=token
コンポーネント側のimportでエラーが出る
tsconfigのbaseurlが効いてないっぽいのでmain.jsで指定
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
module.exports = {
...,
webpackFinal: async (config, { configType }) => {
config.resolve.plugins = [new TsconfigPathsPlugin()];<-- this line
return config;
}
};
コンポーネントでpublicRuntimeConfigを使用してるとエラーが出る
Cannot read property 'publicRuntimeConfig' of undefined
対処
addonもある今回はaddonを採用
1個前でやったbaseUrlを反映させるのもやってくれるらしい
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
module.exports = {
...,
webpackFinal: async (config, { configType }) => {
config.resolve.plugins = [new TsconfigPathsPlugin()];<-- this line
return config;
}
};
muiのthemeを適用するにはpreview.jsで読み込む
_app.tsxで読み込むCSSとかもpreview.jsで適用する
import {ThemeProvider} from '@mui/material/styles'
import CssBaseline from '@mui/material/CssBaseline'
import theme from '../src/mui/theme'
import 'styles/globals.css';
export const decorators = [
(Story) => {
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<Story />
</ThemeProvider>
)
},
]
@emotion/babel-preset-css-propを反映させるために、main.jsに以下を追加した
// 以下を追加
babel: async (options) => ({
...options,
presets: [...options.presets, '@emotion/babel-preset-css-prop'],
}),
}
emotionを設定したらReact is not definedになった
公式のFAQに対処法が乗ってた
Next automatically defines React for all of your files via a babel plugin. In Storybook, you can solve this either by:
1.Adding import React from 'react' to your component files.
2.Adding a .babelrc that includes babel-plugin-react-require
import React from 'react'を書きたくないので2を採用する
yarn add -D babel-plugin-react-require
main.jsに以下を追加
babel: async (options) => ({
...options,
presets: [...options.presets, '@emotion/babel-preset-css-prop'],
plugins: ['react-require'] // 追加:React is not definedの対処
}),
現状のmain.js全体
const path = require('path')
module.exports = {
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.@(js|jsx|ts|tsx)"
],
"addons": [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
{
name: 'storybook-addon-next',
options: {
nextConfigPath: path.resolve(__dirname, '../next.config.js')
}
}
],
"framework": "@storybook/react",
"core": {
"builder": "@storybook/builder-webpack5"
},
babel: async (options) => ({
...options,
presets: [...options.presets, '@emotion/babel-preset-css-prop'],
plugins: ['react-require']
}),
}
public配下の画像表示するためにmain.jsに以下追加
staticDirs: ['../public'],
storoes書き終わったから下記を参考にa11y入れてみる
Github Actionsで自動デプロイする
mainに対するプルリクのみにした
# Workflow name
name: 'Chromatic'
# Event for the workflow
on:
pull_request:
branches:
- main
# List of jobs
jobs:
chromatic-deployment:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Install dependencies
run: yarn
- name: Publish to Chromatic
uses: chromaui/action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} # Github Secretにトークンを登録する
exitZeroOnChanges: true