diff --git a/src/lib/datepicker/datepicker.spec.ts b/src/lib/datepicker/datepicker.spec.ts index 4e7a1c450b30..041b18da34c2 100644 --- a/src/lib/datepicker/datepicker.spec.ts +++ b/src/lib/datepicker/datepicker.spec.ts @@ -1,5 +1,5 @@ import {ENTER, ESCAPE, RIGHT_ARROW} from '@angular/cdk/keycodes'; -import {OverlayContainer} from '@angular/cdk/overlay'; +import {OverlayContainer, Overlay, ScrollDispatcher} from '@angular/cdk/overlay'; import { createKeyboardEvent, dispatchEvent, @@ -27,7 +27,8 @@ import {MatInputModule} from '../input/index'; import {MatDatepicker} from './datepicker'; import {MatDatepickerInput} from './datepicker-input'; import {MatDatepickerToggle} from './datepicker-toggle'; -import {MatDatepickerIntl, MatDatepickerModule} from './index'; +import {MatDatepickerIntl, MatDatepickerModule, MAT_DATEPICKER_SCROLL_STRATEGY} from './index'; +import {Subject} from 'rxjs/Subject'; describe('MatDatepicker', () => { const SUPPORTS_INTL = typeof Intl != 'undefined'; @@ -342,17 +343,55 @@ describe('MatDatepicker', () => { testComponent.datepicker.open(); fixture.detectChanges(); - spyOn(testComponent.datepicker, 'close').and.callThrough(); - + const spy = jasmine.createSpy('close event spy'); + const subscription = testComponent.datepicker.closedStream.subscribe(spy); const backdrop = document.querySelector('.cdk-overlay-backdrop')! as HTMLElement; backdrop.click(); fixture.detectChanges(); flush(); - expect(testComponent.datepicker.close).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledTimes(1); expect(testComponent.datepicker.opened).toBe(false); + subscription.unsubscribe(); })); + + it('should reset the datepicker when it is closed externally', + fakeAsync(inject([OverlayContainer], (oldOverlayContainer: OverlayContainer) => { + + // Destroy the old container manually since resetting the testing module won't do it. + oldOverlayContainer.ngOnDestroy(); + TestBed.resetTestingModule(); + + const scrolledSubject = new Subject(); + + // Stub out a `CloseScrollStrategy` so we can trigger a detachment via the `OverlayRef`. + fixture = createComponent(StandardDatepicker, [MatNativeDateModule], [ + { + provide: ScrollDispatcher, + useValue: {scrolled: () => scrolledSubject} + }, + { + provide: MAT_DATEPICKER_SCROLL_STRATEGY, + deps: [Overlay], + useFactory: (overlay: Overlay) => () => overlay.scrollStrategies.close() + } + ]); + + fixture.detectChanges(); + testComponent = fixture.componentInstance; + + testComponent.datepicker.open(); + fixture.detectChanges(); + + expect(testComponent.datepicker.opened).toBe(true); + + scrolledSubject.next(); + flush(); + fixture.detectChanges(); + + expect(testComponent.datepicker.opened).toBe(false); + }))); }); describe('datepicker with too many inputs', () => { diff --git a/src/lib/datepicker/datepicker.ts b/src/lib/datepicker/datepicker.ts index 527605aca827..90a6484a243b 100644 --- a/src/lib/datepicker/datepicker.ts +++ b/src/lib/datepicker/datepicker.ts @@ -41,6 +41,7 @@ import {MatDialog, MatDialogRef} from '@angular/material/dialog'; import {DOCUMENT} from '@angular/common'; import {Subject} from 'rxjs/Subject'; import {Subscription} from 'rxjs/Subscription'; +import {merge} from 'rxjs/observable/merge'; import {MatCalendar} from './calendar'; import {createMissingDateImplError} from './datepicker-errors'; import {MatDatepickerInput} from './datepicker-input'; @@ -312,9 +313,13 @@ export class MatDatepicker implements OnDestroy { } const completeClose = () => { - this._opened = false; - this.closedStream.emit(); - this._focusedElementBeforeOpen = null; + // The `_opened` could've been reset already if + // we got two events in quick succession. + if (this._opened) { + this._opened = false; + this.closedStream.emit(); + this._focusedElementBeforeOpen = null; + } }; if (this._focusedElementBeforeOpen && @@ -376,7 +381,9 @@ export class MatDatepicker implements OnDestroy { }); this._popupRef = this._overlay.create(overlayConfig); - this._popupRef.backdropClick().subscribe(() => this.close()); + + merge(this._popupRef.backdropClick(), this._popupRef.detachments()) + .subscribe(() => this.close()); } /** Create the popup PositionStrategy. */