Zenn
Closed20

Storybook7へのアップグレード

keitaknkeitakn

概要

表題の通り。

個人サービスで実施していきます。

前提条件

検証で利用するプロジェクトはNext.js製アプリのComponentを提供するpackageです。
その為、next/linknext/image に依存するComponentが存在します。

今までは storybook-addon-next というサードパーティ製のpackageで next/linknext/image をモック化していました。

補足

今回検証したpackageは以下の個人サービスで利用されています。

https://lgtmeow.com/

keitaknkeitakn

公式ドキュメントの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
keitaknkeitakn

新規プロジェクトにインストールしてみる

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

自分のプロジェクト(lgtm-cat-ui)で動作確認(ローカル)

無事に動作している。ように見える。

以前は storybook-addon-next がないと動作しなかった next/link のMockも正常に動作している事を確認。

keitaknkeitakn

不要なpackageを削除

Storybook6.5の時は npx storybook@next init を実行すると依存packageに @babel/corebabel-loader 等が含まれていた。

※ エビデンスとして少し前に試した時のPRを貼っておく。

https://github.com/keitakn/next-example/pull/21/files#diff-7ae45ad102eab3b6d7e7896acd08c427a9b25b346470d7bc6507b6481575d519

これらのpackageは不要になったハズなので削除する。

keitaknkeitakn

この2つのpackageは消しても正常動作したので不要と判断しても良さそう。

  • @babel/core
  • babel-loader
keitaknkeitakn

何故か依存関係に webpack もあった。

どういう経緯で追加したのか覚えていないが、これもStorybook7の npx storybook@next init の結果を見る限り不要なので削除する。

npm uninstall -D webpack
keitaknkeitakn

Storybookの書き方をCSF3に変更する

CSF2の書き方は非推奨になってしまったようなので以下のドキュメントに載っている置き換えコマンドを試してみる。

https://storybook.js.org/docs/react/migration-guide#page-top

npx storybook@latest migrate csf-2-to-3 --glob="src/**/*.stories.tsx"
keitaknkeitakn

実行結果は下記の通り。

成功しているっぽい。

=> 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
keitaknkeitakn

以下は書き換えられた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" />,
};

https://storybook.js.org/docs/react/get-started/whats-a-story

keitaknkeitakn

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

keitaknkeitakn

msw がStorybook上で動作するか確認

msw-storybook-addon を利用しているのでこちらが正常動作するか確認する。

先程のプロジェクト(lgtm-cat-ui)では msw-storybook-addon を利用していないので下記の検証プロジェクトで検証していく。

https://github.com/keitakn/next-example

keitaknkeitakn

特に問題はなかった。実行したPRを貼っておく。

https://github.com/keitakn/next-example/pull/31

ちなみに 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 があっさり動いてくれて安心した。

このスクラップは2023/04/21にクローズされました
ログインするとコメントできます