fix(Field): should limit number input in iOS (#5520)

This commit is contained in:
neverland 2020-01-09 13:58:49 +08:00 committed by GitHub
parent ccdedecee7
commit 3bfcf671b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 181 additions and 127 deletions

View File

@ -17,34 +17,45 @@ The value of field is bound with v-model.
```html ```html
<van-cell-group> <van-cell-group>
<van-field v-model="value" placeholder="Username" /> <van-field v-model="value" placeholder="Text" />
</van-cell-group> </van-cell-group>
``` ```
```js
export default {
data() {
return {
value: ''
};
}
}
```
### Custom Type ### Custom Type
Use `type` prop to custom different type fields. Use `type` prop to custom different type fields.
```html ```html
<van-cell-group> <van-field v-model="text" label="Text" />
<van-field
v-model="username"
required
clearable
label="Username"
right-icon="question-o"
placeholder="Username"
@click-right-icon="$toast('question')"
/>
<van-field <van-field v-model="password" type="password" label="Password" />
v-model="password"
type="password" <van-field v-model="number" type="number" label="Number" />
label="Password"
placeholder="Password" <van-field v-model="tel" type="tel" label="Phone" />
required ```
/>
</van-cell-group> ```js
export default {
data() {
return {
tel: '',
number: '',
username: '',
password: ''
};
}
}
``` ```
### Disabled ### Disabled
@ -52,9 +63,8 @@ Use `type` prop to custom different type fields.
```html ```html
<van-cell-group> <van-cell-group>
<van-field <van-field
label="Text"
value="Disabled" value="Disabled"
label="Username"
left-icon="contact"
disabled disabled
/> />
</van-cell-group> </van-cell-group>

View File

@ -17,38 +17,54 @@ Vue.use(Field);
### 基础用法 ### 基础用法
通过`v-model`绑定输入框的值 可以通过`v-model`双向绑定输入框的值,通过`placeholder`设置占位提示文字
```html ```html
<!-- Field 是基于 Cell 实现的,可以使用 CellGroup 作为父元素来提供外边框。 -->
<van-cell-group> <van-cell-group>
<van-field v-model="value" placeholder="请输入用户名" /> <van-field v-model="value" placeholder="请输入用户名" />
</van-cell-group> </van-cell-group>
``` ```
```js
export default {
data() {
return {
value: ''
};
}
}
```
### 自定义类型 ### 自定义类型
根据`type`属性定义不同类型的输入框 根据`type`属性定义不同类型的输入框,默认值为`text`
```html ```html
<van-cell-group> <!-- type="text" 可以输入任意文本 -->
<van-field <van-field v-model="text" label="文本" />
v-model="username"
required
clearable
label="用户名"
right-icon="question-o"
placeholder="请输入用户名"
@click-right-icon="$toast('question')"
/>
<van-field <!-- type="password" 表示输入密码 -->
v-model="password" <van-field v-model="password" type="password" label="密码" />
type="password"
label="密码" <!-- type="number" 允许输入数字和小数点,调起全键盘 -->
placeholder="请输入密码" <van-field v-model="number" type="number" label="数字" />
required
/> <!-- type="tel" 输入手机号,调起手机号键盘 -->
</van-cell-group> <van-field v-model="tel" type="tel" label="手机号" />
```
```js
export default {
data() {
return {
tel: '',
text: '',
number: '',
password: ''
};
}
}
``` ```
### 禁用输入框 ### 禁用输入框
@ -56,9 +72,8 @@ Vue.use(Field);
```html ```html
<van-cell-group> <van-cell-group>
<van-field <van-field
label="文本"
value="输入框已禁用" value="输入框已禁用"
label="用户名"
left-icon="contact"
disabled disabled
/> />
</van-cell-group> </van-cell-group>

View File

@ -2,20 +2,16 @@
<demo-section> <demo-section>
<demo-block :title="$t('basicUsage')"> <demo-block :title="$t('basicUsage')">
<van-cell-group> <van-cell-group>
<van-field v-model="value" :placeholder="$t('usernamePlaceholder')" /> <van-field v-model="value" :placeholder="$t('textPlaceholder')" />
</van-cell-group> </van-cell-group>
</demo-block> </demo-block>
<demo-block :title="$t('title2')"> <demo-block :title="$t('title2')">
<van-cell-group> <van-cell-group>
<van-field <van-field
v-model="username" v-model="text"
:label="$t('username')" :label="$t('text')"
:placeholder="$t('usernamePlaceholder')" :placeholder="$t('textPlaceholder')"
required
clearable
right-icon="question-o"
@click-right-icon="$toast('question')"
/> />
<van-field <van-field
@ -23,32 +19,40 @@
type="password" type="password"
:label="$t('password')" :label="$t('password')"
:placeholder="$t('passwordPlaceholder')" :placeholder="$t('passwordPlaceholder')"
required />
<van-field
v-model="number"
type="number"
:label="$t('number')"
:placeholder="$t('numberPlaceholder')"
/>
<van-field
v-model="phone"
type="tel"
:label="$t('phone')"
:placeholder="$t('phonePlaceholder')"
/> />
</van-cell-group> </van-cell-group>
</demo-block> </demo-block>
<demo-block :title="$t('title3')"> <demo-block :title="$t('title3')">
<van-cell-group> <van-cell-group>
<van-field <van-field :value="$t('inputDisabled')" :label="$t('text')" disabled />
:value="$t('inputDisabled')"
:label="$t('username')"
left-icon="contact"
disabled
/>
</van-cell-group> </van-cell-group>
</demo-block> </demo-block>
<demo-block :title="$t('title4')"> <demo-block :title="$t('title4')">
<van-cell-group> <van-cell-group>
<van-field <van-field
v-model="username2" v-model="username"
:label="$t('username')" :label="$t('username')"
:placeholder="$t('usernamePlaceholder')" :placeholder="$t('usernamePlaceholder')"
error error
/> />
<van-field <van-field
v-model="phone" v-model="phone2"
:label="$t('phone')" :label="$t('phone')"
:placeholder="$t('phonePlaceholder')" :placeholder="$t('phonePlaceholder')"
:error-message="$t('phoneError')" :error-message="$t('phoneError')"
@ -112,13 +116,18 @@ export default {
title3: '禁用输入框', title3: '禁用输入框',
title4: '错误提示', title4: '错误提示',
title6: '插入按钮', title6: '插入按钮',
message: '留言',
phone: '手机号',
sms: '短信验证码', sms: '短信验证码',
tel: '手机号',
text: '文本',
phone: '手机号',
number: '数字',
message: '留言',
sendSMS: '发送验证码', sendSMS: '发送验证码',
showWordLimit: '显示字数统计', showWordLimit: '显示字数统计',
textareaAutosize: '高度自适应', textareaAutosize: '高度自适应',
smsPlaceholder: '请输入短信验证码', smsPlaceholder: '请输入短信验证码',
textPlaceholder: '请输入文本',
numberPlaceholder: '请输入数字',
phonePlaceholder: '请输入手机号', phonePlaceholder: '请输入手机号',
messagePlaceholder: '请输入留言', messagePlaceholder: '请输入留言',
inputDisabled: '输入框已禁用', inputDisabled: '输入框已禁用',
@ -129,14 +138,19 @@ export default {
title3: 'Disabled', title3: 'Disabled',
title4: 'Error Info', title4: 'Error Info',
title6: 'Insert Button', title6: 'Insert Button',
message: 'Message',
phone: 'Phone',
sms: 'SMS', sms: 'SMS',
tel: 'Tel',
text: 'Text',
phone: 'Phone',
number: 'Number',
message: 'Message',
sendSMS: 'Send SMS', sendSMS: 'Send SMS',
showWordLimit: 'Show Word Limit', showWordLimit: 'Show Word Limit',
textareaAutosize: 'Auto Resize', textareaAutosize: 'Auto Resize',
smsPlaceholder: 'SMS', smsPlaceholder: 'SMS',
textPlaceholder: 'Text',
phonePlaceholder: 'Phone', phonePlaceholder: 'Phone',
numberPlaceholder: 'Number',
messagePlaceholder: 'Message', messagePlaceholder: 'Message',
inputDisabled: 'Disabled', inputDisabled: 'Disabled',
phoneError: 'Invalid phone' phoneError: 'Invalid phone'
@ -146,13 +160,16 @@ export default {
data() { data() {
return { return {
sms: '', sms: '',
text: '',
value: '', value: '',
number: '',
password: '', password: '',
username: '', username: '',
username2: '', username2: '',
message: '', message: '',
message2: '', message2: '',
phone: '1365577' phone: '',
phone2: '12345'
}; };
} }
}; };

View File

@ -7,6 +7,16 @@ import { createNamespace, isObj, isDef, addUnit } from '../utils';
const [createComponent, bem] = createNamespace('field'); const [createComponent, bem] = createNamespace('field');
function formatNumber(value) {
const dotIndex = value.indexOf('.');
if (dotIndex > -1) {
value = value.slice(0, dotIndex + 1) + value.slice(dotIndex).replace(/\./g, '');
}
return value.replace(/[^0-9.]/g, '');
}
export default createComponent({ export default createComponent({
inheritAttrs: false, inheritAttrs: false,
@ -111,6 +121,16 @@ export default createComponent({
target.value = value; target.value = value;
} }
if (this.type === 'number') {
const originValue = value;
value = formatNumber(value);
if (value !== originValue) {
target.value = value;
}
}
return value; return value;
}, },
@ -159,19 +179,6 @@ export default createComponent({
}, },
onKeypress(event) { onKeypress(event) {
if (this.type === 'number') {
const { keyCode } = event;
const allowPoint = String(this.value).indexOf('.') === -1;
const isValidKey =
(keyCode >= 48 && keyCode <= 57) ||
(keyCode === 46 && allowPoint) ||
keyCode === 45;
if (!isValidKey) {
preventDefault(event);
}
}
// trigger blur after click keyboard search button // trigger blur after click keyboard search button
/* istanbul ignore next */ /* istanbul ignore next */
if (this.type === 'search' && event.keyCode === 13) { if (this.type === 'search' && event.keyCode === 13) {
@ -209,11 +216,7 @@ export default createComponent({
const inputSlot = this.slots('input'); const inputSlot = this.slots('input');
if (inputSlot) { if (inputSlot) {
return ( return <div class={bem('control', this.inputAlign)}>{inputSlot}</div>;
<div class={bem('control', this.inputAlign)}>
{inputSlot}
</div>
);
} }
const inputProps = { const inputProps = {
@ -240,7 +243,10 @@ export default createComponent({
return <textarea {...inputProps} />; return <textarea {...inputProps} />;
} }
return <input type={this.type} {...inputProps} />; // type="number" is weired in iOS
const inputType = this.type === 'number' ? 'text' : this.type;
return <input type={inputType} {...inputProps} />;
}, },
genLeftIcon() { genLeftIcon() {
@ -311,10 +317,16 @@ export default createComponent({
<div class={bem('body')}> <div class={bem('body')}>
{this.genInput()} {this.genInput()}
{this.showClear && ( {this.showClear && (
<Icon name="clear" class={bem('clear')} onTouchstart={this.onClear} /> <Icon
name="clear"
class={bem('clear')}
onTouchstart={this.onClear}
/>
)} )}
{this.genRightIcon()} {this.genRightIcon()}
{slots('button') && <div class={bem('button')}>{slots('button')}</div>} {slots('button') && (
<div class={bem('button')}>{slots('button')}</div>
)}
</div> </div>
{this.genWordLimit()} {this.genWordLimit()}
{this.errorMessage && ( {this.errorMessage && (

View File

@ -6,26 +6,7 @@ exports[`renders demo correctly 1`] = `
<div class="van-cell-group van-hairline--top-bottom"> <div class="van-cell-group van-hairline--top-bottom">
<div class="van-cell van-field"> <div class="van-cell van-field">
<div class="van-cell__value van-cell__value--alone"> <div class="van-cell__value van-cell__value--alone">
<div class="van-field__body"><input type="text" placeholder="请输入用户名" class="van-field__control"></div> <div class="van-field__body"><input type="text" placeholder="请输入文本" class="van-field__control"></div>
</div>
</div>
</div>
</div>
<div>
<div class="van-cell-group van-hairline--top-bottom">
<div class="van-cell van-cell--required van-field">
<div class="van-cell__title van-field__label"><span>用户名</span></div>
<div class="van-cell__value">
<div class="van-field__body"><input type="text" placeholder="请输入用户名" class="van-field__control">
<div class="van-field__right-icon"><i class="van-icon van-icon-question-o">
<!----></i></div>
</div>
</div>
</div>
<div class="van-cell van-cell--required van-field">
<div class="van-cell__title van-field__label"><span>密码</span></div>
<div class="van-cell__value">
<div class="van-field__body"><input type="password" placeholder="请输入密码" class="van-field__control"></div>
</div> </div>
</div> </div>
</div> </div>
@ -33,9 +14,35 @@ exports[`renders demo correctly 1`] = `
<div> <div>
<div class="van-cell-group van-hairline--top-bottom"> <div class="van-cell-group van-hairline--top-bottom">
<div class="van-cell van-field"> <div class="van-cell van-field">
<div class="van-field__left-icon"><i class="van-icon van-icon-contact"> <div class="van-cell__title van-field__label"><span>文本</span></div>
<!----></i></div> <div class="van-cell__value">
<div class="van-cell__title van-field__label"><span>用户名</span></div> <div class="van-field__body"><input type="text" placeholder="请输入文本" class="van-field__control"></div>
</div>
</div>
<div class="van-cell van-field">
<div class="van-cell__title van-field__label"><span>密码</span></div>
<div class="van-cell__value">
<div class="van-field__body"><input type="password" placeholder="请输入密码" class="van-field__control"></div>
</div>
</div>
<div class="van-cell van-field">
<div class="van-cell__title van-field__label"><span>数字</span></div>
<div class="van-cell__value">
<div class="van-field__body"><input type="text" placeholder="请输入数字" class="van-field__control"></div>
</div>
</div>
<div class="van-cell van-field">
<div class="van-cell__title van-field__label"><span>手机号</span></div>
<div class="van-cell__value">
<div class="van-field__body"><input type="tel" placeholder="请输入手机号" class="van-field__control"></div>
</div>
</div>
</div>
</div>
<div>
<div class="van-cell-group van-hairline--top-bottom">
<div class="van-cell van-field">
<div class="van-cell__title van-field__label"><span>文本</span></div>
<div class="van-cell__value"> <div class="van-cell__value">
<div class="van-field__body"><input type="text" disabled="disabled" class="van-field__control"></div> <div class="van-field__body"><input type="text" disabled="disabled" class="van-field__control"></div>
</div> </div>

View File

@ -33,7 +33,7 @@ test('click icon event', () => {
expect(wrapper.emitted('click-right-icon')[0][0]).toBeTruthy(); expect(wrapper.emitted('click-right-icon')[0][0]).toBeTruthy();
}); });
test('keypress event', () => { test('number type', () => {
const wrapper = mount(Field, { const wrapper = mount(Field, {
propsData: { propsData: {
value: '', value: '',
@ -41,26 +41,19 @@ test('keypress event', () => {
} }
}); });
const fn = jest.fn(); const input = wrapper.find('input');
const { calls } = fn.mock;
const press = keyCode => wrapper.vm.onKeypress({
keyCode,
preventDefault: fn
});
press(0); input.element.value = '1';
expect(calls.length).toBe(1); input.trigger('input');
expect(wrapper.emitted('input')[0][0]).toEqual('1');
press(50); input.element.value = '1.2.';
expect(calls.length).toBe(1); input.trigger('input');
expect(wrapper.emitted('input')[1][0]).toEqual('1.2');
wrapper.setProps({ value: '0.1' }); input.element.value = '123abc';
press(46); input.trigger('input');
expect(calls.length).toBe(2); expect(wrapper.emitted('input')[2][0]).toEqual('123');
wrapper.setProps({ type: 'text' });
press(0);
expect(calls.length).toBe(2);
}); });
test('render textarea', async () => { test('render textarea', async () => {