diff --git a/src/demo-app/drawer/drawer-demo.html b/src/demo-app/drawer/drawer-demo.html index 58bbe94eb95b..e83193ac1b6c 100644 --- a/src/demo-app/drawer/drawer-demo.html +++ b/src/demo-app/drawer/drawer-demo.html @@ -1,7 +1,7 @@

Basic Use Case

- + Start Side Drawer
diff --git a/src/lib/sidenav/drawer.spec.ts b/src/lib/sidenav/drawer.spec.ts index b3f941de525a..a6ac276b7227 100644 --- a/src/lib/sidenav/drawer.spec.ts +++ b/src/lib/sidenav/drawer.spec.ts @@ -28,45 +28,98 @@ describe('MatDrawer', () => { })); describe('methods', () => { - it('should be able to open and close', fakeAsync(() => { - let fixture = TestBed.createComponent(BasicTestApp); + it('should be able to open', fakeAsync(() => { + const fixture = TestBed.createComponent(BasicTestApp); fixture.detectChanges(); - let testComponent: BasicTestApp = fixture.debugElement.componentInstance; - let drawer = fixture.debugElement.query(By.directive(MatDrawer)); - let drawerBackdropElement = fixture.debugElement.query(By.css('.mat-drawer-backdrop')); + const testComponent: BasicTestApp = fixture.debugElement.componentInstance; + const drawer = fixture.debugElement.query(By.directive(MatDrawer)); + const drawerBackdropElement = fixture.debugElement.query(By.css('.mat-drawer-backdrop')); drawerBackdropElement.nativeElement.style.transition = 'none'; fixture.debugElement.query(By.css('.open')).nativeElement.click(); fixture.detectChanges(); expect(testComponent.openCount).toBe(0); - expect(testComponent.closeCount).toBe(0); + expect(testComponent.openStartCount).toBe(0); tick(); + expect(testComponent.openStartCount).toBe(1); fixture.detectChanges(); expect(testComponent.openCount).toBe(1); - expect(testComponent.closeCount).toBe(0); + expect(testComponent.openStartCount).toBe(1); expect(getComputedStyle(drawer.nativeElement).visibility).toBe('visible'); expect(getComputedStyle(drawerBackdropElement.nativeElement).visibility).toBe('visible'); + })); + + it('should be able to close', fakeAsync(() => { + const fixture = TestBed.createComponent(BasicTestApp); + + fixture.detectChanges(); + + const testComponent: BasicTestApp = fixture.debugElement.componentInstance; + const drawer = fixture.debugElement.query(By.directive(MatDrawer)); + const drawerBackdropElement = fixture.debugElement.query(By.css('.mat-drawer-backdrop')); + + drawerBackdropElement.nativeElement.style.transition = 'none'; + fixture.debugElement.query(By.css('.open')).nativeElement.click(); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); fixture.debugElement.query(By.css('.close')).nativeElement.click(); fixture.detectChanges(); - expect(testComponent.openCount).toBe(1); expect(testComponent.closeCount).toBe(0); + expect(testComponent.closeStartCount).toBe(0); tick(); + expect(testComponent.closeStartCount).toBe(1); fixture.detectChanges(); - expect(testComponent.openCount).toBe(1); expect(testComponent.closeCount).toBe(1); + expect(testComponent.closeStartCount).toBe(1); expect(getComputedStyle(drawer.nativeElement).visibility).toBe('hidden'); expect(getComputedStyle(drawerBackdropElement.nativeElement).visibility).toBe('hidden'); })); + it('should resolve the open method promise with an object', fakeAsync(() => { + const fixture = TestBed.createComponent(BasicTestApp); + fixture.detectChanges(); + const drawer = fixture.debugElement.query(By.directive(MatDrawer)); + + drawer.componentInstance.open().then(result => { + expect(result).toBeTruthy(); + expect(result.type).toBe('open'); + expect(result.animationFinished).toBe(true); + }); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + })); + + it('should resolve the close method promise with an object', fakeAsync(() => { + const fixture = TestBed.createComponent(BasicTestApp); + fixture.detectChanges(); + const drawer = fixture.debugElement.query(By.directive(MatDrawer)); + + drawer.componentInstance.open(); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + + drawer.componentInstance.close().then(result => { + expect(result).toBeTruthy(); + expect(result.type).toBe('close'); + expect(result.animationFinished).toBe(true); + }); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + })); + it('should be able to close while the open animation is running', fakeAsync(() => { const fixture = TestBed.createComponent(BasicTestApp); fixture.detectChanges(); @@ -139,13 +192,16 @@ describe('MatDrawer', () => { tick(); expect(testComponent.openCount).toBe(1, 'Expected one open event.'); + expect(testComponent.openStartCount).toBe(1, 'Expected one open start event.'); expect(testComponent.closeCount).toBe(0, 'Expected no close events.'); + expect(testComponent.closeStartCount).toBe(0, 'Expected no close start events.'); dispatchKeyboardEvent(drawer.nativeElement, 'keydown', ESCAPE); fixture.detectChanges(); tick(); expect(testComponent.closeCount).toBe(1, 'Expected one close event.'); + expect(testComponent.closeStartCount).toBe(1, 'Expected one close start event.'); })); it('should fire the open event when open on init', fakeAsync(() => { @@ -172,6 +228,7 @@ describe('MatDrawer', () => { tick(); expect(testComponent.closeCount).toBe(0); + expect(testComponent.closeStartCount).toBe(0); })); it('should not close by clicking on the backdrop when disableClose is set', fakeAsync(() => { @@ -189,6 +246,7 @@ describe('MatDrawer', () => { tick(); expect(testComponent.closeCount).toBe(0); + expect(testComponent.closeStartCount).toBe(0); })); it('should restore focus on close if focus is inside drawer', fakeAsync(() => { @@ -490,7 +548,9 @@ class DrawerContainerTwoDrawerTestApp { + (openedStart)="openStart()" + (closed)="close()" + (closedStart)="closeStart()"> @@ -498,9 +558,11 @@ class DrawerContainerTwoDrawerTestApp { `, }) class BasicTestApp { - openCount: number = 0; - closeCount: number = 0; - backdropClickedCount: number = 0; + openCount = 0; + openStartCount = 0; + closeCount = 0; + closeStartCount = 0; + backdropClickedCount = 0; @ViewChild('drawerButton') drawerButton: ElementRef; @ViewChild('openButton') openButton: ElementRef; @@ -510,10 +572,18 @@ class BasicTestApp { this.openCount++; } + openStart() { + this.openStartCount++; + } + close() { this.closeCount++; } + closeStart() { + this.closeStartCount++; + } + backdropClicked() { this.backdropClickedCount++; } diff --git a/src/lib/sidenav/drawer.ts b/src/lib/sidenav/drawer.ts index 8497f8e5d5e4..6a9b4410b062 100644 --- a/src/lib/sidenav/drawer.ts +++ b/src/lib/sidenav/drawer.ts @@ -193,21 +193,39 @@ export class MatDrawer implements AfterContentInit, OnDestroy { return this.openedChange.pipe(filter(o => o), map(() => {})); } + /** Event emitted when the drawer has started opening. */ + @Output() + get openedStart(): Observable { + return this._animationStarted.pipe( + filter(e => e.fromState !== e.toState && e.toState.indexOf('open') === 0), + map(() => {}) + ); + } + /** Event emitted when the drawer has been closed. */ @Output('closed') get _closedStream(): Observable { return this.openedChange.pipe(filter(o => !o), map(() => {})); } + /** Event emitted when the drawer has started closing. */ + @Output() + get closedStart(): Observable { + return this._animationStarted.pipe( + filter(e => e.fromState !== e.toState && e.toState === 'void'), + map(() => {}) + ); + } + /** * Event emitted when the drawer is fully opened. - * @deprecated Use `openedChange` instead. + * @deprecated Use `opened` instead. */ @Output('open') onOpen = this._openedStream; /** * Event emitted when the drawer is fully closed. - * @deprecated Use `openedChange` instead. + * @deprecated Use `closed` instead. */ @Output('close') onClose = this._closedStream; @@ -327,8 +345,10 @@ export class MatDrawer implements AfterContentInit, OnDestroy { // TODO(crisbeto): This promise is here for backwards-compatibility. // It should be removed next time we do breaking changes in the drawer. - return new Promise(resolve => { - (isOpen ? this.onOpen : this.onClose).pipe(first()).subscribe(resolve); + return new Promise(resolve => { + this.openedChange.pipe(first()).subscribe(open => { + resolve(new MatDrawerToggleResult(open ? 'open' : 'close', true)); + }); }); }