Skip to content

Commit d8484d8

Browse files
authored
Merge pull request #192 from geo2france/dev
v1.23
2 parents abb22cb + cac32c1 commit d8484d8

96 files changed

Lines changed: 12155 additions & 5713 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/chromatic.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: Chromatic — Visual Regression Tests
2+
3+
on:
4+
push:
5+
branches-ignore:
6+
- main # Couvert par deploy-storybook.yml ; Chromatic tourne sur toutes les branches de travail
7+
pull_request:
8+
types: [opened, synchronize, reopened]
9+
10+
jobs:
11+
chromatic:
12+
name: Run Chromatic
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
- name: Checkout
17+
uses: actions/checkout@v4
18+
with:
19+
# Chromatic a besoin de tout l'historique git pour calculer les diffs correctement
20+
fetch-depth: 0
21+
22+
- name: Setup Node
23+
uses: actions/setup-node@v4
24+
with:
25+
node-version: '20'
26+
cache: 'npm'
27+
28+
- name: Install dependencies
29+
run: npm ci
30+
31+
# Chromatic publie le Storybook et compare visuellement chaque story
32+
# avec la baseline approuvée. Les diffs sont détectables dans l'UI Chromatic
33+
# et bloquent la PR si de nouvelles modifications visuelles sont trouvées.
34+
- name: Run Chromatic
35+
uses: chromaui/action@latest
36+
with:
37+
# Token du projet Chromatic (à créer sur https://www.chromatic.com et
38+
# à ajouter dans Settings > Secrets > CHROMATIC_PROJECT_TOKEN)
39+
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
40+
# Quitte avec un code d'erreur si des changements visuels non approuvés
41+
# sont détectés (bloquant pour les PR)
42+
exitOnceUploaded: false
43+
exitZeroOnChanges: false
44+
# Seuil de tolérance pour éviter les faux positifs liés à l'anti-aliasing
45+
# (en pourcentage de pixels différents par story, 0.2 = 0.2%)
46+
diffThreshold: 0.2

.storybook/manager.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import '@fontsource/inter/400.css';
2+
import '@fontsource/inter/500.css';
3+
import '@fontsource/inter/600.css';
14
import { addons } from 'storybook/manager-api';
25
import { create } from 'storybook/theming';
36

.storybook/preview.ts

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

.storybook/preview.tsx

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import '@fontsource/inter/400.css';
2+
import '@fontsource/inter/500.css';
3+
import '@fontsource/inter/600.css';
4+
import React, { useEffect } from 'react';
5+
import type { Preview, Decorator } from '@storybook/react-vite';
6+
import { ConfigProvider } from 'antd';
7+
import { customTheme } from './manager';
8+
import {
9+
geo2franceLightTheme,
10+
geo2franceDarkTheme,
11+
neutralLightTheme,
12+
neutralDarkTheme,
13+
} from '../src/theme';
14+
import type { ThemeName } from '../src/theme';
15+
16+
type ThemeMode = 'light' | 'dark';
17+
type ThemeKey = `${ThemeName}-${ThemeMode}`;
18+
19+
const THEMES: Record<ThemeKey, object> = {
20+
'geo2france-light': geo2franceLightTheme,
21+
'geo2france-dark': geo2franceDarkTheme,
22+
'neutral-light': neutralLightTheme,
23+
'neutral-dark': neutralDarkTheme,
24+
};
25+
26+
const BG: Record<ThemeMode, string> = {
27+
light: '#ffffff',
28+
dark: '#141414',
29+
};
30+
31+
/**
32+
* Decorator global : enveloppe chaque story dans un ConfigProvider Ant Design
33+
* correspondant au couple (theme × mode) sélectionné dans la toolbar Storybook.
34+
*
35+
* Note : les stories qui contiennent leur propre <DashboardApp> (qui expose son
36+
* propre <ThemeProvider>) ignoreront ce decorator pour la partie layout — c'est
37+
* le comportement attendu (ConfigProvider nested with inherit:true).
38+
*/
39+
const withTheme: Decorator = (Story, context) => {
40+
const themeName = (context.globals.themeName as ThemeName) ?? 'geo2france';
41+
const themeMode = (context.globals.themeMode as ThemeMode) ?? 'light';
42+
const key: ThemeKey = `${themeName}-${themeMode}`;
43+
const antdTheme = THEMES[key] ?? THEMES['geo2france-light'];
44+
const bg = BG[themeMode];
45+
46+
useEffect(() => {
47+
document.body.style.backgroundColor = bg;
48+
return () => {
49+
document.body.style.backgroundColor = '';
50+
};
51+
}, [bg]);
52+
53+
return (
54+
<div style={{ backgroundColor: bg, minHeight: '100vh' }}>
55+
<ConfigProvider theme={antdTheme}>
56+
<Story />
57+
</ConfigProvider>
58+
</div>
59+
);
60+
};
61+
62+
const preview: Preview = {
63+
tags: ['autodocs'],
64+
65+
globalTypes: {
66+
themeName: {
67+
name: 'Thème',
68+
defaultValue: 'geo2france',
69+
toolbar: {
70+
icon: 'paintbrush',
71+
items: [
72+
{ value: 'geo2france', title: 'Géo2France' },
73+
{ value: 'neutral', title: 'Neutral' },
74+
],
75+
dynamicTitle: true,
76+
},
77+
},
78+
themeMode: {
79+
name: 'Mode',
80+
defaultValue: 'light',
81+
toolbar: {
82+
icon: 'mirror',
83+
items: [
84+
{ value: 'light', title: 'Light', right: '☀️' },
85+
{ value: 'dark', title: 'Dark', right: '🌙' },
86+
],
87+
dynamicTitle: true,
88+
},
89+
},
90+
},
91+
92+
decorators: [withTheme],
93+
94+
parameters: {
95+
codePanel: true,
96+
docs: {
97+
theme: customTheme,
98+
},
99+
controls: {
100+
matchers: {
101+
color: /(background|color)$/i,
102+
date: /Date$/i,
103+
},
104+
},
105+
options: {
106+
storySort: {
107+
order: [
108+
'Documentation', ['Introduction'],
109+
'Layout', ['DashboardApp'],
110+
'Dataset',
111+
'Dataviz',
112+
'Controle',
113+
],
114+
},
115+
},
116+
a11y: {
117+
// Mode bloquant : les violations d'accessibilité font échouer les tests vitest/CI.
118+
// Les stories avec des problèmes techniques connus (canvas ECharts) portent
119+
// leur propre surcharge `parameters.a11y.test = 'todo'` avec un commentaire FIXME.
120+
test: 'error',
121+
config: {
122+
rules: [
123+
// FIXME(a11y): La couleur primaire Géo2France (#95c11f) a un ratio de
124+
// contraste ≈ 2.9:1, en-dessous du seuil WCAG AA (4.5:1 texte normal,
125+
// 3:1 grand texte). C'est un choix de charte graphique délibéré.
126+
// À traiter dans un ticket dédié : fix/a11y-geo2france-contrast.
127+
{ id: 'color-contrast', enabled: false },
128+
],
129+
},
130+
},
131+
},
132+
};
133+
134+
export default preview;

CHANGELOG.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Changelog
2+
3+
## [Unreleased] — Visual Identity Theming API
4+
5+
### Added
6+
7+
- **`createVisualIdentity()`** — factory pour créer une identité visuelle personnalisée à partir de tokens simples
8+
- **`VisualIdentityTokens`** / **`VisualIdentityShorthand`** — contrats stables pour définir une identité visuelle client
9+
- **Prop `visualIdentity`** sur `<ThemeProvider>` et `<DashboardApp>` — accepte 3 formes :
10+
- `VisualIdentityThemeBundle` (pré-construit via `createVisualIdentity()`)
11+
- `VisualIdentityTokens` (auto-wrappé)
12+
- `VisualIdentityShorthand` (`{ name, primary, logo }` — démarrage rapide)
13+
- **`useVisualIdentityLogo()`** — hook retournant le logo adapté au mode (light/dark)
14+
- **Intégration automatique du logo** dans `DashboardSider` via `useVisualIdentityLogo()` (avec fallback sur la prop `logo` existante)
15+
- Types exportés : `VisualIdentityColors`, `VisualIdentityTypography`, `VisualIdentityLogo`, `VisualIdentityTokens`, `VisualIdentityShorthand`, `VisualIdentityThemeBundle`
16+
17+
### Removed
18+
19+
- **`theme={ThemeConfig}`** (objet Ant Design brut sur `<ThemeProvider>` / `<DashboardApp>`) — supprimé. La prop `theme` n'accepte plus qu'un preset (`'geo2france'` | `'neutral'`). Utiliser `visualIdentity` pour toute personnalisation.
20+
- **`default_theme`** (alias exporté vers le thème geo2france) — supprimé. Utiliser `theme="geo2france"`.
21+
- **`cardStyles`** (constante de styles figés) — supprimée. Utiliser le hook `useCardStyles()` (dérivé des tokens du thème actif).
22+
23+
### Migration (3 niveaux)
24+
25+
```tsx
26+
// AVANT (n'est plus supporté — la prop theme n'accepte plus de ThemeConfig brut)
27+
<DashboardApp theme={{ token: { colorPrimary: '#FF6B35' } }} logo="/logo.svg" />
28+
29+
// APRÈS — Niveau 1 : forme courte (recommandé pour démarrer)
30+
<DashboardApp visualIdentity={{ name: 'odema', primary: '#FF6B35', logo: '/logo.svg' }} />
31+
32+
// APRÈS — Niveau 2 : light personnalisé, dark dérivé auto
33+
const myVisualIdentity = createVisualIdentity({
34+
name: 'odema',
35+
light: { colorPrimary: '#FF6B35', colorLink: '#0046AD' },
36+
logo: { src: '/logo.svg', alt: 'Odema' },
37+
});
38+
<DashboardApp visualIdentity={myVisualIdentity} />
39+
40+
// APRÈS — Niveau 3 : light + dark explicites
41+
const myVisualIdentity = createVisualIdentity({
42+
name: 'odema',
43+
light: { colorPrimary: '#FF6B35' },
44+
dark: { colorPrimary: '#FF8A5C' },
45+
logo: { src: '/logo.svg', alt: 'Odema', srcDark: '/logo-white.svg' },
46+
});
47+
<DashboardApp visualIdentity={myVisualIdentity} mode="auto" />
48+
```

0 commit comments

Comments
 (0)