Javascript is required
·
8 min read

Document & Test Vue 3 Components With Storybook

Document & Test Vue 3 Components With Storybook Image

Storybook is my tool of choice for UI component documentation. Vue.js is very well supported in the Storybook ecosystem and has first-class integrations with Vuetify and NuxtJS. It also has official support for Vue 3, the latest major installment of Vue.js.

This article will demonstrate how you can set up Storybook with zero-config and built-in TypeScript support, auto-generate controls & documentation, and perform automated snapshot tests for your Vue components.

Why Storybook?

We have components that can have many props, states, slots, etc., which influences its visual representation and more.

This circumstance causes some typical problems for any front-end developer:

  • How can I create documentation for my component that doesn't get outdated?
  • How can I get an overview of all different states and kinds of my component?
  • How can I guarantee that my changes don't influence other states and kinds?
  • How can I show the current implementation to non-developer team members?

Storybook will help us here.

Storybook Setup

First, we need to create a Vue 3 application. We'll use Vite, a new build tool from Evan You, the creator of Vue.js:

bash
npm init vite@latest

Setting up Storybook in an existing Vue 3 project can be done with zero configuration:

bash
npx sb init

This command installs Storybook with its dependencies, configures the Storybook instance, and generates some demo components and stories which are located at src/stories:

Storybook Vue 3 Generated Files

We can now run the following command, which starts a local development server for Storybook and automatically opens it in a new browser tab:

bash
npm run storybook

Storybook Vue 3 Demo

These generated Vue components and stories are good examples of how to write Vue 3 stories. I want to show you some advanced documentation examples using a custom component.

Custom Component Demo

I created a Counter.vue demo component to demonstrate the Storybook integration for this article. The source code is available at GitHub.

The component provides basic counter functionality, has two different visual variants and two slots for custom content.

Let's take a look at the component's code:

1<template>
2  <p>{{ label }}</p>
3  <!-- @slot Slot to show content below label -->
4  <slot name="sub-label" />
5  <div class="container" :class="variant">
6    <button @click="increment()">+</button>
7    <p class="value">{{ count }}</p>
8    <button @click="decrement()">-</button>
9  </div>
10  <!-- @slot Default slot to show any content below the counter -->
11  <slot />
12</template>
13
14<script lang="ts">
15import { ref, watch, PropType } from 'vue'
16import { Variant } from './types'
17
18/**
19 * This is my amazing counter component
20 *
21 * It can increment and decrement!
22 */
23export default {
24  props: {
25    /**
26     * The initial value for the counter
27     */
28    initialValue: {
29      type: Number,
30      default: 0,
31    },
32    /**
33     * Text shown above the counter
34     */
35    label: {
36      type: String,
37      default: 'Counter',
38    },
39    /**
40     * If true, the counter can show negative numbers
41     */
42    allowNegativeValues: {
43      type: Boolean,
44      default: false,
45    },
46    /**
47     * Defines the visual appearance of the counter
48     */
49    variant: {
50      type: String as PropType<Variant>,
51      default: Variant.Default,
52    },
53  },
54  emits: ['counter-update'],
55  setup(props, context) {
56    const count = ref(props.initialValue)
57
58    const increment = () => {
59      count.value += 1
60    }
61
62    const decrement = () => {
63      const newValue = count.value - 1
64      if (newValue < 0 && !props.allowNegativeValues) {
65        count.value = 0
66      } else {
67        count.value -= 1
68      }
69    }
70
71    watch(count, (value) => {
72      context.emit('counter-update', value)
73    })
74
75    return {
76      count,
77      increment,
78      decrement,
79    }
80  },
81}
82</script>
83<style scoped></style>

In the above code, you can see that I've annotated the Vue component with JSDoc comments. Storybook converts them into living documentation alongside our stories.

Warning

Unfortunately, I found no way to add JSDoc comments to the counter-update event. I think it is currently not supported in vue-docgen-api, which Storybook uses under the hood to extract code comments into descriptions. Leave a comment if you know a way how to document events in Vue 3.

Storybook uses so-called stories:

A story captures the rendered state of a UI component. Developers write multiple stories per component that describe all the “interesting” states a component can support.

A component’s stories are defined in a story file that lives alongside the component file. The story file is for development-only, it won't be included in your production bundle.

Now, let's take a look at the code of our Counter.stories.ts:

1import Counter from './Counter.vue'
2import { Variant } from './types'
3
4//👇 This default export determines where your story goes in the story list
5export default {
6  title: 'Counter',
7  component: Counter,
8  //👇 Creates specific argTypes with options
9  argTypes: {
10    variant: {
11      options: Variant,
12    },
13  },
14}
15
16//👇 We create a “template” of