Skip to content

Commit b980004

Browse files
committed
fix: canSubmit with valibot variant schema
1 parent 2a82baa commit b980004

File tree

3 files changed

+149
-0
lines changed

3 files changed

+149
-0
lines changed

packages/react-form/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
"@tanstack/form-core": "workspace:*",
8585
"@tanstack/react-store": "^0.7.0",
8686
"decode-formdata": "^0.9.0",
87+
"valibot": "^1.1.0",
8788
"devalue": "^5.1.1"
8889
},
8990
"devDependencies": {
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import { expect, it } from 'vitest'
2+
import { render } from '@testing-library/react'
3+
import * as v from 'valibot'
4+
import userEvent from '@testing-library/user-event'
5+
import { useForm } from '../src/useForm'
6+
import type { JSX } from 'react'
7+
8+
it('should canSubmit with valibot variant schema', async () => {
9+
const schema = v.variant('aOrB', [
10+
v.object({
11+
aOrB: v.literal('a'),
12+
a: v.pipe(v.string(), v.nonEmpty()),
13+
b: v.pipe(
14+
v.any(),
15+
v.transform(() => undefined),
16+
),
17+
}),
18+
v.object({
19+
aOrB: v.literal('b'),
20+
b: v.pipe(v.string(), v.nonEmpty()),
21+
a: v.pipe(
22+
v.any(),
23+
v.transform(() => undefined),
24+
),
25+
}),
26+
])
27+
28+
const Component = () => {
29+
const form = useForm({
30+
defaultValues: {
31+
aOrB: 'a',
32+
a: '', // invalid initial value
33+
b: '', // invalid initial value
34+
},
35+
validators: {
36+
onMount: schema,
37+
onChange: schema,
38+
},
39+
})
40+
41+
return (
42+
<>
43+
<form.Field name="aOrB">
44+
{(field) => (
45+
<>
46+
<input
47+
title={`${field.name} value`}
48+
value={field.state.value}
49+
onChange={(e) => field.handleChange(e.target.value)}
50+
/>
51+
<input
52+
title={`${field.name} isValid`}
53+
value={field.state.meta.isValid ? 'true' : 'false'}
54+
readOnly
55+
/>
56+
</>
57+
)}
58+
</form.Field>
59+
<form.Field name="a">
60+
{(field) => (
61+
<>
62+
<input
63+
title={`${field.name} value`}
64+
value={field.state.value}
65+
onChange={(e) => field.handleChange(e.target.value)}
66+
/>
67+
<input
68+
title={`${field.name} isValid`}
69+
value={field.state.meta.isValid ? 'true' : 'false'}
70+
readOnly
71+
/>
72+
</>
73+
)}
74+
</form.Field>
75+
<form.Field name="b">
76+
{(field) => (
77+
<>
78+
<input
79+
title={`${field.name} value`}
80+
value={field.state.value}
81+
onChange={(e) => field.handleChange(e.target.value)}
82+
/>
83+
<input
84+
title={`${field.name} isValid`}
85+
value={field.state.meta.isValid ? 'true' : 'false'}
86+
readOnly
87+
/>
88+
</>
89+
)}
90+
</form.Field>
91+
<form.Subscribe
92+
selector={(state) => [state.isValid, state.canSubmit] as const}
93+
>
94+
{([isValid, canSubmit]) => (
95+
<>
96+
<input
97+
title="form isValid"
98+
value={isValid ? 'true' : 'false'}
99+
readOnly
100+
/>
101+
<input
102+
title="form canSubmit"
103+
value={canSubmit ? 'true' : 'false'}
104+
/>
105+
</>
106+
)}
107+
</form.Subscribe>
108+
</>
109+
)
110+
}
111+
112+
const { getByTitle, user } = setup(<Component />)
113+
114+
function getInput(name: string) {
115+
return getByTitle(name) as HTMLInputElement
116+
}
117+
118+
// check initial values
119+
expect.soft(getInput('form isValid').value).toBe('false')
120+
expect.soft(getInput('form canSubmit').value).toBe('false')
121+
122+
// choose option "b" and make "b" valid
123+
await user.clear(getInput('aOrB value'))
124+
await user.type(getInput('aOrB value'), 'b')
125+
await user.type(getInput('b value'), 'foobar') // valid input for "b"
126+
127+
// check that values are as expected on the Field-level
128+
expect.soft(getInput('aOrB value').value).toBe('b')
129+
expect.soft(getInput('aOrB isValid').value).toBe('true')
130+
expect.soft(getInput('a value').value).toBe('')
131+
expect.soft(getInput('a isValid').value).toBe('true') // as we chose "b" option, the value of "a" can be "any"thing
132+
expect.soft(getInput('b value').value).toBe('foobar')
133+
expect.soft(getInput('b isValid').value).toBe('true') // "b" is valid now, as it's not empty anymore
134+
135+
// check the state on the Form-level
136+
expect.soft(getInput('form isValid').value).toBe('true')
137+
expect.soft(getInput('form canSubmit').value).toBe('true')
138+
})
139+
140+
function setup(jsx: JSX.Element) {
141+
return {
142+
user: userEvent.setup(),
143+
...render(jsx),
144+
}
145+
}

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)