Storybook 8 がリリースされたので早速アップグレードしてみた (React/Next.js)
背景
2023年12月頃に alpha 版がリリース、その後 beta, RC と段階を踏んでいた Storybook 8 が、2024年3月13日に正式リリース[1]されました 🎉
シンプルなプロジェクトであれば npx storybook@latest upgrade
でいい感じにアップグレードしてくれるのですが、少し複雑な修正が必要だとうまく行かないことも多く、今回もその例に漏れずエラーが出てしまったため、手動であれこれ対応しながら 7.6.6 から 8.x にアップグレードしました。[2]
すべてを網羅している訳ではなくあくまで私のプロジェクト[3]で対応が必要だった内容をまとめたものとなりますので、必ず公式のマイグレーションガイドも参照ください。
やったこと
Storybook 関連のライブラリのバージョンアップ
公式の storybook
および @storybook/*
ライブラリの 8.x へのバージョンアップを行いました。
非公式のライブラリとして storycap を使用していますが、とても有り難いことに早くも Storybook 8 に対応した v4.3.0 がリリースされており、スムーズにバージョンアップができました。
@storybook/addons
が依存関係に追加されている場合は、エラーになるので削除します。
argTypesRegex
を削除し、ハンドラに fn()
を指定
@storybook/addon-actions
を使用する際、今までは argTypesRegex: "^on.*"
を指定して onXXX()
という関数を action arguments として自動設定するのが一般的でしたが、この方法が非推奨になり、代わりに @storybook/test
から import した fn()
を args
にそれぞれ渡すことが推奨されています。
This is quite useful when your component has dozens (or hundreds) of methods and you do not want to manually apply the fn utility for each of those methods. However, this is not the recommended way of writing actions. That's because automatically inferred args are not available as spies in your play function. If you use argTypesRegex and your stories have play functions, you will need to also define args with the fn utility to test them in your play function.
fn()
を使うことで play function でも自動でその関数をモックしてくれるようです。
またこれに付随した修正だと思われますが、 TypeScript を使用している場合、Props として Optional でない関数が定義されていても勝手に Optional にしてくれていましたがこの挙動がなくなったため、 fn()
を渡さないとエラーになりました。
私のプロジェクトではそこまで数が多くなかったため手動で修正しましたが、規模のあるプロジェクトの場合はこれが一番大きな修正になると思います。
Webpack の Compiler 設定
@storybook/react-webpack5
に今まで含まれていた Babel のセットアップが標準で含まれなくなり、 Babel または SWC を使いたい場合別途設定が必要になりました。
例えば Babel を設定する場合、 @storybook/addon-webpack-compiler-babel
を依存関係に追加した上で main.(js|ts)
の addons
に追加する必要があります。
ドキュメントにあるように、
npx storybook@latest add @storybook/addon-webpack5-compiler-babel
コマンドを実行すると依存関係への追加と main.(js|ts)
の修正の両方をやってくれます。[4]
私のプロジェクトでは monorepo 内の共通 UI ライブラリで React Native Web を使っているのですが、元々 .babelrc
を設置していたため Babel を設定しました。[5]
*.stories.mdx の修正
過去に init
コマンドで作成された Introduction.stories.mdx
をそのまま置いていたところ Error: Invariant failed: No matching indexer found for /.../Introduction.stories.mdx
というエラーが発生しました。
特に使っていなかったので削除してもよかったのですが、マイグレーションガイドに従って storybook migrate mdx-to-csf
コマンドを使って *.stories.mdx
ファイルを *.mdx
ファイルに修正しました。
# Convert stories in MDX to CSF
npx storybook@latest migrate mdx-to-csf --glob "src/**/*.stories.mdx”
Play function 再利用時の型修正
composeStories
を使い Jest 等で play function を再利用するときの .play()
TypeScript の型が Optional になったため .play?.()
もしくは .play!()
に変更する必要がありました。
const { Primary } = composeStories(stories)
// before
await Primary.play(...)
// after
await Primary.play?.(...) // if you don't care whether the play function exists
await Primary.play!(...) // if you want a runtime error when the play function does not exist
ただしマイグレーションガイドによると近日中に Story の play function の有無が型に反映されるようにすることを予定しているとのことなので、この改善が行われれば .play()
に戻すことができるようになりそうです。
There are plans to make the type of the play function be inferred based on your imported story's play function in a near future, so the types will be 100% accurate.
まとめ
ここまで Storybook 8 のアップデート内容について触れてきませんでしたが、
- Chromatic と連携した新しい Visual Testing Addon
- Next.js の React Server Component サポート
- Vite 対応の改善
などを始めとした多くの新機能や改善が行われており、多方面で嬉しいリリースとなっています。
必ずしも常に最新版に追従するべきという訳ではないですが、いずれは避けて通れなくなる可能性が高いので、例えすぐにアップグレードしない場合でも対応内容をある程度は把握しておくといいでしょう。
Discussion