@tanstack/form-core 0.10.0 → 0.10.1

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.
@@ -226,6 +226,63 @@ describe('form api', () => {
226
226
  expect(form.getFieldValue('names')).toStrictEqual(['one', 'three', 'two'])
227
227
  })
228
228
 
229
+ it('should handle fields inside an array', async () => {
230
+ interface Employee {
231
+ firstName: string
232
+ }
233
+ interface Form {
234
+ employees: Partial<Employee>[]
235
+ }
236
+
237
+ const form = new FormApi<Form, unknown>()
238
+
239
+ const field = new FieldApi({
240
+ form,
241
+ name: 'employees',
242
+ defaultValue: [],
243
+ })
244
+
245
+ field.mount()
246
+
247
+ const fieldInArray = new FieldApi({
248
+ form,
249
+ name: `employees.${0}.firstName`,
250
+ defaultValue: 'Darcy',
251
+ })
252
+ fieldInArray.mount()
253
+ expect(field.state.value.length).toBe(1)
254
+ expect(fieldInArray.getValue()).toBe('Darcy')
255
+ })
256
+
257
+ it('should handle deleting fields in an array', async () => {
258
+ interface Employee {
259
+ firstName: string
260
+ }
261
+ interface Form {
262
+ employees: Partial<Employee>[]
263
+ }
264
+
265
+ const form = new FormApi<Form, unknown>()
266
+
267
+ const field = new FieldApi({
268
+ form,
269
+ name: 'employees',
270
+ defaultValue: [],
271
+ })
272
+
273
+ field.mount()
274
+
275
+ const fieldInArray = new FieldApi({
276
+ form,
277
+ name: `employees.${0}.firstName`,
278
+ defaultValue: 'Darcy',
279
+ })
280
+ fieldInArray.mount()
281
+ form.deleteField(`employees.${0}.firstName`)
282
+ expect(field.state.value.length).toBe(1)
283
+ expect(Object.keys(field.state.value[0]!).length).toBe(0)
284
+ })
285
+
229
286
  it('should not wipe values when updating', () => {
230
287
  const form = new FormApi({
231
288
  defaultValues: {
@@ -500,7 +557,6 @@ describe('form api', () => {
500
557
 
501
558
  form.mount()
502
559
  field.mount()
503
-
504
560
  expect(form.state.errors.length).toBe(0)
505
561
  field.setValue('other', { touch: true })
506
562
  field.validate('blur')
@@ -0,0 +1,73 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { deleteBy, getBy, setBy } from '../utils'
3
+
4
+ describe('getBy', () => {
5
+ const structure = {
6
+ name: 'Marc',
7
+ kids: [
8
+ { name: 'Stephen', age: 10 },
9
+ { name: 'Taylor', age: 15 },
10
+ ],
11
+ mother: {
12
+ name: 'Lisa',
13
+ },
14
+ }
15
+
16
+ it('should get subfields by path', () => {
17
+ expect(getBy(structure, 'name')).toBe(structure.name)
18
+ expect(getBy(structure, 'mother.name')).toBe(structure.mother.name)
19
+ })
20
+
21
+ it('should get array subfields by path', () => {
22
+ expect(getBy(structure, 'kids.0.name')).toBe(structure.kids[0]!.name)
23
+ expect(getBy(structure, 'kids.0.age')).toBe(structure.kids[0]!.age)
24
+ })
25
+ })
26
+
27
+ describe('setBy', () => {
28
+ const structure = {
29
+ name: 'Marc',
30
+ kids: [
31
+ { name: 'Stephen', age: 10 },
32
+ { name: 'Taylor', age: 15 },
33
+ ],
34
+ mother: {
35
+ name: 'Lisa',
36
+ },
37
+ }
38
+
39
+ it('should set subfields by path', () => {
40
+ expect(setBy(structure, 'name', 'Lisa').name).toBe('Lisa')
41
+ expect(setBy(structure, 'mother.name', 'Tina').mother.name).toBe('Tina')
42
+ })
43
+
44
+ it('should set array subfields by path', () => {
45
+ expect(setBy(structure, 'kids.0.name', 'Taylor').kids[0].name).toBe(
46
+ 'Taylor',
47
+ )
48
+ expect(setBy(structure, 'kids.0.age', 20).kids[0].age).toBe(20)
49
+ })
50
+ })
51
+
52
+ describe('deleteBy', () => {
53
+ const structure = {
54
+ name: 'Marc',
55
+ kids: [
56
+ { name: 'Stephen', age: 10 },
57
+ { name: 'Taylor', age: 15 },
58
+ ],
59
+ mother: {
60
+ name: 'Lisa',
61
+ },
62
+ }
63
+
64
+ it('should delete subfields by path', () => {
65
+ expect(deleteBy(structure, 'name').name).not.toBeDefined()
66
+ expect(deleteBy(structure, 'mother.name').mother.name).not.toBeDefined()
67
+ })
68
+
69
+ it('should delete array subfields by path', () => {
70
+ expect(deleteBy(structure, 'kids.0.name').kids[0].name).not.toBeDefined()
71
+ expect(deleteBy(structure, 'kids.0.age').kids[0].age).not.toBeDefined()
72
+ })
73
+ })
package/src/utils.ts CHANGED
@@ -17,8 +17,7 @@ export function functionalUpdate<TInput, TOutput = TInput>(
17
17
  * Get a value from an object using a path, including dot notation.
18
18
  */
19
19
  export function getBy(obj: any, path: any) {
20
- const pathArray = makePathArray(path)
21
- const pathObj = pathArray
20
+ const pathObj = makePathArray(path)
22
21
  return pathObj.reduce((current: any, pathPart: any) => {
23
22
  if (typeof current !== 'undefined') {
24
23
  return current[pathPart]
@@ -52,22 +51,59 @@ export function setBy(obj: any, _path: any, updater: Updater<any>) {
52
51
  }
53
52
  }
54
53
 
54
+ if (Array.isArray(parent) && key !== undefined) {
55
+ const prefix = parent.slice(0, key)
56
+ return [
57
+ ...(prefix.length ? prefix : new Array(key)),
58
+ doSet(parent[key]),
59
+ ...parent.slice(key + 1),
60
+ ]
61
+ }
62
+ return [...new Array(key), doSet()]
63
+ }
64
+
65
+ return doSet(obj)
66
+ }
67
+
68
+ /**
69
+ * Delete a field on an object using a path, including dot notation.
70
+ */
71
+ export function deleteBy(obj: any, _path: any) {
72
+ const path = makePathArray(_path)
73
+
74
+ function doDelete(parent: any): any {
75
+ if (path.length === 1) {
76
+ const finalPath = path[0]!
77
+ const { [finalPath]: remove, ...rest } = parent
78
+ return rest
79
+ }
80
+
81
+ const key = path.shift()
82
+
83
+ if (typeof key === 'string') {
84
+ if (typeof parent === 'object') {
85
+ return {
86
+ ...parent,
87
+ [key]: doDelete(parent[key]),
88
+ }
89
+ }
90
+ }
91
+
55
92
  if (typeof key === 'number') {
56
93
  if (Array.isArray(parent)) {
57
94
  const prefix = parent.slice(0, key)
58
95
  return [
59
96
  ...(prefix.length ? prefix : new Array(key)),
60
- doSet(parent[key]),
97
+ doDelete(parent[key]),
61
98
  ...parent.slice(key + 1),
62
99
  ]
63
100
  }
64
- return [...new Array(key), doSet()]
65
101
  }
66
102
 
67
- throw new Error('Uh oh!')
103
+ throw new Error('It seems we have created an infinite loop in deleteBy. ')
68
104
  }
69
105
 
70
- return doSet(obj)
106
+ return doDelete(obj)
71
107
  }
72
108
 
73
109
  const reFindNumbers0 = /^(\d*)$/gm