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:
@@ -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 "melding"\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'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 "melding"\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's a11y label and a content wrapper (<code>.feedback</code> is a flex row).\nErrors are <code>role="alert"</code> (assertive — interrupts) since they need immediate\nattention; other variants stay <code>role="status"</code> (polite) so success/info banners\ndon'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"
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user