@eeacms/volto-eea-website-theme 3.7.0 → 3.9.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 +19 -2
- package/package.json +3 -1
- package/src/actions/index.js +1 -0
- package/src/actions/navigation.js +24 -0
- package/src/actions/print.js +9 -1
- package/src/components/manage/Blocks/ContextNavigation/variations/Accordion.jsx +42 -35
- package/src/components/manage/Blocks/LayoutSettings/LayoutSettingsEdit.test.jsx +383 -0
- package/src/components/manage/Blocks/Title/variations/WebReportPage.test.jsx +232 -0
- package/src/components/theme/Banner/View.jsx +11 -92
- package/src/components/theme/PrintLoader/PrintLoader.jsx +56 -0
- package/src/components/theme/PrintLoader/PrintLoader.test.jsx +91 -0
- package/src/components/theme/PrintLoader/style.less +12 -0
- package/src/components/theme/WebReport/WebReportSectionView.test.jsx +462 -0
- package/src/components/theme/Widgets/ImageViewWidget.test.jsx +26 -0
- package/src/components/theme/Widgets/NavigationBehaviorWidget.jsx +601 -0
- package/src/components/theme/Widgets/NavigationBehaviorWidget.test.jsx +507 -0
- package/src/components/theme/Widgets/SimpleArrayWidget.jsx +183 -0
- package/src/components/theme/Widgets/SimpleArrayWidget.test.jsx +283 -0
- package/src/constants/ActionTypes.js +2 -0
- package/src/customizations/volto/components/manage/History/History.diff +207 -0
- package/src/customizations/volto/components/manage/History/History.jsx +444 -0
- package/src/customizations/volto/components/theme/Comments/Comments.jsx +9 -2
- package/src/customizations/volto/components/theme/Comments/Comments.test.jsx +4 -4
- package/src/customizations/volto/components/theme/Comments/comments.less +16 -0
- package/src/customizations/volto/components/theme/Header/Header.jsx +60 -1
- package/src/customizations/volto/components/theme/View/DefaultView.jsx +42 -33
- package/src/customizations/volto/helpers/Html/Html.jsx +212 -0
- package/src/customizations/volto/helpers/Html/Readme.md +1 -0
- package/src/customizations/volto/server.jsx +375 -0
- package/src/helpers/loadLazyImages.js +11 -0
- package/src/helpers/loadLazyImages.test.js +22 -0
- package/src/helpers/setupPrintView.js +134 -0
- package/src/helpers/setupPrintView.test.js +49 -0
- package/src/index.js +11 -1
- package/src/index.test.js +6 -0
- package/src/middleware/voltoCustom.test.js +282 -0
- package/src/reducers/index.js +2 -1
- package/src/reducers/navigation/navigation.js +47 -0
- package/src/reducers/navigation/navigation.test.js +348 -0
- package/src/reducers/navigation.js +55 -0
- package/src/reducers/print.js +18 -8
- package/src/reducers/print.test.js +117 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,11 +4,28 @@ 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
|
-
### [3.
|
|
7
|
+
### [3.9.0](https://github.com/eea/volto-eea-website-theme/compare/3.8.0...3.9.0) - 20 August 2025
|
|
8
|
+
|
|
9
|
+
#### :rocket: New Features
|
|
10
|
+
|
|
11
|
+
- feat: Provide full versioning for indicators and easy access to previous versions - refs #289335 [dobri1408 - [`6cdeb1d`](https://github.com/eea/volto-eea-website-theme/commit/6cdeb1d56b20d63a5c8c3fc5fc9432620053ba89)]
|
|
12
|
+
- feat: Control Navigation Settings TTW - refs #288509 [dobri1408 - [`0e82ddb`](https://github.com/eea/volto-eea-website-theme/commit/0e82ddb7369be52a63a3ac15e22a18940933a1f4)]
|
|
13
|
+
|
|
14
|
+
#### :hammer_and_wrench: Others
|
|
15
|
+
|
|
16
|
+
- Release 3.9.0 [Alin Voinea - [`b19fa21`](https://github.com/eea/volto-eea-website-theme/commit/b19fa2173b646ad105cf2eb2d70aeef480a76356)]
|
|
17
|
+
### [3.8.0](https://github.com/eea/volto-eea-website-theme/compare/3.7.0...3.8.0) - 7 July 2025
|
|
18
|
+
|
|
19
|
+
#### :rocket: New Features
|
|
20
|
+
|
|
21
|
+
- feat(side-nav): allow content to be inserted before instead of the default append [David Ichim - [`6e184d2`](https://github.com/eea/volto-eea-website-theme/commit/6e184d2dfff0ee693b2d27527fa538dce8bdede7)]
|
|
8
22
|
|
|
9
23
|
#### :hammer_and_wrench: Others
|
|
10
24
|
|
|
11
|
-
-
|
|
25
|
+
- Update package.json [David Ichim - [`af355dd`](https://github.com/eea/volto-eea-website-theme/commit/af355dda36692e048779abc80d968096a0cc3c49)]
|
|
26
|
+
- add dependency on volto-anchors since we have a require which we catch [David Ichim - [`135db9f`](https://github.com/eea/volto-eea-website-theme/commit/135db9fc4d50ef535ef80a499ddd3c9d87dabac6)]
|
|
27
|
+
### [3.7.0](https://github.com/eea/volto-eea-website-theme/compare/3.6.3...3.7.0) - 6 June 2025
|
|
28
|
+
|
|
12
29
|
### [3.6.3](https://github.com/eea/volto-eea-website-theme/compare/3.6.2...3.6.3) - 2 June 2025
|
|
13
30
|
|
|
14
31
|
#### :bug: Bug Fixes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eeacms/volto-eea-website-theme",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.9.0",
|
|
4
4
|
"description": "@eeacms/volto-eea-website-theme: Volto add-on",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"author": "European Environment Agency: IDM2 A-Team",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"react"
|
|
15
15
|
],
|
|
16
16
|
"addons": [
|
|
17
|
+
"@eeacms/volto-anchors",
|
|
17
18
|
"@eeacms/volto-block-toc",
|
|
18
19
|
"@eeacms/volto-group-block",
|
|
19
20
|
"@eeacms/volto-eea-design-system",
|
|
@@ -24,6 +25,7 @@
|
|
|
24
25
|
"url": "git@github.com:eea/volto-eea-website-theme.git"
|
|
25
26
|
},
|
|
26
27
|
"dependencies": {
|
|
28
|
+
"@eeacms/volto-anchors": "*",
|
|
27
29
|
"@eeacms/volto-block-style": "*",
|
|
28
30
|
"@eeacms/volto-block-toc": "*",
|
|
29
31
|
"@eeacms/volto-eea-design-system": "*",
|
package/src/actions/index.js
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { flattenToAppURL } from '@plone/volto/helpers';
|
|
2
|
+
import { GET_NAVIGATION_SETTINGS } from '../constants/ActionTypes';
|
|
3
|
+
|
|
4
|
+
export const getNavigationSettings = (url = '') => {
|
|
5
|
+
let cleanedUrl = typeof url === 'string' ? url : '';
|
|
6
|
+
|
|
7
|
+
if (
|
|
8
|
+
cleanedUrl &&
|
|
9
|
+
typeof cleanedUrl === 'string' &&
|
|
10
|
+
cleanedUrl.endsWith('/edit')
|
|
11
|
+
) {
|
|
12
|
+
cleanedUrl = cleanedUrl.slice(0, -'/edit'.length);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const apiPath = flattenToAppURL(cleanedUrl);
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
type: GET_NAVIGATION_SETTINGS,
|
|
19
|
+
request: {
|
|
20
|
+
op: 'get',
|
|
21
|
+
path: `${apiPath}/@inherit?expand.inherit.behaviors=eea.enhanced_navigation`,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
};
|
package/src/actions/print.js
CHANGED
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
* @module actions/print
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
SET_ISPRINT,
|
|
8
|
+
SET_PRINT_LOADING,
|
|
9
|
+
} from '@eeacms/volto-eea-website-theme/constants/ActionTypes';
|
|
7
10
|
|
|
8
11
|
export const setIsPrint = (data) => {
|
|
9
12
|
return {
|
|
@@ -11,3 +14,8 @@ export const setIsPrint = (data) => {
|
|
|
11
14
|
payload: data,
|
|
12
15
|
};
|
|
13
16
|
};
|
|
17
|
+
|
|
18
|
+
export const setPrintLoading = (isLoading) => ({
|
|
19
|
+
type: SET_PRINT_LOADING,
|
|
20
|
+
payload: isLoading,
|
|
21
|
+
});
|
|
@@ -8,7 +8,7 @@ import { Accordion, Icon } from 'semantic-ui-react';
|
|
|
8
8
|
|
|
9
9
|
import Slugger from 'github-slugger';
|
|
10
10
|
|
|
11
|
-
import { UniversalLink
|
|
11
|
+
import { UniversalLink } from '@plone/volto/components';
|
|
12
12
|
import { withContentNavigation } from '@plone/volto/components/theme/Navigation/withContentNavigation';
|
|
13
13
|
import withEEASideMenu from '@eeacms/volto-block-toc/hocs/withEEASideMenu';
|
|
14
14
|
import { flattenToAppURL } from '@plone/volto/helpers';
|
|
@@ -24,10 +24,12 @@ const AccordionNavigation = ({
|
|
|
24
24
|
navigation = {},
|
|
25
25
|
device,
|
|
26
26
|
isMenuOpenOnOutsideClick,
|
|
27
|
+
hasWideContent,
|
|
27
28
|
}) => {
|
|
28
29
|
const { items = [], title, has_custom_name } = navigation;
|
|
29
30
|
const intl = useIntl();
|
|
30
|
-
const navOpen =
|
|
31
|
+
const navOpen =
|
|
32
|
+
['mobile', 'tablet'].includes(device) || hasWideContent ? false : true;
|
|
31
33
|
const [isNavOpen, setIsNavOpen] = React.useState(navOpen);
|
|
32
34
|
const [activeItems, setActiveItems] = React.useState({});
|
|
33
35
|
const contextNavigationListRef = React.useRef(null);
|
|
@@ -42,6 +44,10 @@ const AccordionNavigation = ({
|
|
|
42
44
|
if (isMenuOpenOnOutsideClick === false) setIsNavOpen(false);
|
|
43
45
|
}, [isMenuOpenOnOutsideClick]);
|
|
44
46
|
|
|
47
|
+
React.useEffect(() => {
|
|
48
|
+
setIsNavOpen(navOpen);
|
|
49
|
+
}, [navOpen]);
|
|
50
|
+
|
|
45
51
|
React.useEffect(() => {
|
|
46
52
|
if (!navOpen) {
|
|
47
53
|
const handleOutsideClick = (event) => {
|
|
@@ -72,7 +78,7 @@ const AccordionNavigation = ({
|
|
|
72
78
|
[onClickSummary],
|
|
73
79
|
);
|
|
74
80
|
|
|
75
|
-
const renderItems = ({ item, level = 0 }) => {
|
|
81
|
+
const renderItems = ({ item, level = 0, index }) => {
|
|
76
82
|
const {
|
|
77
83
|
title,
|
|
78
84
|
href,
|
|
@@ -83,6 +89,7 @@ const AccordionNavigation = ({
|
|
|
83
89
|
} = item;
|
|
84
90
|
const hasChildItems = childItems && childItems.length > 0;
|
|
85
91
|
const normalizedTitle = Slugger.slug(title);
|
|
92
|
+
const firstItem = index === 0;
|
|
86
93
|
|
|
87
94
|
const checkIfActive = () => {
|
|
88
95
|
return activeItems[href] !== undefined ? activeItems[href] : is_in_path;
|
|
@@ -126,7 +133,7 @@ const AccordionNavigation = ({
|
|
|
126
133
|
aria-labelledby={`accordion-title-${normalizedTitle}`}
|
|
127
134
|
role="region"
|
|
128
135
|
>
|
|
129
|
-
<ul className=
|
|
136
|
+
<ul className={`accordion-list ${'level-' + level}`}>
|
|
130
137
|
{childItems.map((child) =>
|
|
131
138
|
renderItems({ item: child, level: level + 1 }),
|
|
132
139
|
)}
|
|
@@ -139,8 +146,10 @@ const AccordionNavigation = ({
|
|
|
139
146
|
className={cx(`title-link contenttype-${type}`, {
|
|
140
147
|
current: is_current,
|
|
141
148
|
in_path: is_in_path,
|
|
149
|
+
navigation_home: firstItem,
|
|
142
150
|
})}
|
|
143
151
|
>
|
|
152
|
+
{firstItem && <Icon className={'ri-home-2-line'} />}
|
|
144
153
|
{title}
|
|
145
154
|
</UniversalLink>
|
|
146
155
|
)}
|
|
@@ -159,31 +168,21 @@ const AccordionNavigation = ({
|
|
|
159
168
|
onKeyDown={onKeyDownSummary}
|
|
160
169
|
ref={summaryRef}
|
|
161
170
|
>
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
className=
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
: intl.formatMessage(messages.navigation)}
|
|
169
|
-
<Icon
|
|
170
|
-
className={
|
|
171
|
-
isNavOpen ? 'ri-arrow-up-s-line' : 'ri-arrow-down-s-line'
|
|
172
|
-
}
|
|
173
|
-
/>
|
|
174
|
-
</MaybeWrap>
|
|
171
|
+
{has_custom_name ? title : intl.formatMessage(messages.navigation)}
|
|
172
|
+
<Icon
|
|
173
|
+
className={
|
|
174
|
+
isNavOpen ? 'ri-arrow-up-s-line' : 'ri-arrow-down-s-line'
|
|
175
|
+
}
|
|
176
|
+
/>
|
|
175
177
|
</summary>
|
|
176
|
-
<
|
|
177
|
-
|
|
178
|
-
|
|
178
|
+
<ul
|
|
179
|
+
className="context-navigation-list accordion-list"
|
|
180
|
+
ref={contextNavigationListRef}
|
|
179
181
|
>
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
{items.map((item) => renderItems({ item }))}
|
|
185
|
-
</ul>
|
|
186
|
-
</MaybeWrap>
|
|
182
|
+
{items.map((item, index) =>
|
|
183
|
+
renderItems({ item, level: 0, index: index }),
|
|
184
|
+
)}
|
|
185
|
+
</ul>
|
|
187
186
|
</details>
|
|
188
187
|
</nav>
|
|
189
188
|
</>
|
|
@@ -209,17 +208,25 @@ AccordionNavigation.propTypes = {
|
|
|
209
208
|
has_custom_name: PropTypes.bool,
|
|
210
209
|
title: PropTypes.string,
|
|
211
210
|
}),
|
|
211
|
+
/**
|
|
212
|
+
* Provided by withEEASideMenu HOC to indicate if page has wide content
|
|
213
|
+
*/
|
|
214
|
+
hasWideContent: PropTypes.bool,
|
|
212
215
|
};
|
|
213
216
|
|
|
214
217
|
export default compose(
|
|
215
218
|
withRouter,
|
|
216
219
|
withContentNavigation,
|
|
217
|
-
(WrappedComponent) => (props) =>
|
|
218
|
-
withEEASideMenu(WrappedComponent)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
220
|
+
(WrappedComponent) => (props) => {
|
|
221
|
+
const Enhanced = withEEASideMenu(WrappedComponent);
|
|
222
|
+
return (
|
|
223
|
+
<Enhanced
|
|
224
|
+
{...props}
|
|
225
|
+
targetParent=".eea.header"
|
|
226
|
+
fixedVisibilitySwitchTarget=".main.bar"
|
|
227
|
+
insertBeforeOnMobile=".banner"
|
|
228
|
+
shouldRender={Boolean(props.navigation?.items?.length)}
|
|
229
|
+
/>
|
|
230
|
+
);
|
|
231
|
+
},
|
|
225
232
|
)(AccordionNavigation);
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
+
import LayoutSettingsEdit from './LayoutSettingsEdit';
|
|
4
|
+
import { EditSchema } from './schema';
|
|
5
|
+
|
|
6
|
+
// Add jest-dom matchers
|
|
7
|
+
import '@testing-library/jest-dom';
|
|
8
|
+
|
|
9
|
+
// Mock dependencies
|
|
10
|
+
jest.mock('./schema', () => ({
|
|
11
|
+
EditSchema: jest.fn(),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
jest.mock('@plone/volto/components', () => ({
|
|
15
|
+
BlockDataForm: jest.fn(({ title, schema, formData, onChangeField }) => (
|
|
16
|
+
<div data-testid="block-data-form">
|
|
17
|
+
<div data-testid="form-title">{title}</div>
|
|
18
|
+
<div data-testid="form-schema">{JSON.stringify(schema)}</div>
|
|
19
|
+
<div data-testid="form-data">{JSON.stringify(formData)}</div>
|
|
20
|
+
<button
|
|
21
|
+
data-testid="change-field-button"
|
|
22
|
+
onClick={() => onChangeField('test-field', 'test-value')}
|
|
23
|
+
>
|
|
24
|
+
Change Field
|
|
25
|
+
</button>
|
|
26
|
+
</div>
|
|
27
|
+
)),
|
|
28
|
+
SidebarPortal: jest.fn(({ selected, children }) => (
|
|
29
|
+
<div data-testid="sidebar-portal" data-selected={selected}>
|
|
30
|
+
{children}
|
|
31
|
+
</div>
|
|
32
|
+
)),
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
jest.mock('./LayoutSettingsView', () => ({
|
|
36
|
+
__esModule: true,
|
|
37
|
+
default: jest.fn((props) => (
|
|
38
|
+
<div data-testid="layout-settings-view" data-props={JSON.stringify(props)}>
|
|
39
|
+
Layout Settings View
|
|
40
|
+
</div>
|
|
41
|
+
)),
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
const mockLayoutSettingsView = require('./LayoutSettingsView').default;
|
|
45
|
+
|
|
46
|
+
const { BlockDataForm, SidebarPortal } = require('@plone/volto/components');
|
|
47
|
+
|
|
48
|
+
describe('LayoutSettingsEdit', () => {
|
|
49
|
+
const mockSchema = {
|
|
50
|
+
title: 'Page layout settings',
|
|
51
|
+
fieldsets: [
|
|
52
|
+
{
|
|
53
|
+
id: 'default',
|
|
54
|
+
title: 'Default',
|
|
55
|
+
fields: ['layout_size', 'body_class'],
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
properties: {
|
|
59
|
+
layout_size: {
|
|
60
|
+
widget: 'style_align',
|
|
61
|
+
title: 'Layout size',
|
|
62
|
+
},
|
|
63
|
+
body_class: {
|
|
64
|
+
title: 'Body class',
|
|
65
|
+
widget: 'creatable_select',
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const defaultProps = {
|
|
71
|
+
selected: true,
|
|
72
|
+
block: 'block-id',
|
|
73
|
+
data: {
|
|
74
|
+
layout_size: 'container_view',
|
|
75
|
+
body_class: 'homepage',
|
|
76
|
+
},
|
|
77
|
+
onChangeBlock: jest.fn(),
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
beforeEach(() => {
|
|
81
|
+
EditSchema.mockReturnValue(mockSchema);
|
|
82
|
+
jest.clearAllMocks();
|
|
83
|
+
mockLayoutSettingsView.mockClear();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('renders without crashing', () => {
|
|
87
|
+
const { container } = render(<LayoutSettingsEdit {...defaultProps} />);
|
|
88
|
+
expect(container).toBeTruthy();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('renders the page title', () => {
|
|
92
|
+
render(<LayoutSettingsEdit {...defaultProps} />);
|
|
93
|
+
expect(screen.getByRole('heading', { level: 3 })).toHaveTextContent(
|
|
94
|
+
'Page layout settings',
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('renders LayoutSettingsView with correct props', () => {
|
|
99
|
+
render(<LayoutSettingsEdit {...defaultProps} />);
|
|
100
|
+
|
|
101
|
+
expect(mockLayoutSettingsView).toHaveBeenCalledWith(defaultProps, {});
|
|
102
|
+
expect(screen.getByTestId('layout-settings-view')).toBeInTheDocument();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('renders SidebarPortal with correct selected prop', () => {
|
|
106
|
+
render(<LayoutSettingsEdit {...defaultProps} />);
|
|
107
|
+
|
|
108
|
+
expect(SidebarPortal).toHaveBeenCalledWith(
|
|
109
|
+
expect.objectContaining({
|
|
110
|
+
selected: true,
|
|
111
|
+
}),
|
|
112
|
+
{},
|
|
113
|
+
);
|
|
114
|
+
expect(screen.getByTestId('sidebar-portal')).toHaveAttribute(
|
|
115
|
+
'data-selected',
|
|
116
|
+
'true',
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('renders SidebarPortal with selected false', () => {
|
|
121
|
+
const props = { ...defaultProps, selected: false };
|
|
122
|
+
render(<LayoutSettingsEdit {...props} />);
|
|
123
|
+
|
|
124
|
+
expect(SidebarPortal).toHaveBeenCalledWith(
|
|
125
|
+
expect.objectContaining({
|
|
126
|
+
selected: false,
|
|
127
|
+
}),
|
|
128
|
+
{},
|
|
129
|
+
);
|
|
130
|
+
expect(screen.getByTestId('sidebar-portal')).toHaveAttribute(
|
|
131
|
+
'data-selected',
|
|
132
|
+
'false',
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('renders BlockDataForm when selected is true', () => {
|
|
137
|
+
render(<LayoutSettingsEdit {...defaultProps} />);
|
|
138
|
+
|
|
139
|
+
expect(BlockDataForm).toHaveBeenCalledWith(
|
|
140
|
+
expect.objectContaining({
|
|
141
|
+
title: mockSchema.title,
|
|
142
|
+
schema: mockSchema,
|
|
143
|
+
formData: defaultProps.data,
|
|
144
|
+
onChangeField: expect.any(Function),
|
|
145
|
+
}),
|
|
146
|
+
{},
|
|
147
|
+
);
|
|
148
|
+
expect(screen.getByTestId('block-data-form')).toBeInTheDocument();
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('does not render BlockDataForm when selected is false', () => {
|
|
152
|
+
const props = { ...defaultProps, selected: false };
|
|
153
|
+
render(<LayoutSettingsEdit {...props} />);
|
|
154
|
+
|
|
155
|
+
expect(BlockDataForm).not.toHaveBeenCalled();
|
|
156
|
+
expect(screen.queryByTestId('block-data-form')).not.toBeInTheDocument();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('calls EditSchema to get schema', () => {
|
|
160
|
+
render(<LayoutSettingsEdit {...defaultProps} />);
|
|
161
|
+
|
|
162
|
+
expect(EditSchema).toHaveBeenCalled();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('passes correct title to BlockDataForm', () => {
|
|
166
|
+
render(<LayoutSettingsEdit {...defaultProps} />);
|
|
167
|
+
|
|
168
|
+
expect(screen.getByTestId('form-title')).toHaveTextContent(
|
|
169
|
+
'Page layout settings',
|
|
170
|
+
);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('passes correct schema to BlockDataForm', () => {
|
|
174
|
+
render(<LayoutSettingsEdit {...defaultProps} />);
|
|
175
|
+
|
|
176
|
+
expect(screen.getByTestId('form-schema')).toHaveTextContent(
|
|
177
|
+
JSON.stringify(mockSchema),
|
|
178
|
+
);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('passes correct formData to BlockDataForm', () => {
|
|
182
|
+
render(<LayoutSettingsEdit {...defaultProps} />);
|
|
183
|
+
|
|
184
|
+
expect(screen.getByTestId('form-data')).toHaveTextContent(
|
|
185
|
+
JSON.stringify(defaultProps.data),
|
|
186
|
+
);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('handles onChangeField correctly', () => {
|
|
190
|
+
render(<LayoutSettingsEdit {...defaultProps} />);
|
|
191
|
+
|
|
192
|
+
const changeButton = screen.getByTestId('change-field-button');
|
|
193
|
+
fireEvent.click(changeButton);
|
|
194
|
+
|
|
195
|
+
expect(defaultProps.onChangeBlock).toHaveBeenCalledWith('block-id', {
|
|
196
|
+
...defaultProps.data,
|
|
197
|
+
'test-field': 'test-value',
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('preserves existing data when changing field', () => {
|
|
202
|
+
const propsWithData = {
|
|
203
|
+
...defaultProps,
|
|
204
|
+
data: {
|
|
205
|
+
layout_size: 'wide_view',
|
|
206
|
+
body_class: 'homepage-inverse',
|
|
207
|
+
existing_field: 'existing_value',
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
render(<LayoutSettingsEdit {...propsWithData} />);
|
|
212
|
+
|
|
213
|
+
const changeButton = screen.getByTestId('change-field-button');
|
|
214
|
+
fireEvent.click(changeButton);
|
|
215
|
+
|
|
216
|
+
expect(defaultProps.onChangeBlock).toHaveBeenCalledWith('block-id', {
|
|
217
|
+
layout_size: 'wide_view',
|
|
218
|
+
body_class: 'homepage-inverse',
|
|
219
|
+
existing_field: 'existing_value',
|
|
220
|
+
'test-field': 'test-value',
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('handles empty data object', () => {
|
|
225
|
+
const propsWithEmptyData = {
|
|
226
|
+
...defaultProps,
|
|
227
|
+
data: {},
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
render(<LayoutSettingsEdit {...propsWithEmptyData} />);
|
|
231
|
+
|
|
232
|
+
const changeButton = screen.getByTestId('change-field-button');
|
|
233
|
+
fireEvent.click(changeButton);
|
|
234
|
+
|
|
235
|
+
expect(defaultProps.onChangeBlock).toHaveBeenCalledWith('block-id', {
|
|
236
|
+
'test-field': 'test-value',
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('handles null data', () => {
|
|
241
|
+
const propsWithNullData = {
|
|
242
|
+
...defaultProps,
|
|
243
|
+
data: null,
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
render(<LayoutSettingsEdit {...propsWithNullData} />);
|
|
247
|
+
|
|
248
|
+
const changeButton = screen.getByTestId('change-field-button');
|
|
249
|
+
fireEvent.click(changeButton);
|
|
250
|
+
|
|
251
|
+
expect(defaultProps.onChangeBlock).toHaveBeenCalledWith('block-id', {
|
|
252
|
+
'test-field': 'test-value',
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('handles undefined data', () => {
|
|
257
|
+
const propsWithUndefinedData = {
|
|
258
|
+
...defaultProps,
|
|
259
|
+
data: undefined,
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
render(<LayoutSettingsEdit {...propsWithUndefinedData} />);
|
|
263
|
+
|
|
264
|
+
const changeButton = screen.getByTestId('change-field-button');
|
|
265
|
+
fireEvent.click(changeButton);
|
|
266
|
+
|
|
267
|
+
expect(defaultProps.onChangeBlock).toHaveBeenCalledWith('block-id', {
|
|
268
|
+
'test-field': 'test-value',
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('works with different block IDs', () => {
|
|
273
|
+
const propsWithDifferentBlock = {
|
|
274
|
+
...defaultProps,
|
|
275
|
+
block: 'different-block-id',
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
render(<LayoutSettingsEdit {...propsWithDifferentBlock} />);
|
|
279
|
+
|
|
280
|
+
const changeButton = screen.getByTestId('change-field-button');
|
|
281
|
+
fireEvent.click(changeButton);
|
|
282
|
+
|
|
283
|
+
expect(defaultProps.onChangeBlock).toHaveBeenCalledWith(
|
|
284
|
+
'different-block-id',
|
|
285
|
+
{
|
|
286
|
+
...defaultProps.data,
|
|
287
|
+
'test-field': 'test-value',
|
|
288
|
+
},
|
|
289
|
+
);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('handles different schema configurations', () => {
|
|
293
|
+
const customSchema = {
|
|
294
|
+
title: 'Custom Layout Settings',
|
|
295
|
+
fieldsets: [
|
|
296
|
+
{
|
|
297
|
+
id: 'advanced',
|
|
298
|
+
title: 'Advanced',
|
|
299
|
+
fields: ['custom_field'],
|
|
300
|
+
},
|
|
301
|
+
],
|
|
302
|
+
properties: {
|
|
303
|
+
custom_field: {
|
|
304
|
+
title: 'Custom Field',
|
|
305
|
+
widget: 'text',
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
EditSchema.mockReturnValue(customSchema);
|
|
311
|
+
|
|
312
|
+
render(<LayoutSettingsEdit {...defaultProps} />);
|
|
313
|
+
|
|
314
|
+
expect(screen.getByTestId('form-title')).toHaveTextContent(
|
|
315
|
+
'Custom Layout Settings',
|
|
316
|
+
);
|
|
317
|
+
expect(screen.getByTestId('form-schema')).toHaveTextContent(
|
|
318
|
+
JSON.stringify(customSchema),
|
|
319
|
+
);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('passes all props to LayoutSettingsView', () => {
|
|
323
|
+
const customProps = {
|
|
324
|
+
...defaultProps,
|
|
325
|
+
customProp: 'custom-value',
|
|
326
|
+
anotherProp: { nested: 'object' },
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
render(<LayoutSettingsEdit {...customProps} />);
|
|
330
|
+
|
|
331
|
+
expect(mockLayoutSettingsView).toHaveBeenCalledWith(customProps, {});
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it('renders all components in correct structure', () => {
|
|
335
|
+
const { container } = render(<LayoutSettingsEdit {...defaultProps} />);
|
|
336
|
+
|
|
337
|
+
// Check that h3 is rendered
|
|
338
|
+
expect(container.querySelector('h3')).toHaveTextContent(
|
|
339
|
+
'Page layout settings',
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
// Check that LayoutSettingsView is rendered
|
|
343
|
+
expect(screen.getByTestId('layout-settings-view')).toBeInTheDocument();
|
|
344
|
+
|
|
345
|
+
// Check that SidebarPortal is rendered
|
|
346
|
+
expect(screen.getByTestId('sidebar-portal')).toBeInTheDocument();
|
|
347
|
+
|
|
348
|
+
// Check that BlockDataForm is rendered inside SidebarPortal when selected
|
|
349
|
+
expect(screen.getByTestId('block-data-form')).toBeInTheDocument();
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it('handles re-renders correctly', () => {
|
|
353
|
+
const { rerender } = render(<LayoutSettingsEdit {...defaultProps} />);
|
|
354
|
+
|
|
355
|
+
expect(EditSchema).toHaveBeenCalledTimes(1);
|
|
356
|
+
|
|
357
|
+
// Re-render with different props
|
|
358
|
+
const newProps = { ...defaultProps, selected: false };
|
|
359
|
+
rerender(<LayoutSettingsEdit {...newProps} />);
|
|
360
|
+
|
|
361
|
+
expect(EditSchema).toHaveBeenCalledTimes(2);
|
|
362
|
+
expect(screen.queryByTestId('block-data-form')).not.toBeInTheDocument();
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('handles onChangeBlock function prop correctly', () => {
|
|
366
|
+
const mockOnChangeBlock = jest.fn();
|
|
367
|
+
const propsWithMockFunction = {
|
|
368
|
+
...defaultProps,
|
|
369
|
+
onChangeBlock: mockOnChangeBlock,
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
render(<LayoutSettingsEdit {...propsWithMockFunction} />);
|
|
373
|
+
|
|
374
|
+
const changeButton = screen.getByTestId('change-field-button');
|
|
375
|
+
fireEvent.click(changeButton);
|
|
376
|
+
|
|
377
|
+
expect(mockOnChangeBlock).toHaveBeenCalledWith('block-id', {
|
|
378
|
+
...defaultProps.data,
|
|
379
|
+
'test-field': 'test-value',
|
|
380
|
+
});
|
|
381
|
+
expect(defaultProps.onChangeBlock).not.toHaveBeenCalled();
|
|
382
|
+
});
|
|
383
|
+
});
|