@tanstack/form-core 0.18.1 → 0.19.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/cjs/FieldApi.cjs +89 -23
- package/dist/cjs/FieldApi.cjs.map +1 -1
- package/dist/cjs/FieldApi.d.cts +8 -5
- package/dist/cjs/FormApi.cjs +32 -9
- package/dist/cjs/FormApi.cjs.map +1 -1
- package/dist/cjs/FormApi.d.cts +1 -0
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +2 -2
- package/dist/esm/FieldApi.d.ts +8 -5
- package/dist/esm/FieldApi.js +89 -23
- package/dist/esm/FieldApi.js.map +1 -1
- package/dist/esm/FormApi.d.ts +1 -0
- package/dist/esm/FormApi.js +32 -9
- package/dist/esm/FormApi.js.map +1 -1
- package/dist/esm/utils.d.ts +2 -2
- package/dist/esm/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/FieldApi.ts +114 -24
- package/src/FormApi.ts +26 -1
- package/src/tests/FieldApi.spec.ts +158 -0
- package/src/tests/FormApi.spec.ts +64 -0
- package/src/utils.ts +2 -2
|
@@ -721,4 +721,162 @@ describe('field api', () => {
|
|
|
721
721
|
await sleep(1)
|
|
722
722
|
expect(fn).toHaveBeenCalledTimes(1)
|
|
723
723
|
})
|
|
724
|
+
|
|
725
|
+
it('should run onChange on a linked field', () => {
|
|
726
|
+
const form = new FormApi({
|
|
727
|
+
defaultValues: {
|
|
728
|
+
password: '',
|
|
729
|
+
confirm_password: '',
|
|
730
|
+
},
|
|
731
|
+
})
|
|
732
|
+
|
|
733
|
+
const passField = new FieldApi({
|
|
734
|
+
form,
|
|
735
|
+
name: 'password',
|
|
736
|
+
})
|
|
737
|
+
|
|
738
|
+
const passconfirmField = new FieldApi({
|
|
739
|
+
form,
|
|
740
|
+
name: 'confirm_password',
|
|
741
|
+
validators: {
|
|
742
|
+
onChangeListenTo: ['password'],
|
|
743
|
+
onChange: ({ value, fieldApi }) => {
|
|
744
|
+
if (value !== fieldApi.form.getFieldValue('password')) {
|
|
745
|
+
return 'Passwords do not match'
|
|
746
|
+
}
|
|
747
|
+
return undefined
|
|
748
|
+
},
|
|
749
|
+
},
|
|
750
|
+
})
|
|
751
|
+
|
|
752
|
+
passField.mount()
|
|
753
|
+
passconfirmField.mount()
|
|
754
|
+
|
|
755
|
+
passField.setValue('one', { touch: true })
|
|
756
|
+
expect(passconfirmField.state.meta.errors).toStrictEqual([
|
|
757
|
+
'Passwords do not match',
|
|
758
|
+
])
|
|
759
|
+
passconfirmField.setValue('one', { touch: true })
|
|
760
|
+
expect(passconfirmField.state.meta.errors).toStrictEqual([])
|
|
761
|
+
passField.setValue('two', { touch: true })
|
|
762
|
+
expect(passconfirmField.state.meta.errors).toStrictEqual([
|
|
763
|
+
'Passwords do not match',
|
|
764
|
+
])
|
|
765
|
+
})
|
|
766
|
+
|
|
767
|
+
it('should run onBlur on a linked field', () => {
|
|
768
|
+
const form = new FormApi({
|
|
769
|
+
defaultValues: {
|
|
770
|
+
password: '',
|
|
771
|
+
confirm_password: '',
|
|
772
|
+
},
|
|
773
|
+
})
|
|
774
|
+
|
|
775
|
+
const passField = new FieldApi({
|
|
776
|
+
form,
|
|
777
|
+
name: 'password',
|
|
778
|
+
})
|
|
779
|
+
|
|
780
|
+
const passconfirmField = new FieldApi({
|
|
781
|
+
form,
|
|
782
|
+
name: 'confirm_password',
|
|
783
|
+
validators: {
|
|
784
|
+
onBlurListenTo: ['password'],
|
|
785
|
+
onBlur: ({ value, fieldApi }) => {
|
|
786
|
+
if (value !== fieldApi.form.getFieldValue('password')) {
|
|
787
|
+
return 'Passwords do not match'
|
|
788
|
+
}
|
|
789
|
+
return undefined
|
|
790
|
+
},
|
|
791
|
+
},
|
|
792
|
+
})
|
|
793
|
+
|
|
794
|
+
passField.mount()
|
|
795
|
+
passconfirmField.mount()
|
|
796
|
+
|
|
797
|
+
passField.setValue('one', { touch: true })
|
|
798
|
+
expect(passconfirmField.state.meta.errors).toStrictEqual([])
|
|
799
|
+
passField.handleBlur()
|
|
800
|
+
expect(passconfirmField.state.meta.errors).toStrictEqual([
|
|
801
|
+
'Passwords do not match',
|
|
802
|
+
])
|
|
803
|
+
passconfirmField.setValue('one', { touch: true })
|
|
804
|
+
expect(passconfirmField.state.meta.errors).toStrictEqual([
|
|
805
|
+
'Passwords do not match',
|
|
806
|
+
])
|
|
807
|
+
passField.handleBlur()
|
|
808
|
+
expect(passconfirmField.state.meta.errors).toStrictEqual([])
|
|
809
|
+
passField.setValue('two', { touch: true })
|
|
810
|
+
passField.handleBlur()
|
|
811
|
+
expect(passconfirmField.state.meta.errors).toStrictEqual([
|
|
812
|
+
'Passwords do not match',
|
|
813
|
+
])
|
|
814
|
+
})
|
|
815
|
+
|
|
816
|
+
it('should run onChangeAsync on a linked field', async () => {
|
|
817
|
+
vi.useRealTimers()
|
|
818
|
+
let resolve!: () => void
|
|
819
|
+
let promise = new Promise((r) => {
|
|
820
|
+
resolve = r as never
|
|
821
|
+
})
|
|
822
|
+
|
|
823
|
+
const fn = vi.fn()
|
|
824
|
+
|
|
825
|
+
const form = new FormApi({
|
|
826
|
+
defaultValues: {
|
|
827
|
+
password: '',
|
|
828
|
+
confirm_password: '',
|
|
829
|
+
},
|
|
830
|
+
})
|
|
831
|
+
|
|
832
|
+
const passField = new FieldApi({
|
|
833
|
+
form,
|
|
834
|
+
name: 'password',
|
|
835
|
+
})
|
|
836
|
+
|
|
837
|
+
const passconfirmField = new FieldApi({
|
|
838
|
+
form,
|
|
839
|
+
name: 'confirm_password',
|
|
840
|
+
validators: {
|
|
841
|
+
onChangeListenTo: ['password'],
|
|
842
|
+
onChangeAsync: async ({ value, fieldApi }) => {
|
|
843
|
+
await promise
|
|
844
|
+
fn()
|
|
845
|
+
if (value !== fieldApi.form.getFieldValue('password')) {
|
|
846
|
+
return 'Passwords do not match'
|
|
847
|
+
}
|
|
848
|
+
return undefined
|
|
849
|
+
},
|
|
850
|
+
},
|
|
851
|
+
})
|
|
852
|
+
|
|
853
|
+
passField.mount()
|
|
854
|
+
passconfirmField.mount()
|
|
855
|
+
|
|
856
|
+
passField.setValue('one', { touch: true })
|
|
857
|
+
resolve()
|
|
858
|
+
// Allow for a micro-tick to allow the promise to resolve
|
|
859
|
+
await sleep(1)
|
|
860
|
+
expect(passconfirmField.state.meta.errors).toStrictEqual([
|
|
861
|
+
'Passwords do not match',
|
|
862
|
+
])
|
|
863
|
+
promise = new Promise((r) => {
|
|
864
|
+
resolve = r as never
|
|
865
|
+
})
|
|
866
|
+
passconfirmField.setValue('one', { touch: true })
|
|
867
|
+
resolve()
|
|
868
|
+
// Allow for a micro-tick to allow the promise to resolve
|
|
869
|
+
await sleep(1)
|
|
870
|
+
expect(passconfirmField.state.meta.errors).toStrictEqual([])
|
|
871
|
+
promise = new Promise((r) => {
|
|
872
|
+
resolve = r as never
|
|
873
|
+
})
|
|
874
|
+
passField.setValue('two', { touch: true })
|
|
875
|
+
resolve()
|
|
876
|
+
// Allow for a micro-tick to allow the promise to resolve
|
|
877
|
+
await sleep(1)
|
|
878
|
+
expect(passconfirmField.state.meta.errors).toStrictEqual([
|
|
879
|
+
'Passwords do not match',
|
|
880
|
+
])
|
|
881
|
+
})
|
|
724
882
|
})
|
|
@@ -192,6 +192,70 @@ describe('form api', () => {
|
|
|
192
192
|
})
|
|
193
193
|
})
|
|
194
194
|
|
|
195
|
+
it('should not wipe validators when resetting', () => {
|
|
196
|
+
const form = new FormApi({
|
|
197
|
+
defaultValues: {
|
|
198
|
+
name: 'test',
|
|
199
|
+
},
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
const field = new FieldApi({
|
|
203
|
+
form,
|
|
204
|
+
name: 'name',
|
|
205
|
+
validators: {
|
|
206
|
+
onChange: ({ value }) => (value.length > 0 ? undefined : 'required'),
|
|
207
|
+
},
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
form.mount()
|
|
211
|
+
|
|
212
|
+
field.mount()
|
|
213
|
+
|
|
214
|
+
field.handleChange('')
|
|
215
|
+
|
|
216
|
+
expect(form.state.isFieldsValid).toEqual(false)
|
|
217
|
+
expect(form.state.canSubmit).toEqual(false)
|
|
218
|
+
|
|
219
|
+
form.reset()
|
|
220
|
+
|
|
221
|
+
expect(form.state).toEqual({
|
|
222
|
+
values: { name: 'test' },
|
|
223
|
+
errors: [],
|
|
224
|
+
errorMap: {},
|
|
225
|
+
fieldMeta: {
|
|
226
|
+
name: {
|
|
227
|
+
isValidating: false,
|
|
228
|
+
isTouched: false,
|
|
229
|
+
isDirty: false,
|
|
230
|
+
isPristine: true,
|
|
231
|
+
touchedErrors: [],
|
|
232
|
+
errors: [],
|
|
233
|
+
errorMap: {},
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
canSubmit: true,
|
|
237
|
+
isFieldsValid: true,
|
|
238
|
+
isFieldsValidating: false,
|
|
239
|
+
isFormValid: true,
|
|
240
|
+
isFormValidating: false,
|
|
241
|
+
isSubmitted: false,
|
|
242
|
+
isSubmitting: false,
|
|
243
|
+
isTouched: false,
|
|
244
|
+
isPristine: true,
|
|
245
|
+
isDirty: false,
|
|
246
|
+
isValid: true,
|
|
247
|
+
isValidating: false,
|
|
248
|
+
submissionAttempts: 0,
|
|
249
|
+
validationMetaMap: {
|
|
250
|
+
onChange: undefined,
|
|
251
|
+
onBlur: undefined,
|
|
252
|
+
onSubmit: undefined,
|
|
253
|
+
onMount: undefined,
|
|
254
|
+
onServer: undefined,
|
|
255
|
+
},
|
|
256
|
+
})
|
|
257
|
+
})
|
|
258
|
+
|
|
195
259
|
it("should get a field's value", () => {
|
|
196
260
|
const form = new FormApi({
|
|
197
261
|
defaultValues: {
|
package/src/utils.ts
CHANGED
|
@@ -157,7 +157,7 @@ interface AsyncValidatorArrayPartialOptions<T> {
|
|
|
157
157
|
asyncDebounceMs?: number
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
interface AsyncValidator<T> {
|
|
160
|
+
export interface AsyncValidator<T> {
|
|
161
161
|
cause: ValidationCause
|
|
162
162
|
validate: T
|
|
163
163
|
debounceMs: number
|
|
@@ -226,7 +226,7 @@ interface SyncValidatorArrayPartialOptions<T> {
|
|
|
226
226
|
validators?: T
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
interface SyncValidator<T> {
|
|
229
|
+
export interface SyncValidator<T> {
|
|
230
230
|
cause: ValidationCause
|
|
231
231
|
validate: T
|
|
232
232
|
}
|