@plone/volto 18.0.0-alpha.41 → 18.0.0-alpha.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/locales/ca/LC_MESSAGES/volto.po +31 -1
  3. package/locales/ca.json +1 -1
  4. package/locales/de/LC_MESSAGES/volto.po +31 -1
  5. package/locales/de.json +1 -1
  6. package/locales/en/LC_MESSAGES/volto.po +31 -1
  7. package/locales/en.json +1 -1
  8. package/locales/es/LC_MESSAGES/volto.po +31 -1
  9. package/locales/es.json +1 -1
  10. package/locales/eu/LC_MESSAGES/volto.po +31 -1
  11. package/locales/eu.json +1 -1
  12. package/locales/fi/LC_MESSAGES/volto.po +31 -1
  13. package/locales/fi.json +1 -1
  14. package/locales/fr/LC_MESSAGES/volto.po +31 -1
  15. package/locales/fr.json +1 -1
  16. package/locales/hi/LC_MESSAGES/volto.po +31 -1
  17. package/locales/hi.json +1 -1
  18. package/locales/it/LC_MESSAGES/volto.po +31 -1
  19. package/locales/it.json +1 -1
  20. package/locales/ja/LC_MESSAGES/volto.po +31 -1
  21. package/locales/ja.json +1 -1
  22. package/locales/nl/LC_MESSAGES/volto.po +31 -1
  23. package/locales/nl.json +1 -1
  24. package/locales/pt/LC_MESSAGES/volto.po +31 -1
  25. package/locales/pt.json +1 -1
  26. package/locales/pt_BR/LC_MESSAGES/volto.po +31 -1
  27. package/locales/pt_BR.json +1 -1
  28. package/locales/ro/LC_MESSAGES/volto.po +31 -1
  29. package/locales/ro.json +1 -1
  30. package/locales/volto.pot +32 -2
  31. package/locales/zh_CN/LC_MESSAGES/volto.po +31 -1
  32. package/locales/zh_CN.json +1 -1
  33. package/package.json +3 -4
  34. package/razzle.config.js +2 -2
  35. package/src/components/index.js +0 -1
  36. package/src/components/manage/Blocks/Block/BlocksForm.jsx +5 -0
  37. package/src/components/manage/Blocks/Block/Order/Item.jsx +6 -2
  38. package/src/components/manage/Blocks/Block/Order/Order.jsx +2 -0
  39. package/src/components/manage/Blocks/Container/Data.jsx +10 -2
  40. package/src/components/manage/Blocks/Image/ImageSidebar.jsx +10 -2
  41. package/src/components/manage/Blocks/Listing/ListingData.jsx +10 -2
  42. package/src/components/manage/Blocks/Maps/MapsSidebar.jsx +3 -1
  43. package/src/components/manage/Blocks/Search/SearchBlockEdit.jsx +2 -0
  44. package/src/components/manage/Blocks/Search/SearchBlockView.jsx +18 -2
  45. package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +1 -1
  46. package/src/components/manage/Blocks/Teaser/Data.jsx +10 -2
  47. package/src/components/manage/Blocks/ToC/Edit.jsx +1 -0
  48. package/src/components/manage/Blocks/Video/Edit.jsx +1 -1
  49. package/src/components/manage/Blocks/Video/VideoSidebar.jsx +3 -1
  50. package/src/components/manage/Contents/Contents.jsx +1 -1
  51. package/src/components/manage/Controlpanels/ContentTypeSchema.jsx +1 -0
  52. package/src/components/manage/Controlpanels/UndoControlpanel.jsx +3 -3
  53. package/src/components/manage/Controlpanels/Users/UserGroupMembershipListing.jsx +28 -12
  54. package/src/components/manage/Controlpanels/Users/UserGroupMembershipMatrix.jsx +12 -4
  55. package/src/components/manage/Form/Form.jsx +85 -20
  56. package/src/components/manage/Form/InlineForm.jsx +2 -4
  57. package/src/components/manage/Form/ModalForm.jsx +1 -1
  58. package/src/components/manage/History/History.jsx +1 -1
  59. package/src/components/manage/Pluggable/Pluggable.test.js +1 -1
  60. package/src/components/manage/Toolbar/Toolbar.jsx +1 -1
  61. package/src/components/manage/Widgets/ArrayWidget.jsx +2 -2
  62. package/src/components/manage/Widgets/ImageWidget.jsx +6 -5
  63. package/src/components/manage/Widgets/RecurrenceWidget/EndField.jsx +7 -1
  64. package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.jsx +80 -31
  65. package/src/components/theme/Search/Search.jsx +13 -5
  66. package/src/components/theme/View/View.jsx +0 -7
  67. package/src/components/theme/View/View.test.jsx +0 -3
  68. package/src/config/Widgets.jsx +1 -1
  69. package/src/config/index.js +7 -2
  70. package/src/config/validation.ts +155 -0
  71. package/src/helpers/Extensions/withBlockExtensions.jsx +1 -1
  72. package/src/helpers/FormValidation/FormValidation.jsx +109 -170
  73. package/src/helpers/FormValidation/FormValidation.test.js +836 -8
  74. package/src/helpers/FormValidation/validators.ts +203 -0
  75. package/src/helpers/MessageLabels/MessageLabels.js +28 -0
  76. package/src/helpers/Url/Url.test.js +4 -4
  77. package/src/helpers/User/User.js +1 -1
  78. package/src/hooks/client/useClient.js +1 -1
  79. package/test-setup-config.jsx +7 -0
  80. package/theme/themes/default/modules/embed.variables +1 -1
  81. package/theme/themes/pastanaga/collections/form.overrides +34 -0
  82. package/theme/themes/pastanaga/extras/sidebar.less +4 -0
  83. package/theme/themes/pastanaga/extras/toolbar.less +10 -3
  84. package/tsconfig.declarations.json +3 -2
  85. package/types/components/index.d.ts +0 -1
  86. package/types/components/manage/Blocks/Block/Order/Order.d.ts +2 -1
  87. package/types/config/RichTextEditor/ToHTML.d.ts +1 -1
  88. package/types/config/Widgets.d.ts +2 -2
  89. package/types/config/validation.d.ts +3 -0
  90. package/types/helpers/Extensions/withBlockExtensions.d.ts +1 -1
  91. package/types/helpers/FormValidation/FormValidation.d.ts +1 -0
  92. package/types/helpers/FormValidation/validators.d.ts +29 -0
  93. package/types/helpers/MessageLabels/MessageLabels.d.ts +36 -0
  94. package/types/helpers/User/User.d.ts +1 -1
  95. package/src/components/theme/SocialSharing/SocialSharing.jsx +0 -48
  96. package/src/components/theme/SocialSharing/SocialSharing.test.jsx +0 -14
@@ -261,13 +261,6 @@ class View extends Component {
261
261
  this.props.content.subjects.length > 0 && (
262
262
  <Tags tags={this.props.content.subjects} />
263
263
  )}
264
- {/* Add opt-in social sharing if required, disabled by default */}
265
- {/* In the future this might be parameterized from the app config */}
266
- {/* <SocialSharing
267
- url={typeof window === 'undefined' ? '' : window.location.href}
268
- title={this.props.content.title}
269
- description={this.props.content.description || ''}
270
- /> */}
271
264
  {this.props.content.allow_discussion && (
272
265
  <Comments pathname={this.props.pathname} />
273
266
  )}
@@ -30,9 +30,6 @@ jest.mock('../../manage/Toolbar/Toolbar', () =>
30
30
  jest.fn(() => <div id="Portal" />),
31
31
  );
32
32
 
33
- jest.mock('../SocialSharing/SocialSharing', () =>
34
- jest.fn(() => <div id="SocialSharing" />),
35
- );
36
33
  jest.mock('../Comments/Comments', () => jest.fn(() => <div id="Comments" />));
37
34
  jest.mock('../Tags/Tags', () => jest.fn(() => <div id="Tags" />));
38
35
  jest.mock('../SlotRenderer/SlotRenderer', () =>
@@ -55,7 +55,6 @@ import ImageWidget from '@plone/volto/components/manage/Widgets/ImageWidget';
55
55
  // Widgets mapping
56
56
  export const widgetMapping = {
57
57
  id: {
58
- schema: SchemaWidget,
59
58
  subjects: TokenWidget,
60
59
  query: QuerystringWidget,
61
60
  recurrence: RecurrenceWidget,
@@ -89,6 +88,7 @@ export const widgetMapping = {
89
88
  autocomplete: SelectAutoComplete,
90
89
  color_picker: ColorPickerWidget,
91
90
  select: SelectWidget,
91
+ schema: SchemaWidget,
92
92
  },
93
93
  vocabulary: {
94
94
  'plone.app.vocabularies.Catalog': ObjectBrowserWidget,
@@ -32,6 +32,7 @@ import applyAddonConfiguration, { addonsInfo } from 'load-volto-addons';
32
32
  import ConfigRegistry from '@plone/volto/registry';
33
33
 
34
34
  import { getSiteAsyncPropExtender } from '@plone/volto/helpers';
35
+ import { registerValidators } from './validation';
35
36
 
36
37
  const host = process.env.HOST || 'localhost';
37
38
  const port = process.env.PORT || '3000';
@@ -97,7 +98,7 @@ let config = {
97
98
  process.env.RAZZLE_INTERNAL_API_PATH ||
98
99
  process.env.RAZZLE_API_PATH ||
99
100
  'http://localhost:8080/Plone', // Set it to '' for disabling the proxy
100
- // proxyRewriteTarget Set it for set a custom target for the proxy or overide the internal VHM rewrite
101
+ // proxyRewriteTarget Set it for set a custom target for the proxy or override the internal VHM rewrite
101
102
  // proxyRewriteTarget: '/VirtualHostBase/http/localhost:8080/Plone/VirtualHostRoot/_vh_api'
102
103
  // proxyRewriteTarget: 'https://myvoltositeinproduction.com'
103
104
  proxyRewriteTarget: process.env.RAZZLE_PROXY_REWRITE_TARGET || undefined,
@@ -209,8 +210,9 @@ let config = {
209
210
  },
210
211
  addonRoutes: [],
211
212
  addonReducers: {},
212
- slots: {},
213
213
  components,
214
+ slots: {},
215
+ utilities: {},
214
216
  };
215
217
 
216
218
  // The apiExpanders depends on a config of the object, so it's done here
@@ -238,5 +240,8 @@ ConfigRegistry.addonRoutes = config.addonRoutes;
238
240
  ConfigRegistry.addonReducers = config.addonReducers;
239
241
  ConfigRegistry.components = config.components;
240
242
  ConfigRegistry.slots = config.slots;
243
+ ConfigRegistry.utilities = config.utilities;
244
+
245
+ registerValidators(ConfigRegistry);
241
246
 
242
247
  applyAddonConfiguration(ConfigRegistry);
@@ -0,0 +1,155 @@
1
+ import { ConfigType } from '@plone/registry';
2
+
3
+ import {
4
+ minLengthValidator,
5
+ maxLengthValidator,
6
+ urlValidator,
7
+ emailValidator,
8
+ isNumberValidator,
9
+ maximumValidator,
10
+ minimumValidator,
11
+ isIntegerValidator,
12
+ maxItemsValidator,
13
+ minItemsValidator,
14
+ hasUniqueItemsValidator,
15
+ startEventDateRangeValidator,
16
+ endEventDateRangeValidator,
17
+ patternValidator,
18
+ } from '@plone/volto/helpers/FormValidation/validators';
19
+
20
+ const registerValidators = (config: ConfigType) => {
21
+ config.registerUtility({
22
+ name: 'minLength',
23
+ type: 'validator',
24
+ dependencies: { fieldType: 'string' },
25
+ method: minLengthValidator,
26
+ });
27
+
28
+ config.registerUtility({
29
+ name: 'maxLength',
30
+ type: 'validator',
31
+ dependencies: { fieldType: 'string' },
32
+ method: maxLengthValidator,
33
+ });
34
+
35
+ config.registerUtility({
36
+ name: 'pattern',
37
+ type: 'validator',
38
+ dependencies: { fieldType: 'string' },
39
+ method: patternValidator,
40
+ });
41
+
42
+ config.registerUtility({
43
+ name: 'minLength',
44
+ type: 'validator',
45
+ dependencies: { fieldType: 'password' },
46
+ method: minLengthValidator,
47
+ });
48
+
49
+ config.registerUtility({
50
+ name: 'maxLength',
51
+ type: 'validator',
52
+ dependencies: { fieldType: 'password' },
53
+ method: maxLengthValidator,
54
+ });
55
+
56
+ config.registerUtility({
57
+ name: 'pattern',
58
+ type: 'validator',
59
+ dependencies: { fieldType: 'password' },
60
+ method: patternValidator,
61
+ });
62
+
63
+ config.registerUtility({
64
+ name: 'email',
65
+ type: 'validator',
66
+ dependencies: { widget: 'email' },
67
+ method: emailValidator,
68
+ });
69
+
70
+ config.registerUtility({
71
+ name: 'url',
72
+ type: 'validator',
73
+ dependencies: { widget: 'url' },
74
+ method: urlValidator,
75
+ });
76
+
77
+ config.registerUtility({
78
+ name: 'number',
79
+ type: 'validator',
80
+ dependencies: { fieldType: 'number' },
81
+ method: isNumberValidator,
82
+ });
83
+
84
+ config.registerUtility({
85
+ name: 'minimum',
86
+ type: 'validator',
87
+ dependencies: { fieldType: 'number' },
88
+ method: minimumValidator,
89
+ });
90
+
91
+ config.registerUtility({
92
+ name: 'maximum',
93
+ type: 'validator',
94
+ dependencies: { fieldType: 'number' },
95
+ method: maximumValidator,
96
+ });
97
+
98
+ config.registerUtility({
99
+ name: 'integer',
100
+ type: 'validator',
101
+ dependencies: { fieldType: 'integer' },
102
+ method: isIntegerValidator,
103
+ });
104
+
105
+ config.registerUtility({
106
+ name: 'minimum',
107
+ type: 'validator',
108
+ dependencies: { fieldType: 'integer' },
109
+ method: minimumValidator,
110
+ });
111
+
112
+ config.registerUtility({
113
+ name: 'maximum',
114
+ type: 'validator',
115
+ dependencies: { fieldType: 'integer' },
116
+ method: maximumValidator,
117
+ });
118
+
119
+ config.registerUtility({
120
+ name: 'maxItems',
121
+ type: 'validator',
122
+ dependencies: { fieldType: 'array' },
123
+ method: maxItemsValidator,
124
+ });
125
+
126
+ config.registerUtility({
127
+ name: 'minItems',
128
+ type: 'validator',
129
+ dependencies: { fieldType: 'array' },
130
+ method: minItemsValidator,
131
+ });
132
+
133
+ config.registerUtility({
134
+ name: 'uniqueItems',
135
+ type: 'validator',
136
+ dependencies: { fieldType: 'array' },
137
+ method: hasUniqueItemsValidator,
138
+ });
139
+
140
+ config.registerUtility({
141
+ name: 'dateRangeValidator',
142
+ type: 'validator',
143
+ dependencies: { behaviorName: 'plone.eventbasic', fieldName: 'start' },
144
+ method: startEventDateRangeValidator,
145
+ });
146
+
147
+ config.registerUtility({
148
+ name: 'dateRangeValidator',
149
+ type: 'validator',
150
+ dependencies: { behaviorName: 'plone.eventbasic', fieldName: 'end' },
151
+ method: endEventDateRangeValidator,
152
+ });
153
+ };
154
+
155
+ export { registerValidators };
@@ -51,7 +51,7 @@ export function resolveExtension(name, extensions, data) {
51
51
  * - extensions is the blocksConfig extensions object for that block
52
52
  * - resolvedExtensions is an object with
53
53
  * `{ variation, <someExtensionA>, <someExtensionB> }` and each of these
54
- * fields hold the coresponding definition object from the block's
54
+ * fields hold the corresponding definition object from the block's
55
55
  * configuration.
56
56
  */
57
57
  export function resolveBlockExtensions(data, blocksConfig) {
@@ -1,4 +1,4 @@
1
- import { map, uniq, keys, intersection, isEmpty } from 'lodash';
1
+ import { map, keys, intersection, isEmpty } from 'lodash';
2
2
  import { messages } from '../MessageLabels/MessageLabels';
3
3
  import config from '@plone/volto/registry';
4
4
  import { toast } from 'react-toastify';
@@ -11,7 +11,12 @@ import Toast from '@plone/volto/components/manage/Toast/Toast';
11
11
  * @param {string | number} valueToCompare can compare '47' < 50
12
12
  * @param {Function} intlFunc
13
13
  */
14
- const validationMessage = (isValid, criterion, valueToCompare, intlFunc) =>
14
+ export const validationMessage = (
15
+ isValid,
16
+ criterion,
17
+ valueToCompare,
18
+ intlFunc,
19
+ ) =>
15
20
  !isValid
16
21
  ? intlFunc(messages[criterion], {
17
22
  len: valueToCompare,
@@ -19,142 +24,7 @@ const validationMessage = (isValid, criterion, valueToCompare, intlFunc) =>
19
24
  : null;
20
25
 
21
26
  /**
22
- * Returns if based on the criterion the value is lower or equal
23
- * @param {string | number} value can compare '47' < 50
24
- * @param {string | number} valueToCompare can compare '47' < 50
25
- * @param {string} maxCriterion
26
- * @param {Function} intlFunc
27
- */
28
- const isMaxPropertyValid = (value, valueToCompare, maxCriterion, intlFunc) => {
29
- const isValid = valueToCompare !== undefined ? value <= valueToCompare : true;
30
- return validationMessage(isValid, maxCriterion, valueToCompare, intlFunc);
31
- };
32
-
33
- /**
34
- * Returns if based on the criterion the value is higher or equal
35
- * @param {string | number} value can compare '47' < 50
36
- * @param {string | number} valueToCompare can compare '47' < 50
37
- * @param {string} minCriterion
38
- * @param {Function} intlFunc
39
- */
40
- const isMinPropertyValid = (value, valueToCompare, minCriterion, intlFunc) => {
41
- const isValid = valueToCompare !== undefined ? value >= valueToCompare : true;
42
- return validationMessage(isValid, minCriterion, valueToCompare, intlFunc);
43
- };
44
-
45
- const widgetValidation = {
46
- email: {
47
- isValidEmail: (emailValue, emailObj, intlFunc) => {
48
- // Email Regex taken from from WHATWG living standard:
49
- // https://html.spec.whatwg.org/multipage/input.html#e-mail-state-(type=email)
50
- const emailRegex =
51
- /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
52
- const isValid = emailRegex.test(emailValue);
53
- return !isValid ? intlFunc(messages.isValidEmail) : null;
54
- },
55
- minLength: (emailValue, emailObj, intlFunc) =>
56
- isMinPropertyValid(
57
- emailValue.length,
58
- emailObj.minLength,
59
- 'minLength',
60
- intlFunc,
61
- ),
62
- maxLength: (emailValue, emailObj, intlFunc) =>
63
- isMaxPropertyValid(
64
- emailValue.length,
65
- emailObj.maxLength,
66
- 'maxLength',
67
- intlFunc,
68
- ),
69
- },
70
- url: {
71
- isValidURL: (urlValue, urlObj, intlFunc) => {
72
- var urlRegex = new RegExp(
73
- '^(https?:\\/\\/)?' + // validate protocol
74
- '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // validate domain name
75
- '((\\d{1,3}\\.){3}\\d{1,3}))|' + // validate OR ip (v4) address
76
- '(localhost)' + // validate OR localhost address
77
- '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // validate port and path
78
- '(\\?[;&a-z\\d%_.~+=-]*)?' + // validate query string
79
- '(\\#[-a-z\\d_]*)?$', // validate fragment locator
80
- 'i',
81
- );
82
- const isValid = urlRegex.test(urlValue);
83
- return !isValid ? intlFunc(messages.isValidURL) : null;
84
- },
85
- minLength: (urlValue, urlObj, intlFunc) =>
86
- isMinPropertyValid(
87
- urlValue.length,
88
- urlObj.minLength,
89
- 'minLength',
90
- intlFunc,
91
- ),
92
- maxLength: (urlValue, urlObj, intlFunc) =>
93
- isMaxPropertyValid(
94
- urlValue.length,
95
- urlObj.maxLength,
96
- 'maxLength',
97
- intlFunc,
98
- ),
99
- },
100
- password: {
101
- minLength: (passwordValue, passwordObj, intlFunc) =>
102
- isMinPropertyValid(
103
- passwordValue.length,
104
- passwordObj.minLength,
105
- 'minLength',
106
- intlFunc,
107
- ),
108
- maxLength: (passwordValue, passwordObj, intlFunc) =>
109
- isMaxPropertyValid(
110
- passwordValue.length,
111
- passwordObj.maxLength,
112
- 'maxLength',
113
- intlFunc,
114
- ),
115
- },
116
- string: {
117
- minLength: (value, itemObj, intlFunc) =>
118
- isMinPropertyValid(
119
- value.length,
120
- itemObj.minLength,
121
- 'minLength',
122
- intlFunc,
123
- ),
124
- maxLength: (value, itemObj, intlFunc) =>
125
- isMaxPropertyValid(
126
- value.length,
127
- itemObj.maxLength,
128
- 'maxLength',
129
- intlFunc,
130
- ),
131
- },
132
- number: {
133
- isNumber: (value, itemObj, intlFunc) => {
134
- const floatRegex = /^[+-]?\d+(\.\d+)?$/;
135
- const isValid = !isNaN(value) && floatRegex.test(value);
136
- return !isValid ? intlFunc(messages.isNumber) : null;
137
- },
138
- minimum: (value, itemObj, intlFunc) =>
139
- isMinPropertyValid(value, itemObj.minimum, 'minimum', intlFunc),
140
- maximum: (value, itemObj, intlFunc) =>
141
- isMaxPropertyValid(value, itemObj.maximum, 'maximum', intlFunc),
142
- },
143
- integer: {
144
- isInteger: (value, itemObj, intlFunc) => {
145
- const intRegex = /^-?[0-9]+$/;
146
- const isValid = !isNaN(value) && intRegex.test(value);
147
- return !isValid ? intlFunc(messages.isInteger) : null;
148
- },
149
- minimum: (value, itemObj, intlFunc) =>
150
- isMinPropertyValid(value, itemObj.minimum, 'minimum', intlFunc),
151
- maximum: (value, itemObj, intlFunc) =>
152
- isMaxPropertyValid(value, itemObj.maximum, 'maximum', intlFunc),
153
- },
154
- };
155
-
156
- /**
157
- * The string that comes my not be a valid JSON
27
+ * The string that comes might not be a valid JSON
158
28
  * @param {string} requestItem
159
29
  */
160
30
  export const tryParseJSON = (requestItem) => {
@@ -171,24 +41,6 @@ export const tryParseJSON = (requestItem) => {
171
41
  return resultObj;
172
42
  };
173
43
 
174
- /**
175
- * Returns errors if obj has unique Items
176
- * @param {Object} field
177
- * @param {*} fieldData
178
- * @returns {Object[string]} - list of errors
179
- */
180
- const hasUniqueItems = (field, fieldData, formatMessage) => {
181
- const errors = [];
182
- if (
183
- field.uniqueItems &&
184
- fieldData &&
185
- uniq(fieldData).length !== fieldData.length
186
- ) {
187
- errors.push(formatMessage(messages.uniqueItems));
188
- }
189
- return errors;
190
- };
191
-
192
44
  /**
193
45
  * If required fields are undefined, return list of errors
194
46
  * @returns {Object[string]} - list of errors
@@ -252,35 +104,122 @@ const validateFieldsPerFieldset = (
252
104
  touchedField,
253
105
  );
254
106
 
255
- map(schema.properties, (field, fieldId) => {
256
- const fieldWidgetType = field.widget || field.type;
257
- const widgetValidationCriteria = widgetValidation[fieldWidgetType]
258
- ? Object.keys(widgetValidation[fieldWidgetType])
259
- : [];
260
- let fieldData = formData[fieldId];
261
- // test each criterion ex maximum, isEmail, isUrl, maxLength etc
262
- const fieldErrors = widgetValidationCriteria
107
+ function checkFieldErrors(fieldValidationCriteria, field, fieldData) {
108
+ return fieldValidationCriteria
263
109
  .map((widgetCriterion) => {
264
110
  const errorMessage =
265
111
  fieldData === undefined || fieldData === null
266
112
  ? null
267
- : widgetValidation[fieldWidgetType][widgetCriterion](
268
- fieldData,
113
+ : widgetCriterion.method({
114
+ value: fieldData,
269
115
  field,
116
+ formData,
270
117
  formatMessage,
271
- );
118
+ });
272
119
  return errorMessage;
273
120
  })
274
121
  .filter((item) => !!item);
122
+ }
123
+
124
+ Object.entries(schema.properties).forEach(([fieldId, field]) => {
125
+ let fieldData = formData[fieldId];
126
+
127
+ // Validation per specific validator set (format property)
128
+ const hasSpecificValidator =
129
+ field.widgetOptions?.frontendOptions?.format || field.format;
130
+ let specificFieldErrors = [];
131
+ if (hasSpecificValidator) {
132
+ const specificValidationCriteria = config.getUtilities({
133
+ type: 'validator',
134
+ dependencies: { format: hasSpecificValidator },
135
+ });
136
+
137
+ specificFieldErrors = checkFieldErrors(
138
+ specificValidationCriteria,
139
+ field,
140
+ fieldData,
141
+ );
142
+ }
143
+
144
+ // Validation per field type
145
+ const fieldType = field.type || 'string'; // defaults to string
146
+ const fieldTypeValidationCriteria = config.getUtilities({
147
+ type: 'validator',
148
+ dependencies: { fieldType },
149
+ });
150
+
151
+ const fieldErrors = checkFieldErrors(
152
+ fieldTypeValidationCriteria,
153
+ field,
154
+ fieldData,
155
+ );
156
+
157
+ // Validation per field widget
158
+ const widgetName =
159
+ field.widgetOptions?.frontendOptions?.widget || field.widget || '';
160
+
161
+ let widgetErrors = [];
162
+ if (widgetName) {
163
+ const widgetNameValidationCriteria = config.getUtilities({
164
+ type: 'validator',
165
+ dependencies: { widget: widgetName },
166
+ });
167
+
168
+ widgetErrors = checkFieldErrors(
169
+ widgetNameValidationCriteria,
170
+ field,
171
+ fieldData,
172
+ );
173
+ }
174
+
175
+ // Validation per specific behavior and field name (for content types)
176
+ const behaviorName = field.behavior;
177
+ let perBehaviorFieldErrors = [];
178
+ if (behaviorName) {
179
+ const specificPerBehaviorFieldValidationCriteria = config.getUtilities({
180
+ type: 'validator',
181
+ dependencies: { behaviorName, fieldName: fieldId },
182
+ });
183
+
184
+ perBehaviorFieldErrors = checkFieldErrors(
185
+ specificPerBehaviorFieldValidationCriteria,
186
+ field,
187
+ fieldData,
188
+ );
189
+ }
190
+
191
+ // Validation per block type validator (for blocks)
192
+ const blockType = formData['@type'];
193
+ let blockTypeFieldErrors = [];
194
+ if (blockType) {
195
+ const blockTypeFieldValidationCriteria = config.getUtilities({
196
+ type: 'validator',
197
+ dependencies: { blockType, fieldName: fieldId },
198
+ });
199
+
200
+ blockTypeFieldErrors = checkFieldErrors(
201
+ blockTypeFieldValidationCriteria,
202
+ field,
203
+ fieldData,
204
+ );
205
+ }
275
206
 
276
- const uniqueErrors = hasUniqueItems(field, fieldData, formatMessage);
277
- const mergedErrors = [...fieldErrors, ...uniqueErrors];
207
+ const mergedErrors = [
208
+ ...specificFieldErrors,
209
+ ...fieldErrors,
210
+ ...widgetErrors,
211
+ ...perBehaviorFieldErrors,
212
+ ...blockTypeFieldErrors,
213
+ ];
278
214
 
279
215
  if (mergedErrors.length > 0) {
280
216
  errors[fieldId] = [
281
217
  ...(errors[fieldId] || []),
218
+ ...specificFieldErrors,
282
219
  ...fieldErrors,
283
- ...uniqueErrors,
220
+ ...widgetErrors,
221
+ ...perBehaviorFieldErrors,
222
+ ...blockTypeFieldErrors,
284
223
  ];
285
224
  }
286
225
  });