Un article de Manon CARBONNEL et Jean-François GREFFIER.
Tour d’horizon de solutions pour collaborer autour d’une bibliothèque de composants custom.
Intro
Les applications front-end modernes se veulent modulaires, grâce à une architecture suivant les principes de la Programmation Orientée Composant (POC). C’est une tendance omniprésente dans les frameworks JavaScript modernes comme VueJS, Angular, etc.
Les composants sont particulièrement intéressants quand ils sont réutilisés, soit quand il s’agit de composants génériques au sein d’une application, soit à plus grande échelle dans un design system. Un design system permet de créer une bibliothèque de composants avec un design unifié, l’image de marque en tête (branding). Certains sont publics, comme Material Design ou le design system de WordPress.
Cependant, il y a de nombreux enjeux pour bien partager, documenter et surtout collaborer autour d’un design system (comme l’a bien expliqué Cécile Freyd-Foucault dans sa conférence au Devoxx 2022 “Petit guide pratique pour commencer un design system”). Heureusement, il existe des outils pour nous faciliter la tâche : nous allons voir en particulier le couple Storybook – Chromatic.
Au-delà de l’envie de bien faire et de mettre en place un design system car c’est considéré comme une bonne pratique, il y a une grosse plus-value immédiate.
Plus précisément :
- Lister et partager les composants
- Tester
- Revoir
- Approuver ou non
- Documenter
- Maintenir
- Intégrer plusieurs composants dans une page
Storybook, le premier outil que nous allons voir, est la base des autres solutions présentées dans cet article.
Storybook
C’est une application web permettant de construire des bibliothèques de composants et de pages de manière isolée. Des milliers d’équipes l’utilisent pour le développement, les tests et la documentation d’interface utilisateur. C’est une solution gratuite et open source.
C’est un moyen simple de partager une bibliothèque de composants, voire un design system entier. Storybook permet aussi de visualiser et d’interagir avec les composants hors de leur écosystème applicatif habituel, et donc de les tester dans un contexte neutre.
En effet, il peut être parfois fastidieux de développer un composant qui doit être intégré dans une application, car il y a de nombreux freins : la complexité de l’application, l’environnement de développement, celui de test, la nécessité de se connecter à un back-end. Avoir des composants isolés facilite le travail de la vérification, aux tests manuels et à la distribution.
Pour rendre la documentation et visualisation de la bibliothèque disponible en temps réel, et sans développer de pages particulières, il suffit d’héberger l’application pour faire un showcase. C’est un gain de temps considérable puisqu’il n’y a plus besoin de maintenir une application supplémentaire.
Stories
Ainsi, les stories constituent des exemples dynamiques de composants. Une documentation vivante et interactive permettant à la fois de voir des cas d’utilisation, mais aussi de visualiser les variantes d’un même composant.
Il suffit de l’ajouter dans les dépendances de votre projet, d’importer vos composants et de leur associer un fichier <name>.stories.js qui contiendra ses arguments possibles et des liens avec son template.
Par exemple pour une Story avec un composant VueJS : un bouton avec plusieurs options (type primary ou secondary, taille, etc.)
Fichier button.vue
<template>
<button type="button" :class="classes" @click="onClick" :style="style">{{ label }}</button>
</template>
<script>
import { reactive, computed } from 'vue';
export default {
name: 'my-button',
props: {
label: {
type: String,
required: true,
},
primary: {
type: Boolean,
default: false,
},
size: {
type: String,
validator: function (value) {
return ['small', 'medium', 'large'].indexOf(value) !== -1;
},
},
backgroundColor: {
type: String,
},
},
emits: ['click'],
setup(props, { emit }) {
props = reactive(props);
return {
classes: computed(() => ({
'storybook-button': true,
'storybook-button--primary': props.primary,
'storybook-button--secondary': !props.primary,
[`storybook-button--${props.size || 'medium'}`]: true,
})),
style: computed(() => ({
backgroundColor: props.backgroundColor,
})),
onClick() {
emit('click');
}
}
},
};
</script>
Lui est associé un fichier Button.stories.js
pour faire le lien avec Storybook :
import MyButton from './Button.vue';
// More on default export: https://storybook.js.org/docs/vue/writing-stories/introduction#default-export
export default {
title: 'Example/Button',
component: MyButton,
// More on argTypes: https://storybook.js.org/docs/vue/api/argtypes
argTypes: {
backgroundColor: { control: 'color' },
onClick: {},
size: {
control: { type: 'select' },
options: ['small', 'medium', 'large'],
},
},
};
// More on component templates: https://storybook.js.org/docs/vue/writing-stories/introduction#using-args
const Template = (args) => ({
// Components used in your story `template` are defined in the `components` object
components: { MyButton },
// The story's `args` need to be mapped into the template through the `setup()` method
setup() {
return { args };
},
// And then the `args` are bound to your component with `v-bind="args"`
template: '<my-button v-bind="args" />',
});
export const Primary = Template.bind({});
// More on args: https://storybook.js.org/docs/vue/writing-stories/args
Primary.args = {
primary: true,
label: 'Button',
};
export const Secondary = Template.bind({});
Secondary.args = {
label: 'Button',
};
export const Large = Template.bind({});
Large.args = {
size: 'large',
label: 'Button',
};
export const Small = Template.bind({});
Small.args = {
size: 'small',
label: 'Button',
};
Et voilà le rendu dans l’interface de Storybook :
À partir de là, on peut changer ses arguments (ici taille et statut primary/secondary), changer le contenu texte ou la locale afin de voir comment réagit l’affichage, ajouter un jeu de données via un JSON pour simuler un retour d’API, etc.
Addons
Enfin, il existe de nombreux addons gratuits et utiles pour enrichir les fonctionnalités de Storybook.
Par exemple, il est possible de construire des pages entières, puis de lier des stories entre elles à l’aide des événements du DOM (typiquement le click). Idéal pour représenter un parcours utilisateur, c’est possible avec l’addon Links.
On peut aussi utiliser l’addon Accessibility. Très intéressant, il permet de tester le rendu possible d’un composant pour des personnes ayant des capacités visuelles variées (flou, plusieurs formes de daltonisme, niveaux de gris, etc.) :
Sélection d’addons intéressants :
- Links
- Accessibility
- Locale i18n, qui permet le changement de locale par une simple select box
- Design, pour avoir la prévisualisation du design UI depuis Figma
Tests
Si Storybook permet un test visuel assez chouette, il ne remplace pas les tests unitaires des composants et les tests end to end.
Tests qui sont par ailleurs intégrables dans Storybook avec des addons :
Chromatic
Chromatic est une plateforme de collaboration qui permet d’héberger des builds Storybook, d’y ajouter des commentaires et de revoir les changements apportés à la collection de composants.
On peut dire qu’il sert à revoir, en détails, les changements suite à un nouveau build d’un design system. Comment ça se passe : pour chaque nouvelle version, un test de régression visuelle est créé puis, à chaque modification, il est possible de valider ou non les changements et d’échanger en équipe directement dans l’outil.
Vous l’aurez compris, Chromatic est fait sur-mesure et exclusivement pour Storybook. Pas étonnant, puisque la même équipe est derrière ces deux produits.
À chaque modification poussée via Git, un build est créé par Chromatic.
Cela va produire plusieurs choses : un Storybook hébergé, des exemples pour chaque composant et une liste de changements détectés par l’application.
Pour chaque changement détecté, un écran permettant de visualiser la différence, de comparer le DOM et surtout d’utiliser un système de commentaires et de validation.
En plus, on note la possibilité de l’insérer dans une chaîne d’Intégration Continue : il s’intègre à Gitlab, Github actions, Jenkins, etc
Chromatic est un outil payant, mais avec un plan freemium raisonnable. En effet, la version gratuite permet de collaborer à plusieurs, et d’intégrer Chromatic à sa CI. Par contre, la version gratuite est limitée à des snapshot Chrome seulement et à 5 000 snapshots par mois. Bien suffisant pour une évaluation en profondeur de l’outil ou même à une petite équipe.
La version premium à partir de 149 $/mois propose plus de snapshots, un nom de domaine personnalisé et d’autres navigateurs.
Même la version gratuite permet d’avoir plusieurs projets et équipes, ce qui est assez souple. À noter qu’il n’est pas possible d’héberger Chromatic chez soi.
Loki
Loki est un outil de test de régression visuelle facile à mettre en place et qui se base sur un navigateur sans interface graphique (headless). Plutôt que de vous obliger à avoir une configuration ou une syntaxe spécifique pour les tests, Loki utilise les stories que vous avez déjà créées. L’outil s’intègre facilement à Storybook et permet de profiter de l’isolation des composants et de tous les exemples de votre bibliothèque de composants afin d’y déceler d’éventuelles régressions.
Loki est un outil simple qui, dès lors qu’un Storybook est disponible, fonctionne en ligne de commande. Cela permet de l’utiliser en local ou dans une Intégration Continue. Il suffit de laisser Loki générer des images de références pour vos stories, et l’outil pourra ensuite s’en servir pour ses tests.
Par contre, il n’y a pas d’interface de collaboration, de validation ou même de comparaison. C’est minimaliste, mais efficace.
Loki peut être une alternative à Chromatic si vous n’êtes intéressé que par l’aspect test de régression visuelle.
Difficultés rencontrées
Durant notre Proof Of Concept, nous avons rencontré des difficultés à interfacer Storybook et Loki avec une stack ViteJS. Y compris avec la future version Storybook 7. Soit la bibliothèque Storybook ne buildait pas en statique, soit les stories des composants VueJS 3 + ViteJS n’étaient pas détectées par Loki. Une configuration plus classique à base de Webpack n’était, elle, pas problématique.
Il a également un point d’attention commun à toutes les solutions de test de régression visuelle : les images de références. En effet, ces images sont créées dans un contexte spécifique qui fait que le rendu peut être légèrement différent. Système d’exploitation, fontes installées, navigateurs sont autant de paramètres qui peuvent créer des différences.
La solution pour Loki est de passer par un Chrome exécuté dans Docker, via l’option target. Ça peut être contraignant car il faut également Docker dans la CI, via une configuration de runner spécifique dans GitLab CI par exemple. Pas de souci pour Chromatic vu que tout est distant.
Conclusion
Au-delà de la maintenabilité d’un design system ou d’une bibliothèque de composants, l’intérêt de ces outils réside dans la collaboration :
- Storybook permet de visualiser et intéragir avec un ensemble de composants (manuellement ou avec des tests)
- Chromatic ajoute un espace de collaboration pour valider, approuver, échanger facilement autour d’un build
- Loki permet d’automatiser des tests de non-régression visuelle pour assurer la qualité des composants
La mise en place d’une bibliothèque de composants, ou un design system, a une plus-value immédiate grâce à ces outils qui est d’assurer la qualité par la documentation et une meilleure testabilité des composants frontend en général : pas besoin d’un design system pour profiter de Storybook ou Chromatic.
Finalement, le plus important ce ne sont pas les outils, c’est la communication et l’auto-organisation :
“Les individus et leurs interactions plus que les processus et les outils” – Manifeste Agile
Sources additionnelles
Voir plus de projets utilisant Storybook ici : https://storybook.js.org/showcase/projects
Cécile Freyd-Foucault
“Petit guide pratique pour commencer un design system”
Jérémie Ledentu
Stop aux régressions visuelles de vos composants UI, avec Storybook et Loki
Ian VanSchooten
Developing, Documenting, and Testing your Vite app with Storybook
Vous avez besoin d’être accompagnés ?
Nos équipes sont à l’écoute !