Storybook7へのアップグレード

概要
表題の通り。
個人サービスで実施していきます。
前提条件
検証で利用するプロジェクトはNext.js製アプリのComponentを提供するpackageです。
その為、next/link
や next/image
に依存するComponentが存在します。
今までは storybook-addon-next というサードパーティ製のpackageで next/link
や next/image
をモック化していました。
補足
今回検証したpackageは以下の個人サービスで利用されています。

Migration guide
こちらを参考に実施する。

公式ドキュメントのAutomatic upgradeを実施
npx storybook@next upgrade --prerelease
を実行したところ以下のエラーが発生した。
Need to install the following packages:
storybook@7.0.1
Ok to proceed? (y) y
(node:19046) ExperimentalWarning: The Fetch API is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
• Checking for latest versions of '@storybook/*' packages
info ,Upgrading /Users/kogakeita/gitrepos/lgtm-cat-ui/package.json
info
info @storybook/addon-a11y ^6.5.16 → ^7.0.1
info @storybook/addon-actions ^6.5.16 → ^7.0.1
info @storybook/addon-essentials ^6.5.16 → ^7.0.1
info @storybook/addon-interactions ^6.5.16 → ^7.0.1
info @storybook/addon-links ^6.5.16 → ^7.0.1
info @storybook/builder-webpack5 ^6.5.16 → ^7.0.1
info @storybook/react ^6.5.16 → ^7.0.1
info @storybook/testing-library ^0.0.13 → ^0.0.14-next.2
info
info Run npm install to install new versions.
info ,npm WARN exec The following package was not found and will be installed: npm-check-updates@16.10.1
info npm WARN deprecated @npmcli/move-file@2.0.1: This functionality has been moved to @npmcli/fs
info
info
info ,Upgrading /Users/kogakeita/gitrepos/lgtm-cat-ui/package.json
info
info No dependencies.
info ,
• Installing upgrades • Preparing to install dependencies. ✓
npm WARN ERESOLVE overriding peer dependency
npm WARN While resolving: @nekochans/lgtm-cat-ui@2.3.0
npm WARN Found: @storybook/addon-actions@6.5.16
npm WARN node_modules/@storybook/addon-actions
npm WARN dev @storybook/addon-actions@"^7.0.1" from the root project
npm WARN 2 more (@storybook/addon-essentials, storybook-addon-next)
npm WARN
npm WARN Could not resolve dependency:
npm WARN peer @storybook/addon-actions@"^6.0.0" from storybook-addon-next@1.7.3
npm WARN node_modules/storybook-addon-next
npm WARN dev storybook-addon-next@"^1.7.3" from the root project
npm ERR! code ERESOLVE
npm ERR! ERESOLVE could not resolve
npm ERR!
npm ERR! While resolving: stylelint-config-prettier@9.0.5
npm ERR! Found: stylelint@15.3.0
npm ERR! node_modules/stylelint
npm ERR! dev stylelint@"^15.3.0" from the root project
npm ERR! peer stylelint@">=15" from stylelint-config-recess-order@4.0.0
npm ERR! node_modules/stylelint-config-recess-order
npm ERR! dev stylelint-config-recess-order@"^4.0.0" from the root project
npm ERR! 3 more (stylelint-config-recommended, ...)
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer stylelint@">= 11.x < 15" from stylelint-config-prettier@9.0.5
npm ERR! node_modules/stylelint-config-prettier
npm ERR! dev stylelint-config-prettier@"^9.0.5" from the root project
npm ERR! peer stylelint-config-prettier@"^9.0.3" from stylelint-config-smarthr@1.1.0
npm ERR! node_modules/stylelint-config-smarthr
npm ERR! dev stylelint-config-smarthr@"^1.1.0" from the root project
npm ERR!
npm ERR! Conflicting peer dependency: stylelint@14.16.1
npm ERR! node_modules/stylelint
npm ERR! peer stylelint@">= 11.x < 15" from stylelint-config-prettier@9.0.5
npm ERR! node_modules/stylelint-config-prettier
npm ERR! dev stylelint-config-prettier@"^9.0.5" from the root project
npm ERR! peer stylelint-config-prettier@"^9.0.3" from stylelint-config-smarthr@1.1.0
npm ERR! node_modules/stylelint-config-smarthr
npm ERR! dev stylelint-config-smarthr@"^1.1.0" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR!
npm ERR! See /Users/kogakeita/.npm/eresolve-report.txt for a full report.
npm ERR! A complete log of this run can be found in:
npm ERR! /Users/kogakeita/.npm/_logs/2023-04-04T07_43_55_740Z-debug-0.log
. ✖
An error occurred while installing dependencies.
ERR! Error
ERR! at NPMProxy.installDependencies (/Users/kogakeita/.npm/_npx/eb8bf615e50a412a/node_modules/@storybook/cli/dist/generate.js:36:2016)
ERR! at doUpgrade (/Users/kogakeita/.npm/_npx/eb8bf615e50a412a/node_modules/@storybook/cli/dist/generate.js:390:2783)
ERR! at async withTelemetry (/Users/kogakeita/.npm/_npx/eb8bf615e50a412a/node_modules/@storybook/core-server/dist/index.js:35:3422)
ERR! at async upgrade (/Users/kogakeita/.npm/_npx/eb8bf615e50a412a/node_modules/@storybook/cli/dist/generate.js:390:3336)
ERR! HandledError
ERR! at NPMProxy.installDependencies (/Users/kogakeita/.npm/_npx/eb8bf615e50a412a/node_modules/@storybook/cli/dist/generate.js:36:2016)
ERR! at doUpgrade (/Users/kogakeita/.npm/_npx/eb8bf615e50a412a/node_modules/@storybook/cli/dist/generate.js:390:2783)
ERR! at async withTelemetry (/Users/kogakeita/.npm/_npx/eb8bf615e50a412a/node_modules/@storybook/core-server/dist/index.js:35:3422)
ERR! at async upgrade (/Users/kogakeita/.npm/_npx/eb8bf615e50a412a/node_modules/@storybook/cli/dist/generate.js:390:3336) {
ERR! handled: true,
ERR! cause: Error
ERR! at NPMProxy.executeCommand (/Users/kogakeita/.npm/_npx/eb8bf615e50a412a/node_modules/@storybook/cli/dist/generate.js:39:1669)
ERR! at NPMProxy.runInstall (/Users/kogakeita/.npm/_npx/eb8bf615e50a412a/node_modules/@storybook/cli/dist/generate.js:39:2569)
ERR! at NPMProxy.installDependencies (/Users/kogakeita/.npm/_npx/eb8bf615e50a412a/node_modules/@storybook/cli/dist/generate.js:36:1931)
ERR! at doUpgrade (/Users/kogakeita/.npm/_npx/eb8bf615e50a412a/node_modules/@storybook/cli/dist/generate.js:390:2783)
ERR! at async withTelemetry (/Users/kogakeita/.npm/_npx/eb8bf615e50a412a/node_modules/@storybook/core-server/dist/index.js:35:3422)
ERR! at async upgrade (/Users/kogakeita/.npm/_npx/eb8bf615e50a412a/node_modules/@storybook/cli/dist/generate.js:390:3336)
ERR! }
zsh: exit 1 npx storybook@next upgrade --prerelease

新規プロジェクトにインストールしてみる
npx create-next-app@latest
で新しいNext.jsのプロジェクトを作成。
npx storybook@next init
を実行しました。
結果、以下のコードが生成されました。
package.json
{
"name": "my-app",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"dependencies": {
"@types/node": "18.15.11",
"@types/react": "18.0.33",
"@types/react-dom": "18.0.11",
"eslint": "8.37.0",
"eslint-config-next": "13.2.4",
"next": "13.2.4",
"react": "18.2.0",
"react-dom": "18.2.0",
"typescript": "5.0.3"
},
"devDependencies": {
"@storybook/addon-essentials": "^7.0.2",
"@storybook/addon-interactions": "^7.0.2",
"@storybook/addon-links": "^7.0.2",
"@storybook/blocks": "^7.0.2",
"@storybook/nextjs": "^7.0.2",
"@storybook/react": "^7.0.2",
"@storybook/testing-library": "^0.0.14-next.2",
"eslint-plugin-storybook": "^0.6.11",
"storybook": "^7.0.2"
}
}
.storybook/main.ts
import type { StorybookConfig } from "@storybook/nextjs";
const config: StorybookConfig = {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
],
framework: {
name: "@storybook/nextjs",
options: {},
},
docs: {
autodocs: "tag",
},
};
export default config;
.storybook/preview.ts
import type { Preview } from "@storybook/react";
const preview: Preview = {
parameters: {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
},
};
export default preview;
.eslintrc.json
{
"extends": [
"next/core-web-vitals",
"plugin:storybook/recommended"
]
}

-
storybook
,build-storybook
のnpm scriptの中身が変更された- 以前までは
start-storybook
,build-storybook
をそれぞれ実行していたがこれはstorybook
コマンドに統一された
- 以前までは
-
@storybook/nextjs
が登場したこれにより storybook-addon-next はその役割を終える事になります(今までありがとうございました) -
@storybook/blocks
が新しく追加されるようになっている -
.storybook/
配下の設定ファイルはTypeScriptで生成されるようになった

これらの情報を元に再度自分のプロジェクトで試してみる。

自分のプロジェクト(lgtm-cat-ui)で動作確認(ローカル)
無事に動作している。ように見える。
以前は storybook-addon-next
がないと動作しなかった next/link
のMockも正常に動作している事を確認。

本プロジェクトでは以前以下の記事で紹介した通りChromaticにStorybookをデプロイしている。
デプロイされたChromatic上のStorybookを閲覧したが特に問題はなさそうだった。
という事で storybook build
も正常に動作しているで問題なさそう。

不要なpackageを削除
Storybook6.5の時は npx storybook@next init
を実行すると依存packageに @babel/core
や babel-loader
等が含まれていた。
※ エビデンスとして少し前に試した時のPRを貼っておく。
これらのpackageは不要になったハズなので削除する。

TypeScriptを5系の最新にアップグレード
Storybookを6.5系の時は以下のエラーが発生してStorybookが起動出来なかったが、7系にアップグレードした事で5系にアップグレード出来るようになった。

第一段階完了
最初のPRが完成。
次のPRで非推奨になってしまったStoryの書き方を修正していく。

Storybookの書き方をCSF3に変更する
CSF2の書き方は非推奨になってしまったようなので以下のドキュメントに載っている置き換えコマンドを試してみる。
npx storybook@latest migrate csf-2-to-3 --glob="src/**/*.stories.tsx"

実行結果は下記の通り。
成功しているっぽい。
=> Applying csf-2-to-3: 22 files
Processing 22 files...
Spawning 9 workers...
Sending 3 files to free worker...
Sending 3 files to free worker...
Sending 3 files to free worker...
Sending 3 files to free worker...
Sending 3 files to free worker...
Sending 3 files to free worker...
Sending 3 files to free worker...
Sending 1 files to free worker...
All done.
Results:
0 errors
0 unmodified
0 skipped
22 ok
Time elapsed: 0.687seconds

以下は書き換えられたStorybookの一部を抜粋。
import type { StoryObj } from '@storybook/react';
import { GlobalMenu } from './';
export default {
component: GlobalMenu,
};
type Story = StoryObj<typeof GlobalMenu>;
export const ViewInJapanese: Story = {
args: { language: 'ja' },
};
export const ViewInEnglish: Story = {
args: { language: 'en' },
};
以下のドキュメントを見ると↓のような感じで Meta
を利用していたりするのでここは自分で書き換えるしかなさそう。
// Button.stories.ts|tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';
const meta: Meta<typeof Button> = {
/* 👇 The title prop is optional.
* See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
* to learn how to generate automatic titles
*/
title: 'Button',
component: Button,
};
export default meta;
type Story = StoryObj<typeof Button>;
/*
*👇 Render functions are a framework specific feature to allow you control on how the component renders.
* See https://storybook.js.org/docs/react/api/csf
* to learn how to use render functions.
*/
export const Primary: Story = {
render: () => <Button primary label="Button" />,
};

このプロジェクトは現実的に置き換えられそうな規模なので自分で直すが、大きなプロジェクトだと npx storybook@latest migrate csf-2-to-3 --glob="src/**/*.stories.tsx"
の結果でエラーが出た箇所を修正するあたりが現実的かと思う。

とりあえず書き換え完了

msw がStorybook上で動作するか確認
msw-storybook-addon を利用しているのでこちらが正常動作するか確認する。
先程のプロジェクト(lgtm-cat-ui
)では msw-storybook-addon を利用していないので下記の検証プロジェクトで検証していく。

特に問題はなかった。実行したPRを貼っておく。
ちなみに msw-storybook-addon のREADMEに書かれているが .storybook/preview.ts
は以下の内容で動作した。
import type { Preview } from '@storybook/react';
import { initialize, mswDecorator } from 'msw-storybook-addon';
initialize();
export const decorators = [mswDecorator];
const preview: Preview = {
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
},
};
export default preview;
msw-storybook-addon
があっさり動いてくれて安心した。