remix-validated-form 5.0.0 → 5.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remix-validated-form",
3
- "version": "5.0.0",
3
+ "version": "5.0.1",
4
4
  "description": "Form component and utils for easy form validation in remix",
5
5
  "main": "./dist/index.cjs.js",
6
6
  "module": "./dist/index.esm.js",
@@ -51,7 +51,7 @@ type SubactionData<
51
51
  // Not all validation libraries support encoding a literal value in the schema type (e.g. yup).
52
52
  // This condition here allows us to provide strictness for users who are using a validation library that does support it,
53
53
  // but also allows us to support users who are using a validation library that doesn't support it.
54
- type DefaultValuesForSubaction<
54
+ type DataForSubaction<
55
55
  DataType,
56
56
  Subaction extends string | undefined
57
57
  > = Subaction extends string // Not all validation libraries support encoding a literal value in the schema type.
@@ -70,7 +70,7 @@ export type FormProps<DataType, Subaction extends string | undefined> = {
70
70
  * after all validations have been run.
71
71
  */
72
72
  onSubmit?: (
73
- data: DataType,
73
+ data: DataForSubaction<DataType, Subaction>,
74
74
  event: React.FormEvent<HTMLFormElement>
75
75
  ) => void | Promise<void>;
76
76
  /**
@@ -83,7 +83,7 @@ export type FormProps<DataType, Subaction extends string | undefined> = {
83
83
  * Accepts an object of default values for the form
84
84
  * that will automatically be propagated to the form fields via `useField`.
85
85
  */
86
- defaultValues?: Partial<DefaultValuesForSubaction<DataType, Subaction>>;
86
+ defaultValues?: Partial<DataForSubaction<DataType, Subaction>>;
87
87
  /**
88
88
  * A ref to the form element.
89
89
  */
@@ -362,7 +362,7 @@ export function ValidatedForm<DataType, Subaction extends string | undefined>({
362
362
  } else {
363
363
  setFieldErrors({});
364
364
  const eventProxy = formEventProxy(e);
365
- await onSubmit?.(result.data, eventProxy);
365
+ await onSubmit?.(result.data as any, eventProxy);
366
366
  if (eventProxy.defaultPrevented) {
367
367
  endSubmit();
368
368
  return;
package/tsconfig.json CHANGED
@@ -4,5 +4,5 @@
4
4
  "module": "esnext"
5
5
  },
6
6
  "include": ["src/**/*.ts", "src/**/*.tsx"],
7
- "exclude": ["node_modules", "**/*.test.ts", "**/*.test.tsx"]
7
+ "exclude": ["node_modules"]
8
8
  }
@@ -1,330 +0,0 @@
1
- import { anyString, TestFormData } from "@remix-validated-form/test-utils";
2
- import { withYup } from "@remix-validated-form/with-yup/src";
3
- import { withZod } from "@remix-validated-form/with-zod";
4
- import * as R from "remeda";
5
- import { Validator } from "remix-validated-form/src";
6
- import { objectFromPathEntries } from "remix-validated-form/src/internal/flatten";
7
- import { describe, it, expect } from "vitest";
8
- import * as yup from "yup";
9
- import { z } from "zod";
10
- import { FORM_ID_FIELD } from "../internal/constants";
11
-
12
- // If adding an adapter, write a validator that validates this shape
13
- type Person = {
14
- firstName: string;
15
- lastName: string;
16
- age?: number;
17
- address: {
18
- streetAddress: string;
19
- city: string;
20
- country: string;
21
- };
22
- pets?: {
23
- animal: string;
24
- name: string;
25
- }[];
26
- };
27
-
28
- type ValidationTestCase = {
29
- name: string;
30
- validator: Validator<Person>;
31
- };
32
-
33
- const validationTestCases: ValidationTestCase[] = [
34
- {
35
- name: "yup",
36
- validator: withYup(
37
- yup.object({
38
- firstName: yup.string().required(),
39
- lastName: yup.string().required(),
40
- age: yup.number(),
41
- address: yup
42
- .object({
43
- streetAddress: yup.string().required(),
44
- city: yup.string().required(),
45
- country: yup.string().required(),
46
- })
47
- .required(),
48
- pets: yup.array().of(
49
- yup.object({
50
- animal: yup.string().required(),
51
- name: yup.string().required(),
52
- })
53
- ),
54
- })
55
- ),
56
- },
57
- {
58
- name: "zod",
59
- validator: withZod(
60
- z.object({
61
- firstName: z.string().min(1),
62
- lastName: z.string().min(1),
63
- age: z.optional(z.number()),
64
- address: z.preprocess(
65
- (value) => (value == null ? {} : value),
66
- z.object({
67
- streetAddress: z.string().min(1),
68
- city: z.string().min(1),
69
- country: z.string().min(1),
70
- })
71
- ),
72
- pets: z
73
- .object({
74
- animal: z.string().min(1),
75
- name: z.string().min(1),
76
- })
77
- .array()
78
- .optional(),
79
- })
80
- ),
81
- },
82
- ];
83
-
84
- describe("Validation", () => {
85
- describe.each(validationTestCases)("Adapter for $name", ({ validator }) => {
86
- describe("validate", () => {
87
- it("should return the data when valid", async () => {
88
- const person: Person = {
89
- firstName: "John",
90
- lastName: "Doe",
91
- age: 30,
92
- address: {
93
- streetAddress: "123 Main St",
94
- city: "Anytown",
95
- country: "USA",
96
- },
97
- pets: [{ animal: "dog", name: "Fido" }],
98
- };
99
- expect(await validator.validate(person)).toEqual({
100
- data: person,
101
- error: undefined,
102
- submittedData: person,
103
- });
104
- });
105
-
106
- it("should omit internal fields", async () => {
107
- const person: Person = {
108
- firstName: "John",
109
- lastName: "Doe",
110
- age: 30,
111
- address: {
112
- streetAddress: "123 Main St",
113
- city: "Anytown",
114
- country: "USA",
115
- },
116
- pets: [{ animal: "dog", name: "Fido" }],
117
-
118
- // @ts-expect-error
119
- // internal filed technically not part of person type
120
- [FORM_ID_FIELD]: "something",
121
- };
122
- expect(await validator.validate(person)).toEqual({
123
- data: R.omit(person as any, [FORM_ID_FIELD]),
124
- error: undefined,
125
- submittedData: person,
126
- formId: "something",
127
- });
128
- });
129
-
130
- it("should return field errors when invalid", async () => {
131
- const obj = { age: "hi!", pets: [{ animal: "dog" }] };
132
- expect(await validator.validate(obj)).toEqual({
133
- data: undefined,
134
- error: {
135
- fieldErrors: {
136
- firstName: anyString,
137
- lastName: anyString,
138
- age: anyString,
139
- "address.city": anyString,
140
- "address.country": anyString,
141
- "address.streetAddress": anyString,
142
- "pets[0].name": anyString,
143
- },
144
- subaction: undefined,
145
- },
146
- submittedData: obj,
147
- });
148
- });
149
-
150
- it("should unflatten data when validating", async () => {
151
- const data = {
152
- firstName: "John",
153
- lastName: "Doe",
154
- age: 30,
155
- "address.streetAddress": "123 Main St",
156
- "address.city": "Anytown",
157
- "address.country": "USA",
158
- "pets[0].animal": "dog",
159
- "pets[0].name": "Fido",
160
- };
161
- expect(await validator.validate(data)).toEqual({
162
- data: {
163
- firstName: "John",
164
- lastName: "Doe",
165
- age: 30,
166
- address: {
167
- streetAddress: "123 Main St",
168
- city: "Anytown",
169
- country: "USA",
170
- },
171
- pets: [{ animal: "dog", name: "Fido" }],
172
- },
173
- error: undefined,
174
- submittedData: objectFromPathEntries(Object.entries(data)),
175
- });
176
- });
177
-
178
- it("should accept FormData directly and return errors", async () => {
179
- const formData = new TestFormData();
180
- formData.set("firstName", "John");
181
- formData.set("lastName", "Doe");
182
- formData.set("address.streetAddress", "123 Main St");
183
- formData.set("address.country", "USA");
184
- formData.set("pets[0].animal", "dog");
185
-
186
- expect(await validator.validate(formData)).toEqual({
187
- data: undefined,
188
- error: {
189
- fieldErrors: {
190
- "address.city": anyString,
191
- "pets[0].name": anyString,
192
- },
193
- subaction: undefined,
194
- },
195
- submittedData: objectFromPathEntries([...formData.entries()]),
196
- });
197
- });
198
-
199
- it("should accept FormData directly and return valid data", async () => {
200
- const formData = new TestFormData();
201
- formData.set("firstName", "John");
202
- formData.set("lastName", "Doe");
203
- formData.set("address.streetAddress", "123 Main St");
204
- formData.set("address.country", "USA");
205
- formData.set("address.city", "Anytown");
206
- formData.set("pets[0].animal", "dog");
207
- formData.set("pets[0].name", "Fido");
208
-
209
- expect(await validator.validate(formData)).toEqual({
210
- data: {
211
- firstName: "John",
212
- lastName: "Doe",
213
- address: {
214
- streetAddress: "123 Main St",
215
- country: "USA",
216
- city: "Anytown",
217
- },
218
- pets: [{ animal: "dog", name: "Fido" }],
219
- },
220
- error: undefined,
221
- subaction: undefined,
222
- submittedData: objectFromPathEntries([...formData.entries()]),
223
- });
224
- });
225
-
226
- it("should return the subaction in the ValidatorError if there is one", async () => {
227
- const person = {
228
- lastName: "Doe",
229
- age: 20,
230
- address: {
231
- streetAddress: "123 Main St",
232
- city: "Anytown",
233
- country: "USA",
234
- },
235
- pets: [{ animal: "dog", name: "Fido" }],
236
- subaction: "updatePerson",
237
- };
238
- expect(await validator.validate(person)).toEqual({
239
- error: {
240
- fieldErrors: {
241
- firstName: anyString,
242
- },
243
- subaction: "updatePerson",
244
- },
245
- data: undefined,
246
- submittedData: person,
247
- });
248
- });
249
- });
250
-
251
- describe("validateField", () => {
252
- it("should not return an error if field is valid", async () => {
253
- const person = {
254
- firstName: "John",
255
- lastName: {}, // invalid, but we should only be validating firstName
256
- };
257
- expect(await validator.validateField(person, "firstName")).toEqual({
258
- error: undefined,
259
- });
260
- });
261
- it("should not return an error if a nested field is valid", async () => {
262
- const person = {
263
- firstName: "John",
264
- lastName: {}, // invalid, but we should only be validating firstName
265
- address: {
266
- streetAddress: "123 Main St",
267
- city: "Anytown",
268
- country: "USA",
269
- },
270
- pets: [{ animal: "dog", name: "Fido" }],
271
- };
272
- expect(
273
- await validator.validateField(person, "address.streetAddress")
274
- ).toEqual({
275
- error: undefined,
276
- });
277
- expect(await validator.validateField(person, "address.city")).toEqual({
278
- error: undefined,
279
- });
280
- expect(
281
- await validator.validateField(person, "address.country")
282
- ).toEqual({
283
- error: undefined,
284
- });
285
- expect(await validator.validateField(person, "pets[0].animal")).toEqual(
286
- {
287
- error: undefined,
288
- }
289
- );
290
- expect(await validator.validateField(person, "pets[0].name")).toEqual({
291
- error: undefined,
292
- });
293
- });
294
-
295
- it("should return an error if field is invalid", async () => {
296
- const person = {
297
- firstName: "John",
298
- lastName: {},
299
- address: {
300
- streetAddress: "123 Main St",
301
- city: 1234,
302
- },
303
- };
304
- expect(await validator.validateField(person, "lastName")).toEqual({
305
- error: anyString,
306
- });
307
- });
308
-
309
- it("should return an error if a nested field is invalid", async () => {
310
- const person = {
311
- firstName: "John",
312
- lastName: {},
313
- address: {
314
- streetAddress: "123 Main St",
315
- city: 1234,
316
- },
317
- pets: [{ animal: "dog" }],
318
- };
319
- expect(
320
- await validator.validateField(person, "address.country")
321
- ).toEqual({
322
- error: anyString,
323
- });
324
- expect(await validator.validateField(person, "pets[0].name")).toEqual({
325
- error: anyString,
326
- });
327
- });
328
- });
329
- });
330
- });