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:
@@ -17,8 +17,8 @@ business content. Keep the machinery, replace the domain.
|
||||
`nswag.json`, `.storybook/`, `proxy.conf.json`, `.npmrc` (`legacy-peer-deps` —
|
||||
and never `npm audit fix --force`, it downgrades Angular).
|
||||
- `src/app/auth/` (fake auth shell) and `src/app/shared/infrastructure/scenario.interceptor.ts` (dev-only).
|
||||
- `docs/architecture/` ADRs 0001–0003 — the decisions still apply; amend, don't delete.
|
||||
- `CLAUDE.md`, `docs/ARCHITECTURE.md`, `docs/fp-tea-atomic-design.md` — update names/examples as contexts change.
|
||||
- `docs/reference/architecture/` ADRs 0001–0003 — the decisions still apply; amend, don't delete.
|
||||
- `CLAUDE.md`, `docs/reference/architecture/ARCHITECTURE.md`, `docs/reference/fp-tea-atomic-design.md` — update names/examples as contexts change.
|
||||
- `.claude/skills/` — these recipes are the point of the template.
|
||||
|
||||
## Strip / replace
|
||||
@@ -39,8 +39,8 @@ business content. Keep the machinery, replace the domain.
|
||||
- Branding: `public/cibg-huisstijl/` + the token bridge in `src/styles.scss` — for a
|
||||
different house style, swap the vendored CSS and re-point the `--rhc-*` bridge
|
||||
(ADR-0003 pattern: bridge, don't rewrite tokens).
|
||||
- `docs/backlog/` WPs, PRDs, and memory-specific docs — new portal, new backlog
|
||||
(keep `docs/backlog/README.md`'s WP process/template if you like the workflow).
|
||||
- `docs/project/backlog/` WPs, PRDs, and memory-specific docs — new portal, new backlog
|
||||
(keep `docs/project/backlog/README.md`'s WP process/template if you like the workflow).
|
||||
|
||||
## Verify — the GREEN gate must pass at every step
|
||||
|
||||
|
||||
@@ -31,6 +31,21 @@ const preview: Preview = {
|
||||
storySort: {
|
||||
order: [
|
||||
'Foundations',
|
||||
[
|
||||
'Overview',
|
||||
'Domain-Driven Design',
|
||||
'Atomic Design',
|
||||
'FP in the UI',
|
||||
'State Machines (TEA)',
|
||||
'RemoteData & Async',
|
||||
"Parse, don't validate",
|
||||
'Design Tokens',
|
||||
'CIBG Gap Register',
|
||||
'Accessibility',
|
||||
'Testing strategy',
|
||||
'BDD',
|
||||
'Internationalization',
|
||||
],
|
||||
'Design System',
|
||||
['Atoms', 'Molecules', 'Organisms', 'Templates', 'Devtools'],
|
||||
'Domein',
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# CLAUDE.md
|
||||
|
||||
Agent guide for this repo. The _why_ lives in `docs/ARCHITECTURE.md`,
|
||||
`docs/architecture/0001-bff-lite-decision-dtos.md`, and the learning guide
|
||||
`docs/fp-tea-atomic-design.md` (FP + The Elm Architecture + atomic design); this
|
||||
Agent guide for this repo. The _why_ lives in `docs/reference/architecture/ARCHITECTURE.md`,
|
||||
`docs/reference/architecture/0001-bff-lite-decision-dtos.md`, and the learning guide
|
||||
`docs/reference/fp-tea-atomic-design.md` (FP + The Elm Architecture + atomic design); this
|
||||
file is the _rules_. When a decision below and those docs disagree, the docs win —
|
||||
update this file.
|
||||
|
||||
@@ -12,7 +12,7 @@ signals. Auth is faked; **data and business rules are served by a minimal ASP.NE
|
||||
Core backend** (`backend/`, see its README) and consumed through an NSwag-generated
|
||||
typed client. The FE renders the backend's decisions. Reference data mimicking
|
||||
BRP/DUO (`Data/SeedData.cs`) is in-memory; applications, documents and the brief
|
||||
persist to a SQLite file via EF Core (WP-22) — `docs/backlog/WP-22-durable-persistence.md`.
|
||||
persist to a SQLite file via EF Core (WP-22) — `docs/project/backlog/WP-22-durable-persistence.md`.
|
||||
|
||||
## Commands
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ eligibility, thresholds); see **[backend/README.md](backend/README.md)**.
|
||||
> **New here:** a **branching intake questionnaire** (`/intake`) where later questions
|
||||
> appear based on earlier answers and progress survives a page reload, plus a visual
|
||||
> walkthrough of the state-management ideas. See
|
||||
> **[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)** for diagrams (atomic-design pyramid,
|
||||
> **[docs/reference/architecture/ARCHITECTURE.md](docs/reference/architecture/ARCHITECTURE.md)** for diagrams (atomic-design pyramid,
|
||||
> the dispatch→reduce→view loop, RemoteData states, and "why not just signals") and a
|
||||
> section on **connecting to a .NET backend**.
|
||||
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
|
||||
The backend that hosts the **business rules** for the BIG-register portal. The
|
||||
frontend renders the decisions this service computes; it does not recompute them
|
||||
(BFF-lite + decision DTOs — see `../docs/architecture/0001-bff-lite-decision-dtos.md`).
|
||||
(BFF-lite + decision DTOs — see `../docs/reference/architecture/0001-bff-lite-decision-dtos.md`).
|
||||
|
||||
No real BRP/DUO: the reference data they'd return (registration, person, diplomas,
|
||||
notes — `Data/SeedData.cs`) is in-memory and seeded, but the endpoints, DTOs,
|
||||
@@ -15,7 +15,7 @@ status codes and error envelope are production-shaped.
|
||||
covers it, see `docker-compose.yml`) does **not** lose data. Delete the file to
|
||||
reset demo data back to empty, the same state a fresh clone starts from. This is
|
||||
a deliberate, right-sized choice for a POC (SQLite, no external DB service) — see
|
||||
`docs/backlog/WP-22-durable-persistence.md`.
|
||||
`docs/project/backlog/WP-22-durable-persistence.md`.
|
||||
|
||||
## Run
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ public static class LetterHtml
|
||||
private static string EncLines(string s) => Enc(s).Replace("\n", "<br>");
|
||||
|
||||
// Walks up from the running assembly's own directory (NOT the process cwd, which
|
||||
// varies by how `dotnet run`/docker/tests invoke it — see docs/backlog/WP-25) until
|
||||
// varies by how `dotnet run`/docker/tests invoke it — see docs/project/backlog/WP-25) until
|
||||
// it finds `public/letter.css`. docker-compose.yml bind-mounts `./public` under the
|
||||
// api container's `/src` for exactly this walk to resolve there too.
|
||||
private static string FindLetterCss()
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# Documentation
|
||||
|
||||
Docs are split by **kind**, and kept out of each other's way:
|
||||
|
||||
- **`reference/` — information.** How the system works and why: architecture, decisions
|
||||
(ADRs), the FP/TEA/atomic learning guide, accessibility and UX reference. Stable
|
||||
knowledge, not tied to a sprint.
|
||||
- **`project/` — administration.** Planning and tracking: the work-package backlog,
|
||||
product requirements (PRDs), and the (superseded) roadmap. This is the moving,
|
||||
process-facing material.
|
||||
|
||||
Teaching material that is best read **next to the components** lives in Storybook, not
|
||||
here — see the **Foundations** section (`src/docs/*.mdx`), starting at *Foundations →
|
||||
Overview*. The `reference/` docs are the long-form source; the Foundations pages are the
|
||||
condensed, cross-linked curriculum.
|
||||
|
||||
## `reference/` — information
|
||||
|
||||
| Doc | What it is |
|
||||
| --- | --- |
|
||||
| [architecture/ARCHITECTURE.md](reference/architecture/ARCHITECTURE.md) | The architecture walkthrough: contexts/layers, state management, parse-don't-validate, the feature recipe, the .NET backend seam. |
|
||||
| [architecture/0001-bff-lite-decision-dtos.md](reference/architecture/0001-bff-lite-decision-dtos.md) | ADR — BFF-lite endpoints + decision DTOs (backend decides, FE renders). |
|
||||
| [architecture/0002-user-groups-and-bounded-contexts.md](reference/architecture/0002-user-groups-and-bounded-contexts.md) | ADR — user groups as actors; identity vs authorization. |
|
||||
| [architecture/0003-cibg-huisstijl.md](reference/architecture/0003-cibg-huisstijl.md) | ADR — adopt CIBG Huisstijl (vendored Bootstrap 5.2) + the token bridge. |
|
||||
| [fp-tea-atomic-design.md](reference/fp-tea-atomic-design.md) | Long-form learning guide: FP + The Elm Architecture + atomic design. |
|
||||
| [wcag-checklist.md](reference/wcag-checklist.md) | Manual WCAG checks automation can't catch (tab order, focus traps, reflow). |
|
||||
| [ui-ux-audit.md](reference/ui-ux-audit.md) | Early UI/UX audit against NL Design System (predates ADR-0003 — read in that light). |
|
||||
|
||||
## `project/` — administration
|
||||
|
||||
| Doc | What it is |
|
||||
| --- | --- |
|
||||
| [backlog/README.md](project/backlog/README.md) | The work-package backlog index (WP-01…WP-28) — the live tracker. |
|
||||
| [prd/0001-mijn-aanvragen-en-wizardstatus.md](project/prd/0001-mijn-aanvragen-en-wizardstatus.md) | PRD — "Mijn aanvragen": running wizards, application status, document preview. |
|
||||
| [prd/0002-attribute-based-access-control.md](project/prd/0002-attribute-based-access-control.md) | PRD — attribute-based access control in the UI. |
|
||||
| [SHOWCASE-ROADMAP.md](project/SHOWCASE-ROADMAP.md) | Superseded roadmap (absorbed into `project/backlog/`) — kept for history. |
|
||||
@@ -1,6 +1,6 @@
|
||||
# Showcase roadmap — superseded
|
||||
|
||||
**This roadmap is superseded by [`docs/backlog/`](backlog/README.md)** (2026-07-02).
|
||||
**This roadmap is superseded by [`docs/project/backlog/`](backlog/README.md)** (2026-07-02).
|
||||
|
||||
The backlog absorbs and corrects this document: its Storybook-as-curriculum track became
|
||||
WP-14/15 (+ per-invariant MDX pages in WP-05/07/08/13/17), its enforcement track became
|
||||
|
||||
@@ -5,7 +5,7 @@ design-system fidelity, DDD/FP consistency, Storybook as curriculum, and WCAG co
|
||||
with automated gates. Source: the architecture/CIBG/a11y audit of 2026-07-02 (plan:
|
||||
"Showcase hardening").
|
||||
|
||||
This backlog **supersedes `docs/SHOWCASE-ROADMAP.md`**.
|
||||
This backlog **supersedes `docs/project/SHOWCASE-ROADMAP.md`**.
|
||||
|
||||
## Session protocol
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ build", so every story added or changed by later WPs is automatically covered.
|
||||
6. Run locally against a fresh `build-storybook`; triage violations: fix trivial ones
|
||||
(labels, roles, contrast via `--rhc-*` tokens); anything structural gets the escape
|
||||
hatch + comment + WP cross-ref.
|
||||
7. Update `docs/backlog/README.md`: GREEN now includes `npm run test-storybook:ci`.
|
||||
7. Update `docs/project/backlog/README.md`: GREEN now includes `npm run test-storybook:ci`.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ Phase 0 — a pure move of wiring, no behavior change.
|
||||
|
||||
## Read first
|
||||
|
||||
- `CLAUDE.md` §1, `docs/ARCHITECTURE.md`
|
||||
- `CLAUDE.md` §1, `docs/reference/architecture/ARCHITECTURE.md`
|
||||
- `eslint.config.mjs`
|
||||
- `src/app/registratie/ui/registratie-wizard/registratie-wizard.component.ts` (lines
|
||||
~18-19: `BrpAdapter`/`parseBrpAddress`, `DuoAdapter`/`parseDuoLookup`)
|
||||
@@ -38,7 +38,7 @@ Phase 0 — a pure move of wiring, no behavior change.
|
||||
lookups move behind a registratie `application/` facade (command or store methods)
|
||||
- `src/app/herregistratie/ui/intake-wizard/intake-wizard.component.ts` — policy resource
|
||||
moves behind **new** `src/app/herregistratie/application/` (folder doesn't exist yet)
|
||||
- `docs/ARCHITECTURE.md` — fix "three contexts, four layers" → six contexts
|
||||
- `docs/reference/architecture/ARCHITECTURE.md` — fix "three contexts, four layers" → six contexts
|
||||
(shared, auth, registratie, herregistratie, brief, showcase), five layers (+ contracts);
|
||||
add the showcase sanction
|
||||
- `CLAUDE.md` §1 — add `brief` to the context list; note the showcase sanction
|
||||
|
||||
@@ -10,7 +10,7 @@ principle (every response through a hand-written `parse*` returning `Result`).
|
||||
|
||||
## Read first
|
||||
|
||||
- `CLAUDE.md` §3 + §4; `docs/architecture/0001-bff-lite-decision-dtos.md`
|
||||
- `CLAUDE.md` §3 + §4; `docs/reference/architecture/0001-bff-lite-decision-dtos.md`
|
||||
- `src/app/registratie/infrastructure/applications.adapter.ts` (+ its spec — the pattern
|
||||
to copy)
|
||||
- The three offenders below
|
||||
|
||||
@@ -5,7 +5,7 @@ Phase: 1 — FP/DDD core
|
||||
|
||||
## Why
|
||||
|
||||
Docs (`docs/fp-tea-atomic-design.md`, ARCHITECTURE §2c) teach `createStore` as THE
|
||||
Docs (`docs/reference/fp-tea-atomic-design.md`, ARCHITECTURE §2c) teach `createStore` as THE
|
||||
wiring, but the wizard pages hand-wire `signal(model)` + local `dispatch()` — juniors see
|
||||
two idioms and copy the wrong one. Machine naming also drifts:
|
||||
`change-request.machine.ts` exports bare `State`/`Msg`; `upload.machine.ts` exports
|
||||
@@ -14,7 +14,7 @@ two idioms and copy the wrong one. Machine naming also drifts:
|
||||
## Read first
|
||||
|
||||
- `src/app/shared/application/store.ts` (`createStore`) + its spec
|
||||
- `docs/fp-tea-atomic-design.md` Part on TEA-in-Angular
|
||||
- `docs/reference/fp-tea-atomic-design.md` Part on TEA-in-Angular
|
||||
- All `*.machine.ts` files (6) and their UI wiring sites (wizard components/pages)
|
||||
|
||||
## Decisions (pre-made, don't relitigate)
|
||||
|
||||
@@ -59,7 +59,7 @@ no spec despite "domain and pure logic must have a spec" (CLAUDE.md §5).
|
||||
(`draft-sync.spec.ts`, `submit-change-request.spec.ts`).
|
||||
- [x] `map3` removed (found in `shared/application/remote-data.ts`, not
|
||||
`shared/kernel/fp.ts` as the WP text guessed — updated the three docs that
|
||||
mentioned it: CLAUDE.md, `docs/ARCHITECTURE.md`, `remote-data.mdx`). The
|
||||
mentioned it: CLAUDE.md, `docs/reference/architecture/ARCHITECTURE.md`, `remote-data.mdx`). The
|
||||
`variant` input on `confirmation.component.ts` no longer exists — already
|
||||
cleaned up before this WP ran; nothing to do.
|
||||
- [x] CLAUDE.md rule added (`Conventions` — DatePipe in templates, `formatDatumNl` in
|
||||
|
||||
@@ -35,7 +35,7 @@ the list-family rationale to document.
|
||||
|
||||
## Read first
|
||||
|
||||
- `docs/architecture/0003-*.md` (ADR-0003 — the token-bridge rationale this extends)
|
||||
- `docs/reference/architecture/0003-*.md` (ADR-0003 — the token-bridge rationale this extends)
|
||||
- The audit's gap list (below)
|
||||
|
||||
## Decisions (pre-made, don't relitigate)
|
||||
|
||||
@@ -41,7 +41,7 @@ Three app-level gaps close the WCAG story:
|
||||
- `src/app/app.config.ts` + a new small `shared/` focus-on-navigation service
|
||||
- `eslint.config.mjs`, `package.json` (angular-eslint)
|
||||
- Any template the new lint rules flag
|
||||
- New `docs/wcag-checklist.md`
|
||||
- New `docs/reference/wcag-checklist.md`
|
||||
- New `src/docs/a11y.mdx` — title `Foundations/Accessibility`
|
||||
|
||||
## Steps
|
||||
@@ -50,7 +50,7 @@ Three app-level gaps close the WCAG story:
|
||||
focus lands on the new page's heading).
|
||||
2. Add angular-eslint; enable the template rules on inline templates; plant a bad
|
||||
pattern, see it fail, remove it; fix real hits.
|
||||
3. `docs/wcag-checklist.md`: manual checks per page (dashboard, wizards, brief, login) —
|
||||
3. `docs/reference/wcag-checklist.md`: manual checks per page (dashboard, wizards, brief, login) —
|
||||
keyboard walk & focus order, no traps, 200% zoom/reflow, NVDA or VoiceOver pass,
|
||||
visible focus, error announcement; status columns (page × check).
|
||||
4. `src/docs/a11y.mdx`: the layered approach — axe gate (WP-01) + template lint + play
|
||||
@@ -75,7 +75,7 @@ Three app-level gaps close the WCAG story:
|
||||
setup. Less code to hand-maintain, same coverage plus more.
|
||||
- The dashboard's checklist pass surfaced a **real bug**: `aanvraag-block`'s warning
|
||||
`app-alert` (two `app-button` actions) overflows the viewport at 320px — its
|
||||
`.feedback` flex row doesn't wrap. Documented in `docs/wcag-checklist.md` with the
|
||||
`.feedback` flex row doesn't wrap. Documented in `docs/reference/wcag-checklist.md` with the
|
||||
root cause, **not fixed** — fixing live component CSS found via the checklist is the
|
||||
"full manual audit" scope this WP's Out-of-scope section explicitly defers, not this
|
||||
WP's own deliverable. Flagged here so it isn't lost.
|
||||
|
||||
@@ -30,9 +30,9 @@ a real foundation to extend.
|
||||
|
||||
## Read first
|
||||
|
||||
- `docs/architecture/0002-user-groups-and-bounded-contexts.md` (the `Principal`
|
||||
- `docs/reference/architecture/0002-user-groups-and-bounded-contexts.md` (the `Principal`
|
||||
union, identity-vs-authorization split — see the deviation noted below)
|
||||
- `docs/prd/0002-attribute-based-access-control.md` §5a, §6, §7, §9-P1
|
||||
- `docs/project/prd/0002-attribute-based-access-control.md` §5a, §6, §7, §9-P1
|
||||
- `backend/src/BigRegister.Api/Domain/Authorization/Authz.cs` (new — the single
|
||||
authorization helper)
|
||||
- `backend/src/BigRegister.Api/Data/BriefStore.cs` (`Review` — now delegates its
|
||||
|
||||
@@ -31,7 +31,7 @@ speculatively.
|
||||
`static Dictionary` + `lock`
|
||||
- `backend/src/BigRegister.Api/Data/SeedData.cs` (current in-memory seed — becomes
|
||||
a first-run DB seed)
|
||||
- `docs/architecture/0001-bff-lite-decision-dtos.md` (confirm this WP doesn't touch
|
||||
- `docs/reference/architecture/0001-bff-lite-decision-dtos.md` (confirm this WP doesn't touch
|
||||
the decision-DTO contracts — persistence is purely behind the existing store
|
||||
interfaces)
|
||||
|
||||
|
||||
@@ -14,11 +14,11 @@ WP-25, editor WP-26) reads what this WP serves.
|
||||
|
||||
## Read first
|
||||
|
||||
- `docs/prd` — the Brief v2 PRD §2a/§3 (two axes, OrgTemplate model, invariants)
|
||||
- `docs/project/prd` — the Brief v2 PRD §2a/§3 (two axes, OrgTemplate model, invariants)
|
||||
- `backend/src/BigRegister.Api/Data/BriefStore.cs` (store idiom + `BriefSeed`)
|
||||
- `backend/src/BigRegister.Api/Domain/Authorization/Authz.cs` (emit+enforce single source)
|
||||
- `backend/src/BigRegister.Api/Data/AppDbContext.cs` (JSON-column precedent, WP-22)
|
||||
- `docs/backlog/WP-18-abac-capability-spine.md` (how the capability spine works)
|
||||
- `docs/project/backlog/WP-18-abac-capability-spine.md` (how the capability spine works)
|
||||
|
||||
## Decisions (pre-made, don't relitigate)
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ domain model, `brief.machine.ts`, and every `BriefMsg` stay byte-identical.
|
||||
- PRD Brief v2 §2b (fidelity note), §4, §10; the sample `voorbeeldbrief-inschrijving.pdf`
|
||||
- `src/app/brief/ui/letter-composer/letter-composer.component.ts` (the `canEdit` pivot)
|
||||
- `src/app/brief/ui/letter-preview/letter-preview.component.ts` (rendering that migrates in)
|
||||
- `docs/backlog/WP-23-org-template-backend.md` (the `orgTemplate` on `BriefViewDto`)
|
||||
- `docs/project/backlog/WP-23-org-template-backend.md` (the `orgTemplate` on `BriefViewDto`)
|
||||
|
||||
## Decisions (pre-made, don't relitigate)
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ same composition is archived with the brief, making sent letters immutable.
|
||||
|
||||
## Read first
|
||||
|
||||
- PRD Brief v2 §2b, §8; `docs/backlog/WP-24-letter-canvas.md` (the `letter.css` contract)
|
||||
- PRD Brief v2 §2b, §8; `docs/project/backlog/WP-24-letter-canvas.md` (the `letter.css` contract)
|
||||
- `backend/src/BigRegister.Api/Program.cs` — upload `content` endpoint (binary house
|
||||
pattern: `.ExcludeFromDescription()` + hand-written FE fetch)
|
||||
- `src/app/shared/upload/upload.adapter.ts` (hand-written transport precedent)
|
||||
|
||||
@@ -12,7 +12,7 @@ content a read-only sample). PRD Brief v2 §5, §7h.
|
||||
|
||||
## Read first
|
||||
|
||||
- PRD Brief v2 §5, §7h; `docs/backlog/WP-23/24/25` (endpoints, canvas, proefbrief)
|
||||
- PRD Brief v2 §5, §7h; `docs/project/backlog/WP-23/24/25` (endpoints, canvas, proefbrief)
|
||||
- `src/app/shared/application/access.store.ts` (`can('orgtemplate:edit')`)
|
||||
- `.claude/skills/form-machine` — the house form idiom this editor follows
|
||||
- `src/app/shared/ui/upload/single-upload` (logo upload reuse)
|
||||
|
||||
@@ -22,7 +22,7 @@ that keep CLAUDE.md and the backlog truthful.
|
||||
already covers all `/api/` calls, the new endpoints included), and
|
||||
`POST /brief/reset`. The demo script documents the mapping; no new interceptor
|
||||
cases, no scenario code.
|
||||
- Demo script lives at `docs/prd/0003-brief-v2-demo-script.md` and follows the §6
|
||||
- Demo script lives at `docs/project/prd/0003-brief-v2-demo-script.md` and follows the §6
|
||||
choreography (compose → preview → switch sub-org seed → "two axes, one render").
|
||||
- One e2e spec, not a suite: drafter composes on canvas → submit → approve → send
|
||||
pins the org-template version; admin publishes → drafter canvas reflects it.
|
||||
@@ -31,10 +31,10 @@ that keep CLAUDE.md and the backlog truthful.
|
||||
|
||||
## Files
|
||||
|
||||
- `docs/prd/0003-brief-v2-demo-script.md` (new)
|
||||
- `docs/project/prd/0003-brief-v2-demo-script.md` (new)
|
||||
- `e2e/brief-v2.spec.ts` (new)
|
||||
- story gap-fill where WP-24..27 left holes
|
||||
- `docs/backlog/README.md` (statuses), `CLAUDE.md` (roles/routes touch-up)
|
||||
- `docs/project/backlog/README.md` (statuses), `CLAUDE.md` (roles/routes touch-up)
|
||||
|
||||
## Steps
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ professional logs in, sees their registration, and can apply for
|
||||
re-registration — "herregistratie").
|
||||
|
||||
> New to functional programming or The Elm Architecture? Start with the progressive
|
||||
> learning guide [`fp-tea-atomic-design.md`](./fp-tea-atomic-design.md), which teaches
|
||||
> learning guide [`fp-tea-atomic-design.md`](../fp-tea-atomic-design.md), which teaches
|
||||
> the concepts (with Elm ↔ this-app examples) and the recipes; this document is the
|
||||
> reference deep-dive it points back to.
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ feature." A senior can skim Parts 1–4 and jump to **Part 5** (FP × atomic des
|
||||
defined in plain words on first use and again in the **glossary** (Part 8).
|
||||
|
||||
This guide is the _teaching_ layer. For the reference deep-dives it points to
|
||||
[`ARCHITECTURE.md`](./ARCHITECTURE.md) and
|
||||
[`ARCHITECTURE.md`](./architecture/ARCHITECTURE.md) and
|
||||
[ADR-0001](./architecture/0001-bff-lite-decision-dtos.md) rather than repeating them.
|
||||
Every code snippet below is real code from this repo, with its file path.
|
||||
|
||||
@@ -127,7 +127,7 @@ Two kinds of type do most of the work:
|
||||
|
||||
The decisive move is choosing types so that **illegal states can't be written down**.
|
||||
Compare three booleans (2³ = 8 combinations, most nonsense) with one union of the 4 real
|
||||
states — see [`ARCHITECTURE.md` §2a](./ARCHITECTURE.md#2a-remotedata--one-value-instead-of-three-booleans)
|
||||
states — see [`ARCHITECTURE.md` §2a](./architecture/ARCHITECTURE.md#2a-remotedata--one-value-instead-of-three-booleans)
|
||||
for the full `RemoteData` treatment and diagram. The wizard's own Model is the same
|
||||
idea (`herregistratie.machine.ts`):
|
||||
|
||||
@@ -216,7 +216,7 @@ This app implements TEA with Angular **signals**. There is no extra state librar
|
||||
important shape difference from textbook Elm: **state is per-wizard, not one global
|
||||
Model** — each flow (`herregistratie`, `intake`, `registratie`) has its own little
|
||||
store. Cross-page state that _must_ be shared lives in one root singleton
|
||||
(`BigProfileStore`, see [`ARCHITECTURE.md` §2e](./ARCHITECTURE.md#2e-optimistic-update--rollback-and-shared-state-across-pages)).
|
||||
(`BigProfileStore`, see [`ARCHITECTURE.md` §2e](./architecture/ARCHITECTURE.md#2e-optimistic-update--rollback-and-shared-state-across-pages)).
|
||||
|
||||
### 4a. The store — TEA's runtime in ~10 lines
|
||||
|
||||
@@ -424,7 +424,7 @@ Molecules compose atoms; organisms compose molecules — exactly like composing
|
||||
functions, where the composite is still pure. `address-fields` is pure because the
|
||||
`form-field` and `text-input` it's built from are pure. Each atomic level only uses the
|
||||
level(s) below it (see the hierarchy diagram in
|
||||
[`ARCHITECTURE.md` §1](./ARCHITECTURE.md#1-the-big-picture-three-contexts-four-layers)).
|
||||
[`ARCHITECTURE.md` §1](./architecture/ARCHITECTURE.md#1-the-big-picture-three-contexts-four-layers)).
|
||||
|
||||
### 5c. Pages / containers are the TEA runtime (the shell)
|
||||
|
||||
@@ -474,7 +474,7 @@ Each property maps to a tangible benefit you can point at in this repo:
|
||||
- **Illegal states won't compile.** `Submitting` carries `Valid` data and has no `errors`
|
||||
field, so "submit with errors showing" is unwritable. A whole bug class disappears
|
||||
before runtime — contrast the 8-state boolean soup in
|
||||
[`ARCHITECTURE.md` §2a](./ARCHITECTURE.md#2a-remotedata--one-value-instead-of-three-booleans).
|
||||
[`ARCHITECTURE.md` §2a](./architecture/ARCHITECTURE.md#2a-remotedata--one-value-instead-of-three-booleans).
|
||||
|
||||
- **Pure presentational components.** `address-fields` is tested by inputs → DOM and
|
||||
reused in two call-sites (the registratie wizard and the change-request form) with no
|
||||
@@ -500,7 +500,7 @@ Each recipe follows the existing pattern and naming, and ends with the same remi
|
||||
### Recipe A — Add an atomic component (atom / molecule / organism)
|
||||
|
||||
**When:** you genuinely need a new building block (not a one-off; reuse must earn it —
|
||||
see [CLAUDE.md §2](../CLAUDE.md)).
|
||||
see [CLAUDE.md §2](../../CLAUDE.md)).
|
||||
|
||||
**Where:** `shared/ui/` if generic; a context's `ui/` if domain-specific. Pick the level
|
||||
by composition: composes nothing → **atom**; composes atoms → **molecule**; composes
|
||||
@@ -634,7 +634,7 @@ _This is the same loop, again — now nested inside the wizard._
|
||||
|
||||
---
|
||||
|
||||
_See also:_ [`ARCHITECTURE.md`](./ARCHITECTURE.md) (reference deep-dive on RemoteData,
|
||||
_See also:_ [`ARCHITECTURE.md`](./architecture/ARCHITECTURE.md) (reference deep-dive on RemoteData,
|
||||
the store, parse-don't-validate, and the .NET backend seam) and
|
||||
[ADR-0001](./architecture/0001-bff-lite-decision-dtos.md) (the BFF-lite + decision-DTO
|
||||
decision). Live demo: `/concepts` in the running app.
|
||||
|
||||
+1473
-1026
File diff suppressed because one or more lines are too long
@@ -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