@tanstack/vue-form 0.16.0 → 0.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/vue-form",
3
- "version": "0.16.0",
3
+ "version": "0.16.2",
4
4
  "description": "Powerful, type-safe forms for Vue.",
5
5
  "author": "tannerlinsley",
6
6
  "license": "MIT",
@@ -34,7 +34,7 @@
34
34
  ],
35
35
  "dependencies": {
36
36
  "@tanstack/vue-store": "^0.3.1",
37
- "@tanstack/form-core": "0.16.0"
37
+ "@tanstack/form-core": "0.16.2"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@vitejs/plugin-vue": "^5.0.4",
@@ -49,7 +49,9 @@
49
49
  "test:types:versions49": "node ../../node_modules/typescript49/lib/tsc.js --project tsconfig.legacy.json",
50
50
  "test:types:versions50": "node ../../node_modules/typescript50/lib/tsc.js",
51
51
  "test:types:versions51": "node ../../node_modules/typescript51/lib/tsc.js",
52
- "test:types:versions52": "tsc",
52
+ "test:types:versions52": "node ../../node_modules/typescript52/lib/tsc.js",
53
+ "test:types:versions53": "node ../../node_modules/typescript53/lib/tsc.js",
54
+ "test:types:versions54": "tsc",
53
55
  "test:types": "pnpm run \"/^test:types:versions.*/\"",
54
56
  "fixme:test:lib": "pnpm run test:2 && pnpm run test:2.7 && pnpm run test:3",
55
57
  "test:lib": "vitest",
@@ -251,6 +251,106 @@ describe('useField', () => {
251
251
  expect(getByText(error)).toBeInTheDocument()
252
252
  })
253
253
 
254
+ it('should handle arrays with subvalues', async () => {
255
+ const fn = vi.fn()
256
+
257
+ type CompVal = { people: Array<string> }
258
+
259
+ const Comp = defineComponent(() => {
260
+ const form = useForm({
261
+ defaultValues: {
262
+ people: [],
263
+ } as CompVal,
264
+ onSubmit: ({ value }) => fn(value),
265
+ })
266
+
267
+ return () => (
268
+ <div>
269
+ <form
270
+ onSubmit={(e) => {
271
+ e.preventDefault()
272
+ e.stopPropagation()
273
+ void form.handleSubmit()
274
+ }}
275
+ >
276
+ <form.Field name="people">
277
+ {({
278
+ field,
279
+ }: {
280
+ field: FieldApi<CompVal, 'people', never, never>
281
+ }) => (
282
+ <div>
283
+ {field.state.value.map((_, i) => {
284
+ return (
285
+ <form.Field key={i} name={`people[${i}]`}>
286
+ {({
287
+ field: subField,
288
+ }: {
289
+ field: FieldApi<
290
+ CompVal,
291
+ `people[${number}]`,
292
+ never,
293
+ never
294
+ >
295
+ }) => (
296
+ <div>
297
+ <label>
298
+ <div>Name for person {i}</div>
299
+ <input
300
+ value={subField.state.value}
301
+ onChange={(e) =>
302
+ subField.handleChange(
303
+ (e.target as HTMLInputElement).value,
304
+ )
305
+ }
306
+ />
307
+ </label>
308
+ <button
309
+ onClick={() => field.removeValue(i)}
310
+ type="button"
311
+ >
312
+ Remove person {i}
313
+ </button>
314
+ </div>
315
+ )}
316
+ </form.Field>
317
+ )
318
+ })}
319
+ <button onClick={() => field.pushValue('')} type="button">
320
+ Add person
321
+ </button>
322
+ </div>
323
+ )}
324
+ </form.Field>
325
+ <button type="submit">Submit</button>
326
+ </form>
327
+ </div>
328
+ )
329
+ })
330
+
331
+ const { queryByText, getByText, findByLabelText, findByText } = render(Comp)
332
+ expect(queryByText('Name for person 0')).not.toBeInTheDocument()
333
+ expect(queryByText('Name for person 1')).not.toBeInTheDocument()
334
+ await user.click(getByText('Add person'))
335
+ const input = await findByLabelText('Name for person 0')
336
+ expect(input).toBeInTheDocument()
337
+ await user.type(input, 'John')
338
+
339
+ await user.click(getByText('Add person'))
340
+ const input2 = await findByLabelText('Name for person 1')
341
+ expect(input).toBeInTheDocument()
342
+ await user.type(input2, 'Jack')
343
+
344
+ expect(queryByText('Name for person 0')).toBeInTheDocument()
345
+ expect(queryByText('Name for person 1')).toBeInTheDocument()
346
+ await user.click(getByText('Remove person 1'))
347
+ expect(queryByText('Name for person 0')).toBeInTheDocument()
348
+ expect(queryByText('Name for person 1')).not.toBeInTheDocument()
349
+
350
+ await user.click(await findByText('Submit'))
351
+ expect(fn).toHaveBeenCalledWith({ people: ['John'] })
352
+ })
353
+
254
354
  it('should handle arrays with subvalues', async () => {
255
355
  const fn = vi.fn()
256
356
 
@@ -305,6 +405,12 @@ describe('useField', () => {
305
405
  }
306
406
  />
307
407
  </label>
408
+ <button
409
+ onClick={() => field.removeValue(i)}
410
+ type="button"
411
+ >
412
+ Remove person {i}
413
+ </button>
308
414
  </div>
309
415
  )}
310
416
  </form.Field>
@@ -327,10 +433,23 @@ describe('useField', () => {
327
433
 
328
434
  const { queryByText, getByText, findByLabelText, findByText } = render(Comp)
329
435
  expect(queryByText('Name for person 0')).not.toBeInTheDocument()
436
+ expect(queryByText('Name for person 1')).not.toBeInTheDocument()
330
437
  await user.click(getByText('Add person'))
331
438
  const input = await findByLabelText('Name for person 0')
332
439
  expect(input).toBeInTheDocument()
333
440
  await user.type(input, 'John')
441
+
442
+ await user.click(getByText('Add person'))
443
+ const input2 = await findByLabelText('Name for person 1')
444
+ expect(input).toBeInTheDocument()
445
+ await user.type(input2, 'Jack')
446
+
447
+ expect(queryByText('Name for person 0')).toBeInTheDocument()
448
+ expect(queryByText('Name for person 1')).toBeInTheDocument()
449
+ await user.click(getByText('Remove person 1'))
450
+ expect(queryByText('Name for person 0')).toBeInTheDocument()
451
+ expect(queryByText('Name for person 1')).not.toBeInTheDocument()
452
+
334
453
  await user.click(await findByText('Submit'))
335
454
  expect(fn).toHaveBeenCalledWith({ people: [{ name: 'John', age: 0 }] })
336
455
  })
package/src/useField.tsx CHANGED
@@ -40,7 +40,7 @@ export type UseField<
40
40
  TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
41
41
  >(
42
42
  opts: Omit<
43
- UseFieldOptions<TParentData, TName, TFieldValidator, TFormValidator>,
43
+ UseFieldOptions<TParentData, TName, TFieldValidator, TFormValidator, TData>,
44
44
  'form'
45
45
  >,
46
46
  ) => {
@@ -133,7 +133,8 @@ type FieldComponentProps<
133
133
  TFormValidator extends
134
134
  | Validator<TParentData, unknown>
135
135
  | undefined = undefined,
136
- > = UseFieldOptions<TParentData, TName, TFieldValidator, TFormValidator>
136
+ TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
137
+ > = UseFieldOptions<TParentData, TName, TFieldValidator, TFormValidator, TData>
137
138
 
138
139
  export type FieldComponent<
139
140
  TParentData,
@@ -148,7 +149,13 @@ export type FieldComponent<
148
149
  TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
149
150
  >(
150
151
  fieldOptions: Omit<
151
- FieldComponentProps<TParentData, TName, TFieldValidator, TFormValidator>,
152
+ FieldComponentProps<
153
+ TParentData,
154
+ TName,
155
+ TFieldValidator,
156
+ TFormValidator,
157
+ TData
158
+ >,
152
159
  'form'
153
160
  >,
154
161
  context: SetupContext<
@@ -184,12 +191,14 @@ export const Field = defineComponent(
184
191
  TFormValidator extends
185
192
  | Validator<TParentData, unknown>
186
193
  | undefined = undefined,
194
+ TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
187
195
  >(
188
196
  fieldOptions: UseFieldOptions<
189
197
  TParentData,
190
198
  TName,
191
199
  TFieldValidator,
192
- TFormValidator
200
+ TFormValidator,
201
+ TData
193
202
  >,
194
203
  context: SetupContext,
195
204
  ) => {