Skip to content

Ajoute un sommaire construit depuis les templates MDX

Sylvain Boulade requested to merge mdx-summary into main

Contexte

Résout https://datahub.incubateur.tech/infrastructure/indicateurs/-/issues/164

Le but principal de cette PR est d'ajouter à nos fiches (version web) un "sommaire" qui permettrait de naviguer entre les différentes sections facilement.

Sur le principe ça semble plutôt simple dans la mesure où il existe déjà un plugin remark (remark-toc) identifié par Ronan qui fait déjà le boulot.

Le problème de ce plugin est qu'il injecte directement le sommaire dans le DOM, donc avec des options de customization relativement limitées, et sans possibilité pour nous de l'intégrer à notre arbre de composants React.

Pour pallier à cela j'ai implémenté un certains nombre d'éléments dans cette PR (plugin remark, contextes react, ...) dont un n'est pas entièrement satisfaisant pour moi, mais pour lequel je n'ai pas trouvé de meilleure alternative. La principale limitation que j'ai constaté est le fait qu'on ne peut (à priori ? je suis loin d'être un expert du MDX 😅) injecter à nos composants MDX que des props de type string (pas hyper adapté pour un sommaire qui a une structure de type arbre).

Il y a pas mal de changements, alors je vais détailler un peu plus dans la section ci-dessous. A dispo également pour en discuter de vive voix s'il y a des points à clarifier 🙂

Contenu

Il y a plusieurs points à connaitre pour comprendre l'implémentation:

  • Nous avons convenu avec Aurélie d'utiliser un dropdown pour afficher le sommaire (principalement pour des raisons de gain de place sur la page), ce qui fait qu'utiliser un plugin qui injecte du DOM déjà généré n'est pas très adapté, l'idéal est d'injecter l'arbre du sommaire généré depuis le MDX dans un composant React en tant que prop (afin de pouvoir avoir toute flexibilité pour ensuite définir des comportements arbitraires pour ce sommaire)
  • Nous ne sommes pas encore sûr du meilleur emplacement pour ce sommaire, pour le moment j'ai utilisé l'emplacement préféré par Aurélie, mais nous avions aussi pensé à l'afficher au niveau de la barre de filtres (c'est-à-dire en dehors du composant qui render le MDX dans notre arbre)

Injection du sommaire dans un composant React

5e4031f3

De mon point de vue la solution "parfaite" aurait été un plugin remark qui nous permet d'injecter le sommaire dans un composant React arbitrairement défini par nos soins, afin de pouvoir entièrement customizer son rendu, toutefois je n'ai pas trouvé un tel plugin.

D'après mes recherches je n'ai également pas trouvé de moyen d'injecter des props autre que de type string aux composants MDX (peut-être ai-je raté quelque chose ici).

Du coup mon idée est de créer un plugin remark qui répond à notre cas d'usage, fortement inspiré de remark-toc.

Je reprends donc la même logique (utilisation de mdast-util-toc pour extraire le sommaire) mais au lieu d'injecter des éléments DOM, j'injecte un élément de type mdxJsxFlowElement qui sera ensuite transformé en composant react.

Pour lui injecter le sommaire, j'extrais les titres de premier niveau (pour cette itération cela me semblait plus simple) dans un array de type { label: string, value: string }[], et je stringifie (😬) cet array pour que ce soit supporté par MDX. C'est vraiment cet aspect de stringification que j'aurais préféré éviter.

A partir de là, nous pouvons définir un composant qui va recevoir une prop "sommaire" qui est une version stringifiée de notre sommaire.

Affichage du sommaire dans un composant hors du contenu MDX

2038f474

Il aurait été simple de créer un composant "Sommaire" et de JSON.parse le sommaire directement dedans pour l'afficher comme on veut.

Toutefois je me suis vite rendu compte que ça manquait de flexibilité: on ne peut afficher le sommaire que dans l'arbre MDX , à l'endroit où il a été injecté.

En discutant avec Aurélie on pensait qu'il pourrait être intéressant de l'afficher dans la barre de filtres entre autres. Même si finalement on a décidé de l'afficher à un endroit qui est quasiment dans le MDX, j'ai préféré garder la logique nécessaire à pouvoir l'afficher où on veut dans la page: ça me semble plus flexible et évolutif (même si bien sûr ça rajoute de la complexité de front).

Mon idée pour ce faire est de construire le composant MDX qui "reçoit" la prop "sommaire" comme un composant "headless" qui au lieu d'afficher lui-même le sommaire (i.e. SetSommaire), va au contraire le stocker dans un contexte React (i.e. SommaireContext).

Suite à cela, nous avons notre sommaire disponible dans un contexte et nous pouvons donc l'afficher où nous voulons et en faire ce que nous voulons.

Rendu visuel

4ffd3fe2 & fb1d8b02 & c69fcd07

Pour le rendu visuel je me suis basé sur les reco d'Aurélie qu'elle m'a préparé dans figma, mais nous pourrons faire évoluer cela si ça semble pertinent.

Un des aspects intéressants est l'introduction d'un hook pour utiliser l'API IntersectionObserver, ici le but est d'ajouter une box-shadow à notre barre sticky lorsqu'on scroll suffisamment dans la page.

J'ai également ajusté quelques aspects mineurs:

  • Extraction d'un composant Select plus global afin de réutiliser certains styles communs, et traduire le palceholder quand la recherche n'a pas de résultats (il disait "No Options"): f74f7646
  • Ajout d'un autre contexte destiné à injecter le "type de document", l'idée est de ne plus avpor nespo, de se baser sur l'URL dans les composants MDX pour le déduire: bb1ea2da

Le rendu donne plus ou moins ça actuellement:

sommaire-below-title

Edited by Sylvain Boulade

Merge request reports