Skip to content

Commit

Permalink
fix(material/expansion): picking up lazy content from child component (
Browse files Browse the repository at this point in the history
…#14477)

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.
  • Loading branch information
crisbeto authored Mar 1, 2022
1 parent 9ad184d commit 64c2d31
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 5 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');
8 changes: 6 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,8 @@ 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 @@ -39,6 +39,7 @@ import {Subject} from 'rxjs';
import {distinctUntilChanged, filter, startWith, take} from 'rxjs/operators';
import {MatAccordionBase, MatAccordionTogglePosition, MAT_ACCORDION} from './accordion-base';
import {matExpansionAnimations} from './expansion-animations';
import {MAT_EXPANSION_PANEL} from './expansion-panel-base';
import {MatExpansionPanelContent} from './expansion-panel-content';

/** MatExpansionPanel's states. */
Expand Down Expand Up @@ -87,6 +88,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 @@ -215,7 +217,7 @@ export class MatExpansionPanel
}

ngAfterContentInit() {
if (this._lazyContent) {
if (this._lazyContent && this._lazyContent._expansionPanel === this) {
// Render the content as soon as the panel becomes open.
this.opened
.pipe(
Expand Down
47 changes: 47 additions & 0 deletions src/material/expansion/expansion.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ describe('MatExpansionPanel', () => {
LazyPanelOpenOnLoad,
PanelWithTwoWayBinding,
PanelWithHeaderTabindex,
NestedLazyPanelWithContent,
],
});
TestBed.compileComponents();
Expand Down Expand Up @@ -91,6 +92,36 @@ describe('MatExpansionPanel', () => {
.toContain('Some content');
}));

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 @@ -621,3 +652,19 @@ class PanelWithTwoWayBinding {
</mat-expansion-panel>`,
})
class PanelWithHeaderTabindex {}

@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;
}
1 change: 1 addition & 0 deletions src/material/expansion/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ export * from './expansion-panel';
export * from './expansion-panel-header';
export * from './expansion-panel-content';
export * from './expansion-animations';
export {MAT_EXPANSION_PANEL} from './expansion-panel-base';
9 changes: 7 additions & 2 deletions tools/public_api_guard/material/expansion.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ export const EXPANSION_PANEL_ANIMATION_TIMING = "225ms cubic-bezier(0.4,0.0,0.2,
// @public
export const MAT_ACCORDION: InjectionToken<MatAccordionBase>;

// @public
export const MAT_EXPANSION_PANEL: InjectionToken<MatExpansionPanelBase>;

// @public
export const MAT_EXPANSION_PANEL_DEFAULT_OPTIONS: InjectionToken<MatExpansionPanelDefaultOptions>;

Expand Down Expand Up @@ -142,13 +145,15 @@ export class MatExpansionPanelActionRow {

// @public
export class MatExpansionPanelContent {
constructor(_template: TemplateRef<any>);
constructor(_template: TemplateRef<any>, _expansionPanel?: MatExpansionPanelBase | undefined);
// (undocumented)
_expansionPanel?: MatExpansionPanelBase | undefined;
// (undocumented)
_template: TemplateRef<any>;
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<MatExpansionPanelContent, "ng-template[matExpansionPanelContent]", never, {}, {}, never>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<MatExpansionPanelContent, never>;
static ɵfac: i0.ɵɵFactoryDeclaration<MatExpansionPanelContent, [null, { optional: true; }]>;
}

// @public
Expand Down

0 comments on commit 64c2d31

Please sign in to comment.