В этой статье мы рассмотрим начало работы с Storybook и VueJS (2.x) и, в частности, с использованием Typescript, синтаксиса в стиле класса и декораторов свойств. Мы построим игру в крестики-нолики (крестики-нолики).

Рассказ какой?

Storybook - это инструмент для изолированной разработки компонентов пользовательского интерфейса с поддержкой большинства популярных фреймворков, включая React, Angular и Vue. По сути, Storybook становится живым, дышащим гидом по строительным блокам вашего проекта. Это может стать отличным дополнением к процессу разработки.

Подход, в котором вы строите свой проект, может измениться, если ваши текущие принципы не коррелируют с атомарным дизайном или микро-интерфейсами. Ключевым моментом здесь является модульность. Storybook поощряет использование компонентно-ориентированной разработки (CDD). Таким образом, использование этой методологии дает несколько больших преимуществ. Разрабатывая по одному компоненту за раз, вы можете избавиться от необходимости манипулировать различными частями приложения для имитации необходимого состояния для компонента. В свою очередь, охват пользовательского интерфейса должен быть более полным, перечисляя все соответствующие состояния, включая те, которые могут быть не так распространены при разработке, например, состояние ошибки или загрузки. Есть две подробные статьи, которые вы можете прочитать здесь и здесь.

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

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

Приступим к настройке

Если вы еще этого не сделали, установите CLI для Vue или, если у вас установлен NPM v5.2 +, вы можете использовать npx, если предпочитаете .

npm install -g @vue/cli

Используйте CLI для создания проекта или NPX и вручную выберите следующие функции: синтаксис в стиле класса, babel с машинописным текстом, SASS (node-sass), VueX и Jest (модульное тестирование).

vue create <app-name>
npx @vue/cli create <app-name>

Хотя мы не будем использовать маршрутизатор в этом руководстве, он может пригодиться, если вы решите расширить его. Затем мы можем добавить Storybook с помощью Vue CLI, поскольку доступен плагин Storybook. Есть и другие способы добавить Storybook в свой проект; однако этот способ требует минимальной настройки, поэтому мы можем начать работу как можно быстрее. Установка CLI также добавит пример компонента вместе с историей.

vue add storybook

Прежде чем продолжить, давайте убедимся, что все работает.

npm run serve // Serves the Vue app locally
npm run test:unit // Runs our Jest, unit Tests
npm run storybook:serve // Serves Storybook locally

Если он не открылся в вашем браузере, перейдите по адресу localhost: 6060, и вы сможете увидеть наш экземпляр Storybook с созданным тестовым компонентом.

Наконец, нам нужно обновить конфигурацию Storybook, чтобы наши истории Typescript находились во всем каталоге проекта. Обновите ‘config / storybook / config.js’ следующим образом:

В этом руководстве мы будем хранить истории и компоненты вместе; однако для сложных или крупных проектов может быть предпочтительнее хранить истории в специальной папке.

Пора собрать компоненты

Итак, в качестве краткого обзора мы построим компонент плитки, затем компонент платы, текстовый компонент и, наконец, экран.

Итак, давайте начнем с компонента плитки и сопутствующей истории: components / Tile.vue и components / Tile.stories.ts.

Мы начнем с простой основы для компонента, которую мы создадим после того, как поработаем над историей. Чтобы запустить наш компонент, мы вызываем метод storiesOf и передаем ему имя, которое будет отображаться в нашем приложении Storybook. Затем мы должны вызвать add () для каждого из состояний, которые мы хотим описать для компонента. На данный момент у нас есть состояние по умолчанию, в котором отображается компонент. Если мы заглянем в наше приложение, вы должны увидеть плитку вместе с состоянием по умолчанию, которое мы только что создали. Окно приложения легко понять, с боковой панелью навигации для всех наших компонентов, разделом холста для отображения наших компонентов и внизу вкладками для различных надстроек, которые в настоящее время находятся в проекте. Использование интерфейса командной строки для установки Storybook включает в себя надстройки Действия и Ручки.

Как и в случае с TDD и написанием тестов в первую очередь, мы построим историю до того, как закончим наш компонент. Компонент плитки должен иметь три состояния; пусто или занято значком «X» или «O» в зависимости от игрока, который щелкнул по нему. Нам также потребуется использовать одно из ранее упомянутых надстроек - действия. Согласно документации, «Действия помогут вам проверить взаимодействия при создании отдельных компонентов пользовательского интерфейса. Часто у вас нет доступа к функциям и состоянию, которые у вас есть в контексте приложения. Используйте action (), чтобы заглушить их. ’.

Итак, давайте обновим нашу историю.

Как упоминалось ранее, нам нужны три состояния для плитки, которые теперь заменяют состояние «по умолчанию», которое у нас было ранее. Чтобы создать обратный вызов, когда мы щелкаем плитку, мы можем использовать action (), и, поскольку нам нужно использовать это во всех различных состояниях, мы можем связать его в одной переменной и передать в Наша история. В случае более сложного компонента с несколькими действиями это может значительно сократить объем написанного кода. Кроме того, для повторного использования у нас есть различные экспортные данные, которые мы можем использовать в другом месте проекта, такие как определение данных Tile (интерфейса), а также пример указанных данных. Перечисления и интерфейсы обычно находятся в отдельном каталоге.

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

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

Следующим компонентом, который нужно построить, будет игровая доска, которая будет состоять из набора плиток доски (9). Компонент прост, поэтому давайте сразу перейдем к истории и компоненту: components / Board.vue и components / Board.stories.ts.

История для доски имеет только два состояния, показывающих, что доска пуста и с некоторыми выбранными плитками. Состояние доски - это массив TileInterfaces, который мы можем передать доске для отображения. Как и в случае с плиткой (ами), на доске необходимо будет отображать действие события щелчка. Однако его функциональность позже будет обрабатываться компонентом экрана, поэтому нам просто нужно подтвердить, что взаимодействие работает правильно. Теперь, когда мы нажимаем на плитку на доске, мы также излучаем плитку, данные о которой вы можете увидеть на вкладке действий.

Прежде чем перейти к следующему компоненту, мы можем удалить созданную историю и компонент для кнопки. Далее мы представим еще один аддон, Knobs. Ручки дают вам возможность управлять реквизитами компонента. Чтобы продемонстрировать это, мы создадим простой текстовый компонент для отображения текущего состояния игры. Благодаря CLI аддон уже установлен.

Создайте текстовый компонент и его историю, components / InfoText.vue и components / InfoText.stories.ts.

О самом компоненте особо нечего сказать; это просто div, который отображает текстовую опору с некоторыми стилями. Теперь мы импортируем декоратор withKnobs и типы кнопок text и boolean, чтобы мы могли определить свойства, которые мы хотим иметь. доступ к в сборнике рассказов. Затем в функцию addDecorator () мы передаем withKnobs в качестве параметра, чтобы, наконец, мы могли интегрировать два типа кнопок для реквизита в историю по умолчанию.

Вернувшись в наше приложение Storybook, на вкладке «Ручки» теперь у нас есть доступ к реквизитам, которые мы определили в истории. Вы можете изменить текст и стили из того, к чему у нас есть доступ с помощью этого компонента. На мой взгляд, это одно из лучших дополнений к Storybook для всех, кто просматривает библиотеку компонентов (а не только разработчиков).

На игровой экран

Последним элементом, который нужно построить, является игровой экран, который будет представлять собой композицию из доски и текстовых компонентов. Для этого мы введем в истории (и игру) еще один аспект - VueX. Если вы использовали те же параметры интерфейса командной строки, нам не нужно устанавливать его. Для самого магазина я предпочитаю использовать следующую библиотеку; однако это не обязательно.

npm install -D vuex-module-decorators

Во-первых, убедитесь, что ваш main.ts отражает то, что находится ниже, и нам нужно создать файл для нашего магазина src / store.ts.

Это просто для определения нашего магазина и включенных в него модулей. Это нормально, если это будет в вашем main.ts, однако я предпочитаю разделение. Теперь мы можем обновить наш магазин, который должен находиться по адресу store / index.ts. Переименуйте его в State.ts и обновите следующим образом:

Есть три основных элемента, которыми мы хотим управлять в нашем магазине: текущее состояние доски, отображаемый текст и текущий игрок. Основное внимание здесь уделяется не VueX, поэтому я не буду много объяснять, но это простой магазин для хранения состояния игры.

Теперь мы можем создать наш экран, views / Game.vue и его историю views / Game / stories.ts. Кроме того, нам нужно обновить наш маршрутизатор и навигацию, чтобы иметь возможность переходить на новый экран, так что давайте сделаем это в первую очередь.

История для игрового экрана будет существенно отличаться от всех предыдущих, представленных до сих пор, вместо различных переменных для данных, которые нам нужны, мы будем использовать VueX. Мы можем включить магазин непосредственно в истории, которые в нем нуждаются, и использовать упрощенную версию того, что мы только что создали, поскольку для этого нужны только данные. В остальном история будет простой, с одним состоянием. Компонент экрана - это композиция из компонентов платы и текста с некоторыми стилями для их размещения.

Теперь, если мы вернемся к Storybook, у нас будет наш экран с данными, загруженными из нашего хранилища VueX вместо импортированных переменных. Мы также можем просмотреть игру в нашем приложении Vue, но без какой-либо логики ничего не получится.

Вот обновленный скрипт для Game.vue с некоторой базовой игровой логикой, делающей игру в некоторой степени играбельной.

Подведение итогов

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

Вот готовый исходный код этого проекта.

Бонус: автоматическое тестирование

Примечание: я не включал этот раздел ранее, так как столкнулся с небольшой ошибкой с VueX и Jest, но по большей части он работает. Код этого раздела также находится в отдельной ветке, которую вы можете найти здесь.

Теперь у нас есть эти истории для наших компонентов, с помощью другого аддона мы также можем получить некоторые автоматизированные тесты из них. Storyshots позволяет нам автоматизировать тесты моментальных снимков пользовательского интерфейса нашего компонента вместе с Jest. С единственной оговоркой, чтобы убедиться, что компоненты отображают данные, которые не изменяются, или что тесты моментальных снимков будут терпеть неудачу каждый раз. Например, если вы используете Date.now (), замените его жестко заданным значением.

Так что добавляем аддон и нужные нам зависимости

npm i -D @storybook/addon-storyshots jest-vue-preprocessor babel-plugin-require-context-hook

Затем обновите конфигурацию babel, babel.config.js.

module.exports = (api) => ({
presets: ['@vue/cli-plugin-babel/preset'],
...(api.env('test') && { plugins: ['require-context-hook'] }),
});

А затем ваша шутливая конфигурация, jest.config.js.

module.exports = {
preset: '@vue/cli-plugin-unit-jest/presets/typescript-and-babel',
transformIgnorePatterns: ['/node_modules/(?!(@storybook/.*\\.vue$))'],
};

Создайте спецификацию, которая инициализирует аддон для нас, tests / unit / storyshots.spec.js.

import initStoryshots from '@storybook/addon-storyshots';
import registerRequireContextHook from 'babel-plugin-require-context-hook/register';
registerRequireContextHook();
initStoryshots({
/* configuration options */
configPath: 'config/storybook',
framework: 'vue',
});

Важно добавить параметр configPath, поскольку vue cli не помещает конфигурацию Storybook в расположение по умолчанию.

Наконец, обновите сценарий test / test: unit в package.json, чтобы он отслеживал изменения.

"test": "vue-cli-service test:unit --watchAll",

Итак, все готово. Не слишком больно, правда? Теперь мы можем запустить наши модульные тесты, и вы должны увидеть все проходящие снимки, всего семь, для семи состояний / историй наших компонентов.

Теперь, если мы зайдем в наш v iews / Game.vue и продублируем компонент InfoText. Это должно привести к сбою нашего теста моментального снимка после повторного запуска тестов.

Наконец, после обновления компонента вам нужно будет обновить снимок, чтобы он снова прошел. Таким образом, мы можем добавить еще один скрипт в наш package.json, который может обновлять снимки.

"test:update": "vue-cli-service test:unit --u",

Если вы сейчас запустите наш новый скрипт, вы увидите, что моментальный снимок был успешно обновлен!