Créer un tableau de status pour vos composants dans Storybook

⚠️ La totalité de cet article a été rédigé pour être mis en place avec @storybook/html en utilisant le format MDX.

Le contexte

Notre besoin était de visualiser les différents status (UI, HTML – Vanilla, A11Y, Doc, Framework) de l’ensemble de nos composants à l’aide d’un tableau récapitulatif.

La centralisation de l’information avait pour objectif d’offrir de l’autonomie aux personnes pour trouver et lire facilement les informations qu’ils recherchaient. Evitant ainsi les A/R entre Confluence, Figma et Storybook.

Le résultat attendu était le suivant :

Les contraintes

  • Le tableau doit être gérer par une page Storybook au format MDX.
  • La solution doit être évolutive (nouveau status, nouveau framework, etc).
  • La mise à jour des status d’un composant doit être simple.
  • Notre bibliothèque est monobloc et nos composants ne disposent pas d’un package.json indépendant.

Les implémentations

1. La simplicité

La première idée fut la plus simple : un tableau HTML statique dans le fichier MDX. Quelqu’un a dit « Un tableau HTML c’est pas si compliqué à maintenir » 😂 D’autres ne m’ont pas cru…

Même si la mise en place a été plutot rapide et plébicitée, ce fut rapidement un échec sans surprise.

Ce fut une vraie galère à maintenir et faire évoluer même avec seulement une dizaine de composants.

Il y aurait bien eu la solution de gestion de tableaux HTML avec un outil en ligne mais l’idée a été tuée avant d’y investir cinq minutes.

2. La praticité

Pour que toute notre histoire devienne pratique, il nous fallait savoir trois choses :

  1. Storybook est-il en mesure de nous fournir la liste de toutes nos stories ?
  2. Où peut-on stocker les informations dans une story ?
  3. Peut-on rendre un tableau HTML dynamique dans un fichier MDX avec Storybook ?

1. La réponse est oui

Comme je n’ai absolument rien tracé pendant mes recherches, je ne sais plus vraiment comment je suis arrivé à trouver ça. Certainement en passant quelques heures sur GitHub et Stack Overflow.

Le paquet @storybook/html met à disposition deux méthodes pour récupérer l’ensemble des contenus :

  • getStorybook va retourner un tableau contenant l’ensemble des stories et des pages. Malheureusement pour nous, les informations retournées sont limitées et ne vont pas couvrir notre futur besoin.

  • raw va retourner un tableau contenant uniquement l’ensemble des stories avec toutes leurs informations. 🥳


J’ai encore quelques regrets sur le fait de ne pas avoir trouvé de solution avec la méthode getStorybook surtout quand je sais qu’il existe un @deprecated devant la méthode raw ici.

Il existe bien une méthode selectStory dans le paquet @storybook/api mais rien ne semble fonctionnel au niveau d’une story (j’ai peut être loupé un truc) 😠. Alors que ça semble tout à fait possible dans une addon : Addon API – useStorybookApi [en]

Pour aller au bout de notre idée, nous profiterons de raw tant que la méthode est disponible 🙏

2. Les paramètres d’une story sont sans limite

On trouve souvent notre bonheur et notre délivrance dans la documentation d’un projet. Dans notre cas, ce sera l’objet parameters [en]. Un espace de stockage qui va servir à différentes choses dans Storybook et qui semble sans restriction. Let’s go.

L’autre avantage de passer par cet objet est la notion d’héritage (cf. : Rules of parameter inheritance [en]). Celle qui nous intéressera le plus dans notre cas sera celle entre le composant et ses stories.

Du coup, dans chaque composant au niveau voulu (composant, story ou les deux), nous aurons dans parameters un objet de la forme :

parameters={{
  uiLibraryName: {
    status: {
      ui: ['ready'],
      html: ['ready', 'includes-js'],
      a11y: [
        {
          value: 'ready',
          description: 'modèle [accordion](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/) "Accordion (Sections With Show/Hide Functionality) - ARIA Authoring Practices")',
        }
      ],
      docs: ['in-progress'],
      frameworkA: ['no-available'],
      frameworkB: ['in-progress'],
      frameworkC: ['scheduled'],
    }
  }
}}

Ce dernier nous pemettra d’avoir la liste de tous nos status avec la possibilité d’avoir un ou N états pour chacun. Nous avons également laissé la possibilité de saisir une description pour un état à l’aide d’un objet (qui accepte du Markdown 😛).

Autre détail, les status ne sont pas à la racine de notre objet uiLibraryName (vous y donnerez le nom que vous voulez) pour se laisser d’autres possibilités dans le futur.

3. Avec html-react-parser rien ne semble impossible

Le paquet html-react-parser nous sert déjà à afficher le résultat d’une story ou plutôt de son template à l’intérieur d’un élément <Canvas> pour ne pas polluer l’arborescence dans la sidebar (cf. : storybookjs/storybook#9209 (comment))

Du coup, nous n’avons pas cherché plus loin pour la création de notre tableau.

Voici l’exemple pour notre légende :

import htmlReactParser from 'html-react-parser'
import { Meta } from '@storybook/addon-docs'
import includeJS from './assets/media/js.svg'

export const componentStatus = {
  scheduled: { icon: '🚀', label: 'Programmé' },
  ready: { icon: '✅', label: 'Validé' },
  'in-progress': { icon: '⏳', label: 'En cours' },
  alert: { icon: '⚠️', label: 'Point de vigilance' },
  blocked: { icon: '🔒', label: 'Bloqué' },
  'not-available': { icon: '➖', label: 'Non implémenté' },
  experimental: { icon: '🧪', label: 'Expérimental' },
  'includes-js': {
    icon: `<img src="${includeJS}" style="width:1.25em;height:1.25em;vertical-align:middle" />`,
    label: 'Inclus du JavaScript',
  },
}
 
<Meta
  title="Composants/Status des composants"
  parameters={{
    options: { isToolshown: false },
  }}
/>

# Statut des composants
 
<br />
 
**Légende** :
 
<ul>
  {htmlReactParser(Object.values(componentStatus)
    .map(
      (s, i) =>
        `<li style="font-size:14px${i ? ';margin-top:.25em' : ''}">${
          s.icon
        }&nbsp;${s.label}</li>`
    )
    .join(''))}
</ul>

4. Le petit tips pour le plaisir

Pour restituer le markdown dans la description de nos status, nous avons utilisés le paquet marked.

La dernière petite chose qui nous resterait à faire et de gérer l’attribut target="_blank" grâce à ce contournement : markedjs/marked#655 (comment). En attendant, on va expliquer aux gens qu’on peut faire ça aussi avec un raccourci clavier 😉

En espérant que tout cela pourra vous être utile :bowtie:

La page complète est dispo juste ici : https://gist.githubusercontent.com/7studio/6af8612b5574d58f458a5a8afc0b626b/raw/1bd27b26e77c21da3f124a2c957c4001c40dbeeb/1.components.stories.mdx

Laisser un commentaire

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