Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extensible validation support in Volto forms #6181

Merged
merged 55 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
d02a64b
Add foundations for extensible validation in forms
sneridagh Jul 10, 2024
d4a6d70
Fix tests
sneridagh Jul 10, 2024
bc7b779
fix build-deps precedence
sneridagh Jul 10, 2024
ad3a0d6
Fix more
sneridagh Jul 10, 2024
1fa71ed
Fix generator tests
sneridagh Jul 10, 2024
f6bba96
Fix react-share package
sneridagh Jul 10, 2024
3a4b000
fix test for social sharing
sneridagh Jul 10, 2024
709ed59
Add custom validation field property
sneridagh Jul 10, 2024
84bc5bf
Merge branch 'main' into extensible-validation
sneridagh Jul 11, 2024
26c82f9
Fix uniqueValidator
sneridagh Jul 12, 2024
3378ade
Add documentation
sneridagh Jul 12, 2024
0101fca
Changelog
sneridagh Jul 12, 2024
7f2cfaa
Changelog
sneridagh Jul 12, 2024
7963197
Checkpoint
sneridagh Jul 12, 2024
b135545
Validation for blocks too :)
sneridagh Jul 12, 2024
3d6ef22
locales
sneridagh Jul 15, 2024
80d7462
Refactor validation in Form
sneridagh Jul 15, 2024
9ad0eea
Changelog
sneridagh Jul 15, 2024
6234d48
Separate type-widget validators. Add behavior-fieldId validator types.
sneridagh Jul 15, 2024
30db8cd
Block-fieldId validators
sneridagh Jul 15, 2024
bef6a77
Changelog
sneridagh Jul 16, 2024
41da3fb
Typos correction for validation.md
ichim-david Jul 16, 2024
2a32edc
Complete default explanation
sneridagh Jul 16, 2024
34490f9
Add more down to earth examples
sneridagh Jul 16, 2024
3152854
Apply suggestions from code review
sneridagh Jul 18, 2024
b89d0ce
Refactor and re-document for clarity and simplicity
sneridagh Jul 22, 2024
11c24c7
locales
sneridagh Jul 22, 2024
e11e9d5
Remove cruft
sneridagh Jul 22, 2024
8094fa4
Bring back correct typing
sneridagh Jul 22, 2024
0b4f253
carry over suggestions from #6161 to index.md
stevepiercy Jul 22, 2024
ca2bdd2
Steve's review and edits of validation.md
stevepiercy Jul 22, 2024
641ce69
Apply suggestions from code review
sneridagh Jul 23, 2024
2a61300
Unify validator names, ending in `Validator`
sneridagh Jul 23, 2024
091711a
Fix start-end date validator i18n msg, add tests
sneridagh Jul 23, 2024
79b9a0f
Add pattern validator
sneridagh Jul 23, 2024
6cb3f2b
Implemented default `maxItems`/`minItems`
sneridagh Jul 23, 2024
0999019
Update docs/source/configuration/validation.md
sneridagh Jul 24, 2024
e787a8a
Apply suggestions from code review
sneridagh Jul 24, 2024
44001cb
Added missing docs for utilities in types
sneridagh Jul 24, 2024
aff0010
Add initial sort for keys when hashing them into the depsString
sneridagh Jul 24, 2024
443f9c7
Fix out of date registry changelog
sneridagh Jul 24, 2024
01a14cf
Include in the documentation all the default validators in Volto
sneridagh Jul 24, 2024
f8b7287
Change the name of the dependency key from `widgetName` to `widget`
sneridagh Jul 24, 2024
f7dee7e
Remove granularity from the paragraph
sneridagh Jul 24, 2024
2578cf5
locales
sneridagh Jul 24, 2024
a2afa51
Add an example of how to build an invariant
sneridagh Jul 24, 2024
0b2c26e
Move the default validators section to the top
sneridagh Jul 24, 2024
3140a42
Merge branch 'main' into extensible-validation-format
sneridagh Jul 24, 2024
2e2df93
Merge branch 'main' into extensible-validation-format
sneridagh Jul 24, 2024
c01bb39
Apply suggestions from code review
sneridagh Jul 26, 2024
f619cd8
Pass `blocksErrors` as a separate prop, aside from the whole errors o…
sneridagh Jul 29, 2024
461b188
Add new `blocksErrors` prop to all stock blocks
sneridagh Jul 29, 2024
5f32593
Remove the optional from Validator type, since the engine always push…
sneridagh Jul 29, 2024
0eaf1ee
Merge branch 'main' into extensible-validation-format
sneridagh Jul 30, 2024
d50cda0
Cypress Test for Validation of Field Types in CoreSandBox (#6217)
Tishasoumya-02 Jul 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 62 additions & 20 deletions docs/source/configuration/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,54 @@ myst:

# Client side form field validation

Volto provides a mechanism for delivering form field validation in an extensible way.
Volto provides an extensible way to validate form field values.
This extensibility is based on the Volto registry.
It applies to content types, custom programatically generated forms, and blocks schema settings.
The mechanism serializes all of them according to the [JSON schema standard](https://json-schema.org/draft/2020-12/json-schema-validation).
Finally Volto generates the form from the serialization.


(voltos-default-validators-label)=

## Volto's default validators

Volto provides a set of validators by default:

### Strings
- minLength
- maxLength
- pattern

### Password
- minLength
- maxLength
- pattern

### Numbers
- isNumber
- minimum
- maximum

### Integers
- isInteger
- minimum
- maximum

### Arrays
- maxItems
- minItems
- uniqueItems

### Per widget
- email
- url

### Event content type
- start/end dates check
sneridagh marked this conversation as resolved.
Show resolved Hide resolved

You can find them in the module {file}`packages/volto/src/config/validators.ts`.


## Register a validator

You can register a validator using the `registerUtility` method in the registry API from your add-on configuration.
Expand All @@ -24,11 +65,7 @@ You can register a validator using the `registerUtility` method in the registry
### Register and declare a simple validator

This section describes how to validate a field with a specific validator, a common use case.
Volto also provides some default validators.

```{seealso}
{ref}`voltos-default-validators-label`
```

#### Volto custom forms and block schema forms

Expand Down Expand Up @@ -107,8 +144,7 @@ The `urlValidator` method validator will be applied for the content type field `

### Advanced scenarios

In case you need more granularity, and you don't have access to modify the existing implementation of the JSON schema definitions for existing content types, blocks, or forms, you can use the following advanced validator registrations, using `field`, `widget`, `behaviorName`, or `blockType` validator registrations.

If, for some reason, you can't modify the existing implementation of the JSON schema definitions for existing content types, blocks, or forms, you can use the following advanced validator registrations, which allows you to register validators depending on the `field`, the `widget`, the `behaviorName` (in case of a content type), or the `blockType` (in case of a block).
sneridagh marked this conversation as resolved.
Show resolved Hide resolved

#### Field `type` validators

Expand Down Expand Up @@ -154,7 +190,7 @@ config.registerUtility({
Field `widget` validators are applied depending on the specified `widget` of the field.
You should specify the `widget` either in the JSON schema of the block or as additional data in the content type definition.

The following example shows how to specify the `widget` either in the JSON schema of the block.
The following example shows how to specify the `widget` in the JSON schema of the block.

```ts
let blockSchema = {
Expand All @@ -176,14 +212,14 @@ config.registerUtility({
type: 'validator',
name: 'phoneNumber',
dependencies: {
widgetName: 'phoneNumber',
widget: 'phoneNumber',
},
method: phoneValidator,
})
```

The following example shows how to specify the `widget` in the content type definition in the schema hints in the backend using `frontendOptions`.
The validation engine will behave the same, applying the `urlValidator` method validator for the content type field `customField` in the previous example.
The validation engine will behave the same as in the JSON schema of the block, applying the `urlValidator` method validator for the content type field `customField` in the previous example.

```python
from plone.supermodel import model
Expand Down Expand Up @@ -243,14 +279,6 @@ config.registerUtility({
```


(voltos-default-validators-label)=

## Volto's default validators

Volto provides a set of validators by default.
You can find them in the module {file}`packages/volto/src/config/validators.ts`.


### Override a validator

You can override a validator in your add-on in the same way as any other component defined in the registry.
Expand Down Expand Up @@ -291,7 +319,21 @@ export const isNumber = ({ value, formatMessage }: Validator) => {
Using `formData`, you can perform validation checks using other field data as source.
This is useful when you want to validate two related fields, such as ensuring the end date of an event is after its start date.
You can create invariant validator types.
In the following code snippet you can check how to create a validator method for checking if the Event content type `start`/`end` fields are valid:
sneridagh marked this conversation as resolved.
Show resolved Hide resolved

```{todo}
Needs example.
```ts
export const startEventDateRangeValidator = ({
value,
field,
formData,
formatMessage,
}: Validator) => {
const isValid =
value && formData.end && new Date(value) < new Date(formData.end);
return !isValid
? formatMessage(messages.startEventRange, {
endDateValueOrEndFieldName: formData.end || 'end',
})
: null;
};
```
24 changes: 13 additions & 11 deletions docs/source/upgrade-guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ The only Volto component that makes use of it is `PersonalPreferences`.
If you shadow it, then you should update this component.
For the rest, it is unlikely that your code refers to this module, since it's used internally by Volto itself.


### Renamed `test-setup-config` module

`test-setup-config.js` has been renamed to `test-setup-config.jsx` since, in fact, it contains JSX.
Expand All @@ -377,6 +378,18 @@ The `react-share` library and `SocialSharing` component has not been used in the
If you still use it, you can add it to your main add-on dependency, and extract the `SocialSharing` component from Volto 17 as a custom component in your add-on code.


### Refactor of `FormValidation` module

The `packages/volto/src/helpers/FormValidation/FormValidation.jsx` module has been heavily refactored.
Some helper functions have been moved to `packages/volto/src/helpers/FormValidation/validators.ts`.
None of those functions were exported in the first place, so no imports will be broken.
If you shadowed the module {file}`packages/volto/src/helpers/FormValidation/FormValidation.jsx`, you should review it and update it accordingly.

```{seealso}
{doc}`../configuration/validation`
```


### `SchemaWidget` widget registration change

Previously, it was registered as a widget `id` assigned to the `schema` key. Due to this common key name, this definition could leak the widget and be applied to unwanted fields.
Expand All @@ -391,17 +404,6 @@ schema: {
// rest of the form definition...
```

### Refactor of `FormValidation` module

The `packages/volto/src/helpers/FormValidation/FormValidation.jsx` module has been heavily refactored.
Some helper functions have been moved to `packages/volto/src/helpers/FormValidation/validators.ts`.
None of those functions were exported in the first place, so no imports will be broken.
If you shadowed the module {file}`packages/volto/src/helpers/FormValidation/FormValidation.jsx`, you should review it and update it accordingly.

```{seealso}
{doc}`../configuration/validation`
```


(volto-upgrade-guide-17.x.x)=

Expand Down
2 changes: 1 addition & 1 deletion packages/registry/news/6161.feature
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Add `getComponents` that match a partial set of dependencies, given a name. @sneridagh
Added Utilities registry, `registerUtility`, `getUtility` and `getUtilities` @sneridagh
1 change: 1 addition & 0 deletions packages/registry/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ class Config {
throw new Error('No method provided');
} else {
depsString = Object.keys(dependencies)
.sort()
.map((key) => `${key}:${dependencies[key]}`)
.join('+');
}
sneridagh marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
1 change: 0 additions & 1 deletion packages/types/news/6161.bugfix

This file was deleted.

2 changes: 2 additions & 0 deletions packages/types/news/6161.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Added `errors` shape to the `BlockEditProps`.
Added typings for Utilities registry @sneridagh
sneridagh marked this conversation as resolved.
Show resolved Hide resolved
35 changes: 25 additions & 10 deletions packages/volto/locales/ca/LC_MESSAGES/volto.po
Original file line number Diff line number Diff line change
Expand Up @@ -1268,11 +1268,6 @@ msgstr ""
msgid "End Date"
msgstr "Data de finalització"

#. Default: "Event end date happens before the event start date"
#: helpers/MessageLabels/MessageLabels
msgid "End event date happens before than the start event date"
msgstr ""

#. Default: "Enter URL or select an item"
#: components/manage/AnchorPlugin/components/LinkButton/AddLinkForm
msgid "Enter URL or select an item"
Expand Down Expand Up @@ -1360,11 +1355,21 @@ msgstr ""
msgid "Event"
msgstr ""

#. Default: "Event end date must be on or after {startDateValueOrStartFieldName}"
#: helpers/MessageLabels/MessageLabels
msgid "Event end date must be on or after {startDateValueOrStartFieldName}"
msgstr ""

#. Default: "Event listing"
#: config/Views
msgid "Event listing"
msgstr "Llista d'esdeveniments"

#. Default: "Event start date must be on or before {endDateValueOrEndFieldName}"
#: helpers/MessageLabels/MessageLabels
msgid "Event start date must be on or before {endDateValueOrEndFieldName}"
msgstr ""

#. Default: "Event view"
#: config/Views
msgid "Event view"
Expand Down Expand Up @@ -3442,11 +3447,6 @@ msgstr "Dividir"
msgid "Start Date"
msgstr "Data d'inici"

#. Default: "Event start date happens later than the event end date"
#: helpers/MessageLabels/MessageLabels
msgid "Start event date happens later than the end event date"
msgstr ""

#. Default: "Start of the recurrence"
#: components/manage/Widgets/RecurrenceWidget/Occurences
msgid "Start of the recurrence"
Expand Down Expand Up @@ -3675,6 +3675,16 @@ msgstr "No s'ha pogut suprimir l'element."
msgid "The link address is:"
msgstr "L'adreça de l'enllaç és:"

#. Default: "The number of items must be greater than or equal to {minItems}"
#: helpers/MessageLabels/MessageLabels
msgid "The number of items must be greater than or equal to {minItems}"
msgstr ""

#. Default: "The number of items must be less than or equal to {maxItems}"
#: helpers/MessageLabels/MessageLabels
msgid "The number of items must be less than or equal to {maxItems}"
msgstr ""

#. Default: "The provided alternative url already exists!"
#: components/manage/Aliases/Aliases
msgid "The provided alternative url already exists!"
Expand All @@ -3691,6 +3701,11 @@ msgstr "El procés de registre ha estat satisfactori. Si us plau, comproveu la v
msgid "The site configuration is outdated and needs to be upgraded."
msgstr ""

#. Default: "The value does not match the pattern {pattern}"
#: helpers/MessageLabels/MessageLabels
msgid "The value does not match the pattern {pattern}"
msgstr ""

#. Default: "The working copy was discarded"
#: components/manage/Toolbar/More
msgid "The working copy was discarded"
Expand Down
35 changes: 25 additions & 10 deletions packages/volto/locales/de/LC_MESSAGES/volto.po
Original file line number Diff line number Diff line change
Expand Up @@ -1267,11 +1267,6 @@ msgstr "Aktiviert?"
msgid "End Date"
msgstr "Enddatum"

#. Default: "Event end date happens before the event start date"
#: helpers/MessageLabels/MessageLabels
msgid "End event date happens before than the start event date"
msgstr ""

#. Default: "Enter URL or select an item"
#: components/manage/AnchorPlugin/components/LinkButton/AddLinkForm
msgid "Enter URL or select an item"
Expand Down Expand Up @@ -1359,11 +1354,21 @@ msgstr "Fehler"
msgid "Event"
msgstr "Ereignis"

#. Default: "Event end date must be on or after {startDateValueOrStartFieldName}"
#: helpers/MessageLabels/MessageLabels
msgid "Event end date must be on or after {startDateValueOrStartFieldName}"
msgstr ""

#. Default: "Event listing"
#: config/Views
msgid "Event listing"
msgstr "Termine"

#. Default: "Event start date must be on or before {endDateValueOrEndFieldName}"
#: helpers/MessageLabels/MessageLabels
msgid "Event start date must be on or before {endDateValueOrEndFieldName}"
msgstr ""

#. Default: "Event view"
#: config/Views
msgid "Event view"
Expand Down Expand Up @@ -3441,11 +3446,6 @@ msgstr "Aufsplitten"
msgid "Start Date"
msgstr "Anfangsdatum"

#. Default: "Event start date happens later than the event end date"
#: helpers/MessageLabels/MessageLabels
msgid "Start event date happens later than the end event date"
msgstr ""

#. Default: "Start of the recurrence"
#: components/manage/Widgets/RecurrenceWidget/Occurences
msgid "Start of the recurrence"
Expand Down Expand Up @@ -3674,6 +3674,16 @@ msgstr "Das Objekt kann nicht gelöscht werden."
msgid "The link address is:"
msgstr "Die Linkadresse lautet:"

#. Default: "The number of items must be greater than or equal to {minItems}"
#: helpers/MessageLabels/MessageLabels
msgid "The number of items must be greater than or equal to {minItems}"
msgstr ""

#. Default: "The number of items must be less than or equal to {maxItems}"
#: helpers/MessageLabels/MessageLabels
msgid "The number of items must be less than or equal to {maxItems}"
msgstr ""

#. Default: "The provided alternative url already exists!"
#: components/manage/Aliases/Aliases
msgid "The provided alternative url already exists!"
Expand All @@ -3690,6 +3700,11 @@ msgstr "Bitte prüfen Sie Ihr E-Mail Postfach. Sie sollten eine E-Mail erhalten
msgid "The site configuration is outdated and needs to be upgraded."
msgstr "Die Seitenkonfiguration ist veraltet und muss aktualisiert werden."

#. Default: "The value does not match the pattern {pattern}"
#: helpers/MessageLabels/MessageLabels
msgid "The value does not match the pattern {pattern}"
msgstr ""

#. Default: "The working copy was discarded"
#: components/manage/Toolbar/More
msgid "The working copy was discarded"
Expand Down
Loading
Loading