yarn storybook # или yarn sbStorybook запускается на порту 6006. Версия: 10.3.3, фреймворк: Vue 3 + Vite.
| Файл | Назначение |
|---|---|
.storybook/main.ts |
Основной конфиг: пути к stories, аддоны, алиасы Vite, env-переменные |
.storybook/preview.ts |
Глобальные декораторы, темы (light/dark), инициализация Pinia и Router |
.storybook/stories.css |
Фоновый паттерн для canvas |
Аддоны: addon-links, addon-docs.
Глобальный setup: Pinia и Vue Router подключаются ко всем stories автоматически через setup() в preview.ts.
Темы: Доступны light и dark (по умолчанию) через Storybook Backgrounds. Декоратор в preview.ts переключает CSS-класс на <html>.
Stories располагаются рядом с компонентом (collocated), файл называется {component-name}.stories.ts.
src/
├── shared/ui/
│ └── badge-number/
│ ├── badge-number.vue
│ └── badge-number.stories.ts # title: "Shared/BadgeNumber"
├── entities/
│ └── ray/ui/
│ └── ray-page/
│ ├── ray-page.vue
│ └── ray-page.stories.ts # title: "Screens/Ray/RayPage"
├── widgets/ui/
│ └── layout-sidebar/
│ ├── layout-sidebar.vue
│ └── layout-sidebar.stories.ts # title: "Widgets/LayoutSidebar"
└── pages/
└── login/ui/
└── login-page/
├── login-page.vue
└── login-page.stories.ts # title: "Screens/Login/LoginPage"
| Слой FSD | Формат title | Пример |
|---|---|---|
shared/ui |
Shared/{ComponentName} |
"Shared/BadgeNumber" |
entities |
Screens/{Entity}/{ComponentName} или Entities/{Entity}/{ComponentName} |
"Screens/Ray/RayPage" |
widgets |
Widgets/{ComponentName} |
"Widgets/LayoutSidebar" |
pages |
Screens/{Page}/{ComponentName} |
"Screens/Login/LoginPage" |
import type { Meta, StoryObj } from '@storybook/vue3-vite'
import MyComponent from './my-component.vue'
export default {
title: 'Shared/MyComponent',
component: MyComponent
} as Meta<typeof MyComponent>
export const Default: StoryObj<typeof MyComponent> = {
args: {
label: 'Hello'
}
}export default {
title: 'Shared/BadgeNumber',
component: BadgeNumber,
render: (args) => ({
setup() {
return { args }
},
components: { BadgeNumber },
template: `
<BadgeNumber v-bind="args">
<button>Button</button>
</BadgeNumber>
`
})
} as Meta<typeof BadgeNumber>import { action } from 'storybook/actions'
export default {
title: 'Shared/PauseButton',
component: PauseButton,
argTypes: {
onToggleUpdate: action('Toggle pause'),
onDelete: action('Delete event')
}
} as Meta<typeof PauseButton>export default {
title: 'Shared/PreviewCardHeader',
component: PreviewCardHeader,
argTypes: {
eventType: {
control: { type: 'select' },
options: Object.values(EventTypes),
mapping: EventTypes
}
}
} as Meta<typeof PreviewCardHeader>export default {
title: 'Screens/Login/LoginPage',
component: LoginPage,
parameters: {
layout: 'fullscreen'
}
} as Meta<typeof LoginPage>import { useRay } from '../../lib'
import { rayCallerMock } from '../../mocks'
const { normalizeRayEvent } = useRay()
export const Default: StoryObj<typeof RayPage> = {
args: {
event: normalizeRayEvent(rayCallerMock)
}
}В main.ts автоматически сканируется директория src/shared/ui/icon-svg/icon-svg-originals и все имена иконок передаются через env-переменную STORYBOOK_ICON_SVG_NAMES. В stories иконок используется:
const iconNames = ((import.meta.env.STORYBOOK_ICON_SVG_NAMES as string) || '').split(',')Каждый экспорт из файла — отдельный вариант компонента:
export const Default: StoryObj<typeof PauseButton> = {
args: { isPaused: false, totalNewEventsCount: 10 }
}
export const Paused: StoryObj<typeof PauseButton> = {
args: { isPaused: true, totalNewEventsCount: 10 }
}
export const Disabled: StoryObj<typeof PauseButton> = {
args: { disabledPause: true, totalNewEventsCount: 0 }
}- Файл
{component-name}.stories.tsрядом с.vueфайлом компонента titleсоответствует слою FSD (см. таблицу выше)- Типизация:
Meta<typeof Component>иStoryObj<typeof Component> - Как минимум один
Defaultэкспорт - Для страниц:
parameters: { layout: "fullscreen" } - Для событий:
argTypesсaction()изstorybook/actions - Для сложных данных: использовать моки из
mocks/директории entity