mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(expansion-panel): toggle not being updated when set programmatically (#5650)
* fix(expansion-panel): toggle not being updated when set programmatically Fixes the toggle arrow not being flipped when the `expanded` is set programmatically, in addition to the `hideToggle` input not working either. This is a regression from #5549 and is a consequence of the fact that the panel header reaches into the panel to determine what to do with the arrow. Fixes #5623. * chore: add a couple of unit tests for the fixes
This commit is contained in:
committed by
Andrew Seguin
parent
66e222fc2e
commit
41c804b36a
@@ -35,8 +35,10 @@ export class AccordionItem implements OnDestroy {
|
||||
@Output() destroyed = new EventEmitter<void>();
|
||||
/** The unique MdAccordionChild id. */
|
||||
readonly id = `cdk-accordion-child-${nextId++}`;
|
||||
|
||||
/** Whether the MdAccordionChild is expanded. */
|
||||
@Input() get expanded(): boolean { return this._expanded; }
|
||||
@Input()
|
||||
get expanded(): boolean { return this._expanded; }
|
||||
set expanded(expanded: boolean) {
|
||||
// Only emit events and update the internal value if the value changes.
|
||||
if (this._expanded !== expanded) {
|
||||
|
||||
@@ -12,6 +12,8 @@ import {
|
||||
Host,
|
||||
ViewEncapsulation,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
OnDestroy,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
trigger,
|
||||
@@ -22,6 +24,9 @@ import {
|
||||
} from '@angular/animations';
|
||||
import {SPACE, ENTER} from '../core/keyboard/keycodes';
|
||||
import {MdExpansionPanel, EXPANSION_PANEL_ANIMATION_TIMING} from './expansion-panel';
|
||||
import {filter} from '../core/rxjs/index';
|
||||
import {merge} from 'rxjs/observable/merge';
|
||||
import {Subscription} from 'rxjs/Subscription';
|
||||
|
||||
|
||||
/**
|
||||
@@ -62,8 +67,22 @@ import {MdExpansionPanel, EXPANSION_PANEL_ANIMATION_TIMING} from './expansion-pa
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class MdExpansionPanelHeader {
|
||||
constructor(@Host() public panel: MdExpansionPanel) {}
|
||||
export class MdExpansionPanelHeader implements OnDestroy {
|
||||
private _parentChangeSubscription: Subscription | null = null;
|
||||
|
||||
constructor(
|
||||
@Host() public panel: MdExpansionPanel,
|
||||
private _changeDetectorRef: ChangeDetectorRef) {
|
||||
|
||||
// Since the toggle state depends on an @Input on the panel, we
|
||||
// need to subscribe and trigger change detection manually.
|
||||
this._parentChangeSubscription = merge(
|
||||
panel.opened,
|
||||
panel.closed,
|
||||
filter.call(panel._inputChanges, changes => !!changes.hideToggle)
|
||||
)
|
||||
.subscribe(() => this._changeDetectorRef.markForCheck());
|
||||
}
|
||||
|
||||
/** Toggles the expanded state of the panel. */
|
||||
_toggle(): void {
|
||||
@@ -103,6 +122,13 @@ export class MdExpansionPanelHeader {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this._parentChangeSubscription) {
|
||||
this._parentChangeSubscription.unsubscribe();
|
||||
this._parentChangeSubscription = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,9 @@ import {
|
||||
forwardRef,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
SimpleChanges,
|
||||
OnChanges,
|
||||
OnDestroy,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
trigger,
|
||||
@@ -27,6 +30,7 @@ import {
|
||||
import {MdAccordion, MdAccordionDisplayMode} from './accordion';
|
||||
import {AccordionItem} from './accordion-item';
|
||||
import {UniqueSelectionDispatcher} from '../core';
|
||||
import {Subject} from 'rxjs/Subject';
|
||||
|
||||
|
||||
/** MdExpansionPanel's states. */
|
||||
@@ -72,10 +76,13 @@ export const EXPANSION_PANEL_ANIMATION_TIMING = '225ms cubic-bezier(0.4,0.0,0.2,
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class MdExpansionPanel extends AccordionItem {
|
||||
export class MdExpansionPanel extends AccordionItem implements OnChanges, OnDestroy {
|
||||
/** Whether the toggle indicator should be hidden. */
|
||||
@Input() hideToggle: boolean = false;
|
||||
|
||||
/** Stream that emits for changes in `@Input` properties. */
|
||||
_inputChanges = new Subject<SimpleChanges>();
|
||||
|
||||
constructor(@Optional() @Host() accordion: MdAccordion,
|
||||
_changeDetectorRef: ChangeDetectorRef,
|
||||
_uniqueSelectionDispatcher: UniqueSelectionDispatcher) {
|
||||
@@ -104,6 +111,14 @@ export class MdExpansionPanel extends AccordionItem {
|
||||
_getExpandedState(): MdExpansionPanelState {
|
||||
return this.expanded ? 'expanded' : 'collapsed';
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
this._inputChanges.next(changes);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._inputChanges.complete();
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({
|
||||
|
||||
@@ -103,12 +103,47 @@ describe('MdExpansionPanel', () => {
|
||||
expect(styles.marginLeft).toBe('37px');
|
||||
expect(styles.marginRight).toBe('37px');
|
||||
}));
|
||||
|
||||
it('should be able to hide the toggle', () => {
|
||||
const fixture = TestBed.createComponent(PanelWithContent);
|
||||
const header = fixture.debugElement.query(By.css('.mat-expansion-panel-header')).nativeElement;
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(header.querySelector('.mat-expansion-indicator'))
|
||||
.toBeTruthy('Expected indicator to be shown.');
|
||||
|
||||
fixture.componentInstance.hideToggle = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(header.querySelector('.mat-expansion-indicator'))
|
||||
.toBeFalsy('Expected indicator to be hidden.');
|
||||
});
|
||||
|
||||
it('should update the indicator rotation when the expanded state is toggled programmatically',
|
||||
fakeAsync(() => {
|
||||
const fixture = TestBed.createComponent(PanelWithContent);
|
||||
|
||||
fixture.detectChanges();
|
||||
tick(250);
|
||||
|
||||
const arrow = fixture.debugElement.query(By.css('.mat-expansion-indicator')).nativeElement;
|
||||
|
||||
expect(arrow.style.transform).toBe('rotate(0deg)', 'Expected no rotation.');
|
||||
|
||||
fixture.componentInstance.expanded = true;
|
||||
fixture.detectChanges();
|
||||
tick(250);
|
||||
|
||||
expect(arrow.style.transform).toBe('rotate(180deg)', 'Expected 180 degree rotation.');
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<md-expansion-panel [expanded]="expanded"
|
||||
[hideToggle]="hideToggle"
|
||||
(opened)="openCallback()"
|
||||
(closed)="closeCallback()">
|
||||
<md-expansion-panel-header>Panel Title</md-expansion-panel-header>
|
||||
@@ -118,6 +153,7 @@ describe('MdExpansionPanel', () => {
|
||||
})
|
||||
class PanelWithContent {
|
||||
expanded: boolean = false;
|
||||
hideToggle: boolean = false;
|
||||
openCallback = jasmine.createSpy('openCallback');
|
||||
closeCallback = jasmine.createSpy('closeCallback');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user