Skip to content
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

feat(module: stepper): 修复输入框输入显示问题 #791

Merged
merged 2 commits into from
May 30, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 17 additions & 6 deletions components/stepper/demo/basic.ts
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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