fix(dialog): remove default aria-label from mat-dialog-close (#15654)

Currently we add an `aria-label` by default to close buttons that don't have text. This is unreliable, because text can come in at any time and it takes a lot of code to keep track of it. Furthermore, the default text is in English which means that sites in different languages will have to override it anyway. These changes remove the default `aria-label` and add some guidance to the docs on how to deal with labelling the close button.

Fixes #15542.
This commit is contained in:
Kristiyan Kostadinov
2019-04-04 00:41:08 +02:00
committed by mmalerba
parent 61b29bfd5a
commit c3ead653db
4 changed files with 7 additions and 45 deletions
+3 -25
View File
@@ -29,25 +29,19 @@ let dialogElementUid = 0;
exportAs: 'matDialogClose',
host: {
'(click)': 'dialogRef.close(dialogResult)',
'[attr.aria-label]': '_hasAriaLabel ? ariaLabel : null',
'[attr.aria-label]': 'ariaLabel || null',
'type': 'button', // Prevents accidental form submits.
}
})
export class MatDialogClose implements OnInit, OnChanges {
/** Screenreader label for the button. */
@Input('aria-label') ariaLabel: string = 'Close dialog';
@Input('aria-label') ariaLabel: string;
/** Dialog close input. */
@Input('mat-dialog-close') dialogResult: any;
@Input('matDialogClose') _matDialogClose: any;
/**
* Whether the button should have an `aria-label`. Used for clearing the
* attribute to prevent it from being read instead of the button's text.
*/
_hasAriaLabel?: boolean;
constructor(
@Optional() public dialogRef: MatDialogRef<any>,
private _elementRef: ElementRef<HTMLElement>,
@@ -62,30 +56,14 @@ export class MatDialogClose implements OnInit, OnChanges {
// be resolved at constructor time.
this.dialogRef = getClosestDialog(this._elementRef, this._dialog.openDialogs)!;
}
if (typeof this._hasAriaLabel === 'undefined') {
const element = this._elementRef.nativeElement;
if (element.hasAttribute('mat-icon-button')) {
this._hasAriaLabel = true;
} else {
const buttonTextContent = element.textContent;
this._hasAriaLabel = !buttonTextContent || buttonTextContent.trim().length === 0;
}
}
}
ngOnChanges(changes: SimpleChanges) {
const proxiedChange =
changes['_matDialogClose'] || changes['_matDialogCloseResult'];
const proxiedChange = changes['_matDialogClose'] || changes['_matDialogCloseResult'];
if (proxiedChange) {
this.dialogResult = proxiedChange.currentValue;
}
if (changes['ariaLabel']) {
this._hasAriaLabel = !!changes['ariaLabel'].currentValue;
}
}
}
+4
View File
@@ -154,6 +154,10 @@ a [focus trap](https://material.angular.io/cdk/a11y/overview#focustrap) to conta
within itself. Once a dialog is closed, it will return focus to the element that was focused
before the dialog was opened.
If you're adding a close button that doesn't have text (e.g. a purely icon-based button), make sure
that it has a meaningful `aria-label` so that users with assistive technology know what it is used
for.
#### Focus management
By default, the first tabbable element within the dialog will receive focus upon open. This can
be configured by setting the `cdkFocusInitial` attribute on another focusable element.
-19
View File
@@ -1143,26 +1143,11 @@ describe('MatDialog', () => {
expect(overlayContainerElement.querySelectorAll('.mat-dialog-container').length).toBe(1);
});
it('should set an aria-label on a button without text', fakeAsync(() => {
let button = overlayContainerElement.querySelector('.close-without-text')!;
expect(button.getAttribute('aria-label')).toBeTruthy();
}));
it('should not have an aria-label if a button has text', fakeAsync(() => {
let button = overlayContainerElement.querySelector('[mat-dialog-close]')!;
expect(button.getAttribute('aria-label')).toBeFalsy();
}));
it('should allow for a user-specified aria-label on the close button', fakeAsync(() => {
let button = overlayContainerElement.querySelector('.close-with-aria-label')!;
expect(button.getAttribute('aria-label')).toBe('Best close button ever');
}));
it('should always have an aria-label on a mat-icon-button', fakeAsync(() => {
let button = overlayContainerElement.querySelector('.close-icon-button')!;
expect(button.getAttribute('aria-label')).toBeTruthy();
}));
it('should override the "type" attribute of the close button', () => {
let button = overlayContainerElement.querySelector('button[mat-dialog-close]')!;
@@ -1508,8 +1493,6 @@ class PizzaMsg {
<mat-dialog-content>Lorem ipsum dolor sit amet.</mat-dialog-content>
<mat-dialog-actions>
<button mat-dialog-close>Close</button>
<button class="close-without-text" mat-dialog-close></button>
<button class="close-icon-button" mat-icon-button mat-dialog-close>exit</button>
<button class="close-with-true" [mat-dialog-close]="true">Close and return true</button>
<button
class="close-with-aria-label"
@@ -1528,8 +1511,6 @@ class ContentElementDialog {}
<mat-dialog-content>Lorem ipsum dolor sit amet.</mat-dialog-content>
<mat-dialog-actions>
<button mat-dialog-close>Close</button>
<button class="close-without-text" mat-dialog-close></button>
<button class="close-icon-button" mat-icon-button mat-dialog-close>exit</button>
<button class="close-with-true" [mat-dialog-close]="true">Close and return true</button>
<button
class="close-with-aria-label"
-1
View File
@@ -45,7 +45,6 @@ export declare const matDialogAnimations: {
};
export declare class MatDialogClose implements OnInit, OnChanges {
_hasAriaLabel?: boolean;
_matDialogClose: any;
ariaLabel: string;
dialogRef: MatDialogRef<any>;