Aller au contenu principal

Afficher le changelog de votre article

Afficher le changelog de votre article

· 4 minutes de lecture
Voir la source
Christophe
Amoureux de Markdown, WSL et Docker ~ Curieux insatiable.
Cet article existe aussi en anglais — lire la version originale.

Traduction automatique

Cette traduction a été produite par une IA. Elle peut contenir des contresens ou des maladresses, en particulier sur les termes techniques. En cas de doute, consultez l'article de référence en anglais — c'est lui qui fait foi.
Cet article fait partie de la série Créer des composants Docusaurus :

Afficher le changelog de votre article

⚡
TL;DR

Cet article montre comment mettre en place un changelog détaillé pour les articles de blog Docusaurus, au-delà du champ last_update par défaut. Il s'agit de créer un composant React personnalisé qui lit un tableau updates depuis le frontmatter YAML de l'article, ce qui permet d'avoir plusieurs notes datées. La solution demande de créer ce composant puis de swizzler le thème BlogPostItem/Content pour afficher automatiquement l'historique des mises à jour.

En tant qu'auteur de blog, il vous arrive forcément de retoucher d'anciens articles pour ajouter ou clarifier un passage. Ou simplement pour supprimer une information obsolète.

Docusaurus propose l'entrée last_update (source) comme illustré ci-dessous, mais vous ne pouvez indiquer qu'une seule date, sans description.

last_update:
date: 1/1/2000
author: custom author name

Créons un tout petit composant pour améliorer ça.

Le résultat​

Une fois le composant branché (voir plus bas), il suffit d'ajouter une entrée updates dans le frontmatter d'un article pour obtenir ceci, automatiquement :

Demo

Pourquoi ça marche​

  • Le composant lit un tableau updates directement dans le frontmatter YAML de l'article — aucun fichier de changelog séparé à maintenir.
  • Contrairement au last_update natif de Docusaurus, il accepte plusieurs entrées, chacune avec sa propre date et sa note.
  • Une fois swizzlé, il s'affiche automatiquement sur chaque article qui contient une entrée updates — rien à appeler manuellement article par article.

Installation​

Nous allons devoir créer un nouveau composant et swizzler le BlogPostPage, afin que le composant soit exécuté automatiquement.

Créer le composant Updated​

Créez un nouveau dossier src/components/Blog/Updated dans lequel vous allez créer deux fichiers : index.tsx et styles.module.css.

import type { JSX } from "react";
import clsx from "clsx";
import Translate from "@docusaurus/Translate";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import { parseMarkdown } from "@site/src/components/Blog/utils/markdown";
import styles from "./styles.module.css";
interface UpdateEntry {
date: string;
note: string;
}
interface Props {
updates?: UpdateEntry[];
}
export default function Updated({ updates }: Props): JSX.Element | null {
const { i18n } = useDocusaurusContext();
if (!updates || updates.length === 0) return null;
const sortedUpdates = [...updates].sort(
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime(),
);
return (
<div className={clsx(styles.revisionHistoryContainer, "margin-top--lg")}>
<h3 className={styles.revisionHistoryTitle}>
<span aria-hidden="true">📜</span>{" "}
<Translate id="blog.updated.changelog">Changelog</Translate>
</h3>
<div className={styles.timeline}>
{sortedUpdates.map((update, i) => {
const isLatest = i === 0;
return (
<div
key={`${update.date}-${i}`}
className={clsx(
styles.timelineItem,
isLatest && styles.timelineItemLatest,
)}
>
<div className={styles.timelineIcon}>
<span aria-hidden="true">{isLatest ? "✨" : "📅"}</span>
</div>
<div className={styles.timelineContent}>
<div className={styles.timelineDate}>
<time dateTime={update.date}>
{new Date(update.date).toLocaleDateString(
i18n.currentLocale,
{ year: "numeric", month: "long", day: "numeric" },
)}
</time>
{isLatest && (
<span className={styles.latestBadge}>
<Translate id="blog.updated.latest">Latest</Translate>
</span>
)}
</div>
<div
className={styles.timelineDescription}
dangerouslySetInnerHTML={{
__html: parseMarkdown(update.note),
}}
/>
</div>
</div>
);
})}
</div>
</div>
);
}

This component displays a changelog or update history for a blog post. It takes an array of updates with dates and notes, sorts them by date (newest first), and renders them as a timeline where the most recent update is highlighted with a sparkle emoji and a 'Latest' badge. If there are no updates, it returns nothing.

/*
* Updated — the timeline structure is good and is kept as-is. What changed:
* it no longer borrows --card-radius from PostCard's module (see that file),
* the timeline node is the shared .mk-dot, and the title is an eyebrow rather
* than a 1.1rem bold heading competing with the article's own.
*/
.revisionHistoryContainer {
composes: mk-surface from global;
padding: 1.25rem 1.375rem;
}
.revisionHistoryTitle {
composes: mk-eyebrow from global;
display: flex;
gap: 0.5rem;
align-items: center;
margin: 0 0 1rem;
}
.timeline {
position: relative;
}
/* The connecting line: centred on the 26px node (13px) rather than the old
* 30px one (15px). */
.timeline::before {
position: absolute;
top: 4px;
left: 13px;
width: 2px;
height: calc(100% - 8px);
content: "";
background: var(--mk-border);
}
.timelineItem {
position: relative;
display: flex;
min-height: 26px;
margin-bottom: 1.25rem;
}
.timelineItem:last-child {
margin-bottom: 0;
}
.timelineIcon {
composes: mk-dot from global;
position: absolute;
top: 0;
left: 0;
z-index: 1;
color: var(--mk-on-line);
background: var(--mk-n-300);
}
/* The latest revision is the only filled terracotta node, with a halo in the
* key tone — the halo used to be a color-mix() of the primary at 15%. */
.timelineItemLatest .timelineIcon {
background: var(--mk-primary);
box-shadow: 0 0 0 4px var(--mk-key-bg);
}
.timelineContent {
flex: 1;
padding-top: 0.15rem;
padding-left: 40px;
}
.timelineDate {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
align-items: center;
margin-bottom: 0.3rem;
font-size: 0.85rem;
font-weight: 600;
color: var(--mk-ink-soft);
font-variant-numeric: tabular-nums;
}
.timelineItemLatest .timelineDate {
color: var(--mk-primary);
}
.latestBadge {
composes: mk-pill from global;
}
.timelineDescription {
font-size: 0.95rem;
line-height: 1.6;
color: var(--mk-ink);
}

This stylesheet styles a revision history timeline component. It creates a vertical timeline with connected dots representing different revisions, where the latest revision gets special styling with a filled dot and a glowing halo. The layout uses flexbox to position content next to timeline nodes, and CSS custom properties to theme colors consistently.

En résumé, le composant vérifie si le frontmatter YAML de votre article de blog contient une entrée updates. Si c'est le cas, il la lit. Ce doit être un tableau avec deux informations : une date et une note.

Surcharger le template BlogPostPage​

Vérifiez d'abord si vous n'avez pas déjà le fichier src/theme/BlogPostItem/Content/index.js. Si vous êtes un lecteur de ce blog et que vous avez déjà personnalisé Docusaurus, vous l'avez peut-être déjà : le composant qui signale qu'un article a plus d'un an swizzle exactement ce même fichier.

Sinon, ouvrez une console et lancez yarn swizzle @docusaurus/theme-classic BlogPostItem/Content.

À partir de maintenant, vous avez un nouveau fichier sur votre disque : src/theme/BlogPostItem/Content/index.js.

Nous devons injecter notre nouveau composant Updated, éditez le fichier index.js pour y insérer le code nécessaire.

Voici les lignes minimales à ajouter au bon endroit dans votre propre fichier.

// [...]
import Updated from "@site/src/components/Blog/Updated";
export default function BlogPostContent({ children }) {
// [...]
const { metadata, isBlogPostPage } = useBlogPost();
// [...]
return (
<div>
// [...]
{isBlogPostPage && (
<>
// [...]
<Updated updates={metadata.frontMatter.updates} />
</>
)}
// [...]
</div>
);
}

Ce composant React affiche le contenu d'un article de blog en récupérant les métadonnées via le hook useBlogPost. Il rend conditionnellement un composant Updated qui affiche l'historique des modifications uniquement si on est sur la page complète de l'article, en passant les données d'updates depuis les frontmatter.

Autres démos​

Utiliser le composant​

Éditez maintenant n'importe lequel de vos articles existants et ajoutez une entrée updates dans votre frontmatter YAML, par exemple

updates:
- date: 2024-11-19
note: review Dockerfile, use Quarto 1.6.36.
- date: 2024-12-01
note: Added new theme options for blog posts.
- date: 2024-12-15
note: Fixed bug with image rendering on mobile devices.
- date: 2025-01-05
note: Implemented search functionality for documentation pages.
- date: 2025-01-20
note: Optimized build process for faster deployment times.

Enregistrez l'article et rafraîchissez votre page web. Si tout est correct, vous verrez l'information dans votre navigateur — le même rendu que celui présenté en haut de cet article.

N'hésitez pas à modifier src/components/Blog/Updated/styles.module.css pour coller à vos propres préférences de style.

Conclusion​

Un petit tableau lu dans le frontmatter et un seul fichier de thème swizzlé remplacent la date unique de last_update par un vrai changelog à plusieurs entrées — les lecteurs voient exactement ce qui a changé et quand, sans que vous ayez à maintenir un fichier séparé.

Ça se marie bien avec l'avertissement affiché sur les articles de plus d'un an : le premier dit ce qui a changé, le second dit quand c'est arrivé pour la dernière fois.


Cet article fait partie de la série Créer des composants Docusaurus :
🦋 Rejoignez la conversation sur BlueskyUn compte Bluesky gratuit est nécessaire pour aimer, repartager ou commenter. Ce bloc interroge l'API publique de Bluesky directement depuis votre navigateur (sans cookie, sans connexion) — votre adresse IP parvient donc aux serveurs de Bluesky dès que cette section se charge.

Chargement des commentaires…

Cet article vous a-t-il été utile ?
Avez-vous suivi les étapes de cet article ?