diff --git a/src/lib/dialog/dialog-config.ts b/src/lib/dialog/dialog-config.ts index add3efbe9063..b99863d1ebb3 100644 --- a/src/lib/dialog/dialog-config.ts +++ b/src/lib/dialog/dialog-config.ts @@ -89,6 +89,9 @@ export class MatDialogConfig { /** ID of the element that describes the dialog. */ ariaDescribedBy?: string | null = null; + /** ID of the element that labels the dialog. */ + ariaLabelledBy?: string | null = null; + /** Aria label to assign to the dialog element */ ariaLabel?: string | null = null; diff --git a/src/lib/dialog/dialog-container.ts b/src/lib/dialog/dialog-container.ts index fbcbacc6c8dd..0a22e9b9c300 100644 --- a/src/lib/dialog/dialog-container.ts +++ b/src/lib/dialog/dialog-container.ts @@ -87,7 +87,7 @@ export class MatDialogContainer extends BasePortalOutlet { _animationStateChanged = new EventEmitter(); /** ID of the element that should be considered as the dialog's label. */ - _ariaLabelledBy: string | null = null; + _ariaLabelledBy: string | null; /** ID for the container DOM element. */ _id: string; @@ -101,6 +101,7 @@ export class MatDialogContainer extends BasePortalOutlet { public _config: MatDialogConfig) { super(); + this._ariaLabelledBy = _config.ariaLabelledBy || null; } /** diff --git a/src/lib/dialog/dialog.spec.ts b/src/lib/dialog/dialog.spec.ts index 1195958d9fff..b2746d5079ab 100644 --- a/src/lib/dialog/dialog.spec.ts +++ b/src/lib/dialog/dialog.spec.ts @@ -1180,6 +1180,51 @@ describe('MatDialog', () => { } }); + describe('aria-labelledby', () => { + it('should be able to set a custom aria-labelledby', () => { + dialog.open(PizzaMsg, { + ariaLabelledBy: 'Labelled By', + viewContainerRef: testViewContainerRef + }); + viewContainerFixture.detectChanges(); + + const container = overlayContainerElement.querySelector('mat-dialog-container')!; + expect(container.getAttribute('aria-labelledby')).toBe('Labelled By'); + }); + + it('should not set the aria-labelledby automatically if it has an aria-label ' + + 'and an aria-labelledby', fakeAsync(() => { + dialog.open(ContentElementDialog, { + ariaLabel: 'Hello there', + ariaLabelledBy: 'Labelled By', + viewContainerRef: testViewContainerRef + }); + viewContainerFixture.detectChanges(); + tick(); + viewContainerFixture.detectChanges(); + + const container = overlayContainerElement.querySelector('mat-dialog-container')!; + expect(container.hasAttribute('aria-labelledby')).toBe(false); + })); + + it('should set the aria-labelledby attribute to the config provided aria-labelledby ' + + 'instead of the mat-dialog-title id', fakeAsync(() => { + dialog.open(ContentElementDialog, { + ariaLabelledBy: 'Labelled By', + viewContainerRef: testViewContainerRef + }); + viewContainerFixture.detectChanges(); + flush(); + let title = overlayContainerElement.querySelector('[mat-dialog-title]')!; + let container = overlayContainerElement.querySelector('mat-dialog-container')!; + flush(); + viewContainerFixture.detectChanges(); + + expect(title.id).toBeTruthy('Expected title element to have an id.'); + expect(container.getAttribute('aria-labelledby')).toBe('Labelled By'); + })); + }); + describe('aria-label', () => { it('should be able to set a custom aria-label', () => { dialog.open(PizzaMsg, { diff --git a/tools/public_api_guard/lib/dialog.d.ts b/tools/public_api_guard/lib/dialog.d.ts index 36cb08056b97..dfdf61450454 100644 --- a/tools/public_api_guard/lib/dialog.d.ts +++ b/tools/public_api_guard/lib/dialog.d.ts @@ -57,6 +57,7 @@ export declare class MatDialogClose implements OnInit, OnChanges { export declare class MatDialogConfig { ariaDescribedBy?: string | null; ariaLabel?: string | null; + ariaLabelledBy?: string | null; autoFocus?: boolean; backdropClass?: string; closeOnNavigation?: boolean;