diff --git a/packages/vant-use/src/useCountDown/index.ts b/packages/vant-use/src/useCountDown/index.ts index 25f5176e6..bdb484118 100644 --- a/packages/vant-use/src/useCountDown/index.ts +++ b/packages/vant-use/src/useCountDown/index.ts @@ -5,7 +5,7 @@ import { onDeactivated, onBeforeUnmount, } from 'vue'; -import { raf, cancelRaf } from '../utils'; +import { raf, cancelRaf, inBrowser } from '../utils'; export type CurrentTime = { days: number; @@ -106,6 +106,12 @@ export function useCountDown(options: UseCountDownOptions) { }; const tick = () => { + // should not start counting in server + // see: https://github.com/youzan/vant/issues/7807 + if (!inBrowser) { + return; + } + if (options.millisecond) { microTick(); } else { diff --git a/src/form/README.md b/src/form/README.md index ff22456c6..5b9b1d767 100644 --- a/src/form/README.md +++ b/src/form/README.md @@ -516,8 +516,8 @@ Use [ref](https://v3.vuejs.org/guide/component-template-refs.html) to get Form i | Name | Description | Attribute | Return value | | --- | --- | --- | --- | | submit | Submit form | - | - | -| validate | Validate form | _name?: string_ | _Promise_ | -| resetValidation | Reset validation | _name?: string_ | - | +| validate | Validate form | _name?: string \| string[]_ | _Promise_ | +| resetValidation | Reset validation | _name?: string \| string[]_ | - | | scrollToField `v2.8.3` | Scroll to field | _name: string, alignToTop: boolean_ | - | ### Slots diff --git a/src/form/README.zh-CN.md b/src/form/README.zh-CN.md index d8bbab979..d258cedca 100644 --- a/src/form/README.zh-CN.md +++ b/src/form/README.zh-CN.md @@ -555,8 +555,8 @@ export default { | 方法名 | 说明 | 参数 | 返回值 | | --- | --- | --- | --- | | submit | 提交表单,与点击提交按钮的效果等价 | - | - | -| validate | 验证表单,支持传入`name`来验证单个表单项 | _name?: string_ | _Promise_ | -| resetValidation | 重置表单项的验证提示,支持传入`name`来重置单个表单项 | _name?: string_ | - | +| validate | 验证表单,支持传入 `name` 来验证单个或部分表单项 | _name?: string \| string[]_ | _Promise_ | +| resetValidation | 重置表单项的验证提示,支持传入 `name` 来重置单个或部分表单项 | _name?: string \| string[]_ | - | | scrollToField `v2.8.3` | 滚动到对应表单项的位置,默认滚动到顶部,第二个参数传 false 可滚动至底部 | _name: string, alignToTop: boolean_ | - | ### Slots diff --git a/src/form/index.js b/src/form/index.js index 1dc808e02..4e1c5d9b9 100644 --- a/src/form/index.js +++ b/src/form/index.js @@ -37,11 +37,19 @@ export default createComponent({ setup(props, { emit, slots }) { const { children, linkChildren } = useChildren(FORM_KEY); - const validateSeq = () => + const getFieldsByNames = (names) => { + if (names) { + return children.filter((field) => names.indexOf(field.name) !== -1); + } + return children; + }; + + const validateSeq = (names) => new Promise((resolve, reject) => { const errors = []; + const fields = getFieldsByNames(names); - children + fields .reduce( (promise, field) => promise.then(() => { @@ -64,9 +72,10 @@ export default createComponent({ }); }); - const validateAll = () => + const validateAll = (names) => new Promise((resolve, reject) => { - Promise.all(children.map((item) => item.validate())).then((errors) => { + const fields = getFieldsByNames(names); + Promise.all(fields.map((item) => item.validate())).then((errors) => { errors = errors.filter((item) => item); if (errors.length) { @@ -96,17 +105,20 @@ export default createComponent({ }; const validate = (name) => { - if (name) { + if (name && !Array.isArray(name)) { return validateField(name); } - return props.validateFirst ? validateSeq() : validateAll(); + return props.validateFirst ? validateSeq(name) : validateAll(name); }; const resetValidation = (name) => { - children.forEach((item) => { - if (!name || item.name === name) { - item.resetValidation(); - } + if (name && !Array.isArray(name)) { + name = [name]; + } + + const fields = getFieldsByNames(name); + fields.forEach((item) => { + item.resetValidation(); }); }; diff --git a/src/form/test/methods.legacy.js b/src/form/test/methods.legacy.js index 5acb4549b..a91700c9e 100644 --- a/src/form/test/methods.legacy.js +++ b/src/form/test/methods.legacy.js @@ -37,7 +37,7 @@ test('validate method - validate all fields', (done) => { }); }); -test('validate method - validate one field and passed', (done) => { +test('validate method - validate one field and failed', (done) => { mountSimpleRulesForm({ mounted() { this.$refs.form.validate('A').catch((err) => { @@ -48,7 +48,7 @@ test('validate method - validate one field and passed', (done) => { }); }); -test('validate method - validate one field and failed', (done) => { +test('validate method - validate one field and passed', (done) => { mountForm({ template: ` @@ -64,6 +64,25 @@ test('validate method - validate one field and failed', (done) => { }); }); +test('validate method - validate two fields and failed', (done) => { + mountForm({ + template: ` + + + + + + `, + data: getSimpleRules, + mounted() { + this.$refs.form.validate(['A', 'B']).catch((error) => { + expect(error).toEqual([{ message: 'B failed', name: 'B' }]); + done(); + }); + }, + }); +}); + test('validate method - unexisted name', (done) => { mountSimpleRulesForm({ mounted() { @@ -85,6 +104,19 @@ test('resetValidation method - reset all fields', (done) => { }); }); +test('resetValidation method - reset two fields', (done) => { + const wrapper = mountSimpleRulesForm({ + mounted() { + this.$refs.form.validate().catch(() => { + this.$refs.form.resetValidation(['A', 'B']); + const errors = wrapper.findAll('.van-field__error-message'); + expect(errors.length).toEqual(0); + done(); + }); + }, + }); +}); + test('resetValidation method - reset one field', (done) => { const wrapper = mountSimpleRulesForm({ mounted() { diff --git a/src/image/index.tsx b/src/image/index.tsx index 812d2abbe..62d553137 100644 --- a/src/image/index.tsx +++ b/src/image/index.tsx @@ -120,7 +120,7 @@ export default createComponent({ }; const renderPlaceholder = () => { - if (loading.value && props.showLoading && inBrowser) { + if (loading.value && props.showLoading) { return
{renderLoadingIcon()}
; } if (error.value && props.showError) { diff --git a/src/stepper/test/index.legacy.js b/src/stepper/test/index.legacy.js index df0d8edb5..41d22efa6 100644 --- a/src/stepper/test/index.legacy.js +++ b/src/stepper/test/index.legacy.js @@ -125,7 +125,7 @@ test('filter value during user input', () => { input.element.value = '2'; input.trigger('input'); expect(input.element.value).toEqual('2'); - expect(wrapper.emitted('input')[1][0]).toEqual('2'); + expect(wrapper.emitted('input')[1][0]).toEqual(2); }); test('shoud watch value and format it', () => { @@ -149,12 +149,11 @@ test('only allow interger', () => { }); const input = wrapper.find('input'); - input.element.value = '1.2'; + input.element.value = '2.2'; input.trigger('input'); input.trigger('blur'); - expect(wrapper.emitted('input')[0][0]).toEqual('1'); - expect(wrapper.emitted('input')[1][0]).toEqual(1); + expect(wrapper.emitted('input')[0][0]).toEqual(2); }); test('input invalid value and blur', () => { @@ -246,8 +245,8 @@ test('async-change prop', () => { 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', () => { @@ -277,6 +276,16 @@ test('show-plus & show-minus props', () => { expect(wrapper.html()).toMatchSnapshot(); }); +test('should hide input when show-input prop is false', () => { + const wrapper = mount(Stepper, { + propsData: { + showInput: false, + }, + }); + + expect(wrapper.find('input').element.style.display).toEqual('none'); +}); + test('decimal-length prop', () => { const wrapper = mount(Stepper, { props: { diff --git a/src/swipe-item/index.js b/src/swipe-item/index.js index 724747dd8..3d1e5334e 100644 --- a/src/swipe-item/index.js +++ b/src/swipe-item/index.js @@ -21,7 +21,9 @@ export default createComponent({ const style = {}; const { vertical } = parent.props; - style[vertical ? 'height' : 'width'] = `${parent.size.value}px`; + if (parent.size.value) { + style[vertical ? 'height' : 'width'] = `${parent.size.value}px`; + } if (state.offset) { style.transform = `translate${vertical ? 'Y' : 'X'}(${state.offset}px)`; diff --git a/src/swipe/index.js b/src/swipe/index.js index 317238c41..16ca44058 100644 --- a/src/swipe/index.js +++ b/src/swipe/index.js @@ -110,13 +110,17 @@ export default createComponent({ const trackStyle = computed(() => { const mainAxis = props.vertical ? 'height' : 'width'; const crossAxis = props.vertical ? 'width' : 'height'; - - return { - [mainAxis]: `${trackSize.value}px`, - [crossAxis]: props[crossAxis] ? `${props[crossAxis]}px` : '', + const style = { transitionDuration: `${state.swiping ? 0 : props.duration}ms`, transform: `translate${props.vertical ? 'Y' : 'X'}(${state.offset}px)`, }; + + if (size.value) { + style[mainAxis] = `${trackSize.value}px`; + style[crossAxis] = props[crossAxis] ? `${props[crossAxis]}px` : ''; + } + + return style; }); const getTargetActive = (pace) => { diff --git a/types/form.d.ts b/types/form.d.ts index 3f11ee7fd..53f5417e0 100644 --- a/types/form.d.ts +++ b/types/form.d.ts @@ -3,9 +3,9 @@ import { VanComponent } from './component'; export class Form extends VanComponent { submit(): void; - validate(name?: string): Promise; + validate(name?: string | string[]): Promise; - resetValidation(name?: string): void; + resetValidation(name?: string | string[]): void; scrollToField(name: string, options?: boolean | ScrollIntoViewOptions): void; }