diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index 9c0b7bdb4..6f3fd36b0 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -2061,6 +2061,31 @@ export class FormApi< ), } } + + /** + * Sets the metadata for all fields in the form. + */ + setAllFieldsMeta = ( + updater: Updater, + ) => { + this.baseStore.setState((prev) => { + const updatedFieldMetaBase = Object.keys(prev.fieldMetaBase).reduce( + (acc, key) => { + acc[key as DeepKeys] = functionalUpdate( + updater, + prev.fieldMetaBase[key as DeepKeys] as never, + ) + return acc + }, + {} as Record, AnyFieldMetaBase>, + ) + + return { + ...prev, + fieldMetaBase: updatedFieldMetaBase, + } + }) + } } function normalizeError(rawError?: FormValidationError): { diff --git a/packages/form-core/tests/FormApi.spec.ts b/packages/form-core/tests/FormApi.spec.ts index bc0e0f70a..bcdbeff90 100644 --- a/packages/form-core/tests/FormApi.spec.ts +++ b/packages/form-core/tests/FormApi.spec.ts @@ -2761,129 +2761,166 @@ describe('form api', () => { field1.setValue('Interest 2') expect(interestsField.state.meta.errors).toStrictEqual([]) }) -}) - -it('should not change the onBlur state of the fields when the form is submitted', async () => { - const form = new FormApi({ - defaultValues: { - firstName: '', - lastName: '', - }, - }) - const firstNameField = new FieldApi({ - form, - name: 'firstName', - }) - firstNameField.mount() + it('should not change the onBlur state of the fields when the form is submitted', async () => { + const form = new FormApi({ + defaultValues: { + firstName: '', + lastName: '', + }, + }) - const lastNameField = new FieldApi({ - form, - name: 'lastName', - }) - lastNameField.mount() + const firstNameField = new FieldApi({ + form, + name: 'firstName', + }) + firstNameField.mount() - firstNameField.handleBlur() + const lastNameField = new FieldApi({ + form, + name: 'lastName', + }) + lastNameField.mount() - expect(firstNameField.state.meta.isBlurred).toBe(true) + firstNameField.handleBlur() - await form.handleSubmit() + expect(firstNameField.state.meta.isBlurred).toBe(true) - expect(firstNameField.state.meta.isBlurred).toBe(true) - expect(lastNameField.state.meta.isBlurred).toBe(false) -}) + await form.handleSubmit() -it('should pass the handleSubmit meta data to onSubmit', async () => { - const form = new FormApi({ - onSubmitMeta: {} as { dinosaur: string }, - onSubmit: async ({ meta }) => { - expect(meta.dinosaur).toEqual('Stegosaurus') - }, + expect(firstNameField.state.meta.isBlurred).toBe(true) + expect(lastNameField.state.meta.isBlurred).toBe(false) }) - await form.handleSubmit({ dinosaur: 'Stegosaurus' }) -}) + it('should pass the handleSubmit meta data to onSubmit', async () => { + const form = new FormApi({ + onSubmitMeta: {} as { dinosaur: string }, + onSubmit: async ({ meta }) => { + expect(meta.dinosaur).toEqual('Stegosaurus') + }, + }) -it('should pass the handleSubmit default meta data to onSubmit', async () => { - const form = new FormApi({ - onSubmitMeta: { dinosaur: 'Frank' } as { dinosaur: string }, - onSubmit: async ({ meta }) => { - expect(meta.dinosaur).toEqual('Frank') - }, + await form.handleSubmit({ dinosaur: 'Stegosaurus' }) }) - await form.handleSubmit() -}) + it('should pass the handleSubmit default meta data to onSubmit', async () => { + const form = new FormApi({ + onSubmitMeta: { dinosaur: 'Frank' } as { dinosaur: string }, + onSubmit: async ({ meta }) => { + expect(meta.dinosaur).toEqual('Frank') + }, + }) -it('should read and update union objects', async () => { - const form = new FormApi({ - defaultValues: { - person: { firstName: 'firstName' }, - } as { person?: { firstName: string } | { age: number } | null }, + await form.handleSubmit() }) - const field = new FieldApi({ - form, - name: 'person.firstName', - }) - field.mount() - expect(field.getValue()).toStrictEqual('firstName') + it('should read and update union objects', async () => { + const form = new FormApi({ + defaultValues: { + person: { firstName: 'firstName' }, + }, + } as { person?: { firstName: string } | { age: number } | null }) - form.setFieldValue('person', { age: 0 }) + const field = new FieldApi({ + form, + name: 'person.firstName', + }) + field.mount() + expect(field.getValue()).toStrictEqual('firstName') - const field2 = new FieldApi({ - form, - name: 'person.age', - }) - field2.mount() - expect(field2.getValue()).toStrictEqual(0) -}) + form.setFieldValue('person', { age: 0 }) -it('should update isSubmitSuccessful correctly during form submission', async () => { - const onSubmit = vi.fn().mockResolvedValue(undefined) - const form = new FormApi({ - defaultValues: { - name: 'test', - }, - onSubmit, + const field2 = new FieldApi({ + form, + name: 'person.age', + }) + field2.mount() + expect(field2.getValue()).toStrictEqual(0) }) - form.mount() + it('should update isSubmitSuccessful correctly during form submission', async () => { + const onSubmit = vi.fn().mockResolvedValue(undefined) + const form = new FormApi({ + defaultValues: { + name: 'test', + }, + onSubmit, + }) - expect(form.state.isSubmitSuccessful).toBe(false) + form.mount() - await form.handleSubmit() + expect(form.state.isSubmitSuccessful).toBe(false) - expect(form.state.isSubmitSuccessful).toBe(true) - expect(onSubmit).toHaveBeenCalledTimes(1) + await form.handleSubmit() - // Simulate a failed submission - onSubmit.mockRejectedValueOnce(new Error('Submission failed')) + expect(form.state.isSubmitSuccessful).toBe(true) + expect(onSubmit).toHaveBeenCalledTimes(1) - await expect(form.handleSubmit()).rejects.toThrow('Submission failed') + // Simulate a failed submission + onSubmit.mockRejectedValueOnce(new Error('Submission failed')) - expect(form.state.isSubmitSuccessful).toBe(false) -}) + await expect(form.handleSubmit()).rejects.toThrow('Submission failed') -it('should reset the fields value and meta to default state', async () => { - const form = new FormApi({ - defaultValues: { - name: 'tony', - } as { name: string }, + expect(form.state.isSubmitSuccessful).toBe(false) }) - form.mount() - const field = new FieldApi({ - form, - name: 'name', + + it('should reset the fields value and meta to default state', async () => { + const form = new FormApi({ + defaultValues: { + name: 'tony', + } as { name: string }, + }) + form.mount() + const field = new FieldApi({ + form, + name: 'name', + }) + + field.mount() + field.setValue('hawk') + + expect(form.state.values.name).toStrictEqual('hawk') + expect(field.state.meta.isTouched).toBe(true) + + form.resetField('name') + expect(form.state.values.name).toStrictEqual('tony') + expect(field.state.meta.isTouched).toBe(false) }) - field.mount() - field.setValue('hawk') + it('should set all fields meta', () => { + const form = new FormApi({ + defaultValues: { + name: 'test', + age: 30, + }, + }) + form.mount() - expect(form.state.values.name).toStrictEqual('hawk') - expect(field.state.meta.isTouched).toBe(true) + const nameField = new FieldApi({ + form, + name: 'name', + }) + nameField.mount() + + const ageField = new FieldApi({ + form, + name: 'age', + }) + ageField.mount() - form.resetField('name') - expect(form.state.values.name).toStrictEqual('tony') - expect(field.state.meta.isTouched).toBe(false) + form.setAllFieldsMeta((prev) => ({ + ...prev, + isTouched: true, + isBlurred: true, + isDirty: true, + })) + + expect(nameField.state.meta.isTouched).toBe(true) + expect(nameField.state.meta.isBlurred).toBe(true) + expect(nameField.state.meta.isDirty).toBe(true) + + expect(ageField.state.meta.isTouched).toBe(true) + expect(ageField.state.meta.isBlurred).toBe(true) + expect(ageField.state.meta.isDirty).toBe(true) + }) })