sanity-advanced-validators 0.3.1 → 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.
Files changed (2) hide show
  1. package/README.md +130 -137
  2. package/package.json +3 -3
package/README.md CHANGED
@@ -1,10 +1,8 @@
1
- *🚓 Never trust a user! 👮*
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. *Maintain sanity with micro-managed validation.*
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
- You can also enforce on only one dimension, or feed a custom error message:
117
+ ---
114
118
 
115
- ```typescript
116
- defineField({
117
- name: "heroImage",
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. *It’s super effective!*
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: 'person',
248
- type: 'object',
232
+ name: "person",
233
+ type: "object",
249
234
  fields: [
250
235
  defineField({
251
- name: 'name',
252
- type: 'string'
236
+ name: "name",
237
+ type: "string",
253
238
  }),
254
239
  defineField({
255
- name: 'name',
256
- type: 'string'
240
+ name: "name",
241
+ type: "string",
257
242
  }),
258
243
  defineField({
259
- name: 'occupation',
260
- type: 'string',
244
+ name: "occupation",
245
+ type: "string",
261
246
  options: {
262
- list: ['doctor', 'lawyer', 'software engineer']
263
- }
247
+ list: ["doctor", "lawyer", "software engineer"],
248
+ },
264
249
  }),
265
250
  defineField({
266
- name: 'explanation',
267
- description: 'Why are you wasting your life this way?',
268
- type: 'text',
269
- validation: rule => rule.custom(requiredIfSiblingEq('occupation', ['doctor', 'lawyer'])),
270
- hidden: ({parent}) => parent.occuption === 'software engineer',
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
- Require for matching slugs.
307
+ Mark a field as `required` for documents with matching slugs.
333
308
 
334
309
  ```typescript
335
- import {requiredIfSlugEq} from 'sanity-advanced-validation'
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: 'page',
339
- type: 'document',
319
+ name: "page",
320
+ type: "document",
340
321
  fields: [
341
322
  defineField({
342
- name: 'slug',
343
- type: 'slug'
323
+ name: "slug",
324
+ type: "slug",
344
325
  }),
345
326
  defineField({
346
- name: 'questionsAndAnswers',
347
- type: 'array',
348
- of: [
349
- {type: 'qaItem'}
350
- ],
351
- validation: rule => rule.custom(requiredIfSlugEq('faq')),
352
- hidden: ({parent}) => parent.slug.current !== 'faq'
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
- import {requiredIfSlugNeq} from 'sanity-advanced-validation'
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: 'page',
386
- type: 'document',
362
+ name: "page",
363
+ type: "document",
387
364
  fields: [
388
365
  defineField({
389
- name: 'slug',
390
- type: 'slug'
366
+ name: "slug",
367
+ type: "slug",
391
368
  }),
392
369
  defineField({
393
- name: 'subnav',
370
+ name: "subnav",
394
371
  description: `Subnav is required on documents that aren’t '/home'`,
395
- type: 'array',
396
- of: [
397
- {type: 'navLink'}
398
- ],
399
- validation: rule => rule.custom(requiredIfSlugNeq('home')),
400
- hidden: ({parent}) => parent.slug.current !== 'home'
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
- #### parameters
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
- operand: string | number | null | Array<string, number, null> // possible slug or slugs you’re testing
409
- key?: string, // name of sibling if not "slug"
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.1",
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
- "dist"
22
- ],
21
+ "dist"
22
+ ],
23
23
  "dependencies": {
24
24
  "@sanity/asset-utils": "^2.2.1",
25
25
  "lodash-es": "^4.0.0",