Skip to content

Commit 5497be5

Browse files
authored
fix(cdk/stepper): show error state if explicitly enabled (#22540)
Currently we have a feature where a step can be forced into an error state using the `hasError` input. The problem is that doing so won't actually change anything by default, because the `showError` option also has to be provided through an injection token. These changes add some logic so that the error will be shown automatically, if it was set explicitly through `hasError`. Fixes #22539.
1 parent f88023c commit 5497be5

File tree

4 files changed

+29
-9
lines changed

4 files changed

+29
-9
lines changed

src/cdk/stepper/stepper.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ export interface StepperOptions {
112112
})
113113
export class CdkStep implements OnChanges {
114114
private _stepperOptions: StepperOptions;
115-
_showError: boolean;
116115
_displayDefaultIndicatorType: boolean;
117116

118117
/** Template for step label if it exists. */
@@ -202,7 +201,6 @@ export class CdkStep implements OnChanges {
202201
@Optional() @Inject(STEPPER_GLOBAL_OPTIONS) stepperOptions?: StepperOptions) {
203202
this._stepperOptions = stepperOptions ? stepperOptions : {};
204203
this._displayDefaultIndicatorType = this._stepperOptions.displayDefaultIndicatorType !== false;
205-
this._showError = !!this._stepperOptions.showError;
206204
}
207205

208206
/** Selects this step component. */
@@ -240,6 +238,13 @@ export class CdkStep implements OnChanges {
240238
}
241239
}
242240

241+
/** Determines whether the error state can be shown. */
242+
_showError(): boolean {
243+
// We want to show the error state either if the user opted into/out of it using the
244+
// global options, or if they've explicitly set it through the `hasError` input.
245+
return this._stepperOptions.showError ?? this._customError != null;
246+
}
247+
243248
static ngAcceptInputType_editable: BooleanInput;
244249
static ngAcceptInputType_hasError: BooleanInput;
245250
static ngAcceptInputType_optional: BooleanInput;
@@ -442,7 +447,7 @@ export class CdkStepper implements AfterContentInit, AfterViewInit, OnDestroy {
442447
}
443448

444449
private _getDefaultIndicatorLogic(step: CdkStep, isCurrentStep: boolean): StepState {
445-
if (step._showError && step.hasError && !isCurrentStep) {
450+
if (step._showError() && step.hasError && !isCurrentStep) {
446451
return STEP_STATE.ERROR;
447452
} else if (!step.completed || isCurrentStep) {
448453
return STEP_STATE.NUMBER;
@@ -453,7 +458,7 @@ export class CdkStepper implements AfterContentInit, AfterViewInit, OnDestroy {
453458

454459
private _getGuidelineLogic(
455460
step: CdkStep, isCurrentStep: boolean, state: StepState = STEP_STATE.NUMBER): StepState {
456-
if (step._showError && step.hasError && !isCurrentStep) {
461+
if (step._showError() && step.hasError && !isCurrentStep) {
457462
return STEP_STATE.ERROR;
458463
} else if (step.completed && !isCurrentStep) {
459464
return STEP_STATE.DONE;

src/material/stepper/public-api.ts

Lines changed: 1 addition & 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-
export {StepperOrientation} from '@angular/cdk/stepper';
9+
export {StepperOrientation, StepState} from '@angular/cdk/stepper';
1010
export * from './stepper-module';
1111
export * from './step-label';
1212
export * from './stepper';

src/material/stepper/stepper.spec.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,21 +1168,22 @@ describe('MatStepper', () => {
11681168
let fixture: ComponentFixture<MatHorizontalStepperWithErrorsApp>;
11691169
let stepper: MatStepper;
11701170

1171-
beforeEach(() => {
1171+
function createFixture(showErrorByDefault: boolean|undefined) {
11721172
fixture = createComponent(
11731173
MatHorizontalStepperWithErrorsApp,
11741174
[{
11751175
provide: STEPPER_GLOBAL_OPTIONS,
1176-
useValue: {showError: true}
1176+
useValue: {showError: showErrorByDefault}
11771177
}],
11781178
[MatFormFieldModule, MatInputModule]
11791179
);
11801180
fixture.detectChanges();
11811181
stepper = fixture.debugElement
11821182
.query(By.css('mat-horizontal-stepper'))!.componentInstance;
1183-
});
1183+
}
11841184

11851185
it('should show error state', () => {
1186+
createFixture(true);
11861187
const nextButtonNativeEl = fixture.debugElement
11871188
.queryAll(By.directive(MatStepperNext))[0].nativeElement;
11881189

@@ -1195,6 +1196,7 @@ describe('MatStepper', () => {
11951196
});
11961197

11971198
it('should respect a custom falsy hasError value', () => {
1199+
createFixture(true);
11981200
const nextButtonNativeEl = fixture.debugElement
11991201
.queryAll(By.directive(MatStepperNext))[0].nativeElement;
12001202

@@ -1210,6 +1212,19 @@ describe('MatStepper', () => {
12101212
expect(stepper._getIndicatorType(0)).not.toBe(STEP_STATE.ERROR);
12111213
});
12121214

1215+
it('should show error state if explicitly enabled, even when disabled globally', () => {
1216+
createFixture(undefined);
1217+
const nextButtonNativeEl = fixture.debugElement
1218+
.queryAll(By.directive(MatStepperNext))[0].nativeElement;
1219+
1220+
stepper.selectedIndex = 1;
1221+
stepper.steps.first.hasError = true;
1222+
nextButtonNativeEl.click();
1223+
fixture.detectChanges();
1224+
1225+
expect(stepper._getIndicatorType(0)).toBe(STEP_STATE.ERROR);
1226+
});
1227+
12131228
});
12141229

12151230
describe('stepper using Material UI Guideline logic', () => {

tools/public_api_guard/cdk/stepper.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
export declare class CdkStep implements OnChanges {
22
_completedOverride: boolean | null;
33
_displayDefaultIndicatorType: boolean;
4-
_showError: boolean;
54
_stepper: CdkStepper;
65
ariaLabel: string;
76
ariaLabelledby: string;
@@ -23,6 +22,7 @@ export declare class CdkStep implements OnChanges {
2322
stepLabel: CdkStepLabel;
2423
constructor(_stepper: CdkStepper, stepperOptions?: StepperOptions);
2524
_markAsInteracted(): void;
25+
_showError(): boolean;
2626
ngOnChanges(): void;
2727
reset(): void;
2828
select(): void;

0 commit comments

Comments
 (0)