From 44b2fe15839ee9c661cb61a3552e1e93b784e814 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Wed, 3 Jul 2019 23:33:31 +0200 Subject: [PATCH] fix(experimental/dialog): not moving focus to container if autoFocus is disabled and focus was moved while animating These changes incorporate #16297 and #16221 into the experimental dialog since it follows a similar focus capturing behavior to `MatDialogContainer`. They ensure that focus is on the dialog container container when the animation is over, because it could've moved while we were animating. It also has an extra check to ensure that we don't move focus unnecessarily if the consumer decided to move focus themselves somewhere within the container. --- src/cdk-experimental/dialog/dialog-container.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/cdk-experimental/dialog/dialog-container.ts b/src/cdk-experimental/dialog/dialog-container.ts index d264023ce4b6..c023d232a0b8 100644 --- a/src/cdk-experimental/dialog/dialog-container.ts +++ b/src/cdk-experimental/dialog/dialog-container.ts @@ -80,7 +80,7 @@ export class CdkDialogContainer extends BasePortalOutlet implements OnDestroy { private _elementFocusedBeforeDialogWasOpened: HTMLElement | null = null; /** The class that traps and manages focus within the dialog. */ - private _focusTrap = this._focusTrapFactory.create(this._elementRef.nativeElement, false); + private _focusTrap = this._focusTrapFactory.create(this._elementRef.nativeElement); // @HostBinding is used in the class as it is expected to be extended. Since @Component decorator // metadata is not inherited by child classes, instead the host binding data is defined in a way @@ -216,6 +216,8 @@ export class CdkDialogContainer extends BasePortalOutlet implements OnDestroy { * focus the dialog instead. */ private _autoFocusFirstTabbableElement() { + const element = this._elementRef.nativeElement; + // If were to attempt to focus immediately, then the content of the dialog would not yet be // ready in instances where change detection has to run first. To deal with this, we simply // wait for the microtask queue to be empty. @@ -224,9 +226,20 @@ export class CdkDialogContainer extends BasePortalOutlet implements OnDestroy { // If we didn't find any focusable elements inside the dialog, focus the // container so the user can't tab into other elements behind it. if (!hasMovedFocus) { - this._elementRef.nativeElement.focus(); + element.focus(); } }); + } else { + const activeElement = this._document.activeElement; + + // Otherwise ensure that focus is on the dialog container. It's possible that a different + // component tried to move focus while the open animation was running. See: + // https://github.com/angular/components/issues/16215. Note that we only want to do this + // if the focus isn't inside the dialog already, because it's possible that the consumer + // turned off `autoFocus` in order to move focus themselves. + if (activeElement !== element && !element.contains(activeElement)) { + element.focus(); + } } }