@@ -3,7 +3,7 @@ import FormObserver from "./FormObserver.js";
3
3
const radiogroupSelector = "fieldset[role='radiogroup']" ;
4
4
const attrs = Object . freeze ( { "aria-describedby" : "aria-describedby" , "aria-invalid" : "aria-invalid" } ) ;
5
5
6
- // NOTE: Generic `T` = Event TYPE. Generic `M` = Error MESSAGE. Generic `E` = ELEMENT
6
+ // NOTE: Generic `T` = Event TYPE. Generic `M` = Error MESSAGE. Generic `E` = ELEMENT. Generic `R` = RENDER by default.
7
7
8
8
/**
9
9
* @template M
@@ -14,42 +14,50 @@ const attrs = Object.freeze({ "aria-describedby": "aria-describedby", "aria-inva
14
14
/**
15
15
* @template M
16
16
* @template {import("./types.d.ts").ValidatableField} [E=import("./types.d.ts").ValidatableField]
17
- * @typedef {
18
- | ErrorMessage<string, E>
19
- | { render: true; message: ErrorMessage<M, E> }
20
- | { render?: false; message: ErrorMessage<string, E> }
17
+ * @template {boolean} [R=false]
18
+ * @typedef {R extends true
19
+ ?
20
+ | ErrorMessage<M, E>
21
+ | { render?: true; message: ErrorMessage<M, E> }
22
+ | { render: false; message: ErrorMessage<string, E> }
23
+ :
24
+ | ErrorMessage<string, E>
25
+ | { render: true; message: ErrorMessage<M, E> }
26
+ | { render?: false; message: ErrorMessage<string, E> }
21
27
} ErrorDetails
22
28
*/
23
29
24
30
/**
25
31
* The errors to display to the user in the various situations where a field fails validation.
26
32
* @template M
27
33
* @template {import("./types.d.ts").ValidatableField} [E=import("./types.d.ts").ValidatableField]
34
+ * @template {boolean} [R=false]
28
35
* @typedef {Object } ValidationErrors
29
36
*
30
37
*
31
- * @property {ErrorDetails<M, E> } [required]
32
- * @property {ErrorDetails<M, E> } [minlength]
33
- * @property {ErrorDetails<M, E> } [min]
34
- * @property {ErrorDetails<M, E> } [maxlength]
35
- * @property {ErrorDetails<M, E> } [max]
36
- * @property {ErrorDetails<M, E> } [step]
37
- * @property {ErrorDetails<M, E> } [type]
38
- * @property {ErrorDetails<M, E> } [pattern]
38
+ * @property {ErrorDetails<M, E, R > } [required]
39
+ * @property {ErrorDetails<M, E, R > } [minlength]
40
+ * @property {ErrorDetails<M, E, R > } [min]
41
+ * @property {ErrorDetails<M, E, R > } [maxlength]
42
+ * @property {ErrorDetails<M, E, R > } [max]
43
+ * @property {ErrorDetails<M, E, R > } [step]
44
+ * @property {ErrorDetails<M, E, R > } [type]
45
+ * @property {ErrorDetails<M, E, R > } [pattern]
39
46
*
40
47
*
41
- * @property {ErrorDetails<M, E> } [badinput] The error to display when the user's input is malformed, such as an
48
+ * @property {ErrorDetails<M, E, R > } [badinput] The error to display when the user's input is malformed, such as an
42
49
* incomplete date.
43
50
* See {@link https://developer.mozilla.org/en-US/docs/Web/API/ValidityState/badInput ValidityState.badInput}
44
51
*
45
52
* @property {
46
- (field: E) => void | ErrorDetails<M, E> | Promise<void | ErrorDetails<M, E>>
53
+ (field: E) => void | ErrorDetails<M, E, R > | Promise<void | ErrorDetails<M, E, R >>
47
54
} [validate] A function that runs custom validation logic for a field. This validation is always run _last_.
48
55
*/
49
56
50
57
/**
51
58
* @template M
52
59
* @template {import("./types.d.ts").ValidatableField} [E=import("./types.d.ts").ValidatableField]
60
+ * @template {boolean} [R=false]
53
61
* @typedef {Object } FormValidityObserverOptions
54
62
*
55
63
* @property {boolean } [useEventCapturing] Indicates that the observer's event listener should be called during
@@ -67,7 +75,10 @@ const attrs = Object.freeze({ "aria-describedby": "aria-describedby", "aria-inva
67
75
* You can replace the default function with your own `renderer` that renders other types of error messages
68
76
* (e.g., DOM Nodes, React Elements, etc.) to the DOM instead.
69
77
*
70
- * @property {ValidationErrors<M, E> } [defaultErrors] The default errors to display for the field constraints.
78
+ * @property {R } [renderByDefault] Determines the default value for every validation constraint's `render` option.
79
+ * (Also sets the default value for {@link FormValidityObserver.setFieldError setFieldError}'s `render` option.)
80
+ *
81
+ * @property {ValidationErrors<M, E, R> } [defaultErrors] The default errors to display for the field constraints.
71
82
* (The `validate` option configures the default _custom validation function_ used for all form fields.)
72
83
*/
73
84
@@ -82,43 +93,45 @@ const attrs = Object.freeze({ "aria-describedby": "aria-describedby", "aria-inva
82
93
* Defaults to `false`.
83
94
*/
84
95
85
- /** @template [M=string] */
96
+ /** @template [M=string] @template {boolean} [R=false] */
86
97
class FormValidityObserver extends FormObserver {
87
98
/** @type {HTMLFormElement | undefined } The `form` currently being observed by the `FormValidityObserver` */ #form;
88
99
/** @type {Document | ShadowRoot | undefined } The Root Node for the currently observed `form`. */ #root;
89
100
90
101
/** @readonly @type {Required<FormValidityObserverOptions<M>>["scroller"] } */ #scrollTo;
91
102
/** @readonly @type {Required<FormValidityObserverOptions<M>>["renderer"] } */ #renderError;
103
+ /** @readonly @type {FormValidityObserverOptions<M, any, R>["renderByDefault"] } */ #renderByDefault;
92
104
/** @readonly @type {FormValidityObserverOptions<M>["defaultErrors"] } */ #defaultErrors;
93
105
94
106
/**
95
107
* @readonly
96
- * @type {Map<string, ValidationErrors<M, any> | undefined> }
108
+ * @type {Map<string, ValidationErrors<M, any, R > | undefined> }
97
109
* The {@link configure}d error messages for the various fields belonging to the observed `form`
98
110
*/
99
111
#errorMessagesByFieldName = new Map ( ) ;
100
112
101
113
/*
102
- * TODO: It's a little weird that we have to declare `M` twice for things to work. Maybe it's related to
114
+ * TODO: It's a little weird that we have to declare `M`/`R` twice for things to work. Maybe it's related to
103
115
* illegal generic constructors?
104
116
*/
105
117
/**
106
118
* @template {import("./types.d.ts").OneOrMany<import("./types.d.ts").EventType>} T
107
119
* @template [M=string]
108
120
* @template {import("./types.d.ts").ValidatableField} [E=import("./types.d.ts").ValidatableField]
121
+ * @template {boolean} [R=false]
109
122
* @overload
110
123
*
111
124
* Provides a way to validate an `HTMLFormElement`'s fields (and to display _accessible_ errors for those fields)
112
125
* in response to the events that the fields emit.
113
126
*
114
127
* @param {T } types The type(s) of event(s) that trigger(s) form field validation.
115
- * @param {FormValidityObserverOptions<M, E> } [options]
116
- * @returns {FormValidityObserver<M> }
128
+ * @param {FormValidityObserverOptions<M, E, R > } [options]
129
+ * @returns {FormValidityObserver<M, R > }
117
130
*/
118
131
119
132
/**
120
133
* @param {import("./types.d.ts").OneOrMany<import("./types.d.ts").EventType> } types
121
- * @param {FormValidityObserverOptions<M> } [options]
134
+ * @param {FormValidityObserverOptions<M, import("./types.d.ts").ValidatableField, R > } [options]
122
135
*/
123
136
constructor ( types , options ) {
124
137
/**
@@ -135,6 +148,7 @@ class FormValidityObserver extends FormObserver {
135
148
super ( types , eventListener , { passive : true , capture : options ?. useEventCapturing } ) ;
136
149
this . #scrollTo = options ?. scroller ?? defaultScroller ;
137
150
this . #renderError = /** @type {any } Necessary because of double `M`s */ ( options ?. renderer ?? defaultErrorRenderer ) ;
151
+ this . #renderByDefault = /** @type {any } Necessary because of double `R`s */ ( options ?. renderByDefault ) ;
138
152
this . #defaultErrors = /** @type {any } Necessary because of double `M`s */ ( options ?. defaultErrors ) ;
139
153
}
140
154
@@ -320,7 +334,8 @@ class FormValidityObserver extends FormObserver {
320
334
* a field validation attempt.
321
335
*
322
336
* @param {import("./types.d.ts").ValidatableField } field The `field` for which the validation was run
323
- * @param {ErrorDetails<M> | void } error The error to apply to the `field`, if any
337
+ * @param {ErrorDetails<M, import("./types.d.ts").ValidatableField, boolean> | void } error The error to apply
338
+ * to the `field`, if any
324
339
* @param {ValidateFieldOptions | undefined } options The options that were used for the field's validation
325
340
*
326
341
* @returns {boolean } `true` if the field passed validation (indicated by a falsy `error` value) and `false` otherwise.
@@ -333,7 +348,7 @@ class FormValidityObserver extends FormObserver {
333
348
334
349
if ( typeof error === "object" ) {
335
350
this . setFieldError ( field . name , /** @type {any } */ ( error ) . message , /** @type {any } */ ( error ) . render ) ;
336
- } else this . setFieldError ( field . name , error ) ;
351
+ } else this . setFieldError ( field . name , /** @type { any } */ ( error ) ) ;
337
352
338
353
if ( options ?. focus ) this . #callAttentionTo( field ) ;
339
354
return false ;
@@ -365,9 +380,10 @@ class FormValidityObserver extends FormObserver {
365
380
* and applies the provided error `message` to it.
366
381
*
367
382
* @param {string } name The name of the invalid form field
368
- * @param {ErrorMessage<M, E> } message The error message to apply to the invalid form field
369
- * @param {true } render When `true`, the error `message` will be rendered to the DOM using the observer's
370
- * {@link FormValidityObserverOptions.renderer `renderer`} function.
383
+ * @param {R extends true ? ErrorMessage<string, E> : ErrorMessage<M, E> } message The error message to apply
384
+ * to the invalid form field
385
+ * @param {R extends true ? false : true } render When `true`, the error `message` will be rendered to the DOM
386
+ * using the observer's {@link FormValidityObserverOptions.renderer `renderer`} function.
371
387
* @returns {void }
372
388
*/
373
389
@@ -377,8 +393,9 @@ class FormValidityObserver extends FormObserver {
377
393
* and applies the provided error `message` to it.
378
394
*
379
395
* @param {string } name The name of the invalid form field
380
- * @param {ErrorMessage<string, E> } message The error message to apply to the invalid form field
381
- * @param {false } [render] When `true`, the error `message` will be rendered to the DOM using the observer's
396
+ * @param {R extends true ? ErrorMessage<M, E> : ErrorMessage<string, E> } message The error message to apply
397
+ * to the invalid form field
398
+ * @param {R } [render] When `true`, the error `message` will be rendered to the DOM using the observer's
382
399
* {@link FormValidityObserverOptions.renderer `renderer`} function.
383
400
* @returns {void }
384
401
*/
@@ -390,7 +407,7 @@ class FormValidityObserver extends FormObserver {
390
407
* @param {boolean } [render]
391
408
* @returns {void }
392
409
*/
393
- setFieldError ( name , message , render ) {
410
+ setFieldError ( name , message , render = this . #renderByDefault ) {
394
411
const field = this . #getTargetField( name ) ;
395
412
if ( ! field ) return ;
396
413
@@ -452,7 +469,7 @@ class FormValidityObserver extends FormObserver {
452
469
*
453
470
* @template {import("./types.d.ts").ValidatableField} E
454
471
* @param {string } name The `name` of the form field
455
- * @param {ValidationErrors<M, E> } errorMessages A `key`-`value` pair of validation constraints (key)
472
+ * @param {ValidationErrors<M, E, R > } errorMessages A `key`-`value` pair of validation constraints (key)
456
473
* and their corresponding error messages (value)
457
474
* @returns {void }
458
475
*
0 commit comments