Skip to content

Commit e629d9e

Browse files
Dynamic aria-describedby attribute
1 parent f00eae6 commit e629d9e

File tree

3 files changed

+62
-1
lines changed

3 files changed

+62
-1
lines changed

src/app/register-email-form/register-email-form.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ <h1>{{MESSAGE_PREFIX + '.header'|translate}}</h1>
1616
<input [className]="(email.invalid) && (email.dirty || email.touched) ? 'form-control is-invalid' :'form-control'"
1717
type="text" id="email" formControlName="email"
1818
[attr.aria-label]="MESSAGE_PREFIX + '.aria.label' | translate"
19-
aria-describedby="email-errors-required email-error-not-valid"
19+
[attr.aria-describedby]="ariaDescribedby"
2020
[attr.aria-invalid]="email.invalid"/>
2121
<div *ngIf="email.invalid && (email.dirty || email.touched)"
2222
class="invalid-feedback show-feedback">

src/app/register-email-form/register-email-form.component.spec.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,4 +210,39 @@ describe('RegisterEmailFormComponent', () => {
210210
expect(router.navigate).not.toHaveBeenCalled();
211211
}));
212212
});
213+
describe('ariaDescribedby', () => {
214+
it('should have required error message when email is empty', () => {
215+
comp.form.patchValue({ email: '' });
216+
comp.checkEmailValidity();
217+
218+
expect(comp.ariaDescribedby).toContain('email-errors-required');
219+
});
220+
221+
it('should have invalid email error message when email is invalid', () => {
222+
comp.form.patchValue({ email: 'invalid-email' });
223+
comp.checkEmailValidity();
224+
225+
expect(comp.ariaDescribedby).toContain('email-error-not-valid');
226+
});
227+
228+
it('should clear ariaDescribedby when email is valid', () => {
229+
comp.form.patchValue({ email: '[email protected]' });
230+
comp.checkEmailValidity();
231+
232+
expect(comp.ariaDescribedby).toBe('');
233+
});
234+
235+
it('should update ariaDescribedby on value changes', () => {
236+
spyOn(comp, 'checkEmailValidity').and.callThrough();
237+
238+
comp.form.patchValue({ email: '' });
239+
expect(comp.ariaDescribedby).toContain('email-errors-required');
240+
241+
comp.form.patchValue({ email: 'invalid-email' });
242+
expect(comp.ariaDescribedby).toContain('email-error-not-valid');
243+
244+
comp.form.patchValue({ email: '[email protected]' });
245+
expect(comp.ariaDescribedby).toBe('');
246+
});
247+
});
213248
});

src/app/register-email-form/register-email-form.component.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ export class RegisterEmailFormComponent implements OnDestroy, OnInit {
109109

110110
subscriptions: Subscription[] = [];
111111

112+
/**
113+
* Stores error messages related to the email field
114+
*/
115+
ariaDescribedby: string = '';
116+
112117
captchaVersion(): Observable<string> {
113118
return this.googleRecaptchaService.captchaVersion();
114119
}
@@ -178,6 +183,13 @@ export class RegisterEmailFormComponent implements OnDestroy, OnInit {
178183
this.disableUntilChecked = res;
179184
this.changeDetectorRef.detectChanges();
180185
}));
186+
187+
/**
188+
* Subscription to email field value changes
189+
*/
190+
this.subscriptions.push(this.email.valueChanges.subscribe(() => {
191+
this.checkEmailValidity();
192+
}));
181193
}
182194

183195
/**
@@ -291,4 +303,18 @@ export class RegisterEmailFormComponent implements OnDestroy, OnInit {
291303
}
292304
}
293305

306+
checkEmailValidity() {
307+
const descriptions = [];
308+
309+
if (this.email.errors?.required) {
310+
descriptions.push('email-errors-required');
311+
}
312+
313+
if (this.email.errors?.pattern || this.email.errors?.email) {
314+
descriptions.push('email-error-not-valid');
315+
}
316+
317+
this.ariaDescribedby = descriptions.join(' ');
318+
}
319+
294320
}

0 commit comments

Comments
 (0)