@plone/volto 18.0.0-alpha.40 → 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 (102) hide show
  1. package/CHANGELOG.md +61 -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 +4 -5
  34. package/razzle.config.js +2 -2
  35. package/src/components/index.js +0 -1
  36. package/src/components/manage/AnchorPlugin/components/LinkButton/AddLinkForm.jsx +1 -1
  37. package/src/components/manage/AnchorPlugin/useLinkEditor.jsx +21 -21
  38. package/src/components/manage/Blocks/Block/BlocksForm.jsx +5 -0
  39. package/src/components/manage/Blocks/Block/Order/Item.jsx +6 -2
  40. package/src/components/manage/Blocks/Block/Order/Order.jsx +2 -0
  41. package/src/components/manage/Blocks/Container/Data.jsx +10 -2
  42. package/src/components/manage/Blocks/Image/ImageSidebar.jsx +10 -2
  43. package/src/components/manage/Blocks/Listing/ListingData.jsx +10 -2
  44. package/src/components/manage/Blocks/Maps/MapsSidebar.jsx +3 -1
  45. package/src/components/manage/Blocks/Search/SearchBlockEdit.jsx +2 -0
  46. package/src/components/manage/Blocks/Search/SearchBlockView.jsx +18 -2
  47. package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +1 -1
  48. package/src/components/manage/Blocks/Teaser/Data.jsx +10 -2
  49. package/src/components/manage/Blocks/ToC/Edit.jsx +1 -0
  50. package/src/components/manage/Blocks/Video/Edit.jsx +1 -1
  51. package/src/components/manage/Blocks/Video/VideoSidebar.jsx +3 -1
  52. package/src/components/manage/Contents/Contents.jsx +1 -1
  53. package/src/components/manage/Controlpanels/ContentTypeSchema.jsx +1 -0
  54. package/src/components/manage/Controlpanels/UndoControlpanel.jsx +3 -3
  55. package/src/components/manage/Controlpanels/Users/UserGroupMembershipListing.jsx +28 -12
  56. package/src/components/manage/Controlpanels/Users/UserGroupMembershipMatrix.jsx +12 -4
  57. package/src/components/manage/Form/Form.jsx +85 -20
  58. package/src/components/manage/Form/InlineForm.jsx +4 -6
  59. package/src/components/manage/Form/ModalForm.jsx +1 -1
  60. package/src/components/manage/History/History.jsx +1 -1
  61. package/src/components/manage/Pluggable/Pluggable.test.js +1 -1
  62. package/src/components/manage/Toolbar/Toolbar.jsx +1 -1
  63. package/src/components/manage/Widgets/ArrayWidget.jsx +2 -2
  64. package/src/components/manage/Widgets/ImageWidget.jsx +34 -10
  65. package/src/components/manage/Widgets/RecurrenceWidget/EndField.jsx +7 -1
  66. package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.jsx +80 -31
  67. package/src/components/theme/Login/Login.jsx +25 -4
  68. package/src/components/theme/Logout/Logout.jsx +2 -2
  69. package/src/components/theme/Search/Search.jsx +13 -5
  70. package/src/components/theme/View/View.jsx +0 -7
  71. package/src/components/theme/View/View.test.jsx +0 -3
  72. package/src/config/Widgets.jsx +1 -1
  73. package/src/config/index.js +7 -2
  74. package/src/config/validation.ts +155 -0
  75. package/src/helpers/Extensions/withBlockExtensions.jsx +1 -1
  76. package/src/helpers/FormValidation/FormValidation.jsx +109 -170
  77. package/src/helpers/FormValidation/FormValidation.test.js +836 -8
  78. package/src/helpers/FormValidation/validators.ts +203 -0
  79. package/src/helpers/MessageLabels/MessageLabels.js +28 -0
  80. package/src/helpers/Url/Url.test.js +4 -4
  81. package/src/helpers/User/User.js +1 -1
  82. package/src/hooks/client/useClient.js +1 -1
  83. package/test-setup-config.jsx +7 -0
  84. package/theme/themes/default/modules/embed.variables +1 -1
  85. package/theme/themes/pastanaga/collections/form.overrides +36 -2
  86. package/theme/themes/pastanaga/extras/blocks.less +14 -5
  87. package/theme/themes/pastanaga/extras/sidebar.less +4 -0
  88. package/theme/themes/pastanaga/extras/toolbar.less +10 -3
  89. package/tsconfig.declarations.json +3 -2
  90. package/types/components/index.d.ts +0 -1
  91. package/types/components/manage/Blocks/Block/Order/Order.d.ts +2 -1
  92. package/types/components/theme/Logout/Logout.d.ts +1 -1
  93. package/types/config/RichTextEditor/ToHTML.d.ts +1 -1
  94. package/types/config/Widgets.d.ts +2 -2
  95. package/types/config/validation.d.ts +3 -0
  96. package/types/helpers/Extensions/withBlockExtensions.d.ts +1 -1
  97. package/types/helpers/FormValidation/FormValidation.d.ts +1 -0
  98. package/types/helpers/FormValidation/validators.d.ts +29 -0
  99. package/types/helpers/MessageLabels/MessageLabels.d.ts +36 -0
  100. package/types/helpers/User/User.d.ts +1 -1
  101. package/src/components/theme/SocialSharing/SocialSharing.jsx +0 -48
  102. package/src/components/theme/SocialSharing/SocialSharing.test.jsx +0 -14
@@ -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
  });