@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.
Files changed (42) hide show
  1. package/CHANGELOG.md +19 -2
  2. package/package.json +3 -1
  3. package/src/actions/index.js +1 -0
  4. package/src/actions/navigation.js +24 -0
  5. package/src/actions/print.js +9 -1
  6. package/src/components/manage/Blocks/ContextNavigation/variations/Accordion.jsx +42 -35
  7. package/src/components/manage/Blocks/LayoutSettings/LayoutSettingsEdit.test.jsx +383 -0
  8. package/src/components/manage/Blocks/Title/variations/WebReportPage.test.jsx +232 -0
  9. package/src/components/theme/Banner/View.jsx +11 -92
  10. package/src/components/theme/PrintLoader/PrintLoader.jsx +56 -0
  11. package/src/components/theme/PrintLoader/PrintLoader.test.jsx +91 -0
  12. package/src/components/theme/PrintLoader/style.less +12 -0
  13. package/src/components/theme/WebReport/WebReportSectionView.test.jsx +462 -0
  14. package/src/components/theme/Widgets/ImageViewWidget.test.jsx +26 -0
  15. package/src/components/theme/Widgets/NavigationBehaviorWidget.jsx +601 -0
  16. package/src/components/theme/Widgets/NavigationBehaviorWidget.test.jsx +507 -0
  17. package/src/components/theme/Widgets/SimpleArrayWidget.jsx +183 -0
  18. package/src/components/theme/Widgets/SimpleArrayWidget.test.jsx +283 -0
  19. package/src/constants/ActionTypes.js +2 -0
  20. package/src/customizations/volto/components/manage/History/History.diff +207 -0
  21. package/src/customizations/volto/components/manage/History/History.jsx +444 -0
  22. package/src/customizations/volto/components/theme/Comments/Comments.jsx +9 -2
  23. package/src/customizations/volto/components/theme/Comments/Comments.test.jsx +4 -4
  24. package/src/customizations/volto/components/theme/Comments/comments.less +16 -0
  25. package/src/customizations/volto/components/theme/Header/Header.jsx +60 -1
  26. package/src/customizations/volto/components/theme/View/DefaultView.jsx +42 -33
  27. package/src/customizations/volto/helpers/Html/Html.jsx +212 -0
  28. package/src/customizations/volto/helpers/Html/Readme.md +1 -0
  29. package/src/customizations/volto/server.jsx +375 -0
  30. package/src/helpers/loadLazyImages.js +11 -0
  31. package/src/helpers/loadLazyImages.test.js +22 -0
  32. package/src/helpers/setupPrintView.js +134 -0
  33. package/src/helpers/setupPrintView.test.js +49 -0
  34. package/src/index.js +11 -1
  35. package/src/index.test.js +6 -0
  36. package/src/middleware/voltoCustom.test.js +282 -0
  37. package/src/reducers/index.js +2 -1
  38. package/src/reducers/navigation/navigation.js +47 -0
  39. package/src/reducers/navigation/navigation.test.js +348 -0
  40. package/src/reducers/navigation.js +55 -0
  41. package/src/reducers/print.js +18 -8
  42. package/src/reducers/print.test.js +117 -0
@@ -0,0 +1,444 @@
1
+ /**
2
+ * History component.
3
+ * @module components/manage/History/History
4
+ * Customized to enable complex versioning
5
+ * https://taskman.eionet.europa.eu/issues/289335?issue_count=4&issue_position=1&next_issue_id=285635
6
+ */
7
+
8
+ import React, { Component } from 'react';
9
+ import PropTypes from 'prop-types';
10
+ import Helmet from '@plone/volto/helpers/Helmet/Helmet';
11
+ import { Link } from 'react-router-dom';
12
+ import { connect } from 'react-redux';
13
+ import { compose } from 'redux';
14
+ import {
15
+ Container as SemanticContainer,
16
+ Dropdown,
17
+ Icon,
18
+ Message,
19
+ Segment,
20
+ Table,
21
+ } from 'semantic-ui-react';
22
+ import concat from 'lodash/concat';
23
+ import map from 'lodash/map';
24
+ import reverse from 'lodash/reverse';
25
+ import find from 'lodash/find';
26
+ import { createPortal } from 'react-dom';
27
+ import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
28
+ import { asyncConnect } from '@plone/volto/helpers/AsyncConnect';
29
+
30
+ import FormattedDate from '@plone/volto/components/theme/FormattedDate/FormattedDate';
31
+ import IconNext from '@plone/volto/components/theme/Icon/Icon';
32
+ import Toolbar from '@plone/volto/components/manage/Toolbar/Toolbar';
33
+ import Forbidden from '@plone/volto/components/theme/Forbidden/Forbidden';
34
+ import Unauthorized from '@plone/volto/components/theme/Unauthorized/Unauthorized';
35
+ import {
36
+ getHistory,
37
+ revertHistory,
38
+ } from '@plone/volto/actions/history/history';
39
+ import { listActions } from '@plone/volto/actions/actions/actions';
40
+ import { getBaseUrl } from '@plone/volto/helpers/Url/Url';
41
+ import config from '@plone/volto/registry';
42
+
43
+ import backSVG from '@plone/volto/icons/back.svg';
44
+
45
+ const messages = defineMessages({
46
+ back: {
47
+ id: 'Back',
48
+ defaultMessage: 'Back',
49
+ },
50
+ history: {
51
+ id: 'History',
52
+ defaultMessage: 'History',
53
+ },
54
+ newerVersionAvailable: {
55
+ id: 'Newer version available',
56
+ defaultMessage: 'Newer version available',
57
+ },
58
+ thereIsNewerVersionAt: {
59
+ id: 'There is a newer version at {link}',
60
+ defaultMessage: 'There is a newer version at {link}',
61
+ },
62
+ olderVersionAvailable: {
63
+ id: 'Older version available',
64
+ defaultMessage: 'Older version available',
65
+ },
66
+ thereIsOlderVersionAt: {
67
+ id: 'There is an older version at {link}',
68
+ defaultMessage: 'There is an older version at {link}',
69
+ },
70
+ });
71
+
72
+ /**
73
+ * History class.
74
+ * @class History
75
+ * @extends Component
76
+ */
77
+ class History extends Component {
78
+ /**
79
+ * Property types.
80
+ * @property {Object} propTypes Property types.
81
+ * @static
82
+ */
83
+ static propTypes = {
84
+ getHistory: PropTypes.func.isRequired,
85
+ revertHistory: PropTypes.func.isRequired,
86
+ revertRequest: PropTypes.shape({
87
+ loaded: PropTypes.bool,
88
+ loading: PropTypes.bool,
89
+ }).isRequired,
90
+ pathname: PropTypes.string.isRequired,
91
+ entries: PropTypes.arrayOf(
92
+ PropTypes.shape({
93
+ transition_title: PropTypes.string,
94
+ type: PropTypes.string,
95
+ action: PropTypes.string,
96
+ state_title: PropTypes.string,
97
+ time: PropTypes.string,
98
+ comments: PropTypes.string,
99
+ actor: PropTypes.shape({ fullname: PropTypes.string }),
100
+ }),
101
+ ).isRequired,
102
+ title: PropTypes.string.isRequired,
103
+ };
104
+
105
+ /**
106
+ * Constructor
107
+ * @method constructor
108
+ * @param {Object} props Component properties
109
+ * @constructs Workflow
110
+ */
111
+ constructor(props) {
112
+ super(props);
113
+ this.onRevert = this.onRevert.bind(this);
114
+ this.state = { isClient: false };
115
+ }
116
+
117
+ /**
118
+ * Component did mount
119
+ * @method componentDidMount
120
+ * @returns {undefined}
121
+ */
122
+ componentDidMount() {
123
+ this.props.getHistory(getBaseUrl(this.props.pathname));
124
+ this.setState({ isClient: true });
125
+ }
126
+
127
+ /**
128
+ * Component will receive props
129
+ * @method componentWillReceiveProps
130
+ * @param {Object} nextProps Next properties
131
+ * @returns {undefined}
132
+ */
133
+ UNSAFE_componentWillReceiveProps(nextProps) {
134
+ if (this.props.revertRequest.loading && nextProps.revertRequest.loaded) {
135
+ this.props.getHistory(getBaseUrl(this.props.pathname));
136
+ }
137
+ }
138
+
139
+ /**
140
+ * On revert
141
+ * @method onRevert
142
+ * @param {object} event Event object
143
+ * @param {number} value Value
144
+ * @returns {undefined}
145
+ */
146
+ onRevert(event, { value }) {
147
+ this.props.revertHistory(getBaseUrl(this.props.pathname), value);
148
+ }
149
+
150
+ processHistoryEntries = () => {
151
+ // Getting the history entries from the props
152
+ // No clue why the reverse(concat()) is necessary
153
+ const entries = reverse(concat(this.props.entries));
154
+ let title = entries.length > 0 ? entries[0].state_title : '';
155
+ for (let x = 1; x < entries.length; x += 1) {
156
+ entries[x].prev_state_title = title;
157
+ title = entries[x].state_title || title;
158
+ }
159
+ // We reverse them again
160
+ reverse(entries);
161
+
162
+ // We identify the latest 'versioning' entry and mark it
163
+ const current_version = find(entries, (item) => item.type === 'versioning');
164
+ if (current_version) {
165
+ current_version.is_current = true;
166
+ }
167
+ return entries;
168
+ };
169
+
170
+ /**
171
+ * Render method.
172
+ * @method render
173
+ * @returns {string} Markup for the component.
174
+ */
175
+ render() {
176
+ const historyAction = find(this.props.objectActions, {
177
+ id: 'history',
178
+ });
179
+ const entries = this.processHistoryEntries();
180
+
181
+ const Container =
182
+ config.getComponent({ name: 'Container' }).component || SemanticContainer;
183
+ return !historyAction ? (
184
+ <>
185
+ {this.props.token ? (
186
+ <Forbidden
187
+ pathname={this.props.pathname}
188
+ staticContext={this.props.staticContext}
189
+ />
190
+ ) : (
191
+ <Unauthorized
192
+ pathname={this.props.pathname}
193
+ staticContext={this.props.staticContext}
194
+ />
195
+ )}
196
+ </>
197
+ ) : (
198
+ <Container id="page-history">
199
+ <Helmet title={this.props.intl.formatMessage(messages.history)} />
200
+ <Segment.Group raised>
201
+ <Segment className="primary">
202
+ <FormattedMessage
203
+ id="History of {title}"
204
+ defaultMessage="History of {title}"
205
+ values={{
206
+ title: <q>{this.props.title}</q>,
207
+ }}
208
+ />
209
+ </Segment>
210
+ <Segment secondary>
211
+ <FormattedMessage
212
+ id="You can view the history of your item below."
213
+ defaultMessage="You can view the history of your item below."
214
+ />
215
+ </Segment>
216
+ {this.props.content?.copied_to && (
217
+ <Message info icon attached="top">
218
+ <Icon name="arrow right" />
219
+ <Message.Content>
220
+ <Message.Header>
221
+ <FormattedMessage {...messages.newerVersionAvailable} />
222
+ </Message.Header>
223
+ <FormattedMessage
224
+ {...messages.thereIsNewerVersionAt}
225
+ values={{
226
+ link: (
227
+ <Link to={new URL(this.props.content.copied_to).pathname}>
228
+ {new URL(this.props.content.copied_to).pathname
229
+ .split('/')
230
+ .pop() || 'newer version'}
231
+ </Link>
232
+ ),
233
+ }}
234
+ />
235
+ </Message.Content>
236
+ </Message>
237
+ )}
238
+ <Table
239
+ selectable
240
+ compact
241
+ attached
242
+ style={{
243
+ tableLayout: 'fixed',
244
+ width: '100%',
245
+ wordWrap: 'break-word',
246
+ }}
247
+ >
248
+ <Table.Header>
249
+ <Table.Row>
250
+ <Table.HeaderCell style={{ width: '5%' }}>
251
+ <FormattedMessage
252
+ id="History Version Number"
253
+ defaultMessage="#"
254
+ />
255
+ </Table.HeaderCell>
256
+ <Table.HeaderCell style={{ width: '25%' }}>
257
+ <FormattedMessage id="What" defaultMessage="What" />
258
+ </Table.HeaderCell>
259
+ <Table.HeaderCell style={{ width: '15%' }}>
260
+ <FormattedMessage id="Who" defaultMessage="Who" />
261
+ </Table.HeaderCell>
262
+ <Table.HeaderCell style={{ width: '15%' }}>
263
+ <FormattedMessage id="When" defaultMessage="When" />
264
+ </Table.HeaderCell>
265
+ <Table.HeaderCell style={{ width: '25%' }}>
266
+ <FormattedMessage
267
+ id="Change Note"
268
+ defaultMessage="Change Note"
269
+ />
270
+ </Table.HeaderCell>
271
+ <Table.HeaderCell style={{ width: '15%' }} />
272
+ </Table.Row>
273
+ </Table.Header>
274
+ <Table.Body>
275
+ {map(entries, (entry) => (
276
+ <Table.Row key={entry.time}>
277
+ <Table.Cell>
278
+ {('version' in entry && entry.version > 0 && (
279
+ <Link
280
+ className="item"
281
+ to={`${getBaseUrl(this.props.pathname)}/diff?one=${
282
+ entry.version - 1
283
+ }&two=${entry.version}`}
284
+ >
285
+ {entry.version}
286
+ </Link>
287
+ )) || <span>{entry.version}</span>}
288
+ </Table.Cell>
289
+ <Table.Cell>
290
+ {('version' in entry && entry.version > 0 && (
291
+ <Link
292
+ className="item"
293
+ to={`${getBaseUrl(this.props.pathname)}/diff?one=${
294
+ entry.version - 1
295
+ }&two=${entry.version}`}
296
+ >
297
+ {entry.transition_title}
298
+ </Link>
299
+ )) || (
300
+ <span>
301
+ {entry.transition_title}
302
+ {entry.type === 'workflow' &&
303
+ ` (${
304
+ entry.action ? `${entry.prev_state_title} → ` : ''
305
+ }${entry.state_title})`}
306
+ </span>
307
+ )}
308
+ </Table.Cell>
309
+ <Table.Cell>{entry.actor.fullname}</Table.Cell>
310
+ <Table.Cell>
311
+ <FormattedDate date={entry.time} />
312
+ </Table.Cell>
313
+ <Table.Cell>{entry.comments}</Table.Cell>
314
+ <Table.Cell>
315
+ {entry.type === 'versioning' && (
316
+ <Dropdown icon="ellipsis horizontal">
317
+ <Dropdown.Menu className="left">
318
+ {'version' in entry && entry.version > 0 && (
319
+ <Link
320
+ className="item"
321
+ to={`${getBaseUrl(
322
+ this.props.pathname,
323
+ )}/diff?one=${entry.version - 1}&two=${
324
+ entry.version
325
+ }`}
326
+ >
327
+ <Icon name="copy" />{' '}
328
+ <FormattedMessage
329
+ id="View changes"
330
+ defaultMessage="View changes"
331
+ />
332
+ </Link>
333
+ )}
334
+ {'version' in entry && (
335
+ <Link
336
+ className="item"
337
+ to={`${getBaseUrl(this.props.pathname)}?version=${
338
+ entry.version
339
+ }`}
340
+ >
341
+ <Icon name="eye" />{' '}
342
+ <FormattedMessage
343
+ id="View this revision"
344
+ defaultMessage="View this revision"
345
+ />
346
+ </Link>
347
+ )}
348
+ {'version' in entry &&
349
+ entry.may_revert &&
350
+ !entry.is_current && (
351
+ <Dropdown.Item
352
+ value={entry.version}
353
+ onClick={this.onRevert}
354
+ >
355
+ <Icon name="undo" />{' '}
356
+ <FormattedMessage
357
+ id="Revert to this revision"
358
+ defaultMessage="Revert to this revision"
359
+ />
360
+ </Dropdown.Item>
361
+ )}
362
+ </Dropdown.Menu>
363
+ </Dropdown>
364
+ )}
365
+ </Table.Cell>
366
+ </Table.Row>
367
+ ))}
368
+ </Table.Body>
369
+ </Table>
370
+ {this.props.content?.copied_from && (
371
+ <Message warning icon attached="bottom">
372
+ <Icon name="arrow left" />
373
+ <Message.Content>
374
+ <Message.Header>
375
+ <FormattedMessage {...messages.olderVersionAvailable} />
376
+ </Message.Header>
377
+ <FormattedMessage
378
+ {...messages.thereIsOlderVersionAt}
379
+ values={{
380
+ link: (
381
+ <Link
382
+ to={new URL(this.props.content.copied_from).pathname}
383
+ >
384
+ {new URL(this.props.content.copied_from).pathname
385
+ .split('/')
386
+ .pop() || 'older version'}
387
+ </Link>
388
+ ),
389
+ }}
390
+ />
391
+ </Message.Content>
392
+ </Message>
393
+ )}
394
+ </Segment.Group>
395
+ {this.state.isClient &&
396
+ createPortal(
397
+ <Toolbar
398
+ pathname={this.props.pathname}
399
+ hideDefaultViewButtons
400
+ inner={
401
+ <Link
402
+ to={`${getBaseUrl(this.props.pathname)}`}
403
+ className="item"
404
+ >
405
+ <IconNext
406
+ name={backSVG}
407
+ className="contents circled"
408
+ size="30px"
409
+ title={this.props.intl.formatMessage(messages.back)}
410
+ />
411
+ </Link>
412
+ }
413
+ />,
414
+ document.getElementById('toolbar'),
415
+ )}
416
+ </Container>
417
+ );
418
+ }
419
+ }
420
+
421
+ export default compose(
422
+ injectIntl,
423
+ asyncConnect([
424
+ {
425
+ key: 'actions',
426
+ // Dispatch async/await to make the operation synchronous, otherwise it returns
427
+ // before the promise is resolved
428
+ promise: async ({ location, store: { dispatch } }) =>
429
+ await dispatch(listActions(getBaseUrl(location.pathname))),
430
+ },
431
+ ]),
432
+ connect(
433
+ (state, props) => ({
434
+ objectActions: state.actions.actions.object,
435
+ token: state.userSession.token,
436
+ entries: state.history.entries,
437
+ pathname: props.location.pathname,
438
+ title: state.content.data?.title,
439
+ content: state.content.data,
440
+ revertRequest: state.history.revert,
441
+ }),
442
+ { getHistory, revertHistory },
443
+ ),
444
+ )(History);
@@ -21,6 +21,7 @@ import { Button, Comment, Container, Icon } from 'semantic-ui-react';
21
21
  import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
22
22
  import { formatRelativeDate } from '@plone/volto/helpers/Utils/Date';
23
23
  import config from '@plone/volto/registry';
24
+ import './comments.less';
24
25
  // import { Button, Grid, Segment, Container } from 'semantic-ui-react';
25
26
 
26
27
  const messages = defineMessages({
@@ -341,7 +342,8 @@ class Comments extends Component {
341
342
  <Comment.Actions>
342
343
  {comment.can_reply && (
343
344
  <Comment.Action
344
- as="a"
345
+ as="button"
346
+ type="button"
345
347
  aria-label={this.props.intl.formatMessage(messages.reply)}
346
348
  onClick={() => this.setReplyTo(comment.comment_id)}
347
349
  >
@@ -350,6 +352,8 @@ class Comments extends Component {
350
352
  )}
351
353
  {comment.is_editable && (
352
354
  <Comment.Action
355
+ as="button"
356
+ type="button"
353
357
  onClick={() =>
354
358
  this.onEdit({
355
359
  id: flattenToAppURL(comment['@id']),
@@ -367,6 +371,8 @@ class Comments extends Component {
367
371
  )}
368
372
  {comment.is_deletable && (
369
373
  <Comment.Action
374
+ as="button"
375
+ type="button"
370
376
  aria-label={this.props.intl.formatMessage(messages.delete)}
371
377
  onClick={() => this.onDelete(flattenToAppURL(comment['@id']))}
372
378
  color="red"
@@ -380,7 +386,8 @@ class Comments extends Component {
380
386
  </Comment.Action>
381
387
  )}
382
388
  <Comment.Action
383
- as="a"
389
+ as="button"
390
+ type="button"
384
391
  onClick={() => this.hideReply(comment.comment_id)}
385
392
  >
386
393
  {allCommentsWithCildren[comment.comment_id].children.length >
@@ -362,11 +362,11 @@ describe('Comments', () => {
362
362
  </Provider>,
363
363
  );
364
364
 
365
- const actions = container.querySelectorAll('.actions a');
365
+ const actions = container.querySelectorAll('.actions button');
366
366
 
367
- fireEvent.click(container.querySelector('a[aria-label="Reply"]'));
368
- fireEvent.click(container.querySelector('a[aria-label="Edit"]'));
369
- fireEvent.click(container.querySelector('a[aria-label="Delete"]'));
367
+ fireEvent.click(container.querySelector('button[aria-label="Reply"]'));
368
+ fireEvent.click(container.querySelector('button[aria-label="Edit"]'));
369
+ fireEvent.click(container.querySelector('button[aria-label="Delete"]'));
370
370
  fireEvent.click(actions[actions.length - 1]);
371
371
  fireEvent.click(
372
372
  container.querySelector('#comment-add-id button[aria-label="Comment"]'),
@@ -0,0 +1,16 @@
1
+ .ui.comments .comment .actions button {
2
+ display: inline-block;
3
+ padding: 0;
4
+ border: none;
5
+ margin: 0em 0.75em 0em 0em;
6
+ background: none;
7
+ color: #000000;
8
+ cursor: pointer;
9
+ font: inherit;
10
+ text-decoration: none;
11
+ }
12
+
13
+ .ui.comments .comment .actions button:focus {
14
+ outline: 1px solid #2185d0;
15
+ outline-offset: 1px;
16
+ }
@@ -11,6 +11,7 @@ import { withRouter } from 'react-router-dom';
11
11
  import { UniversalLink } from '@plone/volto/components';
12
12
  import { getBaseUrl, hasApiExpander } from '@plone/volto/helpers';
13
13
  import { getNavigation } from '@plone/volto/actions';
14
+ import { getNavigationSettings } from '@eeacms/volto-eea-website-theme/actions';
14
15
  import { Header, Logo } from '@eeacms/volto-eea-design-system/ui';
15
16
  import { usePrevious } from '@eeacms/volto-eea-design-system/helpers';
16
17
  import eeaFlag from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/images/Header/eea.png';
@@ -58,6 +59,58 @@ const EEAHeader = ({ pathname, token, items, history, subsite }) => {
58
59
  const width = useSelector((state) => state.screen?.width);
59
60
  const dispatch = useDispatch();
60
61
  const previousToken = usePrevious(token);
62
+ const navigationSettings = useSelector(
63
+ (state) => state.navigationSettings?.settings || {},
64
+ );
65
+ const navigationLoaded = useSelector(
66
+ (state) => state.navigationSettings?.loaded,
67
+ );
68
+
69
+ // Combine navigation settings from backend with config fallback
70
+ const configLayouts = config.settings?.menuItemsLayouts || {};
71
+ const enhancedLayouts = { ...configLayouts };
72
+
73
+ // Map navigation settings to menu item URLs
74
+ if (items) {
75
+ items.forEach((menuItem) => {
76
+ // Check if we have navigation settings for any route that might match this menu item
77
+ Object.keys(navigationSettings).forEach((routeId) => {
78
+ const route = navigationSettings[routeId];
79
+ const backendSettings = {};
80
+
81
+ if (route.hideChildrenFromNavigation !== undefined) {
82
+ backendSettings.hideChildrenFromNavigation =
83
+ route.hideChildrenFromNavigation;
84
+ }
85
+
86
+ if (route.menuItemChildrenListColumns !== undefined) {
87
+ // Convert strings back to integers for header usage
88
+ backendSettings.menuItemChildrenListColumns = Array.isArray(
89
+ route.menuItemChildrenListColumns,
90
+ )
91
+ ? route.menuItemChildrenListColumns
92
+ .map((val) =>
93
+ typeof val === 'string' ? parseInt(val, 10) : val,
94
+ )
95
+ .filter((val) => !isNaN(val))
96
+ : route.menuItemChildrenListColumns;
97
+ }
98
+
99
+ if (route.menuItemColumns !== undefined) {
100
+ // Use menuItemColumns directly as they're already in semantic UI format
101
+ backendSettings.menuItemColumns = route.menuItemColumns;
102
+ }
103
+
104
+ if (Object.keys(backendSettings).length > 0) {
105
+ // Override the config setting with backend data
106
+ enhancedLayouts[routeId] = {
107
+ ...enhancedLayouts[routeId],
108
+ ...backendSettings,
109
+ };
110
+ }
111
+ });
112
+ });
113
+ }
61
114
 
62
115
  React.useEffect(() => {
63
116
  const base_url = getBaseUrl(pathname);
@@ -72,7 +125,12 @@ const EEAHeader = ({ pathname, token, items, history, subsite }) => {
72
125
  if (token !== previousToken) {
73
126
  dispatch(getNavigation(base_url, settings.navDepth));
74
127
  }
75
- }, [pathname, token, dispatch, previousToken]);
128
+
129
+ // Fetch navigation settings
130
+ if (!navigationLoaded) {
131
+ dispatch(getNavigationSettings(pathname));
132
+ }
133
+ }, [pathname, token, dispatch, previousToken, navigationLoaded]);
76
134
 
77
135
  return (
78
136
  <Header menuItems={items}>
@@ -174,6 +232,7 @@ const EEAHeader = ({ pathname, token, items, history, subsite }) => {
174
232
  </div>
175
233
  }
176
234
  menuItems={items}
235
+ menuItemsLayouts={enhancedLayouts}
177
236
  renderGlobalMenuItem={(item, { onClick }) => (
178
237
  <a
179
238
  href={item.url || '/'}