Как создать библиотеку компонентов react с помощью Storybook, TypeScript, SCSS и Rollup?

Введение

Прекрасная вещь в react — это то, что он позволяет вам разбивать пользовательский интерфейс на более мелкие компоненты и повторно использовать их в разных местах вашего проекта, но это еще не все, вы можете стандартизировать ваши компоненты и опубликовать их на npm или yarn и позволить другим людям использовать ваши компоненты в своих проектах.

В этом посте я покажу вам, как можно создать библиотеку компонентов для reactJS, используя Typescript, Storybook, SCSS и Rollup.

Не обязательно создавать большую библиотеку, вы можете создать библиотеку для одного компонента, например, Date Picker, или создать полноценную библиотеку, например, Material UI.

Библиотека компонентов пользовательского интерфейса очень важна для организаций, чтобы поддерживать согласованный пользовательский интерфейс в разных проектах. Однако вам не нужно создавать библиотеку компонентов только для одного проекта, поскольку все компоненты можно поддерживать в этом конкретном проекте, но если вы работаете над несколькими побочными проектами и у вас есть похожие компоненты, которые вы копируете-вставляете из одного проекта в другой, то лучше создать библиотеку для этих компонентов.

Итак, давайте приступим к созданию библиотеки

Настройка библиотеки

Начало работы

Перед началом этого проекта убедитесь, что в вашей системе глобально установлены node js и npm. Если нет, вы можете скачать node отсюда

Создайте папку и назовите ее как угодно, но для данной статьи назовем эту папку react-lib, а если вы планируете опубликовать эту библиотеку на npm, то проверьте, доступно ли имя пакета в реестре npm.

Откройте Терминал, перейдите в каталог проекта и выполните команду

npm init -y
Войти в полноэкранный режим Выйти из полноэкранного режима

Это инициализирует проект node в текущем каталоге. Флаг -y при передаче команде NPM указывает генератору использовать значения по умолчанию вместо того, чтобы задавать вопросы, и файл package.json создается в корне вашей директории

React & React DOM

Давайте установим react и react-dom в качестве dev-зависимостей.

 npm i -D react react-dom
Войдите в полноэкранный режим Выход из полноэкранного режима

Мы создаем эту библиотеку для reactJS, поэтому требуется, чтобы проект, использующий эту библиотеку, имел react и react-dom в качестве зависимостей, поэтому давайте добавим react и react-dom в качестве зависимостей peer в наш файл package.json.

Основная структура папок

Прежде чем добавить storybook и typescript и двигаться дальше, давайте создадим базовую структуру папок для нашего проекта. Вы можете использовать любую структуру папок, которая вам больше нравится.

Установка typescript

Сначала установите typescript глобально в вашей системе с помощью следующей команды

npm i typescript -g
Войти в полноэкранный режим Выйти из полноэкранного режима

Затем установите typescript и @types/react в качестве dev-зависимости в ваш проект.

npm i -D typescript @types/react
Войдите в полноэкранный режим Выйдите из полноэкранного режима

Затем создайте файл tsconfig.json с помощью следующей команды.

tsc --init
Вход в полноэкранный режим Выйти из полноэкранного режима

Этот файл позволит вам настроить дальнейшую конфигурацию и настроить взаимодействие Typescript и компилятора tsc.

Откройте tsconfig.json и измените следующую конфигурацию.

...
"compilerOptions" : {
    ...
    "jsx": "react",
    ...
    "module": "es6",
    ...
    "moduleResolution": "node",
    ....
    "outDir": "./dist",
    ....
  }
....
Войти в полноэкранный режим Выйти из полноэкранного режима

Получение Storybook

Storybook — это инструмент с открытым исходным кодом для создания изолированных компонентов пользовательского интерфейса и страниц. Он упрощает разработку пользовательского интерфейса, тестирование и документирование. Он работает с библиотекой javascript, такой как React, Vue, Angular и т.д.

Чтобы установить storybook в нашу библиотеку, выполните эту команду:

npx sb init
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь вы должны иметь возможность запустить Storybook локально, выполнив команду npm run storybook или, если хотите, yarn storybook.

Вот предварительный просмотр приложения Storybook:

Разбираемся в структуре папок

npx sb init генерирует некоторые файлы и папки, давайте посмотрим на них.

Папка stories.

Storybook достаточно умен и определяет, что проект использует Typescript и React, поэтому он создает некоторые примеры компонентов и страницы документации внутри папки stories в src. Вы можете удалить все из этой папки, но я рекомендую заглянуть внутрь, чтобы немного разобраться.

  • Introduction.stories.mdx содержит документацию, используемую для создания страницы введения в приведенном выше превью сборника рассказов. Файлы с расширением .mdx написаны с использованием MDX, который является слиянием JSX и Markdown. Это помогает писать истории компонентов вместе с их документацией в одном месте.

  • Все файлы типа <ComponentName>.tsx являются компонентами react, созданными с помощью typescript, а файлы типа <ComponentName>.stories.tsx используются для предварительного просмотра историй в сборнике историй и помогают нам разрабатывать компонент в изоляции.

Папка .storybook.

Содержит файлы для настройки Storybook:

"stories": [
   "../src/**/*.stories.mdx",
   "../src/**/*.stories.@(js|jsx|ts|tsx)"
 ]
Вход в полноэкранный режим Выход из полноэкранного режима

Аддоны для приложения Storybook также определяются в main.js..

"addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials"
  ]
Вход в полноэкранный режим Выход из полноэкранного режима
export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  },
}

Войти в полноэкранный режим Выход из полноэкранного режима
Файл package.json

Команда npx sb init добавляет все dev-зависимости, необходимые сборнику рассказов, а также добавляет следующие скрипты в файл package.json


  "scripts": {
    "storybook": "start-storybook -p 6006",
    "build-storybook": "build-storybook"
  }

Вход в полноэкранный режим Выйти из полноэкранного режима

Добавление поддержки SASS

Storybook использует webpack для пакетирования всего кода, когда мы запускаем скрипт storybook или build-storybook.
По умолчанию Storybook не поставляется с поддержкой sass. Чтобы добавить sass в нашу библиотеку, нам нужно добавить некоторые пакеты и расширить конфигурацию webpack в .stoybook/main.js.

Добавьте следующие зависимости dev для добавления поддержки sass

npm i -D sass style-loader css-loader sass-loader@10.2.0
Вход в полноэкранный режим Выйти из полноэкранного режима

Давайте разберемся в этих зависимостях

Для расширения конфигурации webpack добавим следующий код в .storybook/main.js

const path = require("path");

Войти в полноэкранный режим Выход из полноэкранного режима
.....
addons: ["@storybook/addon-links", "@storybook/addon-essentials"],
framework: "@storybook/react",
webpackFinal: async (config, { configType }) => {
    // Make whatever fine-grained changes you need
    config.module.rules.push({
      test: /.scss$/,
      use: ["style-loader", "css-loader", "sass-loader"],
      include: path.resolve(__dirname, "../"),
    });

    // Return the altered config
    return config;
  }
....
Войти в полноэкранный режим Выйти из полноэкранного режима

Добавив поддержку Sass, мы готовы к созданию наших компонентов.

Создание нашего первого компонента

Создание компонента кнопки

Вы можете создать любой компонент, который вам необходим, но для данного поста давайте создадим компонент Button, который можно будет использовать повторно.

Внутри папки components создайте файл Button.tsx .

Сначала мы определим интерфейс реквизитов, которые необходимы кнопке.
Мы начнем с импорта react внутри нашего файла, а наш реквизит кнопки расширяет элемент кнопки HTML, поскольку мы можем передавать различные реквизиты, такие как onClick или type, которые являются родными для элемента кнопки HTML.

import React from "react";

export interface ButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
  children: React.ReactNode;
  variant: "primary" | "danger"; 
  shape?: "rounded";
}
Вход в полноэкранный режим Выход из полноэкранного режима

Мы получаем следующие реквизиты

  • у нас также есть необязательный реквизит shape.

Теперь давайте добавим наш компонент

export const Button: React.FC<ButtonProps> = ({
  children,
  variant,
  shape,
  ...props
}) => {
  const classNames = `btn btn-${variant} btn-${shape}`;
  return (
    <button className={classNames} {...props}>
      {children}
    </button>
  );
};

Войти в полноэкранный режим Выход из полноэкранного режима

Здесь мы возвращаем HTML элемент кнопки из нашей функции, и мы будем использовать реквизиты variant и shape для создания различных имен классов и добавим их в наш элемент <button>, и мы можем нацелить эти определенные классы из нашего SCSS для придания различных стилей нашему компоненту.
Например, btn является базовым классом, а когда мы передаем variant, то у нас будет класс либо btn-primary, либо btn-danger в зависимости от переданного варианта, и мы можем добавить стили для различных вариантов.

Добавление стилей для компонента Button

Создайте файл global.scss в папке scss. Вы можете выбрать структуру папок по своему усмотрению и поместить SCSS код в разные файлы и импортировать их в этот файл global.scss, но для простоты давайте добавим все наши стили в файл global.scss.

/* base styling for our button */
.btn {
  padding: 0.6rem 1rem;
  background: transparent;
  border: 1px solid #1e1e1e;
  cursor: pointer;
}

/* styling for our variants */
.btn-primary {
  border: none;
  background-color: blue;
  color: white;
  &:hover {
    background-color: blue;
  }
}
.btn-danger {
  border: none;
  background-color: red;
  color: white;
  &:hover {
    background-color: red;
  }
}

/* styling for different shape*/
.btn-rounded {
  border-radius: 0.4rem;
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Либо у кнопки будет класс btn-primary, либо btn-danger вместе с другими классами, и соответствующие стили вступят в силу.

Теперь мы создали нашу кнопку и добавили некоторые стили, давайте воспользуемся этой кнопкой и посмотрим, как она выглядит в нашей книге историй.

Создание истории кнопки в storybook

Прежде чем создавать историю для нашей кнопки, давайте импортируем наш SCSS, чтобы его можно было использовать. Откройте файл preview.js в папке .storybook и импортируйте в него файл global.scss.

Импортировав стили в сборник рассказов, давайте создадим рассказ о кнопке.
Откройте папку stories и удалите все файлы, которые были автоматически сгенерированы сборником историй для нас внутри этой папки, создайте файл Button.stories.tsx и напишите следующий код

import React from "react";
import { Story } from "@storybook/react";
import { Button, ButtonProps } from "../components/Button";


export default {
  title: "Button",
  component: Button,
};

const Template: Story<ButtonProps> = args => <Button {...args} />;

export const Primary = Template.bind({});
Primary.args = {
  children: "Primary",
  variant: "primary",
};

export const Danger = Template.bind({});
Danger.args = {
  children: "Danger",
  variant: "danger",
  shape: "rounded",
};

Вход в полноэкранный режим Выйти из полноэкранного режима

Давайте разберемся в этом коде

Мы импортируем наши Button и ButtonProps из файла Button.stories.tsx и начинаем с экспорта объекта по умолчанию, который содержит некоторые метаданные об этой истории, такие как title и component. title — это фактическое название этой истории, а внутри компонента у нас есть компонент Button.

Затем мы создаем функцию Template, которая принимает некоторые аргументы и возвращает наш компонент кнопки.

const Template: Story<ButtonProps> = args => <Button {...args} />;
Вход в полноэкранный режим Выход из полноэкранного режима

Теперь вызовите Template.bind({}) для создания клона нашего шаблона кнопки. Этот Template.bind({}) вернет функцию, которую мы можем сохранить в переменной и экспортировать ее. Вы можете прочитать больше о .bind() здесь

export const Primary = Template.bind({});
Вход в полноэкранный режим Выход из полноэкранного режима

Теперь зададим некоторые args для нашей основной кнопки

Primary.args = {
  children: "Primary",
  variant: "primary",
};
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь выполните команду npm run storybook для запуска сервера разработки, и вы увидите компоненты кнопки

В dev-сервере storybook у нас есть секция управления, которую можно использовать для изменения реквизитов и немедленно увидеть изменения, или мы можем написать больше версий или вариантов нашей кнопки, используя Template.bind({}).

Таким образом, мы можем разрабатывать и тестировать несколько компонентов изолированно, используя storybook.

Комплектация с помощью Rollup

Rollup — это хороший инструмент пакетирования, если мы хотим упаковать библиотеку компонентов React и повторно использовать ее в других проектах.

Rollup нужна точка входа для создания пакета. Мы уже создали файл index.ts в папке src, который будет служить нашей точкой входа для Rollup.

Добавьте в этот файл index.ts экспорт компонентов, которые будут использоваться другими, а также импортируйте сюда файл global.scss, чтобы мы могли создать связку CSS.

Для того чтобы собрать нашу библиотеку, добавим следующие зависимости dev.

npm i -D rollup @rollup/plugin-babel rollup-plugin-peer-deps-external rollup-plugin-scss rollup-plugin-terser @babel/preset-react @rollup/plugin-node-resolve @rollup/plugin-typescript 

Вход в полноэкранный режим Выход из полноэкранного режима

Давайте разберемся в этих зависимостях:

Далее создадим файл rollup.config.js и добавим в него следующее содержимое.

import { babel } from "@rollup/plugin-babel";
import external from "rollup-plugin-peer-deps-external";
import resolve from "@rollup/plugin-node-resolve";
import scss from "rollup-plugin-scss";
import typescript from "@rollup/plugin-typescript";
import { terser } from "rollup-plugin-terser";

export default [
  {
    input: "./src/index.ts",
    output: [
      {
        file: "dist/index.js",
        format: "cjs",
      },
      {
        file: "dist/index.es.js",
        format: "es",
        exports: "named",
      },
    ],
    plugins: [
      scss({
        output: true,
        failOnError: true,
        outputStyle: "compressed",
      }),
      babel({
        exclude: "node_modules/**",
        presets: ["@babel/preset-react"],
      }),
      external(),
      resolve(),
      typescript(),
      terser(),
    ],
  },
];


Вход в полноэкранный режим Выйти из полноэкранного режима

Далее нам нужно обновить package.json. Библиотеки должны распространяться с использованием CommonJS и ES6. Мы указываем пути к выходным файлам с помощью свойств main и module. Мы также используем эти свойства в конфигурационном файле Rollup.

Затем мы добавляем скрипт build, который использует rollup CLI с флагом -c. Это означает, что Rollup будет искать конфигурационный файл с именем rollup.config.js, чтобы связать библиотеку компонентов.

...
 "main": "dist/index.js",
 "module": "dist/index.es.js",
...
"scripts": {
    ...
    "build": "rollup -c", 
  }
...
Войти в полноэкранный режим Выход из полноэкранного режима

Теперь, если вы выполните команду npm run build, она соберет нашу библиотеку и создаст папку dist в корне каталога вашего проекта, которую можно будет развернуть с помощью npm.

Заключительные слова

Мы создали библиотеку компонентов react с нуля, используя typescript. Мы установили storybook для разработки компонентов пользовательского интерфейса в изоляции, настроили его для добавления поддержки SASS и, наконец, собрали все вместе с помощью Rollup.

Я решил написать эту статью после того, как создал библиотеку UI для себя в качестве побочного проекта, если вам интересно, вы можете ознакомиться с ней здесь.

Я надеюсь, что вы нашли это интересным и чему-то научились. Спасибо.

Per Aspera Ad Astra

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *