@eeacms/volto-eea-website-theme 4.3.0 → 4.3.2

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,11 +4,19 @@ 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
- ### [4.3.0](https://github.com/eea/volto-eea-website-theme/compare/4.2.0...4.3.0) - 23 April 2026
7
+ ### [4.3.2](https://github.com/eea/volto-eea-website-theme/compare/4.3.1...4.3.2) - 15 May 2026
8
8
 
9
- #### :rocket: New Features
9
+ #### :bug: Bug Fixes
10
+
11
+ - fix(Header): use navroot language to fetch navigation, fix subsite case - refs #303244 [Miu Razvan - [`c25711e`](https://github.com/eea/volto-eea-website-theme/commit/c25711ec3f8492d442c9a130e5e4cb9feb1e80ad)]
12
+
13
+ ### [4.3.1](https://github.com/eea/volto-eea-website-theme/compare/4.3.0...4.3.1) - 14 May 2026
14
+
15
+ #### :bug: Bug Fixes
16
+
17
+ - fix: Scheduled label in contents - refs #303325 [Teodor Voicu - [`7c7d4d4`](https://github.com/eea/volto-eea-website-theme/commit/7c7d4d43ced32570f2ea1dd104992f5da4164594)]
10
18
 
11
- - feat: Align Toast with EEA Design System Message - refs #294806 [Alin Voinea - [`53106b1`](https://github.com/eea/volto-eea-website-theme/commit/53106b1099b4bfae59a1746e7dc62c49a8ae63cd)]
19
+ ### [4.3.0](https://github.com/eea/volto-eea-website-theme/compare/4.2.0...4.3.0) - 24 April 2026
12
20
 
13
21
  #### :hammer_and_wrench: Others
14
22
 
@@ -77,6 +85,8 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
77
85
 
78
86
  #### :house: Internal changes
79
87
 
88
+ - chore: [JENKINSFILE] add package version in sonarqube [valentinab25 - [`b15b302`](https://github.com/eea/volto-eea-website-theme/commit/b15b302ff88ec6afa6901ee7e1726cc4e04a8739)]
89
+ - chore: [JENKINSFILE] use sonarqube branches [EEA Jenkins - [`36f2e1e`](https://github.com/eea/volto-eea-website-theme/commit/36f2e1e176471b3753d77ab8aaf0cba577b43b36)]
80
90
 
81
91
  ### [3.19.0](https://github.com/eea/volto-eea-website-theme/compare/3.18.1...3.19.0) - 11 February 2026
82
92
 
@@ -139,6 +149,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
139
149
  #### :hammer_and_wrench: Others
140
150
 
141
151
  - Release 3.12.0 [Alin Voinea - [`ea1e961`](https://github.com/eea/volto-eea-website-theme/commit/ea1e96144684b2a8238ae476fd8d47de82ff42b4)]
152
+ - Add Sonarqube tag using bise-frontend addons list [EEA Jenkins - [`2cac56e`](https://github.com/eea/volto-eea-website-theme/commit/2cac56e22e580e042d368b69254172539ed33b65)]
142
153
  ### [3.11.0](https://github.com/eea/volto-eea-website-theme/compare/3.10.1...3.11.0) - 29 September 2025
143
154
 
144
155
  #### :bug: Bug Fixes
@@ -232,6 +243,8 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
232
243
 
233
244
  #### :hammer_and_wrench: Others
234
245
 
246
+ - Add Sonarqube tag using fise-frontend addons list [EEA Jenkins - [`3d50874`](https://github.com/eea/volto-eea-website-theme/commit/3d50874281ebcb91a1ae3727248f656a6f26c603)]
247
+ - Add Sonarqube tag using ied-frontend addons list [EEA Jenkins - [`a3f7676`](https://github.com/eea/volto-eea-website-theme/commit/a3f76767d99cb6dd93cc7fa0eafdea57e9000b2c)]
235
248
  ### [3.5.4](https://github.com/eea/volto-eea-website-theme/compare/3.5.3...3.5.4) - 30 January 2025
236
249
 
237
250
  #### :bug: Bug Fixes
@@ -332,6 +345,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
332
345
 
333
346
  #### :hammer_and_wrench: Others
334
347
 
348
+ - Add Sonarqube tag using bise-frontend addons list [EEA Jenkins - [`e417f83`](https://github.com/eea/volto-eea-website-theme/commit/e417f839312045c56b67ab8134fe5b0622b3e2c3)]
335
349
  ## [3.0.0](https://github.com/eea/volto-eea-website-theme/compare/2.4.0...3.0.0) - 21 October 2024
336
350
 
337
351
  #### :nail_care: Enhancements
@@ -417,6 +431,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
417
431
  - Refs #269520 - other_organisation test [Tripon Eugen - [`7558b27`](https://github.com/eea/volto-eea-website-theme/commit/7558b27a9d1cd6d3480a32b296c7325c796006c9)]
418
432
  - Refs #269520 - other_organisation token widget edit mode [Tripon Eugen - [`b6df127`](https://github.com/eea/volto-eea-website-theme/commit/b6df127bd91b6f55dda8f468e1107037d43752ff)]
419
433
  - Refs #269520 - other_organisation token widget [Tripon Eugen - [`6fd7543`](https://github.com/eea/volto-eea-website-theme/commit/6fd7543894bd2c593a1729116befd95c82ea3c8f)]
434
+ - Add Sonarqube tag using marine-frontend addons list [EEA Jenkins - [`cbc36c7`](https://github.com/eea/volto-eea-website-theme/commit/cbc36c74dfcd5f7cb1013a4c673d9c02e0023766)]
420
435
  ### [2.1.1](https://github.com/eea/volto-eea-website-theme/compare/2.1.0...2.1.1) - 28 May 2024
421
436
 
422
437
  #### :bug: Bug Fixes
@@ -501,6 +516,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
501
516
 
502
517
  #### :hammer_and_wrench: Others
503
518
 
519
+ - Add Sonarqube tag using insitu-frontend addons list [EEA Jenkins - [`adc6730`](https://github.com/eea/volto-eea-website-theme/commit/adc6730e21a37afb865b842182624401de6a29f5)]
504
520
  ### [1.33.1](https://github.com/eea/volto-eea-website-theme/compare/1.33.0...1.33.1) - 4 April 2024
505
521
 
506
522
  #### :bug: Bug Fixes
@@ -634,6 +650,8 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
634
650
 
635
651
  - bump version [Razvan - [`721e939`](https://github.com/eea/volto-eea-website-theme/commit/721e939d12e324b459ebfa78a2e656ee7142a3d6)]
636
652
  - merge master into this branch [Razvan - [`586c8f9`](https://github.com/eea/volto-eea-website-theme/commit/586c8f910bac55a043bd8dda60e9444bd2ae1663)]
653
+ - Add Sonarqube tag using freshwater-frontend addons list [EEA Jenkins - [`fd90044`](https://github.com/eea/volto-eea-website-theme/commit/fd9004442a9d1d465f7601ecdefe3e23c61e6a9c)]
654
+ - Add Sonarqube tag using insitu-frontend addons list [EEA Jenkins - [`4bc3dd3`](https://github.com/eea/volto-eea-website-theme/commit/4bc3dd3ae412a66befd04b5b80fab3716c929240)]
637
655
  - test: Update jest,Jenkinsfile,lint to volto-addons-template PR30 [valentinab25 - [`c4dbd28`](https://github.com/eea/volto-eea-website-theme/commit/c4dbd289358205bc2d849aab7edb11ccf3b89cee)]
638
656
  - fix tests [Razvan - [`042330b`](https://github.com/eea/volto-eea-website-theme/commit/042330bc97d32ffe7ba769b4f2453f71cffed706)]
639
657
  - remove RemoveSchema logic [Razvan - [`08d10f8`](https://github.com/eea/volto-eea-website-theme/commit/08d10f8bf6f75478260e4e4c66d7316ba87b907a)]
@@ -728,6 +746,11 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
728
746
  - test: Add real image to cypress test [Alin Voinea - [`4ff591a`](https://github.com/eea/volto-eea-website-theme/commit/4ff591ae3318c9588b4e2114582c0fa6cfdf31ae)]
729
747
  - test: Add cypress tests for Image block styling position and align [Alin Voinea - [`7341ef7`](https://github.com/eea/volto-eea-website-theme/commit/7341ef7b92714fc0cc3ab0c31c39033e7b3e19e7)]
730
748
  - Revert "change(tests): commented out rss test since title block config is missing" [Alin Voinea - [`fb61191`](https://github.com/eea/volto-eea-website-theme/commit/fb611918d6ca380b89b594f283dcf9f685a4b294)]
749
+ - test: [JENKINS] Use java17 for sonarqube scanner [valentinab25 - [`6a3be30`](https://github.com/eea/volto-eea-website-theme/commit/6a3be3092589411af7808a235f76de5222fd3868)]
750
+ - test: [JENKINS] Run cypress in started frontend container [valentinab25 - [`c3978f2`](https://github.com/eea/volto-eea-website-theme/commit/c3978f23375ef066e9fd6f6c2e34ba6c1c058f69)]
751
+ - test: [JENKINS] Add cpu limit on cypress docker [valentinab25 - [`f672779`](https://github.com/eea/volto-eea-website-theme/commit/f672779e845bec9240ccc901e9f53ec80c5a1819)]
752
+ - test: [JENKINS] Increase shm-size to cypress docker [valentinab25 - [`ae5d8e3`](https://github.com/eea/volto-eea-website-theme/commit/ae5d8e3f4e04dc2808d47ce2ee886e1b23b528da)]
753
+ - test: [JENKINS] Improve cypress time [valentinab25 - [`170ff0c`](https://github.com/eea/volto-eea-website-theme/commit/170ff0c8e3b30e69479bdf1117e811fea94f1027)]
731
754
  ### [1.23.0](https://github.com/eea/volto-eea-website-theme/compare/1.22.1...1.23.0) - 2 November 2023
732
755
 
733
756
  #### :rocket: New Features
@@ -740,6 +763,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
740
763
 
741
764
  #### :house: Internal changes
742
765
 
766
+ - chore: [JENKINS] Refactor automated testing [valentinab25 - [`f28fce3`](https://github.com/eea/volto-eea-website-theme/commit/f28fce3d1eb815f95fb9aa40de42b10b7e8e30c5)]
743
767
  - chore: husky, lint-staged use fixed versions [valentinab25 - [`6d15088`](https://github.com/eea/volto-eea-website-theme/commit/6d150886c5aeb2ca0b569270486e60f7cc274e2c)]
744
768
  - chore:volto 16 in tests, update docs, fix stylelint overrides [valentinab25 - [`20c0323`](https://github.com/eea/volto-eea-website-theme/commit/20c032380b33c0077c869a05136f93e2fb68e5d4)]
745
769
 
@@ -925,6 +949,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
925
949
 
926
950
  #### :house: Internal changes
927
951
 
952
+ - chore: [JENKINS] Deprecate circularity website [valentinab25 - [`370dcbf`](https://github.com/eea/volto-eea-website-theme/commit/370dcbfbf1a8135ce7b1b3b271b004552a631837)]
928
953
 
929
954
  #### :hammer_and_wrench: Others
930
955
 
@@ -1080,6 +1105,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
1080
1105
 
1081
1106
  #### :hammer_and_wrench: Others
1082
1107
 
1108
+ - Add Sonarqube tag using eea-website-frontend addons list [EEA Jenkins - [`6c5e2f8`](https://github.com/eea/volto-eea-website-theme/commit/6c5e2f80456e2061d9e9c15fd0a0b91b9ac70568)]
1083
1109
  ### [1.9.1](https://github.com/eea/volto-eea-website-theme/compare/1.9.0...1.9.1) - 28 February 2023
1084
1110
 
1085
1111
  #### :bug: Bug Fixes
@@ -1226,6 +1252,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
1226
1252
 
1227
1253
  - For some reasons types is a string [Alin Voinea - [`3769a09`](https://github.com/eea/volto-eea-website-theme/commit/3769a0981181d5b633f3498daebbe96be8b4b833)]
1228
1254
  - Fix(redirect): o.filter - refs #157627 [Alin Voinea - [`deb23da`](https://github.com/eea/volto-eea-website-theme/commit/deb23da846444cc96539697fd798429ae0abe89e)]
1255
+ - Add Sonarqube tag using advisory-board-frontend addons list [EEA Jenkins - [`f1fffc5`](https://github.com/eea/volto-eea-website-theme/commit/f1fffc5db96725440863d545580b4e76cce4b796)]
1229
1256
  ### [1.5.0](https://github.com/eea/volto-eea-website-theme/compare/1.4.2...1.5.0) - 9 January 2023
1230
1257
 
1231
1258
  #### :hammer_and_wrench: Others
@@ -1259,6 +1286,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
1259
1286
 
1260
1287
  - Release 1.4.0 [Alin Voinea - [`bd42a0d`](https://github.com/eea/volto-eea-website-theme/commit/bd42a0d26e928cac5d99933194755da3db06b341)]
1261
1288
  - bump version to use as volto-eea-design-system [David Ichim - [`f4be047`](https://github.com/eea/volto-eea-website-theme/commit/f4be047328b46399b03b612d378b18aaf82e7dc1)]
1289
+ - Add Sonarqube tag using advisory-board-frontend addons list [EEA Jenkins - [`9b7cfef`](https://github.com/eea/volto-eea-website-theme/commit/9b7cfefb4d34fc1c948015e491feb370f9795bd8)]
1262
1290
  - test(Jenkins): Run tests and cypress with latest canary @plone/volto [Alin Voinea - [`df252a9`](https://github.com/eea/volto-eea-website-theme/commit/df252a9bfed0bb86cadf53c59dd1603b1e2cd822)]
1263
1291
  ### [1.3.2](https://github.com/eea/volto-eea-website-theme/compare/1.3.1...1.3.2) - 16 December 2022
1264
1292
 
@@ -1268,6 +1296,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
1268
1296
 
1269
1297
  #### :hammer_and_wrench: Others
1270
1298
 
1299
+ - Add Sonarqube tag using cca-frontend addons list [EEA Jenkins - [`a43c658`](https://github.com/eea/volto-eea-website-theme/commit/a43c658a7920c8df95e763b9a637f38ce77eba2c)]
1271
1300
  - Better razzle.config [Tiberiu Ichim - [`81dbf48`](https://github.com/eea/volto-eea-website-theme/commit/81dbf48815fb27facb4f82c9b764540fdf188b2e)]
1272
1301
  - Better razzle.config [Tiberiu Ichim - [`7bc9da2`](https://github.com/eea/volto-eea-website-theme/commit/7bc9da2cd837ab62a95cd29979cdd9b0055b7d67)]
1273
1302
  ### [1.3.1](https://github.com/eea/volto-eea-website-theme/compare/1.3.0...1.3.1) - 28 November 2022
@@ -1278,6 +1307,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
1278
1307
 
1279
1308
  #### :hammer_and_wrench: Others
1280
1309
 
1310
+ - yarn 3 [Alin Voinea - [`ea7a709`](https://github.com/eea/volto-eea-website-theme/commit/ea7a7094945312776e9b6f44e371178603e92139)]
1281
1311
  ### [1.3.0](https://github.com/eea/volto-eea-website-theme/compare/1.2.0...1.3.0) - 22 November 2022
1282
1312
 
1283
1313
  #### :rocket: New Features
@@ -1318,6 +1348,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
1318
1348
  - Add subsite class to body [Tiberiu Ichim - [`74d700f`](https://github.com/eea/volto-eea-website-theme/commit/74d700fbfd6249a8604762a7e4e49cce857db0f3)]
1319
1349
  - Add subsite info to header [Tiberiu Ichim - [`47daf8b`](https://github.com/eea/volto-eea-website-theme/commit/47daf8bb6374a1222040626b19d4154df7ba1b83)]
1320
1350
  - fix eslint [Miu Razvan - [`eb8d0a7`](https://github.com/eea/volto-eea-website-theme/commit/eb8d0a790bc70c0aae256c6ff35f63c4885f338e)]
1351
+ - Add Sonarqube tag using circularity-frontend addons list [EEA Jenkins - [`cc578a4`](https://github.com/eea/volto-eea-website-theme/commit/cc578a413b205a8e61e091fab3a88f94cedefc89)]
1321
1352
  ### [1.1.0](https://github.com/eea/volto-eea-website-theme/compare/1.0.0...1.1.0) - 28 October 2022
1322
1353
 
1323
1354
  #### :nail_care: Enhancements
@@ -1365,6 +1396,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
1365
1396
 
1366
1397
  #### :hammer_and_wrench: Others
1367
1398
 
1399
+ - Add Sonarqube tag using eea-website-frontend addons list [EEA Jenkins - [`33b56ac`](https://github.com/eea/volto-eea-website-theme/commit/33b56acb13fbaf0c5b79e8fc6e13c4b699c79c90)]
1368
1400
  ### [0.7.3](https://github.com/eea/volto-eea-website-theme/compare/0.7.2...0.7.3) - 22 September 2022
1369
1401
 
1370
1402
  #### :hammer_and_wrench: Others
@@ -1632,6 +1664,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
1632
1664
  - Header refactor, add custom logo #5 [ichim-david - [`4950235`](https://github.com/eea/volto-eea-website-theme/commit/49502358105437cfeac3b144e6d301cb59aa2346)]
1633
1665
  - Update footer.config with new publication card component [ichim-david - [`2e38e9a`](https://github.com/eea/volto-eea-website-theme/commit/2e38e9a417f835009d60c80d4eb4b30229f55e45)]
1634
1666
  - feature(breadcrumbs): implement eea-design-system breadcrumb as Volto component #32 #7 [ichim-david - [`181af41`](https://github.com/eea/volto-eea-website-theme/commit/181af4125ce2b9ddac56dab4723cb11c26633221)]
1667
+ - Add Sonarqube tag using eea-website-frontend addons list [EEA Jenkins - [`da8ceb6`](https://github.com/eea/volto-eea-website-theme/commit/da8ceb68ea68bfbc9504e48ccd4d68277f11ab9a)]
1635
1668
  - use breadcrumbs from eea-design-system [nileshgulia1 - [`db2f9e9`](https://github.com/eea/volto-eea-website-theme/commit/db2f9e9a4327420a3cce9a9903cd88549b129eab)]
1636
1669
  - Update theme.config [ichim-david - [`8eca4f4`](https://github.com/eea/volto-eea-website-theme/commit/8eca4f40397a4aeca6d39029c92db78968d37064)]
1637
1670
  - Added keyContent component to theme.config [ichim-david - [`d86f202`](https://github.com/eea/volto-eea-website-theme/commit/d86f202d0274d839487a88b51cae9a0e899beb23)]
@@ -1673,4 +1706,5 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
1673
1706
 
1674
1707
  #### :hammer_and_wrench: Others
1675
1708
 
1709
+ - yarn bootstrap [Alin Voinea - [`6995e9e`](https://github.com/eea/volto-eea-website-theme/commit/6995e9e091f21fdbbdffa8a44fc0e2c626f6d46a)]
1676
1710
  - Initial commit [Alin Voinea - [`6a9c03a`](https://github.com/eea/volto-eea-website-theme/commit/6a9c03a7cebe71ca87e82cf58c42904063e9d8d3)]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-eea-website-theme",
3
- "version": "4.3.0",
3
+ "version": "4.3.2",
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",
@@ -0,0 +1,425 @@
1
+ /**
2
+ * Contents item component.
3
+ * @module components/manage/Contents/ContentsItem
4
+ */
5
+
6
+ import React from 'react';
7
+ import { Button, Table, Menu, Divider } from 'semantic-ui-react';
8
+ import { Link } from 'react-router-dom';
9
+ import PropTypes from 'prop-types';
10
+ import map from 'lodash/map';
11
+ import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
12
+ import Circle from '@plone/volto/components/manage/Contents/circle';
13
+ import FormattedDate from '@plone/volto/components/theme/FormattedDate/FormattedDate';
14
+ import Icon from '@plone/volto/components/theme/Icon/Icon';
15
+ import Popup from '@plone/volto/components/theme/Popup/Popup';
16
+ import { getContentIcon } from '@plone/volto/helpers/Content/Content';
17
+ import moreSVG from '@plone/volto/icons/more.svg';
18
+ import checkboxUncheckedSVG from '@plone/volto/icons/checkbox-unchecked.svg';
19
+ import checkboxCheckedSVG from '@plone/volto/icons/checkbox-checked.svg';
20
+ import cutSVG from '@plone/volto/icons/cut.svg';
21
+ import deleteSVG from '@plone/volto/icons/delete.svg';
22
+ import copySVG from '@plone/volto/icons/copy.svg';
23
+ import showSVG from '@plone/volto/icons/show.svg';
24
+ import moveUpSVG from '@plone/volto/icons/move-up.svg';
25
+ import moveDownSVG from '@plone/volto/icons/move-down.svg';
26
+ import editingSVG from '@plone/volto/icons/editing.svg';
27
+ import dragSVG from '@plone/volto/icons/drag.svg';
28
+ import cx from 'classnames';
29
+
30
+ import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
31
+
32
+ const messages = defineMessages({
33
+ private: {
34
+ id: 'private',
35
+ defaultMessage: 'Private',
36
+ },
37
+ pending: {
38
+ id: 'pending',
39
+ defaultMessage: 'Pending',
40
+ },
41
+ published: {
42
+ id: 'published',
43
+ defaultMessage: 'Published',
44
+ },
45
+ intranet: {
46
+ id: 'intranet',
47
+ defaultMessage: 'Intranet',
48
+ },
49
+ draft: {
50
+ id: 'draft',
51
+ defaultMessage: 'Draft',
52
+ },
53
+ no_workflow_state: {
54
+ id: 'no workflow state',
55
+ defaultMessage: 'No workflow state',
56
+ },
57
+ none: {
58
+ id: 'Not available',
59
+ defaultMessage: 'None',
60
+ },
61
+ });
62
+
63
+ function getColor(string) {
64
+ switch (string) {
65
+ case 'private':
66
+ return '#ed4033';
67
+ case 'published':
68
+ return '#007bc1';
69
+ case 'intranet':
70
+ return '#51aa55';
71
+ case 'draft':
72
+ return '#f6a808';
73
+ default:
74
+ return 'grey';
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Contents item component class.
80
+ * @function ContentsItemComponent
81
+ * @returns {string} Markup of the component.
82
+ */
83
+ export const ContentsItemComponent = ({
84
+ item,
85
+ selected,
86
+ onClick,
87
+ indexes,
88
+ onCut,
89
+ onCopy,
90
+ onDelete,
91
+ onMoveToTop,
92
+ onMoveToBottom,
93
+ connectDragPreview,
94
+ connectDragSource,
95
+ connectDropTarget,
96
+ isDragging,
97
+ order,
98
+ }) => {
99
+ const intl = useIntl();
100
+ const now = Date.now();
101
+ const expirationTime = new Date(item.ExpirationDate).getTime();
102
+ const effectiveTime = new Date(item.EffectiveDate).getTime();
103
+ const isExpired = item.ExpirationDate !== 'None' && expirationTime < now;
104
+ const isScheduled = item.EffectiveDate !== 'None' && effectiveTime > now;
105
+
106
+ return connectDropTarget(
107
+ connectDragPreview(
108
+ <tr
109
+ key={item['@id']}
110
+ className={cx('', { 'dragging-row': isDragging })}
111
+ aria-label={item['@id']}
112
+ >
113
+ <Table.Cell className={cx('', { 'dragging-cell': isDragging })}>
114
+ {connectDragSource(
115
+ <div style={{ display: 'inline-block' }}>
116
+ <Button icon basic>
117
+ <Icon
118
+ name={dragSVG}
119
+ size="20px"
120
+ color="#878f93"
121
+ className="content drag handle"
122
+ />
123
+ </Button>
124
+ </div>,
125
+ )}
126
+ </Table.Cell>
127
+ <Table.Cell className={cx('', { 'dragging-cell': isDragging })}>
128
+ {selected ? (
129
+ <Button
130
+ icon
131
+ basic
132
+ aria-label="Unchecked"
133
+ onClick={(e) => onClick(e, item['@id'])}
134
+ >
135
+ <Icon
136
+ name={checkboxCheckedSVG}
137
+ color="#007eb1"
138
+ size="24px"
139
+ className="checked"
140
+ />
141
+ </Button>
142
+ ) : (
143
+ <Button
144
+ icon
145
+ basic
146
+ aria-label="Checked"
147
+ onClick={(e) => onClick(e, item['@id'])}
148
+ >
149
+ <Icon
150
+ name={checkboxUncheckedSVG}
151
+ color="#826a6a"
152
+ size="24px"
153
+ className="unchecked"
154
+ />
155
+ </Button>
156
+ )}
157
+ </Table.Cell>
158
+ <Table.Cell className={cx('', { 'dragging-cell': isDragging })}>
159
+ <Link
160
+ className="icon-align-name"
161
+ to={`${item['@id']}${item.is_folderish ? '/contents' : ''}`}
162
+ >
163
+ <div
164
+ className="expire-align"
165
+ style={{ flex: '1 1 auto', minWidth: 0 }}
166
+ >
167
+ <Icon
168
+ name={getContentIcon(item['@type'], item.is_folderish)}
169
+ size="20px"
170
+ className="icon-margin"
171
+ color="#878f93"
172
+ title={item['Type'] || item['@type']}
173
+ />{' '}
174
+ <span
175
+ title={item.title}
176
+ style={{ flex: '1 1 auto', minWidth: 0 }}
177
+ >
178
+ {' '}
179
+ {item.title}
180
+ </span>
181
+ </div>
182
+ {isExpired && (
183
+ <Button
184
+ className="button-margin"
185
+ size="mini"
186
+ style={{ flexShrink: 0 }}
187
+ >
188
+ <FormattedMessage id="Expired" defaultMessage="Expired" />
189
+ </Button>
190
+ )}
191
+ {isScheduled && (
192
+ <Button
193
+ className="button-margin effective-future"
194
+ size="mini"
195
+ style={{ flexShrink: 0 }}
196
+ >
197
+ <FormattedMessage id="Scheduled" defaultMessage="Scheduled" />
198
+ </Button>
199
+ )}
200
+ </Link>
201
+ </Table.Cell>
202
+ {map(indexes, (index) => (
203
+ <Table.Cell
204
+ className={cx('', { 'dragging-cell': isDragging })}
205
+ key={index.id}
206
+ >
207
+ {index.type === 'boolean' &&
208
+ (item[index.id] ? (
209
+ <FormattedMessage id="Yes" defaultMessage="Yes" />
210
+ ) : (
211
+ <FormattedMessage id="No" defaultMessage="No" />
212
+ ))}
213
+ {index.type === 'string' &&
214
+ index.id !== 'review_state' &&
215
+ item[index.id]}
216
+ {index.id === 'review_state' && (
217
+ <div>
218
+ <span>
219
+ <Circle color={getColor(item[index.id])} size="15px" />
220
+ </span>
221
+ {messages[item[index.id]]
222
+ ? intl.formatMessage(messages[item[index.id]])
223
+ : item['review_title'] ||
224
+ item['review_state'] ||
225
+ intl.formatMessage(messages.no_workflow_state)}
226
+ </div>
227
+ )}
228
+ {index.type === 'date' && (
229
+ <>
230
+ {item[index?.id] && item[index.id] !== 'None' ? (
231
+ <FormattedDate date={item[index.id]} />
232
+ ) : (
233
+ intl.formatMessage(messages.none)
234
+ )}
235
+ </>
236
+ )}
237
+ {index.type === 'array' && (
238
+ <span>{item[index.id]?.join(', ')}</span>
239
+ )}
240
+ </Table.Cell>
241
+ ))}
242
+ <Table.Cell
243
+ className={cx('', { 'dragging-cell': isDragging })}
244
+ textAlign="right"
245
+ >
246
+ <Popup
247
+ menu={true}
248
+ position="bottom right"
249
+ flowing={true}
250
+ basic={true}
251
+ on="click"
252
+ popper={{
253
+ className: 'dropdown-popup',
254
+ }}
255
+ trigger={
256
+ <Icon
257
+ name={moreSVG}
258
+ className="dropdown-popup-trigger"
259
+ size="24px"
260
+ color="#007eb1"
261
+ />
262
+ }
263
+ >
264
+ <Menu vertical borderless fluid>
265
+ <Link className="item icon-align" to={`${item['@id']}/edit`}>
266
+ <Icon name={editingSVG} color="#007eb1" size="24px" />{' '}
267
+ <FormattedMessage id="Edit" defaultMessage="Edit" />
268
+ </Link>
269
+ <Link className="item right-dropdown icon-align" to={item['@id']}>
270
+ <Icon name={showSVG} color="#007eb1" size="24px" />{' '}
271
+ <FormattedMessage id="View" defaultMessage="View" />
272
+ </Link>
273
+ <Divider />
274
+ <Menu.Item
275
+ onClick={onCut}
276
+ value={item['@id']}
277
+ className="right-dropdown icon-align"
278
+ >
279
+ <Icon name={cutSVG} color="#007eb1" size="24px" />{' '}
280
+ <FormattedMessage id="Cut" defaultMessage="Cut" />
281
+ </Menu.Item>
282
+ <Menu.Item
283
+ onClick={onCopy}
284
+ value={item['@id']}
285
+ className="right-dropdown icon-align"
286
+ >
287
+ <Icon name={copySVG} color="#007eb1" size="24px" />{' '}
288
+ <FormattedMessage id="Copy" defaultMessage="Copy" />
289
+ </Menu.Item>
290
+ <Menu.Item
291
+ onClick={onDelete}
292
+ value={item['@id']}
293
+ className="right-dropdown icon-align"
294
+ >
295
+ <Icon name={deleteSVG} color="#e40166" size="24px" />{' '}
296
+ <FormattedMessage id="Delete" defaultMessage="Delete" />
297
+ </Menu.Item>
298
+ <Divider />
299
+ <Menu.Item
300
+ onClick={onMoveToTop}
301
+ value={order}
302
+ className="right-dropdown icon-align"
303
+ >
304
+ <Icon name={moveUpSVG} color="#007eb1" size="24px" />{' '}
305
+ <FormattedMessage
306
+ id="Move to top of folder"
307
+ defaultMessage="Move to top of folder"
308
+ />
309
+ </Menu.Item>
310
+ <Menu.Item
311
+ onClick={onMoveToBottom}
312
+ value={order}
313
+ className="right-dropdown icon-align"
314
+ >
315
+ <Icon name={moveDownSVG} color="#007eb1" size="24px" />{' '}
316
+ <FormattedMessage
317
+ id="Move to bottom of folder"
318
+ defaultMessage="Move to bottom of folder"
319
+ />
320
+ </Menu.Item>
321
+ </Menu>
322
+ </Popup>
323
+ </Table.Cell>
324
+ </tr>,
325
+ ),
326
+ );
327
+ };
328
+
329
+ /**
330
+ * Property types.
331
+ * @property {Object} propTypes Property types.
332
+ * @static
333
+ */
334
+ ContentsItemComponent.propTypes = {
335
+ item: PropTypes.shape({
336
+ '@id': PropTypes.string,
337
+ title: PropTypes.string,
338
+ is_folderish: PropTypes.bool,
339
+ '@type': PropTypes.string,
340
+ }).isRequired,
341
+ selected: PropTypes.bool.isRequired,
342
+ onClick: PropTypes.func.isRequired,
343
+ indexes: PropTypes.arrayOf(
344
+ PropTypes.shape({
345
+ id: PropTypes.string,
346
+ type: PropTypes.string,
347
+ }),
348
+ ).isRequired,
349
+ onCut: PropTypes.func.isRequired,
350
+ onCopy: PropTypes.func.isRequired,
351
+ onDelete: PropTypes.func.isRequired,
352
+ onMoveToTop: PropTypes.func.isRequired,
353
+ onMoveToBottom: PropTypes.func.isRequired,
354
+ connectDragPreview: PropTypes.func.isRequired,
355
+ connectDragSource: PropTypes.func.isRequired,
356
+ connectDropTarget: PropTypes.func.isRequired,
357
+ isDragging: PropTypes.bool.isRequired,
358
+ order: PropTypes.number.isRequired,
359
+ onOrderItem: PropTypes.func.isRequired,
360
+ };
361
+
362
+ const DragDropConnector = (props) => {
363
+ const { DropTarget, DragSource } = props.reactDnd;
364
+
365
+ const DndConnectedContentsItem = React.useMemo(
366
+ () =>
367
+ DropTarget(
368
+ 'item',
369
+ {
370
+ hover(props, monitor) {
371
+ const id = monitor.getItem().id;
372
+ const dragOrder = monitor.getItem().order;
373
+ const hoverOrder = props.order;
374
+
375
+ if (dragOrder === hoverOrder) {
376
+ return;
377
+ }
378
+
379
+ props.onOrderItem(id, dragOrder, hoverOrder - dragOrder, false);
380
+
381
+ monitor.getItem().order = hoverOrder;
382
+ },
383
+ drop(props, monitor) {
384
+ const id = monitor.getItem().id;
385
+ const dragOrder = monitor.getItem().startOrder;
386
+ const dropOrder = props.order;
387
+
388
+ if (dragOrder === dropOrder) {
389
+ return;
390
+ }
391
+
392
+ props.onOrderItem(id, dragOrder, dropOrder - dragOrder, true);
393
+
394
+ monitor.getItem().order = dropOrder;
395
+ },
396
+ },
397
+ (connect) => ({
398
+ connectDropTarget: connect.dropTarget(),
399
+ }),
400
+ )(
401
+ DragSource(
402
+ 'item',
403
+ {
404
+ beginDrag(props) {
405
+ return {
406
+ id: props.item['@id'],
407
+ order: props.order,
408
+ startOrder: props.order,
409
+ };
410
+ },
411
+ },
412
+ (connect, monitor) => ({
413
+ connectDragSource: connect.dragSource(),
414
+ connectDragPreview: connect.dragPreview(),
415
+ isDragging: monitor.isDragging(),
416
+ }),
417
+ )(ContentsItemComponent),
418
+ ),
419
+ [DragSource, DropTarget],
420
+ );
421
+
422
+ return <DndConnectedContentsItem {...props} />;
423
+ };
424
+
425
+ export default injectLazyLibs('reactDnd')(DragDropConnector);
@@ -0,0 +1,73 @@
1
+ --- node_modules/@plone/volto/src/components/manage/Contents/ContentsItem.jsx
2
+ +++ src/customizations/volto/components/manage/Contents/ContentsItem.jsx
3
+ @@ -97,6 +97,11 @@
4
+ order,
5
+ }) => {
6
+ const intl = useIntl();
7
+ + const now = Date.now();
8
+ + const expirationTime = new Date(item.ExpirationDate).getTime();
9
+ + const effectiveTime = new Date(item.EffectiveDate).getTime();
10
+ + const isExpired = item.ExpirationDate !== 'None' && expirationTime < now;
11
+ + const isScheduled = item.EffectiveDate !== 'None' && effectiveTime > now;
12
+
13
+ return connectDropTarget(
14
+ connectDragPreview(
15
+ @@ -155,7 +160,10 @@
16
+ className="icon-align-name"
17
+ to={`${item['@id']}${item.is_folderish ? '/contents' : ''}`}
18
+ >
19
+ - <div className="expire-align">
20
+ + <div
21
+ + className="expire-align"
22
+ + style={{ flex: '1 1 auto', minWidth: 0 }}
23
+ + >
24
+ <Icon
25
+ name={getContentIcon(item['@type'], item.is_folderish)}
26
+ size="20px"
27
+ @@ -163,21 +171,32 @@
28
+ color="#878f93"
29
+ title={item['Type'] || item['@type']}
30
+ />{' '}
31
+ - <span title={item.title}> {item.title}</span>
32
+ + <span
33
+ + title={item.title}
34
+ + style={{ flex: '1 1 auto', minWidth: 0 }}
35
+ + >
36
+ + {' '}
37
+ + {item.title}
38
+ + </span>
39
+ </div>
40
+ - {item.ExpirationDate !== 'None' &&
41
+ - new Date(item.ExpirationDate).getTime() <
42
+ - new Date().getTime() && (
43
+ - <Button className="button-margin" size="mini">
44
+ - <FormattedMessage id="Expired" defaultMessage="Expired" />
45
+ - </Button>
46
+ - )}
47
+ - {item.EffectiveDate !== 'None' &&
48
+ - new Date(item.EffectiveDate).getTime() > new Date().getTime() && (
49
+ - <Button className="button-margin effective-future" size="mini">
50
+ - <FormattedMessage id="Scheduled" defaultMessage="Scheduled" />
51
+ - </Button>
52
+ - )}
53
+ + {isExpired && (
54
+ + <Button
55
+ + className="button-margin"
56
+ + size="mini"
57
+ + style={{ flexShrink: 0 }}
58
+ + >
59
+ + <FormattedMessage id="Expired" defaultMessage="Expired" />
60
+ + </Button>
61
+ + )}
62
+ + {isScheduled && (
63
+ + <Button
64
+ + className="button-margin effective-future"
65
+ + size="mini"
66
+ + style={{ flexShrink: 0 }}
67
+ + >
68
+ + <FormattedMessage id="Scheduled" defaultMessage="Scheduled" />
69
+ + </Button>
70
+ + )}
71
+ </Link>
72
+ </Table.Cell>
73
+ {map(indexes, (index) => (
@@ -0,0 +1,19 @@
1
+ # ContentsItem.jsx customization
2
+
3
+ This customization shadows Volto core's
4
+ `src/components/manage/Contents/ContentsItem.jsx` from `@plone/volto` 18.33.1.
5
+
6
+ The functional changes are limited to the contents table item link:
7
+
8
+ - The expired and scheduled date checks are computed once per render as
9
+ `isExpired` and `isScheduled`, then reused by the badge rendering.
10
+ - The title wrapper and title text are allowed to shrink with flexbox
11
+ (`flex: '1 1 auto'` and `minWidth: 0`), while the status badges keep their
12
+ intrinsic width with `flexShrink: 0`.
13
+
14
+ The goal is to avoid title text colliding with the `Expired` or `Scheduled`
15
+ badges without hardcoding a pixel width for the title.
16
+
17
+ When upgrading Volto, compare the new core component with this override and
18
+ refresh `ContentsItem.jsx.diff` if upstream changed around the contents item
19
+ title or badge rendering.
@@ -3,26 +3,24 @@
3
3
  * @module components/theme/Header/Header
4
4
  */
5
5
 
6
- import React from 'react';
7
- import { Dropdown, Image } from 'semantic-ui-react';
6
+ import loadable from '@loadable/component';
7
+ import cx from 'classnames';
8
+ import { useEffect, useMemo, useRef } from 'react';
8
9
  import { connect, useDispatch, useSelector } from 'react-redux';
9
-
10
10
  import { withRouter } from 'react-router-dom';
11
+ import { compose } from 'redux';
12
+ import { Dropdown, Image } from 'semantic-ui-react';
13
+
14
+ import { getNavigation } from '@plone/volto/actions/navigation/navigation';
11
15
  import UniversalLink from '@plone/volto/components/manage/UniversalLink/UniversalLink';
12
16
  import { getBaseUrl } from '@plone/volto/helpers/Url/Url';
13
17
  import { hasApiExpander } from '@plone/volto/helpers/Utils/Utils';
14
- import { getNavigation } from '@plone/volto/actions/navigation/navigation';
15
- import { getNavigationSettings } from '@eeacms/volto-eea-website-theme/actions';
16
- import Header from '@eeacms/volto-eea-design-system/ui/Header/Header';
17
- import EEALogo from '@eeacms/volto-eea-website-theme/components/theme/Logo';
18
- import { usePrevious } from '@eeacms/volto-eea-design-system/helpers';
19
- import eeaFlag from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/images/Header/eea.png';
20
-
21
18
  import config from '@plone/volto/registry';
22
- import { compose } from 'redux';
23
19
 
24
- import cx from 'classnames';
25
- import loadable from '@loadable/component';
20
+ import eeaFlag from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/images/Header/eea.png';
21
+ import Header from '@eeacms/volto-eea-design-system/ui/Header/Header';
22
+ import { getNavigationSettings } from '@eeacms/volto-eea-website-theme/actions';
23
+ import EEALogo from '@eeacms/volto-eea-website-theme/components/theme/Logo';
26
24
 
27
25
  const LazyLanguageSwitcher = loadable(() => import('./LanguageSwitcher'));
28
26
  const EMPTY_NAVIGATION_SETTINGS = {};
@@ -32,15 +30,79 @@ function removeTrailingSlash(path) {
32
30
  }
33
31
 
34
32
  /**
35
- * EEA Specific Header component.
33
+ * Merge backend navigation settings into the config-level menu layouts.
36
34
  */
37
- const EEAHeader = ({ pathname, token, items, history, subsite }) => {
38
- const router_pathname = useSelector((state) => {
39
- return removeTrailingSlash(state.router?.location?.pathname) || '';
35
+ function buildEnhancedLayouts(items, navigationSettings) {
36
+ const configLayouts = config.settings?.menuItemsLayouts || {};
37
+ const enhancedLayouts = { ...configLayouts };
38
+
39
+ if (!items) return enhancedLayouts;
40
+
41
+ items.forEach(() => {
42
+ Object.keys(navigationSettings).forEach((routeId) => {
43
+ const route = navigationSettings[routeId];
44
+ const backendSettings = {};
45
+
46
+ if (route.hideChildrenFromNavigation !== undefined) {
47
+ backendSettings.hideChildrenFromNavigation =
48
+ route.hideChildrenFromNavigation;
49
+ }
50
+
51
+ if (route.menuItemChildrenListColumns !== undefined) {
52
+ backendSettings.menuItemChildrenListColumns = Array.isArray(
53
+ route.menuItemChildrenListColumns,
54
+ )
55
+ ? route.menuItemChildrenListColumns
56
+ .map((val) => (typeof val === 'string' ? parseInt(val, 10) : val))
57
+ .filter((val) => !isNaN(val))
58
+ : route.menuItemChildrenListColumns;
59
+ }
60
+
61
+ if (route.menuItemColumns !== undefined) {
62
+ backendSettings.menuItemColumns = route.menuItemColumns;
63
+ }
64
+
65
+ if (Object.keys(backendSettings).length > 0) {
66
+ enhancedLayouts[routeId] = {
67
+ ...enhancedLayouts[routeId],
68
+ ...backendSettings,
69
+ };
70
+ }
71
+ });
40
72
  });
41
73
 
74
+ return enhancedLayouts;
75
+ }
76
+
77
+ /**
78
+ * EEA Specific Header component.
79
+ */
80
+ const EEAHeader = ({ pathname, token, items, history, navroot, subsite }) => {
81
+ // Config / static derived values
82
+ const { eea } = config.settings;
83
+ const headerOpts = eea.headerOpts || {};
84
+ const { logo, logoWhite } = headerOpts;
85
+
42
86
  const isSubsite = subsite?.['@type'] === 'Subsite';
43
87
 
88
+ // Redux state
89
+ const dispatch = useDispatch();
90
+ const width = useSelector((state) => state.screen?.width);
91
+
92
+ const router_pathname = useSelector(
93
+ (state) => removeTrailingSlash(state.router?.location?.pathname) || '',
94
+ );
95
+
96
+ const headerSettings = useSelector(
97
+ (state) => state.reduxAsyncConnect?.headerSettings,
98
+ );
99
+
100
+ const navigationSettings =
101
+ useSelector((state) => state.navigationSettings?.settings) ||
102
+ EMPTY_NAVIGATION_SETTINGS;
103
+
104
+ const updateRequest = useSelector((state) => state.content.update);
105
+
44
106
  const isHomePageInverse = useSelector((state) => {
45
107
  const layout = state.content?.data?.layout;
46
108
  const has_home_layout =
@@ -55,85 +117,69 @@ const EEAHeader = ({ pathname, token, items, history, subsite }) => {
55
117
  );
56
118
  });
57
119
 
58
- const { eea } = config.settings;
59
- const headerOpts = eea.headerOpts || {};
60
- const { logo, logoWhite } = headerOpts;
61
- const width = useSelector((state) => state.screen?.width);
62
- const dispatch = useDispatch();
63
-
64
- const headerSettings = useSelector(
65
- (state) => state.reduxAsyncConnect?.headerSettings,
66
- );
120
+ const prevTokenRef = useRef(undefined);
67
121
 
122
+ // Derived / memoized values
68
123
  const headerSearchBox =
69
124
  headerSettings?.searchBox || eea.headerSearchBox || [];
70
- const previousToken = usePrevious(token);
71
- const navigationSettings =
72
- useSelector((state) => state.navigationSettings?.settings) ||
73
- EMPTY_NAVIGATION_SETTINGS;
74
- const updateRequest = useSelector((state) => state.content.update);
75
-
76
- // Combine navigation settings from backend with config fallback
77
- const configLayouts = config.settings?.menuItemsLayouts || {};
78
- const enhancedLayouts = { ...configLayouts };
79
125
 
80
- // Map navigation settings to menu item URLs
81
- if (items) {
82
- items.forEach((menuItem) => {
83
- // Check if we have navigation settings for any route that might match this menu item
84
- Object.keys(navigationSettings).forEach((routeId) => {
85
- const route = navigationSettings[routeId];
86
- const backendSettings = {};
87
-
88
- if (route.hideChildrenFromNavigation !== undefined) {
89
- backendSettings.hideChildrenFromNavigation =
90
- route.hideChildrenFromNavigation;
91
- }
126
+ const enhancedLayouts = buildEnhancedLayouts(items, navigationSettings);
127
+
128
+ // Prefer navroot.language; fall back to extracting language from pathname
129
+ // (validated against supportedLanguages) when navroot is not yet loaded.
130
+ const navrootLang = useMemo(() => {
131
+ const { supportedLanguages, navigationLanguage } = config.settings;
132
+ if (navroot?.language?.token) return navroot.language.token;
133
+ const supported = supportedLanguages || [];
134
+ const first = pathname.split('/').filter(Boolean)[0];
135
+ if (first === undefined) return navigationLanguage || null;
136
+ return supported.includes(first) ? first : null;
137
+ }, [navroot, pathname]);
138
+
139
+ // Normalize pathname for menu active-item matching when using
140
+ // navigationLanguage. Menu items come from the configured language; rewrite
141
+ // the current language prefix to match. E.g. navLang='en' on /fr/topics ->
142
+ // /en/topics. Uses navroot.language as source of truth instead of parsing
143
+ // the first path segment.
144
+ const normalizedPathname = useMemo(() => {
145
+ const navLang = config.settings.navigationLanguage;
146
+ if (!navLang || !navrootLang || navrootLang === navLang) return pathname;
92
147
 
93
- if (route.menuItemChildrenListColumns !== undefined) {
94
- // Convert strings back to integers for header usage
95
- backendSettings.menuItemChildrenListColumns = Array.isArray(
96
- route.menuItemChildrenListColumns,
97
- )
98
- ? route.menuItemChildrenListColumns
99
- .map((val) =>
100
- typeof val === 'string' ? parseInt(val, 10) : val,
101
- )
102
- .filter((val) => !isNaN(val))
103
- : route.menuItemChildrenListColumns;
104
- }
148
+ const prefix = `/${navrootLang}`;
149
+ if (pathname === prefix) return `/${navLang}`;
150
+ if (pathname.startsWith(`${prefix}/`)) {
151
+ return `/${navLang}${pathname.slice(prefix.length)}`;
152
+ }
153
+ return pathname;
154
+ }, [pathname, navrootLang]);
105
155
 
106
- if (route.menuItemColumns !== undefined) {
107
- // Use menuItemColumns directly as they're already in semantic UI format
108
- backendSettings.menuItemColumns = route.menuItemColumns;
109
- }
156
+ const baseUrl = useMemo(() => {
157
+ const { settings } = config;
158
+ const navLang = settings.navigationLanguage;
159
+ let url = getBaseUrl(pathname);
110
160
 
111
- if (Object.keys(backendSettings).length > 0) {
112
- // Override the config setting with backend data
113
- enhancedLayouts[routeId] = {
114
- ...enhancedLayouts[routeId],
115
- ...backendSettings,
116
- };
117
- }
118
- });
119
- });
120
- }
161
+ if (isSubsite || !navLang || !navrootLang) {
162
+ return url;
163
+ }
121
164
 
122
- // Memoize navigationBaseUrl so it doesn't change on every pathname change
123
- // when navigationLanguage is set to a fixed language
124
- const navigationBaseUrl = React.useMemo(() => {
125
- const { settings } = config;
126
- return settings.navigationLanguage
127
- ? `/${settings.navigationLanguage}`
128
- : getBaseUrl(pathname);
129
- }, [pathname]);
165
+ // When the current navroot's language differs from the configured
166
+ // navigationLanguage, override the base url so navigation is fetched from
167
+ // the configured language root instead of the current navroot.
168
+ if (navLang !== navrootLang) {
169
+ url = `/${settings.navigationLanguage}`;
170
+ } else if (!url && navLang === navrootLang) {
171
+ url = `/${navLang}`;
172
+ }
173
+ return url;
174
+ }, [pathname, navrootLang, isSubsite]);
130
175
 
131
- React.useEffect(() => {
176
+ // Fetch navigation settings on pathname change.
177
+ useEffect(() => {
132
178
  dispatch(getNavigationSettings(pathname));
133
179
  }, [dispatch, pathname]);
134
180
 
135
- // Separate effect for update request to avoid duplicate calls
136
- React.useEffect(() => {
181
+ // Re-fetch navigation settings after a content update for the current page.
182
+ useEffect(() => {
137
183
  if (
138
184
  updateRequest?.loaded &&
139
185
  removeTrailingSlash(updateRequest?.content?.['@id'] || '') ===
@@ -143,46 +189,36 @@ const EEAHeader = ({ pathname, token, items, history, subsite }) => {
143
189
  }
144
190
  }, [updateRequest, dispatch, pathname]);
145
191
 
146
- React.useEffect(() => {
192
+ // Fetch the main navigation tree.
193
+ // Cases that force a fetch:
194
+ // 1. Language mismatch — the current navroot's language differs from the
195
+ // configured navigationLanguage, so the API expander's navigation (if
196
+ // any) is in the wrong language and must be replaced.
197
+ // 2. Token change — auth state affects which nav items are visible, so
198
+ // expander data loaded under the previous token may be stale.
199
+ // 3. No expander available — backend did not pre-supply navigation for
200
+ // this base url, so we fetch it explicitly.
201
+ // Otherwise the expander already supplied correct navigation; no fetch.
202
+ useEffect(() => {
147
203
  const { settings } = config;
204
+ const navLang = settings.navigationLanguage;
205
+ const langMismatch = navLang && navrootLang && navrootLang !== navLang;
206
+ const tokenChanged = prevTokenRef.current !== token;
148
207
 
149
- // When navigationLanguage is configured, always fetch navigation from that language
150
- // We MUST call getNavigation directly because API expanders fetch navigation for the current page
151
- if (settings.navigationLanguage) {
152
- // Always fetch navigation for the configured language
153
- dispatch(getNavigation(navigationBaseUrl, settings.navDepth));
154
- } else {
155
- // When navigationLanguage is not configured, fetch navigation for current page language
156
- // Check if navigation data needs to be fetched based on the API expander availability
157
- if (!hasApiExpander('navigation', navigationBaseUrl)) {
158
- dispatch(getNavigation(navigationBaseUrl, settings.navDepth));
159
- }
160
-
161
- // Additional check for token changes
162
- if (token !== previousToken) {
163
- dispatch(getNavigation(navigationBaseUrl, settings.navDepth));
164
- }
165
- }
166
- }, [navigationBaseUrl, token, dispatch, previousToken]);
167
-
168
- // Normalize pathname for menu matching when using navigationLanguage
169
- // This ensures menu items from the configured language match correctly even when on other language pages
170
- const normalizedPathname = React.useMemo(() => {
171
- const navLang = config.settings.navigationLanguage;
172
- if (!navLang) {
173
- return pathname;
208
+ if (
209
+ langMismatch ||
210
+ tokenChanged ||
211
+ !hasApiExpander('navigation', baseUrl)
212
+ ) {
213
+ dispatch(getNavigation(baseUrl, settings.navDepth));
174
214
  }
215
+ }, [dispatch, baseUrl, navrootLang, token]);
175
216
 
176
- // Replace the language prefix with the configured navigation language for menu matching
177
- // e.g., if navLang='en': /fr/topics -> /en/topics
178
- const pathParts = pathname.split('/').filter(Boolean);
179
- if (pathParts.length > 0 && pathParts[0].length === 2) {
180
- // First segment is a language code, replace it with the navigation language
181
- const rest = pathParts.slice(1).join('/');
182
- return rest ? `/${navLang}/${rest}` : `/${navLang}`;
183
- }
184
- return pathname;
185
- }, [pathname]);
217
+ // Track the previous token value. Runs after the fetch effect so the
218
+ // comparison above sees the value from the prior render.
219
+ useEffect(() => {
220
+ prevTokenRef.current = token;
221
+ }, [token]);
186
222
 
187
223
  return (
188
224
  <Header menuItems={items}>
@@ -327,6 +363,7 @@ export default compose(
327
363
  (state) => ({
328
364
  token: state.userSession.token,
329
365
  items: state.navigation.items,
366
+ navroot: state.content.data?.['@components']?.navroot?.navroot,
330
367
  subsite: state.content.data?.['@components']?.subsite,
331
368
  }),
332
369
  { getNavigation },