diff --git a/src/lib/dialog/dialog-content-directives.ts b/src/lib/dialog/dialog-content-directives.ts index 770da2d8d853..9cfdb7f37d08 100644 --- a/src/lib/dialog/dialog-content-directives.ts +++ b/src/lib/dialog/dialog-content-directives.ts @@ -29,7 +29,7 @@ let dialogElementUid = 0; exportAs: 'matDialogClose', host: { '(click)': 'dialogRef.close(dialogResult)', - '[attr.aria-label]': 'ariaLabel', + '[attr.aria-label]': '_hasAriaLabel ? ariaLabel : null', 'type': 'button', // Prevents accidental form submits. } }) @@ -42,6 +42,12 @@ export class MatDialogClose implements OnInit, OnChanges { @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, private _elementRef: ElementRef, @@ -56,6 +62,17 @@ 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) { @@ -64,6 +81,10 @@ export class MatDialogClose implements OnInit, OnChanges { if (proxiedChange) { this.dialogResult = proxiedChange.currentValue; } + + if (changes.ariaLabel) { + this._hasAriaLabel = !!changes.ariaLabel.currentValue; + } } } diff --git a/src/lib/dialog/dialog.spec.ts b/src/lib/dialog/dialog.spec.ts index b2746d5079ab..246a1863ba0f 100644 --- a/src/lib/dialog/dialog.spec.ts +++ b/src/lib/dialog/dialog.spec.ts @@ -1143,11 +1143,26 @@ 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]')!; @@ -1493,11 +1508,13 @@ class PizzaMsg { Lorem ipsum dolor sit amet. + + + [mat-dialog-close]="true">
Should not close
` @@ -1511,11 +1528,13 @@ class ContentElementDialog {} Lorem ipsum dolor sit amet. + + + [mat-dialog-close]="true">
Should not close
diff --git a/tools/public_api_guard/lib/dialog.d.ts b/tools/public_api_guard/lib/dialog.d.ts index dfdf61450454..1a5aa83166d0 100644 --- a/tools/public_api_guard/lib/dialog.d.ts +++ b/tools/public_api_guard/lib/dialog.d.ts @@ -45,6 +45,7 @@ export declare const matDialogAnimations: { }; export declare class MatDialogClose implements OnInit, OnChanges { + _hasAriaLabel?: boolean; _matDialogClose: any; ariaLabel: string; dialogRef: MatDialogRef;