Skip to content

Commit 358a12d

Browse files
crisbetommalerba
authored andcommitted
feat(stepper): add animationDone event (#10752)
Exposes an `animationDone` event on the stepper that lets consumers react once the new step has completed its enter transition. This is along the same lines as the `MatTabGroup.animationDone`. Fixes #9087.
1 parent 41be069 commit 358a12d

File tree

4 files changed

+38
-2
lines changed

4 files changed

+38
-2
lines changed

src/lib/stepper/stepper-horizontal.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
<div *ngFor="let step of _steps; let i = index"
2424
class="mat-horizontal-stepper-content" role="tabpanel"
2525
[@stepTransition]="_getAnimationDirection(i)"
26+
(@stepTransition.done)="_animationDone($event)"
2627
[id]="_getStepContentId(i)"
2728
[attr.aria-labelledby]="_getStepLabelId(i)"
2829
[attr.aria-expanded]="selectedIndex === i">

src/lib/stepper/stepper-vertical.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<div class="mat-vertical-content-container" [class.mat-stepper-vertical-line]="!isLast">
1919
<div class="mat-vertical-stepper-content" role="tabpanel"
2020
[@stepTransition]="_getAnimationDirection(i)"
21+
(@stepTransition.done)="_animationDone($event)"
2122
[id]="_getStepContentId(i)"
2223
[attr.aria-labelledby]="_getStepLabelId(i)"
2324
[attr.aria-expanded]="selectedIndex === i">

src/lib/stepper/stepper.spec.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
import {StepperOrientation} from '@angular/cdk/stepper';
1313
import {dispatchKeyboardEvent} from '@angular/cdk/testing';
1414
import {Component, DebugElement} from '@angular/core';
15-
import {async, ComponentFixture, inject, TestBed} from '@angular/core/testing';
15+
import {async, ComponentFixture, inject, TestBed, fakeAsync, flush} from '@angular/core/testing';
1616
import {
1717
AbstractControl,
1818
AsyncValidatorFn,
@@ -318,6 +318,28 @@ describe('MatStepper', () => {
318318

319319
expect(optionalLabel.textContent).toBe('Valgfri');
320320
}));
321+
322+
it('should emit an event when the enter animation is done', fakeAsync(() => {
323+
let stepper = fixture.debugElement.query(By.directive(MatStepper)).componentInstance;
324+
let selectionChangeSpy = jasmine.createSpy('selectionChange spy');
325+
let animationDoneSpy = jasmine.createSpy('animationDone spy');
326+
let selectionChangeSubscription = stepper.selectionChange.subscribe(selectionChangeSpy);
327+
let animationDoneSubscription = stepper.animationDone.subscribe(animationDoneSpy);
328+
329+
stepper.selectedIndex = 1;
330+
fixture.detectChanges();
331+
332+
expect(selectionChangeSpy).toHaveBeenCalledTimes(1);
333+
expect(animationDoneSpy).not.toHaveBeenCalled();
334+
335+
flush();
336+
337+
expect(selectionChangeSpy).toHaveBeenCalledTimes(1);
338+
expect(animationDoneSpy).toHaveBeenCalledTimes(1);
339+
340+
selectionChangeSubscription.unsubscribe();
341+
animationDoneSubscription.unsubscribe();
342+
}));
321343
});
322344

323345
describe('icon overrides', () => {

src/lib/stepper/stepper.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
*/
88

99
import {Directionality} from '@angular/cdk/bidi';
10-
import {CdkStep, CdkStepper} from '@angular/cdk/stepper';
10+
import {CdkStep, CdkStepper, StepContentPositionState} from '@angular/cdk/stepper';
11+
import {AnimationEvent} from '@angular/animations';
1112
import {
1213
AfterContentInit,
1314
ChangeDetectionStrategy,
@@ -16,9 +17,11 @@ import {
1617
ContentChild,
1718
ContentChildren,
1819
Directive,
20+
EventEmitter,
1921
forwardRef,
2022
Inject,
2123
Optional,
24+
Output,
2225
QueryList,
2326
SkipSelf,
2427
TemplateRef,
@@ -79,6 +82,9 @@ export class MatStepper extends CdkStepper implements AfterContentInit {
7982
/** Custom icon overrides passed in by the consumer. */
8083
@ContentChildren(MatStepperIcon) _icons: QueryList<MatStepperIcon>;
8184

85+
/** Event emitted when the current step is done transitioning in. */
86+
@Output() readonly animationDone: EventEmitter<void> = new EventEmitter<void>();
87+
8288
/** Consumer-specified template-refs to be used to override the header icons. */
8389
_iconOverrides: {[key: string]: TemplateRef<MatStepperIconContext>} = {};
8490

@@ -96,6 +102,12 @@ export class MatStepper extends CdkStepper implements AfterContentInit {
96102
// Mark the component for change detection whenever the content children query changes
97103
this._steps.changes.pipe(takeUntil(this._destroyed)).subscribe(() => this._stateChanged());
98104
}
105+
106+
_animationDone(event: AnimationEvent) {
107+
if ((event.toState as StepContentPositionState) === 'current') {
108+
this.animationDone.emit();
109+
}
110+
}
99111
}
100112

101113
@Component({

0 commit comments

Comments
 (0)