Чи знайомі ви з концепцією розміщення всіх компонентів інтерфейсу в React?
Можливо, якщо ви тільки починаєте свій шлях у світі React, відповідь буде “ні”.
Але що саме це означає?
Зверніть увагу на приклади, які демонструє react-beautiful-dnd.
Те, що ви бачите в цих прикладах, називається історіями. Інструмент, який використовують для їх створення, відомий як Storybook.
Думаю, тепер ви розумієте, про що піде мова в цій статті. Отож, не гаймо часу та почнемо дослідження.
Що таке Storybook?
Storybook – це ізольоване середовище розробки інтерфейсу користувача, яке служить майданчиком для ваших компонентів. Завдяки ньому ви можете експериментувати з різними станами та можливостями компонентів, не запускаючи основний додаток. Storybook можна запустити на власному порту через відповідну конфігурацію.
Storybook не обмежується лише React. Він може працювати з більшістю популярних фреймворків, таких як Vue, Angular, Mithril, Marko, Svelte та інші.
Детальніше про можливості Storybook ви можете дізнатися тут.
Що таке історія в Storybook?
Історія визначає конкретний відтворений стан вашого компонента. Наприклад, типовий компонент можна використовувати по-різному, залежно від переданих props. Для кожного з цих станів можна створити окрему історію.
Розглянемо компонент Button.
Button може мати різні стани, такі як “вимкнено”, “завантаження”, “основний”, “вторинний”, “маленький”, “великий”, “середній” тощо. Якщо ми захочемо перелічити всі можливі стани, це ускладнить роботу з посібником користувача. Гадаю, ви розумієте. Але ви краще все зрозумієте, коли почнете працювати з Storybook на практиці.
У Storybook ви можете побачити різні варіанти кнопки (великий, середній, малий).
Налаштування Storybook у вашому проекті
Ми встановимо Storybook у проект React.
Почнемо:
- Створіть проект React за допомогою наступної команди. Назву проекту ви можете обрати на свій розсуд.
npx create-react-app storybook-demo
- Тепер встановіть Storybook у ваш проект за допомогою наступної команди.
npx sb init
На цьому налаштування Storybook завершено.
Storybook надає нам окремий сервер.
Як його запустити?
Storybook автоматично додає команду до нашого файлу сценаріїв. Ви можете перевірити це у файлі `package.json` в розділі `scripts`. Для запуску сервера Storybook виконайте наступну команду:
npm run storybook
Storybook запуститься на новому сервері, використовуючи порт, вказаний у розділі `scripts` файлу `package.json`. Storybook автоматично відкриється у вашому браузері за замовчуванням (аналогічно серверу React).
За замовчуванням ви побачите різні історії. Ви можете видалити їх, якщо не хочете, або залишити як приклад. Як ми обговорювали раніше, кнопка може мати декілька станів, і ви побачите їх у Storybook (хоча не всі можливі стани). У заключному розділі цього посібника ми напишемо велику кількість історій для кнопки.
Вивчіть різні розділи Storybook і ознайомтеся з його різними можливостями. У цьому посібнику ми розглянемо деякі з них.
А зараз давайте напишемо нашу першу історію.
Тестування Storybook
Ми вже бачили, як працює Storybook, та ознайомилися з деякими його прикладами.
- Створіть папку під назвою `Button` у папці `src`.
- Створіть файли `Button.jsx`, `Button.css` та `constants.js`.
- Скопіюйте код з наведених нижче фрагментів у відповідні файли.
Button.jsx
import React, { Component } from "react"; import PropTypes from "prop-types"; import "./Button.css"; import { buttonTypes, buttonVariants, buttonSizes } from "./constants"; class Button extends Component { static defaultProps = { isDisabled: false, type: "filled", variant: "oval", size: "medium", backgroundColor: "#1ea7fd", textColor: "#ffffff", }; static buttonTypes = buttonTypes; static buttonVariants = buttonVariants; static buttonSizes = buttonSizes; renderButton = () => { const { text, isDisabled, type, variant, size, backgroundColor, textColor, onClick, } = this.props; return ( <button onClick={onClick} className={`default ${variant} ${size} ${ isDisabled ? "disabled" : "" }`} style={ type === buttonTypes.outline ? { border: `1px solid ${backgroundColor}`, color: "#000000", backgroundColor: "transparent", } : { backgroundColor: `${backgroundColor}`, border: `1px solid ${backgroundColor}`, color: textColor, } } disabled={isDisabled} > {text} </button> ); }; render() { return this.renderButton(); } } Button.propTypes = { text: PropTypes.string, isDisabled: PropTypes.bool, type: PropTypes.oneOf([buttonTypes.outline, buttonTypes.filled]), variant: PropTypes.oneOf([buttonVariants.oval, buttonVariants.rectangular]), size: PropTypes.oneOf([ buttonSizes.small, buttonSizes.medium, buttonSizes.large, ]), backgroundColor: PropTypes.string, textColor: PropTypes.string, onClick: PropTypes.func, }; export { Button };
Button.css
.default { border: none; cursor: pointer; background-color: transparent; } .default:focus { outline: none; } .disabled { opacity: 0.75; cursor: not-allowed; } .small { font-size: 12px; padding: 4px 8px; } .medium { font-size: 14px; padding: 8px 12px; } .large { font-size: 16px; padding: 12px 16px; } .oval { border-radius: 4px; } .rectangular { border-radius: 0; }
constants.js
export const buttonTypes = { outline: "outline", filled: "filled", }; export const buttonVariants = { oval: "oval", rectangular: "rectangular", }; export const buttonSizes = { small: "small", medium: "medium", large: "large", };
Що робить цей код?
Ми створили універсальний компонент Button, який може бути використаний різними способами. Тепер наш компонент може мати різні стани.
Давайте напишемо нашу першу історію, дотримуючись наведених нижче кроків.
- Створіть файл під назвою `Button.stories.jsx`
- Імпортуйте React та наш компонент Button до файлу.
- Тепер визначимо заголовок або шлях до історій наших компонентів. Це можна зробити за допомогою наступного коду.
export default { title: ‘common/Button’, }
Цей код розмістить усі історії з поточного файлу в каталог `common/Button/`.
- Експортуйте кнопку з необхідними атрибутами, як показано нижче.
export const defaultButton = () => ( <Button text=”Default Button” onClick={() => {}} /> );
Ми завершили нашу першу історію. Запустіть Storybook за допомогою наступної команди та подивіться на результат.
npm run storybook
Не хвилюйтеся, ми ще напишемо багато історій.
Яку користь це приносить розробці Frontend?
Яка основна перевага використання Storybook?
Уявімо, що ми працюємо в команді з 10 осіб. Нам потрібно перевірити спільні компоненти, які були написані кожним учасником команди для поточного робочого проекту.
Як ми можемо це зробити?
Нам потрібно перевірити кожен спільний компонент, що забирає багато часу та не є оптимальним рішенням. Тут на допомогу приходить Storybook.
Як Storybook може вирішити цю проблему?
За допомогою Storybook ми можемо писати історії для спільних компонентів (або будь-яких компонентів інтерфейсу користувача). Коли хтось із членів вашої команди захоче перевірити компоненти, створені іншими, він просто запускає сервер Storybook і бачить усі компоненти інтерфейсу, як ми бачили раніше.
За допомогою Storybook можна робити набагато більше, ніж просто переглядати компоненти. Він має концепцію Addons, які надають неймовірні можливості історіям.
Наприклад, якщо нам потрібно перевірити, як компоненти інтерфейсу користувача реагують на різних пристроях, ми можемо скористатися аддоном Viewport у Storybook. Ми дізнаємося більше про аддони в наступних розділах.
Працюємо зі Storybook
У цьому розділі ми напишемо різні історії, які визначають різні стани нашого спільного компонента Button.
Створення історій – це не складно. Історія визначає стан компонента. Якщо ви знаєте атрибути компонента, вам буде легко зрозуміти його різні випадки використання.
Давайте напишемо кілька історій, передаючи різні необов’язкові реквізити.
export const largeButton = () => ( <Button text="Large Button" onClick={() => {}} size="large" /> ); export const outlineSmallButton = () => ( <Button text="Outline Small Button" onClick={() => {}} size="small" type="outline" /> ); export const rectangularLargeButton = () => ( <Button text="Rectangular Large Button" onClick={() => {}} size="large" variant="rectangular" /> ); export const disabledButton = () => ( <Button text="Disabled Button" onClick={() => {}} isDisabled={true} /> ); export const warningButton = () => ( <Button text="Warning Button" onClick={() => {}} backgroundColor="orange" /> );
Ці три історії демонструють різні варіанти використання нашого компонента Button. Тепер ваша черга додати власні варіанти історій для нашого компонента. Спробуйте додати `disabledSmallRectangularButton`, `dangerButton`, `successDisabledButton` тощо.
Я не буду надавати код для цих випадків. Спробуйте написати його самостійно, щоб краще зрозуміти матеріал. Нижче наведено повний код історій, які ми написали на даний момент.
import React from "react"; import { Button } from "./Button"; export default { title: "src/common/Button", }; export const defaultButton = () => ( <Button text="Default Button" onClick={() => {}} /> ); export const largeButton = () => ( <Button text="Large Button" onClick={() => {}} size="large" /> ); export const outlineSmallButton = () => ( <Button text="Outline Small Button" onClick={() => {}} size="small" type="outline" /> ); export const rectangularLargeButton = () => ( <Button text="Rectangular Large Button" onClick={() => {}} size="large" variant="rectangular" /> ); export const disabledButton = () => ( <Button text="Disabled Button" onClick={() => {}} isDisabled={true} /> ); export const warningButton = () => ( <Button text="Disabled Button" onClick={() => {}} backgroundColor="orange" /> );
Тепер ви добре розумієте, як створювати історії для компонентів.
Перейдемо до наступного розділу, де ми познайомимося з аддонами та їхньою роллю в покращенні історій.
Аддони для Storybook
За замовчуванням нам буде доступно декілька аддонів. У цьому розділі ми розглянемо найкорисніші з них.
Давайте покращимо наші історії для Button.
Елементи керування
Елементи керування дозволяють змінювати props компонента безпосередньо в Storybook. Для нашого компонента Button ми можемо додати елементи керування для зміни різних атрибутів.
Наприклад, нам потрібно підібрати найкращий колір для фону кнопки. Це займе багато часу, якщо ми будемо перевіряти кожен колір вручну, змінюючи його в коді. Замість цього, ми можемо додати елемент керування, який дозволить нам вибирати колір прямо в Storybook. Таким чином ми можемо протестувати різні кольори фону без потреби перезапускати код.
Давайте подивимося, як додати елементи керування до наших історій для Button.
По-перше, ми повинні визначити всі атрибути під заголовком, як показано нижче.
export default { title: "src/common/Button", argTypes: { text: { control: "text" }, backgroundColor: { control: "color" }, isDisabled: { control: "boolean" }, size: { control: { type: "select", options: ["small", "medium", "large"] }, }, type: { control: { type: "select", options: ["filled", "outline"] }, }, variant: { control: { type: "select", options: ["oval", "rectangular"] }, }, }, };
Далі розділіть властивості від компонента та передайте їх як аргументи, як показано нижче.
export const outlineSmallButton = (args) => ( <Button {...args} onClick={() => {}} /> ); outlineSmallButton.args = { text: "Outline Small Button", size: "small", type: "outline", };
Тепер ви можете побачити елементи керування в нижній частині вікна попереднього перегляду компонентів.
На вкладці “Елементи керування” в нижній частині вікна попереднього перегляду компонентів ви можете перевірити їхню роботу. Поекспериментуйте з ними.
Оновіть усі історії, як зазначено вище. Це схоже на ознайомлення із синтаксисом аддонів Storybook. У `argTypes` ми використовували різні типи елементів керування. Ви можете знайти всі доступні елементи керування в Storybook тут.
Оновлені історії для Button виглядатимуть так:
import React from "react"; import { Button } from "./Button"; export default { title: "src/common/Button", argTypes: { text: { control: "text" }, backgroundColor: { control: "color" }, isDisabled: { control: "boolean" }, size: { control: { type: "select", options: ["small", "medium", "large"] }, }, type: { control: { type: "select", options: ["filled", "outline"] }, }, variant: { control: { type: "select", options: ["oval", "rectangular"] }, }, }, }; export const defaultButton = (args) => <Button {...args} onClick={() => {}} />; defaultButton.args = { text: "Default Button", }; export const largeButton = (args) => ( <Button {...args} onClick={() => {}} size="large" /> ); largeButton.args = { text: "Large Button", }; export const outlineSmallButton = (args) => ( <Button {...args} onClick={() => {}} /> ); outlineSmallButton.args = { text: "Outline Small Button", size: "small", type: "outline", }; export const rectangularLargeButton = (args) => ( <Button {...args} onClick={() => {}} /> ); rectangularLargeButton.args = { text: "Rectangular Large Button", size: "large", variant: "rectangular", }; export const disabledButton = (args) => <Button {...args} onClick={() => {}} />; disabledButton.args = { text: "Disabled Button", isDisabled: true, }; export const warningButton = (args) => <Button {...args} onClick={() => {}} />; warningButton.args = { text: "Warning Button", backgroundColor: "orange", };
Дії
Дії – це події в JavaScript. Коли ми натискаємо кнопку, це подія JavaScript. Аддон Дії дозволяє нам відстежувати дії, що відбуваються при взаємодії з компонентами.
За допомогою дій ми можемо перевірити, чи події працюють належним чином. Наприклад, вимкнену кнопку не можна натискати, а ввімкнену кнопку можна. За допомогою дій ми можемо в цьому переконатися.
Давайте подивимося, як додати дію до натискання кнопки.
- Імпортуйте дію з аддону Storybook за допомогою наступного оператора.
import { action } from "@storybook/addon-actions";
- Замініть всі `() => {}` наступним оператором.
action("Button is clicked!")
Тепер перейдіть до Storybook та натисніть кнопку. Ви побачите повідомлення під вкладкою “Дії” поруч із вкладкою “Елементи керування”. Повідомлення не з’явиться, якщо ви натиснете на вимкнену кнопку.
Ми можемо використовувати дії для різних подій, таких як `onChange`, `onMouseOver`, `onMouseOut` тощо, щоб переконатися, що вони працюють належним чином. Спробуйте реалізувати те ж саме для `onChange` для елемента введення.
Докладніше про дії можна дізнатися в документації тут.
Фон
За допомогою аддону “Фон” можна змінити фон вікна перегляду. Для цього не потрібно писати код. Ви можете змінити його прямо в Storybook. Перегляньте GIF-зображення нижче.
Видове вікно
За допомогою аддону “Видове вікно” ми можемо перевірити, як наші компоненти відображаються на різних пристроях у Storybook. Перегляньте зображення нижче.
Документи
Ми можемо задокументувати наші компоненти в Storybook за допомогою аддона “Документи”. Це особливо корисно, коли ми працюємо в команді. Члени команди можуть ознайомитися з компонентами та зрозуміти їхню роботу безпосередньо, що економить багато часу для розробників.
У вікні попереднього перегляду компонентів Storybook можна побачити вкладку “Документи” у верхньому правому куті. Вона міститиме всю документацію для всіх історій компонента. Якщо ми хочемо задокументувати компонент з розміткою та відтворенням компонентів, нам потрібно використовувати файл `Button.stories.mdx`. Ми просто пишемо додатковий код розмітки всередині нього разом з історіями компонентів.
Давайте напишемо документ для наших історій. Код включає розмітку та рендеринг компонентів. Це просто вивчення синтаксису. Ви зрозумієте це з першого погляду.
Нижче наведено код документа `Button.stories.mdx`:
<!--- Button.stories.mdx --> import { Meta, Story, Preview, ArgsTable } from '@storybook/addon-docs/blocks'; import { Button } from './Button'; <Meta title="MDX/Button" component={Button} /> # Button Documentation With `MDX` we can define a story for `Button` right in the middle of our Markdown documentation. <ArgsTable of={Button} /> export const Template = (args) => <Button {...args} /> ## Default Button We can write the documentation related to the Default Button <Preview> <Story name="Default Button" args={{ text: 'Default Button' }}> {Template.bind({})} </Story> </Preview> ## Large Button We are writing sample docs for two stories, you can write rest of them <Preview> <Story name="Large Button" args={{ text: "Large Button", }}> {Template.bind({})} </Story> </Preview>
Дізнайтеся більше про компоненти документування тут.
Ви можете дізнатися більше про аддони тут.
Висновок
Сподіваюся, вам сподобався цей урок і ви дізналися про Storybook. Ефективно використовуйте його у своїй команді, щоб зробити вашу роботу продуктивнішою.
Ви новачок у React? Перегляньте ці навчальні ресурси.
Щасливого кодування 🙂