From 9c184eae6ff3636cf4c4329eec1c0e04a6849df8 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Tue, 21 Aug 2018 20:38:17 +0200 Subject: [PATCH] fix(expansion): respect parent accordion hideToggle binding (#12725) * Fixes that the expansion panels no longer respect the bindings of a parent ``. (this has been accidentally changes in #6529) * Fixes that the expansion panel does not re-render if the `[hideToggle]` binding is set on the accordion. * Removes the unused `_getHideToggle` method. This method is no longer used because the panel header directly accesses the panel through DI. --- src/cdk/accordion/accordion.ts | 15 +++++++++-- src/demo-app/expansion/expansion-demo.html | 8 +++--- src/lib/expansion/accordion.spec.ts | 29 +++++++++++++++++++++ src/lib/expansion/accordion.ts | 1 + src/lib/expansion/expansion-panel-header.ts | 16 +++++++----- src/lib/expansion/expansion-panel.ts | 15 +++++------ 6 files changed, 63 insertions(+), 21 deletions(-) diff --git a/src/cdk/accordion/accordion.ts b/src/cdk/accordion/accordion.ts index cb35f01536a3..e3edebbabbda 100644 --- a/src/cdk/accordion/accordion.ts +++ b/src/cdk/accordion/accordion.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, Input} from '@angular/core'; import {coerceBooleanProperty} from '@angular/cdk/coercion'; +import {Directive, Input, OnChanges, OnDestroy, SimpleChanges} from '@angular/core'; import {Subject} from 'rxjs'; /** Used to generate unique ID for each accordion. */ @@ -20,7 +20,10 @@ let nextId = 0; selector: 'cdk-accordion, [cdkAccordion]', exportAs: 'cdkAccordion', }) -export class CdkAccordion { +export class CdkAccordion implements OnDestroy, OnChanges { + /** Emits when the state of the accordion changes */ + readonly _stateChanges = new Subject(); + /** Stream that emits true/false when openAll/closeAll is triggered. */ readonly _openCloseAllActions: Subject = new Subject(); @@ -43,6 +46,14 @@ export class CdkAccordion { this._openCloseAll(false); } + ngOnChanges(changes: SimpleChanges) { + this._stateChanges.next(changes); + } + + ngOnDestroy() { + this._stateChanges.complete(); + } + private _openCloseAll(expanded: boolean): void { if (this.multi) { this._openCloseAllActions.next(expanded); diff --git a/src/demo-app/expansion/expansion-demo.html b/src/demo-app/expansion/expansion-demo.html index 414078a94e10..30bf4918a84d 100644 --- a/src/demo-app/expansion/expansion-demo.html +++ b/src/demo-app/expansion/expansion-demo.html @@ -62,17 +62,17 @@

matAccordion


- - + Section 1

This is the content text that makes sense here.

- + Section 2

This is the content text that makes sense here.

- + Section 3 Reveal Buttons Below diff --git a/src/lib/expansion/accordion.spec.ts b/src/lib/expansion/accordion.spec.ts index f69022c1444d..ebb1b89143c1 100644 --- a/src/lib/expansion/accordion.spec.ts +++ b/src/lib/expansion/accordion.spec.ts @@ -13,6 +13,7 @@ describe('MatAccordion', () => { MatExpansionModule ], declarations: [ + AccordionWithHideToggle, NestedPanel, SetOfItems, ], @@ -93,6 +94,22 @@ describe('MatAccordion', () => { expect(innerPanel.accordion).not.toBe(outerPanel.accordion); }); + + it('should update the expansion panel if hideToggle changed', () => { + const fixture = TestBed.createComponent(AccordionWithHideToggle); + const panel = fixture.debugElement.query(By.directive(MatExpansionPanel)); + + fixture.detectChanges(); + + expect(panel.nativeElement.querySelector('.mat-expansion-indicator')) + .toBeTruthy('Expected the expansion indicator to be present.'); + + fixture.componentInstance.hideToggle = true; + fixture.detectChanges(); + + expect(panel.nativeElement.querySelector('.mat-expansion-indicator')) + .toBeFalsy('Expected the expansion indicator to be removed.'); + }); }); @@ -130,3 +147,15 @@ class NestedPanel { @ViewChild('outerPanel') outerPanel: MatExpansionPanel; @ViewChild('innerPanel') innerPanel: MatExpansionPanel; } + +@Component({template: ` + + + Header +

Content

+
+
` +}) +class AccordionWithHideToggle { + hideToggle = false; +} diff --git a/src/lib/expansion/accordion.ts b/src/lib/expansion/accordion.ts index 8f6cf19f9ea5..b366279e7bbc 100644 --- a/src/lib/expansion/accordion.ts +++ b/src/lib/expansion/accordion.ts @@ -19,6 +19,7 @@ export type MatAccordionDisplayMode = 'default' | 'flat'; @Directive({ selector: 'mat-accordion', exportAs: 'matAccordion', + inputs: ['multi'], host: { class: 'mat-accordion' } diff --git a/src/lib/expansion/expansion-panel-header.ts b/src/lib/expansion/expansion-panel-header.ts index 956a1e08c31f..c871cb83d9c6 100644 --- a/src/lib/expansion/expansion-panel-header.ts +++ b/src/lib/expansion/expansion-panel-header.ts @@ -19,7 +19,7 @@ import { OnDestroy, ViewEncapsulation, } from '@angular/core'; -import {merge, Subscription} from 'rxjs'; +import {merge, Subscription, EMPTY} from 'rxjs'; import {filter} from 'rxjs/operators'; import {matExpansionAnimations} from './expansion-animations'; import {MatExpansionPanel} from './expansion-panel'; @@ -65,16 +65,20 @@ export class MatExpansionPanelHeader implements OnDestroy { private _parentChangeSubscription = Subscription.EMPTY; constructor( - @Host() public panel: MatExpansionPanel, - private _element: ElementRef, - private _focusMonitor: FocusMonitor, - private _changeDetectorRef: ChangeDetectorRef) { + @Host() public panel: MatExpansionPanel, + private _element: ElementRef, + private _focusMonitor: FocusMonitor, + private _changeDetectorRef: ChangeDetectorRef) { + + const accordionHideToggleChange = panel.accordion ? + panel.accordion._stateChanges.pipe(filter(changes => !!changes.hideToggle)) : EMPTY; // Since the toggle state depends on an @Input on the panel, we - // need to subscribe and trigger change detection manually. + // need to subscribe and trigger change detection manually. this._parentChangeSubscription = merge( panel.opened, panel.closed, + accordionHideToggleChange, panel._inputChanges.pipe(filter(changes => !!(changes.hideToggle || changes.disabled))) ) .subscribe(() => this._changeDetectorRef.markForCheck()); diff --git a/src/lib/expansion/expansion-panel.ts b/src/lib/expansion/expansion-panel.ts index 06a3bc47fcb7..7e1dbae211bd 100644 --- a/src/lib/expansion/expansion-panel.ts +++ b/src/lib/expansion/expansion-panel.ts @@ -76,7 +76,9 @@ export class MatExpansionPanel extends _CdkAccordionItem implements AfterContentInit, OnChanges, OnDestroy { /** Whether the toggle indicator should be hidden. */ @Input() - get hideToggle(): boolean { return this._hideToggle; } + get hideToggle(): boolean { + return this._hideToggle || (this.accordion && this.accordion.hideToggle); + } set hideToggle(value: boolean) { this._hideToggle = coerceBooleanProperty(value); } @@ -111,17 +113,12 @@ export class MatExpansionPanel extends _CdkAccordionItem this.accordion = accordion; } - /** Whether the expansion indicator should be hidden. */ - _getHideToggle(): boolean { - if (this.accordion) { - return this.accordion.hideToggle; - } - return this.hideToggle; - } - /** Determines whether the expansion panel should have spacing between it and its siblings. */ _hasSpacing(): boolean { if (this.accordion) { + // We don't need to subscribe to the `stateChanges` of the parent accordion because each time + // the [displayMode] input changes, the change detection will also cover the host bindings + // of this expansion panel. return (this.expanded ? this.accordion.displayMode : this._getExpandedState()) === 'default'; } return false;