@tanstack/vue-form 0.16.1 → 0.18.0

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ![TanStack Form Header](https://github.com/TanStack/form/raw/main/media/repo-header.png)
4
4
 
5
- Hooks for managing form state in Vue
5
+ Compositions for managing form state in Vue
6
6
 
7
7
  <a href="https://twitter.com/intent/tweet?button_hashtag=TanStack" target="\_parent">
8
8
  <img alt="#TanStack" src="https://img.shields.io/twitter/url?color=%2308a0e9&label=%23TanStack&style=social&url=https%3A%2F%2Ftwitter.com%2Fintent%2Ftweet%3Fbutton_hashtag%3DTanStack">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/vue-form",
3
- "version": "0.16.1",
3
+ "version": "0.18.0",
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.1"
37
+ "@tanstack/form-core": "0.18.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@vitejs/plugin-vue": "^5.0.4",
@@ -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
  })