From 87c42787c0b4de5a09abe0d29deb92b28b59023e Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Mon, 6 Feb 2023 00:34:39 +0200 Subject: [PATCH] feat(#4117): add resetField on Form/useForm (#4120) --- packages/vee-validate/src/Form.ts | 5 ++ packages/vee-validate/src/types.ts | 11 ++-- packages/vee-validate/src/useForm.ts | 11 ++++ packages/vee-validate/tests/Form.spec.ts | 68 ++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 5 deletions(-) diff --git a/packages/vee-validate/src/Form.ts b/packages/vee-validate/src/Form.ts index 1925baacc..bbf6b4f9b 100644 --- a/packages/vee-validate/src/Form.ts +++ b/packages/vee-validate/src/Form.ts @@ -22,6 +22,7 @@ type FormSlotProps = UnwrapRef< | 'setFieldTouched' | 'setTouched' | 'resetForm' + | 'resetField' | 'controlledValues' > > & { @@ -93,6 +94,7 @@ const FormImpl = defineComponent({ setValues, setFieldTouched, setTouched, + resetField, } = useForm({ validationSchema: validationSchema.value ? validationSchema : undefined, initialValues, @@ -147,6 +149,7 @@ const FormImpl = defineComponent({ setFieldTouched, setTouched, resetForm, + resetField, }; } @@ -161,6 +164,7 @@ const FormImpl = defineComponent({ resetForm, validate, validateField, + resetField, }); return function renderForm() { @@ -204,6 +208,7 @@ export const Form = FormImpl as typeof FormImpl & { setFieldTouched: FormContext['setFieldTouched']; setTouched: FormContext['setTouched']; resetForm: FormContext['resetForm']; + resetField: FormContext['resetField']; validate: FormContext['validate']; validateField: FormContext['validateField']; $slots: { diff --git a/packages/vee-validate/src/types.ts b/packages/vee-validate/src/types.ts index 90e3bd0f3..5f0fa1ade 100644 --- a/packages/vee-validate/src/types.ts +++ b/packages/vee-validate/src/types.ts @@ -129,12 +129,13 @@ export interface SetFieldValueOptions { } export interface FormActions { setFieldValue(field: T, value: TValues[T], opts?: Partial): void; - setFieldError: (field: keyof TValues, message: string | string[] | undefined) => void; - setErrors: (fields: FormErrors) => void; + setFieldError(field: keyof TValues, message: string | string[] | undefined): void; + setErrors(fields: FormErrors): void; setValues(fields: Partial>): void; - setFieldTouched: (field: keyof TValues, isTouched: boolean) => void; - setTouched: (fields: Partial>) => void; - resetForm: (state?: Partial>) => void; + setFieldTouched(field: keyof TValues, isTouched: boolean): void; + setTouched(fields: Partial>): void; + resetForm(state?: Partial>): void; + resetField(field: keyof TValues, state?: Partial): void; } export interface FormValidationResult { diff --git a/packages/vee-validate/src/useForm.ts b/packages/vee-validate/src/useForm.ts index b52153cbb..507ce3615 100644 --- a/packages/vee-validate/src/useForm.ts +++ b/packages/vee-validate/src/useForm.ts @@ -34,6 +34,7 @@ import { PrivateFieldArrayContext, InvalidSubmissionHandler, MapValues, + FieldState, } from './types'; import { getFromPath, @@ -270,6 +271,7 @@ export function useForm = Record = Record = Record) { + const fieldInstance = fieldsByPath.value[field]; + + if (fieldInstance) { + applyFieldMutation(fieldInstance, f => f.resetField(state)); + } + } + /** * Resets all fields */ diff --git a/packages/vee-validate/tests/Form.spec.ts b/packages/vee-validate/tests/Form.spec.ts index 8f719cd2a..4b24d0811 100644 --- a/packages/vee-validate/tests/Form.spec.ts +++ b/packages/vee-validate/tests/Form.spec.ts @@ -2760,6 +2760,74 @@ describe('
', () => { await flushPromises(); expect(value.value).toBe(true); }); + + test('resets a single field resetField() to initial state in slot scope props', async () => { + const wrapper = mountWithHoc({ + template: ` + + + + {{ errors && errors[0] }} + {{ meta.touched }} + + + + `, + }); + + const error = wrapper.$el.querySelector('#error'); + const touched = wrapper.$el.querySelector('#touched'); + const input = wrapper.$el.querySelector('input'); + + expect(error.textContent).toBe(''); + + setValue(input, ''); + await flushPromises(); + expect(error.textContent).toBe(REQUIRED_MESSAGE); + setValue(input, '123'); + dispatchEvent(input, 'blur'); + await flushPromises(); + expect(touched.textContent).toBe('true'); + wrapper.$el.querySelector('button').click(); + await flushPromises(); + expect(error.textContent).toBe(''); + expect(input.value).toBe(''); + expect(touched.textContent).toBe('false'); + }); + + test('resets a single field resetField() to specific state in slot scope props', async () => { + const wrapper = mountWithHoc({ + template: ` + + + + {{ errors && errors[0] }} + {{ meta.touched }} + + + + `, + }); + + const error = wrapper.$el.querySelector('#error'); + const touched = wrapper.$el.querySelector('#touched'); + const input = wrapper.$el.querySelector('input'); + + expect(error.textContent).toBe(''); + + setValue(input, ''); + await flushPromises(); + expect(error.textContent).toBe(REQUIRED_MESSAGE); + setValue(input, '123'); + dispatchEvent(input, 'blur'); + await flushPromises(); + expect(touched.textContent).toBe('true'); + wrapper.$el.querySelector('button').click(); + await flushPromises(); + expect(error.textContent).toBe(''); + expect(input.value).toBe('test'); + expect(touched.textContent).toBe('true'); + }); }); // #3963