Skip to content

Commit

Permalink
feat: mount method on FormApi (#496)
Browse files Browse the repository at this point in the history
* feat: mount method on FormApi

* fix solid-form test case

* fix: added form.mount() to tests
  • Loading branch information
aadito123 authored Nov 1, 2023
1 parent 79ce230 commit 9862684
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 17 deletions.
14 changes: 12 additions & 2 deletions packages/form-core/src/FormApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ export type FormOptions<TData, ValidatorType> = {
asyncDebounceMs?: number
validator?: ValidatorType
onMount?: ValidateOrFn<TData, ValidatorType>
onMountAsync?: ValidateAsyncFn<TData, ValidatorType>
onMountAsyncDebounceMs?: number
onChange?: ValidateOrFn<TData, ValidatorType>
onChangeAsync?: ValidateAsyncFn<TData, ValidatorType>
onChangeAsyncDebounceMs?: number
Expand Down Expand Up @@ -170,6 +168,18 @@ export class FormApi<TFormData, ValidatorType> {
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<TFormData>)().validate(
this.state.values,
this.options.onMount,
)
}
}

update = (options?: FormOptions<TFormData, ValidatorType>) => {
if (!options) return

Expand Down
30 changes: 16 additions & 14 deletions packages/form-core/src/tests/FormApi.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {},
Expand All @@ -31,7 +31,7 @@ describe('form api', () => {
name: 'test',
},
})

form.mount()
expect(form.state).toEqual({
values: {
name: 'test',
Expand All @@ -58,7 +58,7 @@ describe('form api', () => {
submissionAttempts: 30,
},
})

form.mount()
expect(form.state).toEqual({
values: {},
fieldMeta: {},
Expand All @@ -83,7 +83,7 @@ describe('form api', () => {
name: 'test',
},
})

form.mount()
form.update({
defaultValues: {
name: 'other',
Expand Down Expand Up @@ -119,7 +119,7 @@ describe('form api', () => {
name: 'test',
},
})

form.mount()
form.setFieldValue('name', 'other')
form.state.submissionAttempts = 300

Expand Down Expand Up @@ -151,7 +151,7 @@ describe('form api', () => {
name: 'test',
},
})

form.mount()
expect(form.getFieldValue('name')).toEqual('test')
})

Expand All @@ -161,7 +161,7 @@ describe('form api', () => {
name: 'test',
},
})

form.mount()
form.setFieldValue('name', 'other')

expect(form.getFieldValue('name')).toEqual('other')
Expand All @@ -173,7 +173,7 @@ describe('form api', () => {
names: ['test'],
},
})

form.mount()
form.pushFieldValue('names', 'other')

expect(form.getFieldValue('names')).toStrictEqual(['test', 'other'])
Expand All @@ -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'])
Expand All @@ -197,7 +197,7 @@ describe('form api', () => {
names: ['one', 'two', 'three'],
},
})

form.mount()
form.removeFieldValue('names', 1)

expect(form.getFieldValue('names')).toStrictEqual(['one', 'three'])
Expand All @@ -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'])
Expand All @@ -221,7 +221,7 @@ describe('form api', () => {
name: 'test',
},
})

form.mount()
form.setFieldValue('name', 'other')

expect(form.getFieldValue('name')).toEqual('other')
Expand All @@ -237,7 +237,7 @@ describe('form api', () => {
name: 'test',
},
})

form.mount()
expect(form.getFieldValue('name')).toEqual('test')

form.update({
Expand All @@ -255,7 +255,7 @@ describe('form api', () => {
name: 'one',
},
})

form.mount()
expect(form.getFieldValue('name')).toEqual('one')

form.setFieldValue('name', 'two', { touch: true })
Expand Down Expand Up @@ -297,6 +297,8 @@ describe('form api', () => {
onChange: (v) => (v.length > 0 ? undefined : 'required'),
})

form.mount()

field.mount()

field.handleChange('one')
Expand Down
33 changes: 33 additions & 0 deletions packages/react-form/src/tests/useForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 ? (
<form.Provider>
<h1>{formMounted ? 'Form mounted' : 'Not mounted'}</h1>
</form.Provider>
) : (
<button onClick={() => setMountForm(true)}>Mount form</button>
)}
</>
)
}

const { getByText } = render(<Comp />)
await user.click(getByText('Mount form'))
await waitFor(() => expect(getByText('Form mounted')).toBeInTheDocument())
})
})
1 change: 1 addition & 0 deletions packages/react-form/src/useForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export function useForm<TData, FormValidator>(
const api = new FormApi<TData>(opts)

api.Provider = function Provider(props) {
useIsomorphicLayoutEffect(formApi.mount, [])
return <formContext.Provider {...props} value={{ formApi: api }} />
}
api.Field = Field as any
Expand Down
3 changes: 2 additions & 1 deletion packages/solid-form/src/createForm.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -35,6 +35,7 @@ export function createForm<TData, FormValidator>(
const formApi = new FormApi<TData, FormValidator>(options)

formApi.Provider = function Provider(props) {
onMount(formApi.mount)
return <formContext.Provider {...props} value={{ formApi: formApi }} />
}
formApi.Field = Field as any
Expand Down
34 changes: 34 additions & 0 deletions packages/solid-form/src/tests/createForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -113,4 +114,37 @@ describe('createForm', () => {
await user.click(getByText('Submit'))
expect(submittedData?.firstName).toEqual('OtherName')
})

it('should run on form mount', async () => {
const [formMounted, setFormMounted] = createSignal(false)
const [mountForm, setMountForm] = createSignal(false)
function Comp() {
const form = createForm(() => ({
defaultValues: {
firstName: 'FirstName',
},
onMount: () => {
setFormMounted(true)
return undefined
},
}))

return (
<Show
when={mountForm()}
fallback={
<button onClick={() => setMountForm(true)}>Mount form</button>
}
>
<form.Provider>
<h1>Form mounted</h1>
</form.Provider>
</Show>
)
}

const { getByText } = render(() => <Comp />)
await user.click(getByText('Mount form'))
expect(formMounted()).toBe(true)
})
})
31 changes: 31 additions & 0 deletions packages/vue-form/src/tests/useForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 ? (
<form.Provider>
<h1>{formMounted.value ? 'Form mounted' : 'Not mounted'}</h1>
</form.Provider>
) : (
<button onClick={() => (mountForm.value = true)}>Mount form</button>
)
})

const { getByText, findByText } = render(Comp)
await user.click(getByText('Mount form'))
expect(await findByText('Form mounted')).toBeInTheDocument()
})
})
2 changes: 2 additions & 0 deletions packages/vue-form/src/useForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
type SlotsType,
type SetupContext,
defineComponent,
onMounted,
} from 'vue-demi'

declare module '@tanstack/form-core' {
Expand Down Expand Up @@ -39,6 +40,7 @@ export function useForm<TData, FormValidator>(

api.Provider = defineComponent(
(_, context) => {
onMounted(formApi.mount)
provideFormContext({ formApi: formApi as never })
return () => context.slots.default!()
},
Expand Down

0 comments on commit 9862684

Please sign in to comment.