Skip to content

Commit 8ddc6db

Browse files
committed
Refactor code for shared functionality between vertical and horizontal stepper
1 parent edd0a03 commit 8ddc6db

15 files changed

+239
-86
lines changed

src/cdk/stepper/public_api.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,20 @@ import {CdkStepper, CdkStep} from './stepper';
1111
import {CommonModule} from '@angular/common';
1212
import {CdkStepLabel} from './step-label';
1313
import {CdkStepperNext, CdkStepperPrevious} from './stepper-button';
14+
import {CdkStepIcon} from './step-icon';
15+
import {CdkStepLabelContainer} from './step-label-container';
1416

1517
@NgModule({
1618
imports: [CommonModule],
17-
exports: [CdkStep, CdkStepper, CdkStepLabel, CdkStepperNext, CdkStepperPrevious],
18-
declarations: [CdkStep, CdkStepper, CdkStepLabel, CdkStepperNext, CdkStepperPrevious]
19+
exports: [CdkStep, CdkStepper, CdkStepLabel, CdkStepperNext, CdkStepperPrevious, CdkStepIcon,
20+
CdkStepLabelContainer],
21+
declarations: [CdkStep, CdkStepper, CdkStepLabel, CdkStepperNext, CdkStepperPrevious, CdkStepIcon,
22+
CdkStepLabelContainer]
1923
})
2024
export class CdkStepperModule {}
2125

2226
export * from './stepper';
2327
export * from './step-label';
2428
export * from './stepper-button';
29+
export * from './step-icon';
30+
export * from './step-label-container';

src/cdk/stepper/step-icon.ts

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {Directive, Input} from '@angular/core';
10+
import {CdkStep} from './stepper';
11+
12+
@Directive({
13+
selector: 'cdkStepIcon'
14+
})
15+
export class CdkStepIcon {
16+
/** Step of the icon to be displayed. */
17+
@Input()
18+
step: CdkStep;
19+
20+
/** Whether the step of the icon to be displayed is active. */
21+
@Input()
22+
selected: boolean;
23+
24+
/** Index of the step. */
25+
@Input()
26+
index: number;
27+
28+
/** Whether the user has touched the step that is not selected. */
29+
get notTouched() {
30+
return this._getIndicatorType() == 'number' && !this.selected;
31+
}
32+
33+
/** Returns the type of icon to be displayed. */
34+
_getIndicatorType(): 'number' | 'edit' | 'done' {
35+
if (!this.step.completed || this.selected) {
36+
return 'number';
37+
} else {
38+
return this.step.editable ? 'edit' : 'done';
39+
}
40+
}
41+
}
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {Directive, Input} from '@angular/core';
10+
import {CdkStep} from './stepper';
11+
12+
@Directive({
13+
selector: 'cdkStepLabelContainer'
14+
})
15+
export class CdkStepLabelContainer {
16+
/** Step of the label to be displayed. */
17+
@Input()
18+
step: CdkStep;
19+
20+
/** Whether the step of label to be displayed is selected. */
21+
@Input()
22+
selected: boolean;
23+
24+
/** Whether the label to be displayed is active. */
25+
get active() {
26+
return this.step.completed || this.selected;
27+
}
28+
}

src/cdk/stepper/stepper.ts

-9
Original file line numberDiff line numberDiff line change
@@ -202,15 +202,6 @@ export class CdkStepper {
202202
}
203203
}
204204

205-
_getIndicatorType(index: number): 'number' | 'edit' | 'done' {
206-
const step = this._steps.toArray()[index];
207-
if (!step.completed || this._selectedIndex == index) {
208-
return 'number';
209-
} else {
210-
return step.editable ? 'edit' : 'done';
211-
}
212-
}
213-
214205
private _emitStepperSelectionEvent(newIndex: number): void {
215206
const stepsArray = this._steps.toArray();
216207
this.selectionChange.emit({

src/lib/stepper/_stepper-theme.scss

+9-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
color: mat-color($foreground, disabled-text);
2424
}
2525

26-
.mat-step-indicator {
26+
.mat-step-icon {
2727
background-color: mat-color($primary);
2828
color: mat-color($primary, default-contrast);
2929
}
@@ -33,7 +33,14 @@
3333
color: mat-color($foreground, disabled-text);
3434
}
3535

36-
.mat-step-indicator-not-touched {
36+
//.mat-step-icon {
37+
// &.mat-step-indicator-not-touched {
38+
//
39+
// background-color: mat-color($foreground, disabled-text);
40+
// }
41+
//}
42+
43+
.mat-step-icon-not-touched {
3744
background-color: mat-color($foreground, disabled-text);
3845
}
3946
}

src/lib/stepper/index.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import {MdCommonModule} from '../core';
1818
import {MdStepLabel} from './step-label';
1919
import {MdStepperNext, MdStepperPrevious} from './stepper-button';
2020
import {MdIconModule} from '../icon/index';
21+
import {MdStepIcon} from './step-icon';
22+
import {MdStepLabelContainer} from './step-label-container';
2123

2224
@NgModule({
2325
imports: [
@@ -29,9 +31,9 @@ import {MdIconModule} from '../icon/index';
2931
MdIconModule
3032
],
3133
exports: [MdCommonModule, MdHorizontalStepper, MdVerticalStepper, MdStep, MdStepLabel, MdStepper,
32-
MdStepperNext, MdStepperPrevious],
34+
MdStepperNext, MdStepperPrevious, MdStepIcon, MdStepLabelContainer],
3335
declarations: [MdHorizontalStepper, MdVerticalStepper, MdStep, MdStepLabel, MdStepper,
34-
MdStepperNext, MdStepperPrevious],
36+
MdStepperNext, MdStepperPrevious, MdStepIcon, MdStepLabelContainer],
3537
})
3638
export class MdStepperModule {}
3739

@@ -40,3 +42,5 @@ export * from './stepper-vertical';
4042
export * from './step-label';
4143
export * from './stepper';
4244
export * from './stepper-button';
45+
export * from './step-icon';
46+
export * from './step-label-container';

src/lib/stepper/step-icon.html

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<div [ngSwitch]="_getIndicatorType()">
2+
<span *ngSwitchCase="'number'">{{index + 1}}</span>
3+
<md-icon *ngSwitchCase="'edit'">create</md-icon>
4+
<md-icon *ngSwitchCase="'done'">done</md-icon>
5+
</div>

src/lib/stepper/step-icon.spec.ts

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import {MdStepperModule} from './index';
2+
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
3+
import {Component, ViewChild} from '@angular/core';
4+
import {MdStep, MdStepper} from './stepper';
5+
import {MdStepIcon} from './step-icon';
6+
import {By} from '@angular/platform-browser';
7+
8+
describe('MdStepIcon', () => {
9+
10+
beforeEach(async(() => {
11+
TestBed.configureTestingModule({
12+
imports: [MdStepperModule],
13+
declarations: [SimpleStepIconApp],
14+
providers: [
15+
{provide: MdStepper, useClass: MdStepper}
16+
]
17+
});
18+
TestBed.compileComponents();
19+
}));
20+
21+
describe('setting icon', () => {
22+
let stepIconComponent: MdStepIcon;
23+
let fixture: ComponentFixture<SimpleStepIconApp>;
24+
let testComponent: SimpleStepIconApp;
25+
26+
beforeEach(() => {
27+
fixture = TestBed.createComponent(SimpleStepIconApp);
28+
fixture.detectChanges();
29+
30+
stepIconComponent = fixture.debugElement.query(By.css('md-step-icon')).componentInstance;
31+
testComponent = fixture.componentInstance;
32+
});
33+
34+
it('should set done icon if step is non-editable and completed', () => {
35+
stepIconComponent.selected = true;
36+
fixture.detectChanges();
37+
38+
expect(stepIconComponent._getIndicatorType()).toBe('number');
39+
40+
testComponent.mdStep.completed = true;
41+
testComponent.mdStep.editable = false;
42+
stepIconComponent.selected = false;
43+
fixture.detectChanges();
44+
45+
expect(stepIconComponent._getIndicatorType()).toBe('done');
46+
});
47+
48+
it('should set create icon if step is editable and completed', () => {
49+
stepIconComponent.selected = true;
50+
fixture.detectChanges();
51+
52+
expect(stepIconComponent._getIndicatorType()).toBe('number');
53+
54+
testComponent.mdStep.completed = true;
55+
testComponent.mdStep.editable = true;
56+
stepIconComponent.selected = false;
57+
fixture.detectChanges();
58+
59+
expect(stepIconComponent._getIndicatorType()).toBe('edit');
60+
});
61+
62+
it('should set "mat-step-icon-not-touched" class if the step ', () => {
63+
let stepIconEl = fixture.debugElement.query(By.css('md-step-icon')).nativeElement;
64+
65+
testComponent.mdStep.completed = false;
66+
stepIconComponent.selected = false;
67+
fixture.detectChanges();
68+
69+
expect(stepIconComponent._getIndicatorType()).toBe('number');
70+
expect(stepIconEl.classList).toContain('mat-step-icon-not-touched');
71+
});
72+
});
73+
});
74+
75+
@Component({
76+
template: `
77+
<md-step>step</md-step>
78+
<md-step-icon [step]="mdStep"></md-step-icon>
79+
`
80+
})
81+
class SimpleStepIconApp {
82+
@ViewChild(MdStep) mdStep: MdStep;
83+
}

src/lib/stepper/step-icon.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {Component} from '@angular/core';
10+
import {CdkStepIcon} from '@angular/cdk/stepper';
11+
12+
@Component({
13+
selector: 'md-step-icon, mat-step-icon',
14+
templateUrl: 'step-icon.html',
15+
host: {
16+
'class': 'mat-step-icon',
17+
'[class.mat-step-icon-not-touched]': 'notTouched'
18+
}
19+
})
20+
export class MdStepIcon extends CdkStepIcon { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<div>
2+
<!-- If there is a label template, use it. -->
3+
<ng-container *ngIf="step.stepLabel" [ngTemplateOutlet]="step.stepLabel.template">
4+
</ng-container>
5+
<!-- It there is no label template, fall back to the text label. -->
6+
<div *ngIf="!step.stepLabel">{{step.label}}</div>
7+
8+
<div class="mat-step-optional" *ngIf="step.optional">Optional</div>
9+
</div>
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {Component} from '@angular/core';
10+
import {CdkStepLabelContainer} from '@angular/cdk/stepper';
11+
12+
@Component({
13+
selector: 'md-step-label-container, mat-step-label-container',
14+
templateUrl: 'step-label-container.html',
15+
host: {
16+
'[class.mat-step-label-active]': 'active',
17+
'[class.mat-step-label-inactive]': '!active'
18+
}
19+
})
20+
export class MdStepLabelContainer extends CdkStepLabelContainer { }

src/lib/stepper/stepper-horizontal.html

+4-20
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,13 @@
88
[tabIndex]="_focusIndex == i ? 0 : -1"
99
(click)="step.select()"
1010
(keydown)="_onKeydown($event)">
11-
<div class="mat-step-indicator"
12-
[ngSwitch]="_getIndicatorType(i)"
13-
[class.mat-step-indicator-not-touched]="_getIndicatorType(i) == 'number'">
14-
<span *ngSwitchCase="'number'">{{i + 1}}</span>
15-
<md-icon *ngSwitchCase="'edit'">create</md-icon>
16-
<md-icon *ngSwitchCase="'done'">done</md-icon>
17-
</div>
18-
19-
<div [class.mat-stepper-label-active]="step.completed || selectedIndex == i"
20-
[class.mat-stepper-label-inactive]="!step.completed && selectedIndex != i">
21-
<!-- If there is a label template, use it. -->
22-
<ng-container *ngIf="step.stepLabel" [ngTemplateOutlet]="step.stepLabel.template">
23-
</ng-container>
24-
<!-- It there is no label template, fall back to the text label. -->
25-
<div *ngIf="!step.stepLabel">{{step.label}}</div>
26-
27-
<div class="mat-step-optional" *ngIf="step.optional">Optional</div>
28-
</div>
29-
</div>
30-
11+
<md-step-icon [step]="step" [selected]="selectedIndex == i" [index]="i"></md-step-icon>
12+
<md-step-label-container [step]="step" [selected]="selectedIndex == i">
13+
</md-step-label-container>
3114
<div *ngIf="!isLast" class="mat-stepper-horizontal-line"></div>
3215
</ng-container>
3316
</div>
17+
3418
<div class="mat-horizontal-content-container">
3519
<div *ngFor="let step of _steps; let i = index"
3620
class="mat-horizontal-stepper-content" role="tabpanel"

src/lib/stepper/stepper-vertical.html

+3-18
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,9 @@
66
[tabIndex]="_focusIndex == i ? 0 : -1"
77
(click)="step.select()"
88
(keydown)="_onKeydown($event)">
9-
<div class="mat-step-indicator"
10-
[ngSwitch]="_getIndicatorType(i)"
11-
[class.mat-step-indicator-not-touched]="_getIndicatorType(i) == 'number'">
12-
<span *ngSwitchCase="'number'">{{i + 1}}</span>
13-
<md-icon *ngSwitchCase="'edit'">create</md-icon>
14-
<md-icon *ngSwitchCase="'done'">done</md-icon>
15-
</div>
16-
17-
<div [class.mat-stepper-label-active]="step.completed || selectedIndex == i"
18-
[class.mat-stepper-label-inactive]="!step.completed && selectedIndex != i">
19-
<!-- If there is a label template, use it. -->
20-
<ng-container *ngIf="step.stepLabel"[ngTemplateOutlet]="step.stepLabel.template">
21-
</ng-container>
22-
<!-- It there is no label template, fall back to the text label. -->
23-
<div *ngIf="!step.stepLabel">{{step.label}}</div>
24-
25-
<div class="mat-step-optional" *ngIf="step.optional">Optional</div>
26-
</div>
9+
<md-step-icon [step]="step" [selected]="selectedIndex == i" [index]="i"></md-step-icon>
10+
<md-step-label-container [step]="step" [selected]="selectedIndex == i">
11+
</md-step-label-container>
2712
</div>
2813

2914
<div class="mat-vertical-content-container" [class.mat-stepper-vertical-line]="!isLast">

src/lib/stepper/stepper.scss

+3-3
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ $mat-step-optional-font-size: 12px;
2424
vertical-align: middle;
2525
}
2626

27-
.mat-step-indicator {
27+
.mat-step-icon {
2828
border-radius: 50%;
2929
height: $mat-stepper-label-header-height;
3030
width: $mat-stepper-label-header-height;
@@ -47,7 +47,7 @@ $mat-step-optional-font-size: 12px;
4747
outline: none;
4848
padding: 0 $mat-stepper-side-gap;
4949

50-
.mat-step-indicator {
50+
.mat-step-icon {
5151
margin-right: $mat-stepper-line-gap;
5252
flex: none;
5353
}
@@ -60,7 +60,7 @@ $mat-step-optional-font-size: 12px;
6060
outline: none;
6161
max-height: $mat-stepper-label-header-height;
6262

63-
.mat-step-indicator {
63+
.mat-step-icon {
6464
margin-right: $mat-vertical-stepper-content-margin - $mat-stepper-side-gap;
6565
}
6666
}

0 commit comments

Comments
 (0)