Intégrer Storybook dans un projet Vue.js avec vue-i18n

Dans le précédent article, nous avons vu comment intégrer Vue.js, vue-i18n et Jest dans un projet. Ajoutons un catalogue de composants avec Storybook.

À l’aide du guide propre à Vue, j’installe Storybook dans le projet :

npx sb init

Puis je vérifie que Storybook démarre correctement :

npm run storybook

Bien que la commande npx sb init ait créée un répertoire stories dans src, je préfère enregistrer les stories à côté des composants, comme dans le guide. J’ajoute un premier fichier de stories :

import AtmAccountBalance from './AtmAccountBalance.vue';

export default {
  title: 'Account Balance',
  component: AtmAccountBalance,
};

const Template = (args, { argTypes }) => ({
  props: Object.keys(argTypes),
  components: { AtmAccountBalance },
  template: '',
});

export const PositiveBalance = Template.bind({});
PositiveBalance.args = {
  amount: 1000,
};

Storybook ne sait pas interpréter le bloc custom <i18n> défini dans AtmAccountBalance.vue. Le démarrage de Storybook provoque une erreur :

ERROR in ./src/components/AtmAccountBalance.vue?vue&type=custom&index=0&blockType=i18n (./node_modules/vue-docgen-loader/lib??ref--12!./node_modules/vue-loader/lib??vue-loader-options!./src/components/AtmAccountBalance.vue?vue&type=custom&index=0&blockType=i18n) 16:6
Module parse failed: Unexpected token (16:6)
File was processed with these loaders:
 * ./node_modules/vue-docgen-loader/lib/index.js
 * ./node_modules/vue-docgen-loader/lib/index.js
 * ./node_modules/vue-loader/lib/index.js
You may need an additional loader to handle the result of these loaders.
| 
| {
>   "en": {
|     "account-balance": "Account Balance"
|   },

Tenant compte de l’indication qui accompagne le message d’erreur, j’ajoute dans .storybook/main.js une nouvelle règle à Webpack :

webpackFinal: async (config, { configType }) => {
  config.module.rules.push({
      resourceQuery: /blockType=i18n/,
      type: 'javascript/auto',
      loader: '@intlify/vue-i18n-loader'
    });
  return config;
},

Je redémarre Storybook, puis affiche la story dans le navigateur :

L’erreur affichée est familière. Storybook ne connaît pas les fonctions $t et $n de vue-i18n. Pour permettre l’affichage du solde, j’ajoute un décorateur dans le fichier .storybook/preview.js., dans lequel je mets en œuvre le contournement à base de beforeCreate proposé ici et ici par graup :

import Vue from 'vue'
import i18n from '../src/i18n'

export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
}

export const decorators = [
  (story, context) => {
    const wrapped = story(context);
    return Vue.extend({
      i18n,
      beforeCreate: function() {
        this.$root._i18n = this.$i18n;
      },
      components: { wrapped },
      template: ``,
    });
  },
];

Je redémarre Storybook, qui affiche une nouvelle erreur, liée cette fois à l’utilisation de babel-plugin-require-context-hook dans i18n.js.

ERROR in ./node_modules/babel-plugin-require-context-hook/register.js
Module not found: Error: Can't resolve 'fs' in '/home/arthur/Development/atm-frontend/node_modules/babel-plugin-require-context-hook'
 @ ./node_modules/babel-plugin-require-context-hook/register.js 8:12-25
 @ ./src/i18n.js
 @ ./.storybook/preview.js

La mise en place du plugin comme je l’ai faite dans le précédent article n’est pas satisfaisante. Idéalement, je dois limiter son exécution aux tests. Ma première piste :

if (process.env.NODE_ENV === 'test') {
  require('babel-plugin-require-context-hook/register')();
}

… ne plaît pas beaucoup à eslint :

 ERROR  Failed to compile with 1 error                                                                                                                                      21:57:17

 error  in ./src/i18n.js

Module Error (from ./node_modules/eslint-loader/index.js):

/home/arthur/Development/atm-frontend/src/i18n.js
  5:3  error  Unexpected require()  global-require

✖ 1 problem (1 error, 0 warnings)

Une meilleure solution, proposée par bob76828, est de créer un fichier jest.setup.js à la racine du projet :

import registerRequireContextHook from 'babel-plugin-require-context-hook/register';
registerRequireContextHook();

Puis de le référencer dans jest.config.js :

module.exports = {
  preset: '@vue/cli-plugin-unit-jest',
  transform: {
    '^.+\\.vue$': require.resolve('vue-i18n-jest')
  },
  setupFiles: [
    './jest.setup.js'
  ],
};

Et là tout marche bien. Pour les curieux, le code source du projet est à disposition sur GitHub.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *