@plone/volto 17.0.0-alpha.20 → 17.0.0-alpha.22

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 (108) hide show
  1. package/.gitignore~ +71 -0
  2. package/.yarn/install-state.gz +0 -0
  3. package/CHANGELOG.md +49 -0
  4. package/cypress/support/commands.js +2 -1
  5. package/cypress/support/e2e.js +1 -2
  6. package/locales/ca/LC_MESSAGES/volto.po +24 -5
  7. package/locales/ca.json +1 -1
  8. package/locales/de/LC_MESSAGES/volto.po +37 -18
  9. package/locales/de.json +1 -1
  10. package/locales/en/LC_MESSAGES/volto.po +25 -6
  11. package/locales/en.json +1 -1
  12. package/locales/es/LC_MESSAGES/volto.po +25 -6
  13. package/locales/es.json +1 -1
  14. package/locales/eu/LC_MESSAGES/volto.po +24 -5
  15. package/locales/eu.json +1 -1
  16. package/locales/fi/LC_MESSAGES/volto.po +24 -5
  17. package/locales/fi.json +1 -1
  18. package/locales/fr/LC_MESSAGES/volto.po +24 -5
  19. package/locales/fr.json +1 -1
  20. package/locales/it/LC_MESSAGES/volto.po +250 -231
  21. package/locales/it.json +1 -1
  22. package/locales/ja/LC_MESSAGES/volto.po +24 -5
  23. package/locales/ja.json +1 -1
  24. package/locales/nl/LC_MESSAGES/volto.po +24 -5
  25. package/locales/nl.json +1 -1
  26. package/locales/pt/LC_MESSAGES/volto.po +24 -5
  27. package/locales/pt.json +1 -1
  28. package/locales/pt_BR/LC_MESSAGES/volto.po +25 -6
  29. package/locales/pt_BR.json +1 -1
  30. package/locales/ro/LC_MESSAGES/volto.po +24 -5
  31. package/locales/ro.json +1 -1
  32. package/locales/volto.pot +25 -6
  33. package/locales/zh_CN/LC_MESSAGES/volto.po +24 -5
  34. package/locales/zh_CN.json +1 -1
  35. package/news/4547.breaking~ +1 -0
  36. package/package.json +6 -6
  37. package/packages/volto-slate/package.json +1 -1
  38. package/src/actions/relations/rebuild.js +7 -7
  39. package/src/components/index.js +1 -0
  40. package/src/components/manage/Actions/Actions.jsx +133 -243
  41. package/src/components/manage/Blocks/Container/Edit.jsx +4 -1
  42. package/src/components/manage/Blocks/Container/EditBlockWrapper.jsx +1 -0
  43. package/src/components/manage/Blocks/Grid/Edit.jsx +13 -1
  44. package/src/components/manage/Blocks/Image/Edit.jsx +40 -5
  45. package/src/components/manage/Blocks/Image/Edit.test.jsx +2 -0
  46. package/src/components/manage/Blocks/Image/ImageSidebar.jsx +64 -15
  47. package/src/components/manage/Blocks/Image/View.jsx +26 -5
  48. package/src/components/manage/Blocks/Image/View.test.jsx +20 -0
  49. package/src/components/manage/Blocks/Image/schema.js +1 -9
  50. package/src/components/manage/Blocks/Image/utils.js +14 -0
  51. package/src/components/manage/Blocks/LeadImage/Edit.jsx +32 -10
  52. package/src/components/manage/Blocks/LeadImage/Edit.test.jsx +11 -1
  53. package/src/components/manage/Blocks/LeadImage/LeadImageSidebar.jsx +28 -9
  54. package/src/components/manage/Blocks/LeadImage/LeadImageSidebar.test.jsx +8 -2
  55. package/src/components/manage/Blocks/LeadImage/View.jsx +50 -38
  56. package/src/components/manage/Blocks/LeadImage/View.test.jsx +11 -1
  57. package/src/components/manage/Blocks/Listing/SummaryTemplate.jsx +1 -1
  58. package/src/components/manage/Blocks/Maps/Edit.jsx +135 -209
  59. package/src/components/manage/Blocks/Search/SearchBlockView.jsx +3 -2
  60. package/src/components/manage/Blocks/Teaser/DefaultBody.jsx +13 -23
  61. package/src/components/manage/Contents/Contents.jsx +8 -6
  62. package/src/components/manage/Contents/ContentsPropertiesModal.jsx +1 -13
  63. package/src/components/manage/Controlpanels/Groups/RenderGroups.jsx +2 -2
  64. package/src/components/manage/Controlpanels/Relations/BrokenRelations.jsx +30 -7
  65. package/src/components/manage/Controlpanels/Relations/Relations.jsx +2 -2
  66. package/src/components/manage/Controlpanels/Relations/RelationsMatrix.jsx +53 -59
  67. package/src/components/manage/Controlpanels/Users/RenderUsers.jsx +2 -2
  68. package/src/components/manage/Delete/Delete.jsx +96 -171
  69. package/src/components/manage/Sidebar/AlignBlock.jsx +1 -1
  70. package/src/components/manage/Widgets/SelectUtils.js +1 -1
  71. package/src/components/manage/Workflow/Workflow.jsx +75 -184
  72. package/src/components/theme/Image/Image.jsx +96 -0
  73. package/src/components/theme/Image/Image.test.jsx +125 -0
  74. package/src/components/theme/Logo/Logo.jsx +2 -0
  75. package/src/components/theme/PasswordReset/RequestPasswordReset.jsx +95 -170
  76. package/src/components/theme/PreviewImage/PreviewImage.jsx +25 -14
  77. package/src/components/theme/PreviewImage/PreviewImage.test.js +39 -16
  78. package/src/components/theme/View/AlbumView.jsx +11 -15
  79. package/src/components/theme/View/EventView.jsx +30 -23
  80. package/src/components/theme/View/ImageView.jsx +5 -2
  81. package/src/components/theme/View/ImageView.test.jsx +4 -0
  82. package/src/components/theme/View/ListingView.jsx +5 -3
  83. package/src/components/theme/View/NewsItemView.jsx +7 -13
  84. package/src/components/theme/View/SummaryView.jsx +4 -3
  85. package/src/config/Blocks.jsx +2 -0
  86. package/src/config/Components.jsx +3 -1
  87. package/src/config/index.js~ +223 -0
  88. package/src/express-middleware/files.js +8 -6
  89. package/src/express-middleware/images.js +7 -1
  90. package/src/helpers/MessageLabels/MessageLabels.js +6 -0
  91. package/src/helpers/Url/Url.js +22 -1
  92. package/src/helpers/Url/Url.test.js +41 -0
  93. package/src/reducers/relations/relations.js +1 -1
  94. package/test-setup-config.js +9 -1
  95. package/theme/themes/pastanaga/extras/blocks.less +3 -1
  96. package/theme/themes/pastanaga/extras/main.less +5 -0
  97. package/packages/volto-slate/build/messages/src/blocks/Table/TableBlockEdit.json +0 -90
  98. package/packages/volto-slate/build/messages/src/blocks/Text/DefaultTextBlockEditor.json +0 -6
  99. package/packages/volto-slate/build/messages/src/blocks/Text/DetachedTextBlockEditor.json +0 -6
  100. package/packages/volto-slate/build/messages/src/blocks/Text/SlashMenu.json +0 -6
  101. package/packages/volto-slate/build/messages/src/editor/plugins/AdvancedLink/index.json +0 -10
  102. package/packages/volto-slate/build/messages/src/editor/plugins/Link/index.json +0 -10
  103. package/packages/volto-slate/build/messages/src/editor/plugins/Table/index.json +0 -30
  104. package/packages/volto-slate/build/messages/src/elementEditor/messages.json +0 -10
  105. package/packages/volto-slate/build/messages/src/widgets/HtmlSlateWidget.json +0 -6
  106. package/packages/volto-slate/build/messages/src/widgets/RichTextWidgetView.json +0 -6
  107. package/src/components/manage/Blocks/Teaser/utils.js +0 -44
  108. package/src/components/manage/Blocks/Teaser/utils.test.jsx +0 -229
@@ -1,16 +1,10 @@
1
- /**
2
- * Actions component.
3
- * @module components/manage/Actions/Actions
4
- */
5
-
6
- import React, { Component } from 'react';
1
+ import { useState } from 'react';
7
2
  import PropTypes from 'prop-types';
8
- import { compose } from 'redux';
9
- import { connect } from 'react-redux';
3
+ import { useDispatch, useSelector, shallowEqual } from 'react-redux';
10
4
  import { Link } from 'react-router-dom';
11
5
  import { Dropdown, Icon } from 'semantic-ui-react';
12
6
  import { toast } from 'react-toastify';
13
- import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
7
+ import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
14
8
 
15
9
  import { cut, copy, copyContent, moveContent } from '@plone/volto/actions';
16
10
  import { getBaseUrl } from '@plone/volto/helpers';
@@ -55,267 +49,163 @@ const messages = defineMessages({
55
49
  },
56
50
  });
57
51
 
58
- /**
59
- * Actions container class.
60
- * @class Actions
61
- * @extends Component
62
- */
63
- class Actions extends Component {
64
- /**
65
- * Property types.
66
- * @property {Object} propTypes Property types.
67
- * @static
68
- */
69
- static propTypes = {
70
- actions: PropTypes.shape({
71
- object: PropTypes.arrayOf(PropTypes.object),
72
- object_buttons: PropTypes.arrayOf(PropTypes.object),
73
- user: PropTypes.arrayOf(PropTypes.object),
74
- }),
75
- pathname: PropTypes.string.isRequired,
76
- id: PropTypes.string.isRequired,
77
- title: PropTypes.string.isRequired,
78
- action: PropTypes.string,
79
- source: PropTypes.arrayOf(PropTypes.string),
80
- cut: PropTypes.func.isRequired,
81
- copy: PropTypes.func.isRequired,
82
- copyContent: PropTypes.func.isRequired,
83
- moveContent: PropTypes.func.isRequired,
84
- };
52
+ const Actions = (props) => {
53
+ const intl = useIntl();
54
+ const dispatch = useDispatch();
55
+ const [showRename, setshowRename] = useState(false);
56
+ const actions = useSelector((state) => state.actions.actions, shallowEqual);
57
+ const action = useSelector((state) => state.clipboard.action);
58
+ const source = useSelector((state) => state.clipboard.source, shallowEqual);
85
59
 
86
- /**
87
- * Default properties
88
- * @property {Object} defaultProps Default properties.
89
- * @static
90
- */
91
- static defaultProps = {
92
- action: null,
93
- actions: null,
94
- source: null,
95
- };
60
+ const id = useSelector((state) =>
61
+ state.content.data ? state.content.data.id : '',
62
+ );
63
+ const title = useSelector((state) =>
64
+ state.content.data ? state.content.data.title : '',
65
+ );
96
66
 
97
- /**
98
- * Constructor
99
- * @method constructor
100
- * @param {Object} props Component properties
101
- * @constructs Actions
102
- */
103
- constructor(props) {
104
- super(props);
105
- this.cut = this.cut.bind(this);
106
- this.copy = this.copy.bind(this);
107
- this.paste = this.paste.bind(this);
108
- this.rename = this.rename.bind(this);
109
- this.onRenameOk = this.onRenameOk.bind(this);
110
- this.onRenameCancel = this.onRenameCancel.bind(this);
111
- this.state = {
112
- showRename: false,
113
- };
114
- }
67
+ const onRenameOk = () => {
68
+ setshowRename(false);
69
+ };
115
70
 
116
- /**
117
- * On rename ok
118
- * @method onRenameOk
119
- * @returns {undefined}
120
- */
121
- onRenameOk() {
122
- this.setState({
123
- showRename: false,
124
- });
125
- }
71
+ const onRenameCancel = () => {
72
+ setshowRename(false);
73
+ };
126
74
 
127
- /**
128
- * On rename cancel
129
- * @method onRenameCancel
130
- * @returns {undefined}
131
- */
132
- onRenameCancel() {
133
- this.setState({
134
- showRename: false,
135
- });
136
- }
75
+ const fncut = () => {
76
+ dispatch(cut([getBaseUrl(props.pathname)]));
137
77
 
138
- /**
139
- * Cut handler
140
- * @method cut
141
- * @returns {undefined}
142
- */
143
- cut() {
144
- this.props.cut([getBaseUrl(this.props.pathname)]);
145
78
  toast.success(
146
79
  <Toast
147
80
  success
148
- title={this.props.intl.formatMessage(messages.success)}
149
- content={this.props.intl.formatMessage(messages.messageCut, {
150
- title: this.props.title,
81
+ title={intl.formatMessage(messages.success)}
82
+ content={intl.formatMessage(messages.messageCut, {
83
+ title: title,
151
84
  })}
152
85
  />,
153
86
  );
154
- }
87
+ };
155
88
 
156
- /**
157
- * Copy handler
158
- * @method copy
159
- * @returns {undefined}
160
- */
161
- copy() {
162
- this.props.copy([getBaseUrl(this.props.pathname)]);
89
+ const fncopy = () => {
90
+ dispatch(copy([getBaseUrl(props.pathname)]));
163
91
  toast.success(
164
92
  <Toast
165
93
  success
166
- title={this.props.intl.formatMessage(messages.success)}
167
- content={this.props.intl.formatMessage(messages.messageCopied, {
168
- title: this.props.title,
94
+ title={intl.formatMessage(messages.success)}
95
+ content={intl.formatMessage(messages.messageCopied, {
96
+ title: title,
169
97
  })}
170
98
  />,
171
99
  );
172
- }
100
+ };
173
101
 
174
- /**
175
- * Paste handler
176
- * @method paste
177
- * @returns {undefined}
178
- */
179
- paste() {
180
- if (this.props.action === 'copy') {
181
- this.props.copyContent(
182
- this.props.source,
183
- getBaseUrl(this.props.pathname),
184
- );
102
+ const paste = () => {
103
+ if (action === 'copy') {
104
+ dispatch(copyContent(source, getBaseUrl(props.pathname)));
185
105
  }
186
- if (this.props.action === 'cut') {
187
- this.props.moveContent(
188
- this.props.source,
189
- getBaseUrl(this.props.pathname),
190
- );
106
+ if (action === 'cut') {
107
+ dispatch(moveContent(source, getBaseUrl(props.pathname)));
191
108
  }
192
109
  toast.success(
193
110
  <Toast
194
111
  success
195
- title={this.props.intl.formatMessage(messages.success)}
196
- content={this.props.intl.formatMessage(messages.messagePasted)}
112
+ title={intl.formatMessage(messages.success)}
113
+ content={intl.formatMessage(messages.messagePasted)}
197
114
  />,
198
115
  );
199
- }
116
+ };
200
117
 
201
- /**
202
- * Rename handler
203
- * @method rename
204
- * @returns {undefined}
205
- */
206
- rename() {
207
- this.setState({
208
- showRename: true,
209
- });
210
- }
118
+ const rename = () => {
119
+ setshowRename(true);
120
+ };
211
121
 
212
- /**
213
- * Render method.
214
- * @method render
215
- * @returns {string} Markup for the component.
216
- */
217
- render() {
218
- return (
219
- <Dropdown
220
- item
221
- id="toolbar-actions"
222
- trigger={
223
- <span>
224
- <Icon name="lightning" size="big" />{' '}
225
- <FormattedMessage id="Actions" defaultMessage="Actions" />
226
- </span>
227
- }
228
- >
229
- <Dropdown.Menu>
230
- {this.props.actions.object_buttons &&
231
- this.props.actions.object_buttons.map((item) => {
232
- switch (item.id) {
233
- case 'cut':
234
- return (
235
- <Dropdown.Item
236
- key={item.id}
237
- icon="cut"
238
- text={item.title}
239
- onClick={this.cut}
240
- />
241
- );
242
- case 'copy':
243
- return (
244
- <Dropdown.Item
245
- key={item.id}
246
- icon="copy"
247
- text={item.title}
248
- onClick={this.copy}
249
- />
250
- );
251
- case 'paste':
252
- return (
253
- <Dropdown.Item
254
- key={item.id}
255
- icon="paste"
256
- text={item.title}
257
- onClick={this.paste}
258
- disabled={this.props.action === null}
259
- />
260
- );
261
- case 'delete':
262
- return (
263
- <Link
264
- key={item.id}
265
- to={`${this.props.pathname}/delete`}
266
- className="item"
267
- >
268
- <Icon name="trash" />
269
- {item.title}
270
- </Link>
271
- );
272
- case 'rename':
273
- return (
274
- <Dropdown.Item
275
- key={item.id}
276
- icon="text cursor"
277
- text={item.title}
278
- onClick={this.rename}
279
- />
280
- );
281
- default:
282
- return null;
283
- }
284
- })}
122
+ return (
123
+ <Dropdown
124
+ item
125
+ id="toolbar-actions"
126
+ trigger={
127
+ <span>
128
+ <Icon name="lightning" size="big" />{' '}
129
+ <FormattedMessage id="Actions" defaultMessage="Actions" />
130
+ </span>
131
+ }
132
+ >
133
+ <Dropdown.Menu>
134
+ {actions.object_buttons &&
135
+ actions.object_buttons.map((item) => {
136
+ switch (item.id) {
137
+ case 'cut':
138
+ return (
139
+ <Dropdown.Item
140
+ key={item.id}
141
+ icon="cut"
142
+ text={item.title}
143
+ onClick={fncut}
144
+ />
145
+ );
146
+ case 'copy':
147
+ return (
148
+ <Dropdown.Item
149
+ key={item.id}
150
+ icon="copy"
151
+ text={item.title}
152
+ onClick={fncopy}
153
+ />
154
+ );
155
+ case 'paste':
156
+ return (
157
+ <Dropdown.Item
158
+ key={item.id}
159
+ icon="paste"
160
+ text={item.title}
161
+ onClick={paste}
162
+ disabled={action === null}
163
+ />
164
+ );
165
+ case 'delete':
166
+ return (
167
+ <Link
168
+ key={item.id}
169
+ to={`${props.pathname}/delete`}
170
+ className="item"
171
+ >
172
+ <Icon name="trash" />
173
+ {item.title}
174
+ </Link>
175
+ );
176
+ case 'rename':
177
+ return (
178
+ <Dropdown.Item
179
+ key={item.id}
180
+ icon="text cursor"
181
+ text={item.title}
182
+ onClick={rename}
183
+ />
184
+ );
185
+ default:
186
+ return null;
187
+ }
188
+ })}
285
189
 
286
- <ContentsRenameModal
287
- open={this.state.showRename}
288
- onCancel={this.onRenameCancel}
289
- onOk={this.onRenameOk}
290
- items={[
291
- {
292
- url: this.props.pathname,
293
- title: this.props.title,
294
- id: this.props.id,
295
- },
296
- ]}
297
- />
298
- </Dropdown.Menu>
299
- </Dropdown>
300
- );
301
- }
302
- }
190
+ <ContentsRenameModal
191
+ open={showRename}
192
+ onCancel={onRenameCancel}
193
+ onOk={onRenameOk}
194
+ items={[
195
+ {
196
+ url: props.pathname,
197
+ title: title,
198
+ id: id,
199
+ },
200
+ ]}
201
+ />
202
+ </Dropdown.Menu>
203
+ </Dropdown>
204
+ );
205
+ };
206
+
207
+ Actions.propTypes = {
208
+ pathname: PropTypes.string.isRequired,
209
+ };
303
210
 
304
- export default compose(
305
- injectIntl,
306
- connect(
307
- (state) => ({
308
- actions: state.actions.actions,
309
- action: state.clipboard.action,
310
- source: state.clipboard.source,
311
- id: state.content.data ? state.content.data.id : '',
312
- title: state.content.data ? state.content.data.title : '',
313
- }),
314
- {
315
- cut,
316
- copy,
317
- copyContent,
318
- moveContent,
319
- },
320
- ),
321
- )(Actions);
211
+ export default Actions;
@@ -43,9 +43,12 @@ const ContainerBlockEdit = (props) => {
43
43
  const EditBlockWrapper =
44
44
  blockConfig.editBlockWrapper || DefaultEditBlockWrapper;
45
45
 
46
- const [selectedBlock, setSelectedBlock] = useState(
46
+ let [selectedBlock, setSelectedBlock] = useState(
47
47
  properties.blocks_layout.items[0],
48
48
  );
49
+ if (props.setSelectedBlock) {
50
+ ({ selectedBlock, setSelectedBlock } = props);
51
+ }
49
52
 
50
53
  const blockState = {};
51
54
 
@@ -49,6 +49,7 @@ const EditBlockWrapper = (props) => {
49
49
  role="presentation"
50
50
  className="cell-wrapper"
51
51
  onClick={(e) => {
52
+ e.stopPropagation();
52
53
  onSelectBlock(block);
53
54
  }}
54
55
  >
@@ -1,5 +1,6 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import cx from 'classnames';
3
+ import { useState } from 'react';
3
4
  import ContainerEdit from '../Container/Edit';
4
5
 
5
6
  const GridBlockEdit = (props) => {
@@ -7,6 +8,8 @@ const GridBlockEdit = (props) => {
7
8
 
8
9
  const columnsLength = data?.blocks_layout?.items?.length || 0;
9
10
 
11
+ const [selectedBlock, setSelectedBlock] = useState(null);
12
+
10
13
  return (
11
14
  <div
12
15
  className={cx({
@@ -16,8 +19,17 @@ const GridBlockEdit = (props) => {
16
19
  four: columnsLength >= 4,
17
20
  'grid-items': true,
18
21
  })}
22
+ // This is required to enabling a small "in-between" clickable area
23
+ // for bringing the Grid sidebar alive once you have selected an inner block
24
+ onClick={(e) => setSelectedBlock(null)}
25
+ role="presentation"
19
26
  >
20
- <ContainerEdit {...props} direction="horizontal" />
27
+ <ContainerEdit
28
+ {...props}
29
+ selectedBlock={selectedBlock}
30
+ setSelectedBlock={setSelectedBlock}
31
+ direction="horizontal"
32
+ />
21
33
  </div>
22
34
  );
23
35
  };
@@ -15,14 +15,15 @@ import cx from 'classnames';
15
15
  import { isEqual } from 'lodash';
16
16
 
17
17
  import { Icon, ImageSidebar, SidebarPortal } from '@plone/volto/components';
18
- import { withBlockExtensions } from '@plone/volto/helpers';
19
18
  import { createContent } from '@plone/volto/actions';
20
19
  import {
21
20
  flattenToAppURL,
22
21
  getBaseUrl,
23
22
  isInternalURL,
23
+ withBlockExtensions,
24
24
  validateFileUploadSize,
25
25
  } from '@plone/volto/helpers';
26
+ import config from '@plone/volto/registry';
26
27
 
27
28
  import imageBlockSVG from '@plone/volto/components/manage/Blocks/Image/block-image.svg';
28
29
  import clearSVG from '@plone/volto/icons/clear.svg';
@@ -59,7 +60,7 @@ class Edit extends Component {
59
60
  block: PropTypes.string.isRequired,
60
61
  index: PropTypes.number.isRequired,
61
62
  data: PropTypes.objectOf(PropTypes.any).isRequired,
62
- content: PropTypes.objectOf(PropTypes.any).isRequired,
63
+ content: PropTypes.objectOf(PropTypes.any),
63
64
  request: PropTypes.shape({
64
65
  loading: PropTypes.bool,
65
66
  loaded: PropTypes.bool,
@@ -88,6 +89,7 @@ class Edit extends Component {
88
89
  * @returns {undefined}
89
90
  */
90
91
  UNSAFE_componentWillReceiveProps(nextProps) {
92
+ // Update block data after upload finished
91
93
  if (
92
94
  this.props.request.loading &&
93
95
  nextProps.request.loaded &&
@@ -99,6 +101,8 @@ class Edit extends Component {
99
101
  this.props.onChangeBlock(this.props.block, {
100
102
  ...this.props.data,
101
103
  url: nextProps.content['@id'],
104
+ image_field: 'image',
105
+ image_scales: { image: [nextProps.content.image] },
102
106
  alt: '',
103
107
  });
104
108
  }
@@ -171,6 +175,8 @@ class Edit extends Component {
171
175
  this.props.onChangeBlock(this.props.block, {
172
176
  ...this.props.data,
173
177
  url: flattenToAppURL(this.state.url),
178
+ image_field: undefined,
179
+ image_scales: undefined,
174
180
  });
175
181
  };
176
182
 
@@ -240,10 +246,12 @@ class Edit extends Component {
240
246
  * @returns {string} Markup for the component.
241
247
  */
242
248
  render() {
249
+ const Image = config.getComponent({ name: 'Image' }).component;
243
250
  const { data } = this.props;
244
251
  const placeholder =
245
252
  this.props.data.placeholder ||
246
253
  this.props.intl.formatMessage(messages.ImageBlockInputPlaceholder);
254
+
247
255
  return (
248
256
  <div
249
257
  className={cx(
@@ -255,15 +263,26 @@ class Edit extends Component {
255
263
  )}
256
264
  >
257
265
  {data.url ? (
258
- <img
266
+ <Image
259
267
  className={cx({
260
268
  'full-width': data.align === 'full',
261
269
  large: data.size === 'l',
262
270
  medium: data.size === 'm',
263
271
  small: data.size === 's',
264
272
  })}
273
+ item={
274
+ data.image_scales
275
+ ? {
276
+ '@id': data.url,
277
+ image_field: data.image_field,
278
+ image_scales: data.image_scales,
279
+ }
280
+ : undefined
281
+ }
265
282
  src={
266
- isInternalURL(data.url)
283
+ data.image_scales
284
+ ? undefined
285
+ : isInternalURL(data.url)
267
286
  ? // Backwards compat in the case that the block is storing the full server URL
268
287
  (() => {
269
288
  if (data.size === 'l')
@@ -278,7 +297,10 @@ class Edit extends Component {
278
297
  })()
279
298
  : data.url
280
299
  }
300
+ sizes={config.blocks.blocksConfig.image.getSizes(data)}
281
301
  alt={data.alt || ''}
302
+ loading="lazy"
303
+ responsive={true}
282
304
  />
283
305
  ) : (
284
306
  <div>
@@ -313,7 +335,20 @@ class Edit extends Component {
313
335
  onClick={(e) => {
314
336
  e.stopPropagation();
315
337
  e.preventDefault();
316
- this.props.openObjectBrowser();
338
+ this.props.openObjectBrowser({
339
+ onSelectItem: (
340
+ url,
341
+ { title, image_field, image_scales },
342
+ ) => {
343
+ this.props.onChangeBlock(this.props.block, {
344
+ ...this.props.data,
345
+ url,
346
+ image_field,
347
+ image_scales,
348
+ alt: this.props.data.alt || title || '',
349
+ });
350
+ },
351
+ });
317
352
  }}
318
353
  >
319
354
  <Icon name={navTreeSVG} size="24px" />
@@ -3,6 +3,7 @@ import renderer from 'react-test-renderer';
3
3
  import configureStore from 'redux-mock-store';
4
4
  import { Provider } from 'react-intl-redux';
5
5
  import { waitFor } from '@testing-library/react';
6
+ import { getImageBlockSizes } from './utils';
6
7
  import config from '@plone/volto/registry';
7
8
 
8
9
  import Edit from './Edit';
@@ -25,6 +26,7 @@ config.blocks.blocksConfig = {
25
26
  addPermission: [],
26
27
  view: [],
27
28
  },
29
+ getSizes: getImageBlockSizes,
28
30
  },
29
31
  };
30
32