Skip to content

Commit

Permalink
fix(material/form-field): changes show/hide mechanism of mat-error fo…
Browse files Browse the repository at this point in the history
…r screenreaders

Attempts to fix a bug with Angular Components Form-field component where the
mat-error and mat-hint component are just visually hidden in the DOM, but
visibility is toggled depending on the form field state. Also updates the
.mat-mdc-form-field-subscript-wrapper to have aria-live=assertive in order to
prioritize the error/hint being read when it appears.

Fixes b/370743693
  • Loading branch information
essjay05 committed Oct 7, 2024
1 parent fbe7e33 commit c21be53
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 20 deletions.
38 changes: 18 additions & 20 deletions src/material/form-field/form-field.html
Original file line number Diff line number Diff line change
Expand Up @@ -97,27 +97,25 @@

<div
class="mat-mdc-form-field-subscript-wrapper mat-mdc-form-field-bottom-align"
aria-atomic="true"
aria-live="assertive"
[class.mat-mdc-form-field-subscript-dynamic-size]="subscriptSizing === 'dynamic'"
>
@switch (_getDisplayedMessages()) {
@case ('error') {
<div
class="mat-mdc-form-field-error-wrapper"
[@transitionMessages]="_subscriptAnimationState"
>
<ng-content select="mat-error, [matError]"></ng-content>
</div>
}

@case ('hint') {
<div class="mat-mdc-form-field-hint-wrapper" [@transitionMessages]="_subscriptAnimationState">
@if (hintLabel) {
<mat-hint [id]="_hintLabelId">{{hintLabel}}</mat-hint>
}
<ng-content select="mat-hint:not([align='end'])"></ng-content>
<div class="mat-mdc-form-field-hint-spacer"></div>
<ng-content select="mat-hint[align='end']"></ng-content>
</div>
<div
class="mat-mdc-form-field-error-wrapper"
[@transitionMessages]="_subscriptAnimationState"
>
<ng-content select="mat-error, [matError]"></ng-content>
</div>
<div
class="mat-mdc-form-field-hint-wrapper"
[@transitionMessages]="_subscriptAnimationState"
>
@if (hintLabel) {
<mat-hint [id]="_hintLabelId">{{hintLabel}}</mat-hint>
}
}
<ng-content select="mat-hint:not([align='end'])"></ng-content>
<div class="mat-mdc-form-field-hint-spacer"></div>
<ng-content select="mat-hint[align='end']"></ng-content>
</div>
</div>
53 changes: 53 additions & 0 deletions src/material/form-field/form-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,7 @@ export class MatFormField
this._stateChanges = control.stateChanges.subscribe(() => {
this._updateFocusState();
this._syncDescribedByIds();
this._showOrHideSubscript();
this._changeDetectorRef.markForCheck();
});

Expand Down Expand Up @@ -469,17 +470,20 @@ export class MatFormField
// Re-validate when the number of hints changes.
this._hintChildren.changes.subscribe(() => {
this._processHints();
this._showOrHideSubscript();
this._changeDetectorRef.markForCheck();
});

// Update the aria-described by when the number of errors changes.
this._errorChildren.changes.subscribe(() => {
this._syncDescribedByIds();
this._showOrHideSubscript();
this._changeDetectorRef.markForCheck();
});

// Initial mat-hint validation and subscript describedByIds sync.
this._validateHints();
this._showOrHideSubscript();
this._syncDescribedByIds();
}

Expand Down Expand Up @@ -649,6 +653,7 @@ export class MatFormField
}

if (this._getDisplayedMessages() === 'hint') {
this._showOrHideSubscript();
const startHint = this._hintChildren
? this._hintChildren.find(hint => hint.align === 'start')
: null;
Expand All @@ -673,6 +678,54 @@ export class MatFormField
}
}

/**
* Solves https://github.com/angular/components/issues/29616
* Issues with certain browser and screen reader pairings not able to announce mat-error
* when it's added to the DOM rather than changing the visibility of the hint/error wrappers.
* Changing visibility instead of adding the div wrappers works for all browsers and screen
* readers.
*
* If there is an 'error' or 'hint' message being returned, remove visibility: hidden
* style class and show error or hint section of code. If no 'error' or 'hint' messages are
* being returned and no error children showing in query list, add visibility: hidden
* style class back to error wrapper.
*/
private _showOrHideSubscript() {
switch (this._getDisplayedMessages()) {
case 'error': {
console.log('Show error wrapper');
console.log(this._elementRef.nativeElement.children[1].children[0].classList);
this._elementRef.nativeElement.children[1].children[0].classList.remove(
'cdk-visually-hidden',
);
// Can't show error message and hint at same time
console.log('Hide hint wrapper');
this._elementRef.nativeElement.children[1].children[1].classList.add('cdk-visually-hidden');
console.log(this._elementRef.nativeElement.children[1].children[0].classList);
break;
}
case 'hint': {
console.log('Show hint wrapper');
console.log(this._elementRef.nativeElement.children[1].children[1].classList);
this._elementRef.nativeElement.children[1].children[1].classList.remove(
'cdk-visually-hidden',
);
console.log(this._elementRef.nativeElement.children[1].children[1].classList);
break;
}
}

if (!this._errorChildren || this._errorChildren.length === 0 || !this._control.errorState) {
console.log('hide error wrapper');
this._elementRef.nativeElement.children[1].children[0].classList.add('cdk-visually-hidden');
}

if (!this._hintChildren) {
console.log('hide hint wrapper');
this._elementRef.nativeElement.children[1].children[1].classList.add('cdk-visually-hidden');
}
}

/**
* Updates the horizontal offset of the label in the outline appearance. In the outline
* appearance, the notched-outline and label are not relative to the infix container because
Expand Down

0 comments on commit c21be53

Please sign in to comment.