Skip to content

Commit dd2a040

Browse files
committed
fix(sidenav): throw error when sidenav has 2 sidenavs on the same side at the same time
1 parent 4e4c6a6 commit dd2a040

File tree

5 files changed

+26
-52
lines changed

5 files changed

+26
-52
lines changed

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

+6-3
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

+1-1
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

-4
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,3 @@
135135
}
136136
}
137137
}
138-
139-
.mat-sidenav-invalid {
140-
display: none;
141-
}

src/lib/sidenav/sidenav.spec.ts

+10-8
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

+9-36
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, OnDestroy {
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; }
@@ -221,10 +207,6 @@ export class MdSidenav implements AfterContentInit, OnDestroy {
221207
* @returns Resolves with the result of whether the sidenav was opened or closed.
222208
*/
223209
toggle(isOpen: boolean = !this.opened): Promise<MdSidenavToggleResult> {
224-
if (!this.valid) {
225-
return Promise.resolve(new MdSidenavToggleResult(isOpen ? 'open' : 'close', true));
226-
}
227-
228210
// Shortcut it if we're already opened.
229211
if (isOpen === this.opened) {
230212
return this._toggleAnimationPromise ||
@@ -410,25 +392,20 @@ export class MdSidenavContainer implements AfterContentInit {
410392
* changes.
411393
*/
412394
private _watchSidenavAlign(sidenav: MdSidenav): void {
413-
if (!sidenav) { return; }
414-
sidenav.onAlignChanged.subscribe(() => this._validateDrawers());
395+
if (!sidenav) {
396+
return;
397+
}
398+
// NOTE: We need to wait for the microtask queue to be empty before validating,
399+
// since both drawers may be swapping sides at the same time.
400+
sidenav.onAlignChanged.subscribe(() =>
401+
this._ngZone.onMicrotaskEmpty.first().subscribe(() => this._validateDrawers()));
415402
}
416403

417404
/** Toggles the 'mat-sidenav-opened' class on the main 'md-sidenav-container' element. */
418405
private _setContainerClass(sidenav: MdSidenav, bool: boolean): void {
419406
this._renderer.setElementClass(this._element.nativeElement, 'mat-sidenav-opened', bool);
420407
}
421408

422-
/** Sets the valid state of the drawers. */
423-
private _setDrawersValid(valid: boolean) {
424-
this._sidenavs.forEach((sidenav) => {
425-
sidenav.valid = valid;
426-
});
427-
if (!valid) {
428-
this._start = this._end = this._left = this._right = null;
429-
}
430-
}
431-
432409
/** Validate the state of the sidenav children components. */
433410
private _validateDrawers() {
434411
this._start = this._end = null;
@@ -439,14 +416,12 @@ export class MdSidenavContainer implements AfterContentInit {
439416
for (let sidenav of this._sidenavs.toArray()) {
440417
if (sidenav.align == 'end') {
441418
if (this._end != null) {
442-
this._setDrawersValid(false);
443-
return;
419+
throw new MdDuplicatedSidenavError('end');
444420
}
445421
this._end = sidenav;
446422
} else {
447423
if (this._start != null) {
448-
this._setDrawersValid(false);
449-
return;
424+
throw new MdDuplicatedSidenavError('start');
450425
}
451426
this._start = sidenav;
452427
}
@@ -462,8 +437,6 @@ export class MdSidenavContainer implements AfterContentInit {
462437
this._left = this._end;
463438
this._right = this._start;
464439
}
465-
466-
this._setDrawersValid(true);
467440
}
468441

469442
_onBackdropClicked() {

0 commit comments

Comments
 (0)