docs: reorganize into project/ + reference/, expand Storybook Foundations
Move working docs (backlog, prd, roadmap) under docs/project/ and durable docs (architecture ADRs, guides, audits) under docs/reference/; add a docs/README.md index. Update every path reference in code comments, CLAUDE.md, READMEs, and the new-ssp skill. Expand the Storybook Foundations curriculum (Overview, BDD, i18n; rename Layers→Domain-Driven Design) and reorder the sidebar. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -21,7 +21,7 @@ type Err = Error | undefined;
|
||||
* The dashboard data now comes from ONE screen-shaped ("BFF-lite") call that
|
||||
* returns registration + person + server-computed `decisions`. One request → one
|
||||
* consistent snapshot, instead of stitching three independently loading/erroring
|
||||
* resources together client-side. See docs/architecture/0001-bff-lite-decision-dtos.md.
|
||||
* resources together client-side. See docs/reference/architecture/0001-bff-lite-decision-dtos.md.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class BigProfileStore {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* In production this is GENERATED from the OpenAPI/TypeSpec spec and served by our
|
||||
* own backend, which talks to the BRP behind an adapter. The frontend never sees
|
||||
* the BRP's own wire format. See docs/architecture/0001-bff-lite-decision-dtos.md.
|
||||
* the BRP's own wire format. See docs/reference/architecture/0001-bff-lite-decision-dtos.md.
|
||||
*
|
||||
* "Geen adres bekend" is a first-class outcome (`gevonden: false`), not an error —
|
||||
* the wizard falls back to manual entry (PRD §7). Slice 1 ships only the happy
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* In production these types are GENERATED from the OpenAPI/TypeSpec spec (one
|
||||
* source of truth for both sides), and the `decisions` block is computed BY THE
|
||||
* BACKEND — never recomputed on the client. The frontend renders decisions; it
|
||||
* does not own the rules. See docs/architecture/0001-bff-lite-decision-dtos.md.
|
||||
* does not own the rules. See docs/reference/architecture/0001-bff-lite-decision-dtos.md.
|
||||
*
|
||||
* One screen-shaped call replaces the previous three (BIG-register + BRP + …),
|
||||
* so the page always sees one consistent snapshot instead of three independently
|
||||
|
||||
@@ -25,7 +25,7 @@ const meta: Meta<AanvraagBlockComponent> = {
|
||||
parameters: {
|
||||
// Structural: app-aanvraag-block's host sits between the keuzelijst <ul> and its <li>
|
||||
// — axe's list/listitem rule needs them adjacent regardless of `display:contents`.
|
||||
// WP-11 (CIBG markup fidelity) reworks this markup; see docs/backlog/WP-11-markup-fidelity.md.
|
||||
// WP-11 (CIBG markup fidelity) reworks this markup; see docs/project/backlog/WP-11-markup-fidelity.md.
|
||||
a11y: { disable: true },
|
||||
},
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ const meta: Meta<ChoiceLinkComponent> = {
|
||||
parameters: {
|
||||
// Structural: app-choice-link's host sits between the <ul> and its <li> — axe's
|
||||
// list/listitem rule requires them adjacent regardless of `display:contents`.
|
||||
// WP-11 (CIBG markup fidelity) reworks this markup; see docs/backlog/WP-11-markup-fidelity.md.
|
||||
// WP-11 (CIBG markup fidelity) reworks this markup; see docs/project/backlog/WP-11-markup-fidelity.md.
|
||||
a11y: { disable: true },
|
||||
},
|
||||
};
|
||||
|
||||
@@ -21,7 +21,7 @@ const meta: Meta<ChoiceListComponent> = {
|
||||
}),
|
||||
parameters: {
|
||||
// Structural: app-choice-link's host sits between the <ul> and its <li> —
|
||||
// fixed by the WP-11 markup rework. See docs/backlog/WP-11-markup-fidelity.md.
|
||||
// fixed by the WP-11 markup rework. See docs/project/backlog/WP-11-markup-fidelity.md.
|
||||
a11y: { disable: true },
|
||||
},
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ const meta: Meta<TaskListComponent> = {
|
||||
parameters: {
|
||||
// Structural: app-choice-link's host sits between the keuzelijst <ul> and its <li>
|
||||
// — axe's list/listitem rule needs them adjacent regardless of `display:contents`.
|
||||
// WP-11 (CIBG markup fidelity) reworks this markup; see docs/backlog/WP-11-markup-fidelity.md.
|
||||
// WP-11 (CIBG markup fidelity) reworks this markup; see docs/project/backlog/WP-11-markup-fidelity.md.
|
||||
a11y: { disable: true },
|
||||
},
|
||||
};
|
||||
|
||||
+2
-2
@@ -30,7 +30,7 @@ what the ones below/above it can't.
|
||||
`-desc`/`-error` in the right order; `alert.stories.ts` asserts `role="alert"` for
|
||||
errors vs `role="status"` for info/ok/warning. These run as part of the same
|
||||
`test-storybook:ci` gate as the axe checks, so a regression fails CI, not just a panel.
|
||||
4. **Manual WCAG checklist** (`docs/wcag-checklist.md`) — what none of the above can see:
|
||||
4. **Manual WCAG checklist** (`docs/reference/wcag-checklist.md`) — what none of the above can see:
|
||||
tab order across a whole page, focus traps, 200%-zoom reflow, and how a real screen
|
||||
reader narrates a flow. A living per-page checklist, not a one-time audit — it already
|
||||
caught a real bug (a dashboard alert overflowing at 320px) that no automated layer here
|
||||
@@ -65,5 +65,5 @@ the same way (`withInMemoryScrolling`), both wired once in `app.config.ts` — n
|
||||
fails it on a real axe violation. Both can be locally disabled — the lint rule via a
|
||||
normal ESLint disable comment, axe via `parameters: { a11y: { disable: true } }` — but
|
||||
only with a comment naming _why_ and a cross-reference to the WP expected to remove the
|
||||
skip (see `docs/backlog/WP-13-cibg-gap-register.md`'s marker convention, reused here).
|
||||
skip (see `docs/project/backlog/WP-13-cibg-gap-register.md`'s marker convention, reused here).
|
||||
Grep `a11y: { disable: true }` in `*.stories.ts` for the current list.
|
||||
|
||||
@@ -106,7 +106,7 @@ Periodically we audit for near-duplicate blocks. Some collapse into one; a few *
|
||||
similar but earn their separation. This table records the "don't merge these" verdicts so
|
||||
the next person doesn't spend an afternoon re-deciding. (Deliberate CIBG-specific deviations
|
||||
live in [CIBG gaps](?path=/docs/foundations-cibg-gaps--docs); the FE⇄DS "same shape, different
|
||||
context" cases in [Layers](?path=/docs/foundations-layers--docs).)
|
||||
context" cases in [Domain-driven design](?path=/docs/foundations-domain-driven-design--docs).)
|
||||
|
||||
| Pair | Why kept separate |
|
||||
| --- | --- |
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import { Meta } from '@storybook/addon-docs/blocks';
|
||||
|
||||
<Meta title="Foundations/BDD" />
|
||||
|
||||
# Behaviour-driven tests
|
||||
|
||||
Tests here read as **specifications of behaviour**, not checks of implementation. A test
|
||||
says what the system *does* — in the domain's own words — so a failing test names a broken
|
||||
behaviour, and the suite doubles as living documentation. This is the BDD half of the
|
||||
[Testing strategy](?path=/docs/foundations-testing-strategy--docs) (which owns *what to
|
||||
test, by layer*); BDD owns *how each test is phrased and scoped*.
|
||||
|
||||
## Three rules
|
||||
|
||||
### 1. `describe` = the subject, `it` = one observable behaviour
|
||||
|
||||
The `describe()` block names the unit under test; each `it()` states a single behaviour in
|
||||
**declarative present tense** — the implicit subject is "it". No `should`, no
|
||||
Given/When/Then ceremony: present-tense declaration already reads as a spec.
|
||||
|
||||
```ts
|
||||
describe('parsePostcode', () => {
|
||||
it('normalises to "1234 AB" (uppercase, single space, trimmed)', () => { … });
|
||||
it('rejects malformed input', () => { … });
|
||||
});
|
||||
```
|
||||
|
||||
Read top-to-bottom it *is* the spec: "parsePostcode — normalises to 1234 AB; rejects
|
||||
malformed input."
|
||||
|
||||
### 2. One behaviour per test
|
||||
|
||||
A test asserts **one behaviour**, not one `expect()`. Several assertions that pin down the
|
||||
*same* behaviour belong together; assertions about *different* behaviours belong apart.
|
||||
|
||||
| Keep together (one behaviour) | Split apart (separate behaviours) |
|
||||
| --- | --- |
|
||||
| A `Result`'s `.ok` then its `.value` | The `ok` branch **and** the `err` branch of a transition |
|
||||
| A whole-object `toEqual` | An invalid-input case **and** a valid-input case |
|
||||
| A loop asserting one rule over many inputs | Two independent state transitions |
|
||||
| A truth-table (`draft` → true, `approver` → false) of one rule | An authorization check **and** a rendering check |
|
||||
|
||||
A title that needs `/`, `;`, "then" or "and" to join two behaviours is the smell — split it,
|
||||
and each half gets its own present-tense name.
|
||||
|
||||
### 3. Speak the ubiquitous language (the DDD tie-in)
|
||||
|
||||
Test names use the **domain vocabulary**, not technical jargon — the same words as the
|
||||
[bounded contexts](?path=/docs/foundations-domain-driven-design--docs): a *behandelaar*
|
||||
drafts, a *beoordelaar* approves, a *herregistratie* is *ingediend*. The test name is
|
||||
readable by someone who knows the domain but not the code.
|
||||
|
||||
```ts
|
||||
it('drafter cannot approve or reject even when submitted', …);
|
||||
it('confirmed dutch proficiency requires taalvaardigheid proof', …);
|
||||
```
|
||||
|
||||
## How it fits TDD & DDD
|
||||
|
||||
- **TDD** — the loop is red → green → refactor: write the behaviour as a failing `it`, make
|
||||
it pass, then clean up. Because tests describe behaviour (not internals), a refactor that
|
||||
preserves behaviour keeps them green. Pure domain logic is tested directly — no `TestBed`
|
||||
(see [Testing strategy](?path=/docs/foundations-testing-strategy--docs)).
|
||||
- **DDD** — behaviour is expressed in the ubiquitous language, so the spec and the code
|
||||
share one vocabulary. Domain rules (reducers, value-object parsers, policies) are the
|
||||
richest specs; the wire boundary is tested as "rejects malformed input", the UI as
|
||||
Storybook stories.
|
||||
|
||||
## Where to look
|
||||
|
||||
Canonical behaviour specs in the repo: `registratie/domain/value-objects/postcode.spec.ts`
|
||||
(parser behaviour), `registratie/domain/registratie-wizard.machine.spec.ts` (one transition
|
||||
per test), and backend `AuthzTests.cs` (rule truth-tables). The
|
||||
[Testing strategy](?path=/docs/foundations-testing-strategy--docs) page maps which layer
|
||||
gets which kind of test.
|
||||
@@ -7,7 +7,7 @@ import * as AsyncStories from '../app/shared/ui/async/async.stories';
|
||||
|
||||
The components in this library are the _view_. Behind them, three small functional tools do
|
||||
the heavy lifting — all so that **illegal states can't be represented**. This page is the
|
||||
Storybook front door; the full narrative lives in `docs/fp-tea-atomic-design.md`, and a
|
||||
Storybook front door; the full narrative lives in `docs/reference/fp-tea-atomic-design.md`, and a
|
||||
side-by-side "before/after" runs at the app's **`/concepts`** route.
|
||||
|
||||
## 1. `RemoteData<E,T>` — async has four states, not a boolean soup
|
||||
|
||||
+7
-5
@@ -1,12 +1,14 @@
|
||||
import { Meta } from '@storybook/addon-docs/blocks';
|
||||
|
||||
<Meta title="Foundations/Layers & Contexts" />
|
||||
<Meta title="Foundations/Domain-Driven Design" />
|
||||
|
||||
# Layers & contexts
|
||||
# Domain-driven design: bounded contexts & layers
|
||||
|
||||
This sidebar is laid out to **be** the architecture, not just document it: **Foundations**
|
||||
(this curriculum) → **Design System** (reusable, domain-free) → **Domein** (the six DDD
|
||||
contexts). If a component lives under a context's `ui/`, it's in Domein; everything else
|
||||
This project is **domain-driven**: the code is organised first by **bounded context**
|
||||
(a business capability with its own language) and then by **layer** inside each context,
|
||||
with dependencies pointing inward. The Storybook sidebar is laid out to **be** that
|
||||
architecture, not just document it: **Foundations** (this curriculum) → **Design System**
|
||||
(reusable, domain-free) → **Domein** (the six DDD contexts). If a component lives under a context's `ui/`, it's in Domein; everything else
|
||||
in `shared/ui`/`shared/layout` is Design System. See [Atomic design](?path=/docs/foundations-atomic-design--docs)
|
||||
for the Atoms → Molecules → Organisms → Templates ladder inside Design System.
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Meta } from '@storybook/addon-docs/blocks';
|
||||
|
||||
<Meta title="Foundations/Overview" />
|
||||
|
||||
# Foundations
|
||||
|
||||
The **why** behind this codebase, as a short curriculum. Each page is a condensed,
|
||||
cross-linked take on one idea; the long-form source lives in `docs/reference/`
|
||||
(see the repo's `docs/README.md`). Read them in roughly this order.
|
||||
|
||||
## Architecture & domain
|
||||
|
||||
- [Domain-driven design](?path=/docs/foundations-domain-driven-design--docs) — bounded
|
||||
contexts + layers, dependencies pointing inward (the folder structure *is* the architecture).
|
||||
- [Atomic design](?path=/docs/foundations-atomic-design--docs) — Atoms → Molecules →
|
||||
Organisms → Templates; a new page is composition, not new building blocks.
|
||||
|
||||
## Functional core
|
||||
|
||||
- [FP in the UI](?path=/docs/foundations-fp-in-the-ui--docs) — the three functional tools behind the view.
|
||||
- [State machines (TEA)](?path=/docs/foundations-state-machines-tea--docs) — every form/wizard as Model → Msg → pure `reduce`.
|
||||
- [RemoteData & Async](?path=/docs/foundations-remotedata-async--docs) — the four async states as one value.
|
||||
- [Parse, don't validate](?path=/docs/foundations-parse-dont-validate--docs) — narrow untrusted `unknown` at the boundary into domain types.
|
||||
|
||||
## Design system
|
||||
|
||||
- [Design tokens](?path=/docs/foundations-design-tokens--docs) — semantic `--rhc-*` tokens; no hand-written colours.
|
||||
- [CIBG gap register](?path=/docs/foundations-cibg-gap-register--docs) — where we diverge from the CIBG Huisstijl (ADR-0003).
|
||||
|
||||
## Quality & process
|
||||
|
||||
- [Accessibility](?path=/docs/foundations-accessibility--docs) — four layered a11y tools, each catching a different bug class.
|
||||
- [Testing strategy](?path=/docs/foundations-testing-strategy--docs) — what to test, by layer grain.
|
||||
- [BDD](?path=/docs/foundations-bdd--docs) — how each test is phrased and scoped: one behaviour, in the domain's language.
|
||||
- [Internationalization](?path=/docs/foundations-internationalization--docs) — `$localize` for every user-visible string; the locale seam.
|
||||
@@ -6,7 +6,9 @@ import { Meta } from '@storybook/addon-docs/blocks';
|
||||
|
||||
Tests follow the same grain as the architecture: **push the logic down to where it's pure,
|
||||
test it there directly, and keep the layers above thin.** No single tool covers everything,
|
||||
so each layer gets the cheapest test that catches its class of bug.
|
||||
so each layer gets the cheapest test that catches its class of bug. This page owns *what to
|
||||
test, by layer*; how each test is **phrased and scoped** — one behaviour, in the domain's
|
||||
language — is [BDD](?path=/docs/foundations-bdd--docs).
|
||||
|
||||
## What gets tested where
|
||||
|
||||
|
||||
Reference in New Issue
Block a user