Skip to content

Commit e57f259

Browse files
committed
refactor(material/dialog): switch to CDK dialog internally
Switches the Material dialog to be based on the CDK dialog.
1 parent fb4e395 commit e57f259

File tree

17 files changed

+250
-663
lines changed

17 files changed

+250
-663
lines changed

src/cdk/dialog/dialog-container.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,14 @@ export class CdkDialogContainer<C extends DialogConfig = DialogConfig>
161161
return this._portalOutlet.attachDomPortal(portal);
162162
};
163163

164+
// TODO(crisbeto): this shouldn't be exposed, but there are internal references to it.
165+
/** Captures focus if it isn't already inside the dialog. */
166+
_recaptureFocus() {
167+
if (!this._containsFocus()) {
168+
this._trapFocus();
169+
}
170+
}
171+
164172
/**
165173
* Focuses the provided element. If the element is not focusable, it will add a tabIndex
166174
* attribute to forcefully focus it. The attribute is removed after focus is moved.
@@ -316,8 +324,8 @@ export class CdkDialogContainer<C extends DialogConfig = DialogConfig>
316324
// Clicking on the backdrop will move focus out of dialog.
317325
// Recapture it if closing via the backdrop is disabled.
318326
this._overlayRef.backdropClick().subscribe(() => {
319-
if (this._config.disableClose && !this._containsFocus()) {
320-
this._trapFocus();
327+
if (this._config.disableClose) {
328+
this._recaptureFocus();
321329
}
322330
});
323331
}

src/material-experimental/mdc-dialog/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ ng_test_library(
6464
":mdc-dialog",
6565
"//src/cdk/a11y",
6666
"//src/cdk/bidi",
67+
"//src/cdk/dialog",
6768
"//src/cdk/keycodes",
6869
"//src/cdk/overlay",
6970
"//src/cdk/platform",

src/material-experimental/mdc-dialog/dialog-container.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,18 @@
77
*/
88

99
import {FocusMonitor, FocusTrapFactory, InteractivityChecker} from '@angular/cdk/a11y';
10+
import {OverlayRef} from '@angular/cdk/overlay';
1011
import {DOCUMENT} from '@angular/common';
1112
import {
1213
ChangeDetectionStrategy,
13-
ChangeDetectorRef,
1414
Component,
1515
ElementRef,
1616
Inject,
1717
OnDestroy,
1818
Optional,
1919
ViewEncapsulation,
2020
NgZone,
21+
AfterViewInit,
2122
} from '@angular/core';
2223
import {MatDialogConfig, _MatDialogContainerBase} from '@angular/material/dialog';
2324
import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';
@@ -38,16 +39,19 @@ import {cssClasses, numbers} from '@material/dialog';
3839
host: {
3940
'class': 'mat-mdc-dialog-container mdc-dialog',
4041
'tabindex': '-1',
41-
'aria-modal': 'true',
42-
'[id]': '_id',
42+
'[attr.aria-modal]': '_config.ariaModal',
43+
'[id]': '_config.id',
4344
'[attr.role]': '_config.role',
4445
'[attr.aria-labelledby]': '_config.ariaLabel ? null : _ariaLabelledBy',
4546
'[attr.aria-label]': '_config.ariaLabel',
4647
'[attr.aria-describedby]': '_config.ariaDescribedBy || null',
4748
'[class._mat-animation-noopable]': '!_animationsEnabled',
4849
},
4950
})
50-
export class MatDialogContainer extends _MatDialogContainerBase implements OnDestroy {
51+
export class MatDialogContainer
52+
extends _MatDialogContainerBase
53+
implements OnDestroy, AfterViewInit
54+
{
5155
/** Whether animations are enabled. */
5256
_animationsEnabled: boolean = this._animationMode !== 'NoopAnimations';
5357

@@ -67,30 +71,31 @@ export class MatDialogContainer extends _MatDialogContainerBase implements OnDes
6771
constructor(
6872
elementRef: ElementRef,
6973
focusTrapFactory: FocusTrapFactory,
70-
changeDetectorRef: ChangeDetectorRef,
7174
@Optional() @Inject(DOCUMENT) document: any,
72-
config: MatDialogConfig,
75+
dialogConfig: MatDialogConfig,
7376
checker: InteractivityChecker,
7477
ngZone: NgZone,
78+
overlayRef: OverlayRef,
7579
@Optional() @Inject(ANIMATION_MODULE_TYPE) private _animationMode?: string,
7680
focusMonitor?: FocusMonitor,
7781
) {
7882
super(
7983
elementRef,
8084
focusTrapFactory,
81-
changeDetectorRef,
8285
document,
83-
config,
86+
dialogConfig,
8487
checker,
8588
ngZone,
89+
overlayRef,
8690
focusMonitor,
8791
);
8892
}
8993

90-
override _initializeWithAttachedContent() {
94+
override ngAfterViewInit(): void {
9195
// Delegate to the original dialog-container initialization (i.e. saving the
9296
// previous element, setting up the focus trap and moving focus to the container).
93-
super._initializeWithAttachedContent();
97+
super.ngAfterViewInit();
98+
9499
// Note: Usually we would be able to use the MDC dialog foundation here to handle
95100
// the dialog animation for us, but there are a few reasons why we just leverage
96101
// their styles and not use the runtime foundation code:
@@ -103,7 +108,9 @@ export class MatDialogContainer extends _MatDialogContainerBase implements OnDes
103108
this._startOpenAnimation();
104109
}
105110

106-
ngOnDestroy() {
111+
override ngOnDestroy() {
112+
super.ngOnDestroy();
113+
107114
if (this._animationTimer !== null) {
108115
clearTimeout(this._animationTimer);
109116
}
@@ -177,7 +184,6 @@ export class MatDialogContainer extends _MatDialogContainerBase implements OnDes
177184
*/
178185
private _finishDialogClose = () => {
179186
this._clearAnimationClasses();
180-
this._restoreFocus();
181187
this._animationStateChanged.emit({state: 'closed', totalTime: this._closeAnimationDuration});
182188
};
183189

src/material-experimental/mdc-dialog/dialog-ref.ts

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,9 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {OverlayRef} from '@angular/cdk/overlay';
109
import {MatDialogRef as NonMdcDialogRef} from '@angular/material/dialog';
11-
import {MatDialogContainer} from './dialog-container';
12-
13-
// Counter for unique dialog ids.
14-
let uniqueId = 0;
1510

1611
/**
1712
* Reference to a dialog opened via the MatDialog service.
1813
*/
19-
export class MatDialogRef<T, R = any> extends NonMdcDialogRef<T, R> {
20-
constructor(
21-
overlayRef: OverlayRef,
22-
containerInstance: MatDialogContainer,
23-
id: string = `mat-mdc-dialog-${uniqueId++}`,
24-
) {
25-
super(overlayRef, containerInstance, id);
26-
}
27-
}
14+
export class MatDialogRef<T, R = any> extends NonMdcDialogRef<T, R> {}

src/material-experimental/mdc-dialog/dialog.spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,6 +1325,7 @@ describe('MDC-based MatDialog', () => {
13251325

13261326
tick(500);
13271327
viewContainerFixture.detectChanges();
1328+
flushMicrotasks();
13281329
expect(lastFocusOrigin!).withContext('Expected the trigger button to be blurred').toBeNull();
13291330

13301331
dispatchKeyboardEvent(document.body, 'keydown', ESCAPE);
@@ -1359,6 +1360,7 @@ describe('MDC-based MatDialog', () => {
13591360

13601361
tick(500);
13611362
viewContainerFixture.detectChanges();
1363+
flushMicrotasks();
13621364
expect(lastFocusOrigin!).withContext('Expected the trigger button to be blurred').toBeNull();
13631365

13641366
const backdrop = overlayContainerElement.querySelector(
@@ -1395,6 +1397,7 @@ describe('MDC-based MatDialog', () => {
13951397

13961398
tick(500);
13971399
viewContainerFixture.detectChanges();
1400+
flushMicrotasks();
13981401
expect(lastFocusOrigin!).withContext('Expected the trigger button to be blurred').toBeNull();
13991402

14001403
const closeButton = overlayContainerElement.querySelector(
@@ -1434,6 +1437,7 @@ describe('MDC-based MatDialog', () => {
14341437

14351438
tick(500);
14361439
viewContainerFixture.detectChanges();
1440+
flushMicrotasks();
14371441
expect(lastFocusOrigin!).withContext('Expected the trigger button to be blurred').toBeNull();
14381442

14391443
const closeButton = overlayContainerElement.querySelector(

src/material-experimental/mdc-dialog/dialog.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,18 @@
88

99
import {Overlay, OverlayContainer, ScrollStrategy} from '@angular/cdk/overlay';
1010
import {Location} from '@angular/common';
11-
import {Inject, Injectable, InjectionToken, Injector, Optional, SkipSelf} from '@angular/core';
11+
import {
12+
ANIMATION_MODULE_TYPE,
13+
Inject,
14+
Injectable,
15+
InjectionToken,
16+
Injector,
17+
Optional,
18+
SkipSelf,
19+
} from '@angular/core';
1220
import {_MatDialogBase, MatDialogConfig} from '@angular/material/dialog';
1321
import {MatDialogContainer} from './dialog-container';
1422
import {MatDialogRef} from './dialog-ref';
15-
import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';
1623

1724
/** Injection token that can be used to access the data that was passed in to a dialog. */
1825
export const MAT_DIALOG_DATA = new InjectionToken<any>('MatMdcDialogData');
@@ -57,6 +64,10 @@ export class MatDialog extends _MatDialogBase<MatDialogContainer> {
5764
@Optional() @Inject(MAT_DIALOG_DEFAULT_OPTIONS) defaultOptions: MatDialogConfig,
5865
@Inject(MAT_DIALOG_SCROLL_STRATEGY) scrollStrategy: any,
5966
@Optional() @SkipSelf() parentDialog: MatDialog,
67+
/**
68+
* @deprecated No longer used. To be removed.
69+
* @breaking-change 15.0.0
70+
*/
6071
overlayContainer: OverlayContainer,
6172
/**
6273
* @deprecated No longer used. To be removed.
@@ -78,5 +89,7 @@ export class MatDialog extends _MatDialogBase<MatDialogContainer> {
7889
MAT_DIALOG_DATA,
7990
animationMode,
8091
);
92+
93+
this._idPrefix = 'mat-mdc-dialog-';
8194
}
8295
}

src/material-experimental/mdc-dialog/module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
import {DialogModule} from '@angular/cdk/dialog';
910
import {OverlayModule} from '@angular/cdk/overlay';
1011
import {PortalModule} from '@angular/cdk/portal';
1112
import {NgModule} from '@angular/core';
@@ -20,7 +21,7 @@ import {
2021
} from './dialog-content-directives';
2122

2223
@NgModule({
23-
imports: [OverlayModule, PortalModule, MatCommonModule],
24+
imports: [DialogModule, OverlayModule, PortalModule, MatCommonModule],
2425
exports: [
2526
MatDialogContainer,
2627
MatDialogClose,

src/material-experimental/mdc-dialog/public-api.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ export {
1717
MatDialogState,
1818
MatDialogConfig,
1919
matDialogAnimations,
20-
throwMatDialogContentAlreadyAttachedError,
2120
DialogRole,
2221
DialogPosition,
2322
MAT_DIALOG_SCROLL_STRATEGY_FACTORY,

src/material/dialog/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ ng_module(
2323
"//src:dev_mode_types",
2424
"//src/cdk/a11y",
2525
"//src/cdk/bidi",
26+
"//src/cdk/dialog",
2627
"//src/cdk/keycodes",
2728
"//src/cdk/overlay",
2829
"//src/cdk/platform",

src/material/dialog/dialog-config.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,14 @@ export class MatDialogConfig<D = any> {
112112
autoFocus?: AutoFocusTarget | string | boolean = 'first-tabbable';
113113

114114
/**
115-
* Whether the dialog should restore focus to the
116-
* previously-focused element, after it's closed.
115+
* Whether the dialog should restore focus to the previously-focused element upon closing.
116+
* Has the following behavior based on the type that is passed in:
117+
* - `boolean` - when true, will return focus to the element that was focused before the dialog
118+
* was opened, otherwise won't restore focus at all.
119+
* - `string` - focus will be restored to the first element that matches the CSS selector.
120+
* - `HTMLElement` - focus will be restored to the specific element.
117121
*/
118-
restoreFocus?: boolean = true;
122+
restoreFocus?: boolean | string | HTMLElement = true;
119123

120124
/** Whether to wait for the opening animation to finish before trapping focus. */
121125
delayFocusTrap?: boolean = true;

0 commit comments

Comments
 (0)