diff --git a/src/lib/core/overlay/overlay-ref.ts b/src/lib/core/overlay/overlay-ref.ts index 4ebaa2d2745e..22d18db624c3 100644 --- a/src/lib/core/overlay/overlay-ref.ts +++ b/src/lib/core/overlay/overlay-ref.ts @@ -92,7 +92,9 @@ export class OverlayRef implements PortalHost { // Add class to fade-in the backdrop after one frame. requestAnimationFrame(() => { - this._backdropElement.classList.add('md-overlay-backdrop-showing'); + if (this._backdropElement) { + this._backdropElement.classList.add('md-overlay-backdrop-showing'); + } }); } @@ -101,10 +103,11 @@ export class OverlayRef implements PortalHost { let backdropToDetach = this._backdropElement; if (backdropToDetach) { - backdropToDetach.classList.remove('md-overlay-backdrop-showing'); - backdropToDetach.classList.remove(this._state.backdropClass); - backdropToDetach.addEventListener('transitionend', () => { - backdropToDetach.parentNode.removeChild(backdropToDetach); + let finishDetach = () => { + // It may not be attached to anything in certain cases (e.g. unit tests). + if (backdropToDetach && backdropToDetach.parentNode) { + backdropToDetach.parentNode.removeChild(backdropToDetach); + } // It is possible that a new portal has been attached to this overlay since we started // removing the backdrop. If that is the case, only clear the backdrop reference if it @@ -112,7 +115,16 @@ export class OverlayRef implements PortalHost { if (this._backdropElement == backdropToDetach) { this._backdropElement = null; } - }); + }; + + backdropToDetach.classList.remove('md-overlay-backdrop-showing'); + backdropToDetach.classList.remove(this._state.backdropClass); + backdropToDetach.addEventListener('transitionend', finishDetach); + + // If the backdrop doesn't have a transition, the `transitionend` event won't fire. + // In this case we make it unclickable and we try to remove it after a delay. + backdropToDetach.style.pointerEvents = 'none'; + setTimeout(finishDetach, 500); } } } diff --git a/src/lib/core/overlay/overlay.spec.ts b/src/lib/core/overlay/overlay.spec.ts index b0b63402c4d5..05c0dc39d2b6 100644 --- a/src/lib/core/overlay/overlay.spec.ts +++ b/src/lib/core/overlay/overlay.spec.ts @@ -172,7 +172,7 @@ describe('Overlay', () => { overlayRef.attach(componentPortal); viewContainerFixture.detectChanges(); - let backdrop = overlayContainerElement.querySelector('.md-overlay-backdrop'); + let backdrop = overlayContainerElement.querySelector('.md-overlay-backdrop') as HTMLElement; expect(backdrop).toBeTruthy(); expect(backdrop.classList).not.toContain('md-overlay-backdrop-showing'); @@ -188,7 +188,7 @@ describe('Overlay', () => { overlayRef.attach(componentPortal); viewContainerFixture.detectChanges(); - let backdrop = overlayContainerElement.querySelector('.md-overlay-backdrop'); + let backdrop = overlayContainerElement.querySelector('.md-overlay-backdrop') as HTMLElement; expect(backdrop.classList).toContain('md-overlay-dark-backdrop'); }); @@ -199,10 +199,24 @@ describe('Overlay', () => { overlayRef.attach(componentPortal); viewContainerFixture.detectChanges(); - let backdrop = overlayContainerElement.querySelector('.md-overlay-backdrop'); + let backdrop = overlayContainerElement.querySelector('.md-overlay-backdrop') as HTMLElement; expect(backdrop.classList).toContain('md-overlay-transparent-backdrop'); }); + it('should disable the pointer events of a backdrop that is being removed', () => { + let overlayRef = overlay.create(config); + overlayRef.attach(componentPortal); + + viewContainerFixture.detectChanges(); + let backdrop = overlayContainerElement.querySelector('.md-overlay-backdrop') as HTMLElement; + + expect(backdrop.style.pointerEvents).toBeFalsy(); + + overlayRef.detach(); + + expect(backdrop.style.pointerEvents).toBe('none'); + }); + }); }); diff --git a/src/lib/dialog/dialog.spec.ts b/src/lib/dialog/dialog.spec.ts index d5d32f5d1994..26b5b7855fd6 100644 --- a/src/lib/dialog/dialog.spec.ts +++ b/src/lib/dialog/dialog.spec.ts @@ -5,6 +5,7 @@ import { flushMicrotasks, ComponentFixture, TestBed, + tick, } from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {NgModule, Component, Directive, ViewChild, ViewContainerRef} from '@angular/core'; @@ -207,6 +208,7 @@ describe('MdDialog', () => { .not.toBe('dialog-trigger', 'Expected the focus to change when dialog was opened.'); dialogRef.close(); + tick(500); viewContainerFixture.detectChanges(); flushMicrotasks();