Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ Le format suit [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/) et le pr
## [Unreleased]

### Added
- **Un projet en sources copiées a désormais son propre Storybook** (FSHSP-125) : `ng add @4sh/ui-kit-schematics --with-storybook` copie, à côté de chaque composant, sa story et sa page MDX, pose la configuration Storybook (`storybook/`, cibles `angular.json`, devDependencies, scripts npm) et la chaîne qui alimente la doc (`scripts/docs.config.mjs`, bloc `<ConfigTable>`). Un `npm run storybook` suffit ensuite. Jusqu'ici le projet possédait son code mais aucune doc : il lui restait un Storybook hébergé qui décrit *nos* composants, pas ses copies éditées.

Deux choses font que cette doc appartient au projet. Les tables *Theming* sont lues sur ses propres `.scss` au build, donc elles décrivent ses valeurs, rebranding compris. Et les globs couvrent tout `src/app/shared/components/**` : une story écrite à côté d'un composant maison apparaît sans toucher à la configuration.

Les imports suivent les copies, comme pour les sources — mais les extraits de code des pages ne sont pas réécrits : ce sont des exemples destinés au lecteur, pas des imports à résoudre. Les liens `parameters.design` vers notre fichier Figma sont retirés à la copie : le consommateur n'y a pas accès. Les pages transverses embarquées se limitent à *Foundations*, *Spécifications* et *Configuration* ; `Introduction`, `NpmPackage` et `Overview` parlent du kit lui-même et restent ici.

Désactivé par défaut : sans Storybook, story et MDX sont deux fichiers qui importent des packages absents. Le choix est retenu dans `ui-kit.json` et `update` s'y tient.
- **`@4sh/ui-kit-schematics` embarque son `LICENSE` et ses README** (EN + FR). Le package déclarait `Apache-2.0` sans en fournir le texte, alors que la licence (§4a) demande qu'une redistribution en joigne une copie — d'autant plus ici que ce package existe pour que son contenu soit recopié ailleurs. Sa page npmjs, jusque-là vide, redirige maintenant vers `@4sh/ui-kit` : le compagnon ne s'installe jamais directement.

### Fixed
Expand Down
32 changes: 31 additions & 1 deletion projects/ui-kit-schematics/README.fr.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ ng add @4sh/ui-kit-schematics

Une seule commande : elle pose la fondation (styles, design tokens,
`angular.json`), puis demande quels composants copier et les copie, dépendances
comprises.
comprises. La sélection se fait à la case à cocher — <kbd>espace</kbd> pour
choisir, <kbd>a</kbd> pour tout, <kbd>i</kbd> pour inverser.

```
src/app/shared/
Expand All @@ -37,9 +38,38 @@ jamais une fusion automatique.
| `ng add @4sh/ui-kit-schematics` | fondation **et** composants, d'un coup |
| `ng add @4sh/ui-kit-schematics --skip-components` | fondation seule, composants choisis plus tard |
| `ng add @4sh/ui-kit-schematics --skip-install` | ne pas lancer `npm install` (projet qui pilote son lockfile) |
| `ng add @4sh/ui-kit-schematics --with-storybook` | copier aussi la story et le MDX de chaque composant (voir ci-dessous) |
| `ng generate @4sh/ui-kit-schematics:add` | copier d'autres composants (interactif, ou `--components`, ou `--all`) |
| `ng generate @4sh/ui-kit-schematics:update` | diff des composants copiés face aux sources publiées |

### Votre propre Storybook : `--with-storybook`

Désactivé par défaut. Activé, vous obtenez un Storybook qui tourne, sur les
composants que vous avez copiés :

```bash
npm run storybook
```

Chaque composant arrive avec sa story et sa page MDX, à côté de ses sources. La
configuration atterrit dans `storybook/` — `main.js`, `preview.ts`, le thème du
manager, le sélecteur de marque, et les pages transverses *Foundations*,
*Spécifications* et *Configuration*. Les cibles `storybook` et `build-storybook`
sont ajoutées à `angular.json`, les devDependencies au `package.json`.

Deux choses font que cette doc est **la vôtre**, et non une photo de la nôtre.
Les tables *Theming* sont lues sur vos propres `.scss` au build
(`scripts/docs.config.mjs` en extrait les rôles `///`) : elles décrivent vos
valeurs, rebranding compris. Et les globs couvrent tout
`src/app/shared/components/**` : une story écrite à côté de votre propre
composant apparaît sans toucher à la configuration.

Le choix est retenu dans `ui-kit.json`, et `update` s'y tient.

Non repris : les liens `parameters.design` vers notre fichier Figma — vous ne
pouvez pas l'ouvrir, ils sont retirés à la copie. Remettez votre `node-id` si
vous en avez un.

### `@4sh/ui-kit` n'est délibérément **pas** installé

Cette voie ne met jamais le kit dans `node_modules`, et c'est précisément le but :
Expand Down
32 changes: 31 additions & 1 deletion projects/ui-kit-schematics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ ng add @4sh/ui-kit-schematics
```

One command: it lays the foundation (styles, design tokens, `angular.json`), then
asks which components to copy and copies them, dependencies included.
asks which components to copy and copies them, dependencies included. The prompt
is a checkbox list — <kbd>space</kbd> to pick, <kbd>a</kbd> for all,
<kbd>i</kbd> to invert.

```
src/app/shared/
Expand All @@ -35,9 +37,37 @@ against newer sources — never an automatic merge.
| `ng add @4sh/ui-kit-schematics` | foundation **and** components, in one go |
| `ng add @4sh/ui-kit-schematics --skip-components` | foundation only, pick components later |
| `ng add @4sh/ui-kit-schematics --skip-install` | skip `npm install` (project drives its own lockfile) |
| `ng add @4sh/ui-kit-schematics --with-storybook` | also copy each component's story and MDX (see below) |
| `ng generate @4sh/ui-kit-schematics:add` | copy more components (interactive, or `--components`, or `--all`) |
| `ng generate @4sh/ui-kit-schematics:update` | diff copied components against the published sources |

### Your own Storybook: `--with-storybook`

Off by default. Turned on, you get a working Storybook of the components you
copied:

```bash
npm run storybook
```

Each component arrives with its story and its MDX page, next to its sources. The
config lands in `storybook/` — `main.js`, `preview.ts`, the manager theme, the
brand switcher, and the shared *Foundations* / *Specifications* / *Configuration*
pages. The `storybook` and `build-storybook` targets are added to `angular.json`,
the devDependencies to `package.json`.

Two things make the doc *yours* rather than a snapshot of ours. The *Theming*
tables are read off your own `.scss` at build time (`scripts/docs.config.mjs`
collects the `///` roles), so they describe your values, rebranding included. And
the globs cover `src/app/shared/components/**` whole: a story you write next to
your own component shows up with no config change.

The choice is recorded in `ui-kit.json`, and `update` honours it.

Not carried over: the `parameters.design` links to our Figma file — you cannot
open it, so it is stripped at copy time. Put your own `node-id` back if you have
one.

### `@4sh/ui-kit` is deliberately **not** installed

This path never puts the kit in `node_modules`, and that is the point: with it
Expand Down
12 changes: 9 additions & 3 deletions projects/ui-kit-schematics/src/add/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,24 @@ export function add(options: Schema): Rule {

const kitVersion = readKitVersion();
const units = resolveDependencies(selected);
const manifest = readManifest(tree) ?? emptyManifest(kitVersion);
const existing = readManifest(tree);
// `--with-storybook` n'est passé qu'à l'installation : sur un `add` isolé,
// c'est le manifeste qui porte le choix du projet.
const withStorybook = options.withStorybook ?? existing?.storybook ?? false;
const manifest = existing ?? emptyManifest(kitVersion, withStorybook);

for (const unit of units) {
copyUnit(tree, unit, kitVersion);
copyUnit(tree, unit, kitVersion, { withStorybook });
manifest.components[unit.name] = { version: kitVersion, installedAt: today() };
}
manifest.kitVersion = kitVersion;
manifest.storybook = withStorybook;
writeManifest(tree, manifest);

const extra = units.filter((u) => !selected.includes(u.name)).map((u) => u.name);
context.logger.info(
`✔ ${units.length} unité(s) copiée(s)${extra.length ? ` (dont dépendances : ${extra.join(', ')})` : ''}.`,
`✔ ${units.length} unité(s) copiée(s)${extra.length ? ` (dont dépendances : ${extra.join(', ')})` : ''}` +
`${withStorybook ? ', story et MDX inclus' : ''}.`,
);
return tree;
};
Expand Down
3 changes: 3 additions & 0 deletions projects/ui-kit-schematics/src/add/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ export interface Schema {
components?: string[];
/** Copie tous les composants disponibles, sans prompt. */
all?: boolean;
/** Copie aussi la story et le MDX de chaque composant. Omis → on reconduit le
* choix mémorisé dans `ui-kit.json` à l'installation. */
withStorybook?: boolean;
}
4 changes: 4 additions & 0 deletions projects/ui-kit-schematics/src/add/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
"type": "boolean",
"default": false,
"description": "Copie tous les composants disponibles, sans prompt."
},
"withStorybook": {
"type": "boolean",
"description": "Copie aussi la story et le MDX de chaque composant. Omis → choix mémorisé dans ui-kit.json."
}
}
}
31 changes: 31 additions & 0 deletions projects/ui-kit-schematics/src/ng-add/files/storybook/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Configuration Storybook posée par `ng add @4sh/ui-kit-schematics
* --with-storybook`. Fichier à vous : modifiez-le librement.
*
* Les globs couvrent `components/` en entier — les composants copiés du kit
* comme les vôtres : une story écrite à côté de votre propre composant est
* ramassée sans rien changer ici.
*/
module.exports = {
stories: [
'./docs/**/*.mdx',
'../src/app/shared/components/**/*.mdx',
'../src/app/shared/components/**/*.stories.@(js|jsx|mjs|ts|tsx)',
],
addons: ['@storybook/addon-docs', '@storybook/addon-a11y', '@storybook-community/storybook-dark-mode'],
framework: {
name: '@storybook/angular',
options: {},
},
webpackFinal: async (config) => {
// Angular définit déjà `process.env.NODE_ENV` ; la garder ici la déclare
// deux fois et webpack échoue sur le conflit.
const definePlugin = config.plugins.find(
(p) => p.constructor.name === 'DefinePlugin' && p.definitions['process.env.NODE_ENV'],
);
if (definePlugin) {
delete definePlugin.definitions['process.env.NODE_ENV'];
}
return config;
},
};
56 changes: 56 additions & 0 deletions projects/ui-kit-schematics/src/ng-add/files/storybook/myTheme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* Thème du manager Storybook (barre latérale, barre d'outils) posé par
* `ng add @4sh/ui-kit-schematics --with-storybook`. Fichier à vous.
*
* Ce sont les couleurs du CHÂSSIS Storybook, pas celles de vos composants :
* elles ne peuvent pas venir des design tokens, le manager étant rendu hors de
* votre application. Mettez-y votre marque — et `brandImage` pour votre logo.
*/
import { create } from 'storybook/theming';

const brandContent = {
brandTitle: 'Design System',
brandTarget: '_self',
fontBase: '"Inter", sans-serif',
fontCode: 'monospace',
appBorderRadius: 8,
inputBorderRadius: 4,
};

export const lightTheme = create({
base: 'light',
...brandContent,
appBg: '#f6f7f9',
appContentBg: '#ffffff',
appBorderColor: '#dfe2e7',
textColor: '#111827',
textInverseColor: '#ffffff',
barTextColor: '#6b7280',
barSelectedColor: '#9747ff',
barBg: '#ffffff',
inputBg: '#ffffff',
inputBorder: '#dfe2e7',
inputTextColor: '#111827',
colorPrimary: '#9747ff',
colorSecondary: '#9747ff',
});

export const darkTheme = create({
base: 'dark',
...brandContent,
appBg: '#0f172a',
appContentBg: '#111827',
appBorderColor: '#1f2937',
textColor: '#f3f4f6',
textInverseColor: '#111827',
barTextColor: '#9ca3af',
barSelectedColor: '#ac78ff',
barBg: '#111827',
inputBg: '#1f2937',
inputBorder: '#374151',
inputTextColor: '#f6f7f9',
colorPrimary: '#9747ff',
colorSecondary: '#9747ff',
});

export default lightTheme;
83 changes: 83 additions & 0 deletions projects/ui-kit-schematics/src/ng-add/files/storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* Preview Storybook posée par `ng add @4sh/ui-kit-schematics --with-storybook`.
* Fichier à vous : modifiez-le librement.
*
* Il fait trois choses que les stories du kit supposent : brancher le thème
* clair/sombre sur `data-theme` (comme `ThemeService`), offrir le sélecteur de
* marque de la barre d'outils (comme `BrandService`), et fournir les providers
* Angular dont les stories ont besoin.
*/
import { applicationConfig, Preview } from '@storybook/angular';
import { setCompodocJson } from '@storybook/addon-docs/angular';
import { provideRouter } from '@angular/router';
import { addons } from 'storybook/preview-api';
import { lightTheme, darkTheme } from './myTheme';
import { brandGlobalTypes, withBrand, DEFAULT_BRAND } from './brand-toolbar';
// Descriptions des inputs dans l'onglet API : produites par Compodoc, que le
// builder `@storybook/angular` lance lui-même (`compodoc: true` dans angular.json).
import docJson from '../documentation.json';
// <ui-image>
import { provideUiImageAssets, UiImageAssetsMap } from '../src/app/shared/components/ui/base/ui-image/ui-image';
// `ui-image` lit ses images locales dans une map injectée : le composant ne peut
// pas deviner l'arborescence d'assets d'un projet. Remplissez
// `src/assets/assets-map.json` pour que ses stories affichent vos visuels.
import assetsMap from '../src/assets/assets-map.json';
// </ui-image>

setCompodocJson(docJson);

/** Applique le mode sombre comme le fait `ThemeService` : un attribut sur <html>. */
const syncTheme = (isDark: boolean) => {
if (typeof document === 'undefined') return;
const root = document.documentElement;
if (isDark) root.setAttribute('data-theme', 'dark');
else root.removeAttribute('data-theme');
};

const channel = addons.getChannel();
channel.on('DARK_MODE', (isDark) => {
setTimeout(() => syncTheme(isDark), 0);
});

const preview: Preview = {
initialGlobals: { brand: DEFAULT_BRAND },
globalTypes: brandGlobalTypes,
decorators: [
withBrand,
(Story, context) => {
syncTheme(context.globals['darkMode']);
return Story();
},
applicationConfig({
providers: [
// `provideRouter` : plusieurs composants du kit ont des liens
// (`ui-link`, `ui-breadcrumb`, `ui-menu`…) qui exigent un Router.
// Pas de `provideAnimations` ici : le kit anime en CSS (`ui-motion`).
// Le package `@angular/animations` est installé (le renderer Storybook
// l'exige) : si VOS composants animent avec Angular, ajoutez le
// provider, rien d'autre à installer.
provideRouter([]),
// <ui-image>
provideUiImageAssets(assetsMap as UiImageAssetsMap),
// </ui-image>
],
}),
],
parameters: {
layout: 'centered',
docs: {
story: { inline: true },
},
darkMode: {
dark: darkTheme,
light: lightTheme,
stylePreview: true,
classTarget: 'html',
darkClass: 'dark-mode',
lightClass: 'light-mode',
},
backgrounds: { disable: true },
},
};

export default preview;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* tsconfig lu par Compodoc, qui produit les descriptions de l'onglet API.
Les stories en sont exclues : ce sont des exemples, pas de l'API. */
{
"extends": "./tsconfig.json",
"exclude": ["../src/**/*.spec.ts", "../src/**/*.stories.ts"],
"include": ["../src/**/*"],
"files": ["./typings.d.ts"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/* tsconfig de la configuration Storybook (posé par `ng add
@4sh/ui-kit-schematics --with-storybook`). Il étend celui de l'application
pour compiler stories et preview avec les mêmes règles que votre code. */
{
"extends": "../tsconfig.app.json",
"compilerOptions": {
"types": ["node"],
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true
},
"exclude": ["../src/**/*.spec.ts", "../src/main.ts"],
"include": ["../src/**/*", "./preview.ts"],
"files": ["./typings.d.ts"]
}
Loading
Loading