Skip to content

fix(material/expansion): Expansion panel content flickers during animation for lazy-loaded custom height nested expansion panels #26772

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions src/material/expansion/expansion-panel.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
<ng-content select="mat-expansion-panel-header"></ng-content>
<div class="mat-expansion-panel-content"
role="region"
[@bodyExpansion]="_getExpandedState()"
(@bodyExpansion.done)="_bodyAnimationDone.next($event)"
[attr.aria-labelledby]="_headerId"
[id]="id"
#body>
<div
class="mat-expansion-panel-content"
role="region"
[class.mat-expansion-panel-hidden]="_panelHidden"
[@bodyExpansion]="_getExpandedState()"
(@bodyExpansion.start)="_animationStarted()"
(@bodyExpansion.done)="_bodyAnimationDone.next($event)"
[attr.aria-labelledby]="_headerId"
[id]="id"
#body
>
<div class="mat-expansion-panel-body">
<ng-content></ng-content>
<ng-template [cdkPortalOutlet]="_portal"></ng-template>
Expand Down
4 changes: 4 additions & 0 deletions src/material/expansion/expansion-panel.scss
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@
}
}

.mat-expansion-panel-hidden {
height: 0;
}

.mat-expansion-panel-body {
padding: 0 24px 16px;
}
Expand Down
36 changes: 36 additions & 0 deletions src/material/expansion/expansion-panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export const MAT_EXPANSION_PANEL_DEFAULT_OPTIONS =
'[class.mat-expanded]': 'expanded',
'[class._mat-animation-noopable]': '_animationMode === "NoopAnimations"',
'[class.mat-expansion-panel-spacing]': '_hasSpacing()',
'[@.disabled]': '_animationsDisabled',
},
})
export class MatExpansionPanel
Expand Down Expand Up @@ -150,6 +151,12 @@ export class MatExpansionPanel
/** Stream of body animation done events. */
readonly _bodyAnimationDone = new Subject<AnimationEvent>();

/** Whether Angular animations in the panel body should be disabled. */
_animationsDisabled = false;

/** Whether panel body should be hidden. */
_panelHidden = false;

constructor(
@Optional() @SkipSelf() @Inject(MAT_ACCORDION) accordion: MatAccordionBase,
_changeDetectorRef: ChangeDetectorRef,
Expand Down Expand Up @@ -218,6 +225,9 @@ export class MatExpansionPanel

ngAfterContentInit() {
if (this._lazyContent && this._lazyContent._expansionPanel === this) {
this._panelHidden = !this.expanded;
this._animationsDisabled = !this.expanded;

// Render the content as soon as the panel becomes open.
this.opened
.pipe(
Expand All @@ -227,6 +237,7 @@ export class MatExpansionPanel
)
.subscribe(() => {
this._portal = new TemplatePortal(this._lazyContent._template, this._viewContainerRef);
this._hidePanel();
});
}
}
Expand All @@ -251,6 +262,31 @@ export class MatExpansionPanel

return false;
}

_animationStarted() {
// Currently the `bodyExpansion` animation has a `void => collapsed` transition which is
// there to work around a bug in Angular (see #13088), however this introduces a different
// issue. The new transition will cause to flicker in certain situations (see #22715), if the
// consumer has set a inner lazy-loaded expansion panel's header height that is different from the
// default one.
// Part of work around is to disable animations on the body and re-enabling them after the first animation has run.
// Ideally this wouldn't be necessary if we remove the `void => collapsed` transition, but we have
// to wait for https://github.com/angular/angular/issues/18847 to be resolved.
this._animationsDisabled = false;
}

private _hidePanel(): void {
// Currently the `bodyExpansion` animation has a `void => collapsed` transition which is
// there to work around a bug in Angular (see #13088), however this introduces a different
// issue. The new transition will cause to flicker in certain situations (see #22715), if the
// consumer has set a inner lazy-loaded expansion panel's header height that is different from the
// default one.
// Part of work around is to set non-expanded panel's height to 0px with class on init
// so that outer expansion panel can calculate height of its body properly.
// Ideally this wouldn't be necessary if we remove the `void => collapsed` transition, but we have
// to wait for https://github.com/angular/angular/issues/18847 to be resolved.
this._panelHidden = false;
}
}

/**
Expand Down
4 changes: 4 additions & 0 deletions tools/public_api_guard/material/expansion.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ export class MatExpansionPanel extends CdkAccordionItem implements AfterContentI
readonly afterExpand: EventEmitter<void>;
// (undocumented)
_animationMode: string;
_animationsDisabled: boolean;
// (undocumented)
_animationStarted(): void;
_body: ElementRef<HTMLElement>;
readonly _bodyAnimationDone: Subject<AnimationEvent_2>;
close(): void;
Expand All @@ -125,6 +128,7 @@ export class MatExpansionPanel extends CdkAccordionItem implements AfterContentI
// (undocumented)
ngOnDestroy(): void;
open(): void;
_panelHidden: boolean;
_portal: TemplatePortal;
toggle(): void;
get togglePosition(): MatAccordionTogglePosition;
Expand Down