📚

Bun+ReactプロジェクトにStorybookを導入する

2024/04/27に公開

はじめに

Bunは非常に動作が早いNode.js互換のJavaScriptランタイムになります。今回はBunを使いつつ、フロントエンド開発では絶対に欠かせない、コンポーネントカタログ生成ツールのStorybookを導入します。

この記事では、Bun + Vite + Reactの環境にStorybookを導入する方法を書きます。
bunx run storybook --initでは環境がセットアップが出来なかったので、手動で用意する方法を紹介します。

プロジェクトを生成

以下のコマンドで新しいプロジェクトを生成します。

bun create vite my-app

frameworkはReact、variantはTypeScript + SWCを選択します。

各種設定ファイルの変更

package.jsonを修正する

以下の内容を追加します。

package.json
{
  ...
  "scripts": {
    "sb": "storybook dev -p 6006",
    "sb:build": "storybook build"
  },
  "devDependencies": {
    "storybook": "^8.0.9",
    "@chromatic-com/storybook": "^1.3.1",
    "@storybook/addon-essentials": "^8.0.6",
    "@storybook/addon-interactions": "^8.0.6",
    "@storybook/addon-links": "^8.0.6",
    "@storybook/addon-onboarding": "^8.0.6",
    "@storybook/blocks": "^8.0.6",
    "@storybook/react": "^8.0.6",
    "@storybook/react-vite": "^8.0.6",
    "@storybook/test": "^8.0.6",
  }
}
Storybookの設定ファイルを追加する
.storybook/main.ts
import type { StorybookConfig } from "@storybook/react-vite";

const config: StorybookConfig = {
  stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
  addons: [
    "@storybook/addon-onboarding",
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@chromatic-com/storybook",
    "@storybook/addon-interactions",
  ],
  framework: {
    name: "@storybook/react-vite",
    options: {},
  },
  docs: {
    autodocs: "tag",
  },
};
export default config;

.storybook/preview.tsx
import type { Preview } from "@storybook/react";
import React from "react";
import "../src/index.css";

const preview: Preview = {
  parameters: {
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/i,
      },
    },
  },
  decorators: [(Story) => <Story />],
};

export default preview;

サンプルのコンポーネントとストーリーを追加

サンプルコンポーネントを追加する
src/sampleButton/index.tsx
import "./index.css";

interface ButtonProps {
  /**
   * Is this the principal call to action on the page?
   */
  primary?: boolean;
  /**
   * What background color to use
   */
  backgroundColor?: string;
  /**
   * How large should the button be?
   */
  size?: "small" | "medium" | "large";
  /**
   * Button contents
   */
  label: string;
  /**
   * Optional click handler
   */
  onClick?: () => void;
}

/**
 * Primary UI component for user interaction
 */
export const Button = ({
  primary = false,
  size = "medium",
  backgroundColor,
  label,
  ...props
}: ButtonProps) => {
  const mode = primary
    ? "storybook-button--primary"
    : "storybook-button--secondary";
  return (
    <button
      type="button"
      className={["storybook-button", `storybook-button--${size}`, mode].join(
        " "
      )}
      style={{ backgroundColor }}
      {...props}
    >
      {label}
    </button>
  );
};

export default Button;

src/SampleButtn/index.css
.storybook-button {
  font-family: "Nunito Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-weight: 700;
  border: 0;
  border-radius: 3em;
  cursor: pointer;
  display: inline-block;
  line-height: 1;
}
.storybook-button--primary {
  color: white;
  background-color: #1ea7fd;
}
.storybook-button--secondary {
  color: #333;
  background-color: transparent;
  box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;
}
.storybook-button--small {
  font-size: 12px;
  padding: 10px 16px;
}
.storybook-button--medium {
  font-size: 14px;
  padding: 11px 20px;
}
.storybook-button--large {
  font-size: 16px;
  padding: 12px 24px;
}

ストーリーファイルを追加する
index.stories.tsx
import type { Meta, StoryObj } from "@storybook/react";
import { fn } from "@storybook/test";
import Button from ".";

// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
const meta = {
  title: "Example/Button",
  component: Button,
  parameters: {
    // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
    layout: "centered",
  },
  // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
  tags: ["autodocs"],
  // More on argTypes: https://storybook.js.org/docs/api/argtypes
  argTypes: {
    backgroundColor: { control: "color" },
  },
  // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
  args: { onClick: fn() },
} satisfies Meta<typeof Button>;

export default meta;
type Story = StoryObj<typeof meta>;

// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
export const Primary: Story = {
  args: {
    primary: true,
    label: "Button",
  },
};

export const Secondary: Story = {
  args: {
    label: "Button",
  },
};

export const Large: Story = {
  args: {
    size: "large",
    label: "Button",
  },
};

export const Small: Story = {
  args: {
    size: "small",
    label: "Button",
  },
};

以上で設定完了です。

Storybookの起動

パッケージのインストール

bun i

起動コマンド

bun run sb

おわりに

BunでもStorybookを起動することができました😊
今後もBunを触っていきたいと思います。

コラボスタイル Developers

Discussion