@eeacms/volto-hero-block 7.1.0 → 8.0.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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,33 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
+ ### [8.0.0](https://github.com/eea/volto-hero-block/compare/7.1.0...8.0.0) - 28 July 2025
8
+
9
+ #### :house: Internal changes
10
+
11
+ - chore: package.json [alin - [`61b396a`](https://github.com/eea/volto-hero-block/commit/61b396a72309e7be8cab0d77e92d1159d93a4d70)]
12
+ - chore(release): use * for volto-object-widget instead of develop branch [David Ichim - [`075da00`](https://github.com/eea/volto-hero-block/commit/075da00049b8afc17e200369e544a11d37497d51)]
13
+ - chore: bump major version [Nilesh - [`df4edc7`](https://github.com/eea/volto-hero-block/commit/df4edc70703c74ae89dfcef6dfed28cb63835e02)]
14
+ - style: Automated code fix [eea-jenkins - [`7de43a1`](https://github.com/eea/volto-hero-block/commit/7de43a1730c18530d67cee67b573835eb5457dfe)]
15
+ - style: Automated code fix [eea-jenkins - [`34d6a07`](https://github.com/eea/volto-hero-block/commit/34d6a07ad2d2ef83ef2a4c8c12e5343ca34ac810)]
16
+
17
+ #### :hammer_and_wrench: Others
18
+
19
+ - rafactor: do not preload the image as the LCP improvement is negligible [nileshgulia1 - [`dd49a13`](https://github.com/eea/volto-hero-block/commit/dd49a13ddb538d18278631eb46134926e384963d)]
20
+ - revert to background image with image preload [vladcalin-edw - [`34a9533`](https://github.com/eea/volto-hero-block/commit/34a953327e9778c4a34c8c1b587cfe179b0f05ff)]
21
+ - passing tests [vladcalin-edw - [`840ad7b`](https://github.com/eea/volto-hero-block/commit/840ad7b05395c913d712bb90eb64cc3e129f8646)]
22
+ - removed console [vladcalin-edw - [`b47d199`](https://github.com/eea/volto-hero-block/commit/b47d1994fce50a5715580c117775e365ac9ab16c)]
23
+ - fix pipeline and removed alt [vladcalin-edw - [`2183529`](https://github.com/eea/volto-hero-block/commit/218352982b9d2280073ae4b291efb6c843a57ae8)]
24
+ - fix pipeline [vladcalin-edw - [`6be0b71`](https://github.com/eea/volto-hero-block/commit/6be0b71d339c0ae8960b704faa10a9779c3705b0)]
25
+ - conditional image and using Image from core [vladcalin-edw - [`9000d76`](https://github.com/eea/volto-hero-block/commit/9000d7653bc515ec20c1ba96ed78a975a1cd4990)]
26
+ - cleanup [vladcalin-edw - [`167c2b7`](https://github.com/eea/volto-hero-block/commit/167c2b7775016a8247671319f41d654a5fe7d692)]
27
+ - modified Makefile and pipeline pass [vladcalin-edw - [`b7f6d1d`](https://github.com/eea/volto-hero-block/commit/b7f6d1db006d70c45c1bfb00b906f416d8b48f1c)]
28
+ - removed preload, added Image from core, lazy to eager [vladcalin-edw - [`39c4fd1`](https://github.com/eea/volto-hero-block/commit/39c4fd1c120130d65724916ed9e1419c9764d881)]
29
+ - updated tests [vladcalin-edw - [`0a9f94d`](https://github.com/eea/volto-hero-block/commit/0a9f94da3c9089ddd7d3b8ebc60eaf54500b43e1)]
30
+ - passing pipeline [vladcalin-edw - [`3cf0b98`](https://github.com/eea/volto-hero-block/commit/3cf0b988beadc1c7238bceb62cb4ae1328e9eb92)]
31
+ - small fixes [vladcalin-edw - [`e45156f`](https://github.com/eea/volto-hero-block/commit/e45156f7592690b9f06113b799c87cd6d49f6316)]
32
+ - switched to img [vladcalin-edw - [`51e9362`](https://github.com/eea/volto-hero-block/commit/51e9362d8cb04894be032fd602df270dcc2c441f)]
33
+ - background image preload [vladcalin-edw - [`f5b2e20`](https://github.com/eea/volto-hero-block/commit/f5b2e205ae93d4d51b73fd2c6a05659184150f39)]
7
34
  ### [7.1.0](https://github.com/eea/volto-hero-block/compare/7.0.1...7.1.0) - 28 June 2024
8
35
 
9
36
  #### :bug: Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-hero-block",
3
- "version": "7.1.0",
3
+ "version": "8.0.0",
4
4
  "description": "@eeacms/volto-hero-block: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -15,8 +15,7 @@ import {
15
15
  UniversalLink,
16
16
  } from '@plone/volto/components';
17
17
  import { BodyClass } from '@plone/volto/helpers';
18
-
19
- import { getFieldURL } from '@eeacms/volto-hero-block/helpers';
18
+ import { getFieldURL } from '@plone/volto/helpers/Url/Url';
20
19
  import { HeroBlockSchema } from './schema';
21
20
  import Copyright from './Copyright';
22
21
  import Hero from './Hero';
@@ -117,7 +116,7 @@ export default function Edit(props) {
117
116
  allowedBlocks={'slate'}
118
117
  selectedBlock={selectedBlock}
119
118
  title={data.placeholder}
120
- onSelectBlock={(s, e) => {
119
+ onSelectBlock={(s) => {
121
120
  setSelectedBlock(s);
122
121
  }}
123
122
  onChangeFormData={(newFormData) => {
@@ -1,21 +1,30 @@
1
1
  import React from 'react';
2
2
  import { render } from '@testing-library/react';
3
3
  import { Provider } from 'react-redux';
4
+ import { IntlProvider } from 'react-intl';
4
5
  import configureStore from 'redux-mock-store';
5
6
  import Edit from './Edit';
6
7
  import config from '@plone/volto/registry';
7
8
  import '@testing-library/jest-dom/extend-expect';
8
9
 
10
+ // Mock uuid to avoid node:crypto import issues
11
+ jest.mock('uuid', () => ({
12
+ v4: () => 'mock-uuid-' + Math.random().toString(36).substr(2, 9),
13
+ }));
14
+
9
15
  const mockStore = configureStore([]);
10
16
  const observe = jest.fn();
11
17
  const unobserve = jest.fn();
18
+ const disconnect = jest.fn();
12
19
  jest.mock('@plone/volto/components', () => {
13
20
  return {
14
21
  __esModule: true,
15
22
  BlocksForm: ({ placeholder, children, onChange, onFocus }) => (
16
23
  <div id="test">
17
24
  <div>{placeholder}</div>
18
- {children}
25
+ {typeof children === 'function'
26
+ ? children({}, <div>Mock Edit Block</div>, {})
27
+ : children}
19
28
  </div>
20
29
  ),
21
30
  SidebarPortal: ({ children }) => <div>{children}</div>,
@@ -24,6 +33,15 @@ jest.mock('@plone/volto/components', () => {
24
33
  RenderBlocks: () => <div></div>,
25
34
  };
26
35
  });
36
+
37
+ jest.mock(
38
+ '@plone/volto/components/manage/Blocks/Block/EditBlockWrapper',
39
+ () => {
40
+ return ({ children }) => (
41
+ <div className="edit-block-wrapper">{children}</div>
42
+ );
43
+ },
44
+ );
27
45
  jest.mock('react-router-dom', () => ({
28
46
  useLocation: jest.fn().mockReturnValue({
29
47
  pathname: '/test-jest',
@@ -34,9 +52,10 @@ jest.mock('react-router-dom', () => ({
34
52
  }),
35
53
  }));
36
54
 
37
- window.IntersectionObserver = jest.fn((callback) => ({
55
+ window.IntersectionObserver = jest.fn(() => ({
38
56
  observe,
39
57
  unobserve,
58
+ disconnect,
40
59
  }));
41
60
 
42
61
  config.blocks = {
@@ -45,6 +64,7 @@ config.blocks = {
45
64
  copyrightPrefix: 'Test Prefix',
46
65
  schema: {
47
66
  title: 'Hero',
67
+ required: [],
48
68
  },
49
69
  },
50
70
  },
@@ -53,6 +73,7 @@ config.settings = {
53
73
  slate: {
54
74
  textblockExtensions: [],
55
75
  },
76
+ themeColors: [],
56
77
  };
57
78
 
58
79
  describe('Edit component', () => {
@@ -67,9 +88,11 @@ describe('Edit component', () => {
67
88
 
68
89
  it('renders without crashing', () => {
69
90
  const { container } = render(
70
- <Provider store={store}>
71
- <Edit onChangeBlock={() => {}} onSelectBlock={() => {}} />
72
- </Provider>,
91
+ <IntlProvider locale="en" messages={{}}>
92
+ <Provider store={store}>
93
+ <Edit onChangeBlock={() => {}} onSelectBlock={() => {}} />
94
+ </Provider>
95
+ </IntlProvider>,
73
96
  );
74
97
  expect(container).toBeTruthy();
75
98
  });
@@ -89,9 +112,11 @@ describe('Edit component', () => {
89
112
  };
90
113
 
91
114
  const { container } = render(
92
- <Provider store={store}>
93
- <Edit data={data} onChangeBlock={() => {}} />
94
- </Provider>,
115
+ <IntlProvider locale="en" messages={{}}>
116
+ <Provider store={store}>
117
+ <Edit data={data} onChangeBlock={() => {}} />
118
+ </Provider>
119
+ </IntlProvider>,
95
120
  );
96
121
 
97
122
  expect(container.querySelector('#test')).toBeInTheDocument();
@@ -103,15 +128,18 @@ describe('Edit component', () => {
103
128
  hero: {
104
129
  schema: () => ({
105
130
  title: 'Hero',
131
+ required: [],
106
132
  }),
107
133
  },
108
134
  },
109
135
  };
110
136
  const onSelectBlock = jest.fn();
111
137
  render(
112
- <Provider store={store}>
113
- <Edit onSelectBlock={onSelectBlock} onChangeBlock={() => {}} />
114
- </Provider>,
138
+ <IntlProvider locale="en" messages={{}}>
139
+ <Provider store={store}>
140
+ <Edit onSelectBlock={onSelectBlock} onChangeBlock={() => {}} />
141
+ </Provider>
142
+ </IntlProvider>,
115
143
  );
116
144
  });
117
145
  });
@@ -1,12 +1,11 @@
1
- import React from 'react';
1
+ import React, { useMemo, useRef } from 'react';
2
2
  import cx from 'classnames';
3
3
  import PropTypes from 'prop-types';
4
- import { isInternalURL } from '@plone/volto/helpers/Url/Url';
5
- import { isImageGif, getFieldURL } from '@eeacms/volto-hero-block/helpers';
4
+ import { getImageScaleParams } from '@eeacms/volto-object-widget/helpers';
6
5
  import { useFirstVisited } from '@eeacms/volto-hero-block/hooks';
7
6
 
8
7
  Hero.propTypes = {
9
- image: PropTypes.string,
8
+ image: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
10
9
  fullWidth: PropTypes.bool,
11
10
  fullHeight: PropTypes.bool,
12
11
  alignContent: PropTypes.string,
@@ -18,46 +17,48 @@ Hero.propTypes = {
18
17
  textVariant: PropTypes.string,
19
18
  };
20
19
 
21
- function Hero({
22
- overlay = true,
23
- fullWidth = true,
24
- fullHeight = true,
25
- children,
26
- spaced = false,
27
- inverted = true,
28
- styles,
29
- height,
30
- ...props
31
- }) {
32
- const image = getFieldURL(props.image);
33
- const isExternal = !isInternalURL(image);
20
+ function Hero(props) {
21
+ const {
22
+ image,
23
+ fullWidth = false,
24
+ fullHeight = false,
25
+ height,
26
+ styles,
27
+ overlay = true,
28
+ children,
29
+ spaced = false,
30
+ inverted = false,
31
+ } = props;
32
+
33
+ const scaledImage = useMemo(
34
+ () => (image ? getImageScaleParams(image, 'huge') : null),
35
+ [image],
36
+ );
34
37
  const { alignContent = 'center', backgroundVariant = 'primary' } =
35
38
  styles || {};
36
- const bgImgRef = React.useRef();
39
+ const bgImgRef = useRef();
37
40
  const onScreen = useFirstVisited(bgImgRef);
38
- const containerCssStyles = React.useMemo(
41
+ const containerCssStyles = useMemo(
39
42
  () => ({
40
43
  ...(height && !fullHeight ? { height } : {}),
41
44
  }),
42
45
  [height, fullHeight],
43
46
  );
44
-
45
- const backgroundImageStyle =
46
- onScreen && image
47
- ? {
48
- backgroundImage: isExternal
49
- ? `url(${image})`
50
- : isImageGif(image)
51
- ? `url(${image}/@@images/image)`
52
- : `url(${image}/@@images/image/huge)`,
53
- }
54
- : {};
47
+ const backgroundImageStyle = useMemo(
48
+ () =>
49
+ onScreen && scaledImage
50
+ ? {
51
+ backgroundImage: `url(${scaledImage.download})`,
52
+ }
53
+ : {},
54
+ [onScreen, scaledImage],
55
+ );
55
56
 
56
57
  return (
57
58
  <div
58
59
  className={cx(
59
60
  'eea hero-block',
60
- !image &&
61
+ !scaledImage &&
61
62
  backgroundVariant &&
62
63
  !fullWidth &&
63
64
  `color-bg-${backgroundVariant}`,
@@ -71,7 +72,7 @@ function Hero({
71
72
  <div
72
73
  className={cx(
73
74
  'hero-block-image-wrapper',
74
- !image &&
75
+ !scaledImage &&
75
76
  backgroundVariant &&
76
77
  fullWidth &&
77
78
  `color-bg-${backgroundVariant}`,
@@ -86,7 +87,7 @@ function Hero({
86
87
  ref={bgImgRef}
87
88
  style={backgroundImageStyle}
88
89
  />
89
- {image && overlay && (
90
+ {scaledImage && overlay && (
90
91
  <div className="hero-block-image-overlay dark-overlay-4"></div>
91
92
  )}
92
93
  </div>
@@ -103,7 +103,7 @@ describe('Hero block', () => {
103
103
  const { container } = render(
104
104
  <Provider store={store}>
105
105
  <Hero
106
- image={{ '@type': 'URL', url: 'url_url', href: 'href_url' }}
106
+ image={[{ '@type': 'URL', url: 'url_url', href: 'href_url' }]}
107
107
  overlay={true}
108
108
  fullWidth={false}
109
109
  fullHeight={true}
@@ -122,10 +122,10 @@ describe('Hero block', () => {
122
122
  </Provider>,
123
123
  );
124
124
 
125
- expect(container.querySelector('.eea.hero-block')).toBeInTheDocument();
126
125
  expect(container.querySelector('.hero-block-image')).toHaveStyle({
127
126
  backgroundImage: 'url(url_url)',
128
127
  });
128
+ expect(container.querySelector('.eea.hero-block')).toBeInTheDocument();
129
129
  });
130
130
 
131
131
  it('renders a hero component', () => {
@@ -157,10 +157,10 @@ describe('Hero block', () => {
157
157
  </Provider>,
158
158
  );
159
159
 
160
- expect(container.querySelector('.eea.hero-block')).toBeInTheDocument();
161
160
  expect(container.querySelector('.hero-block-image')).toHaveStyle({
162
161
  backgroundImage: 'url(/foo/bar/@@images/image/huge)',
163
162
  });
163
+ expect(container.querySelector('.eea.hero-block')).toBeInTheDocument();
164
164
  });
165
165
 
166
166
  it('renders a hero component', () => {
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useMemo } from 'react';
2
2
  import cx from 'classnames';
3
3
  import { Icon } from 'semantic-ui-react';
4
4
  import { UniversalLink, RenderBlocks } from '@plone/volto/components';
@@ -6,7 +6,8 @@ import { BodyClass } from '@plone/volto/helpers';
6
6
  import { useLocation } from 'react-router-dom';
7
7
  import Hero from './Hero';
8
8
  import Copyright from './Copyright';
9
- import { serializeText, getFieldURL } from '@eeacms/volto-hero-block/helpers';
9
+ import { serializeText } from '@eeacms/volto-hero-block/helpers';
10
+ import { getFieldURL } from '@plone/volto/helpers/Url/Url';
10
11
  import config from '@plone/volto/registry';
11
12
 
12
13
  const Metadata = ({ buttonLabel, inverted, styles, ...props }) => {
@@ -29,6 +30,7 @@ const View = (props) => {
29
30
  const location = useLocation();
30
31
  const { data = {} } = props;
31
32
  const { text, copyright, copyrightIcon, copyrightPosition } = data;
33
+ const serializedText = useMemo(() => serializeText(text), [text]);
32
34
 
33
35
  const metadata = props.metadata || props.properties;
34
36
  const copyrightPrefix = config.blocks.blocksConfig.hero.copyrightPrefix || '';
@@ -44,7 +46,7 @@ const View = (props) => {
44
46
  content={data?.data || {}}
45
47
  />
46
48
  ) : (
47
- serializeText(text)
49
+ serializedText
48
50
  )}
49
51
  </Hero.Text>
50
52
  <Hero.Meta {...data}>
@@ -5,9 +5,11 @@ import View from './View';
5
5
 
6
6
  const observe = jest.fn();
7
7
  const unobserve = jest.fn();
8
- window.IntersectionObserver = jest.fn((callback) => ({
8
+ const disconnect = jest.fn();
9
+ window.IntersectionObserver = jest.fn(() => ({
9
10
  observe,
10
11
  unobserve,
12
+ disconnect,
11
13
  }));
12
14
  jest.mock('@plone/volto/components', () => {
13
15
  return {
@@ -46,7 +48,7 @@ describe('View Component', () => {
46
48
  text: 'Hello, World!',
47
49
  copyright: 'Copyright 2023',
48
50
  copyrightIcon: 'copyright-icon-class',
49
- copyrightPosition: 'bottom',
51
+ copyrightPosition: 'left',
50
52
  };
51
53
  render(<View data={data} />);
52
54
  });
@@ -13,7 +13,7 @@ const ALIGN_INFO_MAP = {
13
13
  '': [clearSVG, 'None'],
14
14
  };
15
15
 
16
- export const HeroBlockSchema = ({ data }) => {
16
+ export const HeroBlockSchema = ({ data = {} }) => {
17
17
  return {
18
18
  title: 'Hero',
19
19
  fieldsets: [
@@ -43,27 +43,27 @@ export const HeroBlockSchema = ({ data }) => {
43
43
  fullWidth: {
44
44
  title: 'Full width',
45
45
  type: 'boolean',
46
- defaultValue: true,
46
+ default: true,
47
47
  },
48
48
  fullHeight: {
49
49
  title: 'Full height',
50
50
  type: 'boolean',
51
- defaultValue: true,
51
+ default: true,
52
52
  },
53
53
  quoted: {
54
54
  title: 'Quoted',
55
55
  type: 'boolean',
56
- defaultValue: false,
56
+ default: false,
57
57
  },
58
58
  spaced: {
59
59
  title: 'Spaced',
60
60
  type: 'boolean',
61
- defaultValue: false,
61
+ default: false,
62
62
  },
63
63
  inverted: {
64
64
  title: 'Inverted',
65
65
  type: 'boolean',
66
- defaultValue: true,
66
+ default: true,
67
67
  },
68
68
  buttonLabel: {
69
69
  title: 'Button label',
@@ -76,11 +76,12 @@ export const HeroBlockSchema = ({ data }) => {
76
76
  overlay: {
77
77
  title: 'Image darken overlay',
78
78
  type: 'boolean',
79
- defaultValue: true,
79
+ default: true,
80
80
  },
81
81
  image: {
82
82
  title: 'Image',
83
83
  widget: 'attachedimage',
84
+ selectedItemAttrs: ['image_field', 'image_scales'],
84
85
  },
85
86
  copyright: {
86
87
  title: 'Text',
@@ -0,0 +1,66 @@
1
+ import { stylingSchema, HeroBlockSchema } from './schema';
2
+ import config from '@plone/volto/registry';
3
+
4
+ jest.mock('@plone/volto/helpers', () => ({
5
+ addStyling: jest.fn((schema) => schema),
6
+ }));
7
+
8
+ export const EMPTY_STYLES_SCHEMA = {
9
+ fieldsets: [
10
+ {
11
+ id: 'default',
12
+ title: 'Default',
13
+ fields: [],
14
+ },
15
+ ],
16
+ properties: {
17
+ styles: {},
18
+ },
19
+ required: [],
20
+ };
21
+
22
+ describe('stylingSchema', () => {
23
+ it('returns the enhanced stylesSchema', () => {
24
+ const schema = EMPTY_STYLES_SCHEMA;
25
+ expect(stylingSchema(schema)).not.toBe({});
26
+ });
27
+ it('checks if the stylesSchema has desired fields', () => {
28
+ const schema = EMPTY_STYLES_SCHEMA;
29
+ const stylesSchema = stylingSchema(schema);
30
+ expect(
31
+ stylesSchema.properties.styles.schema.fieldsets[0].fields,
32
+ ).toHaveLength(7);
33
+ });
34
+
35
+ it('checks if the backgroundVariant styles has value', () => {
36
+ config.settings.themeColors = [{ value: 'primary', title: 'Primary' }];
37
+ const schema = EMPTY_STYLES_SCHEMA;
38
+ const stylesSchema = stylingSchema(schema);
39
+ expect(
40
+ stylesSchema.properties.styles.schema.properties.backgroundVariant.colors,
41
+ ).toHaveLength(1);
42
+ });
43
+ it('checks if the Text styles has value', () => {
44
+ config.settings.themeColors = [{ value: 'primary', title: 'Primary' }];
45
+ const schema = EMPTY_STYLES_SCHEMA;
46
+ const stylesSchema = stylingSchema(schema);
47
+ expect(
48
+ stylesSchema.properties.styles.schema.properties.textVariant.colors,
49
+ ).toHaveLength(1);
50
+ });
51
+ it('checks if the button styles has value', () => {
52
+ config.settings.themeColors = [{ value: 'primary', title: 'Primary' }];
53
+ const schema = EMPTY_STYLES_SCHEMA;
54
+ const stylesSchema = stylingSchema(schema);
55
+ expect(
56
+ stylesSchema.properties.styles.schema.properties.buttonVariant.colors,
57
+ ).toHaveLength(1);
58
+ });
59
+ });
60
+
61
+ describe('HeroBlockSchema', () => {
62
+ it('checks if the hero block schema is valid', () => {
63
+ const schema = HeroBlockSchema({ data: {} });
64
+ expect(schema.fieldsets).toHaveLength(2);
65
+ });
66
+ });
package/src/helpers.js CHANGED
@@ -1,23 +1,4 @@
1
1
  import { serializeNodes } from '@plone/volto-slate/editor/render';
2
- import isArray from 'lodash/isArray';
3
- import isObject from 'lodash/isObject';
4
- import isString from 'lodash/isString';
5
- import { isInternalURL, flattenToAppURL } from '@plone/volto/helpers';
6
-
7
- export const getFieldURL = (data) => {
8
- let url = data;
9
- const _isObject = data && isObject(data) && !isArray(data);
10
- if (_isObject && data['@type'] === 'URL') {
11
- url = data['value'] ?? data['url'] ?? data['href'] ?? data;
12
- } else if (_isObject) {
13
- url = data['@id'] ?? data['url'] ?? data['href'] ?? data;
14
- }
15
- if (isArray(data)) {
16
- url = data.map((item) => getFieldURL(item));
17
- }
18
- if (isString(url) && isInternalURL(url)) return flattenToAppURL(url);
19
- return url;
20
- };
21
2
 
22
3
  const createEmptyHeader = () => {
23
4
  return {
@@ -27,13 +8,9 @@ const createEmptyHeader = () => {
27
8
  };
28
9
 
29
10
  export const createSlateHeader = (text) => {
30
- return isArray(text) ? text : [createEmptyHeader()];
11
+ return Array.isArray(text) ? text : [createEmptyHeader()];
31
12
  };
32
13
 
33
14
  export const serializeText = (text) => {
34
- return isArray(text) ? serializeNodes(text) : text;
35
- };
36
-
37
- export const isImageGif = (image) => {
38
- return image?.endsWith?.('.gif');
15
+ return Array.isArray(text) ? serializeNodes(text) : text;
39
16
  };
@@ -1,9 +1,4 @@
1
- import {
2
- getFieldURL,
3
- createSlateHeader,
4
- serializeText,
5
- isImageGif,
6
- } from './helpers';
1
+ import { createSlateHeader, serializeText } from './helpers';
7
2
  import { isArray } from 'lodash';
8
3
  import { serializeNodes } from '@plone/volto-slate/editor/render';
9
4
 
@@ -11,94 +6,6 @@ jest.mock('@plone/volto-slate/editor/render', () => ({
11
6
  serializeNodes: jest.fn(),
12
7
  }));
13
8
 
14
- describe('getFieldURL', () => {
15
- it('handles a URL type object with type and value', () => {
16
- const data = {
17
- '@type': 'URL',
18
- value: 'value_url',
19
- url: 'url_url',
20
- href: 'href_url',
21
- };
22
- expect(getFieldURL(data)).toEqual('value_url');
23
- });
24
-
25
- it('handles an object with type and url', () => {
26
- const data = {
27
- '@type': 'URL',
28
- url: 'url_url',
29
- href: 'href_url',
30
- };
31
- expect(getFieldURL(data)).toEqual('url_url');
32
- });
33
-
34
- it('handles an object with type and href', () => {
35
- const data = {
36
- '@type': 'URL',
37
- href: 'href_url',
38
- };
39
- expect(getFieldURL(data)).toEqual('href_url');
40
- });
41
-
42
- it('handles an object with type and no value, url and href', () => {
43
- const data = {
44
- '@type': 'URL',
45
- };
46
- expect(getFieldURL(data)).toEqual({ '@type': 'URL' });
47
- });
48
-
49
- it('handles an object without a specific type and url', () => {
50
- const data = {
51
- url: 'url_url',
52
- href: 'href_url',
53
- };
54
- expect(getFieldURL(data)).toEqual('url_url');
55
- });
56
-
57
- it('handles an object without a specific type and href', () => {
58
- const data = {
59
- href: 'href_url',
60
- };
61
- expect(getFieldURL(data)).toEqual('href_url');
62
- });
63
-
64
- it('handles an object without a specific type and no id, url, href', () => {
65
- const data = {
66
- test: 'test_url',
67
- };
68
- expect(getFieldURL(data)).toEqual({
69
- test: 'test_url',
70
- });
71
- });
72
-
73
- it('handles an array', () => {
74
- const data = [
75
- {
76
- '@type': 'URL',
77
- value: 'value_url',
78
- url: 'url_url',
79
- href: 'href_url',
80
- },
81
- {
82
- '@id': 'id_url',
83
- url: 'url_url',
84
- href: 'href_url',
85
- },
86
- ];
87
- expect(getFieldURL(data)).toEqual(['value_url', 'id_url']);
88
- });
89
-
90
- it('handles a string', () => {
91
- const data = '/some/url';
92
- expect(getFieldURL(data)).toEqual('/some/url');
93
- });
94
-
95
- it('returns the data unchanged for non-object/non-array/non-string inputs', () => {
96
- expect(getFieldURL(42)).toEqual(42);
97
- expect(getFieldURL(undefined)).toEqual(undefined);
98
- expect(getFieldURL(null)).toEqual(null);
99
- });
100
- });
101
-
102
9
  describe('createSlateHeader', () => {
103
10
  it('should return the text if it is an array', () => {
104
11
  const text = ['some', 'text'];
@@ -133,27 +40,3 @@ describe('serializeText', () => {
133
40
  expect(serializeNodes).toHaveBeenCalledWith(text);
134
41
  });
135
42
  });
136
-
137
- describe('isImageGif', () => {
138
- it('should return true when input is a gif image', () => {
139
- const input = 'image.gif';
140
- const result = isImageGif(input);
141
- expect(result).toBeTruthy();
142
- });
143
-
144
- it('should return false when input is not a gif image', () => {
145
- const input = 'image.jpg';
146
- const result = isImageGif(input);
147
- expect(result).toBeFalsy();
148
- });
149
-
150
- it('should return false when input is null or undefined', () => {
151
- let input;
152
- let result = isImageGif(input);
153
- expect(result).toBeFalsy();
154
-
155
- input = null;
156
- result = isImageGif(input);
157
- expect(result).toBeFalsy();
158
- });
159
- });
package/src/hooks.test.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { render } from '@testing-library/react';
2
+ import { render, act } from '@testing-library/react';
3
3
  import { useFirstVisited } from './hooks';
4
4
 
5
5
  let observerCallback;
@@ -16,6 +16,12 @@ window.IntersectionObserver = jest.fn((callback) => {
16
16
  });
17
17
 
18
18
  describe('useFirstVisited', () => {
19
+ beforeEach(() => {
20
+ observeMock.mockClear();
21
+ unobserveMock.mockClear();
22
+ disconnectMock.mockClear();
23
+ });
24
+
19
25
  it('should observe and unobserve the ref element', () => {
20
26
  const ref = { current: {} };
21
27
  const rootMargin = '10px';
@@ -31,7 +37,9 @@ describe('useFirstVisited', () => {
31
37
  expect(container.textContent).toBe('Not Intersected');
32
38
 
33
39
  // Simulate the element becoming visible in the viewport
34
- observerCallback([{ isIntersecting: true }]);
40
+ act(() => {
41
+ observerCallback([{ isIntersecting: true }]);
42
+ });
35
43
 
36
44
  // Re-render: Intersected
37
45
  expect(container.textContent).toBe('Intersected');
@@ -44,7 +52,7 @@ describe('useFirstVisited', () => {
44
52
  expect(disconnectMock).toHaveBeenCalled();
45
53
  });
46
54
 
47
- it('should not observe when intersected is true', () => {
55
+ it('should stay intersected once it becomes true', () => {
48
56
  const ref = { current: {} };
49
57
 
50
58
  const TestComponent = () => {
@@ -58,17 +66,15 @@ describe('useFirstVisited', () => {
58
66
  expect(container.textContent).toBe('Not Intersected');
59
67
 
60
68
  // Simulate the element becoming visible in the viewport
61
- observerCallback([{ isIntersecting: true }]);
69
+ act(() => {
70
+ observerCallback([{ isIntersecting: true }]);
71
+ });
62
72
 
63
73
  // Re-render: Intersected
64
74
  expect(container.textContent).toBe('Intersected');
65
75
 
66
- // Simulate another render
67
- // Intersected should stay true and not observe again
68
- observerCallback([{ isIntersecting: false }]);
69
-
70
- // Re-render: Intersected
71
- expect(container.textContent).toBe('Not Intersected');
72
- // expect(observeMock).not.toHaveBeenCalled();
76
+ // Once intersected is true, it should stay true regardless of further callbacks
77
+ // The hook should not set up a new observer when intersected is already true
78
+ expect(container.textContent).toBe('Intersected');
73
79
  });
74
80
  });