📝

Svelte3のプロジェクトにStorybookを導入する

2021/02/21に公開

Install Storybookに従って、既存の Svelte3 プロジェクトに導入してみる。

使用している Storybook と Svelte のバージョンは次の通り。

  • Storybook 6.1.18
    • @storybook/svelte 6.1.18
  • Svelte 3.32.3

Svelte3 プロジェクトは次の記事の通り Typescript、Pug, Sass を使えるようにしている。

インストール

$ npx sb init

実行すると次のディレクトリが作成される。

  • .storybook
  • src/stories

生成されるファイル群は次の通り。

ファイルツリーを開く
❯ tree .storybook src/stories
.storybook
├── main.js
├── preview.js
src/stories
├── Button.stories.js
├── Button.svelte
├── Header.stories.js
├── Header.svelte
├── Introduction.stories.mdx
├── Page.stories.js
├── Page.svelte
├── assets
│   ├── code-brackets.svg
│   ├── colors.svg
│   ├── comments.svg
│   ├── direction.svg
│   ├── flow.svg
│   ├── plugin.svg
│   ├── repo.svg
│   └── stackalt.svg
├── button.css
├── header.css
└── page.css

サンプルのストーリーが生成されている。

起動

インストールが終わったら次のコマンドで起動する。

$ yarn storybook

自動的にデフォルトブラウザが開き、サンプルの Storybook が参照できる。

ざっと触ってみる

src/stories ディレクトリ以下には、サンプルとなる Svelte コンポーネントとそれらに対応したストーリファイルが作成されていて、それらを参照できる。

コンポーネントの表示モードは CanvasDocs の二種類ある。

Canvas

Canvas 表示では選択したストーリーでコンポーネントのみが表示される。

Button - Canvas

画面下部に表示される Control でコンポーネントの様々なパラメータを変更して試すこともできる。

また、背景をダークにしたり、グリッドの表示もできる。
Button - Dark Grid

更に、モバイル端末をエミュレートするためにプレビューのサイズ変更も可能となっている。
Button - Canvas Small Mobile

Docs

Docs 表示では選択したコンポーネントの ArgsTable とすべてのストーリーが表示される。
ArgsTable ではコンポーネントの引数をインタラクティブに変更できる。

Button - Docs

Typescript 対応

Typescript や Pug、Sass を使ったコンポーネントを扱うためには svelte-preprocess を使う。
Svelte は Rollup を利用するが、Storybook は Webpack を使うので、次の様に .storybook/webpack.config.js を追加する。

.storybook/wobpack.config.js
const sveltePreprocess = require("svelte-preprocess");

module.exports = async ({ config }) => {
  const svelteLoader = config.module.rules.find(r => r.loader && r.loader.includes("svelte-loader"));
  svelteLoader.options = {
    ...svelteLoader.options,
    preprocess: sveltePreprocess(),
  };
  return config;
};

stories ディレクトリを変更する

npx sb init コマンドを実行すると、ストーリーを置くディレクトリは src/stories に配置される。
src ディレクトリにはプロダクションコードのみを起きたいので stories ディレクトリを src 外に出したい。
まあ、好みの問題なので src/stories で気にならないならそのままで良い。
移動させたい場合には、ディレクトリを移動させた後、.storybook/main.jsstories に設定する。

次の例は、<project-root>/src/stories から <project-root>/stories に移動したときの設定変更例。

.storybook/main.js
module.exports = {
  "stories": [
-    "../src/**/*.stories.mdx",
-    "../src/**/*.stories.@(js|jsx|ts|tsx)"
+    "../stories/**/*.stories.mdx",
+    "../stories/**/*.stories.@(js|jsx|ts|tsx)"
  ],
  "addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials"
  ]
}

ViewPort アドオンの設定

Storybook は各種アドオンで機能追加することが可能になっている。
先に紹介したプレビューの表示サイズの変更も @storybook/addon-viewport により実現されている。
@storybook/addon-viewportnpx sb init でインストールされる @storybook/addon-essentials に含まれているので別途インストールは不要。

デフォルトでは表示サイズは Small mobileLarge mobileTablet といったざっくりした種類しか選べない。

次の設定を .storybook/preview.js に加えると iPhone XiPadPixel といった代表的な端末サイズで選択できるようになる。

.storybook/preview.js
import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport';  // <= 追加
export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
  viewport: {                                                   // <= 追加
    viewports: INITIAL_VIEWPORTS,                               // <= 追加
  },                                                            // <= 追加
}

独自の表示サイズを追加したければ次のドキュメントに方法が記載されている。

独自のストーリーを追加する

次の様なコンポーネントのストーリーを作ってみる。これは Svelte の公式テンプレにある App.svelte を Typesctipt 、 Pug 、 Sass で書き直したもの。

App.svelte
<svelte:options tag="my-hello" immutable="{true}"/>
<script lang="ts">
  export let name: string;
</script>

<template lang='pug'>
  main
    h1 Hello {name}!
    p
      | Visit the
      a(href="https://svelte.dev/tutorial") Svelte tutorial
      | to learn how to build Svelte apps.
</template>

<style lang='sass'>
  main
    text-align: center
    padding: 1em
    max-width: 240px
    margin: 0 auto

  h1
    color: #ff3e00
    text-transform: uppercase
    font-size: 4em
    font-weight: 100

  @media (min-width: 640px)
    main
      max-width: none
</style>

Component Story Format (CSF)

標準のストーリーの記述書式。これで記述したストーリーは次の通り。

App.stories.ts
import App from '../src/App.svelte';

export default {
  title: 'Components/App',
  component: App,
  parameters: {
    docs: {
      description: {
        component: 'Svelte3 sanple app',
      },
    },
  },
  argTypes: {
    name: {
      type: { required: true },
      description: 'text',
    },
  },
};

const Template = ({ ...args }) => ({
  Component: App,
  props: args,
});

export const World = Template.bind({});
World.args = {
  name: 'world',
};

export const Hoge = Template.bind({});
Hoge.parameters = {
  docs: {
    description: {
      story: 'Description of this story.',
    },
  },
};
Hoge.args = {
  name: 'hoge',
};

これを表示した結果は次の通り。

Canvas

App - Canvas

Dacs

App - Docs

MDX

MDX は Markdown の中に JSX を記述できるフォーマット[1]。ストーリーを記述するもう1つの形式である。

前述の CSF 形式での記述と同等の内容を記述した MDX は次の通り。

App.stories.mdx
import { Meta, Story, Canvas, ArgsTable } from '@storybook/addon-docs/blocks';
import App from '../src/App.svelte';

<Meta title="Components/App-MDX" component={App}
  argTypes={{
    name: {
      type: { required: true },
      description: 'text',
    },
  }}
/>

export const Template = ({ ...args }) => ({
  Component: App,
  props: args,
});

## App

Svelte3 sanple app

<Canvas>
  <Story name='World'
    args={{
      name: 'world',
    }}>
    {Template.bind({})}
  </Story>
</Canvas>

<ArgsTable story='World'/>

## Stories
### Hoge

Description of this story.

<Canvas>
  <Story name='Hoge'
    args={{
      name: 'hoge',
    }}>
    {Template.bind({})}
  </Story>
</Canvas>

Markdown で記述できるので長文のコンポーネントの説明を記述する場合には CSF よりも書きやすいと思う。
しかし、CSF では自動で入るストーリー毎のセクションタイトルなどを自分で記述する必要がある。
また、記述する上で細かに注意する必要な部分がある。例えば Canvas の閉じタグと ArgsTable タグの間に空行を入れないとレンダリングエラーが発生したりする[2]

まとめ

Svelte3 のプロジェクトに Storybook を導入方法と Typescript に対応した設定、ViewPort アドオンの設定などを説明した。
また、ストーリファイルの作成についても紹介した。

参考

脚注
  1. 現在は様々なフレームワークのコンポーネントに対応しているが、もともと Storybook は React コンポーネント用のツールだったので JSX なのだろう。 ↩︎

  2. 今の所気づいているのはこれだけ。どうしてエラーになるのかわからずハマって結構な時間を無駄にした。他にもあるだろうし、今後のバージョンアップで改善するかもしれない。 ↩︎

Discussion