Sviluppo web
Come usare Storybook senza manutenzione infinita
Una storia per stato, non per combinazione di props: `play` testa l'interazione, l'addon Vitest le esegue in CI e `automigrate` tiene su le major.
In sintesi
- Una storia per stato: le combinazioni di props si provano con i controlli, non con altre storie.
- `play` esegue il test dentro la storia e gira in CI tramite l'addon Vitest.
- Ogni storia duplica i props del componente, quindi il costo di manutenzione cresce con il loro numero.
Storybook resta utile con una regola sola: una storia per stato, non una per combinazione di props. La versione misurata è storybook 10.6.5.
Inizializzare e costruire
npx storybook@latest init
npm run storybook
npx storybook build --output-dir storybook-static
init rileva il framework, aggiunge gli script e crea la cartella .storybook. build produce file statici: si servono con un server statico, senza Node in produzione.
Una storia, uno stato
import type { Meta, StoryObj } from "@storybook/react-vite";
import { fn } from "storybook/test";
import { LoginForm } from "./LoginForm";
const meta = {
component: LoginForm,
args: { onSubmit: fn() },
parameters: { layout: "centered" },
tags: ["autodocs"],
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const Empty: Story = {};
meta.component basta a generare i controlli per le props; tags: ["autodocs"] aggiunge la pagina di documentazione. Se una storia non introduce uno stato diverso, va cancellata invece che mantenuta.
Il test di interazione dentro la storia
import { expect } from "storybook/test";
import { Empty } from "./LoginForm.stories";
export const Filled: StoryObj = {
play: async ({ args, canvas, userEvent }) => {
await userEvent.type(canvas.getByLabelText("Email"), "[email protected]");
await userEvent.type(canvas.getByLabelText("Password"), "segreta");
await userEvent.click(canvas.getByRole("button", { name: "Accedi" }));
await expect(args.onSubmit).toHaveBeenCalledWith({
email: "[email protected]",
password: "segreta",
});
},
};
play gira nel browser di Storybook e in CI tramite l'addon Vitest. expect e userEvent arrivano da storybook/test, con le stesse query di Testing Library.
Eseguire le storie come test
npm i -D @storybook/addon-vitest
npx vitest run
L'addon registra un progetto Vitest che esegue ogni storia in un browser. Il report nomina la storia, non il file, quindi un rosso indica subito quale stato si è rotto.
Aggiornare senza rompere
npx storybook@latest upgrade
npx storybook@latest automigrate
upgrade allinea tutte le dipendenze @storybook/* alla stessa major; automigrate applica le migrazioni automatiche e stampa l'elenco di quelle manuali, con il file da toccare.
Limite dichiarato
Ogni storia duplica i props del componente: se il componente cambia firma, le storie restano indietro e mostrano uno stato che non esiste più. Il build statico cresce con il numero di storie, e Autodocs raddoppia le pagine da mantenere.
Dove continuare
Tre componenti da documentare: popover-form, accordion-05 e la griglia delle icone; il resto è in components/lib e nelle 1.556 anteprime. I test di interazione si legano a Vitest in un progetto React, le query a Testing Library: cinque test. Il confronto fra cataloghi di blocchi è in 21st.dev vs shadcnblocks.
Domande frequenti
Quante storie servono per un componente?
Una per stato visibile: vuoto, caricamento, errore, con dati. Le combinazioni di props si provano con i controlli nella stessa storia, non con una storia per valore.
La play function sostituisce i test di Vitest?
No. La play function verifica l'interazione nel contesto della storia; un test di Vitest verifica il componente con mock e asserzioni isolate. Servono entrambi, su livelli diversi.
Storybook richiede un server in produzione?
No. storybook build produce HTML, JavaScript e CSS statici: si servono con nginx o un CDN, e non serve Node.
Come aggiorno una major senza rifare le storie?
Con storybook upgrade seguito da automigrate. Il secondo comando applica le modifiche automatiche e stampa quelle che richiedono una scelta manuale.
Le storie possono restare indietro rispetto al componente?
Sì, ed è il rischio principale: i props sono duplicati nel file di storia. Un tipo con satisfies Meta rende l'errore visibile in TypeScript, ma non lo impedisce.