Skip to content

Commit 5005971

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 2436acd commit 5005971

File tree

4 files changed

+36
-17
lines changed

4 files changed

+36
-17
lines changed

src/lib/expansion/expansion-panel.html

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
<ng-content select="mat-expansion-panel-header"></ng-content>
22
<div class="mat-expansion-panel-content"
3-
[class.mat-expanded]="expanded"
43
[@bodyExpansion]="_getExpandedState()"
5-
[id]="id">
4+
(@bodyExpansion.done)="_bodyAnimation($event)"
5+
(@bodyExpansion.start)="_bodyAnimation($event)"
6+
[id]="id"
7+
#body>
68
<div class="mat-expansion-panel-body">
79
<ng-content></ng-content>
810
<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 & 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,
@@ -170,6 +170,22 @@ export class MatExpansionPanel extends _MatExpansionPanelMixinBase
170170
super.ngOnDestroy();
171171
this._inputChanges.complete();
172172
}
173+
174+
_bodyAnimation(event: AnimationEvent) {
175+
const classList = event.element.classList;
176+
const cssClass = 'mat-expanded';
177+
const {phaseName, toState} = event;
178+
179+
// Toggle the body's `overflow: hidden` class when closing starts or when expansion ends in
180+
// order to prevent the cases where switching too early would cause the animation to jump.
181+
// Note that we do it directly on the DOM element to avoid the slight delay that comes
182+
// with doing it via change detection.
183+
if (phaseName === 'done' && toState === 'expanded') {
184+
classList.add(cssClass);
185+
} else if (phaseName === 'start' && toState === 'collapsed') {
186+
classList.remove(cssClass);
187+
}
188+
}
173189
}
174190

175191
@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';
@@ -23,19 +23,22 @@ describe('MatExpansionPanel', () => {
2323
TestBed.compileComponents();
2424
}));
2525

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

3435
fixture.componentInstance.expanded = true;
3536
fixture.detectChanges();
36-
expect(headerEl.classes['mat-expanded']).toBeTruthy();
37-
expect(contentEl.classes['mat-expanded']).toBeTruthy();
38-
});
37+
flush();
38+
39+
expect(headerEl.classList).toContain('mat-expanded');
40+
expect(contentEl.classList).toContain('mat-expanded');
41+
}));
3942

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

0 commit comments

Comments
 (0)