Skip to content

Commit

Permalink
chore: Update Documentation for the FormValidityObserver
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
ITenthusiasm committed Apr 13, 2024
1 parent 823a231 commit 90a0835
Show file tree
Hide file tree
Showing 12 changed files with 156 additions and 119 deletions.
2 changes: 1 addition & 1 deletion docs/extras/philosophy.md
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ The next natural step is to provide a way to validate an _entire form_. For that

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()`.

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.
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.

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.

Expand Down
38 changes: 25 additions & 13 deletions docs/form-validity-observer/README.md

Large diffs are not rendered by default.

86 changes: 51 additions & 35 deletions docs/form-validity-observer/integrations/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,14 @@ function createFormValidityObserver<
T extends OneOrMany<EventType>,
M = string,
E extends ValidatableField = ValidatableField,
>(types: T, options?: FormValidityObserverOptions<M, E>): SvelteFormValidityObserver<M> {
const observer = new FormValidityObserver(types, options) as unknown as SvelteFormValidityObserver<M>;
R extends boolean = false,
>(types: T, options?: FormValidityObserverOptions<M, E, R>): SvelteFormValidityObserver<M, R> {
const observer = new FormValidityObserver(types, options) as unknown as SvelteFormValidityObserver<M, R>;
return observer;
}
interface SvelteFormValidityObserver<M = string> extends Omit<FormValidityObserver<M>, "configure"> {}
interface SvelteFormValidityObserver<M = string, R extends boolean = false>
extends Omit<FormValidityObserver<M, R>, "configure"> {}
```
Note: Since we will be augmenting the `FormValidityObserver.configure()` method, we are _not_ copying its type definition to the `SvelteFormValidityObserver` interface.
Expand All @@ -162,8 +164,9 @@ function createFormValidityObserver<
T extends OneOrMany<EventType>,
M = string,
E extends ValidatableField = ValidatableField,
>(types: T, options?: FormValidityObserverOptions<M, E>): SvelteFormValidityObserver<M> {
const observer = new FormValidityObserver(types, options) as unknown as SvelteFormValidityObserver<M>;
R extends boolean = false,
>(types: T, options?: FormValidityObserverOptions<M, E, R>): SvelteFormValidityObserver<M, R> {
const observer = new FormValidityObserver(types, options) as unknown as SvelteFormValidityObserver<M, R>;
/* ---------- Bindings ---------- */
// Form Observer Methods
Expand All @@ -180,7 +183,8 @@ function createFormValidityObserver<
return observer;
}
interface SvelteFormValidityObserver<M = string> extends Omit<FormValidityObserver<M>, "configure"> {}
interface SvelteFormValidityObserver<M = string, R extends boolean = false>
extends Omit<FormValidityObserver<M, R>, "configure"> {}
```
Note: Because we will be enhancing the `configure` method, we _have not_ attached it to the `observer` object that we return.
Expand All @@ -203,8 +207,9 @@ function createFormValidityObserver<
T extends OneOrMany<EventType>,
M = string,
E extends ValidatableField = ValidatableField,
>(types: T, options?: FormValidityObserverOptions<M, E>): SvelteFormValidityObserver<M> {
const observer = new FormValidityObserver(types, options) as unknown as SvelteFormValidityObserver<M>;
R extends boolean = false,
>(types: T, options?: FormValidityObserverOptions<M, E, R>): SvelteFormValidityObserver<M, R> {
const observer = new FormValidityObserver(types, options) as unknown as SvelteFormValidityObserver<M, R>;
/* ---------- Bindings ---------- */
// Apply all bindings...
Expand All @@ -224,7 +229,8 @@ function createFormValidityObserver<
return observer;
}
interface SvelteFormValidityObserver<M = string> extends Omit<FormValidityObserver<M>, "configure"> {
interface SvelteFormValidityObserver<M = string, R extends boolean = false>
extends Omit<FormValidityObserver<M, R>, "configure"> {
autoObserve(form: HTMLFormElement, novalidate?: boolean): ActionReturn;
}
```
Expand Down Expand Up @@ -311,9 +317,10 @@ import type { HTMLInputAttributes } from "svelte/elements";
// Definition of `createFormValidityObserver` ...
interface SvelteFormValidityObserver<M = string> extends Omit<FormValidityObserver<M>, "configure"> {
interface SvelteFormValidityObserver<M = string, R extends boolean = false>
extends Omit<FormValidityObserver<M, R>, "configure"> {
// Augments `FormValidityObserver.configure()`
configure<E extends ValidatableField>(name: string, errorMessages: SvelteValidationErrors<M, E>): SvelteFieldProps;
configure<E extends ValidatableField>(name: string, errorMessages: SvelteValidationErrors<M, E, R>): SvelteFieldProps;
autoObserve(form: HTMLFormElement, novalidate?: boolean): ActionReturn;
}
Expand All @@ -324,23 +331,30 @@ type SvelteFieldProps = Pick<
>;
// Augments `ValidationErrors` type
export interface SvelteValidationErrors<M, E extends ValidatableField = ValidatableField>
extends Pick<ValidationErrors<M, E>, "badinput" | "validate"> {
required?: SvelteErrorDetails<M, HTMLInputAttributes["required"], E> | ErrorMessage<string, E>;
minlength?: SvelteErrorDetails<M, HTMLInputAttributes["minlength"], E>;
min?: SvelteErrorDetails<M, HTMLInputAttributes["min"], E>;
maxlength?: SvelteErrorDetails<M, HTMLInputAttributes["maxlength"], E>;
max?: SvelteErrorDetails<M, HTMLInputAttributes["max"], E>;
step?: SvelteErrorDetails<M, HTMLInputAttributes["step"], E>;
type?: SvelteErrorDetails<M, HTMLInputAttributes["type"], E>;
pattern?: SvelteErrorDetails<M, HTMLInputAttributes["pattern"], E>;
export interface SvelteValidationErrors<M, E extends ValidatableField = ValidatableField, R extends boolean = false>
extends Pick<ValidationErrors<M, E, R>, "badinput" | "validate"> {
required?:
| SvelteErrorDetails<M, HTMLInputAttributes["required"], E, R>
| ErrorMessage<R extends true ? M : string, E>;
minlength?: SvelteErrorDetails<M, HTMLInputAttributes["minlength"], E, R>;
min?: SvelteErrorDetails<M, HTMLInputAttributes["min"], E, R>;
maxlength?: SvelteErrorDetails<M, HTMLInputAttributes["maxlength"], E, R>;
max?: SvelteErrorDetails<M, HTMLInputAttributes["max"], E, R>;
step?: SvelteErrorDetails<M, HTMLInputAttributes["step"], E, R>;
type?: SvelteErrorDetails<M, HTMLInputAttributes["type"], E, R>;
pattern?: SvelteErrorDetails<M, HTMLInputAttributes["pattern"], E, R>;
}
// Augments `ErrorDetails` type
type SvelteErrorDetails<M, V, E extends ValidatableField = ValidatableField> =
type SvelteErrorDetails<M, V, E extends ValidatableField = ValidatableField, R extends boolean = false> =
| V
| { render: true; message: ErrorMessage<M, E>; value: V }
| { render?: false; message: ErrorMessage<string, E>; value: V };
| R extends true
?
| { render?: true; message: ErrorMessage<M, E>; value: V }
| { render: false; message: ErrorMessage<string, E>; value: V }
:
| { render: true; message: ErrorMessage<M, E>; value: V }
| { render?: false; message: ErrorMessage<string, E>; value: V };
```
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.
Expand All @@ -349,7 +363,7 @@ You don't have to understand what these types do to use them. But if you're inte
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`.
`SvelteErrorDetails` is _almost_ the exact same type as [`ErrorDetails`](../types.md#errordetailsm-e). There are only two differences between `SvelteErrorDetail` and `ErrorDetails`:
`SvelteErrorDetails` is _almost_ the exact same type as [`ErrorDetails`](../types.md#errordetailsm-e-r). There are only two differences between `SvelteErrorDetail` and `ErrorDetails`:
<ol>
<li>
Expand All @@ -370,7 +384,7 @@ Our `configure` method has changed the type of the `errorMessages` argument from
</li>
</ol>
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.
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.
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.**)
Expand All @@ -390,8 +404,9 @@ type SvelteFieldProps = Pick<
And we make _this_ the return type of `configure`:
```ts
interface SvelteFormValidityObserver<M = string> extends Omit<FormValidityObserver<M>, "configure"> {
configure<E extends ValidatableField>(name: string, errorMessages: SvelteValidationErrors<M, E>): SvelteFieldProps;
interface SvelteFormValidityObserver<M = string, R extends boolean = false>
extends Omit<FormValidityObserver<M, R>, "configure"> {
configure<E extends ValidatableField>(name: string, errorMessages: SvelteValidationErrors<M, E, R>): SvelteFieldProps;
autoObserve(form: HTMLFormElement, novalidate?: boolean): ActionReturn;
}
```
Expand All @@ -409,23 +424,24 @@ export default function createFormValidityObserver<
T extends OneOrMany<EventType>,
M = string,
E extends ValidatableField = ValidatableField,
>(types: T, options?: FormValidityObserverOptions<M, E>): SvelteFormValidityObserver<M> {
const observer = new FormValidityObserver(types, options) as unknown as SvelteFormValidityObserver<M>;
R extends boolean = false,
>(types: T, options?: FormValidityObserverOptions<M, E, R>): SvelteFormValidityObserver<M, R> {
const observer = new FormValidityObserver(types, options) as unknown as SvelteFormValidityObserver<M, R>;
/* ---------- Bindings ---------- */
// Apply bindings for exposed methods ...
/** **Private** reference to the original {@link FormValidityObserver.configure} method */
const originalConfigure = observer.configure.bind(observer) as FormValidityObserver<M>["configure"];
const originalConfigure = observer.configure.bind(observer) as FormValidityObserver<M, R>["configure"];
/* ---------- Enhancements ---------- */
// Definition for `autoObserver` ...
// Enhanced `configure` method
observer.configure = (name, errorMessages) => {
const keys = Object.keys(errorMessages) as Array<keyof SvelteValidationErrors<M>>;
const keys = Object.keys(errorMessages) as Array<keyof SvelteValidationErrors<M, R>>;
const props = { name } as SvelteFieldProps;
const config = {} as ValidationErrors<M>;
const config = {} as ValidationErrors<M, ValidatableField, R>;
// Build `props` object and error `config` object from `errorMessages`
for (let i = 0; i < keys.length; i++) {
Expand Down Expand Up @@ -516,9 +532,9 @@ If we want to keep our code readable, then the next best solution is to use `any
```ts
observer.configure = (name, errorMessages) => {
const keys = Object.keys(errorMessages) as Array<keyof SvelteValidationErrors<M>>;
const keys = Object.keys(errorMessages) as Array<keyof SvelteValidationErrors<M, ValidatableField, R>>;
const props = { name } as SvelteFieldProps;
const config = {} as ValidationErrors<M>;
const config = {} as ValidationErrors<M, ValidatableField, R>;
// Build `props` object and error `config` object from `errorMessages`
for (let i = 0; i < keys.length; i++) {
Expand Down
12 changes: 6 additions & 6 deletions docs/form-validity-observer/integrations/preact.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ A _convenience_ API for reducing code repetition in a [Preact](https://preactjs.

Creates an enhanced version of the `FormValidityObserver`, known as the `PreactFormValidityObserver`. It accepts the exact same arguments as the [`FormValidityObserver`'s constructor](../README.md#constructor-formvalidityobservertypes-options).

### Return Type: `PreactFormValidityObserver<M>`
### Return Type: `PreactFormValidityObserver<M, R>`

An enhanced version of the `FormValidityObserver`, designed specifically for Preact applications. It has the same Type Parameters as the `FormValidityObserver`. As with the `FormValidityObserver`, the type of `M` is derived from the [`renderer`](../README.md#form-validity-observer-options-renderer) option.
An enhanced version of the `FormValidityObserver`, designed specifically for Preact applications. It has the same Type Parameters as the `FormValidityObserver`. As with the `FormValidityObserver`, the type of `M` is derived from the [`renderer`](../README.md#form-validity-observer-options-renderer) option, and the type of `R` is derived from the [`renderByDefault`](../README.md#form-validity-observer-options-render-by-default) option.

#### Copied Methods

Expand Down Expand Up @@ -77,13 +77,13 @@ class MyFormClass extends Component {

Remember that `autoObserve` is simply a convenience utility for calling `observe` and `unobserve` automatically. You're free to setup and teardown the `FormValidityObserver` manually if you prefer.

#### Function: `configure<E>(name: string, errorMessages: PreactValidationErrors<M, E>): PreactFieldProps`
#### Function: `configure<E>(name: string, errorMessages: PreactValidationErrors<M, E, R>): PreactFieldProps`

An enhanced version of [`FormValidityObserver.configure`](../README.md#method-formvalidityobserverconfigureename-string-errormessages-validationerrorsm-e-void) for `Preact`. In addition to configuring a field's error messages, it generates the props that should be applied to the field based on the provided arguments.
An enhanced version of [`FormValidityObserver.configure`](../README.md#method-formvalidityobserverconfigureename-string-errormessages-validationerrorsm-e-r-void) for `Preact`. In addition to configuring a field's error messages, it generates the props that should be applied to the field based on the provided arguments.

> Note: If the field is _only_ using the configured [`defaultErrors`](../README.md#form-validity-observer-options-default-errors) and/or the browser's default error messages, it _does not_ need to be `configure`d.
The `PreactValidationErrors<M, E>` type is an enhanced version of the core [`ValidationErrors<M, E>`](../types.md#validationerrorsm-e) type. Here is how `PreactValidationErrors` compares to `ValidationErrors`.
The `PreactValidationErrors<M, E, R>` type is an enhanced version of the core [`ValidationErrors<M, E, R>`](../types.md#validationerrorsm-e-r) type. Here is how `PreactValidationErrors` compares to `ValidationErrors`.

##### Properties That Mimic the `ValidationErrors` Properties

Expand Down Expand Up @@ -139,7 +139,7 @@ All the other properties on the `PreactValidationErrors` type are enhancements o

The rules are as follows:

1&rpar; When a constraint is configured with an [`ErrorDetails`](../types.md#errordetailsm-e) object, the object must include a `value` property specifying the value of the constraint. In this scenario, both the field's constraint value _and_ its error message are configured.
1&rpar; When a constraint is configured with an [`ErrorDetails`](../types.md#errordetailsm-e-r) object, the object must include a `value` property specifying the value of the constraint. In this scenario, both the field's constraint value _and_ its error message are configured.

```tsx
import { createFormValidityObserver } from "@form-observer/preact";
Expand Down
Loading

0 comments on commit 90a0835

Please sign in to comment.