diff --git a/apps/ssp/src/app/overzicht/ui/wat-wilt-u-doen.section.stories.ts b/apps/ssp/src/app/overzicht/ui/wat-wilt-u-doen.section.stories.ts new file mode 100644 index 0000000..a458e7b --- /dev/null +++ b/apps/ssp/src/app/overzicht/ui/wat-wilt-u-doen.section.stories.ts @@ -0,0 +1,28 @@ +import type { Meta, StoryObj } from '@storybook/angular'; +import { applicationConfig } from '@storybook/angular'; +import { provideRouter } from '@angular/router'; +import { WatWiltUDoenSection } from './wat-wilt-u-doen.section'; +import { FeatureFlagStore } from '@shared/application/feature-flags.store'; + +const meta: Meta = { + title: 'Domein/Overzicht/Wat Wilt U Doen', + component: WatWiltUDoenSection, + decorators: [applicationConfig({ providers: [provideRouter([])] })], +}; +export default meta; +type Story = StoryObj; + +export const InschrijvingOpen: Story = { + decorators: [ + applicationConfig({ + providers: [{ provide: FeatureFlagStore, useValue: { enabled: () => true } }], + }), + ], +}; +export const InschrijvingDicht: Story = { + decorators: [ + applicationConfig({ + providers: [{ provide: FeatureFlagStore, useValue: { enabled: () => false } }], + }), + ], +}; diff --git a/apps/ssp/src/app/registratie/ui/dashboard/mijn-aanvragen.section.stories.ts b/apps/ssp/src/app/registratie/ui/dashboard/mijn-aanvragen.section.stories.ts index ae81b5e..a8e3d4f 100644 --- a/apps/ssp/src/app/registratie/ui/dashboard/mijn-aanvragen.section.stories.ts +++ b/apps/ssp/src/app/registratie/ui/dashboard/mijn-aanvragen.section.stories.ts @@ -34,7 +34,7 @@ function storeStub(aanvragen: RemoteData, lastErr } const meta: Meta = { - title: 'Domein/Registratie/Dashboard/Mijn Aanvragen', + title: 'Domein/Registratie/Mijn Aanvragen', component: MijnAanvragenSection, decorators: [applicationConfig({ providers: [provideRouter([])] })], }; diff --git a/apps/ssp/src/app/registratie/ui/dashboard/mijn-registratie.section.stories.ts b/apps/ssp/src/app/registratie/ui/dashboard/mijn-registratie.section.stories.ts index 58ec2f1..24a94f5 100644 --- a/apps/ssp/src/app/registratie/ui/dashboard/mijn-registratie.section.stories.ts +++ b/apps/ssp/src/app/registratie/ui/dashboard/mijn-registratie.section.stories.ts @@ -28,7 +28,7 @@ function storeStub(profileRd: RemoteData) { } const meta: Meta = { - title: 'Domein/Registratie/Dashboard/Mijn Registratie', + title: 'Domein/Registratie/Mijn Registratie', component: MijnRegistratieSection, }; export default meta; diff --git a/apps/ssp/src/app/registratie/ui/dashboard/specialismen.section.stories.ts b/apps/ssp/src/app/registratie/ui/dashboard/specialismen.section.stories.ts index 9912e39..1ec519c 100644 --- a/apps/ssp/src/app/registratie/ui/dashboard/specialismen.section.stories.ts +++ b/apps/ssp/src/app/registratie/ui/dashboard/specialismen.section.stories.ts @@ -17,7 +17,7 @@ function storeStub(aantekeningen: RemoteData) } const meta: Meta = { - title: 'Domein/Registratie/Dashboard/Specialismen', + title: 'Domein/Registratie/Specialismen', component: SpecialismenSection, }; export default meta; diff --git a/apps/ssp/src/app/registratie/ui/dashboard/wat-moet-ik-regelen.section.stories.ts b/apps/ssp/src/app/registratie/ui/dashboard/wat-moet-ik-regelen.section.stories.ts new file mode 100644 index 0000000..05fed6a --- /dev/null +++ b/apps/ssp/src/app/registratie/ui/dashboard/wat-moet-ik-regelen.section.stories.ts @@ -0,0 +1,109 @@ +import type { Meta, StoryObj } from '@storybook/angular'; +import { applicationConfig } from '@storybook/angular'; +import { WatMoetIkRegelenSection } from './wat-moet-ik-regelen.section'; +import { BigProfileStore } from '@registratie/application/big-profile.store'; +import { BigProfile } from '@registratie/domain/big-profile'; +import { HerregistratieDecisions } from '@registratie/domain/registration'; +import { RemoteData } from '@shared/application/remote-data'; +import { loading, success } from '@shared/testing/remote-data'; + +const profile: BigProfile = { + registration: { + bigNummer: '19012345601', + naam: 'Dr. A. (Anna) de Vries', + beroep: 'Arts', + registratiedatum: '2012-09-01', + geboortedatum: '1985-03-14', + status: { tag: 'Geregistreerd', herregistratieDatum: '2027-09-01' }, + }, + person: { + naam: 'Dr. A. (Anna) de Vries', + geboortedatum: '1985-03-14', + adres: { straat: 'Rijksweg 1', postcode: '2514 EA', woonplaats: 'Den Haag' }, + }, +}; + +/** Minimal store stand-in — only the members the section's template and class read. */ +function storeStub( + profileRd: RemoteData, + decisionsRd: RemoteData, + pendingHerregistratie: boolean, +) { + return { + profile: () => profileRd, + decisions: () => decisionsRd, + pendingHerregistratie: () => pendingHerregistratie, + reloadProfile: () => {}, + }; +} + +const meta: Meta = { + title: 'Domein/Registratie/Wat Moet Ik Regelen', + component: WatMoetIkRegelenSection, +}; +export default meta; +type Story = StoryObj; + +export const Loading: Story = { + decorators: [ + applicationConfig({ + providers: [{ provide: BigProfileStore, useValue: storeStub(loading(), loading(), false) }], + }), + ], +}; +export const MetTaken: Story = { + decorators: [ + applicationConfig({ + providers: [ + { + provide: BigProfileStore, + useValue: storeStub( + success(profile), + success({ eligibleForHerregistratie: true }), + false, + ), + }, + ], + }), + ], + parameters: { + // Structural: app-choice-link's host sits between the keuzelijst
    and its
  • + // — axe's list/listitem rule needs them adjacent regardless of `display:contents`. + // Same pre-existing gap as task-list.stories.ts and choice-list.stories.ts. WP-11 + // (CIBG markup fidelity) reworks this markup; see + // docs/project/backlog/WP-11-markup-fidelity.md. + a11y: { disable: true }, + }, +}; +export const NietsOpenstaand: Story = { + decorators: [ + applicationConfig({ + providers: [ + { + provide: BigProfileStore, + useValue: storeStub( + success(profile), + success({ eligibleForHerregistratie: false }), + false, + ), + }, + ], + }), + ], +}; +export const InBehandeling: Story = { + decorators: [ + applicationConfig({ + providers: [ + { + provide: BigProfileStore, + useValue: storeStub( + success(profile), + success({ eligibleForHerregistratie: false }), + true, + ), + }, + ], + }), + ], +}; diff --git a/docs/project/readable-codebase/RD-04-story-titles.md b/docs/project/readable-codebase/RD-04-story-titles.md new file mode 100644 index 0000000..e831ec0 --- /dev/null +++ b/docs/project/readable-codebase/RD-04-story-titles.md @@ -0,0 +1,170 @@ +# RD-04 — Story titles to `Domein//`, and the two missing stories + +Status: done +Source: PLAN.md Phase 0 ("Also settle the two deviations the refactor left behind") + +## Why + +CLAUDE.md decision 5 sets one title rule for a component in an app context's `ui/`: +`Domein//` — "full stop, regardless of which atomic layer it is". The three +dashboard-section stories break it. They read `Domein/Registratie/Dashboard/`, a fourth +segment that invents a sidebar folder the rule does not have. All 66 other story files comply. + +RD-03 split the dashboard into two contexts but did not touch the stories. Three of the six +sections still have no story at all. + +## Read first + +- `apps/ssp/src/app/registratie/ui/dashboard/mijn-registratie.section.stories.ts` — the shape + to copy: a `storeStub` returning only the members the template reads, one + `applicationConfig` decorator per story, and `loading`/`success`/`failure` from + `@shared/testing/remote-data`. +- `apps/ssp/src/app/registratie/ui/dashboard/wat-moet-ik-regelen.section.ts` — needs a story. +- `apps/ssp/src/app/overzicht/ui/wat-wilt-u-doen.section.ts` — needs a story. +- `apps/ssp/src/app/registratie/domain/tasks.ts` — `tasksFromProfile`, which decides whether + the task list renders full or empty. +- CLAUDE.md decision 5, the paragraph starting "Story titles mirror the sidebar's". + +## Decisions (pre-made, don't relitigate) + +1. **Retitle the three existing stories. Drop the `Dashboard/` segment only.** + + | File | From | To | + | ----------------------------------------------- | ----------------------------------------------- | ------------------------------------- | + | `dashboard/mijn-aanvragen.section.stories.ts` | `Domein/Registratie/Dashboard/Mijn Aanvragen` | `Domein/Registratie/Mijn Aanvragen` | + | `dashboard/mijn-registratie.section.stories.ts` | `Domein/Registratie/Dashboard/Mijn Registratie` | `Domein/Registratie/Mijn Registratie` | + | `dashboard/specialismen.section.stories.ts` | `Domein/Registratie/Dashboard/Specialismen` | `Domein/Registratie/Specialismen` | + + The files stay where they are. RD-03 decision 3 keeps the four data sections in + `registratie/ui/dashboard/`; the folder is not the title. + +2. **Add exactly two story files, not three.** PLAN's rule is "add one only where the section + has more than one visual state". + + - `wat-moet-ik-regelen.section.stories.ts`, titled `Domein/Registratie/Wat Moet Ik Regelen` + — four states. + - `wat-wilt-u-doen.section.stories.ts`, titled `Domein/Overzicht/Wat Wilt U Doen` — two + states. This creates the `Domein/Overzicht/` sidebar bucket; it is the first story in + that context. + +3. **`beheer-links.section.ts` gets no story.** It has one visual state. Its other branch + renders nothing at all — `@if (adminLinks().length)` wraps the whole template — and a story + whose canvas is blank documents nothing and gives the a11y addon nothing to check. The + capability filter it applies (`AccessStore.can`) is already covered where it can actually be + asserted, in the application layer. Do not add the file. + +4. **`wat-moet-ik-regelen` gets four stories**, from a stub with the four members its template + and class read — `profile()`, `decisions()`, `pendingHerregistratie()`, `reloadProfile()`: + + | Story | `profile()` | `decisions()` | `pendingHerregistratie()` | Renders | + | ----------------- | ------------ | ----------------------------------------------- | ------------------------- | ------------------------------ | + | `Loading` | `loading()` | `loading()` | `false` | two skeleton bars | + | `MetTaken` | `success(p)` | `success({ eligibleForHerregistratie: true })` | `false` | the task list, one task | + | `NietsOpenstaand` | `success(p)` | `success({ eligibleForHerregistratie: false })` | `false` | the "niets openstaan" text | + | `InBehandeling` | `success(p)` | `success({ eligibleForHerregistratie: false })` | `true` | the info alert above the above | + + `Failed` is deliberately absent — ``'s failure template is already exercised by + `Mijn Registratie`'s `Failed` story, and this section adds nothing to it. + +5. **The task count follows from `tasksFromProfile`, so pick the profile deliberately.** With + `status.tag === 'Geregistreerd'`, the list is empty unless `eligibleForHerregistratie` is + true. That is what separates `MetTaken` from `NietsOpenstaand` — the eligibility flag, not + the profile. Reuse the `profile` fixture from `mijn-registratie.section.stories.ts` + verbatim (copy it; do not export it from the other story file and import it — story files + in this repo are self-contained). + +6. **`wat-wilt-u-doen` gets two stories**, from a `FeatureFlagStore` stub whose only member is + `enabled`: + + | Story | stub | Renders | + | ------------------- | -------------------------- | -------------------------------- | + | `InschrijvingOpen` | `{ enabled: () => true }` | six actions, "Inschrijven" first | + | `InschrijvingDicht` | `{ enabled: () => false }` | five actions, no "Inschrijven" | + +7. **`libs/beheer`'s title stays wrong here.** `Domein/Beheer/Stamdata Table Editor` is a + different deviation with a different verdict (the doc changes, not the code). RD-28 owns it. + Do not touch it. + +## Files + +- `apps/ssp/src/app/registratie/ui/dashboard/mijn-aanvragen.section.stories.ts` (title only) +- `apps/ssp/src/app/registratie/ui/dashboard/mijn-registratie.section.stories.ts` (title only) +- `apps/ssp/src/app/registratie/ui/dashboard/specialismen.section.stories.ts` (title only) +- `apps/ssp/src/app/registratie/ui/dashboard/wat-moet-ik-regelen.section.stories.ts` (new) +- `apps/ssp/src/app/overzicht/ui/wat-wilt-u-doen.section.stories.ts` (new) + +## Steps + +1. Retitle the three files per decision 1. Nothing else in those files changes. +2. Write `wat-moet-ik-regelen.section.stories.ts` per decisions 4 and 5. +3. Write `wat-wilt-u-doen.section.stories.ts` per decision 6. +4. `git add` all five files, then run the acceptance commands. +5. Update this ticket's `Status:` to `done` and the README's RD-04 row to `done`. +6. Commit all of it together. + +## Acceptance criteria + +Dry-run against the tree before handover; the "is" numbers are measured, not estimated. + +**Run these after `git add`.** `git grep` and `git ls-files` read tracked files, so a new +story file that is not yet staged does not exist as far as they are concerned. + +```bash +git grep -l "Domein/Registratie/Dashboard" -- apps | wc -l # is 3 -> MUST be 0 +git grep -h "title: 'Domein" -- '*.stories.ts' | grep -c "Domein/[^/]*/[^/]*/" # is 3 -> MUST be 0 +git ls-files '*.stories.ts' | wc -l # is 69 -> MUST be 71 +``` + +The second command counts title lines with a fourth path segment. It is the general form of +the rule, so it also catches a retitle that invents a different extra segment. + +The two new stories exist, with the exact titles from decisions 2, 4 and 6: + +```bash +git grep -c "title: 'Domein/Registratie/Wat Moet Ik Regelen'" -- apps/ssp/src/app/registratie/ui/dashboard/wat-moet-ik-regelen.section.stories.ts # MUST be 1 +git grep -c "title: 'Domein/Overzicht/Wat Wilt U Doen'" -- apps/ssp/src/app/overzicht/ui/wat-wilt-u-doen.section.stories.ts # MUST be 1 +``` + +`beheer-links` still has no story (decision 3): + +```bash +git ls-files 'apps/ssp/src/app/overzicht/ui/beheer-links.section.stories.ts' | wc -l # MUST be 0 +``` + +```bash +npm run ci --full # exits 0 +``` + +## Verification + +**`--full` is required.** This ticket adds two story files, and `npm run ci` does not build +Storybook. A story that fails to compile, or a decorator with a missing provider, is invisible +until the storybook-a11y job runs. Run `npm run ci --full`, not `npm run ci`. + +Verified for you: `layers.mdx` deep-links exactly two story ids — +`design-system-molecules-application-link--navigatie` and +`domein-registratie-aanvraag-block--concept`. Neither is a dashboard section, so the three +retitles break no link. Do not add a new deep link. + +## Out of scope + +- Moving any story file. The four data sections stay in `registratie/ui/dashboard/` + (RD-03 decision 3). +- A story for `beheer-links.section.ts` (decision 3). +- `libs/beheer`'s `Domein/Beheer/…` title (decision 7, RD-28 owns it). +- The dashboard's 8 imports. PLAN settled this: accept, do not fix. +- Renaming the `dashboard/` folder. The name is stale now that the page lives in `overzicht`, + but no rule requires the folder to match a title, and the rename would touch every import. + +## Risks + +- **A story title is a URL.** Storybook derives the story id from the title, so a retitle + changes the id. Verified above that nothing links to these three; do not extend the retitle + to a story outside the table without re-checking. +- **`success(...)` needs the decisions shape, not a boolean.** `store.decisions()` is a + `RemoteData`. The stub returns + `success({ eligibleForHerregistratie: true })`, not `success(true)`. +- **The stub must cover every member the class reads, not only the template.** + `WatMoetIkRegelenSection` reads `decisions()` in a `computed`, which the template never + names. A stub missing it throws at render time, and only `--full` catches that. +- **Do not import a fixture across story files** (decision 5). Copy it. diff --git a/docs/project/readable-codebase/README.md b/docs/project/readable-codebase/README.md index 814d637..f218c8d 100644 --- a/docs/project/readable-codebase/README.md +++ b/docs/project/readable-codebase/README.md @@ -98,7 +98,7 @@ two. Note that RD-15 exists because 22 abandoned agent worktrees are still on di | RD-01 | Scaffold this backlog: README, PLAN, ticket template | — | | done | | RD-02 | `max-lines` rule + `reportUnusedDisableDirectives` + 7 disables | 01 | | done | | RD-03 | `overzicht` context: page + 2 nav sections, boundary edge, admin-links token | 02 | yes | done | -| RD-04 | Story titles to `Domein//`; add the missing stories | 03 | yes | todo | +| RD-04 | Story titles to `Domein//`; add the missing stories | 03 | yes | done | | RD-05 | `createStore` gains the effect map + specs | 02 | | done | | RD-06 | **Bug fix:** 2 single-step forms to the effect map + retry affordance | 05 | yes | done | | RD-07 | Add `Primary` to the 3 wizard machines + specs | 05 | | done |