@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.
@@ -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
  }