@plone/volto 16.21.2 → 16.22.0

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 (66) hide show
  1. package/.changelog.draft +28 -3
  2. package/.yarn/install-state.gz +0 -0
  3. package/CHANGELOG.md +42 -0
  4. package/cypress/support/commands.js +17 -0
  5. package/locales/it/LC_MESSAGES/volto.po +1 -1
  6. package/locales/it.json +1 -1
  7. package/package.json +1 -1
  8. package/packages/volto-slate/package.json +1 -1
  9. package/packages/volto-slate/src/actions/index.js +1 -1
  10. package/packages/volto-slate/src/blocks/Table/TableBlockEdit.jsx +21 -212
  11. package/packages/volto-slate/src/blocks/Table/schema.js +122 -0
  12. package/packages/volto-slate/src/blocks/Text/index.js +2 -2
  13. package/packages/volto-slate/src/editor/index.js +4 -4
  14. package/packages/volto-slate/src/editor/plugins/StyleMenu/utils.js +14 -5
  15. package/packages/volto-slate/src/editor/ui/SlateContextToolbar.jsx +2 -2
  16. package/packages/volto-slate/src/editor/ui/index.js +15 -15
  17. package/packages/volto-slate/src/index.js +2 -2
  18. package/packages/volto-slate/src/utils/blocks.js +7 -0
  19. package/packages/volto-slate/src/widgets/RichTextWidget.jsx +15 -8
  20. package/src/components/manage/AnchorPlugin/index.jsx +2 -2
  21. package/src/components/manage/AnchorPlugin/utils/EditorUtils.js +3 -1
  22. package/src/components/manage/Blocks/Block/Style.jsx +2 -2
  23. package/src/components/manage/Blocks/Listing/getAsyncData.js +7 -2
  24. package/src/components/manage/Blocks/Listing/withQuerystringResults.jsx +3 -4
  25. package/src/components/manage/Blocks/Search/components/Facets.jsx +6 -2
  26. package/src/components/manage/Blocks/Search/components/index.js +13 -13
  27. package/src/components/manage/Blocks/Search/hocs/index.js +2 -2
  28. package/src/components/manage/Blocks/Search/hocs/withQueryString.jsx +2 -2
  29. package/src/components/manage/Blocks/ToC/Schema.jsx +5 -1
  30. package/src/components/manage/Blocks/ToC/variations/HorizontalMenu.jsx +142 -8
  31. package/src/components/manage/Blocks/ToC/variations/index.js +3 -1
  32. package/src/components/manage/Contents/Contents.jsx +8 -6
  33. package/src/components/manage/UniversalLink/UniversalLink.jsx +2 -6
  34. package/src/components/manage/UniversalLink/UniversalLink.test.jsx +36 -0
  35. package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.jsx +1 -1
  36. package/src/components/theme/Login/Login.jsx +1 -1
  37. package/src/components/theme/View/AlbumView.jsx +9 -1
  38. package/src/components/theme/View/EventView.jsx +6 -2
  39. package/src/components/theme/View/FileView.jsx +23 -18
  40. package/src/components/theme/View/ImageView.jsx +37 -32
  41. package/src/components/theme/View/LinkView.jsx +4 -1
  42. package/src/components/theme/View/ListingView.jsx +33 -27
  43. package/src/components/theme/View/SummaryView.jsx +47 -38
  44. package/src/components/theme/View/TabularView.jsx +59 -53
  45. package/src/config/RichTextEditor/Blocks.jsx +2 -2
  46. package/src/config/RichTextEditor/FromHTML.jsx +2 -2
  47. package/src/config/RichTextEditor/Styles.jsx +1 -1
  48. package/src/config/index.js +1 -0
  49. package/src/config/server.js +2 -0
  50. package/src/constants/Indexes.js +3 -1
  51. package/src/express-middleware/devproxy.js +1 -1
  52. package/src/express-middleware/files.js +3 -3
  53. package/src/express-middleware/images.js +4 -4
  54. package/src/express-middleware/ok.js +16 -0
  55. package/src/express-middleware/robotstxt.js +1 -1
  56. package/src/express-middleware/sitemap.js +1 -1
  57. package/src/express-middleware/static.js +3 -3
  58. package/src/helpers/Extensions/index.js +2 -1
  59. package/src/helpers/Url/Url.js +22 -1
  60. package/src/helpers/Url/Url.test.js +41 -0
  61. package/src/helpers/index.js +9 -10
  62. package/src/middleware/index.js +2 -2
  63. package/src/start-server.js +2 -2
  64. package/theme/themes/pastanaga/extras/blocks.less +3 -1
  65. package/theme/themes/pastanaga/extras/main.less +2 -1
  66. package/theme/themes/pastanaga/extras/toc.less +29 -0
@@ -5,8 +5,9 @@
5
5
 
6
6
  import React from 'react';
7
7
  import PropTypes from 'prop-types';
8
- import { Segment, Container } from 'semantic-ui-react';
8
+ import { Segment, Container as SemanticContainer } from 'semantic-ui-react';
9
9
  import { UniversalLink, PreviewImage } from '@plone/volto/components';
10
+ import config from '@plone/volto/registry';
10
11
 
11
12
  /**
12
13
  * List view component class.
@@ -14,32 +15,37 @@ import { UniversalLink, PreviewImage } from '@plone/volto/components';
14
15
  * @params {object} content Content object.
15
16
  * @returns {string} Markup of the component.
16
17
  */
17
- const ListingView = ({ content }) => (
18
- <Container id="page-home">
19
- <section id="content-core">
20
- {content.items.map((item) => (
21
- <Segment key={item.url} className="listing-item">
22
- <Container>
23
- <h2>
24
- <UniversalLink item={item} title={item['@type']}>
25
- {item.title}
26
- </UniversalLink>
27
- </h2>
28
- {item.description && <p>{item.description}</p>}
29
- </Container>
30
- {item.image_field && (
31
- <PreviewImage
32
- item={item}
33
- size="thumb"
34
- alt={item.image_caption ? item.image_caption : item.title}
35
- className="ui image"
36
- />
37
- )}
38
- </Segment>
39
- ))}
40
- </section>
41
- </Container>
42
- );
18
+ const ListingView = ({ content }) => {
19
+ const Container =
20
+ config.getComponent({ name: 'Container' }).component || SemanticContainer;
21
+
22
+ return (
23
+ <Container id="page-home">
24
+ <section id="content-core">
25
+ {content.items.map((item) => (
26
+ <Segment key={item.url} className="listing-item">
27
+ <Container>
28
+ <h2>
29
+ <UniversalLink item={item} title={item['@type']}>
30
+ {item.title}
31
+ </UniversalLink>
32
+ </h2>
33
+ {item.description && <p>{item.description}</p>}
34
+ </Container>
35
+ {item.image_field && (
36
+ <PreviewImage
37
+ item={item}
38
+ size="thumb"
39
+ alt={item.image_caption ? item.image_caption : item.title}
40
+ className="ui image"
41
+ />
42
+ )}
43
+ </Segment>
44
+ ))}
45
+ </section>
46
+ </Container>
47
+ );
48
+ };
43
49
 
44
50
  /**
45
51
  * Property types.
@@ -6,9 +6,10 @@
6
6
  import React from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
  import { UniversalLink } from '@plone/volto/components';
9
- import { Container } from 'semantic-ui-react';
9
+ import { Container as SemanticContainer } from 'semantic-ui-react';
10
10
  import { FormattedMessage } from 'react-intl';
11
11
  import PreviewImage from '../PreviewImage/PreviewImage';
12
+ import config from '@plone/volto/registry';
12
13
 
13
14
  /**
14
15
  * Summary view component class.
@@ -16,43 +17,51 @@ import PreviewImage from '../PreviewImage/PreviewImage';
16
17
  * @param {Object} content Content object.
17
18
  * @returns {string} Markup of the component.
18
19
  */
19
- const SummaryView = ({ content }) => (
20
- <Container className="view-wrapper summary-view">
21
- <article id="content">
22
- <header>
23
- <h1 className="documentFirstHeading">{content.title}</h1>
24
- {content.description && (
25
- <p className="documentDescription">{content.description}</p>
26
- )}
27
- </header>
28
- <section id="content-core">
29
- {content.items.map((item) => (
30
- <article key={item.url}>
31
- <h2>
32
- <UniversalLink item={item} title={item['@type']}>
33
- {item.title}
34
- </UniversalLink>
35
- </h2>
36
- {item.image_field && (
37
- <PreviewImage
38
- item={item}
39
- alt={item.image_caption ? item.image_caption : item.title}
40
- size="thumb"
41
- className="ui image floated right clear"
42
- />
43
- )}
44
- {item.description && <p>{item.description}</p>}
45
- <p>
46
- <UniversalLink item={item}>
47
- <FormattedMessage id="Read More…" defaultMessage="Read More…" />
48
- </UniversalLink>
49
- </p>
50
- </article>
51
- ))}
52
- </section>
53
- </article>
54
- </Container>
55
- );
20
+ const SummaryView = ({ content }) => {
21
+ const Container =
22
+ config.getComponent({ name: 'Container' }).component || SemanticContainer;
23
+
24
+ return (
25
+ <Container className="view-wrapper summary-view">
26
+ <article id="content">
27
+ <header>
28
+ <h1 className="documentFirstHeading">{content.title}</h1>
29
+ {content.description && (
30
+ <p className="documentDescription">{content.description}</p>
31
+ )}
32
+ </header>
33
+ <section id="content-core">
34
+ {content.items.map((item) => (
35
+ <article key={item.url}>
36
+ <h2>
37
+ <UniversalLink item={item} title={item['@type']}>
38
+ {item.title}
39
+ </UniversalLink>
40
+ </h2>
41
+ {item.image_field && (
42
+ <PreviewImage
43
+ item={item}
44
+ alt={item.image_caption ? item.image_caption : item.title}
45
+ size="thumb"
46
+ className="ui image floated right clear"
47
+ />
48
+ )}
49
+ {item.description && <p>{item.description}</p>}
50
+ <p>
51
+ <UniversalLink item={item}>
52
+ <FormattedMessage
53
+ id="Read More…"
54
+ defaultMessage="Read More…"
55
+ />
56
+ </UniversalLink>
57
+ </p>
58
+ </article>
59
+ ))}
60
+ </section>
61
+ </article>
62
+ </Container>
63
+ );
64
+ };
56
65
 
57
66
  /**
58
67
  * Property types.
@@ -6,8 +6,9 @@
6
6
  import React from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
  import { UniversalLink } from '@plone/volto/components';
9
- import { Container, Table } from 'semantic-ui-react';
9
+ import { Container as SemanticContainer, Table } from 'semantic-ui-react';
10
10
  import { FormattedMessage } from 'react-intl';
11
+ import config from '@plone/volto/registry';
11
12
 
12
13
  /**
13
14
  * Tabular view component class.
@@ -15,59 +16,64 @@ import { FormattedMessage } from 'react-intl';
15
16
  * @param {Object} content Content object.
16
17
  * @returns {string} Markup of the component.
17
18
  */
18
- const TabularView = ({ content }) => (
19
- <Container className="view-wrapper">
20
- <article id="content">
21
- <header>
22
- <h1 className="documentFirstHeading">{content.title}</h1>
23
- {content.description && (
24
- <p className="documentDescription">{content.description}</p>
25
- )}
26
- </header>
27
- <section id="content-core">
28
- <Table celled padded>
29
- <Table.Header>
30
- <Table.Row>
31
- <Table.HeaderCell>
32
- <FormattedMessage id="Title" defaultMessage="Title" />
33
- </Table.HeaderCell>
34
- <Table.HeaderCell>
35
- <FormattedMessage
36
- id="Description"
37
- defaultMessage="Description"
38
- />
39
- </Table.HeaderCell>
40
- <Table.HeaderCell>
41
- <FormattedMessage id="Type" defaultMessage="Type" />
42
- </Table.HeaderCell>
43
- <Table.HeaderCell>
44
- <FormattedMessage id="State" defaultMessage="State" />
45
- </Table.HeaderCell>
46
- </Table.Row>
47
- </Table.Header>
48
- <Table.Body>
49
- {content.items.map((item) => (
50
- <Table.Row key={item.url}>
51
- <Table.Cell>
52
- <UniversalLink
53
- item={item}
54
- className="summary url"
55
- title={item['@type']}
56
- >
57
- {item.title}
58
- </UniversalLink>
59
- </Table.Cell>
60
- <Table.Cell>{item.description}</Table.Cell>
61
- <Table.Cell>{item['@type']}</Table.Cell>
62
- <Table.Cell>{item.review_state}</Table.Cell>
19
+ const TabularView = ({ content }) => {
20
+ const Container =
21
+ config.getComponent({ name: 'Container' }).component || SemanticContainer;
22
+
23
+ return (
24
+ <Container className="view-wrapper">
25
+ <article id="content">
26
+ <header>
27
+ <h1 className="documentFirstHeading">{content.title}</h1>
28
+ {content.description && (
29
+ <p className="documentDescription">{content.description}</p>
30
+ )}
31
+ </header>
32
+ <section id="content-core">
33
+ <Table celled padded>
34
+ <Table.Header>
35
+ <Table.Row>
36
+ <Table.HeaderCell>
37
+ <FormattedMessage id="Title" defaultMessage="Title" />
38
+ </Table.HeaderCell>
39
+ <Table.HeaderCell>
40
+ <FormattedMessage
41
+ id="Description"
42
+ defaultMessage="Description"
43
+ />
44
+ </Table.HeaderCell>
45
+ <Table.HeaderCell>
46
+ <FormattedMessage id="Type" defaultMessage="Type" />
47
+ </Table.HeaderCell>
48
+ <Table.HeaderCell>
49
+ <FormattedMessage id="State" defaultMessage="State" />
50
+ </Table.HeaderCell>
63
51
  </Table.Row>
64
- ))}
65
- </Table.Body>
66
- </Table>
67
- </section>
68
- </article>
69
- </Container>
70
- );
52
+ </Table.Header>
53
+ <Table.Body>
54
+ {content.items.map((item) => (
55
+ <Table.Row key={item.url}>
56
+ <Table.Cell>
57
+ <UniversalLink
58
+ item={item}
59
+ className="summary url"
60
+ title={item['@type']}
61
+ >
62
+ {item.title}
63
+ </UniversalLink>
64
+ </Table.Cell>
65
+ <Table.Cell>{item.description}</Table.Cell>
66
+ <Table.Cell>{item['@type']}</Table.Cell>
67
+ <Table.Cell>{item.review_state}</Table.Cell>
68
+ </Table.Row>
69
+ ))}
70
+ </Table.Body>
71
+ </Table>
72
+ </section>
73
+ </article>
74
+ </Container>
75
+ );
76
+ };
71
77
 
72
78
  /**
73
79
  * Property types.
@@ -1,4 +1,4 @@
1
- export default (props) => {
1
+ export default function Blocks(props) {
2
2
  const { draftJs, immutableLib } = props;
3
3
  const { DefaultDraftBlockRenderMap } = draftJs;
4
4
  const { Map } = immutableLib;
@@ -27,4 +27,4 @@ export default (props) => {
27
27
  const listBlockTypes = ['unordered-list-item', 'ordered-list-item'];
28
28
 
29
29
  return { extendedBlockRenderMap, blockStyleFn, listBlockTypes };
30
- };
30
+ }
@@ -1,8 +1,8 @@
1
- export default (element) => {
1
+ export default function FromHTMLCustomBlockFn(element) {
2
2
  if (element.className === 'callout') {
3
3
  return {
4
4
  type: 'callout',
5
5
  };
6
6
  }
7
7
  return null;
8
- };
8
+ }
@@ -13,7 +13,7 @@ import orderedListSVG from '@plone/volto/icons/list-numbered.svg';
13
13
  import blockquoteSVG from '@plone/volto/icons/quote.svg';
14
14
  import calloutSVG from '@plone/volto/icons/megaphone.svg';
15
15
 
16
- export default function (props) {
16
+ export default function Styles(props) {
17
17
  const createInlineStyleButton = props.draftJsCreateInlineStyleButton.default;
18
18
  const createBlockStyleButton = props.draftJsCreateBlockStyleButton.default;
19
19
 
@@ -74,6 +74,7 @@ let config = {
74
74
  port,
75
75
  // The URL Volto is going to be served (see sensible defaults above)
76
76
  publicURL,
77
+ okRoute: '/ok',
77
78
  apiPath,
78
79
  apiExpanders: [
79
80
  // Add the following expanders for only issuing a single request.
@@ -1,6 +1,7 @@
1
1
  import imagesMiddleware from '@plone/volto/express-middleware/images';
2
2
  import filesMiddleware from '@plone/volto/express-middleware/files';
3
3
  import robotstxtMiddleware from '@plone/volto/express-middleware/robotstxt';
4
+ import okMiddleware from '@plone/volto/express-middleware/ok';
4
5
  import sitemapMiddleware from '@plone/volto/express-middleware/sitemap';
5
6
  import staticsMiddleware from '@plone/volto/express-middleware/static';
6
7
  import devProxyMiddleware from '@plone/volto/express-middleware/devproxy';
@@ -11,6 +12,7 @@ const settings = {
11
12
  filesMiddleware(),
12
13
  imagesMiddleware(),
13
14
  robotstxtMiddleware(),
15
+ okMiddleware(),
14
16
  sitemapMiddleware(),
15
17
  staticsMiddleware(),
16
18
  ],
@@ -3,7 +3,7 @@
3
3
  * @module constants/indexes
4
4
  */
5
5
 
6
- export default {
6
+ const Indexes = {
7
7
  sortable_title: { label: 'Title', type: 'string', sort_on: 'sortable_title' },
8
8
  review_state: { label: 'Review state', type: 'string' },
9
9
  ModificationDate: {
@@ -35,6 +35,8 @@ export default {
35
35
  Type: { label: 'Type', type: 'string' },
36
36
  };
37
37
 
38
+ export default Indexes;
39
+
38
40
  export const defaultIndexes = [
39
41
  'review_state',
40
42
  'ModificationDate',
@@ -37,7 +37,7 @@ function getEnv() {
37
37
  return _env;
38
38
  }
39
39
 
40
- export default function () {
40
+ export default function devProxyMiddleware() {
41
41
  const middleware = express.Router();
42
42
  const devProxy = createProxyMiddleware(filter, {
43
43
  selfHandleResponse: true,
@@ -9,7 +9,7 @@ const HEADERS = [
9
9
  'Content-Type',
10
10
  ];
11
11
 
12
- function fileMiddleware(req, res, next) {
12
+ function filesMiddlewareFn(req, res, next) {
13
13
  getAPIResourceWithAuth(req)
14
14
  .then((resource) => {
15
15
  // Just forward the headers that we need
@@ -24,10 +24,10 @@ function fileMiddleware(req, res, next) {
24
24
  .catch(next);
25
25
  }
26
26
 
27
- export default function () {
27
+ export default function filesMiddleware() {
28
28
  const middleware = express.Router();
29
29
 
30
- middleware.all(['**/@@download/*', '**/@@display-file/*'], fileMiddleware);
30
+ middleware.all(['**/@@download/*', '**/@@display-file/*'], filesMiddlewareFn);
31
31
  middleware.id = 'filesResourcesProcessor';
32
32
  return middleware;
33
33
  }
@@ -3,7 +3,7 @@ import { getAPIResourceWithAuth } from '@plone/volto/helpers';
3
3
 
4
4
  const HEADERS = ['content-type', 'content-disposition', 'cache-control'];
5
5
 
6
- function imageMiddleware(req, res, next) {
6
+ function imageMiddlewareFn(req, res, next) {
7
7
  getAPIResourceWithAuth(req)
8
8
  .then((resource) => {
9
9
  // Just forward the headers that we need
@@ -17,11 +17,11 @@ function imageMiddleware(req, res, next) {
17
17
  .catch(next);
18
18
  }
19
19
 
20
- export default function () {
20
+ export default function imagesMiddleware() {
21
21
  const middleware = express.Router();
22
22
 
23
- middleware.all(['**/@@images/*'], imageMiddleware);
24
- middleware.all(['/@portrait/*'], imageMiddleware);
23
+ middleware.all(['**/@@images/*'], imageMiddlewareFn);
24
+ middleware.all(['/@portrait/*'], imageMiddlewareFn);
25
25
  middleware.id = 'imageResourcesProcessor';
26
26
  return middleware;
27
27
  }
@@ -0,0 +1,16 @@
1
+ import express from 'express';
2
+ import config from '@plone/volto/registry';
3
+
4
+ const ok = function (req, res, next) {
5
+ res.type('text/plain');
6
+ res.set('Expires', 'Sat, 1 Jan 2000 00:00:00 GMT');
7
+ res.set('Cache-Control', 'max-age=0, must-revalidate, private');
8
+ res.send('ok');
9
+ };
10
+
11
+ export default function () {
12
+ const middleware = express.Router();
13
+ middleware.all(config?.settings?.okRoute || '/ok', ok);
14
+ middleware.id = 'ok';
15
+ return middleware;
16
+ }
@@ -22,7 +22,7 @@ const envRobots = function (req, res, next) {
22
22
  res.send(process.env.VOLTO_ROBOTSTXT);
23
23
  };
24
24
 
25
- export default function () {
25
+ export default function robotstxtMiddleware() {
26
26
  const middleware = express.Router();
27
27
  if (process.env.VOLTO_ROBOTSTXT) {
28
28
  middleware.all('**/robots.txt', envRobots);
@@ -47,7 +47,7 @@ export const sitemapIndex = function (req, res, next) {
47
47
  });
48
48
  };
49
49
 
50
- export default function () {
50
+ export default function sitemapMiddleware() {
51
51
  const middleware = express.Router();
52
52
 
53
53
  middleware.all('**/sitemap.xml.gz', sitemap);
@@ -2,7 +2,7 @@ import express from 'express';
2
2
  import path from 'path';
3
3
  import config from '@plone/volto/registry';
4
4
 
5
- const staticMiddleware = express.static(
5
+ const staticMiddlewareFn = express.static(
6
6
  process.env.BUILD_DIR
7
7
  ? path.join(process.env.BUILD_DIR, 'public')
8
8
  : process.env.RAZZLE_PUBLIC_DIR,
@@ -24,9 +24,9 @@ const staticMiddleware = express.static(
24
24
  },
25
25
  );
26
26
 
27
- export default function () {
27
+ export default function staticsMiddleware() {
28
28
  const middleware = express.Router();
29
- middleware.all('*', staticMiddleware);
29
+ middleware.all('*', staticMiddlewareFn);
30
30
  middleware.id = 'staticResourcesProcessor';
31
31
  return middleware;
32
32
  }
@@ -1,5 +1,6 @@
1
1
  export * from './withBlockSchemaEnhancer';
2
- export withBlockExtensions, {
2
+ export {
3
+ default as withBlockExtensions,
3
4
  resolveExtension,
4
5
  resolveBlockExtensions,
5
6
  } from './withBlockExtensions';
@@ -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
@@ -10,6 +10,7 @@ import {
10
10
  isCmsUi,
11
11
  isInternalURL,
12
12
  isUrl,
13
+ getFieldURL,
13
14
  normalizeUrl,
14
15
  removeProtocol,
15
16
  addAppURL,
@@ -260,6 +261,46 @@ describe('Url', () => {
260
261
  expect(isUrl(href)).toBe(false);
261
262
  });
262
263
  });
264
+ describe('getFieldURL', () => {
265
+ it('returns app URL if the field is a string', () => {
266
+ const field = `${settings.apiPath}/foo/bar`;
267
+ expect(getFieldURL(field)).toBe('/foo/bar');
268
+ });
269
+ it('returns app URL if the field is an object with "@id"', () => {
270
+ const field = { '@id': '/foo/bar' };
271
+ expect(getFieldURL(field)).toBe('/foo/bar');
272
+ });
273
+ it('returns app URL if the field is an object with type URL', () => {
274
+ const field = { '@type': 'URL', value: '/foo/bar' };
275
+ expect(getFieldURL(field)).toBe('/foo/bar');
276
+ });
277
+ it('returns app URL if the field is an object with url or href properties', () => {
278
+ const fieldUrl = { url: '/foo/bar' };
279
+ const fieldHref = { href: '/foo/bar' };
280
+ expect(getFieldURL(fieldUrl)).toBe('/foo/bar');
281
+ expect(getFieldURL(fieldHref)).toBe('/foo/bar');
282
+ });
283
+ it('returns array of app URL if the field is an array of strings', () => {
284
+ const field = [
285
+ `${settings.apiPath}/foo/bar/1`,
286
+ `${settings.apiPath}/foo/bar/2`,
287
+ ];
288
+ expect(getFieldURL(field)).toStrictEqual(['/foo/bar/1', '/foo/bar/2']);
289
+ });
290
+ it('returns array of app URL if the field is an array of objects', () => {
291
+ const field = [
292
+ {
293
+ '@type': 'URL',
294
+ value: `${settings.apiPath}/foo/bar/1`,
295
+ },
296
+ {
297
+ '@type': 'URL',
298
+ value: `${settings.apiPath}/foo/bar/2`,
299
+ },
300
+ ];
301
+ expect(getFieldURL(field)).toStrictEqual(['/foo/bar/1', '/foo/bar/2']);
302
+ });
303
+ });
263
304
  describe('normalizeUrl', () => {
264
305
  it('normalizeUrl test', () => {
265
306
  const href = `www.example.com`;
@@ -5,9 +5,9 @@
5
5
  */
6
6
 
7
7
  // export { injectLazyLibs } from './Loadable/Loadable';
8
- export Api from '@plone/volto/helpers/Api/Api';
8
+ export { default as Api } from '@plone/volto/helpers/Api/Api';
9
9
  export { getAPIResourceWithAuth } from '@plone/volto/helpers/Api/APIResourceWithAuth';
10
- export Html from '@plone/volto/helpers/Html/Html';
10
+ export { default as Html } from '@plone/volto/helpers/Html/Html';
11
11
  export {
12
12
  getAuthToken,
13
13
  persistAuthToken,
@@ -59,8 +59,8 @@ export {
59
59
  buildStyleClassNamesExtenders,
60
60
  getPreviousNextBlock,
61
61
  } from '@plone/volto/helpers/Blocks/Blocks';
62
- export BodyClass from '@plone/volto/helpers/BodyClass/BodyClass';
63
- export ScrollToTop from '@plone/volto/helpers/ScrollToTop/ScrollToTop';
62
+ export { default as BodyClass } from '@plone/volto/helpers/BodyClass/BodyClass';
63
+ export { default as ScrollToTop } from '@plone/volto/helpers/ScrollToTop/ScrollToTop';
64
64
  export {
65
65
  getBoolean,
66
66
  getVocabName,
@@ -70,11 +70,10 @@ export {
70
70
  getFieldsVocabulary,
71
71
  } from '@plone/volto/helpers/Vocabularies/Vocabularies';
72
72
 
73
- export langmap from './LanguageMap/LanguageMap';
74
- export Helmet from './Helmet/Helmet';
75
- export FormValidation, {
76
- validateFileUploadSize,
77
- } from './FormValidation/FormValidation';
73
+ export { default as langmap } from './LanguageMap/LanguageMap';
74
+ export { default as Helmet } from './Helmet/Helmet';
75
+ export { default as FormValidation } from './FormValidation/FormValidation';
76
+ export { validateFileUploadSize } from './FormValidation/FormValidation';
78
77
  export {
79
78
  difference,
80
79
  getColor,
@@ -111,7 +110,7 @@ export { useDetectClickOutside } from './Utils/useDetectClickOutside';
111
110
  export { useEvent } from './Utils/useEvent';
112
111
  export { usePrevious } from './Utils/usePrevious';
113
112
  export { usePagination } from './Utils/usePagination';
114
- export useUndoManager from './UndoManager/useUndoManager';
113
+ export { default as useUndoManager } from './UndoManager/useUndoManager';
115
114
  export { getCookieOptions } from './Cookies/cookies';
116
115
  export { getWidgetView } from './Widget/widget';
117
116
  export {