@@ -13,6 +13,7 @@ import {
13
13
OverlayConfig ,
14
14
OverlayRef ,
15
15
ScrollStrategy ,
16
+ OverlayContainer ,
16
17
} from '@angular/cdk/overlay' ;
17
18
import { ComponentPortal , ComponentType , PortalInjector , TemplatePortal } from '@angular/cdk/portal' ;
18
19
import { Location } from '@angular/common' ;
@@ -68,6 +69,7 @@ export class MatDialog {
68
69
private _openDialogsAtThisLevel : MatDialogRef < any > [ ] = [ ] ;
69
70
private _afterAllClosedAtThisLevel = new Subject < void > ( ) ;
70
71
private _afterOpenAtThisLevel = new Subject < MatDialogRef < any > > ( ) ;
72
+ private _ariaHiddenElements = new Map < Element , string | null > ( ) ;
71
73
72
74
/** Keeps track of the currently-open dialogs. */
73
75
get openDialogs ( ) : MatDialogRef < any > [ ] {
@@ -98,7 +100,8 @@ export class MatDialog {
98
100
@Optional ( ) location : Location ,
99
101
@Optional ( ) @Inject ( MAT_DIALOG_DEFAULT_OPTIONS ) private _defaultOptions ,
100
102
@Inject ( MAT_DIALOG_SCROLL_STRATEGY ) private _scrollStrategy ,
101
- @Optional ( ) @SkipSelf ( ) private _parentDialog : MatDialog ) {
103
+ @Optional ( ) @SkipSelf ( ) private _parentDialog : MatDialog ,
104
+ private _overlayContainer : OverlayContainer ) {
102
105
103
106
// Close all of the dialogs when the user goes forwards/backwards in history or when the
104
107
// location hash changes. Note that this usually doesn't include clicking on links (unless
@@ -129,6 +132,11 @@ export class MatDialog {
129
132
const dialogRef =
130
133
this . _attachDialogContent < T > ( componentOrTemplateRef , dialogContainer , overlayRef , config ) ;
131
134
135
+ // If this is the first dialog that we're opening, hide all the non-overlay content.
136
+ if ( ! this . openDialogs . length ) {
137
+ this . _hideNonDialogContentFromAssistiveTechnology ( ) ;
138
+ }
139
+
132
140
this . openDialogs . push ( dialogRef ) ;
133
141
dialogRef . afterClosed ( ) . subscribe ( ( ) => this . _removeOpenDialog ( dialogRef ) ) ;
134
142
this . afterOpen . next ( dialogRef ) ;
@@ -295,12 +303,49 @@ export class MatDialog {
295
303
if ( index > - 1 ) {
296
304
this . openDialogs . splice ( index , 1 ) ;
297
305
298
- // no open dialogs are left, call next on afterAllClosed Subject
306
+ // If all the dialogs were closed, remove/restore the `aria-hidden`
307
+ // to a the siblings and emit to the `afterAllClosed` stream.
299
308
if ( ! this . openDialogs . length ) {
309
+ this . _ariaHiddenElements . forEach ( ( previousValue , element ) => {
310
+ if ( previousValue ) {
311
+ element . setAttribute ( 'aria-hidden' , previousValue ) ;
312
+ } else {
313
+ element . removeAttribute ( 'aria-hidden' ) ;
314
+ }
315
+ } ) ;
316
+
317
+ this . _ariaHiddenElements . clear ( ) ;
300
318
this . _afterAllClosed . next ( ) ;
301
319
}
302
320
}
303
321
}
322
+
323
+ /**
324
+ * Hides all of the content that isn't an overlay from assistive technology.
325
+ */
326
+ private _hideNonDialogContentFromAssistiveTechnology ( ) {
327
+ const overlayContainer = this . _overlayContainer . getContainerElement ( ) ;
328
+
329
+ // Ensure that the overlay container is attached to the DOM.
330
+ if ( overlayContainer . parentElement ) {
331
+ const siblings = overlayContainer . parentElement . children ;
332
+
333
+ for ( let i = siblings . length - 1 ; i > - 1 ; i -- ) {
334
+ let sibling = siblings [ i ] ;
335
+
336
+ if ( sibling !== overlayContainer &&
337
+ sibling . nodeName !== 'SCRIPT' &&
338
+ sibling . nodeName !== 'STYLE' &&
339
+ ! sibling . hasAttribute ( 'aria-live' ) ) {
340
+
341
+ this . _ariaHiddenElements . set ( sibling , sibling . getAttribute ( 'aria-hidden' ) ) ;
342
+ sibling . setAttribute ( 'aria-hidden' , 'true' ) ;
343
+ }
344
+ }
345
+ }
346
+
347
+ }
348
+
304
349
}
305
350
306
351
/**
0 commit comments