sanity-advanced-validators 0.3.0 → 0.4.0
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/README.md +130 -137
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
_🚓 Never trust a user! 👮_
|
|
2
2
|
|
|
3
3
|
# Sanity Advanced Validators
|
|
4
4
|
|
|
5
|
-
This package includes a set of Sanity validators for aggressive and weird edge cases.
|
|
6
|
-
|
|
7
|
-
Note: Every validator can accept an optional custom error message as its last parameter. See [minDimensions](#minDimensions) for an example.
|
|
5
|
+
This package includes a set of Sanity validators for aggressive and weird edge cases. _Maintain sanity with micro-managed validation._
|
|
8
6
|
|
|
9
7
|
## Tools
|
|
10
8
|
|
|
@@ -15,12 +13,14 @@ Note: Every validator can accept an optional custom error message as its last pa
|
|
|
15
13
|
- [requiredIfSiblingNeq](#requiredIfSiblingNeq)
|
|
16
14
|
- [requiredIfSlugEq](#requiredIfSlugEq)
|
|
17
15
|
- [requiredIfSlugNeq](#requiredIfSlugNeq)
|
|
16
|
+
- [regex](#regex) 🆕
|
|
18
17
|
- [referencedDocumentRequires](#referencedDocumentRequires)
|
|
19
18
|
- [maxDepth](#maxDepth)
|
|
20
19
|
|
|
21
20
|
## Mega-example
|
|
22
21
|
|
|
23
22
|
Imagine that you’ve got a document that has an optional video file, but…
|
|
23
|
+
|
|
24
24
|
- it’s required on the `/about` page
|
|
25
25
|
- if the video exists, it must be either **MP4** or **MOV**
|
|
26
26
|
- and there must be a poster image that's between **1250x800** and **2500x1600** pixels in size
|
|
@@ -38,7 +38,7 @@ const Page = defineType({
|
|
|
38
38
|
name: "someVideoFile",
|
|
39
39
|
type: "file",
|
|
40
40
|
validation: (rule) =>
|
|
41
|
-
rule.custom(requiredIfSlugEq('about'))
|
|
41
|
+
rule.custom(requiredIfSlugEq('about', 'A video is required if {slugKey} is {operand}.'))
|
|
42
42
|
.custom(fileExtension(['mp4', 'mov']))
|
|
43
43
|
})
|
|
44
44
|
defineField({
|
|
@@ -60,6 +60,11 @@ const Page = defineType({
|
|
|
60
60
|
|
|
61
61
|
Enforces that an uploaded file asset is of a certain format.
|
|
62
62
|
|
|
63
|
+
```typescript
|
|
64
|
+
fileType: string | Array<string>,
|
|
65
|
+
message?: string // optional custom error message; replaces {validFileExtension} with fileType (flattened)
|
|
66
|
+
```
|
|
67
|
+
|
|
63
68
|
```typescript
|
|
64
69
|
import { fileExtension } from "sanity-advanced-validation"
|
|
65
70
|
|
|
@@ -81,17 +86,16 @@ const Page = defineType({
|
|
|
81
86
|
})
|
|
82
87
|
```
|
|
83
88
|
|
|
84
|
-
#### parameters
|
|
85
|
-
```typescript
|
|
86
|
-
fileType: string | Array<string>,
|
|
87
|
-
message?: string // optional custom error message; replaces {validFileExtension} with fileType (flattened)
|
|
88
|
-
```
|
|
89
|
-
|
|
90
89
|
---
|
|
91
90
|
|
|
92
91
|
### minDimensions
|
|
93
92
|
|
|
94
|
-
Enforces that an uploaded image asset is at minimum certain dimensions.
|
|
93
|
+
Enforces that an uploaded image asset is at minimum certain dimensions. You can test on both, just x, or just y.
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
dimensions: {x?: number, y?: number},
|
|
97
|
+
message?: string // optional custom error message; replaces {x} and {y} with your dimension requirements, and {width} and {height} with submitted image dimensions
|
|
98
|
+
```
|
|
95
99
|
|
|
96
100
|
```typescript
|
|
97
101
|
import { minDimensions } from "sanity-advanced-validation"
|
|
@@ -110,35 +114,17 @@ const ImageWithCaption = defineType({
|
|
|
110
114
|
})
|
|
111
115
|
```
|
|
112
116
|
|
|
113
|
-
|
|
117
|
+
---
|
|
114
118
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
type: "image",
|
|
119
|
-
description: "At least 1200px wide; as tall as you like.",
|
|
120
|
-
validation: (rule) => rule.custom(
|
|
121
|
-
minDimensions(
|
|
122
|
-
{ x: 1200 },
|
|
123
|
-
"Uh oh, your image is {width} pixels wide. That’s less than {x}!"
|
|
124
|
-
)
|
|
125
|
-
),
|
|
126
|
-
})
|
|
127
|
-
```
|
|
119
|
+
### maxDimensions
|
|
120
|
+
|
|
121
|
+
Enforces that an uploaded image asset is at most certain dimensions. You can test on both, just x, or just y.
|
|
128
122
|
|
|
129
|
-
#### parameters
|
|
130
123
|
```typescript
|
|
131
|
-
dimensions: {x?: number, y?: number},
|
|
124
|
+
dimensions: {x?: number, y?: number},
|
|
132
125
|
message?: string // optional custom error message; replaces {x} and {y} with your dimension requirements, and {width} and {height} with submitted image dimensions
|
|
133
126
|
```
|
|
134
127
|
|
|
135
|
-
---
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
### maxDimensions
|
|
139
|
-
|
|
140
|
-
Enforces that an uploaded image asset is at most certain dimensions.
|
|
141
|
-
|
|
142
128
|
```typescript
|
|
143
129
|
defineField({
|
|
144
130
|
name: "heroImage",
|
|
@@ -162,23 +148,22 @@ defineField({
|
|
|
162
148
|
})
|
|
163
149
|
```
|
|
164
150
|
|
|
165
|
-
#### parameters
|
|
166
|
-
```typescript
|
|
167
|
-
dimensions: {x?: number, y?: number},
|
|
168
|
-
message?: string // optional custom error message; replaces {x} and {y} with your dimension requirements, and {width} and {height} with submitted image dimensions
|
|
169
|
-
```
|
|
170
|
-
|
|
171
151
|
---
|
|
172
152
|
|
|
173
|
-
|
|
174
153
|
### requiredIfSiblingEq
|
|
175
154
|
|
|
176
|
-
Mark a field as `required` if a sibling field has a particular value. This is the validator we use most.
|
|
155
|
+
Mark a field as `required` if a sibling field has a particular value. This is the validator we use most. _It’s super effective!_
|
|
177
156
|
|
|
178
|
-
This is handy if you have a field that is hidden under some circumstances, but is `required()` when it’s visible.
|
|
157
|
+
This is handy if you have a field that is hidden under some circumstances, but is `required()` when it’s visible.
|
|
179
158
|
|
|
180
159
|
_note:_ This does not work for slugs, because they have to match a nested `.current` value. Use the [requiredIfSlugEq validator](#requiredIfSlugEq) instead.
|
|
181
160
|
|
|
161
|
+
```typescript
|
|
162
|
+
key: string, // name of sibling
|
|
163
|
+
operand: string | number | null | Array<string, number, null> // value that you’re testing for (i.e. if 'name' === operand)
|
|
164
|
+
message?: string // optional custom error message; replaces {key} and {operand} with your input, and {siblingValue} with the value of the sibling you’re testing against.
|
|
165
|
+
```
|
|
166
|
+
|
|
182
167
|
```typescript
|
|
183
168
|
import {requiredIfSiblingEq} from 'sanity-advanced-validation'
|
|
184
169
|
|
|
@@ -231,8 +216,8 @@ defineType({
|
|
|
231
216
|
name: 'phone',
|
|
232
217
|
type: 'string',
|
|
233
218
|
validation: rule => rule.custom(requiredIfSiblingEq(
|
|
234
|
-
'email',
|
|
235
|
-
null,
|
|
219
|
+
'email',
|
|
220
|
+
null,
|
|
236
221
|
"If you don’t have an email address, a phone number is required."
|
|
237
222
|
))
|
|
238
223
|
})
|
|
@@ -244,51 +229,49 @@ And it even works for arrays.
|
|
|
244
229
|
|
|
245
230
|
```typescript
|
|
246
231
|
defineType({
|
|
247
|
-
name:
|
|
248
|
-
type:
|
|
232
|
+
name: "person",
|
|
233
|
+
type: "object",
|
|
249
234
|
fields: [
|
|
250
235
|
defineField({
|
|
251
|
-
name:
|
|
252
|
-
type:
|
|
236
|
+
name: "name",
|
|
237
|
+
type: "string",
|
|
253
238
|
}),
|
|
254
239
|
defineField({
|
|
255
|
-
name:
|
|
256
|
-
type:
|
|
240
|
+
name: "name",
|
|
241
|
+
type: "string",
|
|
257
242
|
}),
|
|
258
243
|
defineField({
|
|
259
|
-
name:
|
|
260
|
-
type:
|
|
244
|
+
name: "occupation",
|
|
245
|
+
type: "string",
|
|
261
246
|
options: {
|
|
262
|
-
list: [
|
|
263
|
-
}
|
|
247
|
+
list: ["doctor", "lawyer", "software engineer"],
|
|
248
|
+
},
|
|
264
249
|
}),
|
|
265
250
|
defineField({
|
|
266
|
-
name:
|
|
267
|
-
description:
|
|
268
|
-
type:
|
|
269
|
-
validation: rule => rule.custom(requiredIfSiblingEq(
|
|
270
|
-
hidden: ({parent}) => parent.occuption ===
|
|
271
|
-
})
|
|
251
|
+
name: "explanation",
|
|
252
|
+
description: "Why are you wasting your life this way?",
|
|
253
|
+
type: "text",
|
|
254
|
+
validation: (rule) => rule.custom(requiredIfSiblingEq("occupation", ["doctor", "lawyer"])),
|
|
255
|
+
hidden: ({ parent }) => parent.occuption === "software engineer",
|
|
256
|
+
}),
|
|
272
257
|
],
|
|
273
258
|
})
|
|
274
259
|
```
|
|
275
260
|
|
|
276
|
-
#### parameters
|
|
277
|
-
```typescript
|
|
278
|
-
key: string, // name of sibling
|
|
279
|
-
operand: string | number | null | Array<string, number, null> // value that you’re testing for (i.e. if 'name' === operand)
|
|
280
|
-
message?: string // optional custom error message; replaces {key} and {operand} with your input, and {siblingValue} with the value of the sibling you’re testing against.
|
|
281
|
-
```
|
|
282
|
-
|
|
283
261
|
---
|
|
284
262
|
|
|
285
|
-
|
|
286
263
|
### requiredIfSiblingNeq
|
|
287
264
|
|
|
288
265
|
For a given object that has multiple fields, mark a field as `required` if a sibling does _not_ have a particular value.
|
|
289
266
|
|
|
290
267
|
_note:_ This does not work for slugs, because they have to match a nested `.current` value. Use the [requiredIfSlugNeq validator](#requiredIfSlugNeq) instead.
|
|
291
268
|
|
|
269
|
+
```typescript
|
|
270
|
+
key: string, // name of sibling
|
|
271
|
+
operand: string | number | null | Array<string, number, null> // value that you’re testing for (i.e. if 'name' === operand)
|
|
272
|
+
message?: string // optional custom error message; replaces {key} and {operand} with your input, and {siblingValue} with the value of the sibling you’re testing against.
|
|
273
|
+
```
|
|
274
|
+
|
|
292
275
|
```typescript
|
|
293
276
|
import {requiredIfSiblingNeq} from 'sanity-advanced-validation'
|
|
294
277
|
|
|
@@ -317,41 +300,37 @@ defineType({
|
|
|
317
300
|
})
|
|
318
301
|
```
|
|
319
302
|
|
|
320
|
-
#### parameters
|
|
321
|
-
```typescript
|
|
322
|
-
key: string, // name of sibling
|
|
323
|
-
operand: string | number | null | Array<string, number, null> // value that you’re testing for (i.e. if 'name' === operand)
|
|
324
|
-
message?: string // optional custom error message; replaces {key} and {operand} with your input, and {siblingValue} with the value of the sibling you’re testing against.
|
|
325
|
-
```
|
|
326
|
-
|
|
327
303
|
---
|
|
328
304
|
|
|
329
|
-
|
|
330
305
|
### requiredIfSlugEq
|
|
331
306
|
|
|
332
|
-
|
|
307
|
+
Mark a field as `required` for documents with matching slugs.
|
|
333
308
|
|
|
334
309
|
```typescript
|
|
335
|
-
|
|
310
|
+
operand: string | number | null | Array<string, number, null> // possible slug or slugs you’re testing
|
|
311
|
+
key?: string, // name of sibling if not "slug"
|
|
312
|
+
message?: string // optional custom error message; replaces {slugKey} and {operand} with your input, and {siblingSlugValue} with the value of the sibling you’re testing against.
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
import { requiredIfSlugEq } from "sanity-advanced-validation"
|
|
336
317
|
|
|
337
318
|
defineType({
|
|
338
|
-
name:
|
|
339
|
-
type:
|
|
319
|
+
name: "page",
|
|
320
|
+
type: "document",
|
|
340
321
|
fields: [
|
|
341
322
|
defineField({
|
|
342
|
-
name:
|
|
343
|
-
type:
|
|
323
|
+
name: "slug",
|
|
324
|
+
type: "slug",
|
|
344
325
|
}),
|
|
345
326
|
defineField({
|
|
346
|
-
name:
|
|
347
|
-
type:
|
|
348
|
-
of: [
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
})
|
|
354
|
-
]
|
|
327
|
+
name: "questionsAndAnswers",
|
|
328
|
+
type: "array",
|
|
329
|
+
of: [{ type: "qaItem" }],
|
|
330
|
+
validation: (rule) => rule.custom(requiredIfSlugEq("faq")),
|
|
331
|
+
hidden: ({ parent }) => parent.slug.current !== "faq",
|
|
332
|
+
}),
|
|
333
|
+
],
|
|
355
334
|
})
|
|
356
335
|
```
|
|
357
336
|
|
|
@@ -364,59 +343,84 @@ defineField({
|
|
|
364
343
|
}),
|
|
365
344
|
```
|
|
366
345
|
|
|
367
|
-
#### parameters
|
|
368
|
-
```typescript
|
|
369
|
-
operand: string | number | null | Array<string, number, null> // possible slug or slugs you’re testing
|
|
370
|
-
key?: string, // name of sibling if not "slug"
|
|
371
|
-
message?: string // optional custom error message; replaces {slugKey} and {operand} with your input, and {siblingSlugValue} with the value of the sibling you’re testing against.
|
|
372
|
-
```
|
|
373
|
-
|
|
374
346
|
---
|
|
375
347
|
|
|
376
|
-
|
|
377
348
|
### requiredIfSlugNeq
|
|
378
349
|
|
|
379
350
|
Require fields on pages that don't match one or more slugs.
|
|
380
351
|
|
|
381
352
|
```typescript
|
|
382
|
-
|
|
353
|
+
operand: string | number | null | Array<string, number, null> // possible slug or slugs you’re testing
|
|
354
|
+
key?: string, // name of sibling if not "slug"
|
|
355
|
+
message?: string // optional custom error message; replaces {slugKey} and {operand} with your input, and {siblingSlugValue} with the value of the sibling you’re testing against.
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
import { requiredIfSlugNeq } from "sanity-advanced-validation"
|
|
383
360
|
|
|
384
361
|
defineType({
|
|
385
|
-
name:
|
|
386
|
-
type:
|
|
362
|
+
name: "page",
|
|
363
|
+
type: "document",
|
|
387
364
|
fields: [
|
|
388
365
|
defineField({
|
|
389
|
-
name:
|
|
390
|
-
type:
|
|
366
|
+
name: "slug",
|
|
367
|
+
type: "slug",
|
|
391
368
|
}),
|
|
392
369
|
defineField({
|
|
393
|
-
name:
|
|
370
|
+
name: "subnav",
|
|
394
371
|
description: `Subnav is required on documents that aren’t '/home'`,
|
|
395
|
-
type:
|
|
396
|
-
of: [
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
})
|
|
402
|
-
]
|
|
372
|
+
type: "array",
|
|
373
|
+
of: [{ type: "navLink" }],
|
|
374
|
+
validation: (rule) => rule.custom(requiredIfSlugNeq("home")),
|
|
375
|
+
hidden: ({ parent }) => parent.slug.current !== "home",
|
|
376
|
+
}),
|
|
377
|
+
],
|
|
403
378
|
})
|
|
404
379
|
```
|
|
405
380
|
|
|
406
|
-
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
### regex
|
|
384
|
+
|
|
385
|
+
Easily test any value against a regular expression.
|
|
386
|
+
|
|
387
|
+
Values can be of type string, number, boolean… even objects!
|
|
388
|
+
|
|
407
389
|
```typescript
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
message?: string // optional custom error message; replaces {slugKey} and {operand} with your input, and {siblingSlugValue} with the value of the sibling you’re testing against.
|
|
390
|
+
pattern: RegExp // regular expression
|
|
391
|
+
message?: string // optional custom error message; replaces {pattern} with your input and {value} as submitted field value
|
|
411
392
|
```
|
|
412
393
|
|
|
413
|
-
|
|
394
|
+
```typescript
|
|
395
|
+
defineField({
|
|
396
|
+
name: 'email',
|
|
397
|
+
type: 'string',
|
|
398
|
+
validation: (rule) => rule.custom(
|
|
399
|
+
regex(
|
|
400
|
+
/^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6})*$/,
|
|
401
|
+
"“{value}” is not a valid email address."
|
|
402
|
+
)
|
|
403
|
+
),
|
|
404
|
+
}),
|
|
405
|
+
```
|
|
414
406
|
|
|
407
|
+
**Custom error messages are highly recommended here.** Without the custom message above, the default response would be:
|
|
408
|
+
```
|
|
409
|
+
“me@googlecom” does not match the pattern /^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6})*$/.
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
---
|
|
415
413
|
|
|
416
414
|
### referencedDocumentRequires
|
|
417
415
|
|
|
418
416
|
You might want to enforce some validation on a referenced document. This validator enforces that a given value is not null in the referenced document.
|
|
419
417
|
|
|
418
|
+
```typescript
|
|
419
|
+
documentType: string // type of document you’re referring to
|
|
420
|
+
field: string, // name of document field that is required
|
|
421
|
+
message?: string // optional custom error message; replaces {documentType} and {field} with your input
|
|
422
|
+
```
|
|
423
|
+
|
|
420
424
|
```typescript
|
|
421
425
|
defineField({
|
|
422
426
|
name: 'referredArticle',
|
|
@@ -427,16 +431,8 @@ defineField({
|
|
|
427
431
|
}),
|
|
428
432
|
```
|
|
429
433
|
|
|
430
|
-
#### parameters
|
|
431
|
-
```typescript
|
|
432
|
-
documentType: string // type of document you’re referring to
|
|
433
|
-
field: string, // name of document field that is required
|
|
434
|
-
message?: string // optional custom error message; replaces {documentType} and {field} with your input
|
|
435
|
-
```
|
|
436
|
-
|
|
437
434
|
---
|
|
438
435
|
|
|
439
|
-
|
|
440
436
|
### maxDepth
|
|
441
437
|
|
|
442
438
|
It can be useful to have a nested type. This often comes up when making some kind of navigation tree, like…
|
|
@@ -490,6 +486,12 @@ const navLink = defineType({
|
|
|
490
486
|
|
|
491
487
|
… but your users might get a little stupid with this, and you may want to enforce navigations only going _n_ layers deep.
|
|
492
488
|
|
|
489
|
+
```typescript
|
|
490
|
+
maxDepth: number // maximum "depth" of embedding (including parent)
|
|
491
|
+
key: string, // name of the field that includes the cursive value (i.e. the field’s own name)
|
|
492
|
+
message?: string // optional custom error message; replaces {maxDepth} and {key} with your input
|
|
493
|
+
```
|
|
494
|
+
|
|
493
495
|
```typescript
|
|
494
496
|
import { maxDepth } from "sanity-advanced-validation"
|
|
495
497
|
|
|
@@ -509,17 +511,8 @@ const navLink = defineType({
|
|
|
509
511
|
|
|
510
512
|
This will enforce that a subnav list can embed in a subnav, which can also be embedded in a subnav — but no further.
|
|
511
513
|
|
|
512
|
-
|
|
513
|
-
#### parameters
|
|
514
|
-
```typescript
|
|
515
|
-
maxDepth: number // maximum "depth" of embedding (including parent)
|
|
516
|
-
key: string, // name of the field that includes the cursive value (i.e. the field’s own name)
|
|
517
|
-
message?: string // optional custom error message; replaces {maxDepth} and {key} with your input
|
|
518
|
-
```
|
|
519
|
-
|
|
520
514
|
---
|
|
521
515
|
|
|
522
|
-
|
|
523
516
|
#### Note to any Sanity dev who looks at this
|
|
524
517
|
|
|
525
518
|
I’d love to include similar logic on my `hidden:` attribute, but I don’t think that’t possible without a `path` array in `hidden`’s `ConditionalPropertyCallbackContext` that’s similar to the one fed to the `ValidationContext` (todo: type this correctly). Wouldn’t this be cool?
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package-name": "sanity-advanced-validators",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Advanced input validation tools for Sanity CMS.",
|
|
5
5
|
"author": "Eric_WVGG",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"module": "./dist/index.cjs",
|
|
19
19
|
"types": "./dist/index.d.ts",
|
|
20
20
|
"files": [
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@sanity/asset-utils": "^2.2.1",
|
|
25
25
|
"lodash-es": "^4.0.0",
|