feat(fp): WP-16 — component a11y: description wiring + alert role

Wires text-input's aria-describedby to the form-field description div
(the BSN hint was rendered but never announced), pins desc-before-error
ordering, and switches alert to role=alert for errors vs role=status
for info/ok/warning. Composition contract enforced by story play tests
(form-field+text-input, alert per variant) run in the WP-01 CI gate.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-04 08:15:24 +02:00
parent 85c805b8bd
commit f3de30b72c
8 changed files with 213 additions and 108 deletions

View File

@@ -11905,7 +11905,7 @@
},
{
"name": "AlertComponent",
"id": "component-AlertComponent-859bbe82cccea40581a39ebff20da27d3087f9cca4ec7a25457e0f7a69f1478d7af94539fbcf20efcc96afff2a0f3252d0c88c566f00cb4eea117d1e0c0769e8",
"id": "component-AlertComponent-e38aceca94288853333b2fd4ad37535f923420f61c9ba25f100a4035a558c0ada40d88b7e7ddd136f6d6099bb7bc9695de3c513720c0b343b5792a4543de19f5",
"file": "src/app/shared/ui/alert/alert.component.ts",
"encapsulation": [],
"entryComponents": [],
@@ -11917,7 +11917,7 @@
"styles": [
"\n .feedback > div {\n flex: 1 1 auto;\n min-width: 0;\n }\n "
],
"template": "<div\n class=\"feedback\"\n [class.feedback-info]=\"type() === 'info'\"\n [class.feedback-success]=\"type() === 'ok'\"\n [class.feedback-warning]=\"type() === 'warning'\"\n [class.feedback-error]=\"type() === 'error'\"\n role=\"status\"\n aria-atomic=\"true\"\n>\n <span class=\"icon\"\n ><span class=\"visually-hidden\">{{ iconLabels[type()] }}</span></span\n >\n <div><ng-content /></div>\n</div>\n",
"template": "<div\n class=\"feedback\"\n [class.feedback-info]=\"type() === 'info'\"\n [class.feedback-success]=\"type() === 'ok'\"\n [class.feedback-warning]=\"type() === 'warning'\"\n [class.feedback-error]=\"type() === 'error'\"\n [attr.role]=\"type() === 'error' ? 'alert' : 'status'\"\n aria-atomic=\"true\"\n>\n <span class=\"icon\"\n ><span class=\"visually-hidden\">{{ iconLabels[type()] }}</span></span\n >\n <div><ng-content /></div>\n</div>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
@@ -11931,7 +11931,7 @@
"indexKey": "",
"optional": false,
"description": "",
"line": 45,
"line": 48,
"required": false
}
],
@@ -11946,7 +11946,7 @@
"indexKey": "",
"optional": false,
"description": "",
"line": 46,
"line": 49,
"modifierKind": [
124,
148
@@ -11960,10 +11960,10 @@
"hostListeners": [],
"standalone": false,
"imports": [],
"description": "<p>Atom: alert/message banner — the CIBG Huisstijl &quot;melding&quot;\n(designsystem.cibg.nl/componenten/meldingen). Thin wrapper over the vendored\n<code>.feedback feedback-*</code> classes: the design system owns surface + icon; we add\nonly the icon&#39;s a11y label and a content wrapper (<code>.feedback</code> is a flex row).</p>\n",
"rawdescription": "\nAtom: alert/message banner — the CIBG Huisstijl \"melding\"\n(designsystem.cibg.nl/componenten/meldingen). Thin wrapper over the vendored\n`.feedback feedback-*` classes: the design system owns surface + icon; we add\nonly the icon's a11y label and a content wrapper (`.feedback` is a flex row).",
"description": "<p>Atom: alert/message banner — the CIBG Huisstijl &quot;melding&quot;\n(designsystem.cibg.nl/componenten/meldingen). Thin wrapper over the vendored\n<code>.feedback feedback-*</code> classes: the design system owns surface + icon; we add\nonly the icon&#39;s a11y label and a content wrapper (<code>.feedback</code> is a flex row).\nErrors are <code>role=&quot;alert&quot;</code> (assertive — interrupts) since they need immediate\nattention; other variants stay <code>role=&quot;status&quot;</code> (polite) so success/info banners\ndon&#39;t interrupt what the user is doing.</p>\n",
"rawdescription": "\nAtom: alert/message banner — the CIBG Huisstijl \"melding\"\n(designsystem.cibg.nl/componenten/meldingen). Thin wrapper over the vendored\n`.feedback feedback-*` classes: the design system owns surface + icon; we add\nonly the icon's a11y label and a content wrapper (`.feedback` is a flex row).\nErrors are `role=\"alert\"` (assertive — interrupts) since they need immediate\nattention; other variants stay `role=\"status\"` (polite) so success/info banners\ndon't interrupt what the user is doing.",
"type": "component",
"sourceCode": "import { Component, input } from '@angular/core';\n\ntype AlertType = 'info' | 'ok' | 'warning' | 'error';\n\n// visually-hidden alternative for the status icon (CIBG a11y requirement).\nconst ICON_LABELS: Record<AlertType, string> = {\n info: $localize`:@@alert.icon.info:Informatie`,\n ok: $localize`:@@alert.icon.ok:Gelukt`,\n warning: $localize`:@@alert.icon.warning:Waarschuwing`,\n error: $localize`:@@alert.icon.error:Foutmelding`,\n};\n\n/** Atom: alert/message banner — the CIBG Huisstijl \"melding\"\n (designsystem.cibg.nl/componenten/meldingen). Thin wrapper over the vendored\n `.feedback feedback-*` classes: the design system owns surface + icon; we add\n only the icon's a11y label and a content wrapper (`.feedback` is a flex row). */\n@Component({\n selector: 'app-alert',\n styles: [\n `\n .feedback > div {\n flex: 1 1 auto;\n min-width: 0;\n }\n `,\n ],\n template: `\n <div\n class=\"feedback\"\n [class.feedback-info]=\"type() === 'info'\"\n [class.feedback-success]=\"type() === 'ok'\"\n [class.feedback-warning]=\"type() === 'warning'\"\n [class.feedback-error]=\"type() === 'error'\"\n role=\"status\"\n aria-atomic=\"true\"\n >\n <span class=\"icon\"\n ><span class=\"visually-hidden\">{{ iconLabels[type()] }}</span></span\n >\n <div><ng-content /></div>\n </div>\n `,\n})\nexport class AlertComponent {\n type = input<AlertType>('info');\n protected readonly iconLabels = ICON_LABELS;\n}\n",
"sourceCode": "import { Component, input } from '@angular/core';\n\ntype AlertType = 'info' | 'ok' | 'warning' | 'error';\n\n// visually-hidden alternative for the status icon (CIBG a11y requirement).\nconst ICON_LABELS: Record<AlertType, string> = {\n info: $localize`:@@alert.icon.info:Informatie`,\n ok: $localize`:@@alert.icon.ok:Gelukt`,\n warning: $localize`:@@alert.icon.warning:Waarschuwing`,\n error: $localize`:@@alert.icon.error:Foutmelding`,\n};\n\n/** Atom: alert/message banner — the CIBG Huisstijl \"melding\"\n (designsystem.cibg.nl/componenten/meldingen). Thin wrapper over the vendored\n `.feedback feedback-*` classes: the design system owns surface + icon; we add\n only the icon's a11y label and a content wrapper (`.feedback` is a flex row).\n Errors are `role=\"alert\"` (assertive — interrupts) since they need immediate\n attention; other variants stay `role=\"status\"` (polite) so success/info banners\n don't interrupt what the user is doing. */\n@Component({\n selector: 'app-alert',\n styles: [\n `\n .feedback > div {\n flex: 1 1 auto;\n min-width: 0;\n }\n `,\n ],\n template: `\n <div\n class=\"feedback\"\n [class.feedback-info]=\"type() === 'info'\"\n [class.feedback-success]=\"type() === 'ok'\"\n [class.feedback-warning]=\"type() === 'warning'\"\n [class.feedback-error]=\"type() === 'error'\"\n [attr.role]=\"type() === 'error' ? 'alert' : 'status'\"\n aria-atomic=\"true\"\n >\n <span class=\"icon\"\n ><span class=\"visually-hidden\">{{ iconLabels[type()] }}</span></span\n >\n <div><ng-content /></div>\n </div>\n `,\n})\nexport class AlertComponent {\n type = input<AlertType>('info');\n protected readonly iconLabels = ICON_LABELS;\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "\n .feedback > div {\n flex: 1 1 auto;\n min-width: 0;\n }\n \n",
@@ -18067,7 +18067,7 @@
},
{
"name": "LoginFormComponent",
"id": "component-LoginFormComponent-b3f04d6cf60e4894fa1e6ecad944a0e206bb9db082d3ea89289e34147a4e3cbcb902189c8870b64400784969bc6c43c4fc64248ec4e46cdb553ec228204518ca",
"id": "component-LoginFormComponent-b6c5fb92e9b69f8a6f42ec2fc097f8501c87d05930404dcac64a29d3b99a12d69ea004881c9163fae2a94ee69aee48807ae0db75ec0336169eb1ad923e54237a",
"file": "src/app/auth/ui/login-form/login-form.component.ts",
"encapsulation": [],
"entryComponents": [],
@@ -18077,7 +18077,7 @@
"selector": "app-login-form",
"styleUrls": [],
"styles": [],
"template": "<form (ngSubmit)=\"submitted.emit(bsn)\" class=\"form-horizontal\">\n <div class=\"form-header\">\n <div class=\"form-action\">\n <span class=\"meta\" i18n=\"@@form.verplichteVelden\">* verplichte velden</span>\n </div>\n </div>\n\n <app-form-field\n i18n-label=\"@@login.bsnLabel\"\n label=\"BSN\"\n fieldId=\"bsn\"\n required\n i18n-description=\"@@login.bsnDescription\"\n description=\"9 cijfers (demo: vul iets in)\"\n >\n <app-text-input inputId=\"bsn\" [(ngModel)]=\"bsn\" name=\"bsn\" placeholder=\"123456789\" />\n </app-form-field>\n\n <app-form-field i18n-label=\"@@login.wachtwoordLabel\" label=\"Wachtwoord\" fieldId=\"pw\" required>\n <app-text-input inputId=\"pw\" type=\"password\" [(ngModel)]=\"password\" name=\"pw\" />\n </app-form-field>\n\n <app-button type=\"submit\" variant=\"primary\" i18n=\"@@login.submit\"\n >Inloggen met DigiD</app-button\n >\n</form>\n",
"template": "<form (ngSubmit)=\"submitted.emit(bsn)\" class=\"form-horizontal\">\n <div class=\"form-header\">\n <div class=\"form-action\">\n <span class=\"meta\" i18n=\"@@form.verplichteVelden\">* verplichte velden</span>\n </div>\n </div>\n\n <app-form-field\n i18n-label=\"@@login.bsnLabel\"\n label=\"BSN\"\n fieldId=\"bsn\"\n required\n i18n-description=\"@@login.bsnDescription\"\n description=\"9 cijfers (demo: vul iets in)\"\n >\n <app-text-input\n inputId=\"bsn\"\n hasDescription\n [(ngModel)]=\"bsn\"\n name=\"bsn\"\n placeholder=\"123456789\"\n />\n </app-form-field>\n\n <app-form-field i18n-label=\"@@login.wachtwoordLabel\" label=\"Wachtwoord\" fieldId=\"pw\" required>\n <app-text-input inputId=\"pw\" type=\"password\" [(ngModel)]=\"password\" name=\"pw\" />\n </app-form-field>\n\n <app-button type=\"submit\" variant=\"primary\" i18n=\"@@login.submit\"\n >Inloggen met DigiD</app-button\n >\n</form>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
@@ -18091,7 +18091,7 @@
"indexKey": "",
"optional": false,
"description": "",
"line": 43,
"line": 49,
"required": false
}
],
@@ -18105,7 +18105,7 @@
"indexKey": "",
"optional": false,
"description": "",
"line": 41
"line": 47
},
{
"name": "password",
@@ -18116,7 +18116,7 @@
"indexKey": "",
"optional": false,
"description": "",
"line": 42
"line": 48
}
],
"methodsClass": [],
@@ -18146,7 +18146,7 @@
"description": "<p>Organism: DigiD-style mock login. No real auth — just composes atoms/molecules.</p>\n",
"rawdescription": "\nOrganism: DigiD-style mock login. No real auth — just composes atoms/molecules.",
"type": "component",
"sourceCode": "import { Component, output } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { FormFieldComponent } from '@shared/ui/form-field/form-field.component';\nimport { TextInputComponent } from '@shared/ui/text-input/text-input.component';\nimport { ButtonComponent } from '@shared/ui/button/button.component';\n\n/** Organism: DigiD-style mock login. No real auth — just composes atoms/molecules. */\n@Component({\n selector: 'app-login-form',\n imports: [FormsModule, FormFieldComponent, TextInputComponent, ButtonComponent],\n template: `\n <form (ngSubmit)=\"submitted.emit(bsn)\" class=\"form-horizontal\">\n <div class=\"form-header\">\n <div class=\"form-action\">\n <span class=\"meta\" i18n=\"@@form.verplichteVelden\">* verplichte velden</span>\n </div>\n </div>\n\n <app-form-field\n i18n-label=\"@@login.bsnLabel\"\n label=\"BSN\"\n fieldId=\"bsn\"\n required\n i18n-description=\"@@login.bsnDescription\"\n description=\"9 cijfers (demo: vul iets in)\"\n >\n <app-text-input inputId=\"bsn\" [(ngModel)]=\"bsn\" name=\"bsn\" placeholder=\"123456789\" />\n </app-form-field>\n\n <app-form-field i18n-label=\"@@login.wachtwoordLabel\" label=\"Wachtwoord\" fieldId=\"pw\" required>\n <app-text-input inputId=\"pw\" type=\"password\" [(ngModel)]=\"password\" name=\"pw\" />\n </app-form-field>\n\n <app-button type=\"submit\" variant=\"primary\" i18n=\"@@login.submit\"\n >Inloggen met DigiD</app-button\n >\n </form>\n `,\n})\nexport class LoginFormComponent {\n bsn = '';\n password = '';\n submitted = output<string>();\n}\n",
"sourceCode": "import { Component, output } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { FormFieldComponent } from '@shared/ui/form-field/form-field.component';\nimport { TextInputComponent } from '@shared/ui/text-input/text-input.component';\nimport { ButtonComponent } from '@shared/ui/button/button.component';\n\n/** Organism: DigiD-style mock login. No real auth — just composes atoms/molecules. */\n@Component({\n selector: 'app-login-form',\n imports: [FormsModule, FormFieldComponent, TextInputComponent, ButtonComponent],\n template: `\n <form (ngSubmit)=\"submitted.emit(bsn)\" class=\"form-horizontal\">\n <div class=\"form-header\">\n <div class=\"form-action\">\n <span class=\"meta\" i18n=\"@@form.verplichteVelden\">* verplichte velden</span>\n </div>\n </div>\n\n <app-form-field\n i18n-label=\"@@login.bsnLabel\"\n label=\"BSN\"\n fieldId=\"bsn\"\n required\n i18n-description=\"@@login.bsnDescription\"\n description=\"9 cijfers (demo: vul iets in)\"\n >\n <app-text-input\n inputId=\"bsn\"\n hasDescription\n [(ngModel)]=\"bsn\"\n name=\"bsn\"\n placeholder=\"123456789\"\n />\n </app-form-field>\n\n <app-form-field i18n-label=\"@@login.wachtwoordLabel\" label=\"Wachtwoord\" fieldId=\"pw\" required>\n <app-text-input inputId=\"pw\" type=\"password\" [(ngModel)]=\"password\" name=\"pw\" />\n </app-form-field>\n\n <app-button type=\"submit\" variant=\"primary\" i18n=\"@@login.submit\"\n >Inloggen met DigiD</app-button\n >\n </form>\n `,\n})\nexport class LoginFormComponent {\n bsn = '';\n password = '';\n submitted = output<string>();\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
@@ -21976,7 +21976,7 @@
},
{
"name": "TextInputComponent",
"id": "component-TextInputComponent-6bb65b77462f371a5f1eef4b78584a11380f10f1ffd7082d2844d9a50d2d6ace877d6e408ef279f4c6057daf08c97ffc6c2fa7c29b6de95c687c34adc9730c94",
"id": "component-TextInputComponent-7771b0ade08f68e587e3e3ebd8348dc85ed70f270c17ed6c8ceed88499dc366f926989029f6216b696a7b800e4dd42a55ff9511a7e99ee941fa0ee2c91513ce0",
"file": "src/app/shared/ui/text-input/text-input.component.ts",
"encapsulation": [],
"entryComponents": [],
@@ -21992,11 +21992,23 @@
"styles": [
"\n :host {\n display: block;\n }\n input {\n inline-size: 100%;\n box-sizing: border-box;\n }\n "
],
"template": "<input\n class=\"form-control\"\n [class.is-invalid]=\"invalid()\"\n [type]=\"type()\"\n [id]=\"inputId()\"\n [attr.aria-invalid]=\"invalid() ? 'true' : null\"\n [attr.aria-describedby]=\"invalid() && inputId() ? inputId() + '-error' : null\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"onTouched()\"\n/>\n",
"template": "<input\n class=\"form-control\"\n [class.is-invalid]=\"invalid()\"\n [type]=\"type()\"\n [id]=\"inputId()\"\n [attr.aria-invalid]=\"invalid() ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"onTouched()\"\n/>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [
{
"name": "hasDescription",
"defaultValue": "false, { transform: booleanAttribute }",
"deprecated": false,
"deprecationMessage": "",
"indexKey": "",
"optional": false,
"description": "<p>Set when the paired form-field renders a <code>-desc</code> hint, so it gets announced.</p>\n",
"line": 43,
"rawdescription": "\nSet when the paired form-field renders a `-desc` hint, so it gets announced.",
"required": false
},
{
"name": "inputId",
"deprecated": false,
@@ -22054,7 +22066,7 @@
"indexKey": "",
"optional": false,
"description": "",
"line": 44
"line": 46
},
{
"name": "onChange",
@@ -22065,7 +22077,7 @@
"indexKey": "",
"optional": false,
"description": "",
"line": 45
"line": 47
},
{
"name": "onTouched",
@@ -22076,7 +22088,7 @@
"indexKey": "",
"optional": false,
"description": "",
"line": 46
"line": 48
},
{
"name": "value",
@@ -22087,10 +22099,20 @@
"indexKey": "",
"optional": false,
"description": "",
"line": 43
"line": 45
}
],
"methodsClass": [
{
"name": "describedBy",
"args": [],
"optional": false,
"returnType": "string | null",
"typeParameters": [],
"line": 50,
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "onInput",
"args": [
@@ -22106,7 +22128,7 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 48,
"line": 60,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
@@ -22148,7 +22170,7 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 55,
"line": 67,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
@@ -22191,7 +22213,7 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 58,
"line": 70,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
@@ -22224,7 +22246,7 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 61,
"line": 73,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
@@ -22256,7 +22278,7 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 52,
"line": 64,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
@@ -22283,7 +22305,7 @@
"description": "<p>Atom: text input. Utrecht textbox wired up as a form control (ngModel/reactive).</p>\n",
"rawdescription": "\nAtom: text input. Utrecht textbox wired up as a form control (ngModel/reactive).",
"type": "component",
"sourceCode": "import { Component, forwardRef, input } from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\n\n/** Atom: text input. Utrecht textbox wired up as a form control (ngModel/reactive). */\n@Component({\n selector: 'app-text-input',\n styles: [\n `\n :host {\n display: block;\n }\n input {\n inline-size: 100%;\n box-sizing: border-box;\n }\n `,\n ],\n template: `\n <input\n class=\"form-control\"\n [class.is-invalid]=\"invalid()\"\n [type]=\"type()\"\n [id]=\"inputId()\"\n [attr.aria-invalid]=\"invalid() ? 'true' : null\"\n [attr.aria-describedby]=\"invalid() && inputId() ? inputId() + '-error' : null\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"onTouched()\"\n />\n `,\n providers: [\n { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TextInputComponent), multi: true },\n ],\n})\nexport class TextInputComponent implements ControlValueAccessor {\n type = input<'text' | 'password' | 'email'>('text');\n placeholder = input('');\n invalid = input(false);\n inputId = input<string>();\n\n value = '';\n disabled = false;\n onChange: (v: string) => void = () => {};\n onTouched: () => void = () => {};\n\n onInput(e: Event) {\n this.value = (e.target as HTMLInputElement).value;\n this.onChange(this.value);\n }\n writeValue(v: string) {\n this.value = v ?? '';\n }\n registerOnChange(fn: (v: string) => void) {\n this.onChange = fn;\n }\n registerOnTouched(fn: () => void) {\n this.onTouched = fn;\n }\n setDisabledState(d: boolean) {\n this.disabled = d;\n }\n}\n",
"sourceCode": "import { Component, booleanAttribute, forwardRef, input } from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\n\n/** Atom: text input. Utrecht textbox wired up as a form control (ngModel/reactive). */\n@Component({\n selector: 'app-text-input',\n styles: [\n `\n :host {\n display: block;\n }\n input {\n inline-size: 100%;\n box-sizing: border-box;\n }\n `,\n ],\n template: `\n <input\n class=\"form-control\"\n [class.is-invalid]=\"invalid()\"\n [type]=\"type()\"\n [id]=\"inputId()\"\n [attr.aria-invalid]=\"invalid() ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"onTouched()\"\n />\n `,\n providers: [\n { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TextInputComponent), multi: true },\n ],\n})\nexport class TextInputComponent implements ControlValueAccessor {\n type = input<'text' | 'password' | 'email'>('text');\n placeholder = input('');\n invalid = input(false);\n inputId = input<string>();\n /** Set when the paired form-field renders a `-desc` hint, so it gets announced. */\n hasDescription = input(false, { transform: booleanAttribute });\n\n value = '';\n disabled = false;\n onChange: (v: string) => void = () => {};\n onTouched: () => void = () => {};\n\n describedBy(): string | null {\n const id = this.inputId();\n if (!id) return null;\n const ids = [\n ...(this.hasDescription() ? [`${id}-desc`] : []),\n ...(this.invalid() ? [`${id}-error`] : []),\n ];\n return ids.length ? ids.join(' ') : null;\n }\n\n onInput(e: Event) {\n this.value = (e.target as HTMLInputElement).value;\n this.onChange(this.value);\n }\n writeValue(v: string) {\n this.value = v ?? '';\n }\n registerOnChange(fn: (v: string) => void) {\n this.onChange = fn;\n }\n registerOnTouched(fn: () => void) {\n this.onTouched = fn;\n }\n setDisabledState(d: boolean) {\n this.disabled = d;\n }\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "\n :host {\n display: block;\n }\n input {\n inline-size: 100%;\n box-sizing: border-box;\n }\n \n",
@@ -22948,6 +22970,16 @@
"rawdescription": "Used by the shell to find what to poll on return: still-in-flight uploads.",
"description": "<p>Used by the shell to find what to poll on return: still-in-flight uploads.</p>\n"
},
{
"name": "initial",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/brief/domain/brief.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "BriefState",
"defaultValue": "{ tag: 'loading' }"
},
{
"name": "initial",
"ctype": "miscellaneous",
@@ -22988,16 +23020,6 @@
"type": "RegistratieState",
"defaultValue": "{\n tag: 'Invullen',\n draft: emptyDraft,\n cursor: 0,\n errors: {},\n upload: initialUpload,\n}"
},
{
"name": "initial",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/brief/domain/brief.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "BriefState",
"defaultValue": "{ tag: 'loading' }"
},
{
"name": "initialUpload",
"ctype": "miscellaneous",
@@ -27258,6 +27280,50 @@
}
]
},
{
"name": "reduce",
"file": "src/app/brief/domain/brief.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "BriefState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "m",
"type": "BriefMsg",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "BriefState",
"jsdoctags": [
{
"name": "s",
"type": "BriefState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "m",
"type": "BriefMsg",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "reduce",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
@@ -27434,50 +27500,6 @@
}
]
},
{
"name": "reduce",
"file": "src/app/brief/domain/brief.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "BriefState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "m",
"type": "BriefMsg",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "BriefState",
"jsdoctags": [
{
"name": "s",
"type": "BriefState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "m",
"type": "BriefMsg",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "reduceUpload",
"file": "src/app/shared/upload/upload.machine.ts",
@@ -30414,6 +30436,18 @@
"defaultValue": "{\n info: $localize`:@@alert.icon.info:Informatie`,\n ok: $localize`:@@alert.icon.ok:Gelukt`,\n warning: $localize`:@@alert.icon.warning:Waarschuwing`,\n error: $localize`:@@alert.icon.error:Foutmelding`,\n}"
}
],
"src/app/brief/domain/brief.machine.ts": [
{
"name": "initial",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/brief/domain/brief.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "BriefState",
"defaultValue": "{ tag: 'loading' }"
}
],
"src/app/herregistratie/domain/herregistratie.machine.ts": [
{
"name": "initial",
@@ -30474,18 +30508,6 @@
"defaultValue": "{\n tag: 'Editing',\n draft: { straat: '', postcode: '', woonplaats: '' },\n errors: {},\n}"
}
],
"src/app/brief/domain/brief.machine.ts": [
{
"name": "initial",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/brief/domain/brief.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "BriefState",
"defaultValue": "{ tag: 'loading' }"
}
],
"src/app/shared/ui/radio-group/radio-group.component.ts": [
{
"name": "JA_NEE",
@@ -41803,8 +41825,8 @@
"type": "component",
"linktype": "component",
"name": "TextInputComponent",
"coveragePercent": 7,
"coverageCount": "1/14",
"coveragePercent": 12,
"coverageCount": "2/16",
"status": "low"
},
{