oxform-core 0.1.0 → 0.1.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.
- package/dist/index.cjs +751 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +443 -0
- package/dist/index.d.ts +443 -0
- package/dist/index.js +747 -0
- package/dist/index.js.map +1 -0
- package/package.json +20 -15
- package/export/index.ts +0 -7
- package/export/schema.ts +0 -1
- package/src/field-api.constants.ts +0 -15
- package/src/field-api.ts +0 -139
- package/src/form-api.ts +0 -84
- package/src/form-api.types.ts +0 -148
- package/src/form-array-field-api.ts +0 -233
- package/src/form-context-api.ts +0 -232
- package/src/form-field-api.ts +0 -174
- package/src/more-types.ts +0 -178
- package/src/tests/array/append.spec.ts +0 -138
- package/src/tests/array/insert.spec.ts +0 -182
- package/src/tests/array/move.spec.ts +0 -175
- package/src/tests/array/prepend.spec.ts +0 -138
- package/src/tests/array/remove.spec.ts +0 -174
- package/src/tests/array/swap.spec.ts +0 -152
- package/src/tests/array/update.spec.ts +0 -148
- package/src/tests/field/change.spec.ts +0 -226
- package/src/tests/field/reset.spec.ts +0 -617
- package/src/tests/field/set-errors.spec.ts +0 -254
- package/src/tests/field-api/field-api.spec.ts +0 -341
- package/src/tests/form-api/reset.spec.ts +0 -535
- package/src/tests/form-api/submit.spec.ts +0 -409
- package/src/types.ts +0 -5
- package/src/utils/get.ts +0 -5
- package/src/utils/testing/sleep.ts +0 -1
- package/src/utils/testing/tests.ts +0 -18
- package/src/utils/update.ts +0 -6
- package/src/utils/validate.ts +0 -8
- package/tsconfig.json +0 -3
- package/tsdown.config.ts +0 -10
- package/vitest.config.ts +0 -3
package/src/more-types.ts
DELETED
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
// reference: https://github.com/TanStack/form/blob/main/packages/form-core/src/util-types.ts#L161
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @private
|
|
5
|
-
*/
|
|
6
|
-
export type UnwrapOneLevelOfArray<T> = T extends (infer U)[] ? U : T;
|
|
7
|
-
|
|
8
|
-
type Narrowable = string | number | bigint | boolean;
|
|
9
|
-
|
|
10
|
-
type NarrowRaw<A> =
|
|
11
|
-
| (A extends [] ? [] : never)
|
|
12
|
-
| (A extends Narrowable ? A : never)
|
|
13
|
-
| {
|
|
14
|
-
[K in keyof A]: A[K] extends Function ? A[K] : NarrowRaw<A[K]>;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
type Try<A1, A2, Catch = never> = A1 extends A2 ? A1 : Catch;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* @private
|
|
21
|
-
*/
|
|
22
|
-
export type Narrow<A> = Try<A, [], NarrowRaw<A>>;
|
|
23
|
-
|
|
24
|
-
export interface AnyDeepKeyAndValue<K extends string = string, V extends any = any> {
|
|
25
|
-
key: K;
|
|
26
|
-
value: V;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export type ArrayAccessor<TParent extends AnyDeepKeyAndValue> =
|
|
30
|
-
`${TParent['key'] extends never ? '' : TParent['key']}.${number}`;
|
|
31
|
-
|
|
32
|
-
export interface ArrayDeepKeyAndValue<
|
|
33
|
-
in out TParent extends AnyDeepKeyAndValue,
|
|
34
|
-
in out T extends ReadonlyArray<any>,
|
|
35
|
-
> extends AnyDeepKeyAndValue {
|
|
36
|
-
key: ArrayAccessor<TParent>;
|
|
37
|
-
value: T[number] | Nullable<TParent['value']>;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export type DeepKeyAndValueArray<
|
|
41
|
-
TParent extends AnyDeepKeyAndValue,
|
|
42
|
-
T extends ReadonlyArray<any>,
|
|
43
|
-
TAcc,
|
|
44
|
-
> = DeepKeysAndValuesImpl<
|
|
45
|
-
NonNullable<T[number]>,
|
|
46
|
-
ArrayDeepKeyAndValue<TParent, T>,
|
|
47
|
-
TAcc | ArrayDeepKeyAndValue<TParent, T>
|
|
48
|
-
>;
|
|
49
|
-
|
|
50
|
-
export type TupleAccessor<
|
|
51
|
-
TParent extends AnyDeepKeyAndValue,
|
|
52
|
-
TKey extends string,
|
|
53
|
-
> = `${TParent['key'] extends never ? '' : TParent['key']}[${TKey}]`;
|
|
54
|
-
|
|
55
|
-
export interface TupleDeepKeyAndValue<
|
|
56
|
-
in out TParent extends AnyDeepKeyAndValue,
|
|
57
|
-
in out T,
|
|
58
|
-
in out TKey extends AllTupleKeys<T>,
|
|
59
|
-
> extends AnyDeepKeyAndValue {
|
|
60
|
-
key: TupleAccessor<TParent, TKey>;
|
|
61
|
-
value: T[TKey] | Nullable<TParent['value']>;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export type AllTupleKeys<T> = T extends any ? keyof T & `${number}` : never;
|
|
65
|
-
|
|
66
|
-
export type DeepKeyAndValueTuple<
|
|
67
|
-
TParent extends AnyDeepKeyAndValue,
|
|
68
|
-
T extends ReadonlyArray<any>,
|
|
69
|
-
TAcc,
|
|
70
|
-
TAllKeys extends AllTupleKeys<T> = AllTupleKeys<T>,
|
|
71
|
-
> = TAllKeys extends any
|
|
72
|
-
? DeepKeysAndValuesImpl<
|
|
73
|
-
NonNullable<T[TAllKeys]>,
|
|
74
|
-
TupleDeepKeyAndValue<TParent, T, TAllKeys>,
|
|
75
|
-
TAcc | TupleDeepKeyAndValue<TParent, T, TAllKeys>
|
|
76
|
-
>
|
|
77
|
-
: never;
|
|
78
|
-
|
|
79
|
-
export type AllObjectKeys<T> = T extends any ? keyof T & (string | number) : never;
|
|
80
|
-
|
|
81
|
-
export type ObjectAccessor<
|
|
82
|
-
TParent extends AnyDeepKeyAndValue,
|
|
83
|
-
TKey extends string | number,
|
|
84
|
-
> = TParent['key'] extends never ? `${TKey}` : `${TParent['key']}.${TKey}`;
|
|
85
|
-
|
|
86
|
-
export type Nullable<T> = T & (undefined | null);
|
|
87
|
-
|
|
88
|
-
export type ObjectValue<TParent extends AnyDeepKeyAndValue, T, TKey extends AllObjectKeys<T>> =
|
|
89
|
-
| T[TKey]
|
|
90
|
-
| Nullable<TParent['value']>;
|
|
91
|
-
|
|
92
|
-
export interface ObjectDeepKeyAndValue<
|
|
93
|
-
in out TParent extends AnyDeepKeyAndValue,
|
|
94
|
-
in out T,
|
|
95
|
-
in out TKey extends AllObjectKeys<T>,
|
|
96
|
-
> extends AnyDeepKeyAndValue {
|
|
97
|
-
key: ObjectAccessor<TParent, TKey>;
|
|
98
|
-
value: ObjectValue<TParent, T, TKey>;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export type DeepKeyAndValueObject<
|
|
102
|
-
TParent extends AnyDeepKeyAndValue,
|
|
103
|
-
T,
|
|
104
|
-
TAcc,
|
|
105
|
-
TAllKeys extends AllObjectKeys<T> = AllObjectKeys<T>,
|
|
106
|
-
> = TAllKeys extends any
|
|
107
|
-
? DeepKeysAndValuesImpl<
|
|
108
|
-
NonNullable<T[TAllKeys]>,
|
|
109
|
-
ObjectDeepKeyAndValue<TParent, T, TAllKeys>,
|
|
110
|
-
TAcc | ObjectDeepKeyAndValue<TParent, T, TAllKeys>
|
|
111
|
-
>
|
|
112
|
-
: never;
|
|
113
|
-
|
|
114
|
-
export type UnknownAccessor<TParent extends AnyDeepKeyAndValue> = TParent['key'] extends never
|
|
115
|
-
? string
|
|
116
|
-
: `${TParent['key']}.${string}`;
|
|
117
|
-
|
|
118
|
-
export interface UnknownDeepKeyAndValue<TParent extends AnyDeepKeyAndValue> extends AnyDeepKeyAndValue {
|
|
119
|
-
key: UnknownAccessor<TParent>;
|
|
120
|
-
value: unknown;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export type DeepKeysAndValues<T> =
|
|
124
|
-
DeepKeysAndValuesImpl<T> extends AnyDeepKeyAndValue ? DeepKeysAndValuesImpl<T> : never;
|
|
125
|
-
|
|
126
|
-
export type DeepKeysAndValuesImpl<T, TParent extends AnyDeepKeyAndValue = never, TAcc = never> = unknown extends T
|
|
127
|
-
? TAcc | UnknownDeepKeyAndValue<TParent>
|
|
128
|
-
: unknown extends T // this stops runaway recursion when T is any
|
|
129
|
-
? T
|
|
130
|
-
: T extends string | number | boolean | bigint | Date
|
|
131
|
-
? TAcc
|
|
132
|
-
: T extends ReadonlyArray<any>
|
|
133
|
-
? number extends T['length']
|
|
134
|
-
? DeepKeyAndValueArray<TParent, T, TAcc>
|
|
135
|
-
: DeepKeyAndValueTuple<TParent, T, TAcc>
|
|
136
|
-
: keyof T extends never
|
|
137
|
-
? TAcc | UnknownDeepKeyAndValue<TParent>
|
|
138
|
-
: T extends object
|
|
139
|
-
? DeepKeyAndValueObject<TParent, T, TAcc>
|
|
140
|
-
: TAcc;
|
|
141
|
-
|
|
142
|
-
export type DeepRecord<T> = {
|
|
143
|
-
[TRecord in DeepKeysAndValues<T> as TRecord['key']]: TRecord['value'];
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* The keys of an object or array, deeply nested.
|
|
148
|
-
*/
|
|
149
|
-
export type DeepKeys<T> = unknown extends T ? string : DeepKeysAndValues<T>['key'];
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Infer the type of a deeply nested property within an object or an array.
|
|
153
|
-
*/
|
|
154
|
-
export type DeepValue<TValue, TAccessor> = unknown extends TValue
|
|
155
|
-
? TValue
|
|
156
|
-
: TAccessor extends DeepKeys<TValue>
|
|
157
|
-
? DeepRecord<TValue>[TAccessor]
|
|
158
|
-
: never;
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* The keys of an object or array, deeply nested and only with a value of TValue
|
|
162
|
-
*/
|
|
163
|
-
export type DeepKeysOfType<TData, TValue> = Extract<
|
|
164
|
-
DeepKeysAndValues<TData>,
|
|
165
|
-
AnyDeepKeyAndValue<string, TValue>
|
|
166
|
-
>['key'];
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Maps the deep keys of TFormData to the shallow keys of TFieldGroupData.
|
|
170
|
-
* Since using template strings as keys is impractical, it relies on shallow keys only.
|
|
171
|
-
*/
|
|
172
|
-
export type FieldsMap<TFormData, TFieldGroupData> = TFieldGroupData extends any[]
|
|
173
|
-
? never
|
|
174
|
-
: string extends keyof TFieldGroupData
|
|
175
|
-
? never
|
|
176
|
-
: {
|
|
177
|
-
[K in keyof TFieldGroupData]: DeepKeysOfType<TFormData, TFieldGroupData[K]>;
|
|
178
|
-
};
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { FormApi } from '#form-api';
|
|
2
|
-
import type { FormOptions } from '#form-api.types';
|
|
3
|
-
import { sleep } from '#utils/testing/sleep';
|
|
4
|
-
import { describe, expect, it } from 'vitest';
|
|
5
|
-
import z from 'zod';
|
|
6
|
-
|
|
7
|
-
const schema = z.object({
|
|
8
|
-
array: z.string().array(),
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
const defaultValues = {
|
|
12
|
-
array: ['item1', 'item2'],
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const setup = (options?: Partial<FormOptions<typeof schema>>) => {
|
|
16
|
-
const form = new FormApi({
|
|
17
|
-
schema,
|
|
18
|
-
defaultValues,
|
|
19
|
-
...options,
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
form['~mount']();
|
|
23
|
-
|
|
24
|
-
return { form };
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
describe('value updates', () => {
|
|
28
|
-
it('should append a value to the end of an array', () => {
|
|
29
|
-
const { form } = setup();
|
|
30
|
-
|
|
31
|
-
form.array.append('array', 'item3');
|
|
32
|
-
|
|
33
|
-
expect(form.field.get('array')).toStrictEqual(['item1', 'item2', 'item3']);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('should append a value if the array is undefined', () => {
|
|
37
|
-
const { form } = setup();
|
|
38
|
-
|
|
39
|
-
form.field.change('array', undefined as any);
|
|
40
|
-
|
|
41
|
-
form.array.append('array', 'item3');
|
|
42
|
-
|
|
43
|
-
expect(form.field.get('array')).toStrictEqual(['item3']);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('should append a value if the array is null', () => {
|
|
47
|
-
const { form } = setup();
|
|
48
|
-
|
|
49
|
-
form.field.change('array', null as any);
|
|
50
|
-
|
|
51
|
-
form.array.append('array', 'item3');
|
|
52
|
-
|
|
53
|
-
expect(form.field.get('array')).toStrictEqual(['item3']);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('should append a value if the array is empty', () => {
|
|
57
|
-
const { form } = setup();
|
|
58
|
-
|
|
59
|
-
form.field.change('array', []);
|
|
60
|
-
|
|
61
|
-
form.array.append('array', 'item3');
|
|
62
|
-
|
|
63
|
-
expect(form.field.get('array')).toStrictEqual(['item3']);
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
describe('meta handling', () => {
|
|
68
|
-
it('should update the array field meta when appending', () => {
|
|
69
|
-
const { form } = setup();
|
|
70
|
-
|
|
71
|
-
form.array.append('array', 'item3');
|
|
72
|
-
|
|
73
|
-
expect(form.field.meta('array').dirty).toBe(true);
|
|
74
|
-
expect(form.field.meta('array').touched).toBe(true);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('should not update the array field meta when appending with should.dirty false', () => {
|
|
78
|
-
const { form } = setup();
|
|
79
|
-
|
|
80
|
-
form.array.append('array', 'item3', { should: { dirty: false } });
|
|
81
|
-
|
|
82
|
-
expect(form.field.meta('array').dirty).toBe(false);
|
|
83
|
-
expect(form.field.meta('array').touched).toBe(true);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('should not update the array field meta when appending with should.touch false', () => {
|
|
87
|
-
const { form } = setup();
|
|
88
|
-
|
|
89
|
-
form.array.append('array', 'item3', { should: { touch: false } });
|
|
90
|
-
|
|
91
|
-
expect(form.field.meta('array').dirty).toBe(true);
|
|
92
|
-
expect(form.field.meta('array').touched).toBe(false);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('should update the last items meta when appending', () => {
|
|
96
|
-
const { form } = setup();
|
|
97
|
-
|
|
98
|
-
form.array.append('array', 'item3');
|
|
99
|
-
|
|
100
|
-
expect(form.field.meta('array.0').dirty).toBe(false);
|
|
101
|
-
expect(form.field.meta('array.0').touched).toBe(false);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('should leave the rest of the items untouched when appending', () => {
|
|
105
|
-
const { form } = setup();
|
|
106
|
-
|
|
107
|
-
form.field.change('array.2', 'updated', { should: { touch: false } });
|
|
108
|
-
form.array.append('array', 'item3');
|
|
109
|
-
|
|
110
|
-
expect(form.field.meta('array.1').dirty).toBe(false);
|
|
111
|
-
expect(form.field.meta('array.1').touched).toBe(false);
|
|
112
|
-
|
|
113
|
-
expect(form.field.meta('array.2').dirty).toBe(true);
|
|
114
|
-
expect(form.field.meta('array.2').touched).toBe(false);
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
describe('validation', () => {
|
|
119
|
-
it('should validate a field', async () => {
|
|
120
|
-
const { form } = setup({ validate: { change: schema } });
|
|
121
|
-
|
|
122
|
-
form.array.append('array', 2 as any);
|
|
123
|
-
|
|
124
|
-
await sleep(0);
|
|
125
|
-
|
|
126
|
-
expect(form.field.meta('array.2').valid).toBe(false);
|
|
127
|
-
expect(form.field.errors('array.2')).toHaveLength(1);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it('should not validate a field when should.validate is false', async () => {
|
|
131
|
-
const { form } = setup({ validate: { change: schema } });
|
|
132
|
-
|
|
133
|
-
form.array.append('array', 2 as any, { should: { validate: false } });
|
|
134
|
-
|
|
135
|
-
expect(form.field.meta('array.2').valid).toBe(true);
|
|
136
|
-
expect(form.field.errors('array.2')).toHaveLength(0);
|
|
137
|
-
});
|
|
138
|
-
});
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
import { FormApi } from '#form-api';
|
|
2
|
-
import type { FormOptions } from '#form-api.types';
|
|
3
|
-
import { sleep } from '#utils/testing/sleep';
|
|
4
|
-
import { describe, expect, it } from 'vitest';
|
|
5
|
-
import z from 'zod';
|
|
6
|
-
|
|
7
|
-
const schema = z.object({
|
|
8
|
-
array: z.string().array(),
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
const defaultValues = {
|
|
12
|
-
array: ['item1', 'item2'],
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const setup = (options?: Partial<FormOptions<typeof schema>>) => {
|
|
16
|
-
const form = new FormApi({
|
|
17
|
-
schema,
|
|
18
|
-
defaultValues,
|
|
19
|
-
...options,
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
form['~mount']();
|
|
23
|
-
|
|
24
|
-
return { form };
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
describe('value updates', () => {
|
|
28
|
-
it('should insert string at beginning (index 0)', () => {
|
|
29
|
-
const { form } = setup();
|
|
30
|
-
|
|
31
|
-
form.array.insert('array', 0, 'inserted');
|
|
32
|
-
|
|
33
|
-
expect(form.field.get('array')).toStrictEqual(['inserted', 'item1', 'item2']);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('should insert string at middle index', () => {
|
|
37
|
-
const { form } = setup();
|
|
38
|
-
|
|
39
|
-
form.array.insert('array', 1, 'inserted');
|
|
40
|
-
|
|
41
|
-
expect(form.field.get('array')).toStrictEqual(['item1', 'inserted', 'item2']);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should insert string at end (index equal to length)', () => {
|
|
45
|
-
const { form } = setup();
|
|
46
|
-
|
|
47
|
-
form.array.insert('array', 2, 'inserted');
|
|
48
|
-
|
|
49
|
-
expect(form.field.get('array')).toStrictEqual(['item1', 'item2', 'inserted']);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('should insert at an index greater than length', () => {
|
|
53
|
-
const { form } = setup();
|
|
54
|
-
|
|
55
|
-
form.array.insert('array', 4, 'inserted');
|
|
56
|
-
|
|
57
|
-
expect(form.field.get('array')).toEqual(['item1', 'item2', undefined, undefined, 'inserted']);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('should insert into an empty array', () => {
|
|
61
|
-
const { form } = setup();
|
|
62
|
-
|
|
63
|
-
form.field.change('array', []);
|
|
64
|
-
form.array.insert('array', 1, 'inserted');
|
|
65
|
-
|
|
66
|
-
expect(form.field.get('array')).toStrictEqual([undefined, 'inserted']);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('should insert into an undefined array', () => {
|
|
70
|
-
const { form } = setup();
|
|
71
|
-
|
|
72
|
-
form.field.change('array', undefined as any);
|
|
73
|
-
form.array.insert('array', 0, 'inserted');
|
|
74
|
-
|
|
75
|
-
expect(form.field.get('array')).toStrictEqual(['inserted']);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should insert into a null array', () => {
|
|
79
|
-
const { form } = setup();
|
|
80
|
-
|
|
81
|
-
form.field.change('array', null as any);
|
|
82
|
-
form.array.insert('array', 0, 'inserted');
|
|
83
|
-
|
|
84
|
-
expect(form.field.get('array')).toStrictEqual(['inserted']);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('should allow passing an updater function', () => {
|
|
88
|
-
const { form } = setup();
|
|
89
|
-
|
|
90
|
-
form.array.insert('array', 0, value => value.length + ' updated');
|
|
91
|
-
|
|
92
|
-
expect(form.field.get('array')).toStrictEqual(['2 updated', 'item1', 'item2']);
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
describe('meta handling', () => {
|
|
97
|
-
it('should update the array field meta when inserting', () => {
|
|
98
|
-
const { form } = setup();
|
|
99
|
-
|
|
100
|
-
form.array.insert('array', 0, 'inserted');
|
|
101
|
-
|
|
102
|
-
expect(form.field.meta('array').dirty).toBe(true);
|
|
103
|
-
expect(form.field.meta('array').touched).toBe(true);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('should not update the array field meta when inserting with should.dirty false', () => {
|
|
107
|
-
const { form } = setup();
|
|
108
|
-
|
|
109
|
-
form.array.insert('array', 0, 'inserted', { should: { dirty: false } });
|
|
110
|
-
|
|
111
|
-
expect(form.field.meta('array').dirty).toBe(false);
|
|
112
|
-
expect(form.field.meta('array').touched).toBe(true);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('should not update the array field meta when inserting with should.touch false', () => {
|
|
116
|
-
const { form } = setup();
|
|
117
|
-
|
|
118
|
-
form.array.insert('array', 0, 'inserted', { should: { touch: false } });
|
|
119
|
-
|
|
120
|
-
expect(form.field.meta('array').dirty).toBe(true);
|
|
121
|
-
expect(form.field.meta('array').touched).toBe(false);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it('should update the inserted item meta when inserting', () => {
|
|
125
|
-
const { form } = setup();
|
|
126
|
-
|
|
127
|
-
form.field.change('array.0', 'updated', { should: { touch: false } });
|
|
128
|
-
form.array.insert('array', 0, 'inserted');
|
|
129
|
-
|
|
130
|
-
expect(form.field.meta('array.0').dirty).toBe(false);
|
|
131
|
-
expect(form.field.meta('array.0').touched).toBe(false);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('should leave the rest of the items untouched when inserting', () => {
|
|
135
|
-
const { form } = setup();
|
|
136
|
-
|
|
137
|
-
form.field.change('array.0', 'updated', { should: { touch: false } });
|
|
138
|
-
form.field.change('array.2', 'updated', { should: { dirty: false } });
|
|
139
|
-
form.array.insert('array', 0, 'inserted');
|
|
140
|
-
|
|
141
|
-
expect(form.field.meta('array.1').dirty).toBe(true);
|
|
142
|
-
expect(form.field.meta('array.1').touched).toBe(false);
|
|
143
|
-
|
|
144
|
-
expect(form.field.meta('array.2').dirty).toBe(false);
|
|
145
|
-
expect(form.field.meta('array.2').touched).toBe(false);
|
|
146
|
-
|
|
147
|
-
expect(form.field.meta('array.3').dirty).toBe(false);
|
|
148
|
-
expect(form.field.meta('array.3').touched).toBe(true);
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
describe('edge cases', () => {
|
|
153
|
-
it('handles negative index', () => {
|
|
154
|
-
const { form } = setup();
|
|
155
|
-
|
|
156
|
-
form.array.insert('array', -100, 'inserted');
|
|
157
|
-
|
|
158
|
-
expect(form.field.get('array')).toEqual(['inserted', 'item1', 'item2']);
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
describe('validation', () => {
|
|
163
|
-
it('should validate on insert by default', async () => {
|
|
164
|
-
const { form } = setup({ validate: { change: schema } });
|
|
165
|
-
|
|
166
|
-
form.array.insert('array', 0, 0 as any);
|
|
167
|
-
|
|
168
|
-
await sleep(0);
|
|
169
|
-
|
|
170
|
-
expect(form.field.meta('array.0').valid).toBe(false);
|
|
171
|
-
expect(form.field.errors('array.0')).toHaveLength(1);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it('should not validate on insert by default when should.validate is false', async () => {
|
|
175
|
-
const { form } = setup({ validate: { change: schema } });
|
|
176
|
-
|
|
177
|
-
form.array.insert('array', 0, 0 as any, { should: { validate: false } });
|
|
178
|
-
|
|
179
|
-
expect(form.field.meta('array.0').valid).toBe(true);
|
|
180
|
-
expect(form.field.errors('array.0')).toHaveLength(0);
|
|
181
|
-
});
|
|
182
|
-
});
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import { FormApi } from '#form-api';
|
|
2
|
-
import type { FormOptions } from '#form-api.types';
|
|
3
|
-
import { describe, expect, it } from 'vitest';
|
|
4
|
-
import z from 'zod';
|
|
5
|
-
|
|
6
|
-
const schema = z.object({
|
|
7
|
-
array: z.string().array(),
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
const defaultValues = {
|
|
11
|
-
array: ['item1', 'item2', 'item3'],
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const setup = (options?: Partial<FormOptions<typeof schema>>) => {
|
|
15
|
-
const form = new FormApi({
|
|
16
|
-
schema,
|
|
17
|
-
defaultValues,
|
|
18
|
-
...options,
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
form['~mount']();
|
|
22
|
-
|
|
23
|
-
return { form };
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
describe('value updates', () => {
|
|
27
|
-
it('should move an item forward in an array', () => {
|
|
28
|
-
const { form } = setup();
|
|
29
|
-
|
|
30
|
-
form.array.move('array', 0, 1);
|
|
31
|
-
|
|
32
|
-
expect(form.field.get('array')).toEqual(['item2', 'item1', 'item3']);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should move an item backward in an array', () => {
|
|
36
|
-
const { form } = setup();
|
|
37
|
-
|
|
38
|
-
form.array.move('array', 2, 1);
|
|
39
|
-
|
|
40
|
-
expect(form.field.get('array')).toEqual(['item1', 'item3', 'item2']);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('moves an item from the beginning to the end of the array', () => {
|
|
44
|
-
const { form } = setup();
|
|
45
|
-
|
|
46
|
-
form.array.move('array', 0, 2);
|
|
47
|
-
|
|
48
|
-
expect(form.field.get('array')).toEqual(['item2', 'item3', 'item1']);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('moves an item from the end to the beginning of the array', () => {
|
|
52
|
-
const { form } = setup();
|
|
53
|
-
|
|
54
|
-
form.array.move('array', 2, 0);
|
|
55
|
-
|
|
56
|
-
expect(form.field.get('array')).toEqual(['item3', 'item1', 'item2']);
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
describe('meta handling', () => {
|
|
61
|
-
it('should update the array field meta when moving', () => {
|
|
62
|
-
const { form } = setup();
|
|
63
|
-
|
|
64
|
-
form.array.move('array', 0, 1);
|
|
65
|
-
|
|
66
|
-
expect(form.field.meta('array').dirty).toBe(true);
|
|
67
|
-
expect(form.field.meta('array').touched).toBe(true);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('should not update the array field meta when moving with should.dirty false', () => {
|
|
71
|
-
const { form } = setup();
|
|
72
|
-
|
|
73
|
-
form.array.move('array', 0, 1, { should: { dirty: false } });
|
|
74
|
-
|
|
75
|
-
expect(form.field.meta('array').dirty).toBe(false);
|
|
76
|
-
expect(form.field.meta('array').touched).toBe(true);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('should not update the array field meta when moving with should.touch false', () => {
|
|
80
|
-
const { form } = setup();
|
|
81
|
-
|
|
82
|
-
form.array.move('array', 0, 1, { should: { touch: false } });
|
|
83
|
-
|
|
84
|
-
expect(form.field.meta('array').dirty).toBe(true);
|
|
85
|
-
expect(form.field.meta('array').touched).toBe(false);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('should update the moved item meta when moving forwards', () => {
|
|
89
|
-
const { form } = setup();
|
|
90
|
-
|
|
91
|
-
form.field.change('array.0', 'updated', { should: { touch: false } });
|
|
92
|
-
form.field.change('array.1', 'updated', { should: { dirty: false } });
|
|
93
|
-
form.array.move('array', 0, 1);
|
|
94
|
-
|
|
95
|
-
expect(form.field.meta('array.0').dirty).toBe(false);
|
|
96
|
-
expect(form.field.meta('array.0').touched).toBe(true);
|
|
97
|
-
|
|
98
|
-
expect(form.field.meta('array.1').dirty).toBe(true);
|
|
99
|
-
expect(form.field.meta('array.1').touched).toBe(false);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('should update the moved item meta when moving backwards', () => {
|
|
103
|
-
const { form } = setup();
|
|
104
|
-
|
|
105
|
-
form.field.change('array.0', 'updated', { should: { touch: false } });
|
|
106
|
-
form.field.change('array.1', 'updated', { should: { dirty: false } });
|
|
107
|
-
form.array.move('array', 1, 0);
|
|
108
|
-
|
|
109
|
-
expect(form.field.meta('array.0').dirty).toBe(true);
|
|
110
|
-
expect(form.field.meta('array.0').touched).toBe(false);
|
|
111
|
-
|
|
112
|
-
expect(form.field.meta('array.1').dirty).toBe(false);
|
|
113
|
-
expect(form.field.meta('array.1').touched).toBe(true);
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
describe('edge cases', () => {
|
|
118
|
-
it('should handle negative from as 0', () => {
|
|
119
|
-
const { form } = setup();
|
|
120
|
-
|
|
121
|
-
form.array.move('array', -100, 1);
|
|
122
|
-
|
|
123
|
-
expect(form.field.get('array')).toEqual(['item2', 'item1', 'item3']);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('should handle negative to as 0', () => {
|
|
127
|
-
const { form } = setup();
|
|
128
|
-
|
|
129
|
-
form.array.move('array', 1, -100);
|
|
130
|
-
|
|
131
|
-
expect(form.field.get('array')).toEqual(['item2', 'item1', 'item3']);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('should handle moving with the same index', () => {
|
|
135
|
-
const { form } = setup();
|
|
136
|
-
|
|
137
|
-
form.array.move('array', 0, 0);
|
|
138
|
-
|
|
139
|
-
expect(form.field.get('array')).toEqual(['item1', 'item2', 'item3']);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it('should move an item across a wide gap', () => {
|
|
143
|
-
const { form } = setup({
|
|
144
|
-
defaultValues: {
|
|
145
|
-
array: ['item1', 'item2', 'item3', 'item4', 'item5', 'item6', 'item7', 'item8', 'item9', 'item10'],
|
|
146
|
-
},
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
form.field.change('array.1', 'moved', { should: { touch: false } });
|
|
150
|
-
form.field.change('array.5', 'updated', { should: { dirty: false } });
|
|
151
|
-
form.array.move('array', 1, 8);
|
|
152
|
-
|
|
153
|
-
expect(form.field.get('array')).toEqual([
|
|
154
|
-
'item1',
|
|
155
|
-
'item3',
|
|
156
|
-
'item4',
|
|
157
|
-
'item5',
|
|
158
|
-
'updated',
|
|
159
|
-
'item7',
|
|
160
|
-
'item8',
|
|
161
|
-
'item9',
|
|
162
|
-
'moved',
|
|
163
|
-
'item10',
|
|
164
|
-
]);
|
|
165
|
-
|
|
166
|
-
expect(form.field.meta('array.1').dirty).toEqual(false);
|
|
167
|
-
expect(form.field.meta('array.1').touched).toEqual(false);
|
|
168
|
-
|
|
169
|
-
expect(form.field.meta('array.4').dirty).toEqual(false);
|
|
170
|
-
expect(form.field.meta('array.4').touched).toEqual(true);
|
|
171
|
-
|
|
172
|
-
expect(form.field.meta('array.8').dirty).toEqual(true);
|
|
173
|
-
expect(form.field.meta('array.8').touched).toEqual(false);
|
|
174
|
-
});
|
|
175
|
-
});
|