StencilJS — отличный инструмент для создания веб-компонентов, а Storybook — для создания дизайнерских систем, но интеграция этих двух инструментов не кажется очень естественной, потому что нет единого правильного способа сделать это.
После долгих исследований я покажу вам простой способ осуществить эту интеграцию и не умереть, пытаясь ?.
Создайте проект трафарета
npm init stencil
Появится несколько вопросов. Ответьте на них следующим образом
✔ Pick a starter > component
✔ Project name > storybook-wc-stencil
После этого у вас будет трафаретный проект с базовым примером компонента внутри.
Установите зависимости
cd storybook-wc-stencil
yarn install
Игнорировать код node_modules при проверке
Добавьте свойство skipLibCheck для исключения кода node_modules
tsconfig.json
{
"compilerOptions": {
...
"skipLibCheck": true,
},
...
Создание файла типизации для импорта tsx
Наш линтер может давать нам проблемы при попытке импортировать md файлы, мы также можем использовать этот файл для другого типа расширений.
src/typings.d.ts
declare module '*.jpg';
declare module '*.md' {
const value: string; // markdown is just a string
export default value;
}
declare module '*.css' {
const content: { [className: string]: string };
export default content;
}
Добавить сюжетную книгу
npx -p @storybook/cli sb init --type html
В результате будет создан проект сборника рассказов
Добавить аддон для заметок
yarn add -D @storybook/addon-notes
.storybook/main.js
module.exports = {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-notes',
],
framework: '@storybook/html',
};
Конфигурация для загрузки компонентов Stencil в Storybook
.storybook/preview.js
import { defineCustomElements } from '../dist/esm/loader';
defineCustomElements();
export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
};
Очистка историй
Давайте удалим все содержимое из каталога stories
Структура проекта
Создайте новый файл my-component.stories.tsx в каталоге src/stories.
Структура вашего проекта должна выглядеть следующим образом
./storybook-wc-stencil/
|
|---- .storybook/
| |---- main.js
| |---- preview.js
|---- src/
| |---- components/
| | |---- my-component/
| | |---- my-component.css
| | |---- my-component.e2e.ts
| | |---- my-component.spec.ts
| | |---- my-component.tsx
| | |---- readme.md
| |---- stories/
| | |---- components/
| | |---- my-component.stories.tsx
| |---- typings.d.ts
|---- .editorconfig
|---- .gitignore
|---- .prettierrc.json
|---- LICENSE
|---- package.json
|---- readme.md
|---- stencil.config.ts
|---- tsconfig.json
|---- yarn.lock
Генерация компонентов
Мы можем использовать следующую команду для автоматической генерации наших компонентов в каталоге компонентов
yarn generate component-name
Запустить проект
Для горячей перезагрузки мы должны выполнить эти две команды параллельно, поэтому мы можем использовать два терминала или создать новый скрипт
yarn build -- --watch
yarn storybook
Первая команда сгенерирует сборку наших компонентов, мы используем флаг --watch
, чтобы всегда генерировать эту сборку при любом изменении.
Структура кода Story
Мы столкнемся с некоторыми недостатками при работе с storybook с помощью трафарета
- Нам нужно определить свойства, которые мы хотим использовать в элементах управления
- Нам нужно определить реквизиты по умолчанию для элементов управления
- Нам нужно добавить описание и типы реквизитов для страниц Docs
- Свойство defaultValue не работает для страниц документов
- Нам нужно передать значения args в шаблоне
// This md file is generated by stencil, and we are going to use it as a note page
import notes from '../../components/my-component/readme.md';
export default {
title: 'UI/My Component',
args: {
// Here we define default values that we want to show on controls
// Also, only props defined here are going to be shown
first: 'Juan Fernando',
middle: 'Gómez',
last: 'Maldonado',
},
argTypes: {
// Here we can add description and prop value type
first: {
description: 'First name',
// First way to define type
table: {
type: {
summary: 'string',
},
},
},
middle: {
// Second and shorter way to define type
type: {
summary: 'string',
},
},
last: {
// We can disable the property
// This will hide it in controls and Doc page
table: {
disable: true,
},
},
},
parameters: {
// This will create a note page for our story component
notes,
},
};
const Template = args =>
`<my-component first="${args.first}" middle="${args.middle}" last="${args.last}"></my-component>`;
export const Basic = Template.bind({});
export const Another = Template.bind({});
Another.args = {
first: 'John',
};
Более чистый способ добавления значений и описания для историй
Чтобы избежать шаблонного кода, я создал простую библиотеку, которая возвращает args, argTypes и пользовательский шаблон для нашего компонента. Эта библиотека — story-wc-generator.
yarn add story-wc-generator
import notes from '../../components/cool-button/readme.md';
import storyGenerator from 'story-wc-generator';
const { args, argTypes, Template } = storyGenerator('cool-button', {
text: { value: 'Click me!', description: 'Text label', type: 'string' },
color: {
value: 'primary',
description: 'Color of button',
control: 'select',
options: ['primary', 'secondary', 'dark'],
type: 'primary | secondary | dark',
},
});
export default {
title: 'UI/Cool Button',
args,
argTypes,
parameters: {
notes,
},
};
export const Primary = Template.bind({});
export const Secondary = Template.bind({});
Secondary.args = {
color: 'secondary',
};
...
Этот пример включает все свойства, которые могут быть использованы, но вы можете обратиться к
документацию для получения дополнительной информации о библиотеке
После создания наших компонентов и историй мы должны получить результат, подобный этому
Вы можете посмотреть репозиторий здесь, а живой проект здесь