@tanstack/react-form 0.10.0 → 0.10.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-form",
3
- "version": "0.10.0",
3
+ "version": "0.10.2",
4
4
  "description": "Powerful, type-safe forms for React.",
5
5
  "author": "tannerlinsley",
6
6
  "license": "MIT",
@@ -53,7 +53,7 @@
53
53
  "dependencies": {
54
54
  "@tanstack/react-store": "0.1.3",
55
55
  "@tanstack/store": "0.1.3",
56
- "@tanstack/form-core": "0.10.0"
56
+ "@tanstack/form-core": "0.10.2"
57
57
  },
58
58
  "peerDependencies": {
59
59
  "react": "^17.0.0 || ^18.0.0",
@@ -1,9 +1,10 @@
1
1
  /// <reference lib="dom" />
2
+ import '@testing-library/jest-dom'
2
3
  import { render, waitFor } from '@testing-library/react'
3
4
  import userEvent from '@testing-library/user-event'
4
- import '@testing-library/jest-dom'
5
5
  import * as React from 'react'
6
6
  import { createFormFactory, useForm } from '..'
7
+ import { sleep } from './utils'
7
8
 
8
9
  const user = userEvent.setup()
9
10
 
@@ -173,7 +174,6 @@ describe('useForm', () => {
173
174
  return error
174
175
  },
175
176
  })
176
-
177
177
  return (
178
178
  <form.Provider>
179
179
  <form.Field
@@ -202,4 +202,298 @@ describe('useForm', () => {
202
202
  await waitFor(() => getByText(error))
203
203
  expect(getByText(error)).toBeInTheDocument()
204
204
  })
205
+
206
+ it('should not validate on change if isTouched is false', async () => {
207
+ type Person = {
208
+ firstName: string
209
+ lastName: string
210
+ }
211
+ const error = 'Please enter a different value'
212
+
213
+ const formFactory = createFormFactory<Person, unknown>()
214
+
215
+ function Comp() {
216
+ const form = formFactory.useForm({
217
+ onChange: (value) => (value.firstName === 'other' ? error : undefined),
218
+ })
219
+
220
+ const errors = form.useStore((s) => s.errors)
221
+ return (
222
+ <form.Provider>
223
+ <form.Field
224
+ name="firstName"
225
+ children={(field) => (
226
+ <div>
227
+ <input
228
+ data-testid="fieldinput"
229
+ name={field.name}
230
+ value={field.state.value}
231
+ onBlur={field.handleBlur}
232
+ onChange={(e) => field.setValue(e.target.value)}
233
+ />
234
+ <p>{errors}</p>
235
+ </div>
236
+ )}
237
+ />
238
+ </form.Provider>
239
+ )
240
+ }
241
+
242
+ const { getByTestId, queryByText } = render(<Comp />)
243
+ const input = getByTestId('fieldinput')
244
+ await user.type(input, 'other')
245
+ expect(queryByText(error)).not.toBeInTheDocument()
246
+ })
247
+
248
+ it('should validate on change if isTouched is true', async () => {
249
+ type Person = {
250
+ firstName: string
251
+ lastName: string
252
+ }
253
+ const error = 'Please enter a different value'
254
+
255
+ const formFactory = createFormFactory<Person, unknown>()
256
+
257
+ function Comp() {
258
+ const form = formFactory.useForm({
259
+ onChange: (value) => (value.firstName === 'other' ? error : undefined),
260
+ })
261
+ const errors = form.useStore((s) => s.errorMap)
262
+ return (
263
+ <form.Provider>
264
+ <form.Field
265
+ name="firstName"
266
+ defaultMeta={{ isTouched: true }}
267
+ children={(field) => (
268
+ <div>
269
+ <input
270
+ data-testid="fieldinput"
271
+ name={field.name}
272
+ value={field.state.value}
273
+ onBlur={field.handleBlur}
274
+ onChange={(e) => field.handleChange(e.target.value)}
275
+ />
276
+ <p>{errors.onChange}</p>
277
+ </div>
278
+ )}
279
+ />
280
+ </form.Provider>
281
+ )
282
+ }
283
+
284
+ const { getByTestId, getByText, queryByText } = render(<Comp />)
285
+ const input = getByTestId('fieldinput')
286
+ expect(queryByText(error)).not.toBeInTheDocument()
287
+ await user.type(input, 'other')
288
+ expect(getByText(error)).toBeInTheDocument()
289
+ })
290
+
291
+ it('should validate on change and on blur', async () => {
292
+ const onChangeError = 'Please enter a different value (onChangeError)'
293
+ const onBlurError = 'Please enter a different value (onBlurError)'
294
+
295
+ function Comp() {
296
+ const form = useForm({
297
+ defaultValues: {
298
+ firstName: '',
299
+ },
300
+ onChange: (vals) => {
301
+ if (vals.firstName === 'other') return onChangeError
302
+ return undefined
303
+ },
304
+ onBlur: (vals) => {
305
+ if (vals.firstName === 'other') return onBlurError
306
+ return undefined
307
+ },
308
+ })
309
+
310
+ const errors = form.useStore((s) => s.errorMap)
311
+ return (
312
+ <form.Provider>
313
+ <form.Field
314
+ name="firstName"
315
+ defaultMeta={{ isTouched: true }}
316
+ children={(field) => (
317
+ <div>
318
+ <input
319
+ data-testid="fieldinput"
320
+ name={field.name}
321
+ value={field.state.value}
322
+ onBlur={field.handleBlur}
323
+ onChange={(e) => field.handleChange(e.target.value)}
324
+ />
325
+ <p>{errors.onChange}</p>
326
+ <p>{errors.onBlur}</p>
327
+ </div>
328
+ )}
329
+ />
330
+ </form.Provider>
331
+ )
332
+ }
333
+ const { getByTestId, getByText, queryByText } = render(<Comp />)
334
+ const input = getByTestId('fieldinput')
335
+ expect(queryByText(onChangeError)).not.toBeInTheDocument()
336
+ expect(queryByText(onBlurError)).not.toBeInTheDocument()
337
+ await user.type(input, 'other')
338
+ expect(getByText(onChangeError)).toBeInTheDocument()
339
+ await user.click(document.body)
340
+ expect(queryByText(onBlurError)).toBeInTheDocument()
341
+ })
342
+
343
+ it('should validate async on change', async () => {
344
+ type Person = {
345
+ firstName: string
346
+ lastName: string
347
+ }
348
+ const error = 'Please enter a different value'
349
+
350
+ const formFactory = createFormFactory<Person, unknown>()
351
+
352
+ function Comp() {
353
+ const form = formFactory.useForm({
354
+ onChangeAsync: async () => {
355
+ await sleep(10)
356
+ return error
357
+ },
358
+ })
359
+ const errors = form.useStore((s) => s.errorMap)
360
+ return (
361
+ <form.Provider>
362
+ <form.Field
363
+ name="firstName"
364
+ defaultMeta={{ isTouched: true }}
365
+ children={(field) => (
366
+ <div>
367
+ <input
368
+ data-testid="fieldinput"
369
+ name={field.name}
370
+ value={field.state.value}
371
+ onBlur={field.handleBlur}
372
+ onChange={(e) => field.handleChange(e.target.value)}
373
+ />
374
+ <p>{errors.onChange}</p>
375
+ </div>
376
+ )}
377
+ />
378
+ </form.Provider>
379
+ )
380
+ }
381
+
382
+ const { getByTestId, getByText, queryByText } = render(<Comp />)
383
+ const input = getByTestId('fieldinput')
384
+ expect(queryByText(error)).not.toBeInTheDocument()
385
+ await user.type(input, 'other')
386
+ await waitFor(() => getByText(error))
387
+ expect(getByText(error)).toBeInTheDocument()
388
+ })
389
+
390
+ it('should validate async on change and async on blur', async () => {
391
+ type Person = {
392
+ firstName: string
393
+ lastName: string
394
+ }
395
+ const onChangeError = 'Please enter a different value (onChangeError)'
396
+ const onBlurError = 'Please enter a different value (onBlurError)'
397
+
398
+ const formFactory = createFormFactory<Person, unknown>()
399
+
400
+ function Comp() {
401
+ const form = formFactory.useForm({
402
+ onChangeAsync: async () => {
403
+ await sleep(10)
404
+ return onChangeError
405
+ },
406
+ onBlurAsync: async () => {
407
+ await sleep(10)
408
+ return onBlurError
409
+ },
410
+ })
411
+ const errors = form.useStore((s) => s.errorMap)
412
+
413
+ return (
414
+ <form.Provider>
415
+ <form.Field
416
+ name="firstName"
417
+ defaultMeta={{ isTouched: true }}
418
+ children={(field) => (
419
+ <div>
420
+ <input
421
+ data-testid="fieldinput"
422
+ name={field.name}
423
+ value={field.state.value}
424
+ onBlur={field.handleBlur}
425
+ onChange={(e) => field.handleChange(e.target.value)}
426
+ />
427
+ <p>{errors.onChange}</p>
428
+ <p>{errors.onBlur}</p>
429
+ </div>
430
+ )}
431
+ />
432
+ </form.Provider>
433
+ )
434
+ }
435
+
436
+ const { getByTestId, getByText, queryByText } = render(<Comp />)
437
+ const input = getByTestId('fieldinput')
438
+
439
+ expect(queryByText(onChangeError)).not.toBeInTheDocument()
440
+ expect(queryByText(onBlurError)).not.toBeInTheDocument()
441
+ await user.type(input, 'other')
442
+ await waitFor(() => getByText(onChangeError))
443
+ expect(getByText(onChangeError)).toBeInTheDocument()
444
+ await user.click(document.body)
445
+ await waitFor(() => getByText(onBlurError))
446
+ expect(getByText(onBlurError)).toBeInTheDocument()
447
+ })
448
+
449
+ it('should validate async on change with debounce', async () => {
450
+ type Person = {
451
+ firstName: string
452
+ lastName: string
453
+ }
454
+ const mockFn = vi.fn()
455
+ const error = 'Please enter a different value'
456
+ const formFactory = createFormFactory<Person, unknown>()
457
+
458
+ function Comp() {
459
+ const form = formFactory.useForm({
460
+ onChangeAsyncDebounceMs: 100,
461
+ onChangeAsync: async () => {
462
+ mockFn()
463
+ await sleep(10)
464
+ return error
465
+ },
466
+ })
467
+ const errors = form.useStore((s) => s.errors)
468
+
469
+ return (
470
+ <form.Provider>
471
+ <form.Field
472
+ name="firstName"
473
+ defaultMeta={{ isTouched: true }}
474
+ children={(field) => (
475
+ <div>
476
+ <input
477
+ data-testid="fieldinput"
478
+ name={field.name}
479
+ value={field.state.value}
480
+ onBlur={field.handleBlur}
481
+ onChange={(e) => field.handleChange(e.target.value)}
482
+ />
483
+ <p>{errors}</p>
484
+ </div>
485
+ )}
486
+ />
487
+ </form.Provider>
488
+ )
489
+ }
490
+
491
+ const { getByTestId, getByText } = render(<Comp />)
492
+ const input = getByTestId('fieldinput')
493
+ await user.type(input, 'other')
494
+ // mockFn will have been called 5 times without onChangeAsyncDebounceMs
495
+ expect(mockFn).toHaveBeenCalledTimes(0)
496
+ await waitFor(() => getByText(error))
497
+ expect(getByText(error)).toBeInTheDocument()
498
+ })
205
499
  })