Skip to content

Commit 958dcda

Browse files
committed
chore: Update Documentation for the FormValidityObserver
Note: We technically have not yet updated the types for the JS framework integrations, so we aren't 100% sure that our docs in that area are correct yet. But we're pretty confident that the documentation should be correct. After reviewing these docs on GitHub and verifying that we didn't break any links, we'll follow up with the integration TypeScript changes (and any docs updates that should be done as a result).
1 parent 94de58f commit 958dcda

File tree

12 files changed

+163
-126
lines changed

12 files changed

+163
-126
lines changed

docs/extras/philosophy.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ The next natural step is to provide a way to validate an _entire form_. For that
318318

319319
If the developer opts out of accessible error messaging, the `setFieldError` and `clearFieldError` methods will fallback to `field.setCustomValidity()`, and `validateField({ focus: true })`/`validateFields({ focus: true })` will fallback to `field.reportValidity()`/`form.reportValidity()`.
320320

321-
As an added bonus, the `FormValidityObserver` exposes a [`configure`](../form-validity-observer/README.md#method-formvalidityobserverconfigureename-string-errormessages-validationerrorsm-e-void) method that enables developers to configure the error messages that should be displayed when a field fails validation. (Any unconfigured error messages will fallback to the `validationMessage` that the browser provides.) It also allows a custom validation function to be configured for the field.
321+
As an added bonus, the `FormValidityObserver` exposes a [`configure`](../form-validity-observer/README.md#method-formvalidityobserverconfigureename-string-errormessages-validationerrorsm-e-r-void) method that enables developers to configure the error messages that should be displayed when a field fails validation. (Any unconfigured error messages will fallback to the `validationMessage` that the browser provides.) It also allows a custom validation function to be configured for the field.
322322

323323
Seeing the big picture here? The `FormValidityObserver` is basically a wrapper for the browser's native features when accessible error messages aren't being used. When accessible error messages are needed, it functions as an _enhancement_ (not a replacement) of the browser's features to satisfy that need. As a bonus, it includes configurable scrolling/rendering functionality as well.
324324

docs/form-validity-observer/README.md

+26-14
Large diffs are not rendered by default.

docs/form-validity-observer/integrations/README.md

+56-40
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,14 @@ function createFormValidityObserver<
135135
T extends OneOrMany<EventType>,
136136
M = string,
137137
E extends ValidatableField = ValidatableField,
138-
>(types: T, options?: FormValidityObserverOptions<M, E>): SvelteFormValidityObserver<M> {
139-
const observer = new FormValidityObserver(types, options) as unknown as SvelteFormValidityObserver<M>;
138+
R extends boolean = false,
139+
>(types: T, options?: FormValidityObserverOptions<M, E, R>): SvelteFormValidityObserver<M, R> {
140+
const observer = new FormValidityObserver(types, options) as unknown as SvelteFormValidityObserver<M, R>;
140141
return observer;
141142
}
142143
143-
interface SvelteFormValidityObserver<M = string> extends Omit<FormValidityObserver<M>, "configure"> {}
144+
interface SvelteFormValidityObserver<M = string, R extends boolean = false>
145+
extends Omit<FormValidityObserver<M, R>, "configure"> {}
144146
```
145147
146148
Note: Since we will be augmenting the `FormValidityObserver.configure()` method, we are _not_ copying its type definition to the `SvelteFormValidityObserver` interface.
@@ -162,8 +164,9 @@ function createFormValidityObserver<
162164
T extends OneOrMany<EventType>,
163165
M = string,
164166
E extends ValidatableField = ValidatableField,
165-
>(types: T, options?: FormValidityObserverOptions<M, E>): SvelteFormValidityObserver<M> {
166-
const observer = new FormValidityObserver(types, options) as unknown as SvelteFormValidityObserver<M>;
167+
R extends boolean = false,
168+
>(types: T, options?: FormValidityObserverOptions<M, E, R>): SvelteFormValidityObserver<M, R> {
169+
const observer = new FormValidityObserver(types, options) as unknown as SvelteFormValidityObserver<M, R>;
167170
168171
/* ---------- Bindings ---------- */
169172
// Form Observer Methods
@@ -180,7 +183,8 @@ function createFormValidityObserver<
180183
return observer;
181184
}
182185
183-
interface SvelteFormValidityObserver<M = string> extends Omit<FormValidityObserver<M>, "configure"> {}
186+
interface SvelteFormValidityObserver<M = string, R extends boolean = false>
187+
extends Omit<FormValidityObserver<M, R>, "configure"> {}
184188
```
185189
186190
Note: Because we will be enhancing the `configure` method, we _have not_ attached it to the `observer` object that we return.
@@ -203,8 +207,9 @@ function createFormValidityObserver<
203207
T extends OneOrMany<EventType>,
204208
M = string,
205209
E extends ValidatableField = ValidatableField,
206-
>(types: T, options?: FormValidityObserverOptions<M, E>): SvelteFormValidityObserver<M> {
207-
const observer = new FormValidityObserver(types, options) as unknown as SvelteFormValidityObserver<M>;
210+
R extends boolean = false,
211+
>(types: T, options?: FormValidityObserverOptions<M, E, R>): SvelteFormValidityObserver<M, R> {
212+
const observer = new FormValidityObserver(types, options) as unknown as SvelteFormValidityObserver<M, R>;
208213
209214
/* ---------- Bindings ---------- */
210215
// Apply all bindings...
@@ -224,7 +229,8 @@ function createFormValidityObserver<
224229
return observer;
225230
}
226231
227-
interface SvelteFormValidityObserver<M = string> extends Omit<FormValidityObserver<M>, "configure"> {
232+
interface SvelteFormValidityObserver<M = string, R extends boolean = false>
233+
extends Omit<FormValidityObserver<M, R>, "configure"> {
228234
autoObserve(form: HTMLFormElement, novalidate?: boolean): ActionReturn;
229235
}
230236
```
@@ -284,15 +290,15 @@ The benefit of this approach, as we mentioned earlier, is that our configuration
284290
<input {...configure("email", { type: { value: "email", message: "Invalid Email" }, required: true })} />
285291
```
286292
287-
Of course, as with the core `FormValidityObserver`, calls to `configure` can be skipped if the developer is _only_ using the browser's default error messages:
293+
Of course, as with the core `FormValidityObserver`, calls to `configure` can be skipped if the developer is _only_ using the using the configured [`defaultErrors`](../README.md#form-validity-observer-options-default-errors) and/or the browser's default error messages:
288294
289295
```html
290296
<input name="email" type="email" required />
291297
```
292298
293299
Note: Our `configure` method should _not_ support adding an error `message` for a constraint without the constraint's `value`. This is because the error message would never get used in that scenario.
294300
295-
Now that we've specified all the requirements, let's implement this functionality. First off, we'll update the `SvelteFormValidityObserver` interface. Some new TypeScript types will have to be added here. If you're only using JavaScript, you can skip this part. :&rpar;
301+
Now that we've specified all of the requirements, let's implement this functionality. First off, we'll update the `SvelteFormValidityObserver` interface. Some new TypeScript types will have to be added here. If you're only using JavaScript, you can skip this part. :&rpar;
296302
297303
#### Adding the TypeScript Types for `configure`
298304
@@ -311,9 +317,10 @@ import type { HTMLInputAttributes } from "svelte/elements";
311317
312318
// Definition of `createFormValidityObserver` ...
313319
314-
interface SvelteFormValidityObserver<M = string> extends Omit<FormValidityObserver<M>, "configure"> {
320+
interface SvelteFormValidityObserver<M = string, R extends boolean = false>
321+
extends Omit<FormValidityObserver<M, R>, "configure"> {
315322
// Augments `FormValidityObserver.configure()`
316-
configure<E extends ValidatableField>(name: string, errorMessages: SvelteValidationErrors<M, E>): SvelteFieldProps;
323+
configure<E extends ValidatableField>(name: string, errorMessages: SvelteValidationErrors<M, E, R>): SvelteFieldProps;
317324
autoObserve(form: HTMLFormElement, novalidate?: boolean): ActionReturn;
318325
}
319326
@@ -324,23 +331,30 @@ type SvelteFieldProps = Pick<
324331
>;
325332
326333
// Augments `ValidationErrors` type
327-
export interface SvelteValidationErrors<M, E extends ValidatableField = ValidatableField>
328-
extends Pick<ValidationErrors<M, E>, "badinput" | "validate"> {
329-
required?: SvelteErrorDetails<M, HTMLInputAttributes["required"], E> | ErrorMessage<string, E>;
330-
minlength?: SvelteErrorDetails<M, HTMLInputAttributes["minlength"], E>;
331-
min?: SvelteErrorDetails<M, HTMLInputAttributes["min"], E>;
332-
maxlength?: SvelteErrorDetails<M, HTMLInputAttributes["maxlength"], E>;
333-
max?: SvelteErrorDetails<M, HTMLInputAttributes["max"], E>;
334-
step?: SvelteErrorDetails<M, HTMLInputAttributes["step"], E>;
335-
type?: SvelteErrorDetails<M, HTMLInputAttributes["type"], E>;
336-
pattern?: SvelteErrorDetails<M, HTMLInputAttributes["pattern"], E>;
334+
export interface SvelteValidationErrors<M, E extends ValidatableField = ValidatableField, R extends boolean = false>
335+
extends Pick<ValidationErrors<M, E, R>, "badinput" | "validate"> {
336+
required?:
337+
| SvelteErrorDetails<M, HTMLInputAttributes["required"], E, R>
338+
| ErrorMessage<R extends true ? M : string, E>;
339+
minlength?: SvelteErrorDetails<M, HTMLInputAttributes["minlength"], E, R>;
340+
min?: SvelteErrorDetails<M, HTMLInputAttributes["min"], E, R>;
341+
maxlength?: SvelteErrorDetails<M, HTMLInputAttributes["maxlength"], E, R>;
342+
max?: SvelteErrorDetails<M, HTMLInputAttributes["max"], E, R>;
343+
step?: SvelteErrorDetails<M, HTMLInputAttributes["step"], E, R>;
344+
type?: SvelteErrorDetails<M, HTMLInputAttributes["type"], E, R>;
345+
pattern?: SvelteErrorDetails<M, HTMLInputAttributes["pattern"], E, R>;
337346
}
338347
339348
// Augments `ErrorDetails` type
340-
type SvelteErrorDetails<M, V, E extends ValidatableField = ValidatableField> =
349+
type SvelteErrorDetails<M, V, E extends ValidatableField = ValidatableField, R extends boolean = false> =
341350
| V
342-
| { render: true; message: ErrorMessage<M, E>; value: V }
343-
| { render?: false; message: ErrorMessage<string, E>; value: V };
351+
| (R extends true
352+
?
353+
| { render?: true; message: ErrorMessage<M, E>; value: V }
354+
| { render: false; message: ErrorMessage<string, E>; value: V }
355+
:
356+
| { render: true; message: ErrorMessage<M, E>; value: V }
357+
| { render?: false; message: ErrorMessage<string, E>; value: V });
344358
```
345359
346360
You don't have to understand what these types do to use them. But if you're interested in understanding what's happening here, let's walk you through what we did.
@@ -349,7 +363,7 @@ You don't have to understand what these types do to use them. But if you're inte
349363
350364
Our `configure` method has changed the type of the `errorMessages` argument from `ValidationErrors` to `SvelteValidationErrors` so that we can configure a field's constraints and error messages simultaneously. The type that enables us to support this feature is `SvelteErrorDetails`.
351365
352-
`SvelteErrorDetails` is _almost_ the exact same type as [`ErrorDetails`](../types.md#errordetailsm-e). There are only two differences between `SvelteErrorDetail` and `ErrorDetails`:
366+
`SvelteErrorDetails` is _almost_ the exact same type as [`ErrorDetails`](../types.md#errordetailsm-e-r). There are only two differences between `SvelteErrorDetails` and `ErrorDetails`:
353367
354368
<ol>
355369
<li>
@@ -362,19 +376,19 @@ Our `configure` method has changed the type of the `errorMessages` argument from
362376
</li>
363377
<li>
364378
<p>
365-
When an object is <em>not</em> being used, <code>SvelteErrorDetails</code> details replaces <code>ErrorMessage&lt;string&gt;</code> with <code>V</code>, where <code>V</code> represents the value of the constraint. This is an <em>alteration</em> of the <code>ErrorDetails</code> type.
379+
When an object is <em>not</em> being used, <code>SvelteErrorDetails</code> details replaces <code>ErrorMessage</code> with <code>V</code>, where <code>V</code> represents the value of the constraint. This is an <em>alteration</em> of the <code>ErrorDetails</code> type.
366380
</p>
367381
<p>
368-
Whenever a raw value is used for <code>SvelteErrorDetails</code>, developers will be able to specify a field's constraint <em>without</em> providing the error message for that constraint. In this scenario, the browser's default error message will be used for that constraint instead. Since it does not make sense to provide an error message without a constraint value, the <code>SvelteErrorDetails</code> type does not support that "use case".
382+
Whenever a raw value is used for <code>SvelteErrorDetails</code>, developers will be able to specify a field's constraint <em>without</em> providing the error message for that constraint. In this scenario, the browser's default error message (or the configured <a href="../README.md#form-validity-observer-options-default-errors">default error</a>) will be used for that constraint instead. Since it does not make sense to provide an error message without a constraint value, the <code>SvelteErrorDetails</code> type does not support that "use case".
369383
</p>
370384
</li>
371385
</ol>
372386
373-
Just as the `ErrorDetails` type forms the foundation of the [`ValidationErrors`](../types.md#validationerrorsm-e) type, so the `SvelteErrorDetails` type forms the foundation of the `SvelteValidationErrors` type. The type definition for `SvelteValidationErrors` is _almost_ the exact same as the type definition for `ValidationErrors`. In fact, the `badinput` and `validate` properties are exactly the same between the 2.
387+
Just as the `ErrorDetails` type forms the foundation of the [`ValidationErrors`](../types.md#validationerrorsm-e-r) type, so the `SvelteErrorDetails` type forms the foundation of the `SvelteValidationErrors` type. The type definition for `SvelteValidationErrors` is _almost_ the exact same as the type definition for `ValidationErrors`. In fact, the `badinput` and `validate` properties are exactly the same between the 2.
374388
375389
The primary way in which the `SvelteValidationErrors` type differs from the `ValidationErrors` type is that it takes constraint values into account (with the help of `SvelteErrorDetails`). It determines the value types that each constraint supports by looking at `Svelte`'s type definition for the `input` field's props (i.e., `HTMLInputAttributes`). (**Note: If you're using a different JS framework, you should use _that_ framework's type definitions for the `input` field's props instead.**)
376390
377-
Notice that the `required` constraint is slightly different from the others in that it supports one additional type: [`ErrorMessage<string>`](../types.md#errormessagem-e). If the developer supplies an error message by itself for the `required` constraint, it is safe to assume that `required` is `true`. This is an assumption that can only be made safely with the `required` constraint because it is a `boolean`.
391+
Notice that the `required` constraint is slightly different from the others in that it supports one additional type: [`ErrorMessage`](../types.md#errormessagem-e). If the developer supplies an error message by itself for the `required` constraint, it is safe to assume that `required` is `true`. This is an assumption that can only be made safely with the `required` constraint because it is a `boolean`.
378392
379393
##### Enhancing the Return Type of `configure`
380394
@@ -390,8 +404,9 @@ type SvelteFieldProps = Pick<
390404
And we make _this_ the return type of `configure`:
391405
392406
```ts
393-
interface SvelteFormValidityObserver<M = string> extends Omit<FormValidityObserver<M>, "configure"> {
394-
configure<E extends ValidatableField>(name: string, errorMessages: SvelteValidationErrors<M, E>): SvelteFieldProps;
407+
interface SvelteFormValidityObserver<M = string, R extends boolean = false>
408+
extends Omit<FormValidityObserver<M, R>, "configure"> {
409+
configure<E extends ValidatableField>(name: string, errorMessages: SvelteValidationErrors<M, E, R>): SvelteFieldProps;
395410
autoObserve(form: HTMLFormElement, novalidate?: boolean): ActionReturn;
396411
}
397412
```
@@ -409,23 +424,24 @@ export default function createFormValidityObserver<
409424
T extends OneOrMany<EventType>,
410425
M = string,
411426
E extends ValidatableField = ValidatableField,
412-
>(types: T, options?: FormValidityObserverOptions<M, E>): SvelteFormValidityObserver<M> {
413-
const observer = new FormValidityObserver(types, options) as unknown as SvelteFormValidityObserver<M>;
427+
R extends boolean = false,
428+
>(types: T, options?: FormValidityObserverOptions<M, E, R>): SvelteFormValidityObserver<M, R> {
429+
const observer = new FormValidityObserver(types, options) as unknown as SvelteFormValidityObserver<M, R>;
414430
415431
/* ---------- Bindings ---------- */
416432
// Apply bindings for exposed methods ...
417433
418434
/** **Private** reference to the original {@link FormValidityObserver.configure} method */
419-
const originalConfigure = observer.configure.bind(observer) as FormValidityObserver<M>["configure"];
435+
const originalConfigure = observer.configure.bind(observer) as FormValidityObserver<M, R>["configure"];
420436
421437
/* ---------- Enhancements ---------- */
422438
// Definition for `autoObserver` ...
423439
424440
// Enhanced `configure` method
425441
observer.configure = (name, errorMessages) => {
426-
const keys = Object.keys(errorMessages) as Array<keyof SvelteValidationErrors<M>>;
442+
const keys = Object.keys(errorMessages) as Array<keyof SvelteValidationErrors<M, ValidatableField, R>>;
427443
const props = { name } as SvelteFieldProps;
428-
const config = {} as ValidationErrors<M>;
444+
const config = {} as ValidationErrors<M, ValidatableField, R>;
429445
430446
// Build `props` object and error `config` object from `errorMessages`
431447
for (let i = 0; i < keys.length; i++) {
@@ -516,9 +532,9 @@ If we want to keep our code readable, then the next best solution is to use `any
516532
517533
```ts
518534
observer.configure = (name, errorMessages) => {
519-
const keys = Object.keys(errorMessages) as Array<keyof SvelteValidationErrors<M>>;
535+
const keys = Object.keys(errorMessages) as Array<keyof SvelteValidationErrors<M, ValidatableField, R>>;
520536
const props = { name } as SvelteFieldProps;
521-
const config = {} as ValidationErrors<M>;
537+
const config = {} as ValidationErrors<M, ValidatableField, R>;
522538
523539
// Build `props` object and error `config` object from `errorMessages`
524540
for (let i = 0; i < keys.length; i++) {

0 commit comments

Comments
 (0)