Skip to content

Commit 5c6f25e

Browse files
crisbetojelbourn
authored andcommitted
fix(bottom-sheet): close on page navigation (#12106)
Similarly to the dialog, closes the bottom sheet when the user navigates to another page. The behavior can be opted out of.
1 parent fcc8875 commit 5c6f25e

File tree

4 files changed

+71
-7
lines changed

4 files changed

+71
-7
lines changed

src/lib/bottom-sheet/bottom-sheet-config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,7 @@ export class MatBottomSheetConfig<D = any> {
3939

4040
/** Aria label to assign to the bottom sheet element. */
4141
ariaLabel?: string | null = null;
42+
43+
/** Whether the bottom sheet should close when the user goes backwards/forwards in history. */
44+
closeOnNavigation?: boolean = true;
4245
}

src/lib/bottom-sheet/bottom-sheet-ref.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
import {Location} from '@angular/common';
910
import {ESCAPE} from '@angular/cdk/keycodes';
1011
import {OverlayRef} from '@angular/cdk/overlay';
11-
import {merge, Observable, Subject} from 'rxjs';
12+
import {merge, Observable, Subject, SubscriptionLike, Subscription} from 'rxjs';
1213
import {filter, take} from 'rxjs/operators';
1314
import {MatBottomSheetContainer} from './bottom-sheet-container';
1415

@@ -35,7 +36,13 @@ export class MatBottomSheetRef<T = any, R = any> {
3536
/** Result to be passed down to the `afterDismissed` stream. */
3637
private _result: R | undefined;
3738

38-
constructor(containerInstance: MatBottomSheetContainer, private _overlayRef: OverlayRef) {
39+
/** Subscription to changes in the user's location. */
40+
private _locationChanges: SubscriptionLike = Subscription.EMPTY;
41+
42+
constructor(
43+
containerInstance: MatBottomSheetContainer,
44+
private _overlayRef: OverlayRef,
45+
location?: Location) {
3946
this.containerInstance = containerInstance;
4047

4148
// Emit when opening animation completes
@@ -54,6 +61,7 @@ export class MatBottomSheetRef<T = any, R = any> {
5461
take(1)
5562
)
5663
.subscribe(() => {
64+
this._locationChanges.unsubscribe();
5765
this._overlayRef.dispose();
5866
this._afterDismissed.next(this._result);
5967
this._afterDismissed.complete();
@@ -65,6 +73,14 @@ export class MatBottomSheetRef<T = any, R = any> {
6573
_overlayRef.keydownEvents().pipe(filter(event => event.keyCode === ESCAPE))
6674
).subscribe(() => this.dismiss());
6775
}
76+
77+
if (location) {
78+
this._locationChanges = location.subscribe(() => {
79+
if (containerInstance.bottomSheetConfig.closeOnNavigation) {
80+
this.dismiss();
81+
}
82+
});
83+
}
6884
}
6985

7086
/**

src/lib/bottom-sheet/bottom-sheet.spec.ts

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import {
2121
TestBed,
2222
tick,
2323
} from '@angular/core/testing';
24+
import {Location} from '@angular/common';
25+
import {SpyLocation} from '@angular/common/testing';
2426
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
2527
import {MatBottomSheet} from './bottom-sheet';
2628
import {MAT_BOTTOM_SHEET_DATA, MatBottomSheetConfig} from './bottom-sheet-config';
@@ -36,19 +38,24 @@ describe('MatBottomSheet', () => {
3638

3739
let testViewContainerRef: ViewContainerRef;
3840
let viewContainerFixture: ComponentFixture<ComponentWithChildViewContainer>;
41+
let mockLocation: SpyLocation;
3942

4043
beforeEach(fakeAsync(() => {
4144
TestBed
42-
.configureTestingModule({imports: [MatBottomSheetModule, BottomSheetTestModule]})
45+
.configureTestingModule({
46+
imports: [MatBottomSheetModule, BottomSheetTestModule],
47+
providers: [{provide: Location, useClass: SpyLocation}]
48+
})
4349
.compileComponents();
4450
}));
4551

46-
beforeEach(inject([MatBottomSheet, OverlayContainer, ViewportRuler],
47-
(bs: MatBottomSheet, oc: OverlayContainer, vr: ViewportRuler) => {
52+
beforeEach(inject([MatBottomSheet, OverlayContainer, ViewportRuler, Location],
53+
(bs: MatBottomSheet, oc: OverlayContainer, vr: ViewportRuler, l: Location) => {
4854
bottomSheet = bs;
4955
overlayContainer = oc;
5056
viewportRuler = vr;
5157
overlayContainerElement = oc.getContainerElement();
58+
mockLocation = l as SpyLocation;
5259
}));
5360

5461
afterEach(() => {
@@ -349,6 +356,42 @@ describe('MatBottomSheet', () => {
349356
expect(spy).toHaveBeenCalledWith(1337);
350357
}));
351358

359+
it('should close the bottom sheet when going forwards/backwards in history', fakeAsync(() => {
360+
bottomSheet.open(PizzaMsg);
361+
362+
expect(overlayContainerElement.querySelector('mat-bottom-sheet-container')).toBeTruthy();
363+
364+
mockLocation.simulateUrlPop('');
365+
viewContainerFixture.detectChanges();
366+
flush();
367+
368+
expect(overlayContainerElement.querySelector('mat-bottom-sheet-container')).toBeFalsy();
369+
}));
370+
371+
it('should close the bottom sheet when the location hash changes', fakeAsync(() => {
372+
bottomSheet.open(PizzaMsg);
373+
374+
expect(overlayContainerElement.querySelector('mat-bottom-sheet-container')).toBeTruthy();
375+
376+
mockLocation.simulateHashChange('');
377+
viewContainerFixture.detectChanges();
378+
flush();
379+
380+
expect(overlayContainerElement.querySelector('mat-bottom-sheet-container')).toBeFalsy();
381+
}));
382+
383+
it('should allow the consumer to disable closing a bottom sheet on navigation', fakeAsync(() => {
384+
bottomSheet.open(PizzaMsg, {closeOnNavigation: false});
385+
386+
expect(overlayContainerElement.querySelector('mat-bottom-sheet-container')).toBeTruthy();
387+
388+
mockLocation.simulateUrlPop('');
389+
viewContainerFixture.detectChanges();
390+
flush();
391+
392+
expect(overlayContainerElement.querySelector('mat-bottom-sheet-container')).toBeTruthy();
393+
}));
394+
352395
describe('passing in data', () => {
353396
it('should be able to pass in data', () => {
354397
const config = {

src/lib/bottom-sheet/bottom-sheet.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {Directionality} from '@angular/cdk/bidi';
1010
import {Overlay, OverlayConfig, OverlayRef} from '@angular/cdk/overlay';
1111
import {ComponentPortal, ComponentType, PortalInjector, TemplatePortal} from '@angular/cdk/portal';
1212
import {ComponentRef, Injectable, Injector, Optional, SkipSelf, TemplateRef} from '@angular/core';
13+
import {Location} from '@angular/common';
1314
import {of as observableOf} from 'rxjs';
1415
import {MAT_BOTTOM_SHEET_DATA, MatBottomSheetConfig} from './bottom-sheet-config';
1516
import {MatBottomSheetContainer} from './bottom-sheet-container';
@@ -41,7 +42,8 @@ export class MatBottomSheet {
4142
constructor(
4243
private _overlay: Overlay,
4344
private _injector: Injector,
44-
@Optional() @SkipSelf() private _parentBottomSheet: MatBottomSheet) {}
45+
@Optional() @SkipSelf() private _parentBottomSheet: MatBottomSheet,
46+
@Optional() private _location?: Location) {}
4547

4648
open<T, D = any, R = any>(component: ComponentType<T>,
4749
config?: MatBottomSheetConfig<D>): MatBottomSheetRef<T, R>;
@@ -54,7 +56,7 @@ export class MatBottomSheet {
5456
const _config = _applyConfigDefaults(config);
5557
const overlayRef = this._createOverlay(_config);
5658
const container = this._attachContainer(overlayRef, _config);
57-
const ref = new MatBottomSheetRef<T, R>(container, overlayRef);
59+
const ref = new MatBottomSheetRef<T, R>(container, overlayRef, this._location);
5860

5961
if (componentOrTemplateRef instanceof TemplateRef) {
6062
container.attachTemplatePortal(new TemplatePortal<T>(componentOrTemplateRef, null!, {

0 commit comments

Comments
 (0)