Часто я сталкиваюсь с проблемой, что хочу начать разработку пользовательского интерфейса снизу вверх: начать с проектирования кнопок, входов и других небольших компонентов и только после этого создавать страницы и устанавливать маршрутизацию.
Эта проблема решается с помощью… Storybook!
Storybook — это инструмент, который позволяет разрабатывать компоненты изолированно.
Согласно официальному веб-сайту Storybook:
Storybook — это инструмент с открытым исходным кодом для создания изолированных компонентов и страниц пользовательского интерфейса. Он упрощает разработку пользовательского интерфейса, тестирование и документирование.
Давайте рассмотрим основные функциональные возможности Storybook на простом примере компонента Button.
В этом примере мы добавим Storybook в React App, но вот список библиотек/фреймворков, с помощью которых вы можете реализовать Storybook: React, Vue, Angular, Web Components, Ember, HTML, Svelte, Preact.
Прежде всего, давайте установим Storybook.
npx create-react-app my-app
cd my-app
npx sb init
Typescript также поддерживается и не требует дополнительной настройки. Не забудьте установить типы.
npm i @storybook/react
Storybook использует Webpack5, и если у вас установлена предыдущая версия, запустите ее:
npx sb@next automigrate
Для запуска приложения используйте:
npm run storybook
Браузер должен открыть localhost на порту 6006, и вы увидите следующее. Если вы хотите изменить порт, вы можете сделать это в файле package.json.
В больших проектах вы также можете добавлять папки в секции для удобства. Вы можете сделать это позже внутри ваших историй.
Текущая часть кода в файле Button.stories.js
export default {
title: 'Example/Button',
...
};
Схема, как вы можете разделить ее на секции и папки:
export default {
title: 'NameOfSection/NameOfFolder/NameOfComponent',
...
};
После загрузки каждого пакета у нас есть обновления: мы видим папку .storybook внутри основной папки и папку screens внутри папки src с Button.js, Button.stories.js и другими файлами.
Я предпочитаю логически разделять код на отдельные файлы, поэтому я удалил все в папке src и создал отдельные папки для компонентов и историй.
Давайте посмотрим, что у нас есть в Button.js
import PropTypes from "prop-types";
function Button({
label,
backgroundColor = "red",
color = "black",
size = "md",
handleClick,
}) {
let scale = 1;
if (size === "sm") scale = 0.5;
if (size === "lg") scale = 2;
const style = {
backgroundColor,
padding: `${scale * 0.5}rem ${scale * 1}rem`,
border: "2px solid black",
borderRadius: "25px",
color,
};
return (
<button onClick={handleClick} style={style}>
{label}
</button>
);
}
Button.propTypes = {
label: PropTypes.string,
backgroundColor: PropTypes.string,
size: PropTypes.oneOf(["sm", "md", "lg"]),
color: PropTypes.oneOf(["blue", "white", "black"]),
handleClick: PropTypes.func,
};
export default Button;
Функция Button по умолчанию принимает параметры, а внутри объекта style мы добавляем CSS и возвращаем компонент.
После этого мы определяем тип реквизита компонента, добавляя или тип реквизита с или без опций для выбора.
Давайте рассмотрим Button.stories.js
import Button from "../components/Button";
export default {
title: "Components/Controls/Button",
component: Button,
argTypes: {
handleClick: { action: "logging it in action section" },
}
};
const Template = (args) => <Button {...args} />;
export const Green = Template.bind({});
Green.args = {
backgroundColor: "green",
label: "Middle Green button",
size: "md",
color: "black",
};
export const Pink = Template.bind({});
Pink.args = {
backgroundColor: "pink",
label: "Middle Pink Button",
color: "blue",
size: "md",
};
export const Small = Template.bind({});
Small.args = {
backgroundColor: "blue",
label: "Small button",
size: "sm",
color: "white",
};
export const Large = Template.bind({});
Large.args = {
label: "Very very very large button",
size: "lg",
color: "black",
backgroundColor: "red",
};
Для активации действий мы добавляем argTypes и определяем функцию handleClick, которая регистрирует события выхода.
Давайте проверим, что произошло с нашими историями на локальном хосте.
Вручную вы можете изменить цвет фона, надпись, размер и цвет, а также нажать на кнопку и получить действие.
Я нажал на иконку отмены и получил этот компонент в начальном состоянии.
Также с помощью декоратора мы можем добавить стилизацию истории.
Например, давайте добавим поля к истории с помощью декораторов в соответствии со схемой
Component.stories.js
export default {
…..
decorators: [
(Story) => (
<div style={{ margin: '5em' }}>
<Story />
</div>
),
],
};
Button.stories.js
import Button from "../components/Button";
export default {
title: "Components/Controls/Button",
component: Button,
argTypes: {
handleClick: { action: "logging it in action section" },
},
decorators: [
(Story) => (
<div style={{ margin: "5rem" }}>
<Story />
</div>
),
],
};
const Template = (args) => <Button {...args} />;
export const Green = Template.bind({});
Green.args = {
backgroundColor: "green",
label: "Middle Green button",
size: "md",
color: "black",
};
export const Pink = Template.bind({});
Pink.args = {
backgroundColor: "pink",
label: "Middle Pink Button",
color: "blue",
size: "md",
};
export const Small = Template.bind({});
Small.args = {
backgroundColor: "blue",
label: "Small button",
size: "sm",
color: "white",
};
export const Large = Template.bind({});
Large.args = {
label: "Very very very large button",
size: "lg",
color: "black",
backgroundColor: "red",
};
Проверим в браузере, изменились ли у нас поля.
Да, все работает 🙂
Это только начало, и вы можете продолжить изучение таких продвинутых тем Storybook, как Storybook Addons и тестирование.
Продолжайте наслаждаться разработкой пользовательского интерфейса с помощью официальной документации Storybook.