Skip to content

Commit

Permalink
feat(stepper): require users to visit non-optional steps (#10048)
Browse files Browse the repository at this point in the history
  • Loading branch information
mmalerba authored and jelbourn committed Feb 27, 2018
1 parent 2837196 commit d26aa6e
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 11 deletions.
4 changes: 3 additions & 1 deletion src/cdk/stepper/stepper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,9 @@ export class CdkStepper implements OnDestroy {
if (this._linear && index >= 0) {
return steps.slice(0, index).some(step => {
const control = step.stepControl;
const isIncomplete = control ? (control.invalid || control.pending) : !step.completed;
const isIncomplete = control ?
(control.invalid || control.pending || !step.interacted) :
!step.completed;
return isIncomplete && !step.optional;
});
}
Expand Down
83 changes: 73 additions & 10 deletions src/lib/stepper/stepper.spec.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
import {Directionality, Direction} from '@angular/cdk/bidi';
import {Direction, Directionality} from '@angular/cdk/bidi';
import {
DOWN_ARROW,
END,
ENTER,
HOME,
LEFT_ARROW,
RIGHT_ARROW,
UP_ARROW,
DOWN_ARROW,
SPACE,
HOME,
END,
UP_ARROW,
} from '@angular/cdk/keycodes';
import {StepperOrientation} from '@angular/cdk/stepper';
import {dispatchKeyboardEvent} from '@angular/cdk/testing';
import {Component, DebugElement} from '@angular/core';
import {async, ComponentFixture, TestBed, inject} from '@angular/core/testing';
import {AbstractControl, AsyncValidatorFn, FormControl, FormGroup, ReactiveFormsModule,
ValidationErrors, Validators} from '@angular/forms';
import {async, ComponentFixture, inject, TestBed} from '@angular/core/testing';
import {
AbstractControl,
AsyncValidatorFn,
FormControl,
FormGroup,
ReactiveFormsModule,
ValidationErrors,
Validators
} from '@angular/forms';
import {By} from '@angular/platform-browser';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {StepperOrientation} from '@angular/cdk/stepper';
import {Observable} from 'rxjs/Observable';
import {map} from 'rxjs/operators/map';
import {take} from 'rxjs/operators/take';
import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject';
import {MatStepperModule} from './index';
import {MatHorizontalStepper, MatStep, MatStepper, MatVerticalStepper} from './stepper';
Expand All @@ -44,6 +51,7 @@ describe('MatStepper', () => {
SimpleStepperWithoutStepControl,
SimpleStepperWithStepControlAndCompletedBinding,
SimpleMatHorizontalStepperApp,
LinearStepperWithValidOptionalStep,
],
providers: [
{provide: Directionality, useFactory: () => ({value: dir})}
Expand Down Expand Up @@ -491,6 +499,8 @@ describe('MatStepper', () => {
testComponent.oneGroup.get('oneCtrl')!.setValue('input');
testComponent.twoGroup.get('twoCtrl')!.setValue('input');
testComponent.validationTrigger.next();
stepperComponent.selectedIndex = 1;
fixture.detectChanges();
stepperComponent.selectedIndex = 2;
fixture.detectChanges();

Expand Down Expand Up @@ -559,6 +569,8 @@ describe('MatStepper', () => {
testComponent.twoGroup.get('twoCtrl')!.setValue('input');
testComponent.threeGroup.get('threeCtrl')!.setValue('valid');
testComponent.validationTrigger.next();
stepperComponent.selectedIndex = 1;
fixture.detectChanges();
stepperComponent.selectedIndex = 2;
fixture.detectChanges();
stepperComponent.selectedIndex = 3;
Expand Down Expand Up @@ -695,6 +707,43 @@ describe('MatStepper', () => {
assertArrowKeyInteractionInRtl(fixture, stepHeaders);
});
});

describe('valid step in linear stepper', () => {
let fixture: ComponentFixture<LinearStepperWithValidOptionalStep>;
let testComponent: LinearStepperWithValidOptionalStep;
let stepper: MatStepper;

beforeEach(() => {
fixture = TestBed.createComponent(LinearStepperWithValidOptionalStep);
fixture.detectChanges();

testComponent = fixture.componentInstance;
stepper = fixture.debugElement
.query(By.css('mat-horizontal-stepper')).componentInstance;
});

it('must be visited if not optional', () => {
stepper.selectedIndex = 2;
fixture.detectChanges();
expect(stepper.selectedIndex).toBe(0);

stepper.selectedIndex = 1;
fixture.detectChanges();
expect(stepper.selectedIndex).toBe(1);

stepper.selectedIndex = 2;
fixture.detectChanges();
expect(stepper.selectedIndex).toBe(2);
});

it('can be skipped entirely if optional', () => {
testComponent.step2Optional = true;
fixture.detectChanges();
stepper.selectedIndex = 2;
fixture.detectChanges();
expect(stepper.selectedIndex).toBe(2);
});
});
});

/** Asserts that keyboard interaction works correctly. */
Expand Down Expand Up @@ -986,3 +1035,17 @@ class SimpleStepperWithStepControlAndCompletedBinding {
`
})
class IconOverridesStepper {}

@Component({
template: `
<mat-horizontal-stepper linear>
<mat-step label="Step 1" [stepControl]="controls[0]"></mat-step>
<mat-step label="Step 2" [stepControl]="controls[1]" [optional]="step2Optional"></mat-step>
<mat-step label="Step 3" [stepControl]="controls[2]"></mat-step>
</mat-horizontal-stepper>
`
})
class LinearStepperWithValidOptionalStep {
controls = [0, 0, 0].map(() => new FormControl());
step2Optional = false;
}

0 comments on commit d26aa6e

Please sign in to comment.