Skip to content

Commit

Permalink
fix(expansion-panel): picking up lazy content from child component
Browse files Browse the repository at this point in the history
We use a `ContentChild` to determine what lazy content to render inside an expansion panel. When the lazy content is nested further down inside another expansion panel, all ancestor panels up the tree will pick up the lowest lazy content from the lowest level. These changes add a check to ensure that the lazy content is rendered out by the closest panel.

Fixes angular#14365.
  • Loading branch information
crisbeto committed Sep 21, 2019
1 parent 7f6972f commit d224dcf
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 4 deletions.
25 changes: 25 additions & 0 deletions src/material/expansion/expansion-panel-base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {InjectionToken} from '@angular/core';
import {CdkAccordionItem} from '@angular/cdk/accordion';

/**
* Base interface for a `MatExpansionPanel`.
* @docs-private
*/
export interface MatExpansionPanelBase extends CdkAccordionItem {
/** Whether the toggle indicator should be hidden. */
hideToggle: boolean;
}

/**
* Token used to provide a `MatExpansionPanel` to `MatExpansionPanelContent`.
* Used to avoid circular imports between `MatExpansionPanel` and `MatExpansionPanelContent`.
*/
export const MAT_EXPANSION_PANEL = new InjectionToken<MatExpansionPanelBase>('MAT_EXPANSION_PANEL');
7 changes: 5 additions & 2 deletions src/material/expansion/expansion-panel-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Directive, TemplateRef} from '@angular/core';
import {Directive, TemplateRef, Inject, Optional} from '@angular/core';
import {MAT_EXPANSION_PANEL, MatExpansionPanelBase} from './expansion-panel-base';

/**
* Expansion panel content that will be rendered lazily
Expand All @@ -16,5 +17,7 @@ import {Directive, TemplateRef} from '@angular/core';
selector: 'ng-template[matExpansionPanelContent]'
})
export class MatExpansionPanelContent {
constructor(public _template: TemplateRef<any>) {}
constructor(
public _template: TemplateRef<any>,
@Inject(MAT_EXPANSION_PANEL) @Optional() public _expansionPanel?: MatExpansionPanelBase) {}
}
4 changes: 3 additions & 1 deletion src/material/expansion/expansion-panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {filter, startWith, take, distinctUntilChanged} from 'rxjs/operators';
import {matExpansionAnimations} from './expansion-animations';
import {MatExpansionPanelContent} from './expansion-panel-content';
import {MAT_ACCORDION, MatAccordionBase, MatAccordionTogglePosition} from './accordion-base';
import {MAT_EXPANSION_PANEL} from './expansion-panel-base';

/** MatExpansionPanel's states. */
export type MatExpansionPanelState = 'expanded' | 'collapsed';
Expand Down Expand Up @@ -90,6 +91,7 @@ export const MAT_EXPANSION_PANEL_DEFAULT_OPTIONS =
// Provide MatAccordion as undefined to prevent nested expansion panels from registering
// to the same accordion.
{provide: MAT_ACCORDION, useValue: undefined},
{provide: MAT_EXPANSION_PANEL, useExisting: MatExpansionPanel}
],
host: {
'class': 'mat-expansion-panel',
Expand Down Expand Up @@ -197,7 +199,7 @@ export class MatExpansionPanel extends CdkAccordionItem implements AfterContentI
}

ngAfterContentInit() {
if (this._lazyContent) {
if (this._lazyContent && this._lazyContent._expansionPanel === this) {
// Render the content as soon as the panel becomes open.
this.opened.pipe(
startWith(null!),
Expand Down
41 changes: 41 additions & 0 deletions src/material/expansion/expansion.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ describe('MatExpansionPanel', () => {
PanelWithCustomMargin,
LazyPanelWithContent,
LazyPanelOpenOnLoad,
NestedLazyPanelWithContent,
PanelWithTwoWayBinding,
],
});
Expand Down Expand Up @@ -70,6 +71,28 @@ describe('MatExpansionPanel', () => {
.toContain('Some content', 'Expected content to be rendered.');
}));

it('should not render lazy content from a child panel inside the parent', fakeAsync(() => {
const fixture = TestBed.createComponent(NestedLazyPanelWithContent);
fixture.componentInstance.parentExpanded = true;
fixture.detectChanges();

const parentContent: HTMLElement =
fixture.nativeElement.querySelector('.parent-panel .mat-expansion-panel-content');
const childContent: HTMLElement =
fixture.nativeElement.querySelector('.child-panel .mat-expansion-panel-content');

expect(parentContent.textContent!.trim())
.toBe('Parent content', 'Expected only parent content to be rendered.');
expect(childContent.textContent!.trim())
.toBe('', 'Expected child content element to be empty.');

fixture.componentInstance.childExpanded = true;
fixture.detectChanges();

expect(childContent.textContent!.trim())
.toBe('Child content', 'Expected child content element to be rendered.');
}));

it('emit correct events for change in panel expanded state', () => {
const fixture = TestBed.createComponent(PanelWithContent);
fixture.componentInstance.expanded = true;
Expand Down Expand Up @@ -490,3 +513,21 @@ class LazyPanelOpenOnLoad {}
class PanelWithTwoWayBinding {
expanded = false;
}


@Component({
template: `
<mat-expansion-panel class="parent-panel" [expanded]="parentExpanded">
Parent content
<mat-expansion-panel class="child-panel" [expanded]="childExpanded">
<ng-template matExpansionPanelContent>Child content</ng-template>
</mat-expansion-panel>
</mat-expansion-panel>
`
})
class NestedLazyPanelWithContent {
parentExpanded = false;
childExpanded = false;
}

3 changes: 2 additions & 1 deletion tools/public_api_guard/material/expansion.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@ export declare class MatExpansionPanelActionRow {
}

export declare class MatExpansionPanelContent {
_expansionPanel?: MatExpansionPanelBase | undefined;
_template: TemplateRef<any>;
constructor(_template: TemplateRef<any>);
constructor(_template: TemplateRef<any>, _expansionPanel?: MatExpansionPanelBase | undefined);
}

export interface MatExpansionPanelDefaultOptions {
Expand Down

0 comments on commit d224dcf

Please sign in to comment.