Storybook

Storybook — это инструмент для разработки UI-компонентов в изоляции. Он позволяет визуализировать компоненты, тестировать их состояния и создавать библиотеку интерфейсных элементов. Для интеграции с Vuetify необходимо установить базовые пакеты:

npx sb init
npm install @storybook/vue3 --save-dev
npm install vuetify@next --save

Далее создается файл конфигурации .storybook/main.js:

module.exports = {
  stories: ['../src/**/*.stories.@(js|ts|vue)'],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
  ],
  framework: '@storybook/vue3',
};

Чтобы Vuetify корректно работал в Storybook, создается файл .storybook/preview.js:

import { app } from '@storybook/vue3';
import { createVuetify } from 'vuetify';
import 'vuetify/styles';
import * as components from 'vuetify/components';
import * as directives from 'vuetify/directives';

const vuetify = createVuetify({
  components,
  directives,
});

app.use(vuetify);

export const parameters = {
  actions: { argTypesRegex: '^on[A-Z].*' },
};

Структура файлов и организация историй

Истории обычно создаются рядом с компонентами в папке src/components. Рекомендуется использовать формат .stories.js или .stories.ts. Пример структуры:

src/
  components/
    Button/
      BaseButton.vue
      BaseButton.stories.js

Файл истории для BaseButton.vue:

import BaseButton from './BaseButton.vue';

export default {
  title: 'Components/BaseButton',
  component: BaseButton,
  argTypes: {
    color: { control: 'color' },
    disabled: { control: 'boolean' },
  },
};

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

export const Primary = Template.bind({});
Primary.args = {
  color: 'primary',
  disabled: false,
};

export const Disabled = Template.bind({});
Disabled.args = {
  color: 'secondary',
  disabled: true,
};

Использование аргументов (Args) и Controls

Args позволяют динамически управлять свойствами компонента. Для компонентов Vuetify особенно важно использовать v-model и props, чтобы изменения в Storybook отражались в реальном времени.

Пример компонента с v-model:

<template>
  <v-text-field v-model="text" label="Введите текст" />
</template>

<script>
export default {
  props: {
    modelValue: String,
  },
  emits: ['update:modelValue'],
  data() {
    return { text: this.modelValue };
  },
  watch: {
    text(newVal) {
      this.$emit('update:modelValue', newVal);
    },
  },
};
</script>

История для этого компонента:

import TextField from './TextField.vue';

export default {
  title: 'Components/TextField',
  component: TextField,
  argTypes: {
    modelValue: { control: 'text' },
  },
};

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

export const Default = Template.bind({});
Default.args = {
  modelValue: '',
};

Декораторы для глобальной настройки

Декораторы позволяют оборачивать все истории в общий контекст, что особенно полезно для Vuetify, чтобы единообразно применять темы, сетки и провайдеры.

import { app } from '@storybook/vue3';
import { createVuetify } from 'vuetify';
import 'vuetify/styles';
import * as components from 'vuetify/components';
import * as directives from 'vuetify/directives';

const vuetify = createVuetify({
  components,
  directives,
});

export const decorators = [
  (story) => {
    app.use(vuetify);
    return { components: { story }, template: '<story />' };
  },
];

Документация компонентов с помощью Docs addon

@storybook/addon-docs позволяет автоматически генерировать документацию компонентов Vuetify. Используется синтаксис MDX для комбинирования текста и визуализации:

import { Meta, Story, ArgsTable } from '@storybook/addon-docs';
import BaseButton from './BaseButton.vue';

<Meta title="Components/BaseButton" component={BaseButton} />

# BaseButton

Компонент кнопки с поддержкой цветов Vuetify и состояний disabled.

<ArgsTable story="Primary" />

<Story name="Primary">
  <BaseButton color="primary">Нажми меня</BaseButton>
</Story>

Тестирование визуальных состояний

Storybook интегрируется с @storybook/addon-interactions и визуальными тестами, что позволяет проверять компоненты Vuetify в различных состояниях:

import { within, userEvent } from '@storybook/testing-library';
import { expect } from '@storybook/jest';

export const ClickTest = Template.bind({});
ClickTest.play = async ({ canvasElement }) => {
  const canvas = within(canvasElement);
  const button = canvas.getByText('Нажми меня');
  await userEvent.click(button);
  expect(button).toBeEnabled();
};

Продвинутая настройка тем Vuetify

Для демонстрации разных тем в Storybook можно использовать декораторы и глобальные параметры:

import { createVuetify } from 'vuetify';
import { app } from '@storybook/vue3';
import 'vuetify/styles';
import * as components from 'vuetify/components';
import * as directives from 'vuetify/directives';

const lightTheme = {
  dark: false,
  colors: { primary: '#1976D2', secondary: '#424242' },
};

const darkTheme = {
  dark: true,
  colors: { primary: '#BB86FC', secondary: '#03DAC6' },
};

export const decorators = [
  (story, context) => {
    const vuetify = createVuetify({
      components,
      directives,
      theme: context.globals.theme === 'dark' ? darkTheme : lightTheme,
    });
    app.use(vuetify);
    return { components: { story }, template: '<story />' };
  },
];

export const globalTypes = {
  theme: {
    name: 'Theme',
    description: 'Выбор темы',
    defaultValue: 'light',
    toolbar: { icon: 'circlehollow', items: ['light', 'dark'] },
  },
};

Интеграция с Vue Router и Pinia

Для компонентов, зависящих от маршрутизации или состояния, Storybook позволяет подключать полноценный контекст:

import { app } from '@storybook/vue3';
import { createRouter, createWebHistory } from 'vue-router';
import { createPinia } from 'pinia';

const router = createRouter({
  history: createWebHistory(),
  routes: [{ path: '/', component: { template: '<div>Главная</div>' } }],
});

const pinia = createPinia();

app.use(router);
app.use(pinia);

Это позволяет демонстрировать Vuetify-компоненты в условиях, максимально приближенных к реальному приложению, с поддержкой маршрутов, состояния и глобальных провайдеров.


Вся структура Storybook с Vuetify строится на комбинации Args, декораторов, тем и интеграций, что делает возможным создание полной библиотеки интерфейсных компонентов с визуальными тестами и документацией.