diff --git a/src/cdk/stepper/stepper.ts b/src/cdk/stepper/stepper.ts index 3d9a3f429055..05f27b1e2786 100644 --- a/src/cdk/stepper/stepper.ts +++ b/src/cdk/stepper/stepper.ts @@ -304,7 +304,8 @@ export class CdkStepper implements OnDestroy { if (this._linear && index >= 0) { return steps.slice(0, index).some(step => { const control = step.stepControl; - return control ? (control.invalid || control.pending) : !step.completed; + const isIncomplete = control ? (control.invalid || control.pending) : !step.completed; + return isIncomplete && !step.optional; }); } diff --git a/src/lib/stepper/stepper.spec.ts b/src/lib/stepper/stepper.spec.ts index 5dd85b3bc260..2fe3f779eafd 100644 --- a/src/lib/stepper/stepper.spec.ts +++ b/src/lib/stepper/stepper.spec.ts @@ -780,13 +780,14 @@ function assertEditableStepChange(fixture: ComponentFixture) { } /** - * Asserts that it is possible to skip an optional step in linear stepper if there is no input - * or the input is valid. + * Asserts that it is possible to skip an optional step in linear + * stepper if there is no input or the input is invalid. */ function assertOptionalStepValidity(testComponent: LinearMatHorizontalStepperApp | LinearMatVerticalStepperApp, fixture: ComponentFixture) { - let stepperComponent = fixture.debugElement.query(By.directive(MatStepper)).componentInstance; + const stepperComponent: MatStepper = fixture.debugElement + .query(By.directive(MatStepper)).componentInstance; testComponent.oneGroup.get('oneCtrl')!.setValue('input'); testComponent.twoGroup.get('twoCtrl')!.setValue('input'); @@ -794,10 +795,11 @@ function assertOptionalStepValidity(testComponent: stepperComponent.selectedIndex = 2; fixture.detectChanges(); + expect(stepperComponent._steps.toArray()[2].optional).toBe(true); expect(stepperComponent.selectedIndex).toBe(2); expect(testComponent.threeGroup.get('threeCtrl')!.valid).toBe(true); - let nextButtonNativeEl = fixture.debugElement + const nextButtonNativeEl = fixture.debugElement .queryAll(By.directive(MatStepperNext))[2].nativeElement; nextButtonNativeEl.click(); fixture.detectChanges(); @@ -812,15 +814,7 @@ function assertOptionalStepValidity(testComponent: expect(testComponent.threeGroup.get('threeCtrl')!.valid).toBe(false); expect(stepperComponent.selectedIndex) - .toBe(2, 'Expected selectedIndex to remain unchanged when optional step input is invalid.'); - - testComponent.threeGroup.get('threeCtrl')!.setValue('valid'); - nextButtonNativeEl.click(); - fixture.detectChanges(); - - expect(testComponent.threeGroup.get('threeCtrl')!.valid).toBe(true); - expect(stepperComponent.selectedIndex) - .toBe(3, 'Expected selectedIndex to change when optional step input is valid.'); + .toBe(3, 'Expected selectedIndex to change when optional step input is invalid.'); } /** Asserts that step header set the correct icon depending on the state of step. */ @@ -841,10 +835,7 @@ function assertCorrectStepIcon(fixture: ComponentFixture, function asyncValidator(minLength: number, validationTrigger: Observable): AsyncValidatorFn { return (control: AbstractControl): Observable => { return validationTrigger.pipe( - map(() => { - const success = control.value && control.value.length >= minLength; - return success ? null : { 'asyncValidation': {}}; - }), + map(() => control.value && control.value.length >= minLength ? null : {asyncValidation: {}}), take(1) ); };