mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2024-12-21 04:50:14 +08:00
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
This commit is contained in:
parent
6edf9db812
commit
4ebecee456
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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. |
|
||||
|
33
src/input-number/demos/zhCN/debug.demo.md
Normal file
33
src/input-number/demos/zhCN/debug.demo.md
Normal file
@ -0,0 +1,33 @@
|
||||
# Debug
|
||||
|
||||
```html
|
||||
<n-input-number
|
||||
v-model:value="value1"
|
||||
placeholder="最小值"
|
||||
:min="-3"
|
||||
:max="5"
|
||||
@update:value="handleUpdateValue1"
|
||||
/>
|
||||
{{ JSON.stringify(value1) }}
|
||||
<n-input-number v-model:value="value2" @update:value="handleUpdateValue2" />
|
||||
{{ 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)
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
@ -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` | 组件值发生变化的回调 |
|
||||
|
@ -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<InputNumberTheme>),
|
||||
@ -19,27 +20,15 @@ const inputNumberProps = {
|
||||
type: Number as PropType<number | null>,
|
||||
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<boolean | undefined>,
|
||||
@ -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<MaybeArray<OnUpdateValue>>,
|
||||
onUpdateValue: [Function, Array] as PropType<MaybeArray<OnUpdateValue>>,
|
||||
onFocus: [Function, Array] as PropType<MaybeArray<(e: FocusEvent) => void>>,
|
||||
onBlur: [Function, Array] as PropType<MaybeArray<(e: FocusEvent) => void>>,
|
||||
// deprecated
|
||||
onChange: {
|
||||
type: [Function, Array] as PropType<
|
||||
MaybeArray<(value: number) => void> | undefined
|
||||
>,
|
||||
type: [Function, Array] as PropType<MaybeArray<OnUpdateValue> | 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}
|
||||
|
1
src/input-number/src/interface.ts
Normal file
1
src/input-number/src/interface.ts
Normal file
@ -0,0 +1 @@
|
||||
export type OnUpdateValue = (value: number | null) => void
|
@ -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)
|
||||
})
|
||||
})
|
58
src/input-number/tests/InputNumber.spec.tsx
Normal file
58
src/input-number/tests/InputNumber.spec.tsx
Normal file
@ -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()
|
||||
})
|
||||
})
|
@ -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)
|
||||
})
|
||||
})
|
19
src/input/tests/Input.spec.tsx
Normal file
19
src/input/tests/Input.spec.tsx
Normal file
@ -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')
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue
Block a user