Skip to content

Commit

Permalink
fix(drawer): re-add openedStart and closedStart events
Browse files Browse the repository at this point in the history
* Re-adds the `openedStart` and `closedStart` events due to popular demand.
* Fixes wrong signature for the returned promise from toggle. The signature states that the resolved value is supposed to be a `MatDrawerToggleResult` while in practice it was `undefined`.
* Splits out a long unit test into two smaller ones.

Fixes #6924.
  • Loading branch information
crisbeto committed Oct 12, 2017
1 parent 70c349c commit bc92898
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 18 deletions.
2 changes: 1 addition & 1 deletion src/demo-app/drawer/drawer-demo.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<h2>Basic Use Case</h2>

<mat-drawer-container class="demo-drawer-container">
<mat-drawer #start (open)="myinput.focus()" mode="side">
<mat-drawer #start (opened)="myinput.focus()" mode="side">
Start Side Drawer
<br>
<button mat-button (click)="start.close()">Close</button>
Expand Down
92 changes: 79 additions & 13 deletions src/lib/sidenav/drawer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,45 +28,94 @@ 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();
}));

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();
}));

it('should be able to close while the open animation is running', fakeAsync(() => {
const fixture = TestBed.createComponent(BasicTestApp);
fixture.detectChanges();
Expand Down Expand Up @@ -139,13 +188,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(() => {
Expand All @@ -172,6 +224,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(() => {
Expand All @@ -189,6 +242,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(() => {
Expand Down Expand Up @@ -490,17 +544,21 @@ class DrawerContainerTwoDrawerTestApp {
<mat-drawer-container (backdropClick)="backdropClicked()">
<mat-drawer #drawer position="start"
(opened)="open()"
(closed)="close()">
(openedStart)="openStart()"
(closed)="close()"
(closedStart)="closeStart()">
<button #drawerButton>Content.</button>
</mat-drawer>
<button (click)="drawer.open()" class="open" #openButton></button>
<button (click)="drawer.close()" class="close" #closeButton></button>
</mat-drawer-container>`,
})
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;
Expand All @@ -510,10 +568,18 @@ class BasicTestApp {
this.openCount++;
}

openStart() {
this.openStartCount++;
}

close() {
this.closeCount++;
}

closeStart() {
this.closeStartCount++;
}

backdropClicked() {
this.backdropClickedCount++;
}
Expand Down
28 changes: 24 additions & 4 deletions src/lib/sidenav/drawer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,15 @@ export class MatDrawer implements AfterContentInit, OnDestroy {
.result();
}

/** Event emitted when the drawer has started opening. */
@Output()
get openedStart(): Observable<void> {
return RxChain.from(this._animationStarted)
.call(filter, e => e.fromState !== e.toState && e.toState.indexOf('open') === 0)
.call(map, () => {})
.result();
}

/** Event emitted when the drawer has been closed. */
@Output('closed')
get _closedStream(): Observable<void> {
Expand All @@ -199,15 +208,24 @@ export class MatDrawer implements AfterContentInit, OnDestroy {
.result();
}

/** Event emitted when the drawer has started closing. */
@Output()
get closedStart(): Observable<void> {
return RxChain.from(this._animationStarted)
.call(filter, e => e.fromState !== e.toState && e.toState === 'void')
.call(map, () => {})
.result();
}

/**
* 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;

Expand Down Expand Up @@ -316,8 +334,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 => {
first.call(isOpen ? this.onOpen : this.onClose).subscribe(resolve);
return new Promise<any>(resolve => {
first.call(this.openedChange).subscribe(open => {
resolve(new MatDrawerToggleResult(open ? 'open' : 'close', true));
});
});
}

Expand Down

0 comments on commit bc92898

Please sign in to comment.