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.