Skip to content

Commit 79524a2

Browse files
committed
fix(sidenav): throw error when sidenav has 2 sidenavs on the same side at the same time
1 parent f40b1b2 commit 79524a2

File tree

5 files changed

+26
-52
lines changed

5 files changed

+26
-52
lines changed

src/demo-app/sidenav/sidenav-demo.html

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,13 @@ <h2>Sidenav Already Opened</h2>
5151
<h2>Dynamic Alignment Sidenav</h2>
5252

5353
<md-sidenav-container class="demo-sidenav-container">
54-
<md-sidenav #dynamicAlignSidenav mode="push" [align]="side">Drawer</md-sidenav>
54+
<md-sidenav #dynamicAlignSidenav1 mode="side" [align]="invert ? 'end' : 'start'">Start</md-sidenav>
55+
<md-sidenav #dynamicAlignSidenav2 mode="side" [align]="invert ? 'start' : 'end'">End</md-sidenav>
5556

5657
<div class="demo-sidenav-content">
57-
<button (click)="dynamicAlignSidenav.toggle()">Toggle sidenav</button>
58-
<button (click)="side = (side == 'start') ? 'end' : 'start'">Change sides</button>
58+
<button (click)="dynamicAlignSidenav1.toggle(); dynamicAlignSidenav2.toggle()">
59+
Toggle sidenavs
60+
</button>
61+
<button (click)="invert = !invert">Change sides</button>
5962
</div>
6063
</md-sidenav-container>

src/demo-app/sidenav/sidenav-demo.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ import {Component, ViewEncapsulation} from '@angular/core';
99
encapsulation: ViewEncapsulation.None,
1010
})
1111
export class SidenavDemo {
12-
side = 'start';
12+
invert = false;
1313
}

src/lib/sidenav/sidenav.scss

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,3 @@
145145
transform: translateZ(0);
146146
}
147147
}
148-
149-
.mat-sidenav-invalid {
150-
display: none;
151-
}

src/lib/sidenav/sidenav.spec.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -373,23 +373,25 @@ describe('MdSidenav', () => {
373373
.toBe(false, 'Expected sidenav not to have a native align attribute.');
374374
});
375375

376-
it('should mark sidenavs invalid when multiple have same align', () => {
376+
it('should throw when multiple sidenavs have the same align', () => {
377377
const fixture = TestBed.createComponent(SidenavDynamicAlign);
378378
fixture.detectChanges();
379379

380380
const testComponent: SidenavDynamicAlign = fixture.debugElement.componentInstance;
381-
const sidenavEl = fixture.debugElement.query(By.css('md-sidenav')).nativeElement;
382-
expect(sidenavEl.classList).not.toContain('mat-sidenav-invalid');
383-
384381
testComponent.sidenav1Align = 'end';
385-
fixture.detectChanges();
386382

387-
expect(sidenavEl.classList).toContain('mat-sidenav-invalid');
383+
expect(() => fixture.detectChanges()).toThrow();
384+
});
388385

389-
testComponent.sidenav2Align = 'start';
386+
it('should not throw when sidenavs swap sides', () => {
387+
const fixture = TestBed.createComponent(SidenavDynamicAlign);
390388
fixture.detectChanges();
391389

392-
expect(sidenavEl.classList).not.toContain('mat-sidenav-invalid');
390+
const testComponent: SidenavDynamicAlign = fixture.debugElement.componentInstance;
391+
testComponent.sidenav1Align = 'end';
392+
testComponent.sidenav2Align = 'start';
393+
394+
expect(() => fixture.detectChanges()).not.toThrow();
393395
});
394396
});
395397

src/lib/sidenav/sidenav.ts

Lines changed: 9 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ export class MdSidenavToggleResult {
6565
'[class.mat-sidenav-over]': '_modeOver',
6666
'[class.mat-sidenav-push]': '_modePush',
6767
'[class.mat-sidenav-side]': '_modeSide',
68-
'[class.mat-sidenav-invalid]': '!valid',
6968
'tabIndex': '-1'
7069
},
7170
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -77,19 +76,6 @@ export class MdSidenav implements AfterContentInit {
7776
/** Alignment of the sidenav (direction neutral); whether 'start' or 'end'. */
7877
private _align: 'start' | 'end' = 'start';
7978

80-
/** Whether this md-sidenav is part of a valid md-sidenav-container configuration. */
81-
get valid() { return this._valid; }
82-
set valid(value) {
83-
value = coerceBooleanProperty(value);
84-
// When the drawers are not in a valid configuration we close them all until they are in a valid
85-
// configuration again.
86-
if (!value) {
87-
this.close();
88-
}
89-
this._valid = value;
90-
}
91-
private _valid = true;
92-
9379
/** Direction which the sidenav is aligned in. */
9480
@Input()
9581
get align() { return this._align; }
@@ -208,10 +194,6 @@ export class MdSidenav implements AfterContentInit {
208194
* @returns Resolves with the result of whether the sidenav was opened or closed.
209195
*/
210196
toggle(isOpen: boolean = !this.opened): Promise<MdSidenavToggleResult> {
211-
if (!this.valid) {
212-
return Promise.resolve(new MdSidenavToggleResult(isOpen ? 'open' : 'close', true));
213-
}
214-
215197
// Shortcut it if we're already opened.
216198
if (isOpen === this.opened) {
217199
return this._toggleAnimationPromise ||
@@ -393,25 +375,20 @@ export class MdSidenavContainer implements AfterContentInit {
393375
* changes.
394376
*/
395377
private _watchSidenavAlign(sidenav: MdSidenav): void {
396-
if (!sidenav) { return; }
397-
sidenav.onAlignChanged.subscribe(() => this._validateDrawers());
378+
if (!sidenav) {
379+
return;
380+
}
381+
// NOTE: We need to wait for the microtask queue to be empty before validating,
382+
// since both drawers may be swapping sides at the same time.
383+
sidenav.onAlignChanged.subscribe(() =>
384+
this._ngZone.onMicrotaskEmpty.first().subscribe(() => this._validateDrawers()));
398385
}
399386

400387
/** Toggles the 'mat-sidenav-opened' class on the main 'md-sidenav-container' element. */
401388
private _setContainerClass(sidenav: MdSidenav, bool: boolean): void {
402389
this._renderer.setElementClass(this._element.nativeElement, 'mat-sidenav-opened', bool);
403390
}
404391

405-
/** Sets the valid state of the drawers. */
406-
private _setDrawersValid(valid: boolean) {
407-
this._sidenavs.forEach((sidenav) => {
408-
sidenav.valid = valid;
409-
});
410-
if (!valid) {
411-
this._start = this._end = this._left = this._right = null;
412-
}
413-
}
414-
415392
/** Validate the state of the sidenav children components. */
416393
private _validateDrawers() {
417394
this._start = this._end = null;
@@ -422,14 +399,12 @@ export class MdSidenavContainer implements AfterContentInit {
422399
for (let sidenav of this._sidenavs.toArray()) {
423400
if (sidenav.align == 'end') {
424401
if (this._end != null) {
425-
this._setDrawersValid(false);
426-
return;
402+
throw new MdDuplicatedSidenavError('end');
427403
}
428404
this._end = sidenav;
429405
} else {
430406
if (this._start != null) {
431-
this._setDrawersValid(false);
432-
return;
407+
throw new MdDuplicatedSidenavError('start');
433408
}
434409
this._start = sidenav;
435410
}
@@ -445,8 +420,6 @@ export class MdSidenavContainer implements AfterContentInit {
445420
this._left = this._end;
446421
this._right = this._start;
447422
}
448-
449-
this._setDrawersValid(true);
450423
}
451424

452425
_onBackdropClicked() {

0 commit comments

Comments
 (0)