RACCOON TECH BLOG

株式会社ラクーンホールディングスのエンジニア/デザイナーから技術情報をはじめ、世の中のためになることや社内のことなどを発信してます。

Storybook駆動開発とは?UIコンポーネントを効率よく作る

こんにちは、デザイン戦略部の哲也です。
Storybook駆動開発を知っていますか?

ページ全体をいきなり作るのではなく、「ボタン」や「モーダル」、「入力欄」などのUI部品を先に個別に作るという開発スタイルがあります。
こうしたUIパーツをひとつずつ作って確認できるツールが、Storybook(ストーリーブック)です。

この記事では、Storybookとは何か、Storybook駆動開発の考え方やメリット・デメリットなどを紹介していきます。

Storybookとは?

Storybookとは、VueやReactなどのフレームワークで作ったUIコンポーネントを単体で開発・確認・ドキュメント化できる開発ツールです。
Storybookを使うと、ページ全体を表示しなくても、個々のボタンやフォームなどのパーツをブラウザ上で確認・切り替え・テストしながら開発することができます。

Storybook駆動開発とは?

Storybook駆動開発とは、ページ全体を作る前に、まずStorybook上でUIコンポーネントを設計・開発・テストする開発手法です。
ページ全体を先に作って、そこにUIをあてはめていくという従来のやり方とは違い、UIを先に作ってあとからページに組み込むという流れになります。

Storybook駆動開発のメリット

デメリット

コード例

簡単なボタンコンポーネントとフォームの入力画面のコンポーネントを例として紹介します。
※VueやStorybookのバージョンによって記述方法が異なるのでご注意ください。

ボタン

MyButton.vue

<template>
  <button :class="['btn', variant]" :type="type">
    <slot />
  </button>
</template>

<script setup>
defineProps({
  variant: { type: String, default: 'primary' },
  type: { type: String, default: 'button' }
});
</script>

<style scoped>
.btn {
  padding: 0.5em 1em;
  border: none;
  cursor: pointer;
}
.primary {
  background-color: #42b983;
  color: white;
}
.secondary {
  background-color: #eee;
  color: #333;
}
</style>

MyButton.stories.js

import MyButton from './MyButton.vue';

export default {
  title: 'Components/MyButton',
  component: MyButton,
  args: {
    type: 'button',
    variant: 'primary',
  }
};

const Template = (args) => ({
  components: { MyButton },
  setup() {
    return { args };
  },
  template: `
    <MyButton v-bind="args">
      {{ args.label }}
    </MyButton>
  `
});

export const Primary = Template.bind({});
Primary.args = {
  label: 'Primary Button',
  variant: 'primary'
};

export const Secondary = Template.bind({});
Secondary.args = {
  label: 'Secondary Button',
  variant: 'secondary'
};

export const SubmitButton = Template.bind({});
SubmitButton.args = {
  label: 'Submit',
  type: 'submit',
  variant: 'primary'
};

プレビュー

Storybook上で以下のように確認できます。
各ボタンの見た目や表示内容を、実際に切り替えながら確認できます。

緑色の「Primary Button」表示

グレー背景の「Secondary Button」表示

Submitボタンの動作確認

入力画面

UserForm.vue

<template>
  <form @submit.prevent="handleSubmit">
    <div>
      <label for="name" class="label">名前</label>
      <input id="name" v-model="name" type="text" />
      <p v-if="nameError" class="error">{{ nameError }}</p>
    </div>
    <div>
      <label for="email" class="label">メールアドレス</label>
      <input id="email" v-model="email" type="email" />
      <p v-if="emailError" class="error">{{ emailError }}</p>
    </div>
    <MyButton type="submit" variant="primary" class="submit-button">
      確認画面へ
    </MyButton>
  </form>
</template>

<script setup>
import { ref } from 'vue';
import MyButton from '../MyButton/MyButton.vue';

const props = defineProps({
  defaultName: { type: String, default: '' },
  defaultEmail: { type: String, default: '' },
  onSubmit: { type: Function, default: () => {} },
});

const name = ref(props.defaultName);
const email = ref(props.defaultEmail);
const nameError = ref('');
const emailError = ref('');

const validate = () => {
  nameError.value = '';
  emailError.value = '';
  let isValid = true;

  if (!name.value.trim()) {
    nameError.value = '名前を入力してください';
    isValid = false;
  }

  if (!email.value.trim()) {
    emailError.value = 'メールアドレスを入力してください';
    isValid = false;
  }

  return isValid;
};

const handleSubmit = () => {
  if (validate()) {
    props.onSubmit({
      name: name.value,
      email: email.value
    });
  }
};
</script>

<style scoped>
.label {
  margin-top: 10px;
  display: block;
}
.error {
  margin-top: 5px;
  color: red;
}
.submit-button {
  margin-top: 20px;
}
</style>

UserForm.stories.js

import UserForm from './UserForm.vue';
import { action } from '@storybook/addon-actions';

export default {
  title: 'Components/UserForm',
  component: UserForm,
};

const Template = (args) => ({
  components: { UserForm },
  setup() {
    return { args };
  },
  template: '<UserForm v-bind="args" />',
});

export const Default = Template.bind({});
Default.args = {
  defaultName: '',
  defaultEmail: '',
  onSubmit: action('フォームデータ'),
};

export const BackToInput = Template.bind({});
BackToInput.args = {
  defaultName: '山田太郎',
  defaultEmail: 'taro@example.com',
  onSubmit: action('フォームデータ'),
};

プレビュー

Storybook上で以下のように確認できます。
入力画面の表示や動作を確認することができます。

初期表示

エラー表示

「確認画面へ」を押した時のデータを確認

確認画面から戻った時の表示

まとめ

今回はボタンと入力画面を例に紹介しました。
実際の業務では、入力 → 確認 → 完了 の3ステップのフォームにStorybookを活用して試してみました。
ボタンなどの細かいパーツを先に作成してStorybookで確認し、
その後「入力画面 → 確認画面 → 完了画面」の順で、各画面をStorybookで動作確認しながら開発を進めました。
動作確認や、propsの違いによる表示のチェックが簡単にできて、とてもスムーズに開発を進められると感じました。
Storybook駆動開発、ぜひ一度試してみてください!

ラクーンホールディングスではエンジニア・デザイナーを大募集中です!
少しでも興味がありましたらぜひご応募ください!

一緒にラクーンのサービスを作りませんか? 採用情報を詳しく見る

関連記事

運営会社:株式会社ラクーンホールディングス(c)2000 RACCOON HOLDINGS, Inc