UIコンポーネントをStorybookでカタログ化してChromaticへ公開するまで

# はじめに

フロントエンドで作ったUIコンポーネントを、デザイナーがブラウザ上から確認、操作できるようにして、ゆくゆくはそうしたコンポーネントをデザインシステムとして公開できないか、試してみたことを備忘録として残します。

# 準備
## ツールの選定

コンポーネントを一覧化できるツールとして、[Storybook](https://storybook.js.org/)が有名です。
また、[Storybookの公式ドキュメントにデプロイする手段としてChromatic](https://storybook.js.org/tutorials/intro-to-storybook/react/ja/deploy/)が挙げられていたので、その2つを採用することにしました。

バージョン管理はBitbucket、CIにはBitbucket Pipelinesを使っています。

## Chromaticについて
[Chromatic](https://www.chromatic.com/)は、見た目や機能の変更や不具合を、自動的にキャッチアップできるツールです。コンポーネントの更新中に、見た目や機能で変わった部分を検出して、その変更が意図したものか、問題ないかどうかを確認することができます。

今回は、そうしたビジュアルテストというより、作成したStorybookを外部公開する用途で使いました。

## 開発環境・バージョン

- Node.js ... v21.6.2
- Vue ... 3.2.47 *Storybookテンプレートのバージョンに準拠
- Storybook ... 7.6.6 *Storybookテンプレートのバージョンに準拠
- Chromatic ... 11.3.0
- バージョン管理: Bitbucket
- CI環境: Bitbucket Pipelines


# 手順

## Bitbucketのリポジトリ設定

1. [Bitbucket](https://bitbucket.org/product/)にサインイン(サインアップ)する
![Screenshot 2024-04-17 at 15.57.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3572572/911c4ea0-0056-d72d-eed5-6e527e79a0d5.png)

2. [Create Repository]をクリック
![Screenshot 2024-04-14 at 10.10.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3572572/5486289a-bac7-9845-4968-ff212c83fc61.png)

3. リポジトリ設定を入力して[リポジトリを作成]をクリック
![Screenshot 2024-04-14 at 10.11.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3572572/9ed88b4e-6bee-5dcf-4b9f-5133e556c25a.png)

4. リポジトリトップより[Clone]をクリックして、コマンドをコピー
![Screenshot 2024-04-14 at 10.11.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3572572/6ba47281-471c-3e70-794d-eac154d38d04.png)
![Screenshot 2024-04-14 at 10.12.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3572572/52da138c-f2f2-36eb-fce1-69f8b931cbb0.png)

5. 自分のPCで任意のディレクトリ上でコピーしたコマンドを実行

## 開発環境の構築

[Storybook Tutorials](https://storybook.js.org/tutorials/intro-to-storybook/vue/en/get-started/)を参考に、以下のコマンドをクローンしたリポジトリの親階層で実行します。

1. `npx degit chromaui/intro-storybook-vue-template {リポジトリ名} --force` ... forceオプションを追加することで、リポジトリの名前のディレクト内を上書きすることができます
2. `npm install` ... パッケージインストール
3. `npm run dev` ... ローカルサーバでコンテンツを表示
4. `npm run storybook` ... ローカルサーバ上でStorybookを立ち上げる
5. `npm run build-storybook` ... Storybookで表示するためのファイルを出力

今回、Vue.jsで作ったコンポーネントをStorybookで扱う想定のため、Storybookのドキュメントに記載されている、chromaui/intro-storybook-vue-templateというテンプレートを使っています。  

このテンプレートをクローンしたら、npm scriptsにステップ2~5のコマンドもpackage.jsonに定義されているので、まずはこれらのコマンドを実行して動作確認をしてみましょう。

確認が終わったら、ここまでの変更を以下のコマンドを順に実行して、Bitbucketのリポジトリにプッシュしましょう。

1. `git add .`
2. `git commit -m "first commit"`
3. `git push`

## Chromaticのプロジェクト設定

1. Chromaticにサインイン(サインアップ)する
2. Add Projectを選択して、Bitbucketで作ったリポジトリを指定する
3. 選択したプロジェクトがどれで使われるか聞かれるので、Storybookを選択

![Screenshot 2024-04-17 at 14.25.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3572572/748c4d74-d8b7-c1d5-f19b-790555c4ff48.png)


そうすると、以下のコマンドが表示されます。

![Screenshot 2024-04-14 at 20.17.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3572572/ebe1ce04-4f38-4e08-f583-8fbddfc1562a.png)


- `npm install --save-dev chromatic`
まずは、chromaticのパッケージをローカルのリポジトリ上でインストールします。

- `npx chromatic --project-token={プロジェクトトークン}`  
その後、上記コマンドをプロジェクトトークンごとコピーして、ローカルリポジトリ上で実行します。

もし、npx chromaticのコマンドで、以下のようなエラーメッセージで失敗した場合は、yarn.lockファイルを削除して再度実行してみてください。

```sh
Internal Error: intro-storybook-vue-template@workspace:.: This package doesn't seem to be present in your lockfile; run "yarn install" to update the lockfile
```

npx chromaticコマンドが成功すると、以下のようなログが表示されます。

```sh
✔ Storybook published
We found 3 components with 8 stories.
ℹ View your Storybook at https://{appId + random string}.chromatic.com/

ℹ Speed up Continuous Integration
Your project is linked to Bitbucket so Chromatic will report results there.
This means you can pass the --exit-once-uploaded flag to skip waiting for build results.
Read more here: https://www.chromatic.com/docs/cli#chromatic-options

✖ Found 8 visual changes: Review the changes at https://www.chromatic.com/build?appId={appId}&number=2

ℹ For CI/CD use cases, this command failed with exit code 1
Pass --exit-zero-on-changes to succeed this command regardless of changes.
Pass --auto-accept-changes to succeed and automatically accept any changes.

⚠ No 'chromatic' script found in your package.json
Would you like me to add it for you? [y/N]
```

この部分は、公開されたStorybookへのURLになります(appId + random stringの部分はプロジェクトによって異なります)。
デザイナーに見てもらいたい場合は、このURLを共有すると良いでしょう。

> View your Storybook at https://{appId + random string}.chromatic.com/

プロジェクトトークンは機密上、リポジトリ内のコードに埋め込んだり、外部へ公開しないようにしてください。

## CI (Bitbucket Pipelines) の設定

ローカルPCから都度、`npx chromatic ...`コマンドで反映するのもできますが、Bitbucketでバージョン管理しているので、そこでの変更を検知して、自動的にChromaticへデプロイできるようにしたいですね。

それには、Bitbucketで作ったリポジトリより、パイプラインを作成する必要があります。

1. 作成したBitbucketリポジトリの左メニューから[パイプライン]を選択
![Screenshot 2024-04-15 at 10.39.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3572572/16b7fd80-b499-d7b2-6fbe-79479d81eb16.png)

2. [Build and test a NodeJS code]を選択
![Screenshot 2024-04-15 at 10.42.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3572572/1cc2266b-ae75-2dda-0661-d39ffe2dc25e.png)

3. パイプライン設定画面(bitbucket-pipelines.ymlの編集、環境変数の指定)が表示されるため、ymlファイルを以下のように編集

![Screenshot 2024-04-15 at 10.52.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3572572/b03108ff-7993-6d5a-1185-d04b8389b37c.png)


```yml
image: node:21.6.2

pipelines:
  branches:
    main:
      - step:
          name: install
          caches:
            - node
          script:
            - npm install
      - step:
          name: lint
          caches:
            - node
          script:
            - npm run lint
      - step:
          name: deploy
          deployment: Production
          caches:
            - node
          script:
            - 'npx chromatic --project-token=${CHROMATIC_PROJECT_TOKEN} --exit-zero-on-changes'
```

Bitbucket Pipelinesの書き方は、以下のような関連記事を見ていただくのが良いですが、Circle CIのようなCIツールと同じような記述で書けると思います。

- [bitbucket-pipelines.yml を設定する](https://ja.confluence.atlassian.com/bitbucket/configure-bitbucket-pipelines-yml-792298910.html)
- [Bitbucket PipelinesでCI/CD入門！ビルドとレンタルサーバーへのFTPデプロイを自動化する](https://www.lyzon.co.jp/blog/2021/20210105_bitbucketpipelines/)

大まかな動きとしては、mainブランチにプッシュされたタイミングで以下の順で処理を走らせるようにしています。

1. `npm install` でパッケージをインストール
2. `npm run lint` でLinterチェック
3. `npx chromatic ...` でChromaticにデプロイ

3のコマンドに、 `--exit-zero-on-changes`オプションが追加されていますが、これは最初にChromaticで公開した際のログにもあったように、CI/CDで使う場合オプションなしだとエラーで中断してしまうため、以下どちらかのオプションを指定する必要があります。

```sh
ℹ For CI/CD use cases, this command failed with exit code 1
Pass --exit-zero-on-changes to succeed this command regardless of changes.
Pass --exit-zero-on-changes to succeed this command regardless of changes.
Pass --auto-accept-changes to succeed and automatically accept any changes.
```

`--auto-accept-changes` オプションだと、UI変更を自動的に受け入れてしまうため、`--exit-zero-on-changes` をセットしておくのが無難でしょう。

CHROMATIC_PROJECT_TOKENの部分は、右パネルにあるAdd variablesのところに環境変数として入力します。具体的には、Name/Valueの部分を以下の値を入れます。

![Screenshot 2024-04-15 at 11.02.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3572572/0379044a-9b20-5307-eff9-08612fdd6add.png)


Name ... `CHROMATIC_PROJECT_TOKEN` の文字列
Value ... [Chromaticのプロジェクト設定]で初回Chromaticへの公開時に表示されたプロジェクトトークンの文字列をセット

これにより、コマンド内のCHROMATIC_PROJECT_TOKENというキーに対して、トークンの文字列が入るようになります。

そして、Name/Valueの右にある[Add]ボタンで追加した後、画面を下へスクロールして[Commit file]ボタンをクリックします。

![Screenshot 2024-04-15 at 11.02.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3572572/2215da69-9d4c-dbc0-40c1-e36e044da40b.png)

すると、mainブランチにbitbucket-pipelines.ymlファイルが追加されて、パイプラインが実行されます。

パイプラインのステップが全て正常に完了したら、StatusにSuccessfulと表示されます。

![Screenshot 2024-04-17 at 15.41.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3572572/7904b7a6-0d48-3d40-640d-61f9c218082b.png)

# まとめ

Storybookを導入して、Chromaticへ公開することでブラウザから見られるようにするところまでをやってみました。
今回はFree planを使いましたが、オフィシャルにデザインシステムとして公開するとしたら、[独自ドメインを設定できるプラン](https://www.chromatic.com/pricing)に変更する必要がありそうです。

デザインシステムなどの目的で外部公開することをメインにするなら、他のホスティングサービスを活用して、Chromaticはサービス開発のビジュアルテスト用途に使うのが良いかもしれません。