Skip to content

Commit

Permalink
fix(material/expansion): Expansion panel content flickers during anim…
Browse files Browse the repository at this point in the history
…ation for lazy-loaded custom height nested expansion panels

This fix introduces workaround for a `void => collapsed` animation that is causing improper behaviour for lazy-loaded body.
First, it disables first `void => collapsed` animation after init to disable recalculation which fixes changing heights of inner panels.
Second, it sets `mat-expansion-panel-hidden` class on content init and removes it on creating lazy-loaded body to remove flicker of parent expansion panel

Fixes angular#22715
  • Loading branch information
xAmaris committed Mar 11, 2023
1 parent ef0fa05 commit 92899e7
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 7 deletions.
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

0 comments on commit 92899e7

Please sign in to comment.