30. Storybook. Налаштування Nuxt + Storybook. Frontend Workshop. Структура директорій у Nuxt
Відео версія
(YouTube - для зворотнього зв'язку)
TL;DR
Storybook корисний для великих проєктів, проєктів з A/B тестами та з багатьма темами.
Змініть структуру проєкту так, щоб увесь код розробників знаходився в окремій директорії. Це допоможе уникнути пошуку у кешах, білдах та node_modules.
pathPrefix— погана практика в іменуванні компонентів у Nuxt. Це дуже ускладнює рефакторинг.
export default defineNuxtConfig({
components: [
{
path: '~/components',
pathPrefix: false,
},
],
})
Зберігання stories та тестів поруч із компонентом не вплине на продакшн-збірку.
Зберігайте всі mock-дані в окремих
.mockфайлах, що дозволить їх перевикористовувати та оновлювати без зайвих проблем.
Storybook змусить вас писати чисті компоненти, залежні лише від аргументів (властивостей). Це гарний підхід.
Налаштування Storybook для Nuxt
npm i --save-dev storybook @nuxtjs/storybook @storybook/addon-essentials @storybook/addon-interactions @storybook/addon-links
Запускайте Storybook окремо від основної збірки, з конфігурацією для відокремлення його від основного проєкту.
{
"scripts": {
"storybook": "IS_STORYBOOK=true storybook dev --port 6006",
"build-storybook": "IS_STORYBOOK=true storybook build"
}
}
Змінюйте конфігурацію Nuxt залежно від наявності змінної IS_STORYBOOK:
export default defineNuxtConfig({
pages: !process.env.IS_STORYBOOK,
ogImage: {
enabled: !process.env.IS_STORYBOOK,
},
vue: {
runtimeCompiler: 'IS_STORYBOOK' in process.env,
},
})
pages— виправляє проблему навігації Storybook з конфігурацією Nuxt маршрутизації.ogImage— виправляє баг з SSR для пакета ogImage, деSSR = falseза замовчуванням у@nuxtjs/storybook.vue.runtimeCompiler— дозволяє рендеринг компонентів на сторінках Storybook. Вимикайте для основної збірки, якщо вона цього не потребує.
Entrypoint для запуску:
import type { StorybookConfig } from '@storybook-vue/nuxt'
import { mergeConfig } from 'vite'
const config: StorybookConfig = {
stories: [
'../app/**/*.mdx',
'../app/components/**/*.stories.ts',
],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-themes',
'@storybook/addon-a11y',
],
framework: {
name: '@storybook-vue/nuxt',
options: {},
},
docs: {},
async viteFinal(config) {
return mergeConfig(config, {
optimizeDeps: {
include: ['jsdoc-type-pratt-parser'],
},
})
},
}
export default config
stories— директорії для пошуку файлів stories.viteFinal— виправляє баг з рендерингом документації.
Файл конфігурації:
import type { Preview } from '@storybook-vue/nuxt'
import { withThemeByClassName } from '@storybook/addon-themes'
import { h, Suspense } from 'vue'
import { MINIMAL_VIEWPORTS } from '@storybook/addon-viewport'
import './stubs/i18n.stub'
import './stubs/nuxt-link.stub'
const preview: Preview = {
parameters: {
layout: 'fullscreen',
viewport: {
viewports: {
desktop: {
name: 'Desktop',
styles: {
height: '100%',
width: '100%',
overflow: 'clip',
},
type: 'desktop',
},
...MINIMAL_VIEWPORTS,
},
defaultViewport: 'desktop',
},
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
},
decorators: [
withThemeByClassName({
themes: {
light: 'light',
dark: 'dark',
},
defaultTheme: 'dark',
}),
(story) => {
return {
setup() {
return () => h(Suspense, {}, [h(story())])
},
}
},
],
}
export default preview
layout— шаблон розміщення компонентів за замовчуванням для всіх історій.viewport— налаштування доступних viewport та встановлення глобального.controls— автоматичне визначення елементів контролю для історій.withThemeByClassName— налаштування тем.(story) => ... Suspense— дозволяє очікування динамічних компонентів (наприклад, Nuxt Icon).
У файлі .storybook/preview.ts також підключайте всі глобальні директиви, компоненти та методи.
Підключення i18n для локалізації:
import { setup } from '@storybook/vue3'
import { createI18n } from 'vue-i18n'
import en from '../../app/locales/en.json'
setup((app) => {
const i18n = createI18n({
locale: 'en',
messages: { en },
})
app.use(i18n)
})
Коригування компонента NuxtLink:
import { setup } from '@storybook/vue3'
import { action } from '@storybook/addon-actions'
import { h } from 'vue'
setup((app) => {
app.component('RouterLink', {
props: ['to'],
methods: {
onClick(e) {
e.preventDefault()
action('switch-route')(this.to)
},
},
render() {
return h('a', { onClick: this.onClick, href: this.to }, this.$slots.default())
},
})
})
Також створіть stub для всіх глобальних методів.