Skip to content

Commit 37575c9

Browse files
crisbetotinayuangao
authored andcommitted
fix(expansion-panel): expand animation jumping (#8779)
Fixes the expansion panel jumping towards the end of the animation due to the overflow going from `visible` to `hidden`.
1 parent adc251c commit 37575c9

File tree

4 files changed

+37
-16
lines changed

4 files changed

+37
-16
lines changed

src/lib/expansion/expansion-panel.html

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
<ng-content select="mat-expansion-panel-header"></ng-content>
22
<div class="mat-expansion-panel-content"
33
role="region"
4-
[class.mat-expanded]="expanded"
54
[@bodyExpansion]="_getExpandedState()"
5+
(@bodyExpansion.done)="_bodyAnimation($event)"
6+
(@bodyExpansion.start)="_bodyAnimation($event)"
7+
[class.mat-expanded]="expanded"
8+
[attr.aria-labelledby]="_headerId"
69
[id]="id"
7-
[attr.aria-labelledby]="_headerId">
10+
#body>
811
<div class="mat-expansion-panel-body">
912
<ng-content></ng-content>
1013
<ng-template [cdkPortalOutlet]="_portal"></ng-template>

src/lib/expansion/expansion-panel.scss

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,10 @@
1111
}
1212

1313
.mat-expansion-panel-content {
14-
.mat-expanded & {
15-
overflow: visible;
16-
}
14+
overflow: hidden;
1715

18-
&, &.ng-animating {
19-
overflow: hidden;
16+
&.mat-expanded {
17+
overflow: visible;
2018
}
2119
}
2220

src/lib/expansion/expansion-panel.ts

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

9+
import {AnimationEvent} from '@angular/animations';
910
import {
1011
ChangeDetectionStrategy,
1112
ChangeDetectorRef,
@@ -139,6 +140,22 @@ export class MatExpansionPanel extends CdkAccordionItem
139140
super.ngOnDestroy();
140141
this._inputChanges.complete();
141142
}
143+
144+
_bodyAnimation(event: AnimationEvent) {
145+
const classList = event.element.classList;
146+
const cssClass = 'mat-expanded';
147+
const {phaseName, toState} = event;
148+
149+
// Toggle the body's `overflow: hidden` class when closing starts or when expansion ends in
150+
// order to prevent the cases where switching too early would cause the animation to jump.
151+
// Note that we do it directly on the DOM element to avoid the slight delay that comes
152+
// with doing it via change detection.
153+
if (phaseName === 'done' && toState === 'expanded') {
154+
classList.add(cssClass);
155+
} else if (phaseName === 'start' && toState === 'collapsed') {
156+
classList.remove(cssClass);
157+
}
158+
}
142159
}
143160

144161
@Directive({

src/lib/expansion/expansion.spec.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {async, TestBed, fakeAsync, tick, ComponentFixture} from '@angular/core/testing';
1+
import {async, TestBed, fakeAsync, tick, ComponentFixture, flush} from '@angular/core/testing';
22
import {Component, ViewChild} from '@angular/core';
33
import {By} from '@angular/platform-browser';
44
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
@@ -26,19 +26,22 @@ describe('MatExpansionPanel', () => {
2626
TestBed.compileComponents();
2727
}));
2828

29-
it('should expand and collapse the panel', () => {
29+
it('should expand and collapse the panel', fakeAsync(() => {
3030
const fixture = TestBed.createComponent(PanelWithContent);
31-
const contentEl = fixture.debugElement.query(By.css('.mat-expansion-panel-content'));
32-
const headerEl = fixture.debugElement.query(By.css('.mat-expansion-panel-header'));
31+
const contentEl = fixture.nativeElement.querySelector('.mat-expansion-panel-content');
32+
const headerEl = fixture.nativeElement.querySelector('.mat-expansion-panel-header');
3333
fixture.detectChanges();
34-
expect(headerEl.classes['mat-expanded']).toBeFalsy();
35-
expect(contentEl.classes['mat-expanded']).toBeFalsy();
34+
35+
expect(headerEl.classList).not.toContain('mat-expanded');
36+
expect(contentEl.classList).not.toContain('mat-expanded');
3637

3738
fixture.componentInstance.expanded = true;
3839
fixture.detectChanges();
39-
expect(headerEl.classes['mat-expanded']).toBeTruthy();
40-
expect(contentEl.classes['mat-expanded']).toBeTruthy();
41-
});
40+
flush();
41+
42+
expect(headerEl.classList).toContain('mat-expanded');
43+
expect(contentEl.classList).toContain('mat-expanded');
44+
}));
4245

4346
it('should be able to render panel content lazily', fakeAsync(() => {
4447
let fixture = TestBed.createComponent(LazyPanelWithContent);

0 commit comments

Comments
 (0)