Введение

Недавно мне поручили настроить новое приложение для одного из наших внутренних инструментов. Одно из предостережений заключается в том, что это новое приложение должно быть переносимым и составным. Приложение должно быть автономным веб-приложением, а также отображаться частично внутри других приложений. После некоторого тщательного рассмотрения я решил, что приложение будет пакетом NPM. Эта настройка позволяет нам экспортировать все приложение или его модули для использования внутри других приложений.

Следующим шагом для меня было создание хорошей среды разработки. Естественно, я выбрал Storybook в качестве основного инструмента построения. Если вы работали фронтенд-инженером, вы, несомненно, столкнулись с Storybook в той или иной форме.

В настоящее время в версии 6.5 Storybook работает с большинством, если не со всеми, популярными платформами Frontend. По своей сути он предоставляет способ изолированного создания компонентов пользовательского интерфейса и страниц. Благодаря этому мы можем работать над каждым компонентом и легко объединять многие в модули, прежде чем отправлять их в производство. Не вдаваясь в тонкости UX-дизайна, Storybook продвигает вперед атомарный дизайн.

Подробнее об атомарном дизайне можно прочитать здесь

Следующим шагом для меня было создание хорошей среды разработки. Естественно, я выбрал Storybook в качестве основного инструмента построения.

Начальная настройка

# Add Storybook:
npx storybook init

Это запустит интерфейс командной строки Storybook и создаст необходимые пакеты для запуска.

Примечание. Это не будет работать с пустым проектом. Нам понадобится React, Angular, Vue… и т. д., прежде чем Storybook CLI сможет инициализировать

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

.storybook/
src/stories/

Каталог src/stories/ содержит несколько примеров. Наше внимание будет сосредоточено в основном на директории .storybook/.

Файл .storybook/main.js содержит некоторые настройки того, где будут храниться истории, и некоторые дополнения. Ниже приведен пример того, как это может выглядеть для проекта ReactJs.

module.exports = {
  "stories": [
    "../src/**/*.stories.mdx",
    "../src/**/*.stories.@(js|jsx|ts|tsx)"
  ],
  "addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@storybook/addon-interactions"
  ],
  "framework": "@storybook/react"
}

На этом этапе мы можем создать сценарий NPM, чтобы начать проект с Storybook.

"storybook": "start-storybook -p 4000",

Порт здесь произвольный

yarn storybook

Это должно открыть Storybook в представлении, подобном этому

Что ж, это здорово! А как насчет TypeScript?

Настройка TypeScript/Webpack

Настройка TypeScript в Storybook идентична настройке обычного приложения. Во-первых, нам нужно настроить наш файл tsconfig.json в соответствии с документом здесь. Также стоит добавить наши истории в исключенный блок в конфиге

"exclude": [
  "node_modules",
  "**/__tests__/*",
  "**/stories/*".   <-- Excluding stories from compilation
]

Затем мы создаем файл webpack.config.js в каталоге .storybook/ (ваша фактическая установка может немного отличаться от этой).

const path = require('path')
const webpack = require('webpack')
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin')
const tsConfigsPathsName = path.join(__dirname, '../tsconfig.json')

module.exports = ({ config, mode }) => {
  config.module.rules.push({
    test: /\.(ts|tsx)$/,
    loader: require.resolve('ts-loader'),
    options: {
      configFile: require.resolve(tsConfigsPathsName),
    },
  })

  config.resolve.plugins = [
    new TsconfigPathsPlugin({
      configFile: tsConfigsPathsName,
    }),
  ]

  config.resolve.extensions.push('.ts', '.tsx')
  return config
}

tsconfig-paths-webpack-plugin используется для разрешения псевдонимов путей. Я уже писал статью о том, как настроить это здесь

«Прекратите использовать относительные пути — используйте псевдонимы импорта для веб-приложения Typescript | Фонг Лам | май 2022 г. | Середина"

Получение данных

С этой частью у меня было больше всего проблем. Хотя запуск пакета как символической ссылки внутри другого приложения был возможен, это было громоздко. Нам нужен был способ вызова API из рассказов Storybook. После некоторых исследований я обнаружил, что мы можем использовать промежуточное ПО аналогично серверу NodeJs.

Создайте файл middleware.js в каталоге .storybook/

const { createProxyMiddleware } = require('http-proxy-middleware')

const URLS = {
  production: 'https://some-app.prod.com/graphql',
  staging: 'https://some-app.staging.com/graphql',
  local: 'http://localhost:3000/graphql',
}

module.exports = router => {
  const targetUrl = URLS[process.env.PROXY]

  router.use(
    '/graphql',
    createProxyMiddleware({
      changeOrigin: true,
      target: targetUrl,
    })
  )
}

Используя пакет http-proxy-middleware, мы можем создать прокси из Storybook для наших конечных точек. Приведенный выше пример позволяет нам запускать сервер на local или указывать на prod/staging, в зависимости от наших флагов process.env.PROXY. Мы можем создать несколько сценариев NPM

"storybook:local": "PROXY=local start-storybook -p 4000",
"storybook:prod": "PROXY=production yarn storybook",
"storybook:staging": "PROXY=staging yarn storybook",

Заключение

Вот и все! Наш Storybook теперь может быть автономным пакетом с TypeScript и GraphQL.

С этой настройкой наша команда может содержать как пользовательский интерфейс, так и бизнес-логику внутри пакета. Мы делим истории на компоненты, а также модули интеграции. При объединении их вместе мы можем иметь работающее приложение в локальной среде разработки. Интенсивно используя React Hooks, мы можем дополнительно модульизовать наши компоненты, как строительные блоки, с минимальными зависимостями от их контейнеров.