Skip to content

Commit e61deb9

Browse files
crisbetojelbourn
authored andcommitted
fix(dialog): focus trap not attached if autoFocus is disabled (#19251)
In #18826 some code was moved around, and as a result, the focus trap was being created lazily only when we interact with its API. The problem is that this means that focus trapping will be disabled if `autoFocus` was turned off. Fixes #19246.
1 parent d3ed9c4 commit e61deb9

File tree

2 files changed

+27
-16
lines changed

2 files changed

+27
-16
lines changed

src/material/dialog/dialog-container.ts

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export class MatDialogContainer extends BasePortalOutlet {
116116
throwMatDialogContentAlreadyAttachedError();
117117
}
118118

119-
this._savePreviouslyFocusedElement();
119+
this._setupFocusTrap();
120120
return this._portalOutlet.attachComponentPortal(portal);
121121
}
122122

@@ -129,7 +129,7 @@ export class MatDialogContainer extends BasePortalOutlet {
129129
throwMatDialogContentAlreadyAttachedError();
130130
}
131131

132-
this._savePreviouslyFocusedElement();
132+
this._setupFocusTrap();
133133
return this._portalOutlet.attachTemplatePortal(portal);
134134
}
135135

@@ -144,14 +144,14 @@ export class MatDialogContainer extends BasePortalOutlet {
144144
throwMatDialogContentAlreadyAttachedError();
145145
}
146146

147-
this._savePreviouslyFocusedElement();
147+
this._setupFocusTrap();
148148
return this._portalOutlet.attachDomPortal(portal);
149149
}
150150

151151
/** Moves focus back into the dialog if it was moved out. */
152152
_recaptureFocus() {
153153
if (!this._containsFocus()) {
154-
const focusWasTrapped = this._getFocusTrap().focusInitialElement();
154+
const focusWasTrapped = this._focusTrap.focusInitialElement();
155155

156156
if (!focusWasTrapped) {
157157
this._elementRef.nativeElement.focus();
@@ -165,7 +165,7 @@ export class MatDialogContainer extends BasePortalOutlet {
165165
// ready in instances where change detection has to run first. To deal with this, we simply
166166
// wait for the microtask queue to be empty.
167167
if (this._config.autoFocus) {
168-
this._getFocusTrap().focusInitialElementWhenReady();
168+
this._focusTrap.focusInitialElementWhenReady();
169169
} else if (!this._containsFocus()) {
170170
// Otherwise ensure that focus is on the dialog container. It's possible that a different
171171
// component tried to move focus while the open animation was running. See:
@@ -200,8 +200,15 @@ export class MatDialogContainer extends BasePortalOutlet {
200200
}
201201
}
202202

203-
/** Saves a reference to the element that was focused before the dialog was opened. */
204-
private _savePreviouslyFocusedElement() {
203+
/**
204+
* Sets up the focus trand and saves a reference to the
205+
* element that was focused before the dialog was opened.
206+
*/
207+
private _setupFocusTrap() {
208+
if (!this._focusTrap) {
209+
this._focusTrap = this._focusTrapFactory.create(this._elementRef.nativeElement);
210+
}
211+
205212
if (this._document) {
206213
this._elementFocusedBeforeDialogWasOpened = this._document.activeElement as HTMLElement;
207214

@@ -222,15 +229,6 @@ export class MatDialogContainer extends BasePortalOutlet {
222229
return element === activeElement || element.contains(activeElement);
223230
}
224231

225-
/** Gets the focus trap associated with the dialog. */
226-
private _getFocusTrap() {
227-
if (!this._focusTrap) {
228-
this._focusTrap = this._focusTrapFactory.create(this._elementRef.nativeElement);
229-
}
230-
231-
return this._focusTrap;
232-
}
233-
234232
/** Callback, invoked whenever an animation on the host completes. */
235233
_onAnimationDone(event: AnimationEvent) {
236234
if (event.toState === 'enter') {

src/material/dialog/dialog.spec.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,6 +1075,19 @@ describe('MatDialog', () => {
10751075
expect(document.activeElement!.tagName).not.toBe('INPUT');
10761076
}));
10771077

1078+
it('should attach the focus trap even if automatic focus is disabled', fakeAsync(() => {
1079+
dialog.open(PizzaMsg, {
1080+
viewContainerRef: testViewContainerRef,
1081+
autoFocus: false
1082+
});
1083+
1084+
viewContainerFixture.detectChanges();
1085+
flushMicrotasks();
1086+
1087+
expect(overlayContainerElement.querySelectorAll('.cdk-focus-trap-anchor').length)
1088+
.toBeGreaterThan(0);
1089+
}));
1090+
10781091
it('should re-focus trigger element when dialog closes', fakeAsync(() => {
10791092
// Create a element that has focus before the dialog is opened.
10801093
let button = document.createElement('button');

0 commit comments

Comments
 (0)