@eeacms/volto-cca-policy 0.3.125 → 0.3.127

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,139 @@ 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
+ ### [0.3.127](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.3...0.3.127) - 27 May 2026
8
+
9
+ #### :bug: Bug Fixes
10
+
11
+ - fix: resolve remaining lint warnings in WorkflowLinkIntegrityModal [Tiberiu Ichim - [`f1add88`](https://github.com/eea/volto-cca-policy/commit/f1add881c7240ba12dd223bfccb0c4ac86f83056)]
12
+ - fix: resolve lint warnings and a11y errors in WorkflowLinkIntegrityModal [Tiberiu Ichim - [`bc60ca6`](https://github.com/eea/volto-cca-policy/commit/bc60ca604f30f62d6fb225bb319f76bab700ff04)]
13
+ - fix: update warning message in link integrity workflow state change modal [Tiberiu Ichim - [`1244b2e`](https://github.com/eea/volto-cca-policy/commit/1244b2e826590050c6b10506651482cd615b8c92)]
14
+ - fix: resolve workflow state transition failure when confirming via link integrity warning modal [Tiberiu Ichim - [`2fd0d89`](https://github.com/eea/volto-cca-policy/commit/2fd0d899fc421b4944de5488902f4bc35f5102d9)]
15
+
16
+ #### :house: Internal changes
17
+
18
+ - style: Automated code fix [eea-jenkins - [`8ca0234`](https://github.com/eea/volto-cca-policy/commit/8ca0234d1d429ed5964061989dad13cd6b44b027)]
19
+ - style: Automated code fix [eea-jenkins - [`1e4caaf`](https://github.com/eea/volto-cca-policy/commit/1e4caafffd7b026c565887d6ace3c7ec87866bfe)]
20
+
21
+ #### :house: Documentation changes
22
+
23
+ - docs: update link-integrity artifact spec to reflect Portal-based modal [Tiberiu Ichim - [`f697e7e`](https://github.com/eea/volto-cca-policy/commit/f697e7e5b453a31032feeb862526d97a1c995483)]
24
+ - docs: convert index.md absolute links to clean relative links [Tiberiu Ichim - [`bfb2d6a`](https://github.com/eea/volto-cca-policy/commit/bfb2d6a6c592991c4f65875c8db5f9b55205bf4d)]
25
+ - docs: reorganize link integrity workflow artifacts, adding README specification and index overview [Tiberiu Ichim - [`1208c96`](https://github.com/eea/volto-cca-policy/commit/1208c9619ff4097a4d954e355895382978b326dc)]
26
+ - docs: add specification for link integrity check during workflow transitions [Tiberiu Ichim - [`842da3d`](https://github.com/eea/volto-cca-policy/commit/842da3d371f0c5c38a538e3d519bc5eb79705e34)]
27
+
28
+ ### [1.0.0-alpha.3](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.2...1.0.0-alpha.3) - 22 May 2026
29
+
30
+ #### :bug: Bug Fixes
31
+
32
+ - fix: imports [kreafox - [`c20878e`](https://github.com/eea/volto-cca-policy/commit/c20878e4c72c91a239a5b5fd1cf2fc11b943dc2a)]
33
+ - fix: undo some changes [kreafox - [`2e7e7fa`](https://github.com/eea/volto-cca-policy/commit/2e7e7fa5596bdcc2b4866c915bc1671fc373ac0a)]
34
+
35
+ #### :hammer_and_wrench: Others
36
+
37
+ - eslint image [iugin - [`c293ec8`](https://github.com/eea/volto-cca-policy/commit/c293ec8a9a525eff431629d7724e65bdd959a07b)]
38
+ - eslint image tag [iugin - [`6f7d575`](https://github.com/eea/volto-cca-policy/commit/6f7d5752466dac4412c6ef119e9f8a624c8f9706)]
39
+ - eslint [iugin - [`a449f36`](https://github.com/eea/volto-cca-policy/commit/a449f36f3b8d5b8e60ff6a3ffcc439b01aa78245)]
40
+ - remove old js files, remains jsx [iugin - [`3ff03ae`](https://github.com/eea/volto-cca-policy/commit/3ff03ae75e5a1a106ebd0547705dcab89d3c4a79)]
41
+ - eslint updates jsx, include and Image tag [iugin - [`ac5abd2`](https://github.com/eea/volto-cca-policy/commit/ac5abd288ec258774a90c2395964521068918d29)]
42
+ ### [1.0.0-alpha.2](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.1...1.0.0-alpha.2) - 21 May 2026
43
+
44
+ #### :bug: Bug Fixes
45
+
46
+ - fix: long gap in blocks chooser [kreafox - [`e86ef53`](https://github.com/eea/volto-cca-policy/commit/e86ef531a3a70a024b0c76b47d09e967fd28ba5b)]
47
+ - fix(block): spacing issue with accordion in tabs block [kreafox - [`cf6de7c`](https://github.com/eea/volto-cca-policy/commit/cf6de7c52d941e75bb1c7e4704abfc5ff47775c3)]
48
+
49
+ #### :hammer_and_wrench: Others
50
+
51
+ - rename files for volto-18 [iugin - [`5ca21ab`](https://github.com/eea/volto-cca-policy/commit/5ca21aba63f0133003267531335a207130717234)]
52
+ - Add artifacts [Tiberiu Ichim - [`37cccf1`](https://github.com/eea/volto-cca-policy/commit/37cccf1d3f1d926c64c7d192b071c7cd0061cb0a)]
53
+ - Add blocks overview [Tiberiu Ichim - [`1aa6e8b`](https://github.com/eea/volto-cca-policy/commit/1aa6e8b780f4f7a00641872ef7a99e2223cd1feb)]
54
+ - Refs #296263 - sidebar menu sticky and add "submenus" [iugin - [`1bce280`](https://github.com/eea/volto-cca-policy/commit/1bce2802e815bb1dd4fabb62d3c7ff7478c46b07)]
55
+ - Refs #296263 - remove border landing page [iugin - [`488b7d1`](https://github.com/eea/volto-cca-policy/commit/488b7d1dfbc307d8571802e86e6343fff52b04ce)]
56
+ - Refs #296263 - add submenu [iugin - [`decc8b1`](https://github.com/eea/volto-cca-policy/commit/decc8b16f2d5aea6214f137b3afde5586806021b)]
57
+ - Refs #298498 - force rebuild [iugin - [`f3deed1`](https://github.com/eea/volto-cca-policy/commit/f3deed10893c81768a33ccb44f4659c28cce02a4)]
58
+ - Refs #298498 - exclude UK [iugin - [`43c122d`](https://github.com/eea/volto-cca-policy/commit/43c122d067029a9b08d729a04f9e17ce07b1911f)]
59
+ - Fix Jenkins. [GhitaB - [`8fa1d86`](https://github.com/eea/volto-cca-policy/commit/8fa1d860b3a572d0769d637fdb3fc4f9ecf149f8)]
60
+ - Fix Jenkins. [GhitaB - [`b1baaf2`](https://github.com/eea/volto-cca-policy/commit/b1baaf2274224f0046281d2cfb629138d01ec121)]
61
+ - Refs #296263 - country map eslint [iugin - [`237cf82`](https://github.com/eea/volto-cca-policy/commit/237cf824d6224947b0b1bf053e0a6fbade7673b8)]
62
+ - Refs #296263 - country map eslint [iugin - [`2506368`](https://github.com/eea/volto-cca-policy/commit/250636839550d91b50be1841f491fbf6415e2b13)]
63
+ - Refs #296263 - country map [iugin - [`9c517cb`](https://github.com/eea/volto-cca-policy/commit/9c517cb37dc211732b6e8511c37727f41fdb776f)]
64
+ - Refs #296263 - country profile landing init [iugin - [`ae9ffb4`](https://github.com/eea/volto-cca-policy/commit/ae9ffb4f3c73b2331014808ee114049dfa8fe9ab)]
65
+ - Refs #296263 - country profile ticket updates [iugin - [`edbf93d`](https://github.com/eea/volto-cca-policy/commit/edbf93d071d770f596fce12cf463151bff4efd72)]
66
+ - Refs #296263 - add Monitoring_Evaluation [iugin - [`d18a9de`](https://github.com/eea/volto-cca-policy/commit/d18a9ded8f65fa7bd58ef8e62985da77dead6562)]
67
+ - Refs #296263 - section name update [iugin - [`23e2e25`](https://github.com/eea/volto-cca-policy/commit/23e2e258bec0f51b20e656907cf2a8ee0cc2c427)]
68
+ - Refs #296263 - Key affected sectors tab [iugin - [`6d2c52f`](https://github.com/eea/volto-cca-policy/commit/6d2c52fda49270e704162bb90e48931bbdb7118e)]
69
+ - Refs #296263 - static countries [iugin - [`a58c3aa`](https://github.com/eea/volto-cca-policy/commit/a58c3aa596defc73fbd07ac0a58e05e510627b65)]
70
+ - Refs #296263 - assesments status map and circles [iugin - [`fa9c4a4`](https://github.com/eea/volto-cca-policy/commit/fa9c4a457baf4ad644438ee9649c17cf0798e3a7)]
71
+ - Refs #296263 - className and callout [iugin - [`e0646d7`](https://github.com/eea/volto-cca-policy/commit/e0646d7b28ca6c0724325678b82be2d855533c6c)]
72
+ - Refs #296263 - className [iugin - [`0f568d7`](https://github.com/eea/volto-cca-policy/commit/0f568d7af4ac0b4f6791b08e7209f4ff5f659327)]
73
+ - Refs #296263 - styles and messages [iugin - [`359f2fc`](https://github.com/eea/volto-cca-policy/commit/359f2fcd207a6e58bc9e32d514ca23183ea6ca8e)]
74
+ - Refs #296263 - jenkins update [iugin - [`b424df5`](https://github.com/eea/volto-cca-policy/commit/b424df51271e975a9721e4c0712c3009b68e53fa)]
75
+ - Refs #296263 - init [iugin - [`01bff47`](https://github.com/eea/volto-cca-policy/commit/01bff4757de9d2b01b9476f19b61715a7983999b)]
76
+ ### [1.0.0-alpha.1](https://github.com/eea/volto-cca-policy/compare/0.3.126...1.0.0-alpha.1) - 20 May 2026
77
+
78
+ #### :rocket: New Features
79
+
80
+ - feat: update Dockerfile and configuration for Volto 18 compatibility [kreafox - [`2c51416`](https://github.com/eea/volto-cca-policy/commit/2c51416baf7e550936bdce3e5f510631bb7164ab)]
81
+
82
+ #### :bug: Bug Fixes
83
+
84
+ - fix: sonarqube reported issues [kreafox - [`8398f64`](https://github.com/eea/volto-cca-policy/commit/8398f64d10f10444a8a96e1b120fc378aefad727)]
85
+ - fix: resolve React key warnings [kreafox - [`aa5d176`](https://github.com/eea/volto-cca-policy/commit/aa5d176006ddbfd172d78497c13934f6679e2ea0)]
86
+ - fix: resolve React key warnings [kreafox - [`7f90769`](https://github.com/eea/volto-cca-policy/commit/7f90769f56189ce7a9b8592d2faf73864b0d9a76)]
87
+ - fix: remove volto 17 testing from Jenkinsfile [kreafox - [`f239097`](https://github.com/eea/volto-cca-policy/commit/f239097f080abf6cf5eb4bfd69361b4416917e21)]
88
+
89
+ #### :nail_care: Enhancements
90
+
91
+ - refactor: update volto-slate customizations, fix eslint [kreafox - [`42c4c00`](https://github.com/eea/volto-cca-policy/commit/42c4c0096c9cc40037fc856f683c70e3ac202620)]
92
+ - change: update HeaderMenuPopUp.js, fix eslint issue [kreafox - [`2e8fa31`](https://github.com/eea/volto-cca-policy/commit/2e8fa319e7f3e26b17472d18db1acb12292be837)]
93
+ - refactor: constants and imports [kreafox - [`44e6627`](https://github.com/eea/volto-cca-policy/commit/44e662780894dff6a01f150923d41a373560195a)]
94
+ - refactor: change files extension from .js to .jsx [kreafox - [`577d8cf`](https://github.com/eea/volto-cca-policy/commit/577d8cf01f4a3024eb92639ff3898c667c266abb)]
95
+ - refactor: move hocs inside hocs folder, update imports [kreafox - [`2284251`](https://github.com/eea/volto-cca-policy/commit/2284251d4dc3438c754747cc041ce39059a1edcc)]
96
+ - refactor: move AST navigation utilities to local definitions [kreafox - [`e2749d6`](https://github.com/eea/volto-cca-policy/commit/e2749d625a259df4af2a37f2287a1778b753adbb)]
97
+ - refactor: update View component to use withOpenLayers [kreafox - [`63f698d`](https://github.com/eea/volto-cca-policy/commit/63f698de1e44c91d4d8e2b9a3b6e0a48167c069d)]
98
+ - refactor: change files extension from .js to .jsx [kreafox - [`939ecd9`](https://github.com/eea/volto-cca-policy/commit/939ecd910b41ec624a69d883882aaebcf642af46)]
99
+ - refactor: update jest-dom import to remove extend-expect usage [kreafox - [`3a9fc09`](https://github.com/eea/volto-cca-policy/commit/3a9fc096f3e345e6dc7a03a774bf6dd486a3e84f)]
100
+ - refactor: replace native img tags with Volto Image component in ImageWidget [kreafox - [`16494d3`](https://github.com/eea/volto-cca-policy/commit/16494d3fe2d855d739f75930855c5d6caa27491f)]
101
+ - refactor: replace native img tags with Volto Image component in ImageWidget [kreafox - [`bce54f7`](https://github.com/eea/volto-cca-policy/commit/bce54f74c7230027a523a8153d17eb1ced6522ed)]
102
+ - refactor: update lodash imports to use individual method paths [kreafox - [`d949f92`](https://github.com/eea/volto-cca-policy/commit/d949f925a8dedcbe7bbb5bd7589bd395275cb242)]
103
+ - refactor: remove barrel imports [kreafox - [`222bb04`](https://github.com/eea/volto-cca-policy/commit/222bb0488631beff2aa5183b63f3f0e6ce4e7327)]
104
+ - refactor: remove barrel imports [kreafox - [`3028ecd`](https://github.com/eea/volto-cca-policy/commit/3028ecd4e9f9be9252a4ad51bf453eaecbd6f4a8)]
105
+ - refactor: remove barrel imports [kreafox - [`1dde57d`](https://github.com/eea/volto-cca-policy/commit/1dde57d21f2cf536c6653b63687d1a560b70fec8)]
106
+ - refactor: remove barrel imports [kreafox - [`da460b8`](https://github.com/eea/volto-cca-policy/commit/da460b80174d01b557ccc727bfd56ae3eb85dc43)]
107
+ - refactor: update ESLint configuration [kreafox - [`a277217`](https://github.com/eea/volto-cca-policy/commit/a27721764461c0073b320897ef5e9d9dc5204721)]
108
+ - refactor: update Jenkinsfile for Volto 18 integration [kreafox - [`9b6013e`](https://github.com/eea/volto-cca-policy/commit/9b6013e0d2c6ddf068f19d536da455f147552c99)]
109
+
110
+ #### :house: Internal changes
111
+
112
+ - chore: bump version to 1.0.0 [kreafox - [`39de87b`](https://github.com/eea/volto-cca-policy/commit/39de87b1b368cbf41fbc77dbb37b07ceb2253baa)]
113
+ - chore: Volto 18 eslint update [kreafox - [`998accc`](https://github.com/eea/volto-cca-policy/commit/998accce812186a91530403e347054adaed147dc)]
114
+ - chore: disable jsx-filename-extension lint rule [kreafox - [`d5a7a70`](https://github.com/eea/volto-cca-policy/commit/d5a7a70315b5d41db4574e365218a9422dd8e614)]
115
+ - style: Automated code fix [eea-jenkins - [`39a1a0f`](https://github.com/eea/volto-cca-policy/commit/39a1a0f4c4dcf242134cc8c940b7632ca87b135d)]
116
+ - style: Automated code fix [eea-jenkins - [`1308bc6`](https://github.com/eea/volto-cca-policy/commit/1308bc6cc13e7c8deedfd23948af8c0deba45f70)]
117
+
118
+ #### :hammer_and_wrench: Others
119
+
120
+ - test: update snapshots [kreafox - [`420d51d`](https://github.com/eea/volto-cca-policy/commit/420d51d1e88323db7295752dc1a184888d3d504e)]
121
+ - test: mock withScrollToTarget in Spotlight.test.jsx [kreafox - [`5fe54fb`](https://github.com/eea/volto-cca-policy/commit/5fe54fb44960bacc9ff0fe1d91bbbeec2bc58e83)]
122
+ - update language import path [iugin - [`1217dcc`](https://github.com/eea/volto-cca-policy/commit/1217dcc9e6aa3971bf30c07bb93203cb73949c23)]
123
+ ### [0.3.126](https://github.com/eea/volto-cca-policy/compare/0.3.125...0.3.126) - 26 May 2026
124
+
125
+ #### :bug: Bug Fixes
126
+
127
+ - fix: update warning message in link integrity workflow state change modal [Tiberiu Ichim - [`1244b2e`](https://github.com/eea/volto-cca-policy/commit/1244b2e826590050c6b10506651482cd615b8c92)]
128
+ - fix: resolve workflow state transition failure when confirming via link integrity warning modal [Tiberiu Ichim - [`2fd0d89`](https://github.com/eea/volto-cca-policy/commit/2fd0d899fc421b4944de5488902f4bc35f5102d9)]
129
+
130
+ #### :house: Internal changes
131
+
132
+ - style: Automated code fix [eea-jenkins - [`1e4caaf`](https://github.com/eea/volto-cca-policy/commit/1e4caafffd7b026c565887d6ace3c7ec87866bfe)]
133
+
134
+ #### :house: Documentation changes
135
+
136
+ - docs: convert index.md absolute links to clean relative links [Tiberiu Ichim - [`bfb2d6a`](https://github.com/eea/volto-cca-policy/commit/bfb2d6a6c592991c4f65875c8db5f9b55205bf4d)]
137
+ - docs: reorganize link integrity workflow artifacts, adding README specification and index overview [Tiberiu Ichim - [`1208c96`](https://github.com/eea/volto-cca-policy/commit/1208c9619ff4097a4d954e355895382978b326dc)]
138
+ - docs: add specification for link integrity check during workflow transitions [Tiberiu Ichim - [`842da3d`](https://github.com/eea/volto-cca-policy/commit/842da3d371f0c5c38a538e3d519bc5eb79705e34)]
139
+
7
140
  ### [0.3.125](https://github.com/eea/volto-cca-policy/compare/0.3.124...0.3.125) - 18 May 2026
8
141
 
9
142
  #### :rocket: New Features
@@ -354,6 +487,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
354
487
 
355
488
  #### :house: Internal changes
356
489
 
490
+ - chore: [JENKINSFILE] add package version in sonarqube [valentinab25 - [`e901251`](https://github.com/eea/volto-cca-policy/commit/e901251592eb54609fbc7eb8fcf7bd452c2da9cd)]
357
491
 
358
492
  ### [0.3.102](https://github.com/eea/volto-cca-policy/compare/0.3.101...0.3.102) - 24 February 2026
359
493
 
@@ -363,6 +497,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
363
497
 
364
498
  #### :house: Internal changes
365
499
 
500
+ - chore: [JENKINSFILE] use sonarqube branches [EEA Jenkins - [`8b3699a`](https://github.com/eea/volto-cca-policy/commit/8b3699a6cbaef662b5e80884ac2c1f7dc9c6591e)]
366
501
 
367
502
  ### [0.3.101](https://github.com/eea/volto-cca-policy/compare/0.3.100...0.3.101) - 28 January 2026
368
503
 
@@ -650,6 +785,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
650
785
  - style: fix item spacing [kreafox - [`7d4535d`](https://github.com/eea/volto-cca-policy/commit/7d4535dbc486e6cb80fc55d6ac8c7c01d9cf48af)]
651
786
  - chore: code cleanup [kreafox - [`842419b`](https://github.com/eea/volto-cca-policy/commit/842419b1bce3dc624755c81c9dea672198cab9e2)]
652
787
  - style: update teaser block styling [kreafox - [`1e0cad8`](https://github.com/eea/volto-cca-policy/commit/1e0cad8e1ed3ae827ee868dce8ad50b292a937ba)]
788
+ - chore: update Makefile, run yarn i18n [kreafox - [`61e6962`](https://github.com/eea/volto-cca-policy/commit/61e6962901068ae6512265417a730bd9befd65fe)]
653
789
  - chore: move styling to theme folder [kreafox - [`8cf6a1b`](https://github.com/eea/volto-cca-policy/commit/8cf6a1b8b14abf472a9c61b28db574f6d9e0bd3b)]
654
790
  - chore: remove ArrayWidget customization [kreafox - [`249bf93`](https://github.com/eea/volto-cca-policy/commit/249bf93054f7726c3d57a8b8ca01b75cd0d30522)]
655
791
  - chore: remove Footer customization [kreafox - [`7038af3`](https://github.com/eea/volto-cca-policy/commit/7038af37d48a328dfc31746f3fa84973e2f840ab)]
@@ -3412,10 +3548,13 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
3412
3548
  - Refs #260715 rast-block wip [Tripon Eugen - [`f19d54e`](https://github.com/eea/volto-cca-policy/commit/f19d54e0b9a6a86bf344eb85b6a1cda7f3de91bf)]
3413
3549
  - Refs #260715 rast-block wip [Tripon Eugen - [`2828537`](https://github.com/eea/volto-cca-policy/commit/2828537b6c084cd1a82162d552fb4ef025b71f9f)]
3414
3550
  - Refs #260715 rast-block updates [Tripon Eugen - [`1e803e5`](https://github.com/eea/volto-cca-policy/commit/1e803e5bd3d3fb7558f261c76c68866be7beb8b5)]
3551
+ - test: [JENKINS] Use java17 for sonarqube scanner [valentinab25 - [`0a15e1b`](https://github.com/eea/volto-cca-policy/commit/0a15e1b2ad081233685e80d5b3c60a8663f6b896)]
3552
+ - test: [JENKINS] Run cypress in started frontend container [valentinab25 - [`9554e44`](https://github.com/eea/volto-cca-policy/commit/9554e44c92a621a52b2adb5a4830fb084ee5734b)]
3415
3553
  ### [0.1.49](https://github.com/eea/volto-cca-policy/compare/0.1.48...0.1.49) - 15 November 2023
3416
3554
 
3417
3555
  #### :house: Internal changes
3418
3556
 
3557
+ - chore: [JENKINS] Refactor automated testing [valentinab25 - [`7b820a6`](https://github.com/eea/volto-cca-policy/commit/7b820a6369c2ddd5203b1a4abe352cb4bb43db7a)]
3419
3558
  - chore: husky, lint-staged use fixed versions [valentinab25 - [`f0a8061`](https://github.com/eea/volto-cca-policy/commit/f0a8061c275c236deb00087c23fac9860a073106)]
3420
3559
 
3421
3560
  #### :hammer_and_wrench: Others
@@ -3432,6 +3571,9 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
3432
3571
  - Refs #259267 - jenkins test [Tripon Eugen - [`cacd31e`](https://github.com/eea/volto-cca-policy/commit/cacd31e7b1afe0983674ed5c7632d2e1d7fa752e)]
3433
3572
  - Refs #259267 - jenkins [Tripon Eugen - [`5b3affe`](https://github.com/eea/volto-cca-policy/commit/5b3affee8401239de10097884c1b7f2349d15ec0)]
3434
3573
  - Refs #259267 - add When, lead image and title to files [Tripon Eugen - [`2cedb23`](https://github.com/eea/volto-cca-policy/commit/2cedb237f898af9057e13fba94b615ef71077204)]
3574
+ - test: [JENKINS] Add cpu limit on cypress docker [valentinab25 - [`4d607a5`](https://github.com/eea/volto-cca-policy/commit/4d607a576e9d0a5c34e48c41b409e7df616ee3d6)]
3575
+ - test: [JENKINS] Increase shm-size to cypress docker [valentinab25 - [`b7f74d5`](https://github.com/eea/volto-cca-policy/commit/b7f74d53513a6edbfbca5cb6d19687929bb1e5db)]
3576
+ - test: [JENKINS] Improve cypress time [valentinab25 - [`db65617`](https://github.com/eea/volto-cca-policy/commit/db656173391f65157098d95d388c25f6429753d8)]
3435
3577
  - Refs #259267 - cca event blocks attachments and check not mandatoty fields [Tripon Eugen - [`3138e5a`](https://github.com/eea/volto-cca-policy/commit/3138e5afb5bfbdbed14e27ed457b16867b7fa414)]
3436
3578
  - Refs #256681 - Fix error in CCA Event view menu. ([React Intl] An id must be provided to format a message.) [GhitaB - [`517eeb8`](https://github.com/eea/volto-cca-policy/commit/517eeb817264a47bbfd6b9b7d22aaf22d44ed224)]
3437
3579
  - Refs #161485 - Fix ECDE name conflict. [GhitaB - [`8bfd99f`](https://github.com/eea/volto-cca-policy/commit/8bfd99ff68bb82a04d1c0ed625fa514fcf46289e)]
@@ -3648,6 +3790,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
3648
3790
 
3649
3791
  #### :house: Internal changes
3650
3792
 
3793
+ - chore: [JENKINS] Remove alpha testing version [valentinab25 - [`ad1ced0`](https://github.com/eea/volto-cca-policy/commit/ad1ced0971ba116c13a3b5fcc039172cc915c919)]
3651
3794
 
3652
3795
  #### :hammer_and_wrench: Others
3653
3796
 
@@ -4128,6 +4271,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
4128
4271
  #### :hammer_and_wrench: Others
4129
4272
 
4130
4273
  - Refs #158294 - Update supported languages list. [GhitaB - [`0a4f91f`](https://github.com/eea/volto-cca-policy/commit/0a4f91f39b7edc367bd4c127d6a8f273c7788361)]
4274
+ - Add Sonarqube tag using cca-frontend addons list [EEA Jenkins - [`8f1f9ce`](https://github.com/eea/volto-cca-policy/commit/8f1f9ce6c22805670cc0800d3c779b6d619d0f31)]
4131
4275
  ### [0.1.1](https://github.com/eea/volto-cca-policy/compare/0.1.0...0.1.1) - 13 December 2022
4132
4276
 
4133
4277
  #### :hammer_and_wrench: Others
@@ -0,0 +1,76 @@
1
+ # Link Integrity Workflow warning Specification
2
+
3
+ ## Overview
4
+
5
+ To prevent breaking the browsing experience of public (anonymous) website visitors, Climate-ADAPT intercepts workflow transitions that limit content visibility (such as changing a state to **Private**, **Reject**, or **Retract**).
6
+
7
+ Before finalizing these transitions, the system performs an asynchronous backend check using the Plone link integrity relation catalog (`@linkintegrity`) to discover if any other published pages link to the target item. If referencing items are found, the user is presented with a warning modal detailing the pages that link to the item, allowing them to either cancel or proceed anyway.
8
+
9
+ ---
10
+
11
+ ## Architectural Workflow
12
+
13
+ ```mermaid
14
+ sequenceDiagram
15
+ autonumber
16
+ actor Editor
17
+ participant WorkflowDropdown as Workflow Dropdown (Workflow.jsx)
18
+ participant API as Plone REST API (/@linkintegrity)
19
+ participant WarningModal as Warning Modal (WorkflowLinkIntegrityModal.jsx)
20
+
21
+ Editor->>WorkflowDropdown: Select "Make Private" / "Reject" / "Retract"
22
+ WorkflowDropdown->>API: Dispatch linkIntegrityCheck([UID])
23
+ WorkflowDropdown->>WarningModal: Open Warning Modal (with Loader active)
24
+ Note over WarningModal: Action buttons are disabled during check
25
+
26
+ alt No breaches exist
27
+ API-->>WorkflowDropdown: Return 0 breaches
28
+ WorkflowDropdown->>WorkflowDropdown: Auto-execute workflow transition
29
+ WorkflowDropdown->>WarningModal: Close Modal automatically
30
+ WorkflowDropdown-->>Editor: Show success toast
31
+ else Breaches exist
32
+ API-->>WorkflowDropdown: Return breach details
33
+ WorkflowDropdown->>WarningModal: Deactivate Loader & enable confirm
34
+ WarningModal-->>Editor: Render list of linking pages & warning copy
35
+
36
+ alt Editor clicks "Cancel"
37
+ Editor->>WarningModal: Click "Cancel"
38
+ WarningModal->>WorkflowDropdown: Reset pending transition & close modal
39
+ else Editor clicks "Change state anyway"
40
+ Editor->>WarningModal: Click "Change state anyway"
41
+ WarningModal->>WorkflowDropdown: Trigger onOk() -> executeTransition()
42
+ WorkflowDropdown->>WorkflowDropdown: Execute workflow transition
43
+ WorkflowDropdown->>WarningModal: Close Modal
44
+ WorkflowDropdown-->>Editor: Show success toast
45
+ end
46
+ end
47
+ ```
48
+
49
+ ---
50
+
51
+ ## Implementation Details
52
+
53
+ ### 1. Workflow Interception (Customization)
54
+ * **Path**: [Workflow.jsx](file:///home/tibi/work/eea.docker.plone-climateadapt/cca/frontend/src/addons/volto-cca-policy/src/customizations/volto/components/manage/Workflow/Workflow.jsx)
55
+ * **Role**: Shadows Volto core's workflow select component. Intercepts transitions containing state keys (`private`, `reject`, `retract`) or URL suffixes (`/reject`, `/retract`). Dispatches `linkIntegrityCheck` and manages `pendingOption` state. Executes transition automatically if 0 breaches are found.
56
+
57
+ ### 2. Warning Modal Component
58
+ * **Path**: [WorkflowLinkIntegrityModal.jsx](file:///home/tibi/work/eea.docker.plone-climateadapt/cca/frontend/src/addons/volto-cca-policy/src/components/manage/Workflow/WorkflowLinkIntegrityModal.jsx)
59
+ * **Role**: A plain HTML overlay dialog rendered via `ReactDOM.createPortal` into a `<div>` appended to `document.body`. Zero `semantic-ui-react` dependencies — no `Confirm`, `Modal`, or `Portal`.
60
+ * **Why not `semantic-ui-react`?** The `Confirm` and `Modal` components use internal Portals, auto-controlled state, and shorthand factory systems that made click handlers unreliable in the toolbar dropdown context. The Toolbar's global `document mousedown` handler (`handleClickOutside`) would intercept clicks, close the menu, and unmount the modal before `onConfirm` could fire.
61
+ * **Key implementation details**:
62
+ * **Portal rendering** — renders at `document.body` level with `z-index: 10000`, placing it outside the toolbar dropdown's DOM subtree and CSS stacking context so it appears as a proper full-page overlay.
63
+ * **Capture-phase `mousedown` guard** — a `capture=true` listener on the portal root calls `e.stopPropagation()`, preventing the Toolbar's `handleClickOutside` from firing while the modal is open.
64
+ * **Synchronous breach derivation** — `computeBreaches()` computes `brokenReferences` and `breaches` during render (no `useEffect` + `useState`), so the data is always in sync with `loading` in the same render cycle. This prevents a race condition where `loading` becomes `false` but `brokenReferences` is still `0`, causing premature modal closure.
65
+ * **Inline styles** — all CSS is embedded via a `<style>` tag, no external stylesheet dependency.
66
+ * **Key UX Features**:
67
+ * Maintains button state `disabled: loading` during ongoing checks to prevent premature actions.
68
+ * Renders a CSS spinner and loading text during the check.
69
+ * Displays the exact list of referencing (source) items and target sub-items.
70
+ * Displays the warning copy:
71
+ > *"By changing the state, we're not breaking references, but may break user experience for final Anonymous users. There are {brokenReferences} {variation} to this item:"*
72
+ * Closes on backdrop click, button click, or `Escape` key.
73
+
74
+ ### 3. Unit Verification Suite
75
+ * **Path**: [WorkflowLinkIntegrityModal.test.jsx](file:///home/tibi/work/eea.docker.plone-climateadapt/cca/frontend/src/addons/volto-cca-policy/src/components/manage/Workflow/WorkflowLinkIntegrityModal.test.jsx)
76
+ * **Role**: Houses Jest/React Testing Library assertions verifying loading indicator visibility, auto-proceed behavior with 0 breaches, warnings list aggregation for multiple breaches, and confirm button presence.
@@ -0,0 +1,33 @@
1
+ # Link Integrity & Workflow Documentation Index
2
+
3
+ This directory houses the research, analysis, and specifications for Plone/Volto Link Integrity tracking and the "Warn Before Private" workflow state transition verification feature.
4
+
5
+ Below is an overview of the role and purpose of each document in this directory:
6
+
7
+ ---
8
+
9
+ ## 📖 Document Catalog
10
+
11
+ ### 1. [README.md](./README.md)
12
+ * **Role**: **Feature Specification & Technical Implementation Guide**
13
+ * **Description**: High-level specification outlining the "Warn before private" workflow transition warning system. It explains how workflow transitions to private or retracted states are intercepted to check for breaches, contains a Mermaid sequence diagram of the operational flow, and details pointers to the exact implementation files (`Workflow.jsx`, `WorkflowLinkIntegrityModal.jsx`, and the unit test suite).
14
+
15
+ ### 2. [understanding-link-integrity.md](./understanding-link-integrity.md)
16
+ * **Role**: **Initial Research & Mechanics Overview**
17
+ * **Description**: Explains the underlying mechanics of Plone's `plone.app.linkintegrity` package, the `@linkintegrity` and `@relations` REST API endpoints, how internal references are stored in the `zc.relation` catalog using `isReferencing`, and proposes the initial architectural solution for shadowing the Volto toolbar component.
18
+
19
+ ### 3. [volto-block-link-discovery.md](./volto-block-link-discovery.md)
20
+ * **Role**: **Block Parser Link Extraction Specs**
21
+ * **Description**: Detailed specifications on how the Plone backend's block-to-HTML parser (via `plone.volto`) parses internal links and retrieves referencing UIDs from complex Volto blocks (e.g. Columns, Teasers, Maps) to keep the relation catalog accurately indexed.
22
+
23
+ ### 4. [volto-block-link-analysis.md](./volto-block-link-analysis.md)
24
+ * **Role**: **Technical Analysis of Internal Linking**
25
+ * **Description**: Analyzes how internal links are serialized, stored, and resolved within customized layouts. Explains matching strategies for Dexterity schemas and Choice relation fields.
26
+
27
+ ### 5. [link-integrity-blocks-report.md](./link-integrity-blocks-report.md)
28
+ * **Role**: **Volto Blocks Reference Index**
29
+ * **Description**: A quick-reference status report listing the Volto blocks whose internal link fields are covered by link integrity tracking and validation rules.
30
+
31
+ ### 6. [link-integrity-block-fields-report.md](./link-integrity-block-fields-report.md)
32
+ * **Role**: **Field Mapping Specification**
33
+ * **Description**: A comprehensive deep-dive report mapping exactly which fields and properties within the custom blocks schemas are indexed and tracked by the link integrity processor.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-cca-policy",
3
- "version": "0.3.125",
3
+ "version": "0.3.127",
4
4
  "description": "@eeacms/volto-cca-policy: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -1,10 +1,10 @@
1
- import React, { useState, useEffect } from 'react';
1
+ import React, { useEffect, useCallback, useRef } from 'react';
2
+ import { createPortal } from 'react-dom';
2
3
  import PropTypes from 'prop-types';
3
4
  import { useSelector } from 'react-redux';
4
5
  import { Link } from 'react-router-dom';
5
6
  import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
6
7
  import { flattenToAppURL } from '@plone/volto/helpers';
7
- import { Confirm, Dimmer, Loader, Table } from 'semantic-ui-react';
8
8
 
9
9
  const messages = defineMessages({
10
10
  confirmHeader: {
@@ -29,138 +29,237 @@ const messages = defineMessages({
29
29
  },
30
30
  });
31
31
 
32
+ /**
33
+ * Derive breach data synchronously from linkintegrityInfo.
34
+ * No useEffect + useState — computed during render so it is always
35
+ * in sync with the loading flag in the same render cycle.
36
+ */
37
+ function computeBreaches(linkintegrityInfo) {
38
+ if (!linkintegrityInfo) {
39
+ return { brokenReferences: 0, breaches: [] };
40
+ }
41
+
42
+ const all = linkintegrityInfo.flatMap((result) =>
43
+ result.breaches.map((source) => ({ source, target: result })),
44
+ );
45
+
46
+ const sourceByUid = new Map();
47
+ const bySource = new Map();
48
+
49
+ for (const entry of all) {
50
+ sourceByUid.set(entry.source.uid, entry.source);
51
+ if (!bySource.has(entry.source.uid)) {
52
+ bySource.set(entry.source.uid, new Set());
53
+ }
54
+ bySource.get(entry.source.uid).add(entry.target);
55
+ }
56
+
57
+ return {
58
+ brokenReferences: bySource.size,
59
+ breaches: Array.from(bySource, ([uid, targets]) => ({
60
+ source: sourceByUid.get(uid),
61
+ targets: Array.from(targets),
62
+ })),
63
+ };
64
+ }
65
+
66
+ /**
67
+ * Plain HTML overlay dialog — no semantic-ui-react, no Portal,
68
+ * no Confirm, no Modal. Just a fixed-position div with inline styles.
69
+ */
32
70
  const WorkflowLinkIntegrityModal = (props) => {
33
71
  const { open, onCancel, onOk } = props;
34
72
  const intl = useIntl();
35
73
  const linkintegrityInfo = useSelector((state) => state.linkIntegrity?.result);
36
74
  const loading = useSelector((state) => state.linkIntegrity?.loading);
37
75
 
38
- const [brokenReferences, setBrokenReferences] = useState(0);
39
- const [breaches, setBreaches] = useState([]);
76
+ const { brokenReferences, breaches } = computeBreaches(linkintegrityInfo);
77
+
78
+ // Keep visible while loading OR while breaches exist.
79
+ // Because brokenReferences is derived synchronously, it is always
80
+ // consistent with `loading` in the same render.
81
+ const show = open && (loading || brokenReferences > 0);
82
+
83
+ // Create a dedicated mount container attached to document.body so the
84
+ // modal renders outside the toolbar dropdown's stacking context.
85
+ // Initialized synchronously (not in useEffect) so it is available on
86
+ // the first render — important for tests and to avoid a flicker.
87
+ const containerRef = useRef(
88
+ (() => {
89
+ const el = document.createElement('div');
90
+ document.body.appendChild(el);
91
+ return el;
92
+ })(),
93
+ );
40
94
 
41
95
  useEffect(() => {
42
- if (linkintegrityInfo) {
43
- const breaches = linkintegrityInfo.flatMap((result) =>
44
- result.breaches.map((source) => ({
45
- source: source,
46
- target: result,
47
- })),
48
- );
49
- const source_by_uid = breaches.reduce(
50
- (acc, value) => acc.set(value.source.uid, value.source),
51
- new Map(),
52
- );
53
- const by_source = breaches.reduce((acc, value) => {
54
- if (acc.get(value.source.uid) === undefined) {
55
- acc.set(value.source.uid, new Set());
56
- }
57
- acc.get(value.source.uid).add(value.target);
58
- return acc;
59
- }, new Map());
60
-
61
- setBrokenReferences(by_source.size);
62
- setBreaches(
63
- Array.from(by_source, (entry) => ({
64
- source: source_by_uid.get(entry[0]),
65
- targets: Array.from(entry[1]),
66
- })),
67
- );
68
- } else {
69
- setBrokenReferences(0);
70
- setBreaches([]);
96
+ const container = containerRef.current;
97
+ return () => {
98
+ if (container && container.parentNode) {
99
+ container.parentNode.removeChild(container);
100
+ }
101
+ };
102
+ }, []);
103
+
104
+ // Close on Escape key
105
+ const handleKeyDown = useCallback(
106
+ (e) => {
107
+ if (e.key === 'Escape') {
108
+ e.preventDefault();
109
+ e.stopPropagation();
110
+ onCancel();
111
+ }
112
+ },
113
+ [onCancel],
114
+ );
115
+
116
+ // Prevent the Toolbar's global `document mousedown` handler from closing
117
+ // the toolbar menu while our modal is open. The Toolbar listens on
118
+ // `document` for `mousedown` and calls `closeMenu()` if the click is
119
+ // outside the toolbar pusher. By adding a capture-phase listener on our
120
+ // portal root, we intercept the event before it bubbles to `document`.
121
+ const handleMousedown = useCallback((e) => {
122
+ e.stopPropagation();
123
+ }, []);
124
+
125
+ useEffect(() => {
126
+ if (show) {
127
+ const container = containerRef.current;
128
+ document.addEventListener('keydown', handleKeyDown);
129
+ // capture=true: fires before any bubbling listeners on ancestors
130
+ container.addEventListener('mousedown', handleMousedown, true);
131
+ return () => {
132
+ document.removeEventListener('keydown', handleKeyDown);
133
+ container.removeEventListener('mousedown', handleMousedown, true);
134
+ };
71
135
  }
72
- }, [linkintegrityInfo]);
73
-
74
- // If we are still loading, show the dimmer
75
- if (loading && open) {
76
- return (
77
- <Confirm
78
- open={open}
79
- header={intl.formatMessage(messages.confirmHeader)}
80
- content={
81
- <div className="content">
82
- <Dimmer active inverted>
83
- <Loader indeterminate size="massive">
84
- {intl.formatMessage(messages.loading)}
85
- </Loader>
86
- </Dimmer>
87
- <div style={{ minHeight: '100px' }} />
136
+ }, [show, handleKeyDown, handleMousedown]);
137
+
138
+ if (!show) return null;
139
+
140
+ return createPortal(
141
+ <>
142
+ <div
143
+ className="li-modal-backdrop"
144
+ role="presentation"
145
+ tabIndex={-1}
146
+ onClick={(e) => {
147
+ // Only cancel when clicking the backdrop itself, not children
148
+ if (e.target === e.currentTarget) {
149
+ onCancel();
150
+ }
151
+ }}
152
+ onKeyDown={(e) => {
153
+ if (e.key === 'Escape') {
154
+ e.preventDefault();
155
+ e.stopPropagation();
156
+ onCancel();
157
+ }
158
+ }}
159
+ data-testid="li-modal-backdrop"
160
+ >
161
+ <div
162
+ className="li-modal-dialog"
163
+ role="dialog"
164
+ aria-modal="true"
165
+ aria-labelledby="li-modal-title"
166
+ data-testid="li-modal-dialog"
167
+ >
168
+ <div className="li-modal-header">
169
+ <span className="li-modal-title" id="li-modal-title">
170
+ {intl.formatMessage(messages.confirmHeader)}
171
+ </span>
88
172
  </div>
89
- }
90
- onCancel={onCancel}
91
- onConfirm={() => {}}
92
- />
93
- );
94
- }
95
173
 
96
- return (
97
- open &&
98
- brokenReferences > 0 && (
99
- <Confirm
100
- open={open}
101
- confirmButton={intl.formatMessage(messages.confirmAction)}
102
- cancelButton={intl.formatMessage(messages.cancel)}
103
- header={intl.formatMessage(messages.confirmHeader)}
104
- content={
105
- <div className="content">
106
- <FormattedMessage
107
- id="Changing the state of this item will break {brokenReferences} {variation} to it."
108
- defaultMessage="Changing the state of this item will break {brokenReferences} {variation} to it."
109
- values={{
110
- brokenReferences: <span>{brokenReferences}</span>,
111
- variation: (
112
- <span>
113
- {brokenReferences === 1 ? (
114
- <FormattedMessage
115
- id="reference"
116
- defaultMessage="reference"
117
- />
118
- ) : (
119
- <FormattedMessage
120
- id="references"
121
- defaultMessage="references"
122
- />
123
- )}
124
- </span>
125
- ),
126
- }}
127
- />
128
- <BrokenLinksList intl={intl} breaches={breaches} />
174
+ <div className="li-modal-content">
175
+ {loading && (
176
+ <div className="li-modal-loading">
177
+ <div className="li-spinner" />
178
+ <span>{intl.formatMessage(messages.loading)}</span>
179
+ </div>
180
+ )}
181
+
182
+ {!loading && brokenReferences > 0 && (
183
+ <>
184
+ <p>
185
+ <FormattedMessage
186
+ id="By changing the state, we're not breaking references, but may break user experience for final Anonymous users. There are {brokenReferences} {variation} to this item:"
187
+ defaultMessage="By changing the state, we're not breaking references, but may break user experience for final Anonymous users. There are {brokenReferences} {variation} to this item:"
188
+ values={{
189
+ brokenReferences: <strong>{brokenReferences}</strong>,
190
+ variation:
191
+ brokenReferences === 1 ? (
192
+ <FormattedMessage
193
+ id="reference"
194
+ defaultMessage="reference"
195
+ />
196
+ ) : (
197
+ <FormattedMessage
198
+ id="references"
199
+ defaultMessage="references"
200
+ />
201
+ ),
202
+ }}
203
+ />
204
+ </p>
205
+ <BrokenLinksList intl={intl} breaches={breaches} />
206
+ </>
207
+ )}
129
208
  </div>
130
- }
131
- onCancel={onCancel}
132
- onConfirm={onOk}
133
- size="small"
134
- />
135
- )
209
+
210
+ <div className="li-modal-actions">
211
+ <button
212
+ className="li-btn li-btn-secondary"
213
+ onClick={onCancel}
214
+ disabled={loading}
215
+ data-testid="li-btn-cancel"
216
+ >
217
+ {intl.formatMessage(messages.cancel)}
218
+ </button>
219
+ <button
220
+ className="li-btn li-btn-primary"
221
+ onClick={onOk}
222
+ disabled={loading}
223
+ data-testid="li-btn-confirm"
224
+ >
225
+ {intl.formatMessage(messages.confirmAction)}
226
+ </button>
227
+ </div>
228
+ </div>
229
+ </div>
230
+ <style>{modalStyles}</style>
231
+ </>,
232
+ containerRef.current,
136
233
  );
137
234
  };
138
235
 
139
236
  const BrokenLinksList = ({ intl, breaches }) => {
140
237
  return (
141
- <div className="broken-links-list" style={{ marginTop: '20px' }}>
142
- <FormattedMessage
143
- id="These items will have broken links"
144
- defaultMessage="These items will have broken links"
145
- />
146
- :
147
- <Table compact>
148
- <Table.Body>
238
+ <div className="li-broken-links-list">
239
+ <p>
240
+ <FormattedMessage
241
+ id="These items will have broken links"
242
+ defaultMessage="These items will have broken links"
243
+ />
244
+ :
245
+ </p>
246
+ <table className="li-breach-table">
247
+ <tbody>
149
248
  {breaches.map((breach) => (
150
- <Table.Row key={breach.source['@id']} verticalAlign="top">
151
- <Table.Cell>
249
+ <tr key={breach.source['@id']}>
250
+ <td className="li-breach-source">
152
251
  <Link
153
252
  to={flattenToAppURL(breach.source['@id'])}
154
253
  title={intl.formatMessage(messages.navigate_to_this_item)}
155
254
  >
156
255
  {breach.source.title}
157
256
  </Link>
158
- </Table.Cell>
159
- <Table.Cell style={{ minWidth: '140px' }}>
257
+ </td>
258
+ <td className="li-breach-label">
160
259
  <FormattedMessage id="refers to" defaultMessage="refers to" />:
161
- </Table.Cell>
162
- <Table.Cell>
163
- <ul style={{ margin: 0 }}>
260
+ </td>
261
+ <td className="li-breach-targets">
262
+ <ul>
164
263
  {breach.targets.map((target) => (
165
264
  <li key={target['@id']}>
166
265
  <Link
@@ -174,11 +273,11 @@ const BrokenLinksList = ({ intl, breaches }) => {
174
273
  </li>
175
274
  ))}
176
275
  </ul>
177
- </Table.Cell>
178
- </Table.Row>
276
+ </td>
277
+ </tr>
179
278
  ))}
180
- </Table.Body>
181
- </Table>
279
+ </tbody>
280
+ </table>
182
281
  </div>
183
282
  );
184
283
  };
@@ -190,3 +289,155 @@ WorkflowLinkIntegrityModal.propTypes = {
190
289
  };
191
290
 
192
291
  export default WorkflowLinkIntegrityModal;
292
+
293
+ /**
294
+ * Minimal inline styles — no external CSS dependency.
295
+ * Matches Volto's general look (white dialog, centred, dark backdrop).
296
+ */
297
+ const modalStyles = `
298
+ .li-modal-backdrop {
299
+ position: fixed;
300
+ inset: 0;
301
+ z-index: 10000;
302
+ display: flex;
303
+ align-items: center;
304
+ justify-content: center;
305
+ background: rgba(0, 0, 0, 0.6);
306
+ }
307
+
308
+ .li-modal-dialog {
309
+ background: #fff;
310
+ border-radius: 8px;
311
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
312
+ max-width: 600px;
313
+ width: 90%;
314
+ max-height: 80vh;
315
+ overflow-y: auto;
316
+ display: flex;
317
+ flex-direction: column;
318
+ }
319
+
320
+ .li-modal-header {
321
+ padding: 16px 20px;
322
+ border-bottom: 1px solid #e0e0e0;
323
+ }
324
+
325
+ .li-modal-title {
326
+ font-size: 18px;
327
+ font-weight: 600;
328
+ color: #333;
329
+ }
330
+
331
+ .li-modal-content {
332
+ padding: 20px;
333
+ min-height: 80px;
334
+ color: #4a4a4a;
335
+ font-size: 14px;
336
+ line-height: 1.5;
337
+ }
338
+
339
+ .li-modal-loading {
340
+ display: flex;
341
+ align-items: center;
342
+ gap: 12px;
343
+ justify-content: center;
344
+ padding: 20px 0;
345
+ }
346
+
347
+ .li-spinner {
348
+ width: 24px;
349
+ height: 24px;
350
+ border: 3px solid #e0e0e0;
351
+ border-top-color: #007bc1;
352
+ border-radius: 50%;
353
+ animation: li-spin 0.7s linear infinite;
354
+ }
355
+
356
+ @keyframes li-spin {
357
+ to { transform: rotate(360deg); }
358
+ }
359
+
360
+ .li-modal-actions {
361
+ display: flex;
362
+ justify-content: flex-end;
363
+ gap: 10px;
364
+ padding: 16px 20px;
365
+ border-top: 1px solid #e0e0e0;
366
+ }
367
+
368
+ .li-btn {
369
+ padding: 8px 18px;
370
+ border-radius: 4px;
371
+ font-size: 14px;
372
+ cursor: pointer;
373
+ border: 1px solid transparent;
374
+ transition: background 0.15s, border-color 0.15s;
375
+ }
376
+
377
+ .li-btn:disabled {
378
+ opacity: 0.5;
379
+ cursor: not-allowed;
380
+ }
381
+
382
+ .li-btn-secondary {
383
+ background: #f5f5f5;
384
+ border-color: #d0d0d0;
385
+ color: #333;
386
+ }
387
+
388
+ .li-btn-secondary:hover:not(:disabled) {
389
+ background: #ebebeb;
390
+ }
391
+
392
+ .li-btn-primary {
393
+ background: #007bc1;
394
+ color: #fff;
395
+ }
396
+
397
+ .li-btn-primary:hover:not(:disabled) {
398
+ background: #005a89;
399
+ }
400
+
401
+ .li-broken-links-list {
402
+ margin-top: 16px;
403
+ }
404
+
405
+ .li-breach-table {
406
+ width: 100%;
407
+ border-collapse: collapse;
408
+ font-size: 13px;
409
+ }
410
+
411
+ .li-breach-table td {
412
+ padding: 6px 8px;
413
+ vertical-align: top;
414
+ border-bottom: 1px solid #eee;
415
+ }
416
+
417
+ .li-breach-label {
418
+ white-space: nowrap;
419
+ padding-left: 12px;
420
+ color: #888;
421
+ width: 1px;
422
+ }
423
+
424
+ .li-breach-targets ul {
425
+ margin: 0;
426
+ padding-left: 18px;
427
+ }
428
+
429
+ .li-breach-targets li {
430
+ margin-bottom: 2px;
431
+ }
432
+
433
+ .li-breach-source a,
434
+ .li-breach-targets a {
435
+ color: #007bc1;
436
+ text-decoration: none;
437
+ }
438
+
439
+ .li-breach-source a:hover,
440
+ .li-breach-targets a:hover {
441
+ text-decoration: underline;
442
+ }
443
+ `;
@@ -55,7 +55,7 @@ describe('WorkflowLinkIntegrityModal', () => {
55
55
  expect(screen.getByText('Checking references...')).toBeInTheDocument();
56
56
  });
57
57
 
58
- it('auto-proceeds when no breaches are found', () => {
58
+ it('renders nothing when no breaches are found (auto-proceed handled by parent)', () => {
59
59
  const store = makeStore({
60
60
  result: [{ breaches: [], '@id': 'http://localhost:8080/Plone/target' }],
61
61
  loaded: true,
@@ -68,11 +68,10 @@ describe('WorkflowLinkIntegrityModal', () => {
68
68
  />,
69
69
  store,
70
70
  );
71
- // brokenReferences === 0 so the Confirm dialog is not rendered
72
71
  expect(container.innerHTML).toBe('');
73
72
  });
74
73
 
75
- it('shows warning modal with breach list when breaches are found', () => {
74
+ it('shows warning dialog with breach list when breaches are found', () => {
76
75
  const store = makeStore({
77
76
  result: [
78
77
  {
@@ -141,7 +140,6 @@ describe('WorkflowLinkIntegrityModal', () => {
141
140
  />,
142
141
  store,
143
142
  );
144
- // Both breaches come from the same source, so brokenReferences === 1
145
143
  expect(screen.getByText('Source Page')).toBeInTheDocument();
146
144
  expect(screen.getByText('Target 1')).toBeInTheDocument();
147
145
  expect(screen.getByText('Target 2')).toBeInTheDocument();
@@ -12,11 +12,33 @@ By shadowing this component, we can intercept transitions that might hide conten
12
12
 
13
13
  1. **Interception Logic**: The `transition` function was modified to check for "private-like" transitions (`private`, `reject`, `retract`).
14
14
  2. **Link Integrity Check**: When a sensitive transition is selected, the `linkIntegrityCheck` action is dispatched to the backend.
15
- 3. **State Management**: Added local state (`showWarningModal`, `pendingOption`) to handle the asynchronous check and the confirmation flow.
15
+ 3. **State Management**: Added local state (`showWarningModal`, `pendingOption`, `transitionTriggered`) to handle the asynchronous check and the confirmation flow.
16
16
  4. **Confirmation Modal**: Integrated `WorkflowLinkIntegrityModal` which displays the list of pages that would have broken links.
17
- 5. **Auto-proceed**: Added an `useEffect` that automatically executes the transition if the link integrity check returns zero breaches.
17
+ 5. **Auto-proceed**: Added an `useEffect` that automatically executes the transition if the link integrity check returns zero breaches. It is guarded by `transitionTriggered` to prevent double-execution if the user clicks "Change state anyway" at the same time.
18
18
  6. **Activity Indicators**: Added `Dimmer` and `Loader` components from `semantic-ui-react` to provide visual feedback while the link integrity check is loading and during the workflow transition execution.
19
19
 
20
+ ## Design Decisions
21
+
22
+ ### Plain HTML dialog via React Portal — no semantic-ui-react
23
+
24
+ The `WorkflowLinkIntegrityModal` renders via `ReactDOM.createPortal` into a `<div>` appended to `document.body`. This places it outside the toolbar dropdown's DOM subtree and CSS stacking context, so it appears as a proper full-page overlay at `z-index: 10000`.
25
+
26
+ No `semantic-ui-react` `Confirm`, `Modal`, or `Portal` is used. Those components rely on Portals, auto-controlled state, and shorthand factory systems that make click handlers unreliable in our use case. The plain HTML approach gives us full, predictable control: `onClick` on a `<button>` always fires.
27
+
28
+ ### Toolbar `handleClickOutside` interference
29
+
30
+ Volto's `Toolbar` component registers a global `document.addEventListener('mousedown', ...)` handler that closes the toolbar menu when clicking outside it. Since our modal renders on top of the toolbar menu, any `mousedown` would bubble up to `document` and trigger the menu close — which unmounts the `Workflow` component and our modal before the button `onClick` can fire.
31
+
32
+ The fix: a **capture-phase** `mousedown` listener on the portal root calls `e.stopPropagation()`, preventing the event from ever reaching `document`. This keeps the toolbar menu open while our modal is visible, without interfering with normal `click` event handling on the buttons.
33
+
34
+ ### Synchronous breach derivation (no useEffect + local state)
35
+
36
+ The breach data (`brokenReferences`, `breaches`) is computed synchronously inside a `computeBreaches()` helper function rather than via `useEffect` + `useState`. This is critical: the `linkIntegrity` reducer sets `loading: false` and `result: data` in the same action (`_SUCCESS`). If breach data were derived via `useEffect` + local state, there would be a render cycle where `loading` is already `false` but `brokenReferences` is still `0` (stale local state), causing the modal to close prematurely before the breach data is processed.
37
+
38
+ ### `transitionTriggered` guard
39
+
40
+ A `transitionTriggered` state flag prevents the auto-proceed `useEffect` from firing after the user has already clicked "Change state anyway", avoiding duplicate workflow transition API calls.
41
+
20
42
  ## Reference
21
43
 
22
44
  See implementation details in:
@@ -220,6 +220,7 @@ const Workflow = (props) => {
220
220
 
221
221
  const [showWarningModal, setShowWarningModal] = useState(false);
222
222
  const [pendingOption, setPendingOption] = useState(null);
223
+ const [transitionTriggered, setTransitionTriggered] = useState(false);
223
224
 
224
225
  useEffect(() => {
225
226
  dispatch(getWorkflow(pathname));
@@ -229,6 +230,7 @@ const Workflow = (props) => {
229
230
  const executeTransition = useCallback(
230
231
  (selectedOption) => {
231
232
  if (selectedOption?.url) {
233
+ setTransitionTriggered(true);
232
234
  dispatch(transitionWorkflow(flattenToAppURL(selectedOption.url)));
233
235
  toast.success(
234
236
  <Toast
@@ -243,7 +245,7 @@ const Workflow = (props) => {
243
245
  );
244
246
 
245
247
  useEffect(() => {
246
- if (showWarningModal) {
248
+ if (showWarningModal && !transitionTriggered) {
247
249
  if (linkintegrityError) {
248
250
  // If the check fails, we shouldn't block the user forever. Proceed with transition.
249
251
  executeTransition(pendingOption);
@@ -269,6 +271,7 @@ const Workflow = (props) => {
269
271
  linkintegrityError,
270
272
  showWarningModal,
271
273
  pendingOption,
274
+ transitionTriggered,
272
275
  content,
273
276
  executeTransition,
274
277
  ]);
@@ -281,6 +284,7 @@ const Workflow = (props) => {
281
284
 
282
285
  if (isPrivateTransition) {
283
286
  setPendingOption(selectedOption);
287
+ setTransitionTriggered(false);
284
288
  dispatch(linkIntegrityCheck([content.UID]));
285
289
  setShowWarningModal(true);
286
290
  } else {
@@ -341,6 +345,7 @@ const Workflow = (props) => {
341
345
  onCancel={() => {
342
346
  setShowWarningModal(false);
343
347
  setPendingOption(null);
348
+ setTransitionTriggered(false);
344
349
  }}
345
350
  onOk={() => {
346
351
  executeTransition(pendingOption);