Skip to content

Commit f51da94

Browse files
authored
Merge pull request #245 from cnumr/cms
chore: sync avec origin/main
2 parents cddfcbc + abba33a commit f51da94

12 files changed

Lines changed: 944 additions & 66 deletions

.changeset/fix-card-accessibility-button-tooltip.md

Lines changed: 0 additions & 10 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,28 @@
11
# gen-referentiel-core
22

3+
## 0.8.2
4+
5+
### Patch Changes
6+
7+
- 4e7e24b: Corrige l'erreur de build avec isomorphic-dompurify en ajoutant jsdom aux externals webpack côté serveur. jsdom lisait `default-stylesheet.css` via `__dirname` qui pointait vers `.next/server/` quand bundlé par webpack au lieu du répertoire du package.
8+
9+
## 0.8.1
10+
11+
### Patch Changes
12+
13+
- 1eac934: fix: améliore l'accessibilité des cards et corrige le tooltip du bouton info
14+
15+
- Déplace le lien de la card entière vers le titre uniquement (card-view.tsx) pour respecter les bonnes pratiques a11y (un lien ne doit pas englober toute une card interactive)
16+
- Ajoute un overlay invisible (`aria-hidden`, `tabIndex=-1`) pour maintenir la surface de clic sur toute la card
17+
- Déplace le lien sur le titre dans FicheCard (Card.tsx) avec `z-10` pour rester au-dessus de l'overlay
18+
- Corrige l'affichage du tooltip natif du bouton (i) en ajoutant `pointer-events-none` sur le SVG enfant, permettant au navigateur de cibler le `<button>` et d'afficher son attribut `title`
19+
20+
- e19a972: fix: corrige l'affichage des cards, le rendu HTML dans MDX et le chargement du .env dans build-local
21+
22+
- **Card (#53)** : supprime la fonction `cleanImpact` (regexp cassée sur des clés de traduction) et conditionne l'affichage de `priority_implementation` / `environmental_impact` aux feature flags du référentiel actif ; corrige le séparateur `: ` pour MESURE_ON_5
23+
- **MDX HTML (#54)** : ajoute les handlers `html` et `html_inline` dans `getMdxComponents` pour rendre le HTML brut inséré dans les fiches ; sanitisation via `isomorphic-dompurify` avec liste blanche de balises courantes
24+
- **build-local (#55)** : charge les variables du fichier `.env` (pattern `set -a / source / set +a`) avant le build, afin que `NEXT_PUBLIC_REF_NAME` et les autres variables soient correctement transmises
25+
326
## 0.8.0
427

528
### Minor Changes

CLAUDE.md

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,24 @@ pnpm build-local # Build local sans MongoDB
1919

2020
Appliquer systématiquement la méthode **APEX** pour chaque tâche :
2121

22-
| Phase | Action |
23-
|-------|--------|
22+
| Phase | Action |
23+
| ----------- | ------------------------------------------------------------------------- |
2424
| **A**nalyze | Lire/explorer le code concerné, comprendre le contexte et les contraintes |
25-
| **P**lan | Présenter le plan d'action et attendre validation avant d'implémenter |
26-
| **E**xecute | Implémenter la solution uniquement après accord |
27-
| e**X**amine | Tester/valider le résultat, vérifier qu'il n'y a pas de régression |
25+
| **P**lan | Présenter le plan d'action et attendre validation avant d'implémenter |
26+
| **E**xecute | Implémenter la solution uniquement après accord |
27+
| e**X**amine | Tester/valider le résultat, vérifier qu'il n'y a pas de régression |
2828

2929
**Règles :**
30+
3031
- Ne jamais coder sans avoir d'abord analysé et planifié
3132
- Toujours attendre la validation du plan avant d'exécuter
3233
- Toujours vérifier le résultat après implémentation
3334
- **Ne JAMAIS faire de commit sans demande explicite de l'utilisateur**
3435
- **Quand l'utilisateur demande un commit, toujours demander s'il veut aussi créer un changeset et une PR**
36+
- **Quand l'utilisateur demande une PR, vérifier si des fichiers `.changeset/*.md` existent (hors `README.md`) — si oui, lancer `pnpm release` avant de créer la PR**
3537

3638
**Commandes de validation (eXamine) :**
39+
3740
```bash
3841
pnpm check-types # Vérifier les types TypeScript
3942
pnpm lint # Vérifier le linting
@@ -45,6 +48,7 @@ pnpm build-local # Vérifier que le build fonctionne
4548
This is a **generic multi-referential platform** for eco-design best practices, maintained by the Collectif Green IT (CNUMR). It's a Next.js web application that uses TinaCMS as a headless CMS to manage eco-design best practices content in MDX format.
4649

4750
The codebase serves as a shared core (`gen-referentiel-core`) that can be configured for different referentials (RWP, RWEB, REIPRO, RIA, etc.) via environment variables:
51+
4852
- `NEXT_PUBLIC_REF_NAME` - Used by Next.js at runtime
4953
- `TINA_PUBLIC_REF_NAME` - Used by TinaCMS at build time
5054

@@ -110,8 +114,8 @@ pnpm doc # Start documentation server
110114
- **fiches/**: Best practice sheets organized by language (fr/, en/, es/)
111115
- Files follow naming: `RWP_<category>.<number>-<slug>.mdx`
112116
- Categories: 1 (Installation), 2 (Fonctionnalités), 3 (Design), 4 (Code), 5 (Médias), 6 (Mesure), 7 (Serveur), 8 (Maintenance)
113-
- **lexique/**: Glossary entries by language *(optionnel)*
114-
- **personas/**: User personas by language *(optionnel)*
117+
- **lexique/**: Glossary entries by language _(optionnel)_
118+
- **personas/**: User personas by language _(optionnel)_
115119
- **home/**: Homepage content by language
116120
- **mentionsLegales/**: Legal notices by language
117121

@@ -120,6 +124,7 @@ pnpm doc # Start documentation server
120124
### App Structure (`app/`)
121125

122126
Uses Next.js App Router with dynamic `[lang]` segment for i18n:
127+
123128
- `app/[lang]/page.tsx` - Homepage
124129
- `app/[lang]/fiches/` - Best practices listing and detail pages
125130
- `app/[lang]/lexique/` - Glossary
@@ -134,6 +139,7 @@ Uses Next.js App Router with dynamic `[lang]` segment for i18n:
134139
### Multi-Referential Configuration
135140

136141
The codebase supports multiple referentials via `referentiel-config.ts`:
142+
137143
- **RWEB** (Web eco-design) - Default referential if not configured
138144
- **RWP** (WordPress eco-design)
139145
- **REIPRO** (Software integration - Intégration de progiciels)
@@ -152,16 +158,17 @@ The active referential is set via `NEXT_PUBLIC_REF_NAME` and `TINA_PUBLIC_REF_NA
152158

153159
Les schémas YAML valident le frontmatter des fichiers MDX via `pnpm lint:md`. Le schéma utilisé dépend de `NEXT_PUBLIC_REF_NAME` :
154160

155-
| Schéma | Référentiels | Particularités |
156-
|--------|--------------|----------------|
157-
| `fiche.schema.yaml` | RWEB, REIPRO, RIA (défaut) | `environmental_impact`/`priority_implementation` = number, `scope` optionnel |
158-
| `fiche.schema.rwp.yaml` | RWP | `environmental_impact`/`priority_implementation` = string, `scope` requis |
161+
| Schéma | Référentiels | Particularités |
162+
| ----------------------- | -------------------------- | ---------------------------------------------------------------------------- |
163+
| `fiche.schema.yaml` | RWEB, REIPRO, RIA (défaut) | `environmental_impact`/`priority_implementation` = number, `scope` optionnel |
164+
| `fiche.schema.rwp.yaml` | RWP | `environmental_impact`/`priority_implementation` = string, `scope` requis |
159165

160166
La sélection est faite dynamiquement dans `.remarkrc.mjs` selon la variable d'environnement.
161167

162168
## Content Editing
163169

164170
Content can be edited:
171+
165172
1. **Locally**: Direct MDX file editing in `src/content/`
166173
2. **Via TinaCMS Admin**: Access at `/admin` when running dev server
167174

@@ -172,9 +179,11 @@ Le projet utilise [Changesets](https://github.com/changesets/changesets) pour g
172179
### Workflow
173180

174181
1. **Après un changement significatif**, créer un changeset :
182+
175183
```bash
176184
pnpm changeset
177185
```
186+
178187
Cela crée un fichier dans `.changeset/` décrivant le changement (patch/minor/major).
179188

180189
2. **Pour publier une nouvelle version** :
@@ -185,11 +194,11 @@ Le projet utilise [Changesets](https://github.com/changesets/changesets) pour g
185194

186195
### Types de versions
187196

188-
| Type | Quand l'utiliser |
189-
|------|------------------|
190-
| `patch` | Bug fixes, corrections mineures |
197+
| Type | Quand l'utiliser |
198+
| ------- | ------------------------------------------- |
199+
| `patch` | Bug fixes, corrections mineures |
191200
| `minor` | Nouvelles fonctionnalités rétro-compatibles |
192-
| `major` | Breaking changes, modifications majeures |
201+
| `major` | Breaking changes, modifications majeures |
193202

194203
### Vérifier la synchronisation
195204

@@ -202,12 +211,14 @@ Format: `<type>(<scope>): <description>`
202211
Types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`, `ci`, `build`, `revert`
203212

204213
Rules:
214+
205215
- Write in French
206216
- Use imperative present tense (ajoute, corrige, améliore)
207217
- Max 200 characters
208218
- No final period
209219

210220
Examples:
221+
211222
- `feat: ajoute authentification OAuth2`
212223
- `fix(auth): corrige la validation du token JWT`
213224
- `docs: met à jour la documentation API`
@@ -241,5 +252,6 @@ Examples:
241252
**Après modification** : Régénérer le lockfile avec `trash node_modules pnpm-lock.yaml && pnpm install`
242253

243254
**Références** :
255+
244256
- https://github.com/tinacms/tina-self-hosted-demo/issues/131
245257
- https://github.com/udecode/plate/issues/1609

components/mdx/mdx-components.tsx

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import DOMPurify from 'isomorphic-dompurify';
12
import Image, { ImageProps, getImageProps } from 'next/image';
23

34
// https://tina.io/docs/editing/markdown/
@@ -16,6 +17,66 @@ import { ui } from '../../i18n/ui';
1617

1718
type Lang = keyof typeof ui;
1819

20+
const HTML_SANITIZE_CONFIG = {
21+
ALLOWED_TAGS: [
22+
'b',
23+
'strong',
24+
'i',
25+
'em',
26+
'u',
27+
's',
28+
'del',
29+
'ins',
30+
'sup',
31+
'sub',
32+
'small',
33+
'mark',
34+
'abbr',
35+
'cite',
36+
'code',
37+
'kbd',
38+
'samp',
39+
'var',
40+
'br',
41+
'span',
42+
'a',
43+
'p',
44+
'ul',
45+
'ol',
46+
'li',
47+
'blockquote',
48+
'pre',
49+
'hr',
50+
'table',
51+
'thead',
52+
'tbody',
53+
'tfoot',
54+
'tr',
55+
'td',
56+
'th',
57+
'caption',
58+
'figure',
59+
'figcaption',
60+
],
61+
ALLOWED_ATTR: [
62+
'href',
63+
'title',
64+
'lang',
65+
'class',
66+
'id',
67+
'aria-label',
68+
'aria-hidden',
69+
'aria-describedby',
70+
'role',
71+
'colspan',
72+
'rowspan',
73+
'scope',
74+
],
75+
};
76+
77+
const sanitizeHtml = (html: string) =>
78+
DOMPurify.sanitize(html, HTML_SANITIZE_CONFIG);
79+
1980
const CodeBlock = ({ language, value }) => {
2081
return (
2182
<SyntaxHighlighter
@@ -45,6 +106,12 @@ export const getMdxComponents = (lang: Lang = 'fr') => ({
45106
h6: HeadingsStrong,
46107
PositionableImage: PositionableImage,
47108
code_block: CodeBlock,
109+
html: ({ value }: { value: string }) => (
110+
<span dangerouslySetInnerHTML={{ __html: sanitizeHtml(value) }} />
111+
),
112+
html_inline: ({ value }: { value: string }) => (
113+
<span dangerouslySetInnerHTML={{ __html: sanitizeHtml(value) }} />
114+
),
48115
...(getRefConfig().featuresEnabled.lexique_tooltips
49116
? {
50117
p: (props: any) => (
@@ -92,7 +159,10 @@ export const getMdxComponents = (lang: Lang = 'fr') => ({
92159
<>
93160
<picture>
94161
<source srcSet={image} />
95-
<img alt={alt} {...rest} />
162+
<img
163+
alt={alt}
164+
{...rest}
165+
/>
96166
</picture>
97167
{props.caption && (
98168
<figcaption className="text-center text-sm text-gray-500">

components/pages/fiche/Card.tsx

Lines changed: 23 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -39,23 +39,6 @@ export const FicheCard: FunctionComponent<FicheCardProps> = ({
3939

4040
const t = useTranslations(lang);
4141

42-
function cleanImpact(meta) {
43-
// TODO: replace with a proper translation
44-
let regexp: RegExp;
45-
switch (lang) {
46-
case 'es':
47-
regexp = /t("Fort")|t("Moyen")|t("Faible")/gi;
48-
break;
49-
case 'en':
50-
regexp = /t("High")|t("Medium")|t("Low")/gi;
51-
break;
52-
default:
53-
regexp = /t("Fuerte")|t("Medio")|t("Débil")/gi;
54-
break;
55-
}
56-
return fiche[`${meta}`]?.replace(regexp, ' ') || 'TBD';
57-
}
58-
5942
const getDataFilters = () => {
6043
let filters = {};
6144
if (getRefConfig().featuresEnabled.lifecycle) {
@@ -129,9 +112,7 @@ export const FicheCard: FunctionComponent<FicheCardProps> = ({
129112
}
130113
if (getRefConfig().featuresEnabled.environmental_impact === MESURE_ON_3) {
131114
metas.push(
132-
t('Impact environnemental') +
133-
': ' +
134-
t(cleanImpact('environmental_impact'))
115+
t('Impact environnemental') + ': ' + t(fiche.environmental_impact)
135116
);
136117
}
137118
if (getRefConfig().featuresEnabled.lifecycle) {
@@ -200,50 +181,54 @@ export const FicheCard: FunctionComponent<FicheCardProps> = ({
200181
</h2>
201182
<div className="flex w-full flex-col gap-2">
202183
<div className="flex flex-row items-center justify-start gap-2">
203-
{typeof fiche[`priority_implementation`] === 'number' ? (
184+
{getRefConfig().featuresEnabled.priority_implementation ===
185+
MESURE_ON_5 && (
204186
<span
205187
title={t("Priorité d'implémentation")}
206188
aria-label={`${t("Priorité d'implémentation")} : ${
207-
fiche[`priority_implementation`]
189+
fiche.priority_implementation
208190
}`}
209191
role="img"
210192
className="text-base">
211193
{t("Priorité d'implémentation") +
212-
' ' +
213-
fiche[`priority_implementation`]}
194+
': ' +
195+
fiche.priority_implementation}
214196
</span>
215-
) : (
197+
)}
198+
{getRefConfig().featuresEnabled.priority_implementation ===
199+
MESURE_ON_3 && (
216200
<span
217201
title={t("Priorité d'implémentation")}
218-
aria-label={`${t("Priorité d'implémentation")} : ${
219-
fiche[`priority_implementation`]
220-
}`}
202+
aria-label={`${t("Priorité d'implémentation")} : ${t(
203+
fiche.priority_implementation
204+
)}`}
221205
role="img">
222-
{t(cleanImpact('priority_implementation'))}
206+
{t(fiche.priority_implementation)}
223207
</span>
224208
)}
225-
<span>|</span>
209+
{getRefConfig().featuresEnabled.priority_implementation &&
210+
getRefConfig().featuresEnabled.environmental_impact && (
211+
<span aria-hidden="true">|</span>
212+
)}
226213
{getRefConfig().featuresEnabled.environmental_impact ===
227214
MESURE_ON_3 && (
228215
<span
229216
title={t('Impact environnemental')}
230-
aria-label={`${t('Impact environnemental')} : ${
231-
fiche[`environmental_impact`]
232-
}`}>
233-
{t(cleanImpact('environmental_impact'))}
217+
aria-label={`${t('Impact environnemental')} : ${t(
218+
fiche.environmental_impact
219+
)}`}>
220+
{t(fiche.environmental_impact)}
234221
</span>
235222
)}
236223
{getRefConfig().featuresEnabled.environmental_impact ===
237224
MESURE_ON_5 && (
238225
<span
239226
title={t('Impact environnemental')}
240227
aria-label={`${t('Impact environnemental')} : ${t(
241-
fiche[`environmental_impact`]
228+
fiche.environmental_impact
242229
)}`}
243230
className="text-base">
244-
{`${t('Impact environnemental')}: ${
245-
fiche[`environmental_impact`]
246-
}`}
231+
{`${t('Impact environnemental')}: ${fiche.environmental_impact}`}
247232
</span>
248233
)}
249234
</div>

0 commit comments

Comments
 (0)