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

Add foundations for extensible validation in forms #6161

Closed
wants to merge 25 commits into from

Conversation

sneridagh
Copy link
Member

@sneridagh sneridagh commented Jul 10, 2024

No description provided.

Copy link

netlify bot commented Jul 10, 2024

Deploy Preview for plone-components canceled.

Name Link
🔨 Latest commit 3152854
🔍 Latest deploy log https://app.netlify.com/sites/plone-components/deploys/6698c82ca89ad600086fc5c5

@sneridagh sneridagh requested a review from stevepiercy July 12, 2024 13:44
@ericof
Copy link
Member

ericof commented Jul 14, 2024

@wesleybl, @luxcas This is about getting validation also on blocks

```

It takes two `dependencies`.
The first element should be the `default` identifier, and the second you can set it up to identify the validator.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not explained why default should be the first identifier.

```ts
config.registerComponent({
name: 'fieldValidator',
dependencies: ['integer', 'maximum'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here the default is missing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ummm, in that line, the default is not needed here, because it's not a default validator, it's a type one, so the first dependency is the name of the type.

customField: {
title: 'Default field',
description: '',
validator: 'isURL',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe allow passing directly a validator function as well? We're way past the point "schemas should be serializable" and for one-offs it's easier to declare them inline.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the pattern we are teaching is go for "named" components and call them by name (partially because of the duality forms from the backend/content types and the client defined forms in blocks), I'd refrain to do so, because then we will have two use cases, one where you can do it, and another use case in which you can't.

Also, in terms of explaining this in docs will became tricky and separate this is already confusing (I've been there when reviewing this with @ericof). Some other considerations is testability, because in that regard, modularization >> inlining things.

So, in principle, I would be -1 for this.

@ichim-david ichim-david requested review from avoinea and a team July 16, 2024 10:50
docs/source/upgrade-guide/index.md Outdated Show resolved Hide resolved
packages/volto/src/helpers/MessageLabels/MessageLabels.js Outdated Show resolved Hide resolved
packages/volto/src/helpers/MessageLabels/MessageLabels.js Outdated Show resolved Hide resolved
name: 'fieldValidator',
dependencies: ['phoneNumber', 'isValidPhone'],
component: phoneValidator,
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What makes this a widget validator instead of a type validator?

});
```

The first dependency should be the `id` of the block, and the second the `id` of the field.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it the @type of the block?

```

It does not need to be tied to any field `type` or `widget` definition.
It runs in addition to all the above, so it complements the other validators if any apply.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My biggest piece of feedback on this overall is using an array for the dependencies feels like trying to fit a square peg into a round hole. It's quite hard to keep track of all the different ways validators can be registered and which values need to go in the dependencies for each one. And it also seems to be like there might be some ambiguous cases, like if there is a field type and a widget type with the same name.

I don't know if the registry supports this but would it be possible to make things a bit more explicit by specifying the dependencies as an object instead of an array? So for example:

config.registerComponent({
  name: 'fieldValidator',
  dependencies: {
    blockType: 'slider',
    fieldName: 'url',
    validatorName: 'isURL',
  },
  component: urlValidator,
});

Copy link
Member Author

@sneridagh sneridagh Jul 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davisagli TBH, I'm not fully satisfied with it... I thought that the reason was mainly because we can't get it to work as satisfying as the ZCA works :) As you know, I initially envisioned and implemented the resolution algorithm using a string and concating the deps if we have an array, so I've played with that concept, even in the new getComponents registry API method.

Using an object would be possible too, I guess. I can play with the idea, and leave the original API in place also.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davisagli I did an initial implementation of using an object instead of an array but I have doubts on how to hash the resultant key to store that object, so then getComponents work in the same simple way, under the same concepts that we had before. It does work, but under some assumptions that I do not like or they are not satisfactory/safe :/

Maybe if you have some time, we can talk about it.

@tiberiuichim
Copy link
Contributor

tiberiuichim commented Jul 19, 2024

@sneridagh Is it possible to pass parameters to validators? For example, if I have a validator "minmax", would I have to create and register a custom one for each use case?

Edit: nevermind, the validator function gets the field definition, so it's possible to set those in the schema


### `default` validators

These validators are registered and applied to all fields.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For context, does "all" mean for all form blocks, or something else? I guess form blocks, but please correct me, if not.

Suggested change
These validators are registered and applied to all fields.
Default validators are registered and applied to all fields for all form blocks.

});
```

It takes two `dependencies` being the first element a fixed `default` identifier, and the second you can set it up to identify the validator itself.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to flesh out what the dependencies key actually means and how to use it.

  1. What's the purpose of the default identifier?
  2. Can I use more than two identifiers in the dependencies array?
Suggested change
It takes two `dependencies` being the first element a fixed `default` identifier, and the second you can set it up to identify the validator itself.
The `dependencies` key accepts an array of two values.
The first element has the identifier of `default`, and the second element has the identifier of the validator.
The second identifier can be any string.
In the following example, the second identifier is `minLength`.

```

It takes two `dependencies` being the first element a fixed `default` identifier, and the second you can set it up to identify the validator itself.
In the case of the example, this other dependency is `minLength`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
In the case of the example, this other dependency is `minLength`.


It takes two `dependencies` being the first element a fixed `default` identifier, and the second you can set it up to identify the validator itself.
In the case of the example, this other dependency is `minLength`.
It can be any string.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
It can be any string.

dependencies: ['default', 'minLength'],
component: minLengthValidator,
});
```
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move the example after the explanation. It's jarring to throw an example before its description without some kind of introduction.


Volto provides a set of validators by default, you can find them in this module: `packages/volto/src/config/validators.ts`

### How to override them
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
### How to override them
### Override Volto's default validators


### How to override them

You can override them in your add-on as any other component defined in the registry, by redefining them using the same `dependencies`, and providing your own.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
You can override them in your add-on as any other component defined in the registry, by redefining them using the same `dependencies`, and providing your own.
You can override Volto's default validators in your add-on as any other component defined in the registry.
To do so, use the `dependencies` key, and provide your own values.
```{todo}
Provide usage example.
```

};
```

This is an example of an `isNumber` validator:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This is an example of an `isNumber` validator:
The following example shows an `isNumber` validator.

};
```

Using the `formData` you can perform validation checks using other field data as source.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Using the `formData` you can perform validation checks using other field data as source.
Using `formData`, you can perform validation checks between multiple fields' data.

```

Using the `formData` you can perform validation checks using other field data as source.
This is interesting in the case that two fields are related, like `start` and `end` dates.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This is interesting in the case that two fields are related, like `start` and `end` dates.
This is useful when you need to compare the values of multiple fields, such as ensuring that the end date and time of an event is after its start.
```{todo}
Provide usage example.
```

@ichim-david
Copy link
Member

Closing this pull request since we have the superseding #6181

@ichim-david ichim-david deleted the extensible-validation branch July 29, 2024 05:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants