From bc83a13cffffb88d9e20204e39ab53adda84674e Mon Sep 17 00:00:00 2001 From: aadito123 <63646058+aadito123@users.noreply.github.com> Date: Tue, 31 Oct 2023 22:26:59 +0800 Subject: [PATCH 1/3] feat: mount method on FormApi --- packages/form-core/src/FormApi.ts | 14 ++++++-- .../react-form/src/tests/useForm.test.tsx | 33 +++++++++++++++++ packages/react-form/src/useForm.tsx | 1 + packages/solid-form/src/createForm.tsx | 3 +- .../solid-form/src/tests/createForm.test.tsx | 35 +++++++++++++++++++ packages/vue-form/src/tests/useForm.test.tsx | 31 ++++++++++++++++ packages/vue-form/src/useForm.tsx | 2 ++ 7 files changed, 116 insertions(+), 3 deletions(-) diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index 24fd363d6..38b811224 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -24,8 +24,6 @@ export type FormOptions = { asyncDebounceMs?: number validator?: ValidatorType onMount?: ValidateOrFn - onMountAsync?: ValidateAsyncFn - onMountAsyncDebounceMs?: number onChange?: ValidateOrFn onChangeAsync?: ValidateAsyncFn onChangeAsyncDebounceMs?: number @@ -170,6 +168,18 @@ export class FormApi { this.update(opts || {}) } + mount = () => { + if (typeof this.options.onMount === 'function') { + return this.options.onMount(this.state.values, this) + } + if (this.options.validator) { + return (this.options.validator as Validator)().validate( + this.state.values, + this.options.onMount, + ) + } + } + update = (options?: FormOptions) => { if (!options) return diff --git a/packages/react-form/src/tests/useForm.test.tsx b/packages/react-form/src/tests/useForm.test.tsx index 8318b35ba..e0088755d 100644 --- a/packages/react-form/src/tests/useForm.test.tsx +++ b/packages/react-form/src/tests/useForm.test.tsx @@ -124,4 +124,37 @@ describe('useForm', () => { expect(getByText('Submitted data: OtherName')).toBeInTheDocument(), ) }) + + it('should run on form mount', async () => { + function Comp() { + const [formMounted, setFormMounted] = React.useState(false) + const [mountForm, setMountForm] = React.useState(false) + + const form = useForm({ + defaultValues: { + firstName: 'FirstName', + }, + onMount: () => { + setFormMounted(true) + return undefined + }, + }) + + return ( + <> + {mountForm ? ( + +

{formMounted ? 'Form mounted' : 'Not mounted'}

+
+ ) : ( + + )} + + ) + } + + const { getByText } = render() + await user.click(getByText('Mount form')) + await waitFor(() => expect(getByText('Form mounted')).toBeInTheDocument()) + }) }) diff --git a/packages/react-form/src/useForm.tsx b/packages/react-form/src/useForm.tsx index 262a5226e..97f07db73 100644 --- a/packages/react-form/src/useForm.tsx +++ b/packages/react-form/src/useForm.tsx @@ -31,6 +31,7 @@ export function useForm( const api = new FormApi(opts) api.Provider = function Provider(props) { + useIsomorphicLayoutEffect(formApi.mount, []) return } api.Field = Field as any diff --git a/packages/solid-form/src/createForm.tsx b/packages/solid-form/src/createForm.tsx index 041d2f3d9..dca7d613b 100644 --- a/packages/solid-form/src/createForm.tsx +++ b/packages/solid-form/src/createForm.tsx @@ -1,6 +1,6 @@ import type { FormOptions, FormState } from '@tanstack/form-core' import { FormApi, functionalUpdate } from '@tanstack/form-core' -import { createComputed, type JSXElement } from 'solid-js' +import { createComputed, onMount, type JSXElement } from 'solid-js' import { useStore } from '@tanstack/solid-store' import { Field, @@ -35,6 +35,7 @@ export function createForm( const formApi = new FormApi(options) formApi.Provider = function Provider(props) { + onMount(formApi.mount) return } formApi.Field = Field as any diff --git a/packages/solid-form/src/tests/createForm.test.tsx b/packages/solid-form/src/tests/createForm.test.tsx index 1cf6485db..3f00c6963 100644 --- a/packages/solid-form/src/tests/createForm.test.tsx +++ b/packages/solid-form/src/tests/createForm.test.tsx @@ -2,6 +2,7 @@ import { render, screen, waitFor } from '@solidjs/testing-library' import '@testing-library/jest-dom' import userEvent from '@testing-library/user-event' import { createFormFactory, createForm } from '..' +import { Show, createSignal } from 'solid-js' const user = userEvent.setup() @@ -113,4 +114,38 @@ describe('createForm', () => { await user.click(getByText('Submit')) expect(submittedData?.firstName).toEqual('OtherName') }) + + it('should run on form mount', async () => { + function Comp() { + const [formMounted, setFormMounted] = createSignal(false) + const [mountForm, setMountForm] = createSignal(false) + + const form = createForm(() => ({ + defaultValues: { + firstName: 'FirstName', + }, + onMount: () => { + setFormMounted(true) + return undefined + }, + })) + + return ( + setMountForm(true)}>Mount form + } + > + +

{formMounted() ? 'Form mounted' : 'Not mounted'}

+
+
+ ) + } + + const { getByText, findByText } = render(() => ) + await user.click(getByText('Mount form')) + expect(await findByText('Form mounted')).toBeInTheDocument() + }) }) diff --git a/packages/vue-form/src/tests/useForm.test.tsx b/packages/vue-form/src/tests/useForm.test.tsx index 92363161d..757f23bd1 100644 --- a/packages/vue-form/src/tests/useForm.test.tsx +++ b/packages/vue-form/src/tests/useForm.test.tsx @@ -138,4 +138,35 @@ describe('useForm', () => { expect(getByText('Submitted data: OtherName')).toBeInTheDocument(), ) }) + + it('should run on form mount', async () => { + const Comp = defineComponent(() => { + const formMounted = ref(false) + const mountForm = ref(false) + + const form = useForm({ + defaultValues: { + firstName: 'FirstName', + }, + onMount: () => { + formMounted.value = true + return undefined + }, + }) + form.provideFormContext() + + return () => + mountForm.value ? ( + +

{formMounted.value ? 'Form mounted' : 'Not mounted'}

+
+ ) : ( + + ) + }) + + const { getByText, findByText } = render(Comp) + await user.click(getByText('Mount form')) + expect(await findByText('Form mounted')).toBeInTheDocument() + }) }) diff --git a/packages/vue-form/src/useForm.tsx b/packages/vue-form/src/useForm.tsx index 359f04615..e6815fd98 100644 --- a/packages/vue-form/src/useForm.tsx +++ b/packages/vue-form/src/useForm.tsx @@ -7,6 +7,7 @@ import { type SlotsType, type SetupContext, defineComponent, + onMounted, } from 'vue-demi' declare module '@tanstack/form-core' { @@ -39,6 +40,7 @@ export function useForm( api.Provider = defineComponent( (_, context) => { + onMounted(formApi.mount) provideFormContext({ formApi: formApi as never }) return () => context.slots.default!() }, From 61f9c18a5def1c2b2b7563656c19825ebb646713 Mon Sep 17 00:00:00 2001 From: aadito123 <63646058+aadito123@users.noreply.github.com> Date: Tue, 31 Oct 2023 22:35:43 +0800 Subject: [PATCH 2/3] fix solid-form test case --- packages/solid-form/src/tests/createForm.test.tsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/solid-form/src/tests/createForm.test.tsx b/packages/solid-form/src/tests/createForm.test.tsx index 3f00c6963..77e30316a 100644 --- a/packages/solid-form/src/tests/createForm.test.tsx +++ b/packages/solid-form/src/tests/createForm.test.tsx @@ -116,10 +116,9 @@ describe('createForm', () => { }) it('should run on form mount', async () => { + const [formMounted, setFormMounted] = createSignal(false) + const [mountForm, setMountForm] = createSignal(false) function Comp() { - const [formMounted, setFormMounted] = createSignal(false) - const [mountForm, setMountForm] = createSignal(false) - const form = createForm(() => ({ defaultValues: { firstName: 'FirstName', @@ -138,14 +137,14 @@ describe('createForm', () => { } > -

{formMounted() ? 'Form mounted' : 'Not mounted'}

+

Form mounted

) } - const { getByText, findByText } = render(() => ) + const { getByText } = render(() => ) await user.click(getByText('Mount form')) - expect(await findByText('Form mounted')).toBeInTheDocument() + expect(formMounted()).toBe(true) }) }) From aa5e5c90f4683bda7e5c9a5f27afc2d8e38df649 Mon Sep 17 00:00:00 2001 From: aadito123 Date: Wed, 1 Nov 2023 10:12:22 +0800 Subject: [PATCH 3/3] fix: added form.mount() to tests --- packages/form-core/src/tests/FormApi.spec.ts | 30 +++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/packages/form-core/src/tests/FormApi.spec.ts b/packages/form-core/src/tests/FormApi.spec.ts index d36d37aee..43a24e1a8 100644 --- a/packages/form-core/src/tests/FormApi.spec.ts +++ b/packages/form-core/src/tests/FormApi.spec.ts @@ -6,7 +6,7 @@ import { FieldApi } from '../FieldApi' describe('form api', () => { it('should get default form state', () => { const form = new FormApi() - + form.mount() expect(form.state).toEqual({ values: {}, fieldMeta: {}, @@ -31,7 +31,7 @@ describe('form api', () => { name: 'test', }, }) - + form.mount() expect(form.state).toEqual({ values: { name: 'test', @@ -58,7 +58,7 @@ describe('form api', () => { submissionAttempts: 30, }, }) - + form.mount() expect(form.state).toEqual({ values: {}, fieldMeta: {}, @@ -83,7 +83,7 @@ describe('form api', () => { name: 'test', }, }) - + form.mount() form.update({ defaultValues: { name: 'other', @@ -119,7 +119,7 @@ describe('form api', () => { name: 'test', }, }) - + form.mount() form.setFieldValue('name', 'other') form.state.submissionAttempts = 300 @@ -151,7 +151,7 @@ describe('form api', () => { name: 'test', }, }) - + form.mount() expect(form.getFieldValue('name')).toEqual('test') }) @@ -161,7 +161,7 @@ describe('form api', () => { name: 'test', }, }) - + form.mount() form.setFieldValue('name', 'other') expect(form.getFieldValue('name')).toEqual('other') @@ -173,7 +173,7 @@ describe('form api', () => { names: ['test'], }, }) - + form.mount() form.pushFieldValue('names', 'other') expect(form.getFieldValue('names')).toStrictEqual(['test', 'other']) @@ -185,7 +185,7 @@ describe('form api', () => { names: ['one', 'two', 'three'], }, }) - + form.mount() form.insertFieldValue('names', 1, 'other') expect(form.getFieldValue('names')).toStrictEqual(['one', 'other', 'three']) @@ -197,7 +197,7 @@ describe('form api', () => { names: ['one', 'two', 'three'], }, }) - + form.mount() form.removeFieldValue('names', 1) expect(form.getFieldValue('names')).toStrictEqual(['one', 'three']) @@ -209,7 +209,7 @@ describe('form api', () => { names: ['one', 'two', 'three'], }, }) - + form.mount() form.swapFieldValues('names', 1, 2) expect(form.getFieldValue('names')).toStrictEqual(['one', 'three', 'two']) @@ -221,7 +221,7 @@ describe('form api', () => { name: 'test', }, }) - + form.mount() form.setFieldValue('name', 'other') expect(form.getFieldValue('name')).toEqual('other') @@ -237,7 +237,7 @@ describe('form api', () => { name: 'test', }, }) - + form.mount() expect(form.getFieldValue('name')).toEqual('test') form.update({ @@ -255,7 +255,7 @@ describe('form api', () => { name: 'one', }, }) - + form.mount() expect(form.getFieldValue('name')).toEqual('one') form.setFieldValue('name', 'two', { touch: true }) @@ -297,6 +297,8 @@ describe('form api', () => { onChange: (v) => (v.length > 0 ? undefined : 'required'), }) + form.mount() + field.mount() field.handleChange('one')