• 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

1.37
/projects/igniteui-angular/stepper/src/stepper/step/step.component.ts
1
import { AfterViewInit, booleanAttribute, ChangeDetectorRef, Component, ContentChild, ElementRef, EventEmitter, forwardRef, HostBinding, HostListener, Input, OnDestroy, Output, Renderer2, TemplateRef, ViewChild, inject } from '@angular/core';
2
import { takeUntil } from 'rxjs/operators';
3
import { IgxStep, IgxStepper, IgxStepperOrientation, IgxStepType, IGX_STEPPER_COMPONENT, IGX_STEP_COMPONENT, HorizontalAnimationType } from '../stepper.common';
4
import { IgxStepContentDirective, IgxStepIndicatorDirective } from '../stepper.directive';
5
import { IgxStepperService } from '../stepper.service';
6
import { NgClass, NgTemplateOutlet } from '@angular/common';
7
import { IgxRippleDirective } from 'igniteui-angular/directives';
8
import { ToggleAnimationPlayer, ToggleAnimationSettings } from 'igniteui-angular/expansion-panel';
9
import { CarouselAnimationDirection, IgxSlideComponentBase } from 'igniteui-angular/carousel';
10
import { ɵIgxDirectionality, PlatformUtil } from 'igniteui-angular/core';
11

12
let NEXT_ID = 0;
3✔
13

14
/**
15
 * The IgxStepComponent is used within the `igx-stepper` element and it holds the content of each step.
16
 * It also supports custom indicators, title and subtitle.
17
 *
18
 * @igxModule IgxStepperModule
19
 *
20
 * @igxKeywords step
21
 *
22
 * @example
23
 * ```html
24
 *  <igx-stepper>
25
 *  ...
26
 *    <igx-step [active]="true" [completed]="true">
27
 *      ...
28
 *    </igx-step>
29
 *  ...
30
 *  </igx-stepper>
31
 * ```
32
 */
33
@Component({
34
    selector: 'igx-step',
35
    templateUrl: 'step.component.html',
36
    providers: [
37
        { provide: IGX_STEP_COMPONENT, useExisting: IgxStepComponent }
38
    ],
39
    imports: [NgClass, IgxRippleDirective, NgTemplateOutlet]
40
})
41
export class IgxStepComponent extends ToggleAnimationPlayer implements IgxStep, AfterViewInit, OnDestroy, IgxSlideComponentBase {
3✔
UNCOV
42
    public stepper = inject<IgxStepper>(IGX_STEPPER_COMPONENT);
×
UNCOV
43
    public cdr = inject(ChangeDetectorRef);
×
UNCOV
44
    public renderer = inject(Renderer2);
×
UNCOV
45
    protected platform = inject(PlatformUtil);
×
UNCOV
46
    protected stepperService = inject(IgxStepperService);
×
UNCOV
47
    private element = inject<ElementRef<HTMLElement>>(ElementRef);
×
UNCOV
48
    private dir = inject(ɵIgxDirectionality);
×
49

50

51
    /**
52
     * Get/Set the `id` of the step component.
53
     * Default value is `"igx-step-0"`;
54
     * ```html
55
     * <igx-step id="my-first-step"></igx-step>
56
     * ```
57
     * ```typescript
58
     * const stepId = this.step.id;
59
     * ```
60
     */
61
    @HostBinding('attr.id')
62
    @Input()
UNCOV
63
    public id = `igx-step-${NEXT_ID++}`;
×
64

65
    /**
66
     * Get/Set whether the step is interactable.
67
     *
68
     * ```html
69
     * <igx-stepper>
70
     * ...
71
     *     <igx-step [disabled]="true"></igx-step>
72
     * ...
73
     * </igx-stepper>
74
     * ```
75
     *
76
     * ```typescript
77
     * this.stepper.steps[1].disabled = true;
78
     * ```
79
     */
80
    @Input({ transform: booleanAttribute })
81
    public set disabled(value: boolean) {
UNCOV
82
        this._disabled = value;
×
UNCOV
83
        if (this.stepper.linear) {
×
UNCOV
84
            this.stepperService.calculateLinearDisabledSteps();
×
85
        }
86
    }
87

88
    public get disabled(): boolean {
UNCOV
89
        return this._disabled;
×
90
    }
91

92
    /**
93
     * Get/Set whether the step is completed.
94
     *
95
     * @remarks
96
     * When set to `true` the following separator is styled `solid`.
97
     *
98
     * ```html
99
     * <igx-stepper>
100
     * ...
101
     *     <igx-step [completed]="true"></igx-step>
102
     * ...
103
     * </igx-stepper>
104
     * ```
105
     *
106
     * ```typescript
107
     * this.stepper.steps[1].completed = true;
108
     * ```
109
     */
110
    @Input({ transform: booleanAttribute })
111
    @HostBinding('class.igx-stepper__step--completed')
UNCOV
112
    public completed = false;
×
113

114
    /**
115
     * Get/Set whether the step is valid.
116
     *```html
117
     * <igx-step [isValid]="form.form.valid">
118
     *      ...
119
     *      <div igxStepContent>
120
     *          <form #form="ngForm">
121
     *              ...
122
     *          </form>
123
     *      </div>
124
     * </igx-step>
125
     * ```
126
     */
127
    @Input({ transform: booleanAttribute })
128
    public get isValid(): boolean {
UNCOV
129
        return this._valid;
×
130
    }
131

132
    public set isValid(value: boolean) {
UNCOV
133
        this._valid = value;
×
UNCOV
134
        if (this.stepper.linear && this.index !== undefined) {
×
UNCOV
135
            this.stepperService.calculateLinearDisabledSteps();
×
136
        }
137
    }
138

139
    /**
140
     * Get/Set whether the step is optional.
141
     *
142
     * @remarks
143
     * Optional steps validity does not affect the default behavior when the stepper is in linear mode i.e.
144
     * if optional step is invalid the user could still move to the next step.
145
     *
146
     * ```html
147
     * <igx-step [optional]="true"></igx-step>
148
     * ```
149
     * ```typescript
150
     * this.stepper.steps[1].optional = true;
151
     * ```
152
     */
153
    @Input({ transform: booleanAttribute })
UNCOV
154
    public optional = false;
×
155

156
    /**
157
     * Get/Set the active state of the step
158
     *
159
     * ```html
160
     * <igx-step [active]="true"></igx-step>
161
     * ```
162
     *
163
     * ```typescript
164
     * this.stepper.steps[1].active = true;
165
     * ```
166
     *
167
     * @param value: boolean
168
     */
169
    @HostBinding('attr.aria-selected')
170
    @Input({ transform: booleanAttribute })
171
    public set active(value: boolean) {
UNCOV
172
        if (value) {
×
UNCOV
173
            this.stepperService.expandThroughApi(this);
×
174
        } else {
UNCOV
175
            this.stepperService.collapse(this);
×
176
        }
177
    }
178

179
    public get active(): boolean {
UNCOV
180
        return this.stepperService.activeStep === this;
×
181
    }
182

183
    /** @hidden @internal */
184
    @HostBinding('attr.tabindex')
185
    @Input()
186
    public set tabIndex(value: number) {
UNCOV
187
        this._tabIndex = value;
×
188
    }
189

190
    public get tabIndex(): number {
UNCOV
191
        return this._tabIndex;
×
192
    }
193

194
    /** @hidden @internal **/
195
    @HostBinding('attr.role')
UNCOV
196
    public role = 'tab';
×
197

198
    /** @hidden @internal */
199
    @HostBinding('attr.aria-controls')
200
    public get contentId(): string {
UNCOV
201
        return this.content?.id;
×
202
    }
203

204
    /** @hidden @internal */
205
    @HostBinding('class.igx-stepper__step')
UNCOV
206
    public cssClass = true;
×
207

208
    /** @hidden @internal */
209
    @HostBinding('class.igx-stepper__step--disabled')
210
    public get generalDisabled(): boolean {
UNCOV
211
        return this.disabled || this.linearDisabled;
×
212
    }
213

214
    /** @hidden @internal */
215
    @HostBinding('class')
216
    public get titlePositionTop(): string {
UNCOV
217
        if (this.stepper.stepType !== IgxStepType.Full) {
×
UNCOV
218
            return 'igx-stepper__step--simple';
×
219
        }
220

UNCOV
221
        return `igx-stepper__step--${this.titlePosition}`;
×
222
    }
223

224
    /**
225
     * Emitted when the step's `active` property changes. Can be used for two-way binding.
226
     *
227
     * ```html
228
     * <igx-step [(active)]="this.isActive">
229
     * </igx-step>
230
     * ```
231
     *
232
     * ```typescript
233
     * const step: IgxStepComponent = this.stepper.step[0];
234
     * step.activeChange.subscribe((e: boolean) => console.log("Step active state change to ", e))
235
     * ```
236
     */
237
    @Output()
UNCOV
238
    public activeChange = new EventEmitter<boolean>();
×
239

240
    /** @hidden @internal */
241
    @ViewChild('contentTemplate', { static: true })
242
    public contentTemplate: TemplateRef<any>;
243

244
    /** @hidden @internal */
245
    @ViewChild('customIndicator', { static: true })
246
    public customIndicatorTemplate: TemplateRef<any>;
247

248
    /** @hidden @internal */
249
    @ViewChild('contentContainer')
250
    public contentContainer: ElementRef;
251

252
    /** @hidden @internal */
UNCOV
253
    @ContentChild(forwardRef(() => IgxStepIndicatorDirective))
×
254
    public indicator: IgxStepIndicatorDirective;
255

256
    /** @hidden @internal */
UNCOV
257
    @ContentChild(forwardRef(() => IgxStepContentDirective))
×
258
    public content: IgxStepContentDirective;
259

260
    /**
261
     * Get the step index inside of the stepper.
262
     *
263
     * ```typescript
264
     * const step = this.stepper.steps[1];
265
     * const stepIndex: number = step.index;
266
     * ```
267
     */
268
    public get index(): number {
UNCOV
269
        return this._index;
×
270
    }
271

272
    /** @hidden @internal */
273
    public get indicatorTemplate(): TemplateRef<any> {
UNCOV
274
        if (this.active && this.stepper.activeIndicatorTemplate) {
×
UNCOV
275
            return this.stepper.activeIndicatorTemplate;
×
276
        }
277

UNCOV
278
        if (!this.isValid && this.stepper.invalidIndicatorTemplate) {
×
UNCOV
279
            return this.stepper.invalidIndicatorTemplate;
×
280
        }
281

UNCOV
282
        if (this.completed && this.stepper.completedIndicatorTemplate) {
×
UNCOV
283
            return this.stepper.completedIndicatorTemplate;
×
284
        }
285

UNCOV
286
        if (this.indicator) {
×
UNCOV
287
            return this.customIndicatorTemplate;
×
288
        }
289

UNCOV
290
        return null;
×
291
    }
292

293
    /** @hidden @internal */
294
    public get direction(): CarouselAnimationDirection {
UNCOV
295
        return this.stepperService.previousActiveStep
×
296
            && this.stepperService.previousActiveStep.index > this.index
297
            ? CarouselAnimationDirection.PREV
298
            : CarouselAnimationDirection.NEXT;
299
    }
300

301
    /** @hidden @internal */
302
    public get isAccessible(): boolean {
UNCOV
303
        return !this.disabled && !this.linearDisabled;
×
304
    }
305

306
    /** @hidden @internal */
307
    public get isHorizontal(): boolean {
UNCOV
308
        return this.stepper.orientation === IgxStepperOrientation.Horizontal;
×
309
    }
310

311
    /** @hidden @internal */
312
    public get isTitleVisible(): boolean {
UNCOV
313
        return this.stepper.stepType !== IgxStepType.Indicator;
×
314
    }
315

316
    /** @hidden @internal */
317
    public get isIndicatorVisible(): boolean {
UNCOV
318
        return this.stepper.stepType !== IgxStepType.Title;
×
319
    }
320

321
    /** @hidden @internal */
322
    public get titlePosition(): string {
UNCOV
323
        return this.stepper.titlePosition ? this.stepper.titlePosition : this.stepper._defaultTitlePosition;
×
324
    }
325

326
    /** @hidden @internal */
327
    public get linearDisabled(): boolean {
UNCOV
328
        return this.stepperService.linearDisabledSteps.has(this);
×
329
    }
330

331
    /** @hidden @internal */
332
    public get collapsing(): boolean {
UNCOV
333
        return this.stepperService.collapsingSteps.has(this);
×
334
    }
335

336
    /** @hidden @internal */
337
    public override get animationSettings(): ToggleAnimationSettings {
UNCOV
338
        return this.stepper.verticalAnimationSettings;
×
339
    }
340

341
    /** @hidden @internal */
342
    public get contentClasses(): any {
UNCOV
343
        if (this.isHorizontal) {
×
UNCOV
344
            return { 'igx-stepper__body-content': true, 'igx-stepper__body-content--active': this.active };
×
345
        } else {
UNCOV
346
            return 'igx-stepper__step-content';
×
347
        }
348
    }
349

350
    /** @hidden @internal */
351
    public get stepHeaderClasses(): any {
UNCOV
352
        return {
×
353
            'igx-stepper__step--optional': this.optional,
354
            'igx-stepper__step-header--current': this.active,
355
            'igx-stepper__step-header--invalid': !this.isValid
×
356
                && this.stepperService.visitedSteps.has(this) && !this.active && this.isAccessible
357
        };
358
    }
359

360
    /** @hidden @internal */
361
    public get nativeElement(): HTMLElement {
UNCOV
362
        return this.element.nativeElement;
×
363
    }
364
    /** @hidden @internal */
365
    public previous: boolean;
366
    /** @hidden @internal */
367
    public _index: number;
UNCOV
368
    private _tabIndex = -1;
×
UNCOV
369
    private _valid = true;
×
UNCOV
370
    private _focused = false;
×
UNCOV
371
    private _disabled = false;
×
372

373
    /** @hidden @internal */
374
    @HostListener('focus')
375
    public onFocus(): void {
UNCOV
376
        this._focused = true;
×
UNCOV
377
        this.stepperService.focusedStep = this;
×
UNCOV
378
        if (this.stepperService.focusedStep !== this.stepperService.activeStep) {
×
UNCOV
379
            this.stepperService.activeStep.tabIndex = -1;
×
380
        }
381
    }
382

383
    /** @hidden @internal */
384
    @HostListener('blur')
385
    public onBlur(): void {
UNCOV
386
        this._focused = false;
×
UNCOV
387
        this.stepperService.activeStep.tabIndex = 0;
×
388
    }
389

390
    /** @hidden @internal */
391
    @HostListener('keydown', ['$event'])
392
    public handleKeydown(event: KeyboardEvent): void {
UNCOV
393
        if (!this._focused) {
×
394
            return;
×
395
        }
UNCOV
396
        const key = event.key;
×
UNCOV
397
        if (this.stepper.orientation === IgxStepperOrientation.Horizontal) {
×
UNCOV
398
            if (key === this.platform.KEYMAP.ARROW_UP || key === this.platform.KEYMAP.ARROW_DOWN) {
×
399
                return;
×
400
            }
401
        }
UNCOV
402
        if (!(this.platform.isNavigationKey(key) || this.platform.isActivationKey(event))) {
×
403
            return;
×
404
        }
UNCOV
405
        event.preventDefault();
×
UNCOV
406
        this.handleNavigation(key);
×
407
    }
408

409
    /** @hidden @internal */
410
    public ngAfterViewInit(): void {
UNCOV
411
        this.openAnimationDone.pipe(takeUntil(this.destroy$)).subscribe(
×
412
            () => {
UNCOV
413
                if (this.stepperService.activeStep === this) {
×
UNCOV
414
                    this.stepper.activeStepChanged.emit({ owner: this.stepper, index: this.index });
×
415
                }
416
            }
417
        );
UNCOV
418
        this.closeAnimationDone.pipe(takeUntil(this.destroy$)).subscribe(() => {
×
UNCOV
419
            this.stepperService.collapse(this);
×
UNCOV
420
            this.cdr.markForCheck();
×
421
        });
422
    }
423

424
    /** @hidden @internal */
425
    public onPointerDown(event: MouseEvent): void {
426
        event.stopPropagation();
×
427
        if (this.isHorizontal) {
×
428
            this.changeHorizontalActiveStep();
×
429
        } else {
430
            this.changeVerticalActiveStep();
×
431
        }
432
    }
433

434
    /** @hidden @internal */
435
    public handleNavigation(key: string): void {
UNCOV
436
        switch (key) {
×
437
            case this.platform.KEYMAP.HOME:
UNCOV
438
                this.stepper.steps.filter(s => s.isAccessible)[0]?.nativeElement.focus();
×
UNCOV
439
                break;
×
440
            case this.platform.KEYMAP.END:
UNCOV
441
                this.stepper.steps.filter(s => s.isAccessible).pop()?.nativeElement.focus();
×
UNCOV
442
                break;
×
443
            case this.platform.KEYMAP.ARROW_UP:
UNCOV
444
                this.previousStep?.nativeElement.focus();
×
UNCOV
445
                break;
×
446
            case this.platform.KEYMAP.ARROW_LEFT:
UNCOV
447
                if (this.dir.rtl && this.stepper.orientation === IgxStepperOrientation.Horizontal) {
×
448
                    this.nextStep?.nativeElement.focus();
×
449
                } else {
UNCOV
450
                    this.previousStep?.nativeElement.focus();
×
451
                }
UNCOV
452
                break;
×
453
            case this.platform.KEYMAP.ARROW_DOWN:
UNCOV
454
                this.nextStep?.nativeElement.focus();
×
UNCOV
455
                break;
×
456
            case this.platform.KEYMAP.ARROW_RIGHT:
UNCOV
457
                if (this.dir.rtl && this.stepper.orientation === IgxStepperOrientation.Horizontal) {
×
458
                    this.previousStep?.nativeElement.focus();
×
459
                } else {
UNCOV
460
                    this.nextStep?.nativeElement.focus();
×
461
                }
UNCOV
462
                break;
×
463
            case this.platform.KEYMAP.SPACE:
464
            case this.platform.KEYMAP.ENTER:
UNCOV
465
                if (this.isHorizontal) {
×
UNCOV
466
                    this.changeHorizontalActiveStep();
×
467
                } else {
468
                    this.changeVerticalActiveStep();
×
469
                }
UNCOV
470
                break;
×
471
            default:
472
                return;
×
473
        }
474
    }
475

476
    /** @hidden @internal */
477
    public changeHorizontalActiveStep(): void {
UNCOV
478
        if (this.stepper.animationType === HorizontalAnimationType.none && this.stepperService.activeStep !== this) {
×
UNCOV
479
            const argsCanceled = this.stepperService.emitActivatingEvent(this);
×
UNCOV
480
            if (argsCanceled) {
×
481
                return;
×
482
            }
483

UNCOV
484
            this.active = true;
×
UNCOV
485
            this.stepper.activeStepChanged.emit({ owner: this.stepper, index: this.index });
×
UNCOV
486
            return;
×
487
        }
UNCOV
488
        this.stepperService.expand(this);
×
UNCOV
489
        if (this.stepper.animationType === HorizontalAnimationType.fade) {
×
UNCOV
490
            if (this.stepperService.collapsingSteps.has(this.stepperService.previousActiveStep)) {
×
UNCOV
491
                this.stepperService.previousActiveStep.active = false;
×
492
            }
493
        }
494
    }
495

496
    private get nextStep(): IgxStepComponent | null {
UNCOV
497
        const focusedStep = this.stepperService.focusedStep;
×
UNCOV
498
        if (focusedStep) {
×
UNCOV
499
            if (focusedStep.index === this.stepper.steps.length - 1) {
×
500
                return this.stepper.steps.find(s => s.isAccessible);
×
501
            }
502

UNCOV
503
            const nextAccessible = this.stepper.steps.find((s, i) => i > focusedStep.index && s.isAccessible);
×
UNCOV
504
            return nextAccessible ? nextAccessible : this.stepper.steps.find(s => s.isAccessible);
×
505
        }
506

507
        return null;
×
508
    }
509

510
    private get previousStep(): IgxStepComponent | null {
UNCOV
511
        const focusedStep = this.stepperService.focusedStep;
×
UNCOV
512
        if (focusedStep) {
×
UNCOV
513
            if (focusedStep.index === 0) {
×
514
                return this.stepper.steps.filter(s => s.isAccessible).pop();
×
515
            }
516

517
            let prevStep;
UNCOV
518
            for (let i = focusedStep.index - 1; i >= 0; i--) {
×
UNCOV
519
                const step = this.stepper.steps[i];
×
UNCOV
520
                if (step.isAccessible) {
×
UNCOV
521
                    prevStep = step;
×
UNCOV
522
                    break;
×
523
                }
524
            }
525

UNCOV
526
            return prevStep ? prevStep : this.stepper.steps.filter(s => s.isAccessible).pop();
×
527

528
        }
529

530
        return null;
×
531
    }
532

533
    private changeVerticalActiveStep(): void {
534
        this.stepperService.expand(this);
×
535

536
        if (!this.animationSettings.closeAnimation) {
×
537
            this.stepperService.previousActiveStep?.openAnimationPlayer?.finish();
×
538
        }
539

540
        if (!this.animationSettings.openAnimation) {
×
541
            this.stepperService.activeStep.closeAnimationPlayer?.finish();
×
542
        }
543
    }
544
}
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