- 
                Notifications
    You must be signed in to change notification settings 
- Fork 6.8k
feat(stepper): Support additional properties for step #6509
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
af7d725
              1bd2486
              81eee36
              edd0a03
              b214f70
              a1f1977
              ec3993a
              c8a71f4
              1a5af09
              File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -77,6 +77,26 @@ export class CdkStep { | |
| @Input() | ||
| label: string; | ||
|  | ||
| @Input() | ||
| get editable() { return this._editable; } | ||
| set editable(value: any) { | ||
| this._editable = coerceBooleanProperty(value); | ||
| } | ||
| private _editable = true; | ||
|  | ||
| /** Whether the completion of step is optional or not. */ | ||
| @Input() | ||
| get optional() { return this._optional; } | ||
| set optional(value: any) { | ||
| this._optional = coerceBooleanProperty(value); | ||
| } | ||
| private _optional = false; | ||
|  | ||
| /** Return whether step is completed or not. */ | ||
| get completed() { | ||
| return this._stepControl ? this._stepControl.valid && this.interacted : this.interacted; | ||
| } | ||
|  | ||
| constructor(private _stepper: CdkStepper) { } | ||
|  | ||
| /** Selects this step component. */ | ||
|  | @@ -109,6 +129,7 @@ export class CdkStepper { | |
| @Input() | ||
| get selectedIndex() { return this._selectedIndex; } | ||
| set selectedIndex(index: number) { | ||
| if (index < this._selectedIndex && !this._steps.toArray()[index].editable) { return; } | ||
| if (this._anyControlsInvalid(index)) { | ||
| // remove focus from clicked step header if the step is not able to be selected | ||
| this._stepHeader.toArray()[index].nativeElement.blur(); | ||
|  | @@ -211,7 +232,7 @@ export class CdkStepper { | |
| const stepsArray = this._steps.toArray(); | ||
| stepsArray[this._selectedIndex].interacted = true; | ||
| if (this._linear) { | ||
| return stepsArray.slice(0, index).some(step => step.stepControl.invalid); | ||
| return stepsArray.slice(0, index).some(step => step.stepControl.invalid && !step.optional); | ||
|          | ||
| } | ||
| return false; | ||
| } | ||
|  | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -19,12 +19,12 @@ <h3>Linear Vertical Stepper Demo using a single form</h3> | |
| </div> | ||
| </md-step> | ||
|  | ||
| <md-step formGroupName="1" [stepControl]="formArray.get([1])"> | ||
| <md-step formGroupName="1" [stepControl]="formArray.get([1])" [optional]="true"> | ||
|          | ||
| <ng-template mdStepLabel> | ||
| <div>Fill out your phone number</div> | ||
| </ng-template> | ||
| <md-input-container> | ||
| <input mdInput placeholder="Phone number" formControlName="phoneFormCtrl"> | ||
| <input mdInput placeholder="Phone number" formControlName="phoneFormCtrl" required> | ||
| <md-error>This field is required</md-error> | ||
| </md-input-container> | ||
| <div> | ||
|  | @@ -88,44 +88,41 @@ <h3>Linear Horizontal Stepper Demo using a different form for each step</h3> | |
| </md-horizontal-stepper> | ||
|  | ||
| <h3>Vertical Stepper Demo</h3> | ||
| <md-checkbox [(ngModel)]="isNonEditable">Make steps non-editable</md-checkbox> | ||
| <md-vertical-stepper> | ||
| <md-step> | ||
| <md-step [editable]="!isNonEditable"> | ||
| <ng-template mdStepLabel>Fill out your name</ng-template> | ||
| <md-form-field> | ||
| <input mdInput placeholder="First Name"> | ||
| <md-error>This field is required</md-error> | ||
| </md-form-field> | ||
|  | ||
| <md-form-field> | ||
| <input mdInput placeholder="Last Name"> | ||
| <md-error>This field is required</md-error> | ||
| </md-form-field> | ||
| <div> | ||
| <button md-button mdStepperNext type="button">Next</button> | ||
| </div> | ||
| </md-step> | ||
|  | ||
| <md-step> | ||
| <md-step [editable]="!isNonEditable"> | ||
| <ng-template mdStepLabel> | ||
| <div>Fill out your phone number</div> | ||
| </ng-template> | ||
| <md-form-field> | ||
| <input mdInput placeholder="Phone number"> | ||
| <md-error>This field is required</md-error> | ||
| </md-form-field> | ||
| <div> | ||
| <button md-button mdStepperPrevious type="button">Back</button> | ||
| <button md-button mdStepperNext type="button">Next</button> | ||
| </div> | ||
| </md-step> | ||
|  | ||
| <md-step> | ||
| <md-step [editable]="!isNonEditable"> | ||
| <ng-template mdStepLabel> | ||
| <div>Fill out your address</div> | ||
| </ng-template> | ||
| <md-form-field> | ||
| <input mdInput placeholder="Address"> | ||
| <md-error>This field is required</md-error> | ||
| </md-form-field> | ||
| <div> | ||
| <button md-button mdStepperPrevious type="button">Back</button> | ||
|  | @@ -148,12 +145,10 @@ <h3>Horizontal Stepper Demo</h3> | |
| <ng-template mdStepLabel>Fill out your name</ng-template> | ||
| <md-form-field> | ||
| <input mdInput placeholder="First Name"> | ||
| <md-error>This field is required</md-error> | ||
| </md-form-field> | ||
|  | ||
| <md-form-field> | ||
| <input mdInput placeholder="Last Name"> | ||
| <md-error>This field is required</md-error> | ||
| </md-form-field> | ||
| <div> | ||
| <button md-button mdStepperNext type="button">Next</button> | ||
|  | @@ -166,7 +161,6 @@ <h3>Horizontal Stepper Demo</h3> | |
| </ng-template> | ||
| <md-form-field> | ||
| <input mdInput placeholder="Phone number"> | ||
| <md-error>This field is required</md-error> | ||
| </md-form-field> | ||
| <div> | ||
| <button md-button mdStepperPrevious type="button">Back</button> | ||
|  | @@ -180,7 +174,6 @@ <h3>Horizontal Stepper Demo</h3> | |
| </ng-template> | ||
| <md-form-field> | ||
| <input mdInput placeholder="Address"> | ||
| <md-error>This field is required</md-error> | ||
| </md-form-field> | ||
| <div> | ||
| <button md-button mdStepperPrevious type="button">Back</button> | ||
|  | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -8,16 +8,25 @@ | |
| [tabIndex]="_focusIndex == i ? 0 : -1" | ||
| (click)="step.select()" | ||
| (keydown)="_onKeydown($event)"> | ||
| <div class="mat-stepper-index"> | ||
| <div class="mat-stepper-index-new" *ngIf="!step.completed || selectedIndex == i"> | ||
| {{i + 1}} | ||
| </div> | ||
| <div class="mat-stepper-index-interacted" *ngIf="step.completed && selectedIndex != i"> | ||
|          | ||
| <md-icon *ngIf="!step.editable">done</md-icon> | ||
| <md-icon *ngIf="step.editable">create</md-icon> | ||
| </div> | ||
|  | ||
| <div class="mat-stepper-label"> | ||
| <div [ngClass]="{ | ||
|          | ||
| 'mat-stepper-label-active': step.completed || selectedIndex == i, | ||
| 'mat-stepper-label-inactive': !step.completed && selectedIndex != i | ||
| }"> | ||
| <!-- If there is a label template, use it. --> | ||
| <ng-container *ngIf="step.stepLabel" [ngTemplateOutlet]="step.stepLabel.template"> | ||
| </ng-container> | ||
| <!-- It there is no label template, fall back to the text label. --> | ||
| <div *ngIf="!step.stepLabel">{{step.label}}</div> | ||
|  | ||
| <div class="mat-step-optional" *ngIf="step.optional">Optional</div> | ||
| </div> | ||
| </div> | ||
|  | ||
|  | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -6,19 +6,28 @@ | |
| [tabIndex]="_focusIndex == i ? 0 : -1" | ||
| (click)="step.select()" | ||
| (keydown)="_onKeydown($event)"> | ||
| <div class="mat-stepper-index"> | ||
| <div class="mat-stepper-index-new" *ngIf="!step.completed || selectedIndex == i"> | ||
| {{i + 1}} | ||
| </div> | ||
| <div class="mat-stepper-index-interacted" *ngIf="step.completed && selectedIndex != i"> | ||
|          | ||
| <md-icon *ngIf="!step.editable">done</md-icon> | ||
| <md-icon *ngIf="step.editable">create</md-icon> | ||
| </div> | ||
|  | ||
| <div class="mat-stepper-label"> | ||
| <div [ngClass]="{ | ||
| 'mat-stepper-label-active': step.completed || selectedIndex == i, | ||
| 'mat-stepper-label-inactive': !step.completed && selectedIndex != i | ||
| }"> | ||
| <!-- If there is a label template, use it. --> | ||
| <ng-container *ngIf="step.stepLabel"[ngTemplateOutlet]="step.stepLabel.template"> | ||
| </ng-container> | ||
| <!-- It there is no label template, fall back to the text label. --> | ||
| <div *ngIf="!step.stepLabel">{{step.label}}</div> | ||
| </div> | ||
|  | ||
| <div class="mat-step-optional" *ngIf="step.optional">Optional</div> | ||
| </div> | ||
| </div> | ||
|  | ||
| <div class="mat-vertical-content-container" [class.mat-stepper-vertical-line]="!isLast"> | ||
| <div class="mat-vertical-stepper-content" role="tabpanel" | ||
| [@stepTransition]="_getAnimationDirection(i)" | ||
|  | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm I wonder if this is something we want to give the user control of by making it an
@Input()?@jelbourn WDYT of something like this:
This way most users could use the default completeness behavior, but they could still override if they want
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems reasonable, though
_defaultCompletedwould be a function