Skip to content

Commit

Permalink
Merge pull request #41 from x0k/structure-change
Browse files Browse the repository at this point in the history
Implement array and object fields revalidation
  • Loading branch information
x0k authored Nov 30, 2024
2 parents 871e2d6 + f859158 commit 94b19cf
Show file tree
Hide file tree
Showing 35 changed files with 386 additions and 214 deletions.
5 changes: 5 additions & 0 deletions .changeset/quick-coins-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@sjsf/form": minor
---

Implement array and object fields revalidation
5 changes: 5 additions & 0 deletions .changeset/twenty-mirrors-clap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"playground": minor
---

Allow to customize validation events and modifiers
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
title: Fields validation mode
sidebar:
order: 11
---

```typescript
type FieldsValidationMode = number;

/** Validation is triggered on input event */
export const ON_INPUT = 1;
/** Validation is triggered on change event */
export const ON_CHANGE = ON_INPUT << 1;
/** Validation is triggered on blur event */
export const ON_BLUR = ON_CHANGE << 1;
/** Validation is triggered on add/remove item in array */
export const ON_ARRAY_CHANGE = ON_BLUR << 1;
/** Validation is triggered on add/remove/rename property in object */
export const ON_OBJECT_CHANGE = ON_ARRAY_CHANGE << 1;

/** Validation is not triggered before first change event */
export const AFTER_CHANGED = ON_OBJECT_CHANGE << 1;
/** Validation is not triggered before first blur event */
export const AFTER_TOUCHED = AFTER_CHANGED << 1;
/** Validation is not triggered before first form submission */
export const AFTER_SUBMITTED = AFTER_TOUCHED << 1;
2 changes: 1 addition & 1 deletion apps/docs/src/content/docs/api-reference/handlers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ interface FormProps<T> {
/**
* Field validation error handler
*/
onFieldValidationFailure?: (
onFieldsValidationFailure?: (
state: FailedMutation<unknown>,
config: Config,
value: SchemaValue | undefined
Expand Down

This file was deleted.

4 changes: 2 additions & 2 deletions apps/docs/src/content/docs/guides/_async-validation.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
validator: createAsyncValidator({ ajv }),
handleValidationProcessError,
schema: schema,
inputsValidationMode: ON_INPUT,
fieldsValidationMode: ON_INPUT,
onSubmit: console.log,
});
Expand All @@ -48,7 +48,7 @@
};
</script>

form validation: {statusNames[form.validation.status]}, inputs validation: {statusNames[form.fieldValidation.status]}
form validation: {statusNames[form.validation.status]}, fields validation: {statusNames[form.fieldsValidation.status]}
<SimpleForm
{form}
novalidate
Expand Down
9 changes: 9 additions & 0 deletions apps/docs/src/content/docs/guides/_fields-validation.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
import { ShadowHost } from "@/components/shadow";
import Form from "./_fields-validation.svelte";
---

<ShadowHost client:only="svelte">
<Form client:only="svelte" />
</ShadowHost>
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
const form = useCustomForm({
schema: objectSchema,
inputsValidationMode: ON_INPUT | ON_CHANGE | AFTER_SUBMITTED,
fieldsValidationMode: ON_INPUT | ON_CHANGE | AFTER_SUBMITTED,
onSubmit: console.log,
});
</script>
Expand Down
9 changes: 0 additions & 9 deletions apps/docs/src/content/docs/guides/_inputs-validation.astro

This file was deleted.

23 changes: 12 additions & 11 deletions apps/docs/src/content/docs/guides/validation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ sidebar:

import { Code, Card, LinkCard } from '@astrojs/starlight/components';

import { DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS } from '@sjsf/form';
import { DEFAULT_FIELDS_VALIDATION_DEBOUNCE_MS } from '@sjsf/form';

import LiveValidation from './_live-validation.astro'
import liveValidationCode from './_live-validation.svelte?raw'

import InputsValidation from './_inputs-validation.astro'
import inputsValidationCode from './_inputs-validation.svelte?raw'
import FieldsValidation from './_fields-validation.astro'
import fieldsValidationCode from './_fields-validation.svelte?raw'

import AsyncValidation from './_async-validation.astro'
import asyncValidationCode from './_async-validation.svelte?raw'
Expand Down Expand Up @@ -44,27 +44,28 @@ By utilizing Svelte 5 reactivity, we can easily implement live validation.
While it is possible, this approach has low efficiency, because usually
revalidation of the whole form when changing one field does not make sense.

## Inputs validation
## Fields validation

Instead of full form revalidation, we propose to perform revalidation of only the input field and full validation of the form when submitting.
Instead of full form revalidation, we propose to perform revalidation of only the changing field and
full validation of the form when submitting.

:::note

By default, inputs validation is performed with a debounce of {DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS}ms.
By default, fields validation is performed with a debounce of {DEFAULT_FIELDS_VALIDATION_DEBOUNCE_MS}ms.

This can be changed by passing the `validationDebounceMs` option.
This can be changed by passing the `fieldsValidationDebounceMs` option.

:::

<Code code={inputsValidationCode} lang="svelte" />
<Code code={fieldsValidationCode} lang="svelte" />

<Card>
<InputsValidation />
<FieldsValidation />
</Card>

The form in this example will only revalidate input fields on the `input` and `change` events after the first submission of the form.

<LinkCard title="Inputs validation mode API reference" href="../../api-reference/inputs-validation-mode/" />
<LinkCard title="Fields validation mode API reference" href="../../api-reference/fields-validation-mode/" />

## Async validation

Expand All @@ -75,7 +76,7 @@ The form supports asynchronous validation, please see your validator page for mo
By default, a new form validation process can only be started after the previous one is completed;
a new input validation process aborts the previous one.

You can change this behavior by passing the `validationCombinator` and `fieldValidationCombinator` options.
You can change this behavior by passing the `validationCombinator` and `fieldsValidationCombinator` options.

:::

Expand Down
6 changes: 3 additions & 3 deletions apps/docs/src/content/docs/validators/_ajv.svelte
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
<script lang="ts">
import { SimpleForm, ON_INPUT } from "@sjsf/form";
import { createValidator } from "@sjsf/ajv8-validator";
import { createValidator2 } from "@sjsf/ajv8-validator";
import { useCustomForm } from "@/components/custom-form";
import { schema, uiSchema } from './_shared';
const validator = createValidator();
const validator = createValidator2();
const form = useCustomForm({
schema,
uiSchema,
validator,
inputsValidationMode: ON_INPUT,
fieldsValidationMode: ON_INPUT,
initialValue: {
id: "Invalid",
skills: ["karate", "budo", "aikido"],
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/src/content/docs/validators/_cfworker.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
schema,
uiSchema,
validator,
inputsValidationMode: ON_INPUT,
fieldsValidationMode: ON_INPUT,
initialValue: {
id: "Invalid",
skills: ["karate", "budo", "aikido"],
Expand Down
4 changes: 2 additions & 2 deletions apps/docs/src/content/docs/validators/_zod.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@
type Value = z.infer<typeof schema>;
const validator = createValidator({ schema, uiSchema });
const validator = createValidator({ schema });
const form = useCustomForm({
schema: zodToJsonSchema(schema, { errorMessages: true }) as Schema,
uiSchema,
validator,
inputsValidationMode: ON_INPUT,
fieldsValidationMode: ON_INPUT,
initialValue: initialValue as Value,
});
</script>
Expand Down
5 changes: 4 additions & 1 deletion apps/docs/src/content/docs/validators/cfworker.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ import sharedCode from './_shared.ts?raw';

This validator is experimental.

At least `data-url` and `color` formats are ignored.
- `data-url` and `color` formats are ignored.
- By itself this validator does not support `undefined` values. And we don't
have any additional logic to handle it in this adapter. So you should provide
custom `merger` instance and/or use `emptyValue` options in your UI schema.

:::

Expand Down
4 changes: 2 additions & 2 deletions apps/docs/src/content/docs/validators/zod.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ References:

## Caveats

If you using this library only for full form validation (without inputs validation mode)
If you using this library only for full form validation (without fields validation mode)
and your form does not have and `oneOf`, `anyOf`, `dependencies` `if` keywords or recursive references,
you can use this library pretty safely.

If you are using inputs validation mode then starting from this point
If you are using fields validation mode then starting from this point
internally we start to use `json-schema-to-zod`.
So first of all, please read this warning [about using this library at runtime](https://github.com/StefanTerdell/json-schema-to-zod?tab=readme-ov-file#use-at-runtime).
However, I believe you are still safe as only small leafy bits of the circuit are transformed with this approach.
Expand Down
Loading

0 comments on commit 94b19cf

Please sign in to comment.