From f2d23ba0491d6ee68b31495d84cf645992f9ae9a Mon Sep 17 00:00:00 2001 From: JeremyWuuuuu <15975785+JeremyWuuuuu@users.noreply.github.com> Date: Thu, 24 Mar 2022 17:44:01 +0800 Subject: [PATCH] fix(components): [el-form] clear validate after reset fields - Enhancement for #6758 - Add test case against changes --- .../components/form/__tests__/form.spec.tsx | 35 ++++++++++++------- packages/components/form/src/form-item.vue | 21 +++++++++-- 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/packages/components/form/__tests__/form.spec.tsx b/packages/components/form/__tests__/form.spec.tsx index 080330ba6f..6cef04607d 100644 --- a/packages/components/form/__tests__/form.spec.tsx +++ b/packages/components/form/__tests__/form.spec.tsx @@ -28,7 +28,7 @@ describe('Form', () => { installStyle() }) - test('label width', async () => { + it('label width', async () => { const wrapper = mount({ setup() { const form = reactive({ @@ -46,7 +46,7 @@ describe('Form', () => { expect(findStyle(wrapper, '.el-form-item__label').width).toBe('80px') }) - test('auto label width', async () => { + it('auto label width', async () => { const labelPosition = ref('right') const wrapper = mount({ setup() { @@ -100,7 +100,7 @@ describe('Form', () => { expect(marginRight).toEqual(marginRight1) }) - test('form-item auto label width', async () => { + it('form-item auto label width', async () => { const wrapper = mount({ setup() { const form = reactive({ @@ -155,7 +155,7 @@ describe('Form', () => { expect(labelWidth2).toEqual('auto') }) - test('inline form', () => { + it('inline form', () => { const wrapper = mount({ setup() { const form = reactive({ @@ -177,7 +177,7 @@ describe('Form', () => { expect(wrapper.classes()).toContain('el-form--inline') }) - test('label position', () => { + it('label position', () => { const wrapper = mount({ setup() { const form = reactive({ @@ -214,7 +214,7 @@ describe('Form', () => { ) }) - test('label size', () => { + it('label size', () => { const wrapper = mount({ setup() { const form = reactive({ @@ -238,7 +238,7 @@ describe('Form', () => { ) }) - test('show message', (done) => { + it('show message', (done) => { const wrapper = mount({ setup() { const form = reactive({ @@ -277,7 +277,8 @@ describe('Form', () => { }) }) - test('reset field', async () => { + it('reset field', async () => { + jest.useFakeTimers() const form = reactive({ name: '', address: '', @@ -333,13 +334,23 @@ describe('Form', () => { const formRef = wrapper.findComponent({ ref: 'form' }).vm as FormInstance formRef.resetFields() + // first await waits for the validation to be dispatched. + await rAF() + await nextTick() + // after validation dispatched, it will update `validateStateDebounced` with a 100ms delay. + // That's why we put this `jest.runAllTimers` here. + jest.runAllTimers() + // after timer fired, we should wait for the UI to be updated. + await rAF() await nextTick() expect(form.name).toBe('') expect(form.address).toBe('') expect(form.type.length).toBe(0) + expect(wrapper.findAll('.el-form-item__error')).toHaveLength(0) + jest.useRealTimers() }) - test('clear validate', async () => { + it('clear validate', async () => { const wrapper = mount({ setup() { const form = reactive({ @@ -408,7 +419,7 @@ describe('Form', () => { expect(addressField.validateMessage).toBe('') }) - test('scroll to field', () => { + it('scroll to field', () => { const wrapper = mount({ setup() { return () => ( @@ -439,7 +450,7 @@ describe('Form', () => { window.HTMLElement.prototype.scrollIntoView = oldScrollIntoView }) - test('validate return parameters', async () => { + it('validate return parameters', async () => { const form = reactive({ name: 'test', age: '', @@ -501,7 +512,7 @@ describe('Form', () => { expect(res.fields).toBe(undefined) }) - test('validate status', async () => { + it('validate status', async () => { const form = reactive({ age: '20', }) diff --git a/packages/components/form/src/form-item.vue b/packages/components/form/src/form-item.vue index 10fe362533..8528f3d2bc 100644 --- a/packages/components/form/src/form-item.vue +++ b/packages/components/form/src/form-item.vue @@ -44,7 +44,7 @@ import { useSlots, } from 'vue' import AsyncValidator from 'async-validator' -import { clone } from 'lodash-unified' +import { clone, isEqual } from 'lodash-unified' import { refDebounced } from '@vueuse/core' import { addUnit, @@ -89,7 +89,9 @@ const validateState = ref('') const validateStateDebounced = refDebounced(validateState, 100) const validateMessage = ref('') const formItemRef = ref() +// special inline value. let initialValue: any = undefined +let isResettingField = false const labelStyle = computed(() => { if (formContext.labelPosition === 'top') { @@ -251,6 +253,12 @@ const doValidate = async (rules: RuleItem[]): Promise => { } const validate: FormItemContext['validate'] = async (trigger, callback) => { + // skip validation if its resetting + if (isResettingField) { + isResettingField = false + return false + } + const hasCallback = isFunction(callback) if (!validateEnabled.value) { callback?.(false) @@ -286,8 +294,15 @@ const resetField: FormItemContext['resetField'] = async () => { const model = formContext.model if (!model || !props.prop) return - getProp(model, props.prop).value = initialValue - await nextTick() + const computedValue = getProp(model, props.prop) + + if (!isEqual(computedValue.value, initialValue)) { + // prevent validation from being triggered + isResettingField = true + } + + computedValue.value = initialValue + await nextTick() clearValidate() }