Skip to content

Commit c55c0e1

Browse files
crisbetojosephperrott
authored andcommitted
fix(overlay): clear timeout if the backdrop transition completes early (#11938)
1 parent 8e44f89 commit c55c0e1

File tree

2 files changed

+21
-2
lines changed

2 files changed

+21
-2
lines changed

src/cdk/overlay/overlay-ref.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
332332
let backdropToDetach = this._backdropElement;
333333

334334
if (backdropToDetach) {
335+
let timeoutId: number;
335336
let finishDetach = () => {
336337
// It may not be attached to anything in certain cases (e.g. unit tests).
337338
if (backdropToDetach && backdropToDetach.parentNode) {
@@ -344,6 +345,8 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
344345
if (this._backdropElement == backdropToDetach) {
345346
this._backdropElement = null;
346347
}
348+
349+
clearTimeout(timeoutId);
347350
};
348351

349352
backdropToDetach.classList.remove('cdk-overlay-backdrop-showing');
@@ -352,7 +355,9 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
352355
this._toggleClasses(backdropToDetach, this._config.backdropClass, false);
353356
}
354357

355-
backdropToDetach.addEventListener('transitionend', finishDetach);
358+
this._ngZone.runOutsideAngular(() => {
359+
backdropToDetach!.addEventListener('transitionend', finishDetach);
360+
});
356361

357362
// If the backdrop doesn't have a transition, the `transitionend` event won't fire.
358363
// In this case we make it unclickable and we try to remove it after a delay.
@@ -361,7 +366,7 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
361366
// Run this outside the Angular zone because there's nothing that Angular cares about.
362367
// If it were to run inside the Angular zone, every test that used Overlay would have to be
363368
// either async or fakeAsync.
364-
this._ngZone.runOutsideAngular(() => setTimeout(finishDetach, 500));
369+
timeoutId = this._ngZone.runOutsideAngular(() => setTimeout(finishDetach, 500));
365370
}
366371
}
367372

src/cdk/overlay/overlay.spec.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
EventEmitter,
1010
} from '@angular/core';
1111
import {Direction, Directionality} from '@angular/cdk/bidi';
12+
import {dispatchFakeEvent} from '@angular/cdk/testing';
1213
import {
1314
ComponentPortal,
1415
PortalModule,
@@ -292,6 +293,19 @@ describe('Overlay', () => {
292293
expect(overlayRef.backdropElement).toBeFalsy('Expected backdrop element not to be referenced.');
293294
}));
294295

296+
it('should clear the backdrop timeout if the transition finishes first', fakeAsync(() => {
297+
const overlayRef = overlay.create({hasBackdrop: true});
298+
299+
overlayRef.attach(componentPortal);
300+
overlayRef.detach();
301+
302+
const backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop')!;
303+
dispatchFakeEvent(backdrop, 'transitionend');
304+
305+
// Note: we don't `tick` or `flush` here. The assertion is that
306+
// `fakeAsync` will throw if we have an unflushed timer.
307+
}));
308+
295309
it('should be able to use the `Overlay` provider during app initialization', () => {
296310
/** Dummy provider that depends on `Overlay`. */
297311
@Injectable()

0 commit comments

Comments
 (0)