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')
+ })
+})