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

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 (74) hide show
  1. package/.yarn/install-state.gz +0 -0
  2. package/CHANGELOG.md +19 -0
  3. package/cypress/support/commands.js +2 -1
  4. package/cypress/support/e2e.js +1 -2
  5. package/locales/ca/LC_MESSAGES/volto.po +10 -1
  6. package/locales/ca.json +1 -1
  7. package/locales/de/LC_MESSAGES/volto.po +10 -1
  8. package/locales/de.json +1 -1
  9. package/locales/en/LC_MESSAGES/volto.po +10 -1
  10. package/locales/en.json +1 -1
  11. package/locales/es/LC_MESSAGES/volto.po +10 -1
  12. package/locales/es.json +1 -1
  13. package/locales/eu/LC_MESSAGES/volto.po +10 -1
  14. package/locales/eu.json +1 -1
  15. package/locales/fi/LC_MESSAGES/volto.po +10 -1
  16. package/locales/fi.json +1 -1
  17. package/locales/fr/LC_MESSAGES/volto.po +10 -1
  18. package/locales/fr.json +1 -1
  19. package/locales/it/LC_MESSAGES/volto.po +11 -2
  20. package/locales/it.json +1 -1
  21. package/locales/ja/LC_MESSAGES/volto.po +10 -1
  22. package/locales/ja.json +1 -1
  23. package/locales/nl/LC_MESSAGES/volto.po +10 -1
  24. package/locales/nl.json +1 -1
  25. package/locales/pt/LC_MESSAGES/volto.po +10 -1
  26. package/locales/pt.json +1 -1
  27. package/locales/pt_BR/LC_MESSAGES/volto.po +10 -1
  28. package/locales/pt_BR.json +1 -1
  29. package/locales/ro/LC_MESSAGES/volto.po +10 -1
  30. package/locales/ro.json +1 -1
  31. package/locales/volto.pot +10 -1
  32. package/locales/zh_CN/LC_MESSAGES/volto.po +10 -1
  33. package/locales/zh_CN.json +1 -1
  34. package/package.json +4 -4
  35. package/packages/volto-slate/package.json +1 -1
  36. package/src/components/index.js +1 -0
  37. package/src/components/manage/Blocks/Image/Edit.jsx +40 -5
  38. package/src/components/manage/Blocks/Image/Edit.test.jsx +2 -0
  39. package/src/components/manage/Blocks/Image/ImageSidebar.jsx +64 -15
  40. package/src/components/manage/Blocks/Image/View.jsx +25 -5
  41. package/src/components/manage/Blocks/Image/View.test.jsx +20 -0
  42. package/src/components/manage/Blocks/Image/schema.js +1 -9
  43. package/src/components/manage/Blocks/Image/utils.js +14 -0
  44. package/src/components/manage/Blocks/LeadImage/Edit.jsx +32 -10
  45. package/src/components/manage/Blocks/LeadImage/Edit.test.jsx +11 -1
  46. package/src/components/manage/Blocks/LeadImage/LeadImageSidebar.jsx +28 -9
  47. package/src/components/manage/Blocks/LeadImage/LeadImageSidebar.test.jsx +8 -2
  48. package/src/components/manage/Blocks/LeadImage/View.jsx +50 -38
  49. package/src/components/manage/Blocks/LeadImage/View.test.jsx +11 -1
  50. package/src/components/manage/Blocks/Listing/SummaryTemplate.jsx +1 -1
  51. package/src/components/manage/Blocks/Teaser/DefaultBody.jsx +13 -23
  52. package/src/components/manage/Contents/Contents.jsx +8 -6
  53. package/src/components/manage/Sidebar/AlignBlock.jsx +1 -1
  54. package/src/components/theme/Image/Image.jsx +96 -0
  55. package/src/components/theme/Image/Image.test.jsx +125 -0
  56. package/src/components/theme/Logo/Logo.jsx +2 -0
  57. package/src/components/theme/PreviewImage/PreviewImage.jsx +25 -14
  58. package/src/components/theme/PreviewImage/PreviewImage.test.js +39 -16
  59. package/src/components/theme/View/AlbumView.jsx +11 -15
  60. package/src/components/theme/View/EventView.jsx +30 -23
  61. package/src/components/theme/View/ImageView.jsx +5 -2
  62. package/src/components/theme/View/ImageView.test.jsx +4 -0
  63. package/src/components/theme/View/ListingView.jsx +5 -3
  64. package/src/components/theme/View/NewsItemView.jsx +7 -13
  65. package/src/components/theme/View/SummaryView.jsx +4 -3
  66. package/src/config/Blocks.jsx +2 -0
  67. package/src/config/Components.jsx +2 -1
  68. package/src/helpers/Url/Url.js +22 -1
  69. package/src/helpers/Url/Url.test.js +41 -0
  70. package/test-setup-config.js +9 -1
  71. package/theme/themes/pastanaga/extras/blocks.less +2 -0
  72. package/theme/themes/pastanaga/extras/main.less +5 -0
  73. package/src/components/manage/Blocks/Teaser/utils.js +0 -44
  74. package/src/components/manage/Blocks/Teaser/utils.test.jsx +0 -229
@@ -0,0 +1,125 @@
1
+ import React from 'react';
2
+ import renderer from 'react-test-renderer';
3
+ import Image from './Image';
4
+
5
+ test('renders an image component with fetchpriority high', () => {
6
+ const component = renderer.create(
7
+ <Image
8
+ item={{
9
+ image: {
10
+ download: 'http://localhost:3000/image/@@images/image/image.png',
11
+ width: 400,
12
+ height: 400,
13
+ scales: {
14
+ preview: {
15
+ download:
16
+ 'http://localhost:3000/image/@@images/image/image-400.png',
17
+ width: 400,
18
+ height: 400,
19
+ },
20
+ },
21
+ },
22
+ }}
23
+ imageField="image"
24
+ alt="alt text"
25
+ />,
26
+ );
27
+ const json = component.toJSON();
28
+ expect(json).toMatchSnapshot();
29
+ });
30
+
31
+ test('renders an image component with lazy loading', () => {
32
+ const component = renderer.create(
33
+ <Image
34
+ item={{
35
+ image: {
36
+ download: 'http://localhost:3000/image/@@images/image/image.png',
37
+ width: 400,
38
+ height: 400,
39
+ scales: {
40
+ preview: {
41
+ download:
42
+ 'http://localhost:3000/image/@@images/image/image-400.png',
43
+ width: 400,
44
+ height: 400,
45
+ },
46
+ },
47
+ },
48
+ }}
49
+ imageField="image"
50
+ alt="alt text"
51
+ loading="lazy"
52
+ />,
53
+ );
54
+ const json = component.toJSON();
55
+ expect(json).toMatchSnapshot();
56
+ });
57
+
58
+ test('renders an image component with responsive class', () => {
59
+ const component = renderer.create(
60
+ <Image
61
+ item={{
62
+ image: {
63
+ download: 'http://localhost:3000/image/@@images/image/image.png',
64
+ width: 400,
65
+ height: 400,
66
+ scales: {
67
+ preview: {
68
+ download:
69
+ 'http://localhost:3000/image/@@images/image/image-400.png',
70
+ width: 400,
71
+ height: 400,
72
+ },
73
+ },
74
+ },
75
+ }}
76
+ imageField="image"
77
+ alt="alt text"
78
+ responsive={true}
79
+ />,
80
+ );
81
+ const json = component.toJSON();
82
+ expect(json).toMatchSnapshot();
83
+ });
84
+
85
+ test('renders an image component from a catalog brain', () => {
86
+ const component = renderer.create(
87
+ <Image
88
+ item={{
89
+ '@id': 'http://localhost:3000/image',
90
+ image_field: 'image',
91
+ image_scales: {
92
+ image: [
93
+ {
94
+ download: '@@images/image/image.png',
95
+ width: 400,
96
+ height: 400,
97
+ scales: {
98
+ preview: {
99
+ download: '@@images/image/image-400.png',
100
+ width: 400,
101
+ height: 400,
102
+ },
103
+ },
104
+ },
105
+ ],
106
+ },
107
+ }}
108
+ imageField="image"
109
+ alt="alt text"
110
+ />,
111
+ );
112
+ const json = component.toJSON();
113
+ expect(json).toMatchSnapshot();
114
+ });
115
+
116
+ test('renders an image component from a string src', () => {
117
+ const component = renderer.create(
118
+ <Image
119
+ src="http://localhost:3000/image/@@images/image/image.png"
120
+ alt="alt text"
121
+ />,
122
+ );
123
+ const json = component.toJSON();
124
+ expect(json).toMatchSnapshot();
125
+ });
@@ -42,6 +42,8 @@ const Logo = () => {
42
42
  src={LogoImage}
43
43
  alt={intl.formatMessage(messages.plonesite)}
44
44
  title={intl.formatMessage(messages.plonesite)}
45
+ width="158.2"
46
+ height="40.7"
45
47
  />
46
48
  </UniversalLink>
47
49
  );
@@ -1,34 +1,45 @@
1
- import React from 'react';
2
1
  import PropTypes from 'prop-types';
3
2
 
4
- import { flattenToAppURL } from '@plone/volto/helpers';
5
3
  import config from '@plone/volto/registry';
6
4
 
7
5
  import DefaultImageSVG from '@plone/volto/components/manage/Blocks/Listing/default-image.svg';
8
6
 
9
7
  /**
10
8
  * Renders a preview image for a catalog brain result item.
11
- *
12
9
  */
13
- function PreviewImage(props) {
14
- const { item, size = 'preview', alt, ...rest } = props;
15
- const src = item.image_field
16
- ? flattenToAppURL(`${item['@id']}/@@images/${item.image_field}/${size}`)
17
- : config.getComponent({
18
- name: 'DefaultImage',
19
- dependencies: ['listing', 'summary'],
20
- }).component || DefaultImageSVG;
10
+ function PreviewImage({ item, alt, ...rest }) {
11
+ const Image = config.getComponent({ name: 'Image' }).component;
21
12
 
22
- return <img src={src} alt={alt ?? item.title} {...rest} />;
13
+ if (item.image_field && item.image_scales?.[item.image_field]?.[0]) {
14
+ return (
15
+ <Image item={item} imageField={item.image_field} alt={alt} {...rest} />
16
+ );
17
+ } else {
18
+ return (
19
+ <img
20
+ src={
21
+ config.getComponent({
22
+ name: 'DefaultImage',
23
+ dependencies: ['listing', 'summary'],
24
+ }).component || DefaultImageSVG
25
+ }
26
+ alt={alt}
27
+ {...rest}
28
+ width="400"
29
+ height="300"
30
+ />
31
+ );
32
+ }
23
33
  }
24
34
 
25
35
  PreviewImage.propTypes = {
26
- size: PropTypes.string,
27
36
  item: PropTypes.shape({
28
37
  '@id': PropTypes.string.isRequired,
29
- image_field: PropTypes.string,
30
38
  title: PropTypes.string.isRequired,
39
+ image_field: PropTypes.string,
40
+ image_scales: PropTypes.object,
31
41
  }),
42
+ alt: PropTypes.string.isRequired,
32
43
  };
33
44
 
34
45
  export default PreviewImage;
@@ -7,35 +7,56 @@ describe('PreviewImage', () => {
7
7
  it('renders a preview image', () => {
8
8
  const item = {
9
9
  image_field: 'image',
10
- title: 'Item title',
11
- '@id': 'http://localhost:3000/something',
12
- };
13
- const component = renderer.create(<PreviewImage item={item} />);
14
- const json = component.toJSON();
15
- expect(json).toMatchSnapshot();
16
- });
17
-
18
- it('renders a preview image with extra props', () => {
19
- const item = {
20
- image_field: 'image',
10
+ image_scales: {
11
+ image: [
12
+ {
13
+ download: '@@images/image',
14
+ width: 400,
15
+ height: 400,
16
+ scales: {
17
+ preview: {
18
+ download: '@@images/image-400.png',
19
+ width: 400,
20
+ height: 400,
21
+ },
22
+ },
23
+ },
24
+ ],
25
+ },
21
26
  title: 'Item title',
22
27
  '@id': 'http://localhost:3000/something',
23
28
  };
24
29
  const component = renderer.create(
25
- <PreviewImage item={item} className="extra" />,
30
+ <PreviewImage item={item} alt={item.title} />,
26
31
  );
27
32
  const json = component.toJSON();
28
33
  expect(json).toMatchSnapshot();
29
34
  });
30
35
 
31
- it('renders a preview image with custom size', () => {
36
+ it('renders a preview image with extra props', () => {
32
37
  const item = {
33
38
  image_field: 'image',
39
+ image_scales: {
40
+ image: [
41
+ {
42
+ download: '@@images/image',
43
+ width: 400,
44
+ height: 400,
45
+ scales: {
46
+ preview: {
47
+ download: '@@images/image-400.png',
48
+ width: 400,
49
+ height: 400,
50
+ },
51
+ },
52
+ },
53
+ ],
54
+ },
34
55
  title: 'Item title',
35
56
  '@id': 'http://localhost:3000/something',
36
57
  };
37
58
  const component = renderer.create(
38
- <PreviewImage item={item} size="large" />,
59
+ <PreviewImage item={item} alt={item.title} className="extra" />,
39
60
  );
40
61
  const json = component.toJSON();
41
62
  expect(json).toMatchSnapshot();
@@ -46,7 +67,9 @@ describe('PreviewImage', () => {
46
67
  title: 'Item title',
47
68
  '@id': 'http://localhost:3000/something',
48
69
  };
49
- const component = renderer.create(<PreviewImage item={item} />);
70
+ const component = renderer.create(
71
+ <PreviewImage item={item} alt={item.title} />,
72
+ );
50
73
  const json = component.toJSON();
51
74
  expect(json).toMatchSnapshot();
52
75
  });
@@ -57,7 +80,7 @@ describe('PreviewImage', () => {
57
80
  '@id': 'http://localhost:3000/something',
58
81
  };
59
82
  const component = renderer.create(
60
- <PreviewImage item={item} className="extra" />,
83
+ <PreviewImage item={item} alt={item.title} className="extra" />,
61
84
  );
62
85
  const json = component.toJSON();
63
86
  expect(json).toMatchSnapshot();
@@ -3,7 +3,7 @@
3
3
  * @module components/theme/View/AlbumView
4
4
  */
5
5
 
6
- import React, { Component } from 'react';
6
+ import React from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
  import {
9
9
  Container as SemanticContainer,
@@ -11,7 +11,7 @@ import {
11
11
  Segment,
12
12
  } from 'semantic-ui-react';
13
13
  import { Button, Modal, Grid } from 'semantic-ui-react';
14
- import { Icon, UniversalLink, PreviewImage } from '@plone/volto/components';
14
+ import { Icon, UniversalLink } from '@plone/volto/components';
15
15
  import config from '@plone/volto/registry';
16
16
 
17
17
  import openSVG from '@plone/volto/icons/open.svg';
@@ -24,7 +24,7 @@ import backSVG from '@plone/volto/icons/back.svg';
24
24
  * @param {Object} content Content object.
25
25
  * @returns {string} Markup of the component.
26
26
  */
27
- class AlbumView extends Component {
27
+ class AlbumView extends React.Component {
28
28
  constructor(props) {
29
29
  super(props);
30
30
 
@@ -63,6 +63,8 @@ class AlbumView extends Component {
63
63
  const { content } = this.props;
64
64
  const Container =
65
65
  config.getComponent({ name: 'Container' }).component || SemanticContainer;
66
+ const PreviewImage = config.getComponent({ name: 'PreviewImage' })
67
+ .component;
66
68
 
67
69
  return (
68
70
  <Container className="view-wrapper">
@@ -88,17 +90,16 @@ class AlbumView extends Component {
88
90
  <Segment className="imageborder">
89
91
  <PreviewImage
90
92
  item={item}
91
- alt={
92
- item.image_caption
93
- ? item.image_caption
94
- : item.title
95
- }
93
+ alt={item.image_caption || item.title}
96
94
  onClick={() => {
97
95
  this.setState({
98
96
  openIndex: index,
99
97
  });
100
98
  }}
101
99
  className="ui middle aligned image"
100
+ responsive={true}
101
+ loading="lazy"
102
+ title={item.title}
102
103
  />
103
104
  </Segment>
104
105
  </Grid.Column>
@@ -141,20 +142,15 @@ class AlbumView extends Component {
141
142
  <Modal.Content image>
142
143
  <PreviewImage
143
144
  item={item}
144
- alt={
145
- item.image_caption
146
- ? item.image_caption
147
- : item.title
148
- }
145
+ alt={item.image_caption}
149
146
  onClick={() => {
150
147
  this.setState({
151
148
  openIndex: index,
152
149
  });
153
150
  }}
154
- size="large"
155
151
  className="ui image"
152
+ responsive={true}
156
153
  />
157
-
158
154
  <Modal.Description>
159
155
  <p>{item.description}</p>
160
156
  </Modal.Description>
@@ -6,34 +6,41 @@
6
6
  import React from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
  import { hasBlocksData, flattenHTMLToAppURL } from '@plone/volto/helpers';
9
- import { Image, Grid } from 'semantic-ui-react';
9
+ import { Grid } from 'semantic-ui-react';
10
10
  import RenderBlocks from '@plone/volto/components/theme/View/RenderBlocks';
11
11
  import { EventDetails } from '@plone/volto/components';
12
12
  import { Container as SemanticContainer } from 'semantic-ui-react';
13
13
  import config from '@plone/volto/registry';
14
14
 
15
- const EventTextfieldView = ({ content }) => (
16
- <React.Fragment>
17
- {content.title && <h1 className="documentFirstHeading">{content.title}</h1>}
18
- {content.description && (
19
- <p className="documentDescription">{content.description}</p>
20
- )}
21
- {content.image && (
22
- <Image
23
- className="document-image"
24
- src={content.image.scales.thumb.download}
25
- floated="right"
26
- />
27
- )}
28
- {content.text && (
29
- <div
30
- dangerouslySetInnerHTML={{
31
- __html: flattenHTMLToAppURL(content.text.data),
32
- }}
33
- />
34
- )}
35
- </React.Fragment>
36
- );
15
+ const EventTextfieldView = ({ content }) => {
16
+ const Image = config.getComponent({ name: 'Image' }).component;
17
+
18
+ return (
19
+ <React.Fragment>
20
+ {content.title && (
21
+ <h1 className="documentFirstHeading">{content.title}</h1>
22
+ )}
23
+ {content.description && (
24
+ <p className="documentDescription">{content.description}</p>
25
+ )}
26
+ {content.image && (
27
+ <Image
28
+ className="document-image ui right floated image"
29
+ item={content}
30
+ imageField="image"
31
+ alt=""
32
+ />
33
+ )}
34
+ {content.text && (
35
+ <div
36
+ dangerouslySetInnerHTML={{
37
+ __html: flattenHTMLToAppURL(content.text.data),
38
+ }}
39
+ />
40
+ )}
41
+ </React.Fragment>
42
+ );
43
+ };
37
44
 
38
45
  /**
39
46
  * EventView view component class.
@@ -18,6 +18,7 @@ import config from '@plone/volto/registry';
18
18
  * @returns {string} Markup of the component.
19
19
  */
20
20
  const ImageView = ({ content }) => {
21
+ const Image = config.getComponent({ name: 'Image' }).component;
21
22
  const Container =
22
23
  config.getComponent({ name: 'Container' }).component || SemanticContainer;
23
24
 
@@ -32,9 +33,11 @@ const ImageView = ({ content }) => {
32
33
  )}
33
34
  {content?.image?.download && (
34
35
  <a href={flattenToAppURL(content.image.download)}>
35
- <img
36
+ <Image
37
+ item={content}
38
+ imageField="image"
36
39
  alt={content.title}
37
- src={flattenToAppURL(content.image.scales.preview.download)}
40
+ responsive={true}
38
41
  />
39
42
  <figcaption>
40
43
  <FormattedMessage
@@ -23,9 +23,13 @@ test('renders an image view component', () => {
23
23
  image: {
24
24
  size: 123123,
25
25
  download: 'file:///preview.jpg',
26
+ width: 400,
27
+ height: 400,
26
28
  scales: {
27
29
  preview: {
28
30
  download: 'file:///preview.jpg',
31
+ width: 400,
32
+ height: 400,
29
33
  },
30
34
  },
31
35
  },
@@ -6,7 +6,7 @@
6
6
  import React from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
  import { Segment, Container as SemanticContainer } from 'semantic-ui-react';
9
- import { UniversalLink, PreviewImage } from '@plone/volto/components';
9
+ import { UniversalLink } from '@plone/volto/components';
10
10
  import config from '@plone/volto/registry';
11
11
 
12
12
  /**
@@ -18,6 +18,7 @@ import config from '@plone/volto/registry';
18
18
  const ListingView = ({ content }) => {
19
19
  const Container =
20
20
  config.getComponent({ name: 'Container' }).component || SemanticContainer;
21
+ const PreviewImage = config.getComponent({ name: 'PreviewImage' }).component;
21
22
 
22
23
  return (
23
24
  <Container id="page-home">
@@ -35,9 +36,10 @@ const ListingView = ({ content }) => {
35
36
  {item.image_field && (
36
37
  <PreviewImage
37
38
  item={item}
38
- size="thumb"
39
- alt={item.image_caption ? item.image_caption : item.title}
39
+ alt={item.image_caption}
40
40
  className="ui image"
41
+ responsive={true}
42
+ loading="lazy"
41
43
  />
42
44
  )}
43
45
  </Segment>
@@ -5,12 +5,8 @@
5
5
 
6
6
  import React from 'react';
7
7
  import PropTypes from 'prop-types';
8
- import { Container as SemanticContainer, Image } from 'semantic-ui-react';
9
- import {
10
- hasBlocksData,
11
- flattenToAppURL,
12
- flattenHTMLToAppURL,
13
- } from '@plone/volto/helpers';
8
+ import { Container as SemanticContainer } from 'semantic-ui-react';
9
+ import { hasBlocksData, flattenHTMLToAppURL } from '@plone/volto/helpers';
14
10
  import RenderBlocks from '@plone/volto/components/theme/View/RenderBlocks';
15
11
  import config from '@plone/volto/registry';
16
12
 
@@ -21,6 +17,7 @@ import config from '@plone/volto/registry';
21
17
  * @returns {string} Markup of the component.
22
18
  */
23
19
  const NewsItemView = ({ content }) => {
20
+ const Image = config.getComponent({ name: 'Image' }).component;
24
21
  const Container =
25
22
  config.getComponent({ name: 'Container' }).component || SemanticContainer;
26
23
 
@@ -41,15 +38,12 @@ const NewsItemView = ({ content }) => {
41
38
  )}
42
39
  {content.image && (
43
40
  <Image
44
- className="documentImage"
41
+ className="documentImage ui right floated image"
45
42
  alt={content.title}
46
43
  title={content.title}
47
- src={
48
- content.image['content-type'] === 'image/svg+xml'
49
- ? flattenToAppURL(content.image.download)
50
- : flattenToAppURL(content.image.scales.mini.download)
51
- }
52
- floated="right"
44
+ item={content}
45
+ imageField="image"
46
+ responsive={true}
53
47
  />
54
48
  )}
55
49
  {content.text && (
@@ -8,7 +8,6 @@ import PropTypes from 'prop-types';
8
8
  import { UniversalLink } from '@plone/volto/components';
9
9
  import { Container as SemanticContainer } from 'semantic-ui-react';
10
10
  import { FormattedMessage } from 'react-intl';
11
- import PreviewImage from '../PreviewImage/PreviewImage';
12
11
  import config from '@plone/volto/registry';
13
12
 
14
13
  /**
@@ -20,6 +19,7 @@ import config from '@plone/volto/registry';
20
19
  const SummaryView = ({ content }) => {
21
20
  const Container =
22
21
  config.getComponent({ name: 'Container' }).component || SemanticContainer;
22
+ const PreviewImage = config.getComponent({ name: 'PreviewImage' }).component;
23
23
 
24
24
  return (
25
25
  <Container className="view-wrapper summary-view">
@@ -41,9 +41,10 @@ const SummaryView = ({ content }) => {
41
41
  {item.image_field && (
42
42
  <PreviewImage
43
43
  item={item}
44
- alt={item.image_caption ? item.image_caption : item.title}
45
- size="thumb"
44
+ alt={item.image_caption}
46
45
  className="ui image floated right clear"
46
+ responsive={true}
47
+ loading="lazy"
47
48
  />
48
49
  )}
49
50
  {item.description && <p>{item.description}</p>}
@@ -75,6 +75,7 @@ import {
75
75
  DateRangeFacetFilterListEntry,
76
76
  } from '@plone/volto/components/manage/Blocks/Search/components';
77
77
  import getListingBlockAsyncData from '@plone/volto/components/manage/Blocks/Listing/getAsyncData';
78
+ import { getImageBlockSizes } from '@plone/volto/components/manage/Blocks/Image/utils';
78
79
 
79
80
  // block sidebar schemas (not the Dexterity Layout block settings schemas)
80
81
  import HeroImageLeftBlockSchema from '@plone/volto/components/manage/Blocks/HeroImageLeft/schema';
@@ -258,6 +259,7 @@ const blocksConfig = {
258
259
  restricted: false,
259
260
  mostUsed: true,
260
261
  sidebarTab: 1,
262
+ getSizes: getImageBlockSizes,
261
263
  },
262
264
  leadimage: {
263
265
  id: 'leadimage',
@@ -1,6 +1,7 @@
1
- import { App, PreviewImage } from '@plone/volto/components';
1
+ import { App, PreviewImage, Image } from '@plone/volto/components';
2
2
 
3
3
  export const components = {
4
4
  PreviewImage: { component: PreviewImage },
5
5
  App: { component: App },
6
+ Image: { component: Image },
6
7
  };
@@ -3,7 +3,7 @@
3
3
  * @module helpers/Url
4
4
  */
5
5
 
6
- import { last, memoize } from 'lodash';
6
+ import { last, memoize, isArray, isObject, isString } from 'lodash';
7
7
  import { urlRegex, telRegex, mailRegex } from './urlRegex';
8
8
  import prependHttp from 'prepend-http';
9
9
  import config from '@plone/volto/registry';
@@ -251,6 +251,27 @@ export function isUrl(url) {
251
251
  return urlRegex().test(url);
252
252
  }
253
253
 
254
+ /**
255
+ * Get field url
256
+ * @method getFieldURL
257
+ * @param {object} data
258
+ * @returns {string | any} URL string value if field is of url type or any.
259
+ */
260
+ export const getFieldURL = (data) => {
261
+ let url = data;
262
+ const _isObject = data && isObject(data) && !isArray(data);
263
+ if (_isObject && data['@type'] === 'URL') {
264
+ url = data['value'] ?? data['url'] ?? data['href'] ?? data;
265
+ } else if (_isObject) {
266
+ url = data['@id'] ?? data['url'] ?? data['href'] ?? data;
267
+ }
268
+ if (isArray(data)) {
269
+ url = data.map((item) => getFieldURL(item));
270
+ }
271
+ if (isString(url) && isInternalURL(url)) return flattenToAppURL(url);
272
+ return url;
273
+ };
274
+
254
275
  /**
255
276
  * Normalize URL, adds protocol (if required eg. user has not entered the protocol)
256
277
  * @method normalizeUrl