Skip to content

Commit

Permalink
feat(stepper): Address previous comments + add directionality support (
Browse files Browse the repository at this point in the history
…#6775)

* code cleanup + rtl support

* Fix based on review

* modify code for unit tests
  • Loading branch information
jwshinjwshin authored and tinayuangao committed Sep 6, 2017
1 parent 835014a commit c396596
Show file tree
Hide file tree
Showing 11 changed files with 238 additions and 131 deletions.
43 changes: 26 additions & 17 deletions src/cdk/stepper/stepper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {LEFT_ARROW, RIGHT_ARROW, ENTER, SPACE} from '@angular/cdk/keycodes';
import {CdkStepLabel} from './step-label';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {AbstractControl} from '@angular/forms';
import {Directionality} from '@angular/cdk/bidi';
import {Direction, Directionality} from '@angular/cdk/bidi';

/** Used to generate unique ID for each stepper component. */
let nextId = 0;
Expand Down Expand Up @@ -151,8 +151,7 @@ export class CdkStepper {
@Input()
get selected() { return this._steps[this.selectedIndex]; }
set selected(step: CdkStep) {
let index = this._steps.toArray().indexOf(step);
this.selectedIndex = index;
this.selectedIndex = this._steps.toArray().indexOf(step);
}

/** Event emitted when the selected step has changed. */
Expand Down Expand Up @@ -192,12 +191,11 @@ export class CdkStepper {
_getAnimationDirection(index: number): StepContentPositionState {
const position = index - this._selectedIndex;
if (position < 0) {
return 'previous';
return this._layoutDirection() === 'rtl' ? 'next' : 'previous';
} else if (position > 0) {
return 'next';
} else {
return 'current';
return this._layoutDirection() === 'rtl' ? 'previous' : 'next';
}
return 'current';
}

/** Returns the type of icon to be displayed. */
Expand All @@ -224,17 +222,17 @@ export class CdkStepper {
_onKeydown(event: KeyboardEvent) {
switch (event.keyCode) {
case RIGHT_ARROW:
if (this._dir && this._dir.value === 'rtl') {
this._focusStep((this._focusIndex + this._steps.length - 1) % this._steps.length);
if (this._layoutDirection() === 'rtl') {
this._focusPreviousStep();
} else {
this._focusStep((this._focusIndex + 1) % this._steps.length);
this._focusNextStep();
}
break;
case LEFT_ARROW:
if (this._dir && this._dir.value === 'rtl') {
this._focusStep((this._focusIndex + 1) % this._steps.length);
if (this._layoutDirection() === 'rtl') {
this._focusNextStep();
} else {
this._focusStep((this._focusIndex + this._steps.length - 1) % this._steps.length);
this._focusPreviousStep();
}
break;
case SPACE:
Expand All @@ -248,17 +246,28 @@ export class CdkStepper {
event.preventDefault();
}

private _focusNextStep() {
this._focusStep((this._focusIndex + 1) % this._steps.length);
}

private _focusPreviousStep() {
this._focusStep((this._focusIndex + this._steps.length - 1) % this._steps.length);
}

private _focusStep(index: number) {
this._focusIndex = index;
this._stepHeader.toArray()[this._focusIndex].nativeElement.focus();
}

private _anyControlsInvalid(index: number): boolean {
const stepsArray = this._steps.toArray();
stepsArray[this._selectedIndex].interacted = true;
if (this._linear) {
return stepsArray.slice(0, index).some(step => step.stepControl.invalid);
this._steps.toArray()[this._selectedIndex].interacted = true;
if (this._linear && index >= 0) {
return this._steps.toArray().slice(0, index).some(step => step.stepControl.invalid);
}
return false;
}

private _layoutDirection(): Direction {
return this._dir && this._dir.value === 'rtl' ? 'rtl' : 'ltr';
}
}
6 changes: 2 additions & 4 deletions src/demo-app/stepper/stepper-demo.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import {Component} from '@angular/core';
import {AbstractControl, FormBuilder, FormGroup, Validators} from '@angular/forms';

const EMAIL_REGEX = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;

@Component({
moduleId: module.id,
selector: 'stepper-demo',
Expand Down Expand Up @@ -36,7 +34,7 @@ export class StepperDemo {
lastNameFormCtrl: ['', Validators.required],
}),
this._formBuilder.group({
emailFormCtrl: ['', Validators.pattern(EMAIL_REGEX)]
emailFormCtrl: ['', Validators.email]
}),
])
});
Expand All @@ -47,7 +45,7 @@ export class StepperDemo {
});

this.emailFormGroup = this._formBuilder.group({
emailCtrl: ['', Validators.pattern(EMAIL_REGEX)]
emailCtrl: ['', Validators.email]
});
}
}
10 changes: 5 additions & 5 deletions src/lib/stepper/_stepper-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@
background-color: mat-color($background, hover);
}

.mat-step-label-active {
color: mat-color($foreground, text);
}

.mat-step-label-inactive,
.mat-step-label,
.mat-step-optional {
color: mat-color($foreground, disabled-text);
}
Expand All @@ -31,6 +27,10 @@
background-color: mat-color($foreground, disabled-text);
color: mat-color($primary, default-contrast);
}

.mat-step-label.mat-step-label-active {
color: mat-color($foreground, text);
}
}

.mat-stepper-horizontal, .mat-stepper-vertical {
Expand Down
13 changes: 11 additions & 2 deletions src/lib/stepper/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,17 @@ import {MdStepHeader} from './step-header';
CdkStepperModule,
MdIconModule
],
exports: [MdCommonModule, MdHorizontalStepper, MdVerticalStepper, MdStep, MdStepLabel, MdStepper,
MdStepperNext, MdStepperPrevious, MdStepHeader],
exports: [
MdCommonModule,
MdHorizontalStepper,
MdVerticalStepper,
MdStep,
MdStepLabel,
MdStepper,
MdStepperNext,
MdStepperPrevious,
MdStepHeader
],
declarations: [MdHorizontalStepper, MdVerticalStepper, MdStep, MdStepLabel, MdStepper,
MdStepperNext, MdStepperPrevious, MdStepHeader],
})
Expand Down
19 changes: 10 additions & 9 deletions src/lib/stepper/step-header.html
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
<div [class.mat-step-icon]="icon != 'number' || selected"
[class.mat-step-icon-not-touched]="icon == 'number' && !selected">
<span *ngIf="icon == 'number'">{{index + 1}}</span>
<md-icon *ngIf="icon == 'edit'">create</md-icon>
<md-icon *ngIf="icon == 'done'">done</md-icon>
<div [class.mat-step-icon]="icon !== 'number' || selected"
[class.mat-step-icon-not-touched]="icon == 'number' && !selected"
[ngSwitch]="icon">
<span *ngSwitchCase="'number'">{{index + 1}}</span>
<md-icon *ngSwitchCase="'edit'">create</md-icon>
<md-icon *ngSwitchCase="'done'">done</md-icon>
</div>
<div [class.mat-step-label-active]="active"
[class.mat-step-label-inactive]="!active">
<div class="mat-step-label"
[class.mat-step-label-active]="active">
<!-- If there is a label template, use it. -->
<ng-container *ngIf="_templateLabel" [ngTemplateOutlet]="label.template">
<ng-container *ngIf="_templateLabel()" [ngTemplateOutlet]="label.template">
</ng-container>
<!-- It there is no label template, fall back to the text label. -->
<div class="mat-step-text-label" *ngIf="_stringLabel">{{label}}</div>
<div class="mat-step-text-label" *ngIf="_stringLabel()">{{label}}</div>

<div class="mat-step-optional" *ngIf="optional">Optional</div>
</div>
Expand Down
3 changes: 1 addition & 2 deletions src/lib/stepper/step-header.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ $mat-step-header-icon-size: 16px !default;
width: $mat-step-header-icon-size;
}

.mat-step-label-active,
.mat-step-label-inactive {
.mat-step-label {
display: inline-block;
white-space: nowrap;
overflow: hidden;
Expand Down
10 changes: 4 additions & 6 deletions src/lib/stepper/step-header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,10 @@ import {MdStepLabel} from './step-label';
})
export class MdStepHeader {
/** Icon for the given step. */
@Input()
icon: string;
@Input() icon: string;

/** Label of the given step. */
@Input()
label: MdStepLabel | string;
@Input() label: MdStepLabel | string;

/** Index of the given step. */
@Input()
Expand Down Expand Up @@ -63,12 +61,12 @@ export class MdStepHeader {
private _optional: boolean;

/** Returns string label of given step if it is a text label. */
get _stringLabel(): string | null {
_stringLabel(): string | null {
return this.label instanceof MdStepLabel ? null : this.label;
}

/** Returns MdStepLabel if the label of given step is a template label. */
get _templateLabel(): MdStepLabel | null {
_templateLabel(): MdStepLabel | null {
return this.label instanceof MdStepLabel ? this.label : null;
}
}
8 changes: 4 additions & 4 deletions src/lib/stepper/stepper-horizontal.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
<md-step-header class="mat-horizontal-stepper-header"
(click)="step.select()"
(keydown)="_onKeydown($event)"
[tabIndex]="_focusIndex == i ? 0 : -1"
[tabIndex]="_focusIndex === i ? 0 : -1"
[id]="_getStepLabelId(i)"
[attr.aria-controls]="_getStepContentId(i)"
[attr.aria-selected]="selectedIndex == i"
[index]="i"
[icon]="_getIndicatorType(i)"
[label]="step.stepLabel || step.label"
[selected]="selectedIndex == i"
[active]="step.completed || selectedIndex == i"
[selected]="selectedIndex === i"
[active]="step.completed || selectedIndex === i"
[optional]="step.optional">
</md-step-header>
<div *ngIf="!isLast" class="mat-stepper-horizontal-line"></div>
Expand All @@ -24,7 +24,7 @@
[@stepTransition]="_getAnimationDirection(i)"
[id]="_getStepContentId(i)"
[attr.aria-labelledby]="_getStepLabelId(i)"
[attr.aria-expanded]="selectedIndex == i">
[attr.aria-expanded]="selectedIndex === i">
<ng-container [ngTemplateOutlet]="step.content"></ng-container>
</div>
</div>
8 changes: 4 additions & 4 deletions src/lib/stepper/stepper-vertical.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
[tabIndex]="_focusIndex == i ? 0 : -1"
[id]="_getStepLabelId(i)"
[attr.aria-controls]="_getStepContentId(i)"
[attr.aria-selected]="selectedIndex == i"
[attr.aria-selected]="selectedIndex === i"
[index]="i"
[icon]="_getIndicatorType(i)"
[label]="step.stepLabel || step.label"
[selected]="selectedIndex == i"
[active]="step.completed || selectedIndex == i"
[selected]="selectedIndex === i"
[active]="step.completed || selectedIndex === i"
[optional]="step.optional">
</md-step-header>

Expand All @@ -19,7 +19,7 @@
[@stepTransition]="_getAnimationDirection(i)"
[id]="_getStepContentId(i)"
[attr.aria-labelledby]="_getStepLabelId(i)"
[attr.aria-expanded]="selectedIndex == i">
[attr.aria-expanded]="selectedIndex === i">
<div class="mat-vertical-content">
<ng-container [ngTemplateOutlet]="step.content"></ng-container>
</div>
Expand Down
Loading

0 comments on commit c396596

Please sign in to comment.