@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,14 +1,8 @@
1
- /**
2
- * Edit video block.
3
- * @module components/manage/Blocks/Title/Edit
4
- */
5
-
6
- import React, { Component } from 'react';
1
+ import React, { useState, useCallback, useMemo } from 'react';
7
2
  import PropTypes from 'prop-types';
8
- import { defineMessages, injectIntl } from 'react-intl';
3
+ import { defineMessages, useIntl } from 'react-intl';
9
4
  import { Button, Input, Message } from 'semantic-ui-react';
10
5
  import cx from 'classnames';
11
- import { isEqual } from 'lodash';
12
6
 
13
7
  import { Icon, SidebarPortal, VideoSidebar } from '@plone/volto/components';
14
8
  import clearSVG from '@plone/volto/icons/clear.svg';
@@ -16,7 +10,6 @@ import aheadSVG from '@plone/volto/icons/ahead.svg';
16
10
  import videoBlockSVG from '@plone/volto/components/manage/Blocks/Video/block-video.svg';
17
11
  import Body from '@plone/volto/components/manage/Blocks/Video/Body';
18
12
  import { withBlockExtensions } from '@plone/volto/helpers';
19
- import { compose } from 'redux';
20
13
 
21
14
  const messages = defineMessages({
22
15
  VideoFormDescription: {
@@ -29,183 +22,123 @@ const messages = defineMessages({
29
22
  },
30
23
  });
31
24
 
32
- /**
33
- * Edit video block class.
34
- * @class Edit
35
- * @extends Component
36
- */
37
- class Edit extends Component {
38
- /**
39
- * Property types.
40
- * @property {Object} propTypes Property types.
41
- * @static
42
- */
43
- static propTypes = {
44
- selected: PropTypes.bool.isRequired,
45
- block: PropTypes.string.isRequired,
46
- index: PropTypes.number.isRequired,
47
- data: PropTypes.objectOf(PropTypes.any).isRequired,
48
- onChangeBlock: PropTypes.func.isRequired,
49
- onSelectBlock: PropTypes.func.isRequired,
50
- onDeleteBlock: PropTypes.func.isRequired,
51
- onFocusPreviousBlock: PropTypes.func.isRequired,
52
- onFocusNextBlock: PropTypes.func.isRequired,
53
- handleKeyDown: PropTypes.func.isRequired,
54
- };
55
-
56
- /**
57
- * Constructor
58
- * @method constructor
59
- * @param {Object} props Component properties
60
- * @constructs WysiwygEditor
61
- */
62
- constructor(props) {
63
- super(props);
64
-
65
- this.onChangeUrl = this.onChangeUrl.bind(this);
66
- this.onSubmitUrl = this.onSubmitUrl.bind(this);
67
- this.onKeyDownVariantMenuForm = this.onKeyDownVariantMenuForm.bind(this);
68
- this.state = {
69
- url: '',
70
- };
71
- }
72
-
73
- /**
74
- * Change url handler
75
- * @method onChangeUrl
76
- * @param {Object} target Target object
77
- * @returns {undefined}
78
- */
79
- onChangeUrl({ target }) {
80
- this.setState({
81
- url: target.value,
82
- });
83
- }
25
+ const Edit = (props) => {
26
+ const { data, block, onChangeBlock, selected } = props;
27
+ const intl = useIntl();
28
+ const [url, setUrl] = useState('');
84
29
 
85
- /**
86
- * @param {*} nextProps
87
- * @returns {boolean}
88
- * @memberof Edit
89
- */
90
- shouldComponentUpdate(nextProps) {
91
- return (
92
- this.props.selected ||
93
- nextProps.selected ||
94
- !isEqual(this.props.data, nextProps.data)
95
- );
96
- }
30
+ const onChangeUrl = ({ target }) => {
31
+ setUrl(target.value);
32
+ };
97
33
 
98
- /**
99
- * Submit url handler
100
- * @method onSubmitUrl
101
- * @returns {undefined}
102
- */
103
- onSubmitUrl() {
104
- this.props.onChangeBlock(this.props.block, {
105
- ...this.props.data,
106
- url: this.state.url,
34
+ const onSubmitUrl = useCallback(() => {
35
+ onChangeBlock(block, {
36
+ ...data,
37
+ url: url,
107
38
  });
108
- }
39
+ }, [onChangeBlock, block, data, url]);
109
40
 
110
- resetSubmitUrl = () => {
111
- this.setState({
112
- url: '',
113
- });
41
+ const resetSubmitUrl = () => {
42
+ setUrl('');
114
43
  };
115
44
 
116
- /**
117
- * Keydown handler on Variant Menu Form
118
- * This is required since the ENTER key is already mapped to a onKeyDown
119
- * event and needs to be overriden with a child onKeyDown.
120
- * @method onKeyDownVariantMenuForm
121
- * @param {Object} e Event object
122
- * @returns {undefined}
123
- */
124
- onKeyDownVariantMenuForm(e) {
125
- if (e.key === 'Enter') {
126
- e.preventDefault();
127
- e.stopPropagation();
128
- this.onSubmitUrl();
129
- } else if (e.key === 'Escape') {
130
- e.preventDefault();
131
- e.stopPropagation();
132
- // TODO: Do something on ESC key
133
- }
134
- }
45
+ const onKeyDownVariantMenuForm = useCallback(
46
+ (e) => {
47
+ if (e.key === 'Enter') {
48
+ e.preventDefault();
49
+ e.stopPropagation();
50
+ onSubmitUrl();
51
+ } else if (e.key === 'Escape') {
52
+ e.preventDefault();
53
+ e.stopPropagation();
54
+ // TODO: Do something on ESC key
55
+ }
56
+ },
57
+ [onSubmitUrl],
58
+ );
135
59
 
136
- /**
137
- * Render method.
138
- * @method render
139
- * @returns {string} Markup for the component.
140
- */
141
- render() {
142
- const { data } = this.props;
143
- const placeholder =
144
- this.props.data.placeholder ||
145
- this.props.intl.formatMessage(messages.VideoBlockInputPlaceholder);
146
- return (
147
- <div
148
- className={cx(
149
- 'block video align',
150
- {
151
- selected: this.props.selected,
152
- center: !Boolean(this.props.data.align),
153
- },
154
- this.props.data.align,
155
- )}
156
- >
157
- {data.url ? (
158
- <Body data={this.props.data} isEditMode={true} />
159
- ) : (
160
- <Message>
161
- <center>
162
- <img src={videoBlockSVG} alt="" />
163
- <div className="toolbar-inner">
164
- <Input
165
- onKeyDown={this.onKeyDownVariantMenuForm}
166
- onChange={this.onChangeUrl}
167
- placeholder={placeholder}
168
- value={this.state.url}
169
- // Prevents propagation to the Dropzone and the opening
170
- // of the upload browser dialog
171
- onClick={(e) => e.stopPropagation()}
172
- />
173
- {this.state.url && (
174
- <Button.Group>
175
- <Button
176
- basic
177
- className="cancel"
178
- onClick={(e) => {
179
- e.stopPropagation();
180
- this.setState({ url: '' });
181
- }}
182
- >
183
- <Icon name={clearSVG} size="30px" />
184
- </Button>
185
- </Button.Group>
186
- )}
60
+ const placeholder = useMemo(
61
+ () =>
62
+ data.placeholder ||
63
+ intl.formatMessage(messages.VideoBlockInputPlaceholder),
64
+ [intl, data],
65
+ );
66
+
67
+ return (
68
+ <div
69
+ className={cx(
70
+ 'block video align',
71
+ {
72
+ selected: selected,
73
+ center: !Boolean(data.align),
74
+ },
75
+ data.align,
76
+ )}
77
+ >
78
+ {data.url ? (
79
+ <Body data={data} isEditMode={true} />
80
+ ) : (
81
+ <Message>
82
+ <center>
83
+ <img src={videoBlockSVG} alt="" />
84
+ <div className="toolbar-inner">
85
+ <Input
86
+ onKeyDown={onKeyDownVariantMenuForm}
87
+ onChange={onChangeUrl}
88
+ placeholder={placeholder}
89
+ value={url}
90
+ // Prevents propagation to the Dropzone and the opening
91
+ // of the upload browser dialog
92
+ onClick={(e) => e.stopPropagation()}
93
+ />
94
+ {url && (
187
95
  <Button.Group>
188
96
  <Button
189
97
  basic
190
- primary
98
+ className="cancel"
191
99
  onClick={(e) => {
192
100
  e.stopPropagation();
193
- this.onSubmitUrl();
101
+ setUrl('');
194
102
  }}
195
103
  >
196
- <Icon name={aheadSVG} size="30px" />
104
+ <Icon name={clearSVG} size="30px" />
197
105
  </Button>
198
106
  </Button.Group>
199
- </div>
200
- </center>
201
- </Message>
202
- )}
203
- <SidebarPortal selected={this.props.selected}>
204
- <VideoSidebar {...this.props} resetSubmitUrl={this.resetSubmitUrl} />
205
- </SidebarPortal>
206
- </div>
207
- );
208
- }
209
- }
107
+ )}
108
+ <Button.Group>
109
+ <Button
110
+ basic
111
+ primary
112
+ onClick={(e) => {
113
+ e.stopPropagation();
114
+ onSubmitUrl();
115
+ }}
116
+ >
117
+ <Icon name={aheadSVG} size="30px" />
118
+ </Button>
119
+ </Button.Group>
120
+ </div>
121
+ </center>
122
+ </Message>
123
+ )}
124
+ <SidebarPortal selected={selected}>
125
+ <VideoSidebar {...props} resetSubmitUrl={resetSubmitUrl} />
126
+ </SidebarPortal>
127
+ </div>
128
+ );
129
+ };
130
+
131
+ Edit.propTypes = {
132
+ selected: PropTypes.bool.isRequired,
133
+ block: PropTypes.string.isRequired,
134
+ index: PropTypes.number.isRequired,
135
+ data: PropTypes.objectOf(PropTypes.any).isRequired,
136
+ onChangeBlock: PropTypes.func.isRequired,
137
+ onSelectBlock: PropTypes.func.isRequired,
138
+ onDeleteBlock: PropTypes.func.isRequired,
139
+ onFocusPreviousBlock: PropTypes.func.isRequired,
140
+ onFocusNextBlock: PropTypes.func.isRequired,
141
+ handleKeyDown: PropTypes.func.isRequired,
142
+ };
210
143
 
211
- export default compose(injectIntl, withBlockExtensions)(Edit);
144
+ export default withBlockExtensions(Edit);
@@ -0,0 +1,57 @@
1
+ import { injectIntl } from 'react-intl';
2
+ import React from 'react';
3
+ import VideoComponent from './Edit';
4
+ import { RealStoreWrapper as Wrapper } from '@plone/volto/storybook';
5
+
6
+ const IntlVideoComponent = injectIntl(VideoComponent);
7
+
8
+ function StoryComponent(args) {
9
+ return (
10
+ <Wrapper
11
+ customStore={{
12
+ intl: {
13
+ locale: 'en',
14
+ messages: {},
15
+ },
16
+ }}
17
+ >
18
+ <div id="toolbar" style={{ display: 'none' }} />
19
+ <IntlVideoComponent
20
+ data={{
21
+ ...args,
22
+ '@type': 'video',
23
+ }}
24
+ selected={false}
25
+ block="1234"
26
+ onChangeBlock={() => {}}
27
+ onSelectBlock={() => {}}
28
+ onDeleteBlock={() => {}}
29
+ onFocusPreviousBlock={() => {}}
30
+ onFocusNextBlock={() => {}}
31
+ handleKeyDown={() => {}}
32
+ index={1}
33
+ />
34
+ </Wrapper>
35
+ );
36
+ }
37
+
38
+ export const Video = StoryComponent.bind({});
39
+
40
+ Video.args = {
41
+ url: 'https://www.youtube.com/watch?v=ayjNbKju-8s',
42
+ };
43
+
44
+ export default {
45
+ title: 'Public components/Video/Video',
46
+ component: Video,
47
+ decorators: [
48
+ (Story) => (
49
+ <div className="ui segment form attached" style={{ width: '600px' }}>
50
+ <Story />
51
+ </div>
52
+ ),
53
+ ],
54
+ argTypes: {
55
+ // controlled value prop
56
+ },
57
+ };
@@ -18,7 +18,8 @@ const messages = defineMessages({
18
18
  });
19
19
 
20
20
  const VideoSidebar = (props) => {
21
- const { data, block, onChangeBlock, navRoot, contentType } = props;
21
+ const { data, block, blocksErrors, onChangeBlock, navRoot, contentType } =
22
+ props;
22
23
  const intl = useIntl();
23
24
  const schema = VideoBlockSchema({ ...props, intl });
24
25
 
@@ -44,6 +45,7 @@ const VideoSidebar = (props) => {
44
45
  block={block}
45
46
  navRoot={navRoot}
46
47
  contentType={contentType}
48
+ errors={blocksErrors}
47
49
  />
48
50
  )}
49
51
  </>
@@ -156,7 +156,7 @@ const messages = defineMessages({
156
156
  defaultMessage: 'Item(s) has been updated.',
157
157
  },
158
158
  messageReorder: {
159
- id: 'Item succesfully moved.',
159
+ id: 'Item successfully moved.',
160
160
  defaultMessage: 'Item successfully moved.',
161
161
  },
162
162
  messagePasted: {
@@ -1532,6 +1532,9 @@ class Contents extends Component {
1532
1532
  onCancel={this.onPropertiesCancel}
1533
1533
  onOk={this.onPropertiesOk}
1534
1534
  items={this.state.selected}
1535
+ values={map(this.state.selected, (id) =>
1536
+ find(this.state.items, { '@id': id }),
1537
+ ).filter((item) => item)}
1535
1538
  />
1536
1539
  {this.state.showWorkflow && (
1537
1540
  <ContentsWorkflowModal
@@ -0,0 +1,46 @@
1
+ import { injectIntl } from 'react-intl';
2
+ import React from 'react';
3
+ import ContentsBreadcrumbsComponent from './ContentsBreadcrumbs';
4
+ import { RealStoreWrapper as Wrapper } from '@plone/volto/storybook';
5
+
6
+ const IntlContentBreadcrumbsComponent = injectIntl(
7
+ ContentsBreadcrumbsComponent,
8
+ );
9
+ const breadcrumbs = [
10
+ { title: 'Blog', url: '/blog' },
11
+ { title: 'My first blog', url: '/blog/my-first-blog' },
12
+ ];
13
+ function StoryComponent(args) {
14
+ return (
15
+ <Wrapper
16
+ customStore={{
17
+ intl: {
18
+ locale: 'en',
19
+ messages: {},
20
+ },
21
+ }}
22
+ >
23
+ <div id="toolbar" style={{ display: 'none' }} />
24
+ <IntlContentBreadcrumbsComponent
25
+ {...args}
26
+ pathname="/blog"
27
+ items={breadcrumbs}
28
+ />
29
+ </Wrapper>
30
+ );
31
+ }
32
+
33
+ export const ContentBreadcrumbs = StoryComponent.bind({});
34
+
35
+ export default {
36
+ title: 'Public components/Contents/Content Breadcrumbs',
37
+ component: ContentBreadcrumbs,
38
+ decorators: [
39
+ (Story) => (
40
+ <div className="ui segment form attached" style={{ width: '400px' }}>
41
+ <Story />
42
+ </div>
43
+ ),
44
+ ],
45
+ argTypes: {},
46
+ };
@@ -1,12 +1,14 @@
1
- import React, { useEffect } from 'react';
1
+ import { useEffect } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { useDispatch, useSelector } from 'react-redux';
4
4
  import { isEmpty, map } from 'lodash';
5
5
  import { defineMessages, useIntl } from 'react-intl';
6
6
 
7
7
  import { usePrevious } from '@plone/volto/helpers';
8
+ import { cloneDeepSchema } from '@plone/volto/helpers/Utils/Utils';
8
9
  import { updateContent } from '@plone/volto/actions';
9
10
  import { ModalForm } from '@plone/volto/components/manage/Form';
11
+ import config from '@plone/volto/registry';
10
12
 
11
13
  const messages = defineMessages({
12
14
  properties: {
@@ -65,12 +67,83 @@ const messages = defineMessages({
65
67
  });
66
68
 
67
69
  const ContentsPropertiesModal = (props) => {
68
- const { onCancel, onOk, open, items } = props;
70
+ const { onCancel, onOk, open, items, values } = props;
69
71
  const intl = useIntl();
70
72
  const dispatch = useDispatch();
71
73
  const request = useSelector((state) => state.content.update);
72
74
  const prevrequestloading = usePrevious(request.loading);
73
75
 
76
+ const baseSchema = {
77
+ fieldsets: [
78
+ {
79
+ id: 'default',
80
+ title: intl.formatMessage(messages.default),
81
+ fields: [
82
+ 'effective',
83
+ 'expires',
84
+ 'rights',
85
+ 'creators',
86
+ 'exclude_from_nav',
87
+ ],
88
+ },
89
+ ],
90
+ properties: {
91
+ effective: {
92
+ description: intl.formatMessage(messages.effectiveDescription),
93
+ title: intl.formatMessage(messages.effectiveTitle),
94
+ type: 'string',
95
+ widget: 'datetime',
96
+ },
97
+ expires: {
98
+ description: intl.formatMessage(messages.expiresDescription),
99
+ title: intl.formatMessage(messages.expiresTitle),
100
+ type: 'string',
101
+ widget: 'datetime',
102
+ },
103
+ rights: {
104
+ description: intl.formatMessage(messages.rightsDescription),
105
+ title: intl.formatMessage(messages.rightsTitle),
106
+ type: 'string',
107
+ widget: 'textarea',
108
+ },
109
+ creators: {
110
+ description: intl.formatMessage(messages.creatorsDescription),
111
+ title: intl.formatMessage(messages.creatorsTitle),
112
+ type: 'array',
113
+ },
114
+ exclude_from_nav: {
115
+ description: intl.formatMessage(messages.excludeFromNavDescription),
116
+ title: intl.formatMessage(messages.excludeFromNavTitle),
117
+ type: 'boolean',
118
+ },
119
+ },
120
+ required: [],
121
+ };
122
+ const schemaEnhancer = config.settings.contentPropertiesSchemaEnhancer;
123
+ let schema = schemaEnhancer
124
+ ? schemaEnhancer({
125
+ schema: cloneDeepSchema(baseSchema),
126
+ intl,
127
+ })
128
+ : baseSchema;
129
+
130
+ const initialData = {};
131
+ if (values?.length) {
132
+ for (const name of Object.keys(schema.properties)) {
133
+ const firstValue = values[0][name];
134
+ // should not show floor or ceiling dates
135
+ if (
136
+ (name === 'effective' && firstValue && firstValue <= '1970') ||
137
+ (name === 'expires' && firstValue && firstValue >= '2499')
138
+ ) {
139
+ continue;
140
+ }
141
+ if (values.every((item) => item[name] === firstValue)) {
142
+ initialData[name] = firstValue;
143
+ }
144
+ }
145
+ }
146
+
74
147
  useEffect(() => {
75
148
  if (prevrequestloading && request.loaded) {
76
149
  onOk();
@@ -78,13 +151,19 @@ const ContentsPropertiesModal = (props) => {
78
151
  }, [onOk, prevrequestloading, request.loaded]);
79
152
 
80
153
  const onSubmit = (data) => {
81
- if (isEmpty(data)) {
154
+ let changes = {};
155
+ for (const name of Object.keys(data)) {
156
+ if (data[name] !== initialData[name]) {
157
+ changes[name] = data[name];
158
+ }
159
+ }
160
+ if (isEmpty(changes)) {
82
161
  onOk();
83
162
  } else {
84
163
  dispatch(
85
164
  updateContent(
86
165
  items,
87
- map(items, () => data),
166
+ map(items, () => changes),
88
167
  ),
89
168
  );
90
169
  }
@@ -97,54 +176,8 @@ const ContentsPropertiesModal = (props) => {
97
176
  onSubmit={onSubmit}
98
177
  onCancel={onCancel}
99
178
  title={intl.formatMessage(messages.properties)}
100
- schema={{
101
- fieldsets: [
102
- {
103
- id: 'default',
104
- title: intl.formatMessage(messages.default),
105
- fields: [
106
- 'effective',
107
- 'expires',
108
- 'rights',
109
- 'creators',
110
- 'exclude_from_nav',
111
- ],
112
- },
113
- ],
114
- properties: {
115
- effective: {
116
- description: intl.formatMessage(messages.effectiveDescription),
117
- title: intl.formatMessage(messages.effectiveTitle),
118
- type: 'string',
119
- widget: 'datetime',
120
- },
121
- expires: {
122
- description: intl.formatMessage(messages.expiresDescription),
123
- title: intl.formatMessage(messages.expiresTitle),
124
- type: 'string',
125
- widget: 'datetime',
126
- },
127
- rights: {
128
- description: intl.formatMessage(messages.rightsDescription),
129
- title: intl.formatMessage(messages.rightsTitle),
130
- type: 'string',
131
- widget: 'textarea',
132
- },
133
- creators: {
134
- description: intl.formatMessage(messages.creatorsDescription),
135
- title: intl.formatMessage(messages.creatorsTitle),
136
- type: 'array',
137
- },
138
- exclude_from_nav: {
139
- description: intl.formatMessage(
140
- messages.excludeFromNavDescription,
141
- ),
142
- title: intl.formatMessage(messages.excludeFromNavTitle),
143
- type: 'boolean',
144
- },
145
- },
146
- required: [],
147
- }}
179
+ schema={schema}
180
+ formData={initialData}
148
181
  />
149
182
  )
150
183
  );