From 927bf464bfcc52c4f5027cebfe8ca84c19dfce5e Mon Sep 17 00:00:00 2001 From: neverland Date: Thu, 12 Sep 2019 17:35:16 +0800 Subject: [PATCH] feat(Stepper): add decimal-length prop (#4443) --- src/stepper/README.md | 23 ++--- src/stepper/README.zh-CN.md | 33 ++++--- src/stepper/demo/index.vue | 81 +++++------------ src/stepper/index.js | 87 +++++++++++++------ .../test/__snapshots__/demo.spec.js.snap | 10 ++- .../test/__snapshots__/index.spec.js.snap | 2 +- src/stepper/test/index.spec.js | 76 ++++++++++------ 7 files changed, 174 insertions(+), 138 deletions(-) diff --git a/src/stepper/README.md b/src/stepper/README.md index 237157048..edba40b68 100644 --- a/src/stepper/README.md +++ b/src/stepper/README.md @@ -51,6 +51,18 @@ export default { ``` +### Decimal Length + +```html + +``` + +### Custom Size + +```html + +``` + ### Async Change ```html @@ -82,16 +94,6 @@ export default { } ``` -### Custom Size - -```html - -``` - ## API ### Props @@ -110,6 +112,7 @@ export default { | button-size | Button size | *string \| number* | `28px` | 2.0.5 | | show-plus | Whether to show plus button | *boolean* | `true` | 2.1.2 | | show-minus | Whether to show minus button | *boolean* | `true` | 2.1.2 | +| decimal-length | Decimal length | *number* | - | 2.2.1 | ### Events diff --git a/src/stepper/README.zh-CN.md b/src/stepper/README.zh-CN.md index 9542d9cde..f6ba0e3a4 100644 --- a/src/stepper/README.zh-CN.md +++ b/src/stepper/README.zh-CN.md @@ -65,6 +65,22 @@ export default { ``` +### 固定小数位数 + +通过设置`decimal-length`属性可以保留固定的小数位数 + +```html + +``` + +### 自定义大小 + +通过`input-width`属性设置输入框宽度,通过`button-size`属性设置按钮大小和输入框高度 + +```html + +``` + ### 异步变更 如果需要异步地修改输入框的值,可以设置`async-change`属性,并在`change`事件中手动修改`value` @@ -100,25 +116,13 @@ export default { } ``` -### 自定义大小 - -通过`input-width`属性设置输入框宽度,通过`button-size`属性设置按钮大小和输入框高度 - -```html - -``` - ## API ### Props | 参数 | 说明 | 类型 | 默认值 | 版本 | |------|------|------|------|------| -| v-model | 当前输入值 | *string \| number* | 最小值 | - | +| v-model | 当前输入值 | *string \| number* | min | - | | min | 最小值 | *string \| number* | `1` | - | | max | 最大值 | *string \| number* | - | - | | step | 步长 | *string \| number* | `1` | - | @@ -127,9 +131,10 @@ export default { | disable-input | 是否禁用输入框 | *boolean* | `false` | - | | async-change | 是否开启异步变更,开启后需要手动控制输入值 | *boolean* | `false` | - | | input-width | 输入框宽度,默认单位为`px` | *string \| number* | `32px` | - | -| button-size | 按钮大小,默认单位为`px`,输入框高度会和按钮大小保持一致 | *string \| number* | `28px` | 2.0.5 | +| button-size | 按钮大小以及输入框高度,默认单位为`px` | *string \| number* | `28px` | 2.0.5 | | show-plus | 是否显示增加按钮 | *boolean* | `true` | 2.1.2 | | show-minus | 是否显示减少按钮 | *boolean* | `true` | 2.1.2 | +| decimal-length | 固定显示的小数位数 | *number* | - | 2.2.1 | ### Events diff --git a/src/stepper/demo/index.vue b/src/stepper/demo/index.vue index 0fdb0c88e..066c4b2f9 100644 --- a/src/stepper/demo/index.vue +++ b/src/stepper/demo/index.vue @@ -1,73 +1,35 @@ @@ -80,14 +42,16 @@ export default { range: '限制输入范围', integer: '限制输入整数', asyncChange: '异步变更', - customSize: '自定义大小' + customSize: '自定义大小', + decimalLength: '固定小数位数' }, 'en-US': { step: 'Step', range: 'Range', integer: 'Integer', asyncChange: 'Async Change', - customSize: 'Custom Size' + customSize: 'Custom Size', + decimalLength: 'Decimal Length' } }, @@ -99,7 +63,8 @@ export default { stepper4: 1, stepper5: 1, stepper6: 1, - stepper7: 1 + stepper7: 1, + stepper8: 1 }; }, diff --git a/src/stepper/index.js b/src/stepper/index.js index 3c4dad747..14db7b9d4 100644 --- a/src/stepper/index.js +++ b/src/stepper/index.js @@ -7,6 +7,10 @@ const [createComponent, bem] = createNamespace('stepper'); const LONG_PRESS_START_TIME = 600; const LONG_PRESS_INTERVAL = 200; +function equal(value1, value2) { + return String(value1) === String(value2); +} + export default createComponent({ props: { value: null, @@ -16,6 +20,7 @@ export default createComponent({ buttonSize: [Number, String], asyncChange: Boolean, disableInput: Boolean, + decimalLength: Number, min: { type: [Number, String], default: 1 @@ -43,8 +48,10 @@ export default createComponent({ }, data() { - const value = this.range(isDef(this.value) ? this.value : this.defaultValue); - if (value !== +this.value) { + const defaultValue = isDef(this.value) ? this.value : this.defaultValue; + const value = this.format(defaultValue); + + if (!equal(value, this.value)) { this.$emit('input', value); } @@ -90,7 +97,7 @@ export default createComponent({ watch: { value(val) { - if (val !== this.currentValue) { + if (!equal(val, this.currentValue)) { this.currentValue = this.format(val); } }, @@ -103,29 +110,54 @@ export default createComponent({ methods: { // filter illegal characters - format(value) { + filter(value) { value = String(value).replace(/[^0-9.-]/g, ''); - return value === '' ? 0 : this.integer ? Math.floor(value) : +value; + + if (this.integer && value.indexOf('.') !== -1) { + value = value.split('.')[0]; + } + + return value; }, - // limit value range - range(value) { - return Math.max(Math.min(this.max, this.format(value)), this.min); + format(value) { + value = this.filter(value); + + // format range + value = value === '' ? 0 : +value; + value = Math.max(Math.min(this.max, value), this.min); + + // format decimal + if (isDef(this.decimalLength)) { + value = value.toFixed(this.decimalLength); + } + + return value; }, onInput(event) { const { value } = event.target; - const formatted = this.format(value); + // allow input to be empty + if (value === '') { + return; + } + + const formatted = this.filter(value); + + if (!equal(value, formatted)) { + event.target.value = formatted; + } + + this.emitChange(formatted); + }, + + emitChange(value) { if (this.asyncChange) { - event.target.value = this.currentValue; - this.$emit('input', formatted); - this.$emit('change', formatted); + this.$emit('input', value); + this.$emit('change', value); } else { - if (+value !== formatted) { - event.target.value = formatted; - } - this.currentValue = formatted; + this.currentValue = value; } }, @@ -138,15 +170,17 @@ export default createComponent({ } const diff = type === 'minus' ? -this.step : +this.step; - const value = Math.round((this.currentValue + diff) * 100) / 100; - if (this.asyncChange) { - this.$emit('input', value); - this.$emit('change', value); - } else { - this.currentValue = this.range(value); + let value = +this.currentValue + diff; + + // avoid float number + if (!isDef(this.decimalLength)) { + value = Math.round(value * 100) / 100; } + value = this.format(value); + + this.emitChange(value); this.$emit(type); }, @@ -155,14 +189,11 @@ export default createComponent({ }, onBlur(event) { - this.currentValue = this.range(this.currentValue); + const value = this.format(event.target.value); + event.target.value = value; + this.currentValue = value; this.$emit('blur', event); - // fix edge case when input is empty and min is 0 - if (this.currentValue === 0) { - event.target.value = this.currentValue; - } - resetScroll(); }, diff --git a/src/stepper/test/__snapshots__/demo.spec.js.snap b/src/stepper/test/__snapshots__/demo.spec.js.snap index d37bc1995..60d21c169 100644 --- a/src/stepper/test/__snapshots__/demo.spec.js.snap +++ b/src/stepper/test/__snapshots__/demo.spec.js.snap @@ -33,9 +33,9 @@ exports[`renders demo correctly 1`] = `
-
异步变更
+
固定小数位数
-
+
@@ -44,5 +44,11 @@ exports[`renders demo correctly 1`] = `
+
+
异步变更
+
+
+
+
`; diff --git a/src/stepper/test/__snapshots__/index.spec.js.snap b/src/stepper/test/__snapshots__/index.spec.js.snap index 79cac6a84..906faebe1 100644 --- a/src/stepper/test/__snapshots__/index.spec.js.snap +++ b/src/stepper/test/__snapshots__/index.spec.js.snap @@ -4,6 +4,6 @@ exports[`disable stepper input 1`] = `
`; -exports[`input width 1`] = `
`; +exports[`input-width prop 1`] = `
`; exports[`show-plus & show-minus props 1`] = `
`; diff --git a/src/stepper/test/index.spec.js b/src/stepper/test/index.spec.js index 38c8d4a17..e6080371f 100644 --- a/src/stepper/test/index.spec.js +++ b/src/stepper/test/index.spec.js @@ -60,35 +60,45 @@ test('long press', async () => { expect(wrapper.emitted('input')).toEqual([[2], [3], [4]]); }); -test('correct value when value is not correct', () => { +test('filter value during user input', () => { const wrapper = mount(Stepper, { propsData: { - value: 50, - max: 30, - min: 10 + value: 1 } }); - const input = wrapper.find('input'); - input.element.value = 1; - input.trigger('input'); - input.element.value = 'abc'; - input.trigger('input'); - wrapper.setData({ value: 'abc' }); - input.trigger('input'); - wrapper.setData({ value: '' }); - input.trigger('input'); - wrapper.setData({ value: 40 }); - input.trigger('input'); - wrapper.setData({ value: 30 }); + const input = wrapper.find('.van-stepper__input'); + input.element.value = ''; input.trigger('input'); + expect(wrapper.emitted('input')).toBeFalsy(); - expect(wrapper.emitted('input')).toEqual([[30], [1], [0], [40], [30]]); + input.element.value = 'a'; + input.trigger('input'); + expect(input.element.value).toEqual(''); + expect(wrapper.emitted('input')).toBeFalsy(); + + input.element.value = '2'; + input.trigger('input'); + expect(input.element.value).toEqual('2'); + expect(wrapper.emitted('input')[0][0]).toEqual('2'); +}); + +test('shoud watch value and format it', () => { + const wrapper = mount(Stepper, { + propsData: { + value: 1, + max: 5 + } + }); + + wrapper.setData({ value: 10 }); + expect(wrapper.emitted('input')[0][0]).toEqual(5); }); test('only allow interger', () => { const wrapper = mount(Stepper, { propsData: { + value: 1, integer: true } }); @@ -98,7 +108,8 @@ test('only allow interger', () => { input.trigger('input'); input.trigger('blur'); - expect(wrapper.emitted('input')).toEqual([[1]]); + expect(wrapper.emitted('input')[0][0]).toEqual('1'); + expect(wrapper.emitted('input')[1][0]).toEqual(1); }); test('stepper focus', () => { @@ -121,16 +132,15 @@ test('stepper blur', () => { }); const input = wrapper.find('input'); - input.trigger('blur'); input.element.value = ''; input.trigger('input'); input.trigger('blur'); - expect(wrapper.emitted('input')).toEqual([[0], [3]]); + expect(wrapper.emitted('input')[0][0]).toEqual(3); expect(wrapper.emitted('blur')).toBeTruthy(); }); -test('input width', () => { +test('input-width prop', () => { const wrapper = mount(Stepper, { propsData: { inputWidth: '10rem' @@ -139,7 +149,7 @@ test('input width', () => { expect(wrapper).toMatchSnapshot(); }); -test('async change', () => { +test('async-change prop', () => { const wrapper = mount(Stepper, { propsData: { value: 1, @@ -157,8 +167,8 @@ test('async change', () => { input.element.value = '3'; input.trigger('input'); - expect(wrapper.emitted('input')[1][0]).toEqual(3); - expect(wrapper.emitted('change')[1][0]).toEqual(3); + expect(wrapper.emitted('input')[1][0]).toEqual('3'); + expect(wrapper.emitted('change')[1][0]).toEqual('3'); }); test('min value is 0', () => { @@ -174,7 +184,7 @@ test('min value is 0', () => { input.trigger('input'); input.trigger('blur'); - expect(wrapper.emitted('input')[0][0]).toEqual(0); + expect(input.element.value).toEqual('0'); }); test('show-plus & show-minus props', () => { @@ -187,3 +197,19 @@ test('show-plus & show-minus props', () => { expect(wrapper).toMatchSnapshot(); }); + +test('decimal-length prop', () => { + const wrapper = mount(Stepper, { + propsData: { + value: 1, + step: 0.2, + decimalLength: 2 + } + }); + + expect(wrapper.emitted('input')[0][0]).toEqual('1.00'); + + const plus = wrapper.find('.van-stepper__plus'); + plus.trigger('click'); + expect(wrapper.emitted('input')[1][0]).toEqual('1.20'); +});