diff --git a/docs/SHOWCASE-ROADMAP.md b/docs/SHOWCASE-ROADMAP.md new file mode 100644 index 0000000..76a43ec --- /dev/null +++ b/docs/SHOWCASE-ROADMAP.md @@ -0,0 +1,96 @@ +# Showcase roadmap + +Prioritised backlog for making this POC a stronger teaching showcase for **atomic +design + FP in the UI** — optimising for maintainability, speed, and low cognitive load +for junior *and* senior developers. + +## How to read this + +Each track below is a table of concrete items: **Item · Where · Why · Effort · Priority**. +Nothing here is built yet — pick items off the top. Priorities: **P1** = highest +teaching-value-per-effort, do first; **P2** = strengthens the showcase; **P3** = backlog. +Effort is a rough solo estimate. + +This roadmap is deliberately scoped to three tracks (Storybook-as-curriculum, FP +primitives, Enforcement depth). A fourth track — onboarding docs (GETTING-STARTED, +GLOSSARY, layer-dependency diagram, ADR index) — was identified but de-scoped for now. + +### Current strengths (what NOT to touch) + +The foundations are already exemplary and should be preserved as-is: + +- **Pure reducers / state machines** — `*.machine.ts` model illegal states away with + tagged unions; effects live in commands, never in `reduce`. +- **`RemoteData`** (`src/app/shared/application/remote-data.ts`) — `map`/`map2`/ + `map3`/`andThen`/`fromResource`/`foldRemote`, rendered via the `` molecule. +- **Parse-don't-validate value objects** (`src/app/registratie/domain/value-objects/`) — + branded types built only through a `Result`-returning parser. +- **Anti-corruption parsers** at every DTO seam (`infrastructure/*.adapter.ts`). +- **ESLint-enforced layer boundaries** (`eslint.config.mjs`): no `any`, `domain/` imports + no Angular, cross-context direction locked. +- **Storybook** — 96% story coverage, a11y addon + docs addon on, titled `Layer/Name`. +- **Token linting** (`npm run check:tokens`) blocks hardcoded colours. + +The gaps below are about *teaching reach*, not correctness. + +--- + +## Track A — Storybook as a curriculum + +Storybook is the showcase vehicle, but today it renders isolated components with no +narrative. `.storybook/main.ts` already globs `../src/**/*.mdx` and loads +`@storybook/addon-docs`, so MDX pages drop in with zero config. + +| Item | Where | Why (teaching value / cognitive load) | Effort | Prio | +|------|-------|----------------------------------------|--------|------| +| **Atomic-design primer (MDX)** | `src/docs/atomic-design.mdx` | The pyramid + the "each layer imports only below" rule, with a *live* composition chain of real components: `button` (atom) → `form-field` (molecule) → `document-upload` (organism) → `page-shell` (template). Links to the ESLint rules that enforce it. Gives newcomers the mental model before they read code. | 2–3h | P1 | +| **Design-token gallery (MDX)** | `src/docs/design-tokens.mdx` | RHC colours, the `--rhc-space-max-*` spacing scale (with resolved px), typography; plus when to use a raw `--rhc-*` token vs. an `--app-*` wrapper from `src/styles.scss`. Answers the senior's "what's the actual value of `--rhc-space-max-md`?" and ties to `check:tokens`. | 2h | P1 | +| **FP-in-the-UI primer (MDX)** | `src/docs/fp-in-ui.mdx` | A Storybook front door to the concepts already in `src/app/showcase/concepts.page.ts` and `docs/fp-tea-atomic-design.md` (RemoteData, machine, parse-don't-validate) so the component library and the FP guide cross-link instead of living apart. | 1.5h | P1 | +| **`argTypes` / controls** | high-traffic atom stories: `button`, `text-input`, `radio-group`, `status-badge`, `alert` (`src/app/shared/ui/*/*.stories.ts`) | Lets devs explore every variant from the Controls panel instead of editing story code. Fastest "play with it" path for juniors. | 1.5h | P2 | +| **Anatomy story for `async`** | `src/app/shared/ui/async/async.stories.ts` | Show all four states (Loading / Empty / Failure / Success) in one view — it teaches the exhaustive fold but doesn't currently *display* it exhaustively. | 45m | P2 | +| **`play` / interaction tests** | 2–3 stories (button disabled-during-submit; `form-field` error announced via `role="alert"`; `async` retry calls `reload()`) | Demonstrates behaviour, not just appearance, and doubles as regression cover. Feeds Track C's CI gate. | 2h | P2 | +| **Shell story** | `src/app/shared/layout/shell/shell.stories.ts` | The only component with no story; a canvas showing the persistent header/footer completes the library. | 30m | P3 | + +--- + +## Track B — FP primitives + +The pure core is excellent but under-demonstrates a few composition tools; learners +currently hand-branch and reinvent them. + +| Item | Where | Why (teaching value / cognitive load) | Effort | Prio | +|------|-------|----------------------------------------|--------|------| +| **`Result` combinators** | `src/app/shared/kernel/fp.ts` (+ new `fp.spec.ts`) | Add `map`, `mapErr`, `andThen`, `fold`, `getOrElse`. Today only direct `r.ok ? … : …` branching exists — fine for one step, painful across a pipeline. **Biggest single FP gap.** Then refactor **one** real call site to show `parse ▸ mapErr(localize) ▸ map(toDomain)`. | 1.5h | P1 | +| **Value-object specs** | `postcode.spec.ts`, `uren.spec.ts`, `big-nummer.spec.ts` next to `src/app/registratie/domain/value-objects/*.ts` | The repo's own rule is "domain must have a spec," yet only `email` does. Cover normalisation (`"1234AB"`→`"1234 AB"`), boundaries, and invalid inputs. Closing this makes the rule credible and teaches value-object testing. | 1h | P1 | +| **Property-based test example** | one spec (e.g. Postcode normalisation idempotence, or the `RemoteData.map` functor-composition law); adds `fast-check` dev-dep | Every current test is example-based. One law-based test shows how to catch edge cases you didn't enumerate. **Flag:** introduces a new dev dependency — justified because property testing is an explicit teaching goal. | 1h | P2 | +| **`NonEmptyArray` helper** | `src/app/shared/kernel/fp.ts`; use for `vraagIds` / required upload categories | Makes "at least one" a *type* rather than a runtime check — a crisp "make illegal states unrepresentable" lesson applied to collections. | 1h | P3 | +| **Effect/command testability (note)** | `src/app/registratie/application/{draft-sync,submit-*}.ts` | Direction only, not built here: inject the HTTP effect so commands are unit-testable without a full component. Documents how to close the one untested layer. | — | P3 | + +--- + +## Track C — Enforcement depth + +ESLint checks imports; several documented rules are otherwise only hoped-for. Turning +them into machine-checked guarantees is itself a teachable "fitness function" pattern. + +| Item | Where | Why (teaching value / cognitive load) | Effort | Prio | +|------|-------|----------------------------------------|--------|------| +| **Architecture-fitness test** | `src/architecture.spec.ts` + `npm run check:architecture`, wired into `.github/workflows/ci.yml` | A vitest that scans `domain/` for Angular/HTTP/RxJS imports and asserts cross-context direction. Belt-and-suspenders over ESLint, and a worked example of an executable architecture rule. | 2h | P1 | +| **a11y as a CI gate** | Storybook test-runner + axe (or vitest + axe on rendered atoms), added to `ci.yml` | The a11y addon's checks are visible in the panel but not enforced. Gating them turns "we care about a11y" into a guarantee — high value for a design-system showcase. | 2–3h | P1 | +| **Interaction tests in CI** | run Track-A `play` tests via the Storybook test-runner in `ci.yml` | Makes behavioural stories part of the pipeline, not just docs. | 1h | P2 | +| **Domain coverage gate** | vitest coverage threshold on `domain/**` | Guarantees the "domain must have a spec" rule holds as the code grows. | 45m | P3 | +| **Dependency-graph visual** | `madge` script producing an SVG/JSON of the layer graph | A generated picture makes the architecture graspable at a glance and catches accidental edges. | 45m | P3 | + +--- + +## Suggested sequencing + +Do P1 across tracks, interleaved so each shows value early: + +1. **[B]** `Result` combinators + value-object specs — small, self-contained, immediate. +2. **[A]** the three MDX pages — the visible "showcase" upgrade. +3. **[C]** architecture-fitness test + a11y-in-CI — locks the guarantees in. + +Then P2 (controls, anatomy/interaction stories, property test, interaction tests in CI). +P3 items are backlog. None of this changes runtime behaviour or the public component +API; it is additive teaching + enforcement.