From 4ebecee456b1cb93b7389d8549c275fa2719563c Mon Sep 17 00:00:00 2001 From: 07akioni <07akioni2@gmail.com> Date: Wed, 23 Jun 2021 13:35:14 +0800 Subject: [PATCH] fix(input-number): many fixes (#259) * fix(input-number): many fixes - Fix `n-input-number` lacks `on-update-value` prop. - Fix `n-input-number`'s value can't be null. - Fix `n-input-number`'s button doesn't work after value is cleared, closes [#251](https://github.com/TuSimple/naive-ui/issues/251). - `n-input-number` will focus directly, closes [#244](https://github.com/TuSimple/naive-ui/issues/244). * docs(input-number): fixes * docs --- CHANGELOG.en-US.md | 9 ++- CHANGELOG.zh-CN.md | 9 ++- .../demos/enUS/index.demo-entry.md | 4 +- src/input-number/demos/zhCN/debug.demo.md | 33 +++++++++++ .../demos/zhCN/index.demo-entry.md | 3 +- src/input-number/src/InputNumber.tsx | 55 ++++++++---------- src/input-number/src/interface.ts | 1 + src/input-number/tests/InputNumber.spec.ts | 17 ------ src/input-number/tests/InputNumber.spec.tsx | 58 +++++++++++++++++++ src/input/tests/Input.spec.ts | 8 --- src/input/tests/Input.spec.tsx | 19 ++++++ 11 files changed, 155 insertions(+), 61 deletions(-) create mode 100644 src/input-number/demos/zhCN/debug.demo.md create mode 100644 src/input-number/src/interface.ts delete mode 100644 src/input-number/tests/InputNumber.spec.ts create mode 100644 src/input-number/tests/InputNumber.spec.tsx delete mode 100644 src/input/tests/Input.spec.ts create mode 100644 src/input/tests/Input.spec.tsx diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md index 5c2c913c35f..222444cb452 100644 --- a/CHANGELOG.en-US.md +++ b/CHANGELOG.en-US.md @@ -14,7 +14,14 @@ ### Fixes -- `n-select` can't input in filterable mode in single mode in iOS Safari, closes [#230](https://github.com/TuSimple/naive-ui/issues/230) +- Fix `n-select` can't input in filterable mode in single mode in iOS Safari, closes [#230](https://github.com/TuSimple/naive-ui/issues/230) +- Fix `n-input-number` lacks `on-update-value` prop. +- Fix `n-input-number`'s value can't be null. +- Fix `n-input-number`'s button doesn't work after value is cleared, closes [#251](https://github.com/TuSimple/naive-ui/issues/251). + +## Refactors + +- `n-input-number` will focus directly, closes [#244](https://github.com/TuSimple/naive-ui/issues/244). ## 2.13.0 (2021-06-21) diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index b0f187996b5..1a73c5222c4 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -14,7 +14,14 @@ ### Fixes -- `n-select` 在可过滤单选模式下在 iOS Safari 无法输入,关闭 [#230](https://github.com/TuSimple/naive-ui/issues/230) +- 修复 `n-select` 在可过滤单选模式下在 iOS Safari 无法输入,关闭 [#230](https://github.com/TuSimple/naive-ui/issues/230) +- 修复 `n-input-number` 缺少 `on-update-value` 属性 +- 修复 `n-input-number` 值无法为 `null` +- 修复 `n-input-number` 的按钮在值清空后无法使用,关闭 [#251](https://github.com/TuSimple/naive-ui/issues/251) + +## Refactors + +- `n-input-number` 会直接聚焦,关闭 [#244](https://github.com/TuSimple/naive-ui/issues/244) ## 2.13.0 (2021-06-21) diff --git a/src/input-number/demos/enUS/index.demo-entry.md b/src/input-number/demos/enUS/index.demo-entry.md index a937228d36d..74b4f9890b9 100644 --- a/src/input-number/demos/enUS/index.demo-entry.md +++ b/src/input-number/demos/enUS/index.demo-entry.md @@ -29,7 +29,7 @@ show-button | size | `'small' \| 'medium' \| 'large'` | `'medium'` | The size of input box. | | step | `number` | `1` | The number to which the current value is increased or decreased. It can be an integer or decimal. | | validator | `(value) => boolean` | `undefined` | Setup custom validation. | -| value | `number` | `undefined` | Value in controlled mode. | +| value | `number \| null` | `undefined` | Value in controlled mode. | | on-blur | `(event: FocusEvent) => void` | `undefined` | Callback when blur. | | on-focus | `(event: FocusEvent) => void` | `undefined` | Callback when focused. | -| on-update:value | `(value: number) => void` | `undefined` | Callback when the component's value changes. | +| on-update:value | `(value: number \| null) => void` | `undefined` | Callback when the component's value changes. | diff --git a/src/input-number/demos/zhCN/debug.demo.md b/src/input-number/demos/zhCN/debug.demo.md new file mode 100644 index 00000000000..43622b51ed7 --- /dev/null +++ b/src/input-number/demos/zhCN/debug.demo.md @@ -0,0 +1,33 @@ +# Debug + +```html + +{{ JSON.stringify(value1) }} + +{{ JSON.stringify(value2) }} +``` + +```js +import { defineComponent, ref } from 'vue' + +export default defineComponent({ + setup () { + return { + handleUpdateValue1 (v) { + console.log(v) + }, + handleUpdateValue2 (v) { + console.log(v) + }, + value1: ref(null), + value2: ref(null) + } + } +}) +``` diff --git a/src/input-number/demos/zhCN/index.demo-entry.md b/src/input-number/demos/zhCN/index.demo-entry.md index 8915b93fb57..ec0ad1645b9 100644 --- a/src/input-number/demos/zhCN/index.demo-entry.md +++ b/src/input-number/demos/zhCN/index.demo-entry.md @@ -13,6 +13,7 @@ size step validator show-button +debug ``` ## Props @@ -32,4 +33,4 @@ show-button | value | `number \| null` | `undefined` | 受控模式下的值 | | on-blur | `(event: FocusEvent) => void` | `undefined` | 移除焦点的回调 | | on-focus | `(event: FocusEvent) => void` | `undefined` | 获取焦点的回调 | -| on-update:value | `(value: number) => void` | `undefined` | 组件值发生变化的回调 | +| on-update:value | `(value: number \| null) => void` | `undefined` | 组件值发生变化的回调 | diff --git a/src/input-number/src/InputNumber.tsx b/src/input-number/src/InputNumber.tsx index e34a0bae634..77fc2d4275f 100644 --- a/src/input-number/src/InputNumber.tsx +++ b/src/input-number/src/InputNumber.tsx @@ -11,6 +11,7 @@ import type { ThemeProps } from '../../_mixins' import { warn, call, MaybeArray, ExtractPublicPropTypes } from '../../_utils' import { inputNumberLight, InputNumberTheme } from '../styles' import { parse, validator, format, parseNumber } from './utils' +import type { OnUpdateValue } from './interface' const inputNumberProps = { ...(useTheme.props as ThemeProps), @@ -19,27 +20,15 @@ const inputNumberProps = { type: Number as PropType, default: null }, - value: { - type: Number, - default: undefined - }, + value: Number, step: { type: [Number, String], default: 1 }, - min: { - type: [Number, String], - default: undefined - }, - max: { - type: [Number, String], - default: undefined - }, + min: [Number, String], + max: [Number, String], size: String as PropType<'small' | 'medium' | 'large'>, - disabled: { - type: Boolean, - default: false - }, + disabled: Boolean, validator: Function as PropType<(value: number) => boolean>, bordered: { type: Boolean as PropType, @@ -49,17 +38,13 @@ const inputNumberProps = { type: Boolean, default: true }, - // eslint-disable-next-line vue/prop-name-casing - 'onUpdate:value': [Function, Array] as PropType< - MaybeArray<(value: number) => void> - >, + 'onUpdate:value': [Function, Array] as PropType>, + onUpdateValue: [Function, Array] as PropType>, onFocus: [Function, Array] as PropType void>>, onBlur: [Function, Array] as PropType void>>, // deprecated onChange: { - type: [Function, Array] as PropType< - MaybeArray<(value: number) => void> | undefined - >, + type: [Function, Array] as PropType | undefined>, validator: () => { if (__DEV__) { warn( @@ -124,39 +109,47 @@ export default defineComponent({ if (parsedNumber !== null) return parsedNumber else return null }) - const doUpdateValue = (value: number): void => { + const doUpdateValue = (value: number | null): void => { const { value: mergedValue } = mergedValueRef if (value === mergedValue) return - const { 'onUpdate:value': onUpdateValue, onChange } = props + const { + 'onUpdate:value': _onUpdateValue, + onUpdateValue, + onChange + } = props const { nTriggerFormInput, nTriggerFormChange } = formItem if (onChange) call(onChange, value) if (onUpdateValue) call(onUpdateValue, value) + if (_onUpdateValue) call(_onUpdateValue, value) uncontrolledValueRef.value = value nTriggerFormInput() nTriggerFormChange() } const deriveValueFromDisplayedValue = ( offset = 0, - postUpdateIfValid = true + doUpdateIfValid = true ): null | number | false => { const { value: displayedValue } = displayedValueRef const parsedValue = parse(displayedValue) - if (parsedValue === null) return null + if (parsedValue === null) { + if (doUpdateIfValid) doUpdateValue(null) + return null + } if (validator(parsedValue)) { let nextValue = parsedValue + offset if (validator(nextValue)) { const { value: mergedMax } = mergedMaxRef const { value: mergedMin } = mergedMinRef if (mergedMax !== null && nextValue > mergedMax) { - if (!postUpdateIfValid) return false + if (!doUpdateIfValid) return false nextValue = mergedMax } if (mergedMin !== null && nextValue < mergedMin) { - if (!postUpdateIfValid) return false + if (!doUpdateIfValid) return false nextValue = mergedMin } if (props.validator && !props.validator(nextValue)) return false - if (postUpdateIfValid) doUpdateValue(nextValue) + if (doUpdateIfValid) doUpdateValue(nextValue) return nextValue } } @@ -307,6 +300,7 @@ export default defineComponent({ } function handleUpdateDisplayedValue (value: string): void { displayedValueRef.value = value + deriveValueFromDisplayedValue() } watch(mergedValueRef, () => { deriveDisplayedValueFromValue() @@ -360,7 +354,6 @@ export default defineComponent({ bordered={this.mergedBordered} value={this.displayedValue} onUpdateValue={this.handleUpdateDisplayedValue} - passively-activated theme={this.mergedTheme.peers.Input} themeOverrides={this.mergedTheme.peerOverrides.Input} builtinThemeOverrides={this.inputThemeOverrides} diff --git a/src/input-number/src/interface.ts b/src/input-number/src/interface.ts new file mode 100644 index 00000000000..9492f536851 --- /dev/null +++ b/src/input-number/src/interface.ts @@ -0,0 +1 @@ +export type OnUpdateValue = (value: number | null) => void diff --git a/src/input-number/tests/InputNumber.spec.ts b/src/input-number/tests/InputNumber.spec.ts deleted file mode 100644 index c7ddddbff22..00000000000 --- a/src/input-number/tests/InputNumber.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { mount } from '@vue/test-utils' -import { NInputNumber } from '../index' -import { NButton } from '../../button' - -describe('n-input-number', () => { - it('should work with import on demand', () => { - mount(NInputNumber) - }) - - it('should work with `show-button` prop', async () => { - const wrapper = mount(NInputNumber) - expect(wrapper.findComponent(NButton).exists()).toBe(true) - - await wrapper.setProps({ showButton: false }) - expect(wrapper.findComponent(NButton).exists()).toBe(false) - }) -}) diff --git a/src/input-number/tests/InputNumber.spec.tsx b/src/input-number/tests/InputNumber.spec.tsx new file mode 100644 index 00000000000..91ffea93acc --- /dev/null +++ b/src/input-number/tests/InputNumber.spec.tsx @@ -0,0 +1,58 @@ +import { mount } from '@vue/test-utils' +import { NInputNumber } from '../index' +import { NButton } from '../../button' + +describe('n-input-number', () => { + it('should work with import on demand', () => { + mount(NInputNumber) + }) + + it('should work with `show-button` prop', async () => { + const wrapper = mount(NInputNumber) + expect(wrapper.findComponent(NButton).exists()).toBe(true) + + await wrapper.setProps({ showButton: false }) + expect(wrapper.findComponent(NButton).exists()).toBe(false) + }) + + it('should work with default value', async () => { + const wrapper = mount(NInputNumber, { + props: { + defaultValue: 1 + } + }) + expect(wrapper.find('input').element.value).toEqual('1') + }) + + it('should not trigger update if value is same', async () => { + const onUpdateValue = jest.fn() + const wrapper = mount(NInputNumber, { + attachTo: document.body, + props: { + defaultValue: 1, + onUpdateValue + } + }) + wrapper.find('input').element.value = '' + await wrapper.find('input').trigger('input') + expect(onUpdateValue).toHaveBeenCalledWith(null) + wrapper.unmount() + }) + + it('trigger focus & blur event', () => { + const onFocus = jest.fn() + const onBlur = jest.fn() + const wrapper = mount(NInputNumber, { + attachTo: document.body, + props: { + onFocus, + onBlur + } + }) + wrapper.find('input').element.focus() + expect(onFocus).toHaveBeenCalledTimes(1) + wrapper.find('input').element.blur() + expect(onBlur).toHaveBeenCalledTimes(1) + wrapper.unmount() + }) +}) diff --git a/src/input/tests/Input.spec.ts b/src/input/tests/Input.spec.ts deleted file mode 100644 index 49ff8276067..00000000000 --- a/src/input/tests/Input.spec.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { mount } from '@vue/test-utils' -import { NInput } from '../index' - -describe('n-input', () => { - it('should work with import on demand', () => { - mount(NInput) - }) -}) diff --git a/src/input/tests/Input.spec.tsx b/src/input/tests/Input.spec.tsx new file mode 100644 index 00000000000..9082beb8444 --- /dev/null +++ b/src/input/tests/Input.spec.tsx @@ -0,0 +1,19 @@ +import { mount } from '@vue/test-utils' +import { NInput } from '../index' + +describe('n-input', () => { + it('should work with import on demand', () => { + mount(NInput) + }) + it('should call input callbacks', async () => { + const onUpdateValue = jest.fn() + const wrapper = mount(NInput, { + props: { + onUpdateValue + } + }) + wrapper.find('input').element.value = 'cool' + await wrapper.find('input').trigger('input') + expect(onUpdateValue).toHaveBeenCalledWith('cool') + }) +})