• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IgniteUI / igniteui-angular / 20960087204

13 Jan 2026 02:19PM UTC coverage: 12.713% (-78.8%) from 91.5%
20960087204

Pull #16746

github

web-flow
Merge 9afce6e5d into a967f087e
Pull Request #16746: fix(csv): export summaries - master

1008 of 16803 branches covered (6.0%)

19 of 23 new or added lines in 2 files covered. (82.61%)

24693 existing lines in 336 files now uncovered.

3985 of 31345 relevant lines covered (12.71%)

2.49 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.83
/projects/igniteui-angular/stepper/src/stepper/stepper.component.ts
1
import { AnimationReferenceMetadata, useAnimation } from '@angular/animations';
2
import { NgTemplateOutlet } from '@angular/common';
3
import { AfterContentInit, Component, ContentChild, ContentChildren, ElementRef, EventEmitter, HostBinding, Input, OnChanges, OnDestroy, OnInit, Output, QueryList, SimpleChanges, TemplateRef, booleanAttribute, inject } from '@angular/core';
4
import { Subject } from 'rxjs';
5
import { takeUntil } from 'rxjs/operators';
6

7
import { IgxCarouselComponentBase } from 'igniteui-angular/carousel';
8
import { IgxStepComponent } from './step/step.component';
9
import {
10
    IgxStepper, IgxStepperOrientation, IgxStepperTitlePosition, IgxStepType,
11
    IGX_STEPPER_COMPONENT, IStepChangedEventArgs, IStepChangingEventArgs, VerticalAnimationType,
12
    HorizontalAnimationType
13
} from './stepper.common';
14
import {
15
    IgxStepActiveIndicatorDirective,
16
    IgxStepCompletedIndicatorDirective,
17
    IgxStepInvalidIndicatorDirective
18
} from './stepper.directive';
19
import { IgxStepperService } from './stepper.service';
20
import { fadeIn, growVerIn, growVerOut } from 'igniteui-angular/animations';
21
import { ToggleAnimationSettings } from 'igniteui-angular/expansion-panel';
22

23

24
// TODO: common interface between IgxCarouselComponentBase and ToggleAnimationPlayer?
25

26
/**
27
 * IgxStepper provides a wizard-like workflow by dividing content into logical steps.
28
 *
29
 * @igxModule IgxStepperModule
30
 *
31
 * @igxKeywords stepper
32
 *
33
 * @igxGroup Layouts
34
 *
35
 * @remarks
36
 * The Ignite UI for Angular Stepper component allows the user to navigate between multiple steps.
37
 * It supports horizontal and vertical orientation as well as keyboard navigation and provides API methods to control the active step.
38
 * The component offers keyboard navigation and API to control the active step.
39
 *
40
 * @example
41
 * ```html
42
 * <igx-stepper>
43
 *  <igx-step [active]="true">
44
 *      <igx-icon igxStepIndicator>home</igx-icon>
45
 *      <p igxStepTitle>Home</p>
46
 *      <div igxStepContent>
47
 *         ...
48
 *      </div>
49
 *  </igx-step>
50
 *  <igx-step [optional]="true">
51
 *      <div igxStepContent>
52
 *          ...
53
 *      </div>
54
 *  </igx-step>
55
 *  <igx-step>
56
 *      <div igxStepContent>
57
 *          ...
58
 *      </div>
59
 *  </igx-step>
60
 * </igx-stepper>
61
 * ```
62
 */
63
@Component({
64
    selector: 'igx-stepper',
65
    templateUrl: 'stepper.component.html',
66
    providers: [
67
        IgxStepperService,
68
        { provide: IGX_STEPPER_COMPONENT, useExisting: IgxStepperComponent },
69
    ],
70
    imports: [NgTemplateOutlet]
71
})
72
export class IgxStepperComponent extends IgxCarouselComponentBase implements IgxStepper, OnChanges, OnInit, AfterContentInit, OnDestroy {
3✔
UNCOV
73
    private stepperService = inject(IgxStepperService);
×
UNCOV
74
    private element = inject<ElementRef<HTMLElement>>(ElementRef);
×
75

76

77
    /**
78
     * Get/Set the animation type of the stepper when the orientation direction is vertical.
79
     *
80
     * @remarks
81
     * Default value is `grow`. Other possible values are `fade` and `none`.
82
     *
83
     * ```html
84
     * <igx-stepper verticalAnimationType="none">
85
     * <igx-stepper>
86
     * ```
87
     */
88
    @Input()
89
    public get verticalAnimationType(): VerticalAnimationType {
90
        return this._verticalAnimationType;
×
91
    }
92

93
    public set verticalAnimationType(value: VerticalAnimationType) {
94
        // TODO: activeChange event is not emitted for the collapsing steps (loop through collapsing steps and emit)
UNCOV
95
        this.stepperService.collapsingSteps.clear();
×
UNCOV
96
        this._verticalAnimationType = value;
×
97

UNCOV
98
        switch (value) {
×
99
            case 'grow':
UNCOV
100
                this.verticalAnimationSettings = this.updateVerticalAnimationSettings(growVerIn, growVerOut);
×
UNCOV
101
                break;
×
102
            case 'fade':
UNCOV
103
                this.verticalAnimationSettings = this.updateVerticalAnimationSettings(fadeIn, null);
×
UNCOV
104
                break;
×
105
            case 'none':
UNCOV
106
                this.verticalAnimationSettings = this.updateVerticalAnimationSettings(null, null);
×
UNCOV
107
                break;
×
108
        }
109
    }
110

111
    /**
112
     * Get/Set the animation type of the stepper when the orientation direction is horizontal.
113
     *
114
     * @remarks
115
     * Default value is `grow`. Other possible values are `fade` and `none`.
116
     *
117
     * ```html
118
     * <igx-stepper animationType="none">
119
     * <igx-stepper>
120
     * ```
121
     */
122
    @Input()
123
    public get horizontalAnimationType(): HorizontalAnimationType {
124
        return this.animationType;
×
125
    }
126

127
    public set horizontalAnimationType(value: HorizontalAnimationType) {
128
        // TODO: activeChange event is not emitted for the collapsing steps (loop through collapsing steps and emit)
UNCOV
129
        this.stepperService.collapsingSteps.clear();
×
UNCOV
130
        this.animationType = value;
×
131
    }
132

133
    /**
134
     * Get/Set the animation duration.
135
     * ```html
136
     * <igx-stepper [animationDuration]="500">
137
     * <igx-stepper>
138
     * ```
139
     */
140
    @Input()
141
    public get animationDuration(): number {
UNCOV
142
        return this.defaultAnimationDuration;
×
143
    }
144

145
    public set animationDuration(value: number) {
UNCOV
146
        if (value && value > 0) {
×
UNCOV
147
            this.defaultAnimationDuration = value;
×
UNCOV
148
            return;
×
149
        }
UNCOV
150
        this.defaultAnimationDuration = this._defaultAnimationDuration;
×
151
    }
152

153
    /**
154
     * Get/Set whether the stepper is linear.
155
     *
156
     * @remarks
157
     * If the stepper is in linear mode and if the active step is valid only then the user is able to move forward.
158
     *
159
     * ```html
160
     * <igx-stepper [linear]="true"></igx-stepper>
161
     * ```
162
     */
163
    @Input({ transform: booleanAttribute })
164
    public get linear(): boolean {
UNCOV
165
        return this._linear;
×
166
    }
167

168
    public set linear(value: boolean) {
UNCOV
169
        this._linear = value;
×
UNCOV
170
        if (this._linear && this.steps.length > 0) {
×
171
            // when the stepper is in linear mode we should calculate which steps should be disabled
172
            // and which are visited i.e. their validity should be correctly displayed.
UNCOV
173
            this.stepperService.calculateVisitedSteps();
×
UNCOV
174
            this.stepperService.calculateLinearDisabledSteps();
×
175
        } else {
UNCOV
176
            this.stepperService.linearDisabledSteps.clear();
×
177
        }
178
    }
179

180
    /**
181
     * Get/Set the stepper orientation.
182
     *
183
     * ```typescript
184
     * this.stepper.orientation = IgxStepperOrientation.Vertical;
185
     * ```
186
     */
187
    @HostBinding('attr.aria-orientation')
188
    @Input()
189
    public get orientation(): IgxStepperOrientation {
UNCOV
190
        return this._orientation;
×
191
    }
192

193
    public set orientation(value: IgxStepperOrientation) {
UNCOV
194
        if (this._orientation === value) {
×
UNCOV
195
            return;
×
196
        }
197

198
        // TODO: activeChange event is not emitted for the collapsing steps
UNCOV
199
        this.stepperService.collapsingSteps.clear();
×
UNCOV
200
        this._orientation = value;
×
UNCOV
201
        this._defaultTitlePosition = this._orientation === IgxStepperOrientation.Horizontal ?
×
202
            IgxStepperTitlePosition.Bottom : IgxStepperTitlePosition.End;
203
    }
204

205
    /**
206
     * Get/Set the type of the steps.
207
     *
208
     * ```typescript
209
     * this.stepper.stepType = IgxStepType.Indicator;
210
     * ```
211
     */
212
    @Input()
UNCOV
213
    public stepType: IgxStepType = IgxStepType.Full;
×
214

215
    /**
216
     * Get/Set whether the content is displayed above the steps.
217
     *
218
     * @remarks
219
     * Default value is `false` and the content is below the steps.
220
     *
221
     * ```typescript
222
     * this.stepper.contentTop = true;
223
     * ```
224
     */
225
    @Input({ transform: booleanAttribute })
UNCOV
226
    public contentTop = false;
×
227

228
    /**
229
     * Get/Set the position of the steps title.
230
     *
231
     * @remarks
232
     * The default value when the stepper is horizontally orientated is `bottom`.
233
     * In vertical layout the default title position is `end`.
234
     *
235
     * ```typescript
236
     * this.stepper.titlePosition = IgxStepperTitlePosition.Top;
237
     * ```
238
     */
239
    @Input()
UNCOV
240
    public titlePosition: IgxStepperTitlePosition = null;
×
241

242
    /** @hidden @internal **/
243
    @HostBinding('class.igx-stepper')
UNCOV
244
    public cssClass = 'igx-stepper';
×
245

246
    /** @hidden @internal **/
247
    @HostBinding('attr.role')
UNCOV
248
    public role = 'tablist';
×
249

250
    /** @hidden @internal **/
251
    @HostBinding('class.igx-stepper--horizontal')
252
    public get directionClass() {
UNCOV
253
        return this.orientation === IgxStepperOrientation.Horizontal;
×
254
    }
255

256
    /**
257
     * Emitted when the stepper's active step is changing.
258
     *
259
     *```html
260
     * <igx-stepper (activeStepChanging)="handleActiveStepChanging($event)">
261
     * </igx-stepper>
262
     * ```
263
     *
264
     *```typescript
265
     * public handleActiveStepChanging(event: IStepChangingEventArgs) {
266
     *  if (event.newIndex < event.oldIndex) {
267
     *      event.cancel = true;
268
     *  }
269
     * }
270
     *```
271
     */
272
    @Output()
UNCOV
273
    public activeStepChanging = new EventEmitter<IStepChangingEventArgs>();
×
274

275
    /**
276
     * Emitted when the active step is changed.
277
     *
278
     * @example
279
     * ```
280
     * <igx-stepper (activeStepChanged)="handleActiveStepChanged($event)"></igx-stepper>
281
     * ```
282
     */
283
    @Output()
UNCOV
284
    public activeStepChanged = new EventEmitter<IStepChangedEventArgs>();
×
285

286
    /** @hidden @internal */
287
    @ContentChild(IgxStepInvalidIndicatorDirective, { read: TemplateRef })
288
    public invalidIndicatorTemplate: TemplateRef<IgxStepInvalidIndicatorDirective>;
289

290
    /** @hidden @internal */
291
    @ContentChild(IgxStepCompletedIndicatorDirective, { read: TemplateRef })
292
    public completedIndicatorTemplate: TemplateRef<IgxStepCompletedIndicatorDirective>;
293

294
    /** @hidden @internal */
295
    @ContentChild(IgxStepActiveIndicatorDirective, { read: TemplateRef })
296
    public activeIndicatorTemplate: TemplateRef<IgxStepActiveIndicatorDirective>;
297

298
    /** @hidden @internal */
299
    @ContentChildren(IgxStepComponent, { descendants: false })
300
    private _steps: QueryList<IgxStepComponent>;
301

302
    /**
303
     * Get all steps.
304
     *
305
     * ```typescript
306
     * const steps: IgxStepComponent[] = this.stepper.steps;
307
     * ```
308
     */
309
    public get steps(): IgxStepComponent[] {
UNCOV
310
        return this._steps?.toArray() || [];
×
311
    }
312

313
    /** @hidden @internal */
314
    public get nativeElement(): HTMLElement {
UNCOV
315
        return this.element.nativeElement;
×
316
    }
317

318
    /** @hidden @internal */
UNCOV
319
    public verticalAnimationSettings: ToggleAnimationSettings = {
×
320
        openAnimation: growVerIn,
321
        closeAnimation: growVerOut,
322
    };
323
    /** @hidden @internal */
UNCOV
324
    public _defaultTitlePosition: IgxStepperTitlePosition = IgxStepperTitlePosition.Bottom;
×
UNCOV
325
    private destroy$ = new Subject<void>();
×
UNCOV
326
    private _orientation: IgxStepperOrientation = IgxStepperOrientation.Horizontal;
×
UNCOV
327
    private _verticalAnimationType: VerticalAnimationType = VerticalAnimationType.Grow;
×
UNCOV
328
    private _linear = false;
×
UNCOV
329
    private readonly _defaultAnimationDuration = 350;
×
330

331
    constructor() {
UNCOV
332
        super();
×
UNCOV
333
        this.stepperService.stepper = this;
×
334
    }
335

336
    /** @hidden @internal */
337
    public ngOnChanges(changes: SimpleChanges): void {
UNCOV
338
        if (changes['animationDuration']) {
×
UNCOV
339
            this.verticalAnimationType = this._verticalAnimationType;
×
340
        }
341
    }
342

343
    /** @hidden @internal */
344
    public ngOnInit(): void {
UNCOV
345
        this.enterAnimationDone.pipe(takeUntil(this.destroy$)).subscribe(() => {
×
UNCOV
346
            this.activeStepChanged.emit({ owner: this, index: this.stepperService.activeStep.index });
×
347
        });
UNCOV
348
        this.leaveAnimationDone.pipe(takeUntil(this.destroy$)).subscribe(() => {
×
UNCOV
349
            if (this.stepperService.collapsingSteps.size === 1) {
×
UNCOV
350
                this.stepperService.collapse(this.stepperService.previousActiveStep);
×
351
            } else {
UNCOV
352
                Array.from(this.stepperService.collapsingSteps).slice(0, this.stepperService.collapsingSteps.size - 1)
×
UNCOV
353
                    .forEach(step => this.stepperService.collapse(step));
×
354
            }
355
        });
356

357

358
    }
359

360
    /** @hidden @internal */
361
    public ngAfterContentInit(): void {
362
        let activeStep;
UNCOV
363
        this.steps.forEach((step, index) => {
×
UNCOV
364
            this.updateStepAria(step, index);
×
UNCOV
365
            if (!activeStep && step.active) {
×
UNCOV
366
                activeStep = step;
×
367
            }
368
        });
UNCOV
369
        if (!activeStep) {
×
UNCOV
370
            this.activateFirstStep(true);
×
371
        }
372

UNCOV
373
        if (this.linear) {
×
UNCOV
374
            this.stepperService.calculateLinearDisabledSteps();
×
375
        }
376

UNCOV
377
        this.handleStepChanges();
×
378
    }
379

380
    /** @hidden @internal */
381
    public override ngOnDestroy(): void {
UNCOV
382
        super.ngOnDestroy();
×
UNCOV
383
        this.destroy$.next();
×
UNCOV
384
        this.destroy$.complete();
×
385
    }
386

387
    /**
388
     * Activates the step at a given index.
389
     *
390
     *```typescript
391
     * this.stepper.navigateTo(1);
392
     *```
393
     */
394
    public navigateTo(index: number): void {
UNCOV
395
        const step = this.steps[index];
×
UNCOV
396
        if (!step || this.stepperService.activeStep === step) {
×
UNCOV
397
            return;
×
398
        }
UNCOV
399
        this.activateStep(step);
×
400
    }
401

402
    /**
403
     * Activates the next enabled step.
404
     *
405
     *```typescript
406
     * this.stepper.next();
407
     *```
408
     */
409
    public next(): void {
UNCOV
410
        this.moveToNextStep();
×
411
    }
412

413
    /**
414
     * Activates the previous enabled step.
415
     *
416
     *```typescript
417
     * this.stepper.prev();
418
     *```
419
     */
420
    public prev(): void {
421
        this.moveToNextStep(false);
×
422
    }
423

424
    /**
425
     * Resets the stepper to its initial state i.e. activates the first step.
426
     *
427
     * @remarks
428
     * The steps' content will not be automatically reset.
429
     *```typescript
430
     * this.stepper.reset();
431
     *```
432
     */
433
    public reset(): void {
UNCOV
434
        this.stepperService.visitedSteps.clear();
×
UNCOV
435
        const activeStep = this.steps.find(s => !s.disabled);
×
UNCOV
436
        if (activeStep) {
×
UNCOV
437
            this.activateStep(activeStep);
×
438
        }
439
    }
440

441
    /** @hidden @internal */
442
    public playHorizontalAnimations(): void {
UNCOV
443
        this.previousItem = this.stepperService.previousActiveStep;
×
UNCOV
444
        this.currentItem = this.stepperService.activeStep;
×
UNCOV
445
        this.triggerAnimations();
×
446
    }
447

448
    protected getPreviousElement(): HTMLElement {
UNCOV
449
        return this.stepperService.previousActiveStep?.contentContainer.nativeElement;
×
450
    }
451

452
    protected getCurrentElement(): HTMLElement {
UNCOV
453
        return this.stepperService.activeStep.contentContainer.nativeElement;
×
454
    }
455

456
    private updateVerticalAnimationSettings(
457
        openAnimation: AnimationReferenceMetadata,
458
        closeAnimation: AnimationReferenceMetadata): ToggleAnimationSettings {
UNCOV
459
        const customCloseAnimation = useAnimation(closeAnimation, {
×
460
            params: {
461
                duration: this.animationDuration + 'ms'
462
            }
463
        });
UNCOV
464
        const customOpenAnimation = useAnimation(openAnimation, {
×
465
            params: {
466
                duration: this.animationDuration + 'ms'
467
            }
468
        });
469

UNCOV
470
        return {
×
471
            openAnimation: openAnimation ? customOpenAnimation : null,
×
472
            closeAnimation: closeAnimation ? customCloseAnimation : null
×
473
        };
474
    }
475

476
    private updateStepAria(step: IgxStepComponent, index: number): void {
UNCOV
477
        step._index = index;
×
UNCOV
478
        step.renderer.setAttribute(step.nativeElement, 'aria-setsize', (this.steps.length).toString());
×
UNCOV
479
        step.renderer.setAttribute(step.nativeElement, 'aria-posinset', (index + 1).toString());
×
480
    }
481

482
    private handleStepChanges(): void {
UNCOV
483
        this._steps.changes.pipe(takeUntil(this.destroy$)).subscribe(steps => {
×
UNCOV
484
            Promise.resolve().then(() => {
×
UNCOV
485
                steps.forEach((step, index) => {
×
UNCOV
486
                    this.updateStepAria(step, index);
×
487
                });
488

489
                // when the active step is removed
UNCOV
490
                const hasActiveStep = this.steps.find(s => s === this.stepperService.activeStep);
×
UNCOV
491
                if (!hasActiveStep) {
×
UNCOV
492
                    this.activateFirstStep();
×
493
                }
494
                // TO DO: mark step added before the active as visited?
UNCOV
495
                if (this.linear) {
×
UNCOV
496
                    this.stepperService.calculateLinearDisabledSteps();
×
497
                }
498
            });
499
        });
500
    }
501

502
    private activateFirstStep(activateInitially = false) {
×
UNCOV
503
        const firstEnabledStep = this.steps.find(s => !s.disabled);
×
UNCOV
504
        if (firstEnabledStep) {
×
UNCOV
505
            firstEnabledStep.active = true;
×
UNCOV
506
            if (activateInitially) {
×
UNCOV
507
                firstEnabledStep.activeChange.emit(true);
×
UNCOV
508
                this.activeStepChanged.emit({ owner: this, index: firstEnabledStep.index });
×
509
            }
510
        }
511
    }
512

513
    private activateStep(step: IgxStepComponent) {
UNCOV
514
        if (this.orientation === IgxStepperOrientation.Horizontal) {
×
UNCOV
515
            step.changeHorizontalActiveStep();
×
516
        } else {
UNCOV
517
            this.stepperService.expand(step);
×
518
        }
519
    }
520

521
    private moveToNextStep(next = true) {
×
UNCOV
522
        let steps: IgxStepComponent[] = this.steps;
×
UNCOV
523
        let activeStepIndex = this.stepperService.activeStep.index;
×
UNCOV
524
        if (!next) {
×
525
            steps = this.steps.reverse();
×
526
            activeStepIndex = steps.findIndex(s => s === this.stepperService.activeStep);
×
527
        }
528

UNCOV
529
        const nextStep = steps.find((s, i) => i > activeStepIndex && s.isAccessible);
×
UNCOV
530
        if (nextStep) {
×
UNCOV
531
            this.activateStep(nextStep);
×
532
        }
533
    }
534
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc