Skip to content

Commit 574b2d1

Browse files
committed
fix(expansion-panel): expand animation jumping
Fixes the expansion panel jumping towards the end of the animation due to the overflow going from `visible` to `hidden`.
1 parent 5210b3e commit 574b2d1

File tree

4 files changed

+38
-17
lines changed

4 files changed

+38
-17
lines changed

src/lib/expansion/expansion-panel.html

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
<ng-content select="mat-expansion-panel-header"></ng-content>
2-
<div [class.mat-expanded]="expanded" class="mat-expansion-panel-content"
3-
[@bodyExpansion]="_getExpandedState()" [id]="id">
2+
<div class="mat-expansion-panel-content"
3+
[@bodyExpansion]="_getExpandedState()"
4+
(@bodyExpansion.done)="_bodyAnimation($event)"
5+
(@bodyExpansion.start)="_bodyAnimation($event)"
6+
[id]="id"
7+
#body>
48
<div class="mat-expansion-panel-body">
59
<ng-content></ng-content>
610
</div>

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 & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {animate, state, style, transition, trigger} from '@angular/animations';
9+
import {animate, state, style, transition, trigger, AnimationEvent} from '@angular/animations';
1010
import {
1111
ChangeDetectionStrategy,
1212
ChangeDetectorRef,
@@ -142,6 +142,22 @@ export class MatExpansionPanel extends _MatExpansionPanelMixinBase
142142
super.ngOnDestroy();
143143
this._inputChanges.complete();
144144
}
145+
146+
_bodyAnimation(event: AnimationEvent) {
147+
const classList = event.element.classList;
148+
const cssClass = 'mat-expanded';
149+
const {phaseName, toState} = event;
150+
151+
// Toggle the body's `overflow: hidden` class when closing starts or when expansion ends in
152+
// order to prevent the cases where switching too early would cause the animation to jump.
153+
// Note that we do it directly on the DOM element to avoid the slight delay that comes
154+
// with doing it via change detection.
155+
if (phaseName === 'done' && toState === 'expanded') {
156+
classList.add(cssClass);
157+
} else if (phaseName === 'start' && toState === 'collapsed') {
158+
classList.remove(cssClass);
159+
}
160+
}
145161
}
146162

147163
@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';
@@ -21,19 +21,22 @@ describe('MatExpansionPanel', () => {
2121
TestBed.compileComponents();
2222
}));
2323

24-
it('should expand and collapse the panel', () => {
24+
it('should expand and collapse the panel', fakeAsync(() => {
2525
const fixture = TestBed.createComponent(PanelWithContent);
26-
const contentEl = fixture.debugElement.query(By.css('.mat-expansion-panel-content'));
27-
const headerEl = fixture.debugElement.query(By.css('.mat-expansion-panel-header'));
26+
const contentEl = fixture.nativeElement.querySelector('.mat-expansion-panel-content');
27+
const headerEl = fixture.nativeElement.querySelector('.mat-expansion-panel-header');
2828
fixture.detectChanges();
29-
expect(headerEl.classes['mat-expanded']).toBeFalsy();
30-
expect(contentEl.classes['mat-expanded']).toBeFalsy();
29+
30+
expect(headerEl.classList).not.toContain('mat-expanded');
31+
expect(contentEl.classList).not.toContain('mat-expanded');
3132

3233
fixture.componentInstance.expanded = true;
3334
fixture.detectChanges();
34-
expect(headerEl.classes['mat-expanded']).toBeTruthy();
35-
expect(contentEl.classes['mat-expanded']).toBeTruthy();
36-
});
35+
flush();
36+
37+
expect(headerEl.classList).toContain('mat-expanded');
38+
expect(contentEl.classList).toContain('mat-expanded');
39+
}));
3740

3841
it('emit correct events for change in panel expanded state', () => {
3942
const fixture = TestBed.createComponent(PanelWithContent);

0 commit comments

Comments
 (0)