From ccbfedcf1a23b28626c8a8ffce0ff55bdfa19caf Mon Sep 17 00:00:00 2001 From: crisbeto Date: Tue, 21 Feb 2017 19:45:12 +0100 Subject: [PATCH] fix(dialog): close all dialogs on popstate/hashchange Closes all of the open dialogs when the user goes forwards/backwards in history. Fixes #2601. --- src/lib/dialog/dialog.spec.ts | 40 ++++++++++++++++++++++++++++++++--- src/lib/dialog/dialog.ts | 12 ++++++++++- src/lib/dialog/index.ts | 2 ++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/lib/dialog/dialog.spec.ts b/src/lib/dialog/dialog.spec.ts index 53e67ca2066a..53caa582650a 100644 --- a/src/lib/dialog/dialog.spec.ts +++ b/src/lib/dialog/dialog.spec.ts @@ -16,6 +16,8 @@ import {NgModule, Inject, } from '@angular/core'; import {By} from '@angular/platform-browser'; +import {Location} from '@angular/common'; +import {SpyLocation} from '@angular/common/testing'; import {MdDialogModule} from './index'; import {MdDialog} from './dialog'; import {MdDialogContainer} from './dialog-container'; @@ -31,6 +33,7 @@ describe('MdDialog', () => { let testViewContainerRef: ViewContainerRef; let viewContainerFixture: ComponentFixture; + let mockLocation: SpyLocation; beforeEach(async(() => { TestBed.configureTestingModule({ @@ -39,15 +42,17 @@ describe('MdDialog', () => { {provide: OverlayContainer, useFactory: () => { overlayContainerElement = document.createElement('div'); return {getContainerElement: () => overlayContainerElement}; - }} + }}, + {provide: Location, useClass: SpyLocation} ], }); TestBed.compileComponents(); })); - beforeEach(inject([MdDialog], (d: MdDialog) => { + beforeEach(inject([MdDialog, Location], (d: MdDialog, l: Location) => { dialog = d; + mockLocation = l as SpyLocation; })); beforeEach(() => { @@ -323,6 +328,34 @@ describe('MdDialog', () => { }); })); + it('should close all dialogs when the user goes forwards/backwards in history', async(() => { + dialog.open(PizzaMsg); + dialog.open(PizzaMsg); + + expect(overlayContainerElement.querySelectorAll('md-dialog-container').length).toBe(2); + + mockLocation.simulateUrlPop(''); + viewContainerFixture.detectChanges(); + + viewContainerFixture.whenStable().then(() => { + expect(overlayContainerElement.querySelectorAll('md-dialog-container').length).toBe(0); + }); + })); + + it('should close all open dialogs when the location hash changes', async(() => { + dialog.open(PizzaMsg); + dialog.open(PizzaMsg); + + expect(overlayContainerElement.querySelectorAll('md-dialog-container').length).toBe(2); + + mockLocation.simulateHashChange(''); + viewContainerFixture.detectChanges(); + + viewContainerFixture.whenStable().then(() => { + expect(overlayContainerElement.querySelectorAll('md-dialog-container').length).toBe(0); + }); + })); + describe('passing in data', () => { it('should be able to pass in data', () => { let config = { @@ -485,7 +518,8 @@ describe('MdDialog with a parent MdDialog', () => { {provide: OverlayContainer, useFactory: () => { overlayContainerElement = document.createElement('div'); return {getContainerElement: () => overlayContainerElement}; - }} + }}, + {provide: Location, useClass: SpyLocation} ], }); diff --git a/src/lib/dialog/dialog.ts b/src/lib/dialog/dialog.ts index 3cc1c57ebe67..10bf07373c58 100644 --- a/src/lib/dialog/dialog.ts +++ b/src/lib/dialog/dialog.ts @@ -1,4 +1,5 @@ import {Injector, ComponentRef, Injectable, Optional, SkipSelf, TemplateRef} from '@angular/core'; +import {Location} from '@angular/common'; import {Observable} from 'rxjs/Observable'; import {Subject} from 'rxjs/Subject'; import {Overlay, OverlayRef, ComponentType, OverlayState, ComponentPortal} from '../core'; @@ -46,7 +47,16 @@ export class MdDialog { constructor( private _overlay: Overlay, private _injector: Injector, - @Optional() @SkipSelf() private _parentDialog: MdDialog) { } + private _location: Location, + @Optional() @SkipSelf() private _parentDialog: MdDialog) { + + // Close all of the dialogs when the user goes forwards/backwards in history or when the + // location hash changes. Note that this usually doesn't include clicking on links (unless + // the user is using the `HashLocationStrategy`). + if (!_parentDialog) { + _location.subscribe(() => this.closeAll()); + } + } /** * Opens a modal dialog containing the given component. diff --git a/src/lib/dialog/index.ts b/src/lib/dialog/index.ts index 664f8e4b0ab5..ee9a480b6396 100644 --- a/src/lib/dialog/index.ts +++ b/src/lib/dialog/index.ts @@ -1,4 +1,5 @@ import {NgModule, ModuleWithProviders} from '@angular/core'; +import {CommonModule} from '@angular/common'; import { OverlayModule, PortalModule, @@ -17,6 +18,7 @@ import { @NgModule({ imports: [ + CommonModule, OverlayModule, PortalModule, A11yModule,