mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(sidenav): throw error when sidenav has 2 sidenavs on the same side at the same time (#3369)
This commit is contained in:
@@ -51,10 +51,13 @@
|
||||
<h2>Dynamic Alignment Sidenav</h2>
|
||||
|
||||
<md-sidenav-container class="demo-sidenav-container">
|
||||
<md-sidenav #dynamicAlignSidenav mode="push" [align]="side">Drawer</md-sidenav>
|
||||
<md-sidenav #dynamicAlignSidenav1 mode="side" [align]="invert ? 'end' : 'start'">Start</md-sidenav>
|
||||
<md-sidenav #dynamicAlignSidenav2 mode="side" [align]="invert ? 'start' : 'end'">End</md-sidenav>
|
||||
|
||||
<div class="demo-sidenav-content">
|
||||
<button (click)="dynamicAlignSidenav.toggle()">Toggle sidenav</button>
|
||||
<button (click)="side = (side == 'start') ? 'end' : 'start'">Change sides</button>
|
||||
<button (click)="dynamicAlignSidenav1.toggle(); dynamicAlignSidenav2.toggle()">
|
||||
Toggle sidenavs
|
||||
</button>
|
||||
<button (click)="invert = !invert">Change sides</button>
|
||||
</div>
|
||||
</md-sidenav-container>
|
||||
|
||||
@@ -9,5 +9,5 @@ import {Component, ViewEncapsulation} from '@angular/core';
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class SidenavDemo {
|
||||
side = 'start';
|
||||
invert = false;
|
||||
}
|
||||
|
||||
@@ -135,7 +135,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mat-sidenav-invalid {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -373,23 +373,25 @@ describe('MdSidenav', () => {
|
||||
.toBe(false, 'Expected sidenav not to have a native align attribute.');
|
||||
});
|
||||
|
||||
it('should mark sidenavs invalid when multiple have same align', () => {
|
||||
it('should throw when multiple sidenavs have the same align', () => {
|
||||
const fixture = TestBed.createComponent(SidenavDynamicAlign);
|
||||
fixture.detectChanges();
|
||||
|
||||
const testComponent: SidenavDynamicAlign = fixture.debugElement.componentInstance;
|
||||
const sidenavEl = fixture.debugElement.query(By.css('md-sidenav')).nativeElement;
|
||||
expect(sidenavEl.classList).not.toContain('mat-sidenav-invalid');
|
||||
|
||||
testComponent.sidenav1Align = 'end';
|
||||
|
||||
expect(() => fixture.detectChanges()).toThrow();
|
||||
});
|
||||
|
||||
it('should not throw when sidenavs swap sides', () => {
|
||||
const fixture = TestBed.createComponent(SidenavDynamicAlign);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(sidenavEl.classList).toContain('mat-sidenav-invalid');
|
||||
|
||||
const testComponent: SidenavDynamicAlign = fixture.debugElement.componentInstance;
|
||||
testComponent.sidenav1Align = 'end';
|
||||
testComponent.sidenav2Align = 'start';
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(sidenavEl.classList).not.toContain('mat-sidenav-invalid');
|
||||
expect(() => fixture.detectChanges()).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -65,7 +65,6 @@ export class MdSidenavToggleResult {
|
||||
'[class.mat-sidenav-over]': '_modeOver',
|
||||
'[class.mat-sidenav-push]': '_modePush',
|
||||
'[class.mat-sidenav-side]': '_modeSide',
|
||||
'[class.mat-sidenav-invalid]': '!valid',
|
||||
'tabIndex': '-1'
|
||||
},
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
@@ -77,19 +76,6 @@ export class MdSidenav implements AfterContentInit, OnDestroy {
|
||||
/** Alignment of the sidenav (direction neutral); whether 'start' or 'end'. */
|
||||
private _align: 'start' | 'end' = 'start';
|
||||
|
||||
/** Whether this md-sidenav is part of a valid md-sidenav-container configuration. */
|
||||
get valid() { return this._valid; }
|
||||
set valid(value) {
|
||||
value = coerceBooleanProperty(value);
|
||||
// When the drawers are not in a valid configuration we close them all until they are in a valid
|
||||
// configuration again.
|
||||
if (!value) {
|
||||
this.close();
|
||||
}
|
||||
this._valid = value;
|
||||
}
|
||||
private _valid = true;
|
||||
|
||||
/** Direction which the sidenav is aligned in. */
|
||||
@Input()
|
||||
get align() { return this._align; }
|
||||
@@ -221,10 +207,6 @@ export class MdSidenav implements AfterContentInit, OnDestroy {
|
||||
* @returns Resolves with the result of whether the sidenav was opened or closed.
|
||||
*/
|
||||
toggle(isOpen: boolean = !this.opened): Promise<MdSidenavToggleResult> {
|
||||
if (!this.valid) {
|
||||
return Promise.resolve(new MdSidenavToggleResult(isOpen ? 'open' : 'close', true));
|
||||
}
|
||||
|
||||
// Shortcut it if we're already opened.
|
||||
if (isOpen === this.opened) {
|
||||
return this._toggleAnimationPromise ||
|
||||
@@ -410,8 +392,13 @@ export class MdSidenavContainer implements AfterContentInit {
|
||||
* changes.
|
||||
*/
|
||||
private _watchSidenavAlign(sidenav: MdSidenav): void {
|
||||
if (!sidenav) { return; }
|
||||
sidenav.onAlignChanged.subscribe(() => this._validateDrawers());
|
||||
if (!sidenav) {
|
||||
return;
|
||||
}
|
||||
// NOTE: We need to wait for the microtask queue to be empty before validating,
|
||||
// since both drawers may be swapping sides at the same time.
|
||||
sidenav.onAlignChanged.subscribe(() =>
|
||||
this._ngZone.onMicrotaskEmpty.first().subscribe(() => this._validateDrawers()));
|
||||
}
|
||||
|
||||
/** Toggles the 'mat-sidenav-opened' class on the main 'md-sidenav-container' element. */
|
||||
@@ -419,16 +406,6 @@ export class MdSidenavContainer implements AfterContentInit {
|
||||
this._renderer.setElementClass(this._element.nativeElement, 'mat-sidenav-opened', bool);
|
||||
}
|
||||
|
||||
/** Sets the valid state of the drawers. */
|
||||
private _setDrawersValid(valid: boolean) {
|
||||
this._sidenavs.forEach((sidenav) => {
|
||||
sidenav.valid = valid;
|
||||
});
|
||||
if (!valid) {
|
||||
this._start = this._end = this._left = this._right = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Validate the state of the sidenav children components. */
|
||||
private _validateDrawers() {
|
||||
this._start = this._end = null;
|
||||
@@ -439,14 +416,12 @@ export class MdSidenavContainer implements AfterContentInit {
|
||||
for (let sidenav of this._sidenavs.toArray()) {
|
||||
if (sidenav.align == 'end') {
|
||||
if (this._end != null) {
|
||||
this._setDrawersValid(false);
|
||||
return;
|
||||
throw new MdDuplicatedSidenavError('end');
|
||||
}
|
||||
this._end = sidenav;
|
||||
} else {
|
||||
if (this._start != null) {
|
||||
this._setDrawersValid(false);
|
||||
return;
|
||||
throw new MdDuplicatedSidenavError('start');
|
||||
}
|
||||
this._start = sidenav;
|
||||
}
|
||||
@@ -462,8 +437,6 @@ export class MdSidenavContainer implements AfterContentInit {
|
||||
this._left = this._end;
|
||||
this._right = this._start;
|
||||
}
|
||||
|
||||
this._setDrawersValid(true);
|
||||
}
|
||||
|
||||
_onBackdropClicked() {
|
||||
|
||||
Reference in New Issue
Block a user