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

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 (187) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/finalreleasechangelog.py +48 -0
  3. package/locales/ca/LC_MESSAGES/volto.po +39 -13
  4. package/locales/ca.json +1 -1
  5. package/locales/de/LC_MESSAGES/volto.po +40 -14
  6. package/locales/de.json +1 -1
  7. package/locales/en/LC_MESSAGES/volto.po +39 -13
  8. package/locales/en.json +1 -1
  9. package/locales/es/LC_MESSAGES/volto.po +40 -14
  10. package/locales/es.json +1 -1
  11. package/locales/eu/LC_MESSAGES/volto.po +40 -14
  12. package/locales/eu.json +1 -1
  13. package/locales/fi/LC_MESSAGES/volto.po +40 -14
  14. package/locales/fi.json +1 -1
  15. package/locales/fr/LC_MESSAGES/volto.po +40 -14
  16. package/locales/fr.json +1 -1
  17. package/locales/hi/LC_MESSAGES/volto.po +40 -14
  18. package/locales/hi.json +1 -1
  19. package/locales/it/LC_MESSAGES/volto.po +40 -14
  20. package/locales/it.json +1 -1
  21. package/locales/ja/LC_MESSAGES/volto.po +39 -13
  22. package/locales/ja.json +1 -1
  23. package/locales/nl/LC_MESSAGES/volto.po +39 -13
  24. package/locales/nl.json +1 -1
  25. package/locales/pt/LC_MESSAGES/volto.po +39 -13
  26. package/locales/pt.json +1 -1
  27. package/locales/pt_BR/LC_MESSAGES/volto.po +40 -14
  28. package/locales/pt_BR.json +1 -1
  29. package/locales/ro/LC_MESSAGES/volto.po +39 -13
  30. package/locales/ro.json +1 -1
  31. package/locales/volto.pot +40 -14
  32. package/locales/zh_CN/LC_MESSAGES/volto.po +40 -14
  33. package/locales/zh_CN.json +1 -1
  34. package/package.json +5 -6
  35. package/razzle.config.js +3 -3
  36. package/src/components/index.js +0 -1
  37. package/src/components/manage/Actions/Actions.stories.jsx +138 -0
  38. package/src/components/manage/Add/Add.jsx +7 -4
  39. package/src/components/manage/BlockChooser/BlockChooser.jsx +9 -1
  40. package/src/components/manage/Blocks/Block/BlocksForm.jsx +5 -0
  41. package/src/components/manage/Blocks/Block/Edit.jsx +24 -8
  42. package/src/components/manage/Blocks/Block/EditBlockWrapper.jsx +17 -1
  43. package/src/components/manage/Blocks/Block/Order/Item.jsx +8 -2
  44. package/src/components/manage/Blocks/Block/Order/Order.jsx +2 -0
  45. package/src/components/manage/Blocks/Container/Data.jsx +10 -2
  46. package/src/components/manage/Blocks/Grid/View.jsx +3 -0
  47. package/src/components/manage/Blocks/Image/ImageSidebar.jsx +10 -2
  48. package/src/components/manage/Blocks/LeadImage/Edit.jsx +74 -126
  49. package/src/components/manage/Blocks/Listing/ListingData.jsx +10 -2
  50. package/src/components/manage/Blocks/Maps/MapsSidebar.jsx +3 -1
  51. package/src/components/manage/Blocks/Search/SearchBlockEdit.jsx +2 -0
  52. package/src/components/manage/Blocks/Search/SearchBlockView.jsx +18 -2
  53. package/src/components/manage/Blocks/Search/components/SortOn.jsx +82 -55
  54. package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +1 -1
  55. package/src/components/manage/Blocks/Search/widgets/SelectMetadataField.jsx +107 -176
  56. package/src/components/manage/Blocks/Teaser/Data.jsx +10 -2
  57. package/src/components/manage/Blocks/Teaser/DefaultBody.jsx +15 -8
  58. package/src/components/manage/Blocks/ToC/Edit.jsx +36 -28
  59. package/src/components/manage/Blocks/Video/Edit.jsx +105 -172
  60. package/src/components/manage/Blocks/Video/Edit.stories.jsx +57 -0
  61. package/src/components/manage/Blocks/Video/VideoSidebar.jsx +3 -1
  62. package/src/components/manage/Contents/Contents.jsx +4 -1
  63. package/src/components/manage/Contents/ContentsBreadcrumbs.stories.jsx +46 -0
  64. package/src/components/manage/Contents/ContentsPropertiesModal.jsx +85 -52
  65. package/src/components/manage/Contents/ContentsUploadModal.jsx +230 -323
  66. package/src/components/manage/Contents/ContentsUploadModal.stories.jsx +56 -0
  67. package/src/components/manage/Controlpanels/AddonsControlpanel.jsx +323 -441
  68. package/src/components/manage/Controlpanels/Aliases.jsx +452 -580
  69. package/src/components/manage/Controlpanels/Aliases.stories.jsx +74 -0
  70. package/src/components/manage/Controlpanels/ContentTypeSchema.jsx +1 -0
  71. package/src/components/manage/Controlpanels/Controlpanel.jsx +41 -2
  72. package/src/components/manage/Controlpanels/Controlpanel.test.jsx +55 -24
  73. package/src/components/manage/Controlpanels/DatabaseInformation.jsx +162 -229
  74. package/src/components/manage/Controlpanels/Groups/RenderGroups.jsx +74 -122
  75. package/src/components/manage/Controlpanels/UndoControlpanel.jsx +3 -3
  76. package/src/components/manage/Controlpanels/Users/UserGroupMembershipListing.jsx +28 -12
  77. package/src/components/manage/Controlpanels/Users/UserGroupMembershipMatrix.jsx +12 -4
  78. package/src/components/manage/Display/Display.jsx +92 -148
  79. package/src/components/manage/Display/Display.stories.jsx +46 -0
  80. package/src/components/manage/Edit/Edit.jsx +2 -4
  81. package/src/components/manage/Form/Form.jsx +85 -20
  82. package/src/components/manage/Form/InlineForm.jsx +2 -4
  83. package/src/components/manage/Form/ModalForm.jsx +1 -1
  84. package/src/components/manage/History/History.jsx +1 -1
  85. package/src/components/manage/Pluggable/Pluggable.test.js +1 -1
  86. package/src/components/manage/Preferences/ChangePassword.jsx +94 -172
  87. package/src/components/manage/Preferences/ChangePassword.stories.jsx +41 -0
  88. package/src/components/manage/Preferences/PersonalInformation.jsx +50 -115
  89. package/src/components/manage/Preferences/PersonalPreferences.jsx +46 -100
  90. package/src/components/manage/Preferences/PersonalPreferences.stories.jsx +48 -0
  91. package/src/components/manage/Toolbar/More.jsx +308 -399
  92. package/src/components/manage/Toolbar/Toolbar.jsx +1 -1
  93. package/src/components/manage/Widgets/ArrayWidget.jsx +2 -2
  94. package/src/components/manage/Widgets/DatetimeWidget.jsx +121 -175
  95. package/src/components/manage/Widgets/ImageWidget.jsx +6 -5
  96. package/src/components/manage/Widgets/RecurrenceWidget/EndField.jsx +7 -1
  97. package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.jsx +80 -31
  98. package/src/components/manage/Widgets/ReferenceWidget.jsx +134 -210
  99. package/src/components/theme/Register/Register.jsx +70 -142
  100. package/src/components/theme/Register/Register.stories.jsx +49 -0
  101. package/src/components/theme/Search/Search.jsx +13 -5
  102. package/src/components/theme/Tags/Tags.jsx +19 -10
  103. package/src/components/theme/Tags/Tags.test.jsx +9 -11
  104. package/src/components/theme/View/AlbumView.jsx +122 -167
  105. package/src/components/theme/View/LinkView.jsx +4 -0
  106. package/src/components/theme/View/LinkView.test.jsx +2 -0
  107. package/src/components/theme/View/View.jsx +0 -13
  108. package/src/components/theme/View/View.test.jsx +0 -3
  109. package/src/config/ControlPanels.js +49 -43
  110. package/src/config/Widgets.jsx +1 -1
  111. package/src/config/config.test.js +1 -0
  112. package/src/config/index.js +23 -2
  113. package/src/config/slots.js +12 -0
  114. package/src/config/validation.ts +155 -0
  115. package/src/helpers/Blocks/Blocks.js +12 -7
  116. package/src/helpers/Blocks/Blocks.test.js +15 -0
  117. package/src/helpers/Blocks/cloneBlocks.ts +1 -1
  118. package/src/helpers/Extensions/withBlockExtensions.jsx +1 -1
  119. package/src/helpers/FormValidation/FormValidation.jsx +128 -172
  120. package/src/helpers/FormValidation/FormValidation.test.js +836 -8
  121. package/src/helpers/FormValidation/validators.ts +203 -0
  122. package/src/helpers/MessageLabels/MessageLabels.js +28 -0
  123. package/src/helpers/Url/Url.test.js +19 -6
  124. package/src/helpers/Url/urlRegex.js +1 -1
  125. package/src/helpers/User/User.js +1 -1
  126. package/src/helpers/index.js +2 -0
  127. package/src/hooks/client/useClient.js +1 -1
  128. package/src/middleware/api.js +4 -2
  129. package/src/middleware/index.js +1 -0
  130. package/src/middleware/userSessionReset.js +46 -0
  131. package/src/store.js +2 -0
  132. package/test-setup-config.jsx +10 -0
  133. package/theme/themes/default/modules/embed.variables +1 -1
  134. package/theme/themes/pastanaga/collections/form.overrides +34 -0
  135. package/theme/themes/pastanaga/extras/blocks.less +6 -0
  136. package/theme/themes/pastanaga/extras/sidebar.less +4 -0
  137. package/theme/themes/pastanaga/extras/toolbar.less +10 -3
  138. package/tsconfig.declarations.json +3 -2
  139. package/types/components/index.d.ts +0 -1
  140. package/types/components/manage/Actions/Actions.stories.d.ts +8 -0
  141. package/types/components/manage/Blocks/Block/Order/Order.d.ts +2 -1
  142. package/types/components/manage/Blocks/LeadImage/Edit.d.ts +14 -5
  143. package/types/components/manage/Blocks/Search/widgets/SelectMetadataField.d.ts +0 -5
  144. package/types/components/manage/Blocks/ToC/Edit.d.ts +1 -6
  145. package/types/components/manage/Blocks/Video/Edit.d.ts +1 -1
  146. package/types/components/manage/Blocks/Video/Edit.stories.d.ts +8 -0
  147. package/types/components/manage/Contents/ContentsBreadcrumbs.stories.d.ts +8 -0
  148. package/types/components/manage/Contents/ContentsUploadModal.d.ts +14 -2
  149. package/types/components/manage/Contents/ContentsUploadModal.stories.d.ts +8 -0
  150. package/types/components/manage/Contents/index.d.ts +1 -1
  151. package/types/components/manage/Controlpanels/AddonsControlpanel.d.ts +2 -2
  152. package/types/components/manage/Controlpanels/Aliases.d.ts +2 -2
  153. package/types/components/manage/Controlpanels/Aliases.stories.d.ts +8 -0
  154. package/types/components/manage/Controlpanels/DatabaseInformation.d.ts +2 -2
  155. package/types/components/manage/Controlpanels/Groups/RenderGroups.d.ts +10 -5
  156. package/types/components/manage/Controlpanels/index.d.ts +4 -4
  157. package/types/components/manage/Display/Display.stories.d.ts +8 -0
  158. package/types/components/manage/Preferences/ChangePassword.d.ts +2 -2
  159. package/types/components/manage/Preferences/ChangePassword.stories.d.ts +8 -0
  160. package/types/components/manage/Preferences/PersonalInformation.d.ts +7 -2
  161. package/types/components/manage/Preferences/PersonalPreferences.d.ts +5 -1
  162. package/types/components/manage/Preferences/PersonalPreferences.stories.d.ts +8 -0
  163. package/types/components/manage/Toolbar/More.d.ts +8 -5
  164. package/types/components/manage/Widgets/DatetimeWidget.d.ts +0 -85
  165. package/types/components/manage/Widgets/DatetimeWidget.stories.d.ts +0 -1
  166. package/types/components/manage/Widgets/ReferenceWidget.d.ts +27 -2
  167. package/types/components/manage/Widgets/index.d.ts +1 -1
  168. package/types/components/theme/Register/Register.d.ts +2 -2
  169. package/types/components/theme/Register/Register.stories.d.ts +9 -0
  170. package/types/components/theme/Tags/Tags.d.ts +15 -7
  171. package/types/components/theme/View/AlbumView.d.ts +3 -17
  172. package/types/config/ControlPanels.d.ts +8 -0
  173. package/types/config/RichTextEditor/ToHTML.d.ts +1 -1
  174. package/types/config/Widgets.d.ts +3 -3
  175. package/types/config/slots.d.ts +21 -0
  176. package/types/config/validation.d.ts +3 -0
  177. package/types/helpers/Blocks/Blocks.d.ts +6 -0
  178. package/types/helpers/Extensions/withBlockExtensions.d.ts +1 -1
  179. package/types/helpers/FormValidation/FormValidation.d.ts +2 -0
  180. package/types/helpers/FormValidation/validators.d.ts +29 -0
  181. package/types/helpers/MessageLabels/MessageLabels.d.ts +36 -0
  182. package/types/helpers/User/User.d.ts +1 -1
  183. package/types/helpers/index.d.ts +2 -2
  184. package/types/middleware/index.d.ts +1 -0
  185. package/types/middleware/userSessionReset.d.ts +5 -0
  186. package/src/components/theme/SocialSharing/SocialSharing.jsx +0 -48
  187. package/src/components/theme/SocialSharing/SocialSharing.test.jsx +0 -14
@@ -1,134 +1,86 @@
1
- /**
2
- * Users controlpanel groups.
3
- * @module components/manage/Controlpanels/UsersControlpanelGroups
4
- */
5
1
  import PropTypes from 'prop-types';
6
- import React, { Component } from 'react';
7
- import { FormattedMessage, injectIntl } from 'react-intl';
2
+ import { FormattedMessage } from 'react-intl';
8
3
  import { Dropdown, Table, Checkbox } from 'semantic-ui-react';
9
4
  import trashSVG from '@plone/volto/icons/delete.svg';
10
5
  import ploneSVG from '@plone/volto/icons/plone.svg';
11
6
  import { Icon } from '@plone/volto/components';
12
7
  import { canAssignRole } from '@plone/volto/helpers';
13
8
 
14
- /**
15
- * UsersControlpanelGroups class.
16
- * @class UsersControlpanelGroups
17
- * @extends Component
18
- */
19
- class RenderGroups extends Component {
20
- /**
21
- * Property types.
22
- * @property {Object} propTypes Property types.
23
- * @static
24
- */
25
- static propTypes = {
26
- //single group
27
- group: PropTypes.shape({
28
- title: PropTypes.string,
29
- description: PropTypes.string,
30
- email: PropTypes.string,
31
- groupname: PropTypes.string,
32
- roles: PropTypes.arrayOf(PropTypes.string),
33
- }).isRequired,
34
-
35
- roles: PropTypes.arrayOf(
36
- PropTypes.shape({
37
- id: PropTypes.string,
38
- }),
39
- ).isRequired,
40
- inheritedRole: PropTypes.array,
41
- onDelete: PropTypes.func.isRequired,
42
- isUserManager: PropTypes.bool.isRequired,
9
+ const RenderGroups = (props) => {
10
+ const onChange = (event, { value }) => {
11
+ const [group, role] = value.split('.');
12
+ props.updateGroups(group, role);
43
13
  };
44
-
45
- /**
46
- * Constructor
47
- * @method constructor
48
- * @param {Object} props Component properties
49
- * @constructs Sharing
50
- */
51
- constructor(props) {
52
- super(props);
53
- this.onChange = this.onChange.bind(this);
54
- }
55
-
56
- /**
57
- * @param {*} event
58
- * @param {*} { value }
59
- * @memberof UsersControlpanelUser
60
- */
61
- onChange(event, { value }) {
62
- const [group, role] = value.split('&role=');
63
- this.props.updateGroups(group, role);
64
- }
65
-
66
- /**
67
- *@param {*}
68
- *@returns {Boolean}
69
- *@memberof RenderGroups
70
- */
71
- isAuthGroup = (roleId) => {
72
- return this.props.inheritedRole.includes(roleId);
14
+ const isAuthGroup = (roleId) => {
15
+ return props.inheritedRole.includes(roleId);
73
16
  };
74
-
75
- canDeleteGroup() {
76
- if (this.props.isUserManager) return true;
77
- return !this.props.group.roles.includes('Manager');
78
- }
79
-
80
- /**
81
- * Render method.
82
- * @method render
83
- * @returns {string} Markup for the component.
84
- */
85
- render() {
86
- return (
87
- <Table.Row key={this.props.group.title}>
88
- <Table.Cell>{this.props.group.groupname}</Table.Cell>
89
- {this.props.roles.map((role) => (
90
- <Table.Cell key={role.id}>
91
- {this.props.inheritedRole &&
92
- this.props.inheritedRole.includes(role.id) &&
93
- this.props.group.roles.includes('Authenticated') ? (
94
- <Icon
95
- name={ploneSVG}
96
- size="20px"
97
- color="#007EB1"
98
- title={'plone-svg'}
99
- />
100
- ) : (
101
- <Checkbox
102
- checked={
103
- this.props.group.id === 'AuthenticatedUsers'
104
- ? this.isAuthGroup(role.id)
105
- : this.props.group.roles.includes(role.id)
106
- }
107
- onChange={this.onChange}
108
- value={`${this.props.group.id}&role=${role.id}`}
109
- disabled={!canAssignRole(this.props.isUserManager, role)}
110
- />
111
- )}
112
- </Table.Cell>
113
- ))}
114
- <Table.Cell textAlign="right">
115
- {this.canDeleteGroup() && (
116
- <Dropdown icon="ellipsis horizontal">
117
- <Dropdown.Menu className="left">
118
- <Dropdown.Item
119
- onClick={this.props.onDelete}
120
- value={this.props.group['@id']}
121
- >
122
- <Icon name={trashSVG} size="15px" />
123
- <FormattedMessage id="Delete" defaultMessage="Delete" />
124
- </Dropdown.Item>
125
- </Dropdown.Menu>
126
- </Dropdown>
17
+ const canDeleteGroup = () => {
18
+ if (props.isUserManager) return true;
19
+ return !props.group.roles.includes('Manager');
20
+ };
21
+ return (
22
+ <Table.Row key={props.group.title}>
23
+ <Table.Cell>{props.group.groupname}</Table.Cell>
24
+ {props.roles.map((role) => (
25
+ <Table.Cell key={role.id}>
26
+ {props.inheritedRole &&
27
+ props.inheritedRole.includes(role.id) &&
28
+ props.group.roles.includes('Authenticated') ? (
29
+ <Icon
30
+ name={ploneSVG}
31
+ size="20px"
32
+ color="#007EB1"
33
+ title={'plone-svg'}
34
+ />
35
+ ) : (
36
+ <Checkbox
37
+ checked={
38
+ props.group.id === 'AuthenticatedUsers'
39
+ ? isAuthGroup(role.id)
40
+ : props.group.roles.includes(role.id)
41
+ }
42
+ onChange={onChange}
43
+ value={`${props.group.id}.${role.id}`}
44
+ disabled={!canAssignRole(props.isUserManager, role)}
45
+ />
127
46
  )}
128
47
  </Table.Cell>
129
- </Table.Row>
130
- );
131
- }
132
- }
48
+ ))}
49
+ <Table.Cell textAlign="right">
50
+ {canDeleteGroup() && (
51
+ <Dropdown icon="ellipsis horizontal">
52
+ <Dropdown.Menu className="left">
53
+ <Dropdown.Item
54
+ onClick={props.onDelete}
55
+ value={props.group['@id']}
56
+ >
57
+ <Icon name={trashSVG} size="15px" />
58
+ <FormattedMessage id="Delete" defaultMessage="Delete" />
59
+ </Dropdown.Item>
60
+ </Dropdown.Menu>
61
+ </Dropdown>
62
+ )}
63
+ </Table.Cell>
64
+ </Table.Row>
65
+ );
66
+ };
67
+
68
+ RenderGroups.propTypes = {
69
+ //single group
70
+ group: PropTypes.shape({
71
+ title: PropTypes.string,
72
+ description: PropTypes.string,
73
+ email: PropTypes.string,
74
+ groupname: PropTypes.string,
75
+ roles: PropTypes.arrayOf(PropTypes.string),
76
+ }).isRequired,
133
77
 
134
- export default injectIntl(RenderGroups);
78
+ roles: PropTypes.arrayOf(
79
+ PropTypes.shape({
80
+ id: PropTypes.string,
81
+ }),
82
+ ).isRequired,
83
+ inheritedRole: PropTypes.array,
84
+ onDelete: PropTypes.func.isRequired,
85
+ };
86
+ export default RenderGroups;
@@ -389,7 +389,7 @@ class UndoControlpanel extends Component {
389
389
  }
390
390
 
391
391
  /**
392
- * Handle next and prev buttons visiblity when transactions are sorted
392
+ * Handle next and prev buttons visibility when transactions are sorted
393
393
  * @method handleSortedNextPrevButtons
394
394
  * @returns {undefined}
395
395
  */
@@ -412,7 +412,7 @@ class UndoControlpanel extends Component {
412
412
  }
413
413
 
414
414
  /**
415
- * Handle next and prev buttons visiblity when transactions are not sorted
415
+ * Handle next and prev buttons visibility when transactions are not sorted
416
416
  * @method handleNotSortedNextPrevButtons
417
417
  * @returns {undefined}
418
418
  */
@@ -435,7 +435,7 @@ class UndoControlpanel extends Component {
435
435
  }
436
436
 
437
437
  /**
438
- * Handle next, prev buttons and table visiblity
438
+ * Handle next, prev buttons and table visibility
439
439
  * @method handleTableVisiblity
440
440
  * @returns {undefined}
441
441
  */
@@ -1,5 +1,5 @@
1
- import React, { useEffect, useState } from 'react';
2
- import { cloneDeep, uniqBy } from 'lodash';
1
+ import React, { useEffect, useState, useMemo } from 'react';
2
+ import { cloneDeep, uniqBy, debounce } from 'lodash';
3
3
  import { useIntl } from 'react-intl';
4
4
  import { useSelector, useDispatch, shallowEqual } from 'react-redux';
5
5
  import jwtDecode from 'jwt-decode';
@@ -114,25 +114,41 @@ const ListingTemplate = ({
114
114
  matrix_options = [];
115
115
  }
116
116
 
117
+ const debouncedListUsers = useMemo(
118
+ () =>
119
+ debounce((query_user, groups_filter, userLimit) => {
120
+ dispatch(
121
+ listUsers({
122
+ search: query_user,
123
+ groups_filter: groups_filter.map((el) => el.value),
124
+ limit: userLimit,
125
+ }),
126
+ );
127
+ }, 300),
128
+ [dispatch],
129
+ );
130
+
117
131
  useEffect(() => {
118
132
  // Get users.
119
133
  if (show_users) {
120
- dispatch(
121
- listUsers({
122
- search: query_user,
123
- groups_filter: groups_filter.map((el) => el.value),
124
- limit: userLimit,
125
- }),
126
- );
134
+ debouncedListUsers(query_user, groups_filter, userLimit);
127
135
  }
128
- }, [dispatch, query_user, groups_filter, show_users, userLimit]);
136
+ }, [debouncedListUsers, query_user, groups_filter, show_users, userLimit]);
137
+
138
+ const debouncedListGroups = useMemo(
139
+ () =>
140
+ debounce((query_group) => {
141
+ dispatch(listGroups(query_group));
142
+ }, 300),
143
+ [dispatch],
144
+ );
129
145
 
130
146
  useEffect(() => {
131
147
  // Get matrix groups.
132
148
  if (show_matrix_options) {
133
- dispatch(listGroups(query_group));
149
+ debouncedListGroups(query_group);
134
150
  }
135
- }, [dispatch, query_group, show_matrix_options, groups_filter]);
151
+ }, [debouncedListGroups, query_group, show_matrix_options]);
136
152
 
137
153
  const onSelectOptionHandler = (selectedvalue, checked, singleClick) => {
138
154
  singleClick = singleClick ?? false;
@@ -1,9 +1,9 @@
1
- import React, { useEffect, useState } from 'react';
1
+ import React, { useEffect, useState, useMemo } from 'react';
2
2
  import { useSelector, useDispatch } from 'react-redux';
3
3
  import { useIntl } from 'react-intl';
4
4
  import { Checkbox, Form, Input } from 'semantic-ui-react';
5
5
 
6
- import { isEqual } from 'lodash';
6
+ import { isEqual, debounce } from 'lodash';
7
7
 
8
8
  import { messages } from '@plone/volto/helpers';
9
9
  import { listGroups } from '@plone/volto/actions'; // getRegistry
@@ -38,12 +38,20 @@ const UserGroupMembershipMatrix = ({ many_users, many_groups }) => {
38
38
  });
39
39
  }
40
40
 
41
+ const debouncedListGroups = useMemo(
42
+ () =>
43
+ debounce((query_group_filter) => {
44
+ dispatch(listGroups('', query_group_filter));
45
+ }, 300),
46
+ [dispatch],
47
+ );
48
+
41
49
  useEffect(() => {
42
50
  // TODO fetch group for at least query_group_filter.length > 1?
43
51
  if (!many_groups || (many_groups && query_group_filter.length > 1)) {
44
- dispatch(listGroups('', query_group_filter));
52
+ debouncedListGroups(query_group_filter);
45
53
  }
46
- }, [dispatch, many_groups, query_group_filter]);
54
+ }, [debouncedListGroups, many_groups, query_group_filter]);
47
55
 
48
56
  const onReset = (event) => {
49
57
  // event.preventDefault();
@@ -1,14 +1,14 @@
1
- import React, { Component, Fragment } from 'react';
1
+ import React, { useState, useEffect } from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { connect } from 'react-redux';
3
+ import { useDispatch, useSelector } from 'react-redux';
4
4
  import { compose } from 'redux';
5
5
  import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
6
6
 
7
7
  import { getSchema, updateContent, getContent } from '@plone/volto/actions';
8
8
  import { getLayoutFieldname } from '@plone/volto/helpers';
9
- import { Icon } from '@plone/volto/components';
10
- import { FormFieldWrapper } from '@plone/volto/components/manage/Widgets';
11
- import { defineMessages, injectIntl } from 'react-intl';
9
+ import { usePrevious } from '@plone/volto/helpers';
10
+ import { FormFieldWrapper, Icon } from '@plone/volto/components';
11
+ import { defineMessages, useIntl } from 'react-intl';
12
12
  import config from '@plone/volto/registry';
13
13
 
14
14
  import downSVG from '@plone/volto/icons/down-key.svg';
@@ -110,152 +110,96 @@ const customSelectStyles = {
110
110
  }),
111
111
  };
112
112
 
113
- /**
114
- * Display container class.
115
- * @class Display
116
- * @extends Component
117
- */
118
- class DisplaySelect extends Component {
119
- /**
120
- * Property types.
121
- * @property {Object} propTypes Property types.
122
- * @static
123
- */
124
- static propTypes = {
125
- getSchema: PropTypes.func.isRequired,
126
- updateContent: PropTypes.func.isRequired,
127
- getContent: PropTypes.func.isRequired,
128
- loaded: PropTypes.bool.isRequired,
129
- pathname: PropTypes.string.isRequired,
130
- layouts: PropTypes.arrayOf(PropTypes.string),
131
- layout: PropTypes.string,
132
- type: PropTypes.string.isRequired,
133
- };
134
-
135
- /**
136
- * Default properties
137
- * @property {Object} defaultProps Default properties.
138
- * @static
139
- */
140
- static defaultProps = {
141
- layouts: [],
142
- layout: '',
143
- };
144
-
145
- state = {
146
- selectedOption: {
147
- value: this.props.layout,
148
- label:
149
- this.props.intl.formatMessage({
150
- id: config.views.layoutViewsNamesMapping?.[this.props.layout],
151
- defaultMessage:
152
- config.views.layoutViewsNamesMapping?.[this.props.layout],
153
- }) || this.props.layout,
154
- },
155
- };
156
-
157
- componentDidMount() {
158
- this.props.getSchema(this.props.type);
159
- }
160
-
161
- /**
162
- * Component will receive props
163
- * @method componentWillReceiveProps
164
- * @param {Object} nextProps Next properties
165
- * @returns {undefined}
166
- */
167
- UNSAFE_componentWillReceiveProps(nextProps) {
168
- if (nextProps.pathname !== this.props.pathname) {
169
- this.props.getSchema(nextProps.type);
113
+ const DisplaySelect = (props) => {
114
+ const { pathname } = props;
115
+ const intl = useIntl();
116
+ const dispatch = useDispatch();
117
+ const loaded = useSelector((state) => state.content.update.loaded);
118
+ const layouts = useSelector((state) =>
119
+ state.schema.schema ? state.schema.schema.layouts : [],
120
+ );
121
+ const layout = useSelector((state) =>
122
+ state.content.data
123
+ ? state.content.data[getLayoutFieldname(state.content.data)]
124
+ : '',
125
+ );
126
+ const [selectedOption, setselectedOption] = useState({
127
+ value: layout,
128
+ label:
129
+ intl.formatMessage({
130
+ id: config.views.layoutViewsNamesMapping?.[layout],
131
+ defaultMessage: config.views.layoutViewsNamesMapping?.[layout],
132
+ }) || layout,
133
+ });
134
+
135
+ const type = useSelector((state) =>
136
+ state.content.data ? state.content.data['@type'] : '',
137
+ );
138
+ const prevloaded = usePrevious(loaded);
139
+ const prevpathname = usePrevious(pathname);
140
+
141
+ useEffect(() => {
142
+ dispatch(getSchema(type));
143
+ }, [dispatch, type]);
144
+ useEffect(() => {
145
+ if (pathname !== prevpathname) {
146
+ dispatch(getSchema(type));
170
147
  }
171
- if (!this.props.loaded && nextProps.loaded) {
172
- this.props.getContent(nextProps.pathname);
148
+ if (!prevloaded && loaded) {
149
+ dispatch(getContent(pathname));
173
150
  }
174
- }
175
-
176
- /**
177
- * On set layout handler
178
- * @method setLayout
179
- * @param {Object} event Event object
180
- * @returns {undefined}
181
- */
182
- setLayout = (selectedOption) => {
183
- this.props.updateContent(this.props.pathname, {
184
- layout: selectedOption.value,
185
- });
186
- this.setState({ selectedOption });
151
+ }, [dispatch, type, pathname, prevpathname, prevloaded, loaded]);
152
+
153
+ const setLayout = (selectedOption) => {
154
+ dispatch(
155
+ updateContent(pathname, {
156
+ layout: selectedOption.value,
157
+ }),
158
+ );
159
+ setselectedOption(selectedOption);
187
160
  };
188
161
 
189
- selectValue = (option) => (
190
- <Fragment>
191
- <span className="Select-value-label">{option.label}</span>
192
- </Fragment>
193
- );
194
-
195
- optionRenderer = (option) => (
196
- <Fragment>
197
- <span style={{ marginRight: 'auto' }}>{option.label}</span>
198
- <Icon name={checkSVG} size="24px" />
199
- </Fragment>
200
- );
201
-
202
- render() {
203
- const { selectedOption } = this.state;
204
- const Select = this.props.reactSelect.default;
205
- const layoutsNames = config.views.layoutViewsNamesMapping;
206
- const layoutOptions = this.props.layouts
207
- .filter(
208
- (layout) =>
209
- Object.keys(config.views.contentTypesViews).includes(layout) ||
210
- Object.keys(config.views.layoutViews).includes(layout),
211
- )
212
- .map((item) => ({
213
- value: item,
214
- label:
215
- this.props.intl.formatMessage({
216
- id: layoutsNames[item],
217
- defaultMessage: layoutsNames[item],
218
- }) || item,
219
- }));
162
+ const Select = props.reactSelect.default;
163
+ const layoutsNames = config.views.layoutViewsNamesMapping;
164
+ const layoutOptions = layouts
165
+ .filter(
166
+ (layout) =>
167
+ Object.keys(config.views.contentTypesViews).includes(layout) ||
168
+ Object.keys(config.views.layoutViews).includes(layout),
169
+ )
170
+ .map((item) => ({
171
+ value: item,
172
+ label:
173
+ intl.formatMessage({
174
+ id: layoutsNames[item],
175
+ defaultMessage: layoutsNames[item],
176
+ }) || item,
177
+ }));
178
+
179
+ return layoutOptions?.length > 1 ? (
180
+ <FormFieldWrapper
181
+ id="display-select"
182
+ title={intl.formatMessage(messages.Viewmode)}
183
+ {...props}
184
+ >
185
+ <Select
186
+ name="display-select"
187
+ className="react-select-container"
188
+ classNamePrefix="react-select"
189
+ options={layoutOptions}
190
+ styles={customSelectStyles}
191
+ theme={selectTheme}
192
+ components={{ DropdownIndicator, Option }}
193
+ onChange={setLayout}
194
+ defaultValue={selectedOption}
195
+ isSearchable={false}
196
+ />
197
+ </FormFieldWrapper>
198
+ ) : null;
199
+ };
220
200
 
221
- return layoutOptions?.length > 1 ? (
222
- <FormFieldWrapper
223
- id="display-select"
224
- title={this.props.intl.formatMessage(messages.Viewmode)}
225
- {...this.props}
226
- >
227
- <Select
228
- name="display-select"
229
- className="react-select-container"
230
- classNamePrefix="react-select"
231
- options={layoutOptions}
232
- styles={customSelectStyles}
233
- theme={selectTheme}
234
- components={{ DropdownIndicator, Option }}
235
- onChange={this.setLayout}
236
- defaultValue={selectedOption}
237
- isSearchable={false}
238
- />
239
- </FormFieldWrapper>
240
- ) : null;
241
- }
242
- }
201
+ DisplaySelect.propTypes = {
202
+ pathname: PropTypes.string.isRequired,
203
+ };
243
204
 
244
- export default compose(
245
- injectIntl,
246
- injectLazyLibs('reactSelect'),
247
- connect(
248
- (state) => ({
249
- loaded: state.content.update.loaded,
250
- layouts: state.schema.schema ? state.schema.schema.layouts : [],
251
- layout: state.content.data
252
- ? state.content.data[getLayoutFieldname(state.content.data)]
253
- : '',
254
- layout_fieldname: state.content.data
255
- ? getLayoutFieldname(state.content.data)
256
- : '',
257
- type: state.content.data ? state.content.data['@type'] : '',
258
- }),
259
- { getSchema, updateContent, getContent },
260
- ),
261
- )(DisplaySelect);
205
+ export default compose(injectLazyLibs('reactSelect'))(DisplaySelect);
@@ -0,0 +1,46 @@
1
+ import { injectIntl } from 'react-intl';
2
+ import React from 'react';
3
+ import DisplayComponent from './Display';
4
+ import { RealStoreWrapper as Wrapper } from '@plone/volto/storybook';
5
+
6
+ const IntlDisplayComponent = injectIntl(DisplayComponent);
7
+
8
+ function StoryComponent(args) {
9
+ return (
10
+ <Wrapper
11
+ customStore={{
12
+ content: {
13
+ update: { loaded: true },
14
+ data: { layout: 'summary_view', '@type': 'Folder' },
15
+ },
16
+ schema: {
17
+ schema: {
18
+ layouts: ['summary_view', 'listing_view'],
19
+ },
20
+ },
21
+ intl: {
22
+ locale: 'en',
23
+ messages: {},
24
+ },
25
+ }}
26
+ >
27
+ <div id="toolbar" style={{ display: 'none' }} />
28
+ <IntlDisplayComponent {...args} pathname="/test" />
29
+ </Wrapper>
30
+ );
31
+ }
32
+
33
+ export const Display = StoryComponent.bind({});
34
+
35
+ export default {
36
+ title: 'Public components/Display',
37
+ component: Display,
38
+ decorators: [
39
+ (Story) => (
40
+ <div className="ui segment form attached" style={{ width: '400px' }}>
41
+ <Story />
42
+ </div>
43
+ ),
44
+ ],
45
+ argTypes: {},
46
+ };
@@ -5,7 +5,7 @@
5
5
 
6
6
  import React, { Component } from 'react';
7
7
  import PropTypes from 'prop-types';
8
- import { Helmet } from '@plone/volto/helpers';
8
+ import { Helmet, extractInvariantErrors } from '@plone/volto/helpers';
9
9
  import { connect } from 'react-redux';
10
10
  import { compose } from 'redux';
11
11
  import { asyncConnect, hasApiExpander } from '@plone/volto/helpers';
@@ -208,9 +208,7 @@ class Edit extends Component {
208
208
  const errorsList = tryParseJSON(error);
209
209
  let erroMessage;
210
210
  if (Array.isArray(errorsList)) {
211
- const invariantErrors = errorsList
212
- .filter((errorItem) => !('field' in errorItem))
213
- .map((errorItem) => errorItem['message']);
211
+ const invariantErrors = extractInvariantErrors(errorsList);
214
212
  if (invariantErrors.length > 0) {
215
213
  // Plone invariant validation message.
216
214
  erroMessage = invariantErrors.join(' - ');