Vueの単一ファイルコンポーネントを使用したストーリーブックの使用


Storybook 設計、書き込み、テストコンポーネントの分離のための素晴らしいツールです.それを使用すると、作成することができますし、すべてのコードとは、実際のアプリケーションでそれらを囲むビジネスロジックを設定する必要がないコンポーネントをテストします.一度に1つのコンポーネントを開発することに焦点を当てたストーリーブックのワークフローは、カプセル化とモジュール性に役立ちます-私たちは、分離の各コンポーネントを開発する場合は、アプリケーションの他の部分に結合されているコンポーネントを開発する可能性は低いです.
ストーリーブックは、反応のコンポーネントを開発するためのツールとしての生活を始めたが、今ではVueを含む他の多くのUIフレームワークのための大きなサポートをしています.Vueを使用してストーリーブックを設定すると簡単ですが、Vueの単一のファイルのコンポーネント(SFC)と、我々は、関連付けられたテンプレート、ロジックを維持するストーリーを書くことができますし、スタイルは、同じファイル内の共同で、ストーリーブックUIのソースを表示することができます.

物語とVueのCLIプロジェクトを設定する


VEE CLIを使用して、ストーリーブックとシンプルなプロジェクトを設定しましょう.
  • の最新バージョンをインストールする
  •   npm install -g @vue/cli
    
  • デフォルトのプリセットで新規プロジェクトを作成する
  •   vue create vue-storybook-tutorial --default
    
  • 新しく作成したプロジェクトディレクトリに変更し、ストーリーブックをインストールします
  •   cd vue-storybook-tutorial
      npx -p @storybook/cli sb init --type vue
    

    シンプルなボタンコンポーネントの作成


    さあ、話を書くための簡単なボタンコンポーネントを作成しましょう.コンポーネントには1つの小文字があります.color どちらの値もnormal (デフォルト)primary .
    <template>
        <button class="button" :class="`button-color--${color}`">
            <slot></slot>
        </button>
    </template>
    
    <script>
    export default {
        name: 'Button',
        props: {
            color: {
                type: String,
                default: 'normal', // 'normal' or 'primary'
            },
        },
    };
    </script>
    
    <style scoped>
    .button {
        appearance: none;
        border: none;
        font-family: sans-serif;
        padding: 8px 16px;
        border-radius: 2px;
    }
    
    .button-color--normal {
        background-color: #eee;
        color: #222;
    }
    
    .button-color--normal:hover,
    .button-color--normal:focus {
        background-color: #e0e0e0;
    }
    
    .button-color--normal:active {
        background-color: #bdbdbd;
    }
    
    .button-color--primary {
        background-color: #2196f3;
        color: #fff;
    }
    
    .button-color--primary:hover,
    .button-color--primary:focus {
        background-color: #1e88e5;
    }
    
    .button-color--primary:active {
        background-color: #1976D2;
    }
    </style>
    

    物語を書く


    インストールすると、ストーリーブックはstories/ ディレクトリにいくつかのサンプルの話で.それらのサンプルを削除して、我々自身の物語を加えましょうstories/Button.stories.js ボタンコンポーネント.
    import Button from '../src/components/Button';
    
    export default {
        title: 'Button',
    };
    
    export const normalButton = () => ({
        components: { Button },
        template: '<Button>Normal Button</Button>',
    });
    
    export const primaryButton = () => ({
        components: { Button },
        template: '<Button color="primary">Normal Button</Button>',
    });
    
    上のコードは新しいComponent Story Format これは、ストーリーブックの外で私たちの物語を使用する機能など、いくつかの良い利点があります-例えば、我々の自動テストで.
    ストーリーブックを実行し、提供されたURLを見てストーリーを見ることができます.
    yarn storybook
    

    ストーリーブックのボタンコンポーネントの2つのストーリー
    これにより、我々は作業童話のセットアップ、1つがありますsuggested in the Storybook docs . しかし、私は構文の強調表示と他の有用な編集コントロールがないので、文字列テンプレートでストーリーを書くという考えが好きではありません.代替案はJSXを書くことです、しかし、それはトレードオフとともに来ます、そして、私はJavaScriptの完全な力がこの場合必要であると思いません.
    私たちがVueのものを使うことができるならばsingle file components ( .vue ファイルを書くには?我々はできることが判明!

    単一ファイルコンポーネントでのストーリーの記述


    各々の物語をそれ自身のファイルに動かしましょう.The .story ファイル名のサフィックスは必要ありませんが、コンポーネントがストーリーであることを迅速な指標として機能します.stories/ButtonNormal.story.vue :
    <template>
        <Button>Normal Button</Button>
    </template>
    
    <script>
    import Button from '../src/components/Button.vue';
    
    export default {
        name: 'ButtonNormal',
        components: { Button },
    };
    </script>
    
    stories/ButtonPrimary.story.vue :
    <template>
        <Button color="primary">Primary Button</Button>
    </template>
    
    <script>
    import Button from '../src/components/Button.vue';
    
    export default {
        name: 'ButtonPrimary',
        components: { Button },
    };
    </script>
    
    我々は現在更新stories/Button.stories.js 新しいコンポーネントを使用するには、次の手順に従います.
    import ButtonNormal from './ButtonNormal.story.vue';
    import ButtonPrimary from './ButtonPrimary.story.vue';
    
    export default {
        title: 'Button',
    };
    
    export const normalButton = () => ButtonNormal;
    
    export const primaryButton = () => ButtonPrimary;
    
    現在実行中yarn storybook 以前と同じストーリーを生成する必要があります.

    何を得たか


    同じことをするために異なるアプローチがあるとき、いつものように、あらゆるアプローチはトレードオフで来ます.この場合の主な欠点は、各ストーリーに必要なSFC形式の追加ファイルと関連するボイラープレートです.
    しかし、私は我々が得るもののためにそれの価値があると思います:
  • 構文強調表示と完全なエディタをサポートする慣用のVueテンプレート
  • 私たちがそれを必要とする物語のためのスコープのCSSスタイル
  • より大きい物語のためにコードを整理するtidier方法
  • 我々はここで停止することができますが、私たちが行うことができます重要な改善があります:ストーリーブックのUIでストーリーのソースを表示する機能を追加します.

    物語の源を見る


    役人がいるStorysource addon 物語のソースを表示するためのサポートを追加します.残念ながら我々はここでのセットアップでは動作しませんので、それを使用することはできません:それは我々が我々のストーリーインラインを書いていると仮定しますが、我々はしていない-彼らは別のファイルからインポートされます.
    ストーリーのソースを表示するには、このセットアップで動作する独自のソースパネルでストーリーブックUIを拡張する必要があります.そのためには、
  • 追加する<include-source> custom block ストーリーコンポーネントのファイルには、ストーリーソースをロードするカスタムWebpackローダを書き込む
  • ストーリーブックUIでソースを表示するにはaddonを書き込みます
  • < include source >カスタムSFCブロック


    最初のステップは、ストーリーソースを読んで、ビルド時にストーリーオブジェクトにそれを添付しているので、実行時にUIで表示できるようになります.そのためには二つのことが必要です.
  • へのパス.story.vue ファイルを読むことができるので
  • ソースを読み込み、コンポーネントに添付するWebpackローダ
  • 残念なことに、Webpackのローダは、現在処理されているコード(またはWebPack用語の「エントリ」)をファイルのパスに直接アクセスできません.しかし、彼らがアクセスするのは、そのエントリのソースです.それで、我々はエントリのファイルのパスを埋め込むことができて、代わりにそれを使用することができます.
    これを行うには良い方法はVUEローダーのカスタムブロック機能を使用して、デフォルトの横にある独自のブロックを定義することができます<template> , <script> , and <style> ブロック.VUEローダはブロックを解析し、その内容をカスタムのWebpackローダに渡します.そして、それは注釈を付けるために解析されたコンポーネントも受けます.

    カスタムブロックの追加


    各々の終わりに.story.vue ファイルを追加しましょう<include-source> ファイルのパスをブロックします.stories/ButtonNormal.story.vue :
    + <include-source>stories/ButtonNormal.story.vue</include-source>
    
    stories/ButtonPrimary.story.vue :
    + <include-source>stories/ButtonPrimary.story.vue</include-source>
    
    ここで、ストーリーブロックのWebPack設定を拡張してカスタムブロックを処理するローダを追加します.ファイルを作成する.storybook/webpack.config.js 次のコンテンツを使用します.
    const path = require('path');
    
    module.exports = ({ config }) => {
        // Add a custom loader to load and attach the source of the file
        // specified in a <include-source> custom block of a Vue file
        config.module.rules.push({
            // The block type: <include-source>
            resourceQuery: /blockType=include-source/,
            // The custom loader: source-loader.js file in the current directory
            loader: path.resolve(__dirname, 'source-loader.js'),
            // Pass the repo's root path in the loader options to resolve the
            // relative source file paths
            options: {
                rootPath: path.resolve(__dirname, '..'),
            },
        });
    
        return config;
    };
    
    でファイルを作成する.storybook/source-loader.js カスタムローダを使用します.
    const fs = require('fs');
    const path = require('path');
    
    module.exports = function(source, sourceMap) {
        // `source` (the string in the custom <include-source> block) contains the file path
        const filePath = path.join(this.query.rootPath, source.trim());
    
        // Read the referenced file and remove the <include-source> block, so it doesn't
        // show up in the source code that will be shown in the UI
        const fileContent = fs
            .readFileSync(filePath, 'utf8')
            .replace(/<include-source>.*<\/include-source>\n/, '');
    
        // Generate a function that'll receive the Vue component and attach the source
        this.callback(
            null,
            `export default function (Component) {
                Component.options.__source = ${JSON.stringify(fileContent)};
            }`,
            sourceMap
        );
    };
    

    ストーリーブックのソースパネルの追加


    ビルド時に対応するコンポーネントに接続されている各ストーリーのソースで、ソースコードを表示する新しいパネルを追加するストーリーブックアドオンを記述できます.
    ファイルを作成する.storybook/source-addon.js 次のコンテンツを使用します.
    import React from 'react';
    import { addons, types } from '@storybook/addons';
    import { useParameter } from '@storybook/api';
    import { AddonPanel } from '@storybook/components';
    import { SyntaxHighlighter } from '@storybook/components';
    
    const ADDON_ID = 'vueStorySource';
    const PARAM_KEY = 'source';
    const PANEL_ID = `${ADDON_ID}/panel`;
    
    // The SourcePanel component (React)
    const SourcePanel = ({ active }) => {
        const source = useParameter(PARAM_KEY, null);
        return active && source
            ? React.createElement(
                    SyntaxHighlighter,
                    {
                        language: 'html',
                        showLineNumbers: false,
                        copyable: true,
                        padded: true,
                        format: false,
                    },
                    source
                )
            : null;
    };
    
    // Register the addon
    addons.register(ADDON_ID, () => {
        const render = ({ active, key }) =>
            React.createElement(
                AddonPanel,
                { active, key },
                React.createElement(SourcePanel, { active })
            );
    
        addons.add(PANEL_ID, {
            type: types.PANEL,
            title: 'Source',
            render,
            paramKey: PARAM_KEY,
        });
    });
    
    上のコードはSourcePanel を使用するコンポーネント useParameter ストーリーのフックは、物語のソースを取得し、それを使用してレンダリング SyntaxHighlighter コンポーネントのストーリーブックに含まれます.The source パラメータは、物語の parameters 以下のように設定されます.

    store ()ヘルパー関数


    加えるsource パラメータを設定する必要があります.story.vue コンポーネントソースと定義されているときにストーリーオブジェクトにアタッチします.私たちはすべての物語のためにこれをしているので、Aを書きましょうstory そのロジックをラップするヘルパー関数.
    で新しいファイルを作成するstories/story.js 次のコンテンツを使用します.
    export function story(StoryComponent, options = {}) {
        // Get the `withSource` option, default to true. Making this an option
        // allows us to opt-out of displaying the source of a story.
        const { withSource } = Object.assign({ withSource: true }, options);
    
        // The story export that Storybook will use
        const storyExport = () => StoryComponent;
    
        // Attach the source as a story paramter
        if (withSource) {
            storyExport.story = {
                parameters: {
                    // `.__source` is from our custom <include-source> SFC block
                    // and webpack loader
                    source: StoryComponent.__source,
                },
            };
        }
    
        return storyExport;
    }
    
    これで、各ストーリーの定義を更新しますstories/Button.stories.js ソースをストーリーパラメータとして添付するヘルパーを使用するには、次の手順に従います.
    import { story } from './story';
    import ButtonNormal from './ButtonNormal.story.vue';
    import ButtonPrimary from './ButtonPrimary.story.vue';
    
    export default {
        title: 'Button',
    };
    
    export const normalButton = story(ButtonNormal);
    
    export const primaryButton = story(ButtonPrimary);
    
    我々が特定の物語の上で源を望まないならば、我々は{ withSource: false } への2番目のパラメータとしてstory() 機能
    export const storyWithDisabledSource = story(MyStory, { withSource: false });
    

    ソース帳をストーリーブックで登録する


    最後のものは、絵本の我々の新しいパネルを見るために、アドオンを登録することです.更新.storybook/addons.js 新しいアドオンをインポートして登録するには、次の手順に従います.
    import '@storybook/addon-actions/register';
    import '@storybook/addon-links/register';
    
    import './source-addon';
    
    現在実行中yarn storybook 選択したストーリーのソースが表示される新しいパネルソースを追加する必要があります.

    ストーリーのソースとボタンのコンポーネントの2つの物語

    結論


    この記事では、ストーリーブックのストーリーのソースを表示する機能を持つ単一のファイルコンポーネントの記事を書くための適切なセットアップを示してきました.私は以下のコメントでこの質問に答えてうれしいです.
    この投稿用の完全なソースコードはhttps://github.com/JosephusPaye/vue-storybook-tutorial .
    このポストのアイデアは私の仕事から来ましたKeen UI , VUEのための軽量材料に触発されたUIコンポーネントライブラリチェックアウトstorybook branch 使用中のこのセットアップの現実の例のために.

    付加する


    このポストは私の一部であり、2020年には毎週公開されています.