Skip to content

Commit 129c9f3

Browse files
committed
fix(expansion-panel): picking up lazy content from child component
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 #14365.
1 parent 09dc459 commit 129c9f3

File tree

5 files changed

+76
-4
lines changed

5 files changed

+76
-4
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {InjectionToken} from '@angular/core';
10+
import {CdkAccordionItem} from '@angular/cdk/accordion';
11+
12+
/**
13+
* Base interface for a `MatExpansionPanel`.
14+
* @docs-private
15+
*/
16+
export interface MatExpansionPanelBase extends CdkAccordionItem {
17+
/** Whether the toggle indicator should be hidden. */
18+
hideToggle: boolean;
19+
}
20+
21+
/**
22+
* Token used to provide a `MatExpansionPanel` to `MatExpansionPanelContent`.
23+
* Used to avoid circular imports between `MatExpansionPanel` and `MatExpansionPanelContent`.
24+
*/
25+
export const MAT_EXPANSION_PANEL = new InjectionToken<MatExpansionPanelBase>('MAT_EXPANSION_PANEL');

src/material/expansion/expansion-panel-content.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

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

1112
/**
1213
* Expansion panel content that will be rendered lazily
@@ -16,5 +17,7 @@ import {Directive, TemplateRef} from '@angular/core';
1617
selector: 'ng-template[matExpansionPanelContent]'
1718
})
1819
export class MatExpansionPanelContent {
19-
constructor(public _template: TemplateRef<any>) {}
20+
constructor(
21+
public _template: TemplateRef<any>,
22+
@Inject(MAT_EXPANSION_PANEL) @Optional() public _expansionPanel?: MatExpansionPanelBase) {}
2023
}

src/material/expansion/expansion-panel.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import {filter, startWith, take, distinctUntilChanged} from 'rxjs/operators';
4040
import {matExpansionAnimations} from './expansion-animations';
4141
import {MatExpansionPanelContent} from './expansion-panel-content';
4242
import {MAT_ACCORDION, MatAccordionBase, MatAccordionTogglePosition} from './accordion-base';
43+
import {MAT_EXPANSION_PANEL} from './expansion-panel-base';
4344

4445
/** MatExpansionPanel's states. */
4546
export type MatExpansionPanelState = 'expanded' | 'collapsed';
@@ -89,6 +90,7 @@ export const MAT_EXPANSION_PANEL_DEFAULT_OPTIONS =
8990
// Provide MatAccordion as undefined to prevent nested expansion panels from registering
9091
// to the same accordion.
9192
{provide: MAT_ACCORDION, useValue: undefined},
93+
{provide: MAT_EXPANSION_PANEL, useExisting: MatExpansionPanel}
9294
],
9395
host: {
9496
'class': 'mat-expansion-panel',
@@ -193,7 +195,7 @@ export class MatExpansionPanel extends CdkAccordionItem implements AfterContentI
193195
}
194196

195197
ngAfterContentInit() {
196-
if (this._lazyContent) {
198+
if (this._lazyContent && this._lazyContent._expansionPanel === this) {
197199
// Render the content as soon as the panel becomes open.
198200
this.opened.pipe(
199201
startWith(null!),

src/material/expansion/expansion.spec.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ describe('MatExpansionPanel', () => {
2929
PanelWithCustomMargin,
3030
LazyPanelWithContent,
3131
LazyPanelOpenOnLoad,
32+
NestedLazyPanelWithContent,
3233
PanelWithTwoWayBinding,
3334
],
3435
});
@@ -74,6 +75,28 @@ describe('MatExpansionPanel', () => {
7475
.toContain('Some content', 'Expected content to be rendered.');
7576
}));
7677

78+
it('should not render lazy content from a child panel inside the parent', fakeAsync(() => {
79+
const fixture = TestBed.createComponent(NestedLazyPanelWithContent);
80+
fixture.componentInstance.parentExpanded = true;
81+
fixture.detectChanges();
82+
83+
const parentContent: HTMLElement =
84+
fixture.nativeElement.querySelector('.parent-panel .mat-expansion-panel-content');
85+
const childContent: HTMLElement =
86+
fixture.nativeElement.querySelector('.child-panel .mat-expansion-panel-content');
87+
88+
expect(parentContent.textContent!.trim())
89+
.toBe('Parent content', 'Expected only parent content to be rendered.');
90+
expect(childContent.textContent!.trim())
91+
.toBe('', 'Expected child content element to be empty.');
92+
93+
fixture.componentInstance.childExpanded = true;
94+
fixture.detectChanges();
95+
96+
expect(childContent.textContent!.trim())
97+
.toBe('Child content', 'Expected child content element to be rendered.');
98+
}));
99+
77100
it('emit correct events for change in panel expanded state', () => {
78101
const fixture = TestBed.createComponent(PanelWithContent);
79102
fixture.componentInstance.expanded = true;
@@ -494,3 +517,21 @@ class LazyPanelOpenOnLoad {}
494517
class PanelWithTwoWayBinding {
495518
expanded = false;
496519
}
520+
521+
522+
@Component({
523+
template: `
524+
<mat-expansion-panel class="parent-panel" [expanded]="parentExpanded">
525+
Parent content
526+
527+
<mat-expansion-panel class="child-panel" [expanded]="childExpanded">
528+
<ng-template matExpansionPanelContent>Child content</ng-template>
529+
</mat-expansion-panel>
530+
</mat-expansion-panel>
531+
`
532+
})
533+
class NestedLazyPanelWithContent {
534+
parentExpanded = false;
535+
childExpanded = false;
536+
}
537+

tools/public_api_guard/material/expansion.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,9 @@ export declare class MatExpansionPanelActionRow {
7474
}
7575

7676
export declare class MatExpansionPanelContent {
77+
_expansionPanel?: MatExpansionPanelBase | undefined;
7778
_template: TemplateRef<any>;
78-
constructor(_template: TemplateRef<any>);
79+
constructor(_template: TemplateRef<any>, _expansionPanel?: MatExpansionPanelBase | undefined);
7980
static ɵdir: i0.ɵɵDirectiveDefWithMeta<MatExpansionPanelContent, "ng-template[matExpansionPanelContent]", never, {}, {}, never>;
8081
static ɵfac: i0.ɵɵFactoryDef<MatExpansionPanelContent>;
8182
}

0 commit comments

Comments
 (0)