Skip to content
Permalink
Browse files

feat(module:steps): support for clickable steps (#3934)

  • Loading branch information...
hsuanxyz authored and wendzhue committed Aug 9, 2019
1 parent 9ea40da commit ac866ceb3a645e3b22c37bbead42a012b165a059
@@ -0,0 +1,15 @@
---
order: 10
title:
zh-CN: 可点击
en-US: Clickable
---

## zh-CN

订阅 `(nzIndexChange)` 后,Steps 变为可点击状态。

## en-US

subscribe `(nzIndexChange)` makes Steps clickable.

@@ -0,0 +1,25 @@
import { Component } from '@angular/core';

@Component({
selector: 'nz-demo-steps-clickable',
template: `
<nz-steps [nzCurrent]="index" (nzIndexChange)="onIndexChange($event)">
<nz-step nzTitle="Finished" nzDescription="This is a description."></nz-step>
<nz-step nzTitle="In Progress" nzDescription="This is a description."></nz-step>
<nz-step nzTitle="Waiting" nzDescription="This is a description."></nz-step>
</nz-steps>
<nz-divider></nz-divider>
<nz-steps nzDirection="vertical" [nzCurrent]="index" (nzIndexChange)="onIndexChange($event)">
<nz-step nzTitle="Finished" nzDescription="This is a description."></nz-step>
<nz-step nzTitle="In Progress" nzDescription="This is a description."></nz-step>
<nz-step nzTitle="Waiting" nzDescription="This is a description."></nz-step>
</nz-steps>
`
})
export class NzDemoStepsClickableComponent {
index = 0;

onIndexChange(index: number): void {
this.index = index;
}
}
@@ -1,5 +1,6 @@
import { NzStepsModule } from 'ng-zorro-antd/steps';
import { NzPopoverModule } from 'ng-zorro-antd/popover';
import { NzButtonModule } from 'ng-zorro-antd/button';
import { NzDividerModule } from 'ng-zorro-antd/divider';

export const moduleList = [ NzStepsModule, NzPopoverModule, NzButtonModule ];
export const moduleList = [ NzStepsModule, NzPopoverModule, NzButtonModule, NzDividerModule ];
@@ -42,6 +42,7 @@ The whole of the step bar.
| `[nzSize]` | To specify the size of the step bar, `default` and `small` are currently supported | `'small' \| 'default'` | `'default'` |
| `[nzStatus]` | To specify the status of current step, can be set to one of the following values: `wait` `process` `finish` `error` | `'wait' \| 'process' \| 'finish' \| 'error'` | `'process'` |
| `[nzStartIndex]` | To specify the starting number | `number` | `0` |
| `(nzIndexChange)` | Trigger event when step click | `number` | - |

### nz-step

@@ -43,6 +43,7 @@ import { NzStepsModule } from 'ng-zorro-antd/steps';
| `[nzSize]` | 指定大小,目前支持普通(`default`)和迷你(`small`) | `'small' \| 'default'` | `'default'` |
| `[nzStatus]` | 指定当前步骤的状态,可选 `wait` `process` `finish` `error` | `'wait' \| 'process' \| 'finish' \| 'error'` | `'process'` |
| `[nzStartIndex]` | 指定起始位置的序号 | `number` | `0` |
| `(nzIndexChange)` | 点击单个步骤时触发的事件 | `number` | - |

### nz-step

@@ -12,13 +12,15 @@ import {
Component,
ElementRef,
Input,
OnDestroy,
Renderer2,
TemplateRef,
ViewChild,
ViewEncapsulation
} from '@angular/core';

import { NgClassType } from 'ng-zorro-antd/core';
import { Subject } from 'rxjs';

@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -33,10 +35,12 @@ import { NgClassType } from 'ng-zorro-antd/core';
'[class.ant-steps-item-finish]': 'nzStatus === "finish"',
'[class.ant-steps-item-error]': 'nzStatus === "error"',
'[class.ant-steps-custom]': '!!nzIcon',
'[class.ant-steps-next-error]': '(outStatus === "error") && (currentIndex === index + 1)'
'[class.ant-steps-next-error]': '(outStatus === "error") && (currentIndex === index + 1)',
'[attr.role]': 'clickable ? "button" : null',
'(click)': 'onClick()'
}
})
export class NzStepComponent {
export class NzStepComponent implements OnDestroy {
@ViewChild('processDotTemplate', { static: false }) processDotTemplate: TemplateRef<void>;

@Input() nzTitle: string | TemplateRef<void>;
@@ -80,6 +84,8 @@ export class NzStepComponent {
last = false;
outStatus = 'process';
showProcessDot = false;
clickable = false;
click$ = new Subject<number>();

get currentIndex(): number {
return this._currentIndex;
@@ -98,7 +104,17 @@ export class NzStepComponent {
renderer.addClass(elementRef.nativeElement, 'ant-steps-item');
}

onClick(): void {
if (this.clickable && this.currentIndex !== this.index) {
this.click$.next(this.index);
}
}

markForCheck(): void {
this.cdr.markForCheck();
}

ngOnDestroy(): void {
this.click$.complete();
}
}
@@ -11,17 +11,19 @@ import {
ChangeDetectionStrategy,
Component,
ContentChildren,
EventEmitter,
Input,
OnChanges,
OnDestroy,
OnInit,
Output,
QueryList,
SimpleChanges,
TemplateRef,
ViewEncapsulation
} from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { merge, Subject, Subscription } from 'rxjs';
import { startWith, takeUntil } from 'rxjs/operators';

import { toBoolean, NgClassType, NzSizeDSType } from 'ng-zorro-antd/core';

@@ -58,12 +60,15 @@ export class NzStepsComponent implements OnChanges, OnInit, OnDestroy, AfterCont
}
this.updateChildrenSteps();
}
showProcessDot = false;
customProcessDotTemplate: TemplateRef<{ $implicit: TemplateRef<void>; status: string; index: number }>;

classMap: NgClassType;
@Output() readonly nzIndexChange = new EventEmitter<number>();

private destroy$ = new Subject<void>();
private indexChangeSubscription: Subscription;

showProcessDot = false;
customProcessDotTemplate: TemplateRef<{ $implicit: TemplateRef<void>; status: string; index: number }>;
classMap: NgClassType;

ngOnChanges(changes: SimpleChanges): void {
if (changes.nzStartIndex || changes.nzDirection || changes.nzStatus || changes.nzCurrent) {
@@ -82,14 +87,21 @@ export class NzStepsComponent implements OnChanges, OnInit, OnDestroy, AfterCont
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
if (this.indexChangeSubscription) {
this.indexChangeSubscription.unsubscribe();
}
}

ngAfterContentInit(): void {
this.updateChildrenSteps();
if (this.steps) {
this.steps.changes.pipe(takeUntil(this.destroy$)).subscribe(() => {
this.updateChildrenSteps();
});
this.steps.changes
.pipe(
startWith(null),
takeUntil(this.destroy$)
)
.subscribe(() => {
this.updateChildrenSteps();
});
}
}

@@ -103,13 +115,20 @@ export class NzStepsComponent implements OnChanges, OnInit, OnDestroy, AfterCont
if (this.customProcessDotTemplate) {
step.customProcessTemplate = this.customProcessDotTemplate;
}
step.clickable = this.nzIndexChange.observers.length > 0;
step.direction = this.nzDirection;
step.index = index + this.nzStartIndex;
step.currentIndex = this.nzCurrent;
step.last = length === index + 1;
step.markForCheck();
});
});
if (this.indexChangeSubscription) {
this.indexChangeSubscription.unsubscribe();
}
this.indexChangeSubscription = merge(...this.steps.map(step => step.click$)).subscribe(index =>
this.nzIndexChange.emit(index)
);
}
}

@@ -10,7 +10,9 @@ import {
import { async, fakeAsync, tick, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';

import { NzDividerModule } from 'ng-zorro-antd/divider';
import { NzIconTestModule } from 'ng-zorro-antd/icon/testing';
import { NzDemoStepsClickableComponent } from './demo/clickable';

import { NzStepComponent } from './nz-step.component';
import { NzStepsComponent } from './nz-steps.component';
@@ -19,9 +21,10 @@ import { NzStepsModule } from './nz-steps.module';
describe('steps', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [NzStepsModule, NzIconTestModule],
imports: [NzStepsModule, NzIconTestModule, NzDividerModule],
declarations: [
NzTestOuterStepsComponent,
NzDemoStepsClickableComponent,
NzTestInnerStepStringComponent,
NzTestInnerStepTemplateComponent,
NzTestStepForComponent,
@@ -361,6 +364,47 @@ describe('steps', () => {
expect(innerSteps[2].nativeElement.querySelector('.ant-steps-icon').innerText.trim()).toBe('3');
}));
});
describe('step clickable', () => {
let fixture: ComponentFixture<NzDemoStepsClickableComponent>;
let testComponent: NzDemoStepsClickableComponent;
let innerSteps: DebugElement[];
beforeEach(() => {
fixture = TestBed.createComponent(NzDemoStepsClickableComponent);
testComponent = fixture.debugElement.componentInstance;
innerSteps = fixture.debugElement.queryAll(By.directive(NzStepComponent));
});
it('should clickable', fakeAsync(() => {
fixture.detectChanges();
tick();
fixture.detectChanges();
innerSteps
.map(step => step.nativeElement)
.forEach((e: HTMLElement) => {
expect(e.getAttribute('role')).toBe('button');
});
}));

it('should output work', fakeAsync(() => {
fixture.detectChanges();
tick();
fixture.detectChanges();
spyOn(testComponent, 'onIndexChange');
innerSteps[1].nativeElement.click();
fixture.detectChanges();
expect(testComponent.onIndexChange).toHaveBeenCalledWith(1);
}));

it("should can't click when status is process", fakeAsync(() => {
testComponent.index = 0;
fixture.detectChanges();
tick();
fixture.detectChanges();
spyOn(testComponent, 'onIndexChange');
innerSteps[0].nativeElement.click();
fixture.detectChanges();
expect(testComponent.onIndexChange).not.toHaveBeenCalled();
}));
});
});

@Component({

0 comments on commit ac866ce

Please sign in to comment.
You can’t perform that action at this time.