Skip to content

Commit

Permalink
feat(module: stepper): 增加只能输入数字的限制;增加小数和负数的输入判断;3、增加 blur 时对输入值进行有效判断…
Browse files Browse the repository at this point in the history
…并回填;增加相应测试用例;增加 demo 示例 (#791)
  • Loading branch information
shuizhongxiong committed May 30, 2021
1 parent 1d9a4a6 commit 71fb188
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 58 deletions.
23 changes: 17 additions & 6 deletions components/stepper/demo/basic.ts
Expand Up @@ -4,25 +4,36 @@ import { Component } from '@angular/core';
selector: 'demo-stepper-basic',
template: `
<List>
<ListItem [extra]="stepperNgModel">Show number value</ListItem>
<ListItem [extra]="stepper">Show number value</ListItem>
<ListItem [extra]="stepperDecimal">Decimal step</ListItem>
<ListItem [extra]="stepperDisabled">Disabled</ListItem>
<ListItem [extra]="stepperReadOnly">ReadOnly</ListItem>
</List>
<ng-template #stepper>
<Stepper [value]="value" [min]="1" [max]="3" [showNumber]="true" (onChange)="change($event)"></Stepper>
<Stepper [showNumber]="true" [(ngModel)]="value" (ngModelChange)="change($event)"></Stepper>
</ng-template>
<ng-template #stepperDecimal>
<Stepper
[min]="1"
[max]="10"
[showNumber]="true"
[step]="0.1"
[(ngModel)]="decimalValue"
(ngModelChange)="change($event)"
></Stepper>
</ng-template>
<ng-template #stepperDisabled>
<Stepper [defaultValue]="6" [min]="1" [max]="10" [disabled]="true" [showNumber]="true"></Stepper>
</ng-template>
<ng-template #stepperNgModel>
<Stepper [(ngModel)]="value1" [min]="1" [max]="10" [showNumber]="true" (ngModelChange)="change($event)"></Stepper>
<ng-template #stepperReadOnly>
<Stepper [defaultValue]="6" [min]="1" [max]="10" [readOnly]="true" [showNumber]="true"></Stepper>
</ng-template>
`,
styles: [``]
})
export class DemoStepperBasicComponent {
value = 3;
value1 = 6;
value = 0;
decimalValue = 6;

constructor() {}

Expand Down
6 changes: 5 additions & 1 deletion components/stepper/stepper.component.html
Expand Up @@ -20,14 +20,18 @@
</div>
<div class="{{ prefixCls }}-input-wrap">
<input
class="{{ prefixCls }}-input"
type="text"
style="outline:none"
class="{{ prefixCls }}-input"
[disabled]="disabled"
[readonly]="readOnly"
[autocomplete]="'off'"
[max]="max"
[min]="min"
[(ngModel)]="value"
(ngModelChange)="inputChange($event)"
(compositionstart)="compositionStart()"
(compositionend)="compositionEnd()"
(blur)="inputBlur()"
/>
</div>
111 changes: 70 additions & 41 deletions components/stepper/stepper.component.spec.ts
@@ -1,7 +1,7 @@
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { ComponentFixture, TestBed, fakeAsync, waitForAsync } from '@angular/core/testing';
import { ComponentFixture, TestBed, fakeAsync, waitForAsync, tick } from '@angular/core/testing';
import { StepperModule } from './stepper.module';

describe('StepperComponent', () => {
Expand All @@ -12,12 +12,14 @@ describe('StepperComponent', () => {
let upButton;
let downButton;
let inputEle;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [TestStepperComponent],
imports: [StepperModule, FormsModule]
}).compileComponents();
}));
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [TestStepperComponent],
imports: [StepperModule, FormsModule]
}).compileComponents();
})
);

beforeEach(() => {
fixture = TestBed.createComponent(TestStepperComponent);
Expand All @@ -42,7 +44,6 @@ describe('StepperComponent', () => {
'up-disabled'
);
});

it('should min work', () => {
component.min = 9;
component.value = 9;
Expand All @@ -68,8 +69,7 @@ describe('StepperComponent', () => {
'up-disabled'
);
});

it('should dowDisabled work', () => {
it('should downDisabled work', () => {
component.min = 5;
component.value = 9;
component.step = 3;
Expand All @@ -83,29 +83,6 @@ describe('StepperComponent', () => {
);
});

it('should defaultValue work', () => {
component.defaultValue = 11;
fixture.detectChanges();
expect(component.defaultValue).toBe(11, 'value == defaultValue');
});
it('should step work', () => {
component.value = 1e-13;
component.step = 2;
fixture.detectChanges();
downButton.click();
expect(component.value).toBe(-1.9999999999999, 'step is 2');
component.step = 3;
fixture.detectChanges();
upButton.click();
expect(component.value).toBe(1.0000000000001, 'step is 3');
});

it('should showNumber work', () => {
expect(stepperEle.nativeElement.classList).toContain('showNumber', 'showNumber');
component.showNumber = false;
fixture.detectChanges();
expect(stepperEle.nativeElement.classList).not.toContain('showNumber', 'showNumber is not show');
});
it('should disabled work', () => {
expect(stepperEle.nativeElement.classList).not.toContain('am-stepper-disabled', 'not contain am-stepper-disabled');
component.disabled = true;
Expand Down Expand Up @@ -150,27 +127,79 @@ describe('StepperComponent', () => {

expect(stepperEle.nativeElement.classList).not.toContain('am-stepper-disabled', 'contain am-stepper-disabled');
});

it('should readOnly work', fakeAsync(() => {
component.readOnly = true;
fixture.detectChanges();
expect(inputEle.getAttribute('readonly')).not.toBeNull('readonly is null');
}));

it('should defaultValue work', () => {
component.defaultValue = 11;
fixture.detectChanges();
expect(component.defaultValue).toBe(11, 'value == defaultValue');
});
it('should step work', () => {
component.value = 1e-13;
component.step = 2;
fixture.detectChanges();
downButton.click();
expect(component.value).toBe(-1.9999999999999, 'step is 2');
component.step = 3;
fixture.detectChanges();
upButton.click();
expect(component.value).toBe(1.0000000000001, 'step is 3');
});
it('should showNumber work', () => {
expect(stepperEle.nativeElement.classList).toContain('showNumber', 'showNumber');
component.showNumber = false;
fixture.detectChanges();
expect(stepperEle.nativeElement.classList).not.toContain('showNumber', 'showNumber is not show');
});

it('should only input numbers work', fakeAsync(() => {
inputEle.value = 'ssssss';
inputEle.dispatchEvent(new UIEvent('input'));
tick(0);
expect(component.value).toBe(0, 'input ssssss');

inputEle.value = '测试中文';
inputEle.dispatchEvent(new UIEvent('input'));
tick(0);
expect(component.value).toBe(0, 'input 测试中文');

inputEle.value = 's1s.s2s.s3s';
inputEle.dispatchEvent(new UIEvent('input'));
tick(0);
expect(component.value).toBe(123, 'input s1s.s2s.s3s');

inputEle.value = '-1-2-3';
inputEle.dispatchEvent(new UIEvent('input'));
tick(0);
expect(component.value).toBe(-123, 'input -1-2-3');
}));

it('should blur check work', fakeAsync(() => {
component.max = 20;
component.min = 10;
fixture.detectChanges();

inputEle.value = 34;
inputEle.dispatchEvent(new UIEvent('input'));
expect(component.value).toBe(20, 'set input');
tick(0);
inputEle.dispatchEvent(new Event('blur'));
expect(component.value).toBe(20, 'check max input');

inputEle.value = 4;
inputEle.dispatchEvent(new UIEvent('input'));
expect(component.value).toBe(10, 'set input');
tick(0);
inputEle.dispatchEvent(new Event('blur'));
expect(component.value).toBe(10, 'check min input');

inputEle.value = 15;
inputEle.dispatchEvent(new UIEvent('input'));
expect(component.value).toBe(15, 'set input');

component.readOnly = true;
fixture.detectChanges();
expect(inputEle.getAttribute('readonly')).not.toBeNull('readonly is null');
tick(0);
inputEle.dispatchEvent(new Event('blur'));
expect(component.value).toBe(15, 'check normal input');
}));

it('should onChange work', () => {
Expand Down
73 changes: 63 additions & 10 deletions components/stepper/stepper.component.ts
Expand Up @@ -30,6 +30,7 @@ export class StepperComponent implements OnChanges, ControlValueAccessor {
private _downDisabled: boolean = false;
private _isUpClick: boolean = false;
private _isDownClick: boolean = false;
private _inputLock = false;

@Input()
get max(): number {
Expand Down Expand Up @@ -141,20 +142,72 @@ export class StepperComponent implements OnChanges, ControlValueAccessor {
}
}

compositionStart() {
this._inputLock = true;
}

compositionEnd() {
this._inputLock = false;
}

inputChange(event) {
const value = event;
this._value = value ? +value : 0;
// 'compositionend' is earlier than ngModelChange, Therefore use timer to make ngModelChange runs after 'compositionend' event
setTimeout(() => {
if (this._inputLock) {
return;
}

const allowDecimal = this._step % 1 !== 0;
const allowNegative = this._min < 0;
let decimalFlag = false;
let negativeFlag = false;
let value = event.toString().replace(/\D/g, (match, index, str) => {
if (allowDecimal && match === '.' && !decimalFlag) {
decimalFlag = true;
return '.';
}
if (allowNegative && match === '-' && !negativeFlag) {
negativeFlag = true;
return '-';
}
return '';
});

if (negativeFlag && value.indexOf('-') > 0) {
value = value.replace(/-/g, '');
}

if (!isNaN(value)) {
this._value = +value;
this._upDisabled = this.plus(this._value, this._step) > this._max ? true : false;
this._downDisabled = this.minus(this._value, this._step) < this._min ? true : false;
}

this.setCls();
this.onChange.emit(this._value);
this.onChangeFn(this._value);
}, 0);
}

inputBlur() {
let value = +this._value;
if (+this._value === -0) {
value = 0;
}
if (this._value < this._min) {
this._value = this._min;
value = this._min;
} else if (this._value > this._max) {
value = this._max;
}
if (this._value > this._max) {
this._value = this._max;

const len = this._step.toString().length - this._step.toString().indexOf('.') - 1;
value = +value.toFixed(len);

if (value !== this._value) {
this._value = value;
this.onChange.emit(this._value);
this.onChangeFn(this._value);
}
this._upDisabled = this.plus(this._value, this._step) > this._max ? true : false;
this._downDisabled = this.minus(this._value, this._step) < this._min ? true : false;
this.setCls();
this.onChange.emit(this._value);
this.onChangeFn(this._value);
}

setCls() {
Expand Down

0 comments on commit 71fb188

Please sign in to comment.