@eeacms/volto-cca-policy 0.3.85 → 0.3.86

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,7 +4,7 @@ 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.85](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.1...0.3.85) - 14 October 2025
7
+ ### [0.3.86](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.1...0.3.86) - 31 October 2025
8
8
 
9
9
  #### :rocket: Dependency updates
10
10
 
@@ -14,6 +14,10 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
14
14
 
15
15
  #### :bug: Bug Fixes
16
16
 
17
+ - fix(tests): increase code coverage [kreafox - [`66cd2b3`](https://github.com/eea/volto-cca-policy/commit/66cd2b39a9b7b026c171d99e2bf3b8a47b7cddd2)]
18
+ - fix(tests): update hyperlink text in PlanningTab [kreafox - [`05080e0`](https://github.com/eea/volto-cca-policy/commit/05080e0727730a5fe7c67d884ad6453eaf82081a)]
19
+ - fix(mission): adjust margins for nested lists and accordion padding [kreafox - [`1abf18f`](https://github.com/eea/volto-cca-policy/commit/1abf18fabe9efffa552ed2e6a8b57f9b1d59f7a6)]
20
+ - fix(mission): improve URL extraction logic - refs #291190 [kreafox - [`8e4c6e4`](https://github.com/eea/volto-cca-policy/commit/8e4c6e42dc35b4c940c39f9f25d587f66d0b242d)]
17
21
  - fix(mission): fix assessment risk numbering to match sorted order [kreafox - [`9ecea88`](https://github.com/eea/volto-cca-policy/commit/9ecea885394f1ae6ad63ffb03d8a10cd706caffb)]
18
22
  - fix: duplicated code - make ItemsSection reusable [kreafox - [`52e2625`](https://github.com/eea/volto-cca-policy/commit/52e2625914f699c194f75d561f97959ab6c21fbf)]
19
23
  - fix: add missing props for title block [kreafox - [`1d8efa0`](https://github.com/eea/volto-cca-policy/commit/1d8efa0136960261196e7585f89f4ff7db75ae17)]
@@ -21,6 +25,8 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
21
25
 
22
26
  #### :nail_care: Enhancements
23
27
 
28
+ - change(mission): add third level for sectors and hazards - refs #291189 [kreafox - [`5cf352c`](https://github.com/eea/volto-cca-policy/commit/5cf352ce46284b3cbd106276b60a1d4fea89235d)]
29
+ - refactor(mission): simplify sector rendering logic in Action tab [kreafox - [`8413ccc`](https://github.com/eea/volto-cca-policy/commit/8413cccfd67e9a598f88474b8a034e7998063d23)]
24
30
  - change(mission): improve AccordionList [kreafox - [`cf1497f`](https://github.com/eea/volto-cca-policy/commit/cf1497ff50ff45cdb0bb8e995683969bb3e3c065)]
25
31
  - refactor(mission): remove unnecessary line breaks and simplify message rendering - refs #291190 [kreafox - [`430a123`](https://github.com/eea/volto-cca-policy/commit/430a12371b6e1c6a77ea65b6a434eda883568c64)]
26
32
  - refactor(mission): use ItemsSection in PlanningTab - refs #291190 [kreafox - [`9e96aaa`](https://github.com/eea/volto-cca-policy/commit/9e96aaac0686f6688b3406aca578b598882a9c29)]
@@ -40,6 +46,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
40
46
 
41
47
  #### :hammer_and_wrench: Others
42
48
 
49
+ - test: try to use specific version for plone-backend [kreafox - [`79c542e`](https://github.com/eea/volto-cca-policy/commit/79c542e8d55d2f1663b768c555880599634ae527)]
43
50
  - test(mission): add unit tests for NoDataReported and StatisticSection components [kreafox - [`36c6e36`](https://github.com/eea/volto-cca-policy/commit/36c6e36278124759240c896786f2ac0c20674730)]
44
51
  - test: resolve Jest errors in component tests [kreafox - [`6dbb735`](https://github.com/eea/volto-cca-policy/commit/6dbb73540e8301fcb42a4d52db7004f5a42db4d8)]
45
52
  - test: fix PlanningTab mock data to match component structure [kreafox - [`48e76e1`](https://github.com/eea/volto-cca-policy/commit/48e76e1e50e0c6d8a6a500fec3d1831419faeee9)]
@@ -129,8 +136,22 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
129
136
  - Add some loadable for components [Tiberiu Ichim - [`1793962`](https://github.com/eea/volto-cca-policy/commit/179396211c66a6a2465b2d1b6c0f2afc40fc7189)]
130
137
  - Refs #284961 - test [Tripon Eugen - [`c989f1f`](https://github.com/eea/volto-cca-policy/commit/c989f1f8638c0c5233c5c49f8673c9a2cdc7937e)]
131
138
  - Refs #284961 - add translations [Tripon Eugen - [`04ee988`](https://github.com/eea/volto-cca-policy/commit/04ee988c086d393b9b37ce1ea8d24f5e84f266aa)]
132
- ### [1.0.0-alpha.0](https://github.com/eea/volto-cca-policy/compare/0.3.84...1.0.0-alpha.0) - 15 July 2025
139
+ ### [1.0.0-alpha.0](https://github.com/eea/volto-cca-policy/compare/0.3.85...1.0.0-alpha.0) - 15 July 2025
133
140
 
141
+ ### [0.3.85](https://github.com/eea/volto-cca-policy/compare/0.3.84...0.3.85) - 14 October 2025
142
+
143
+ #### :bug: Bug Fixes
144
+
145
+ - fix(mission): fix assessment risk numbering to match sorted order [kreafox - [`9ecea88`](https://github.com/eea/volto-cca-policy/commit/9ecea885394f1ae6ad63ffb03d8a10cd706caffb)]
146
+
147
+ #### :nail_care: Enhancements
148
+
149
+ - change(mission): improve AccordionList [kreafox - [`cf1497f`](https://github.com/eea/volto-cca-policy/commit/cf1497ff50ff45cdb0bb8e995683969bb3e3c065)]
150
+ - refactor(mission): remove unnecessary line breaks and simplify message rendering - refs #291190 [kreafox - [`430a123`](https://github.com/eea/volto-cca-policy/commit/430a12371b6e1c6a77ea65b6a434eda883568c64)]
151
+
152
+ #### :hammer_and_wrench: Others
153
+
154
+ - test(mission): add unit tests for NoDataReported and StatisticSection components [kreafox - [`36c6e36`](https://github.com/eea/volto-cca-policy/commit/36c6e36278124759240c896786f2ac0c20674730)]
134
155
  ### [0.3.84](https://github.com/eea/volto-cca-policy/compare/0.3.83...0.3.84) - 7 October 2025
135
156
 
136
157
  #### :bug: Bug Fixes
@@ -236,7 +257,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
236
257
  - style: fix item spacing [kreafox - [`7d4535d`](https://github.com/eea/volto-cca-policy/commit/7d4535dbc486e6cb80fc55d6ac8c7c01d9cf48af)]
237
258
  - chore: code cleanup [kreafox - [`842419b`](https://github.com/eea/volto-cca-policy/commit/842419b1bce3dc624755c81c9dea672198cab9e2)]
238
259
  - style: update teaser block styling [kreafox - [`1e0cad8`](https://github.com/eea/volto-cca-policy/commit/1e0cad8e1ed3ae827ee868dce8ad50b292a937ba)]
239
- - chore: update Makefile, run yarn i18n [kreafox - [`61e6962`](https://github.com/eea/volto-cca-policy/commit/61e6962901068ae6512265417a730bd9befd65fe)]
240
260
  - chore: move styling to theme folder [kreafox - [`8cf6a1b`](https://github.com/eea/volto-cca-policy/commit/8cf6a1b8b14abf472a9c61b28db574f6d9e0bd3b)]
241
261
  - chore: remove ArrayWidget customization [kreafox - [`249bf93`](https://github.com/eea/volto-cca-policy/commit/249bf93054f7726c3d57a8b8ca01b75cd0d30522)]
242
262
  - chore: remove Footer customization [kreafox - [`7038af3`](https://github.com/eea/volto-cca-policy/commit/7038af37d48a328dfc31746f3fa84973e2f840ab)]
@@ -2999,13 +3019,10 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
2999
3019
  - Refs #260715 rast-block wip [Tripon Eugen - [`f19d54e`](https://github.com/eea/volto-cca-policy/commit/f19d54e0b9a6a86bf344eb85b6a1cda7f3de91bf)]
3000
3020
  - Refs #260715 rast-block wip [Tripon Eugen - [`2828537`](https://github.com/eea/volto-cca-policy/commit/2828537b6c084cd1a82162d552fb4ef025b71f9f)]
3001
3021
  - Refs #260715 rast-block updates [Tripon Eugen - [`1e803e5`](https://github.com/eea/volto-cca-policy/commit/1e803e5bd3d3fb7558f261c76c68866be7beb8b5)]
3002
- - test: [JENKINS] Use java17 for sonarqube scanner [valentinab25 - [`0a15e1b`](https://github.com/eea/volto-cca-policy/commit/0a15e1b2ad081233685e80d5b3c60a8663f6b896)]
3003
- - test: [JENKINS] Run cypress in started frontend container [valentinab25 - [`9554e44`](https://github.com/eea/volto-cca-policy/commit/9554e44c92a621a52b2adb5a4830fb084ee5734b)]
3004
3022
  ### [0.1.49](https://github.com/eea/volto-cca-policy/compare/0.1.48...0.1.49) - 15 November 2023
3005
3023
 
3006
3024
  #### :house: Internal changes
3007
3025
 
3008
- - chore: [JENKINS] Refactor automated testing [valentinab25 - [`7b820a6`](https://github.com/eea/volto-cca-policy/commit/7b820a6369c2ddd5203b1a4abe352cb4bb43db7a)]
3009
3026
  - chore: husky, lint-staged use fixed versions [valentinab25 - [`f0a8061`](https://github.com/eea/volto-cca-policy/commit/f0a8061c275c236deb00087c23fac9860a073106)]
3010
3027
 
3011
3028
  #### :hammer_and_wrench: Others
@@ -3022,9 +3039,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
3022
3039
  - Refs #259267 - jenkins test [Tripon Eugen - [`cacd31e`](https://github.com/eea/volto-cca-policy/commit/cacd31e7b1afe0983674ed5c7632d2e1d7fa752e)]
3023
3040
  - Refs #259267 - jenkins [Tripon Eugen - [`5b3affe`](https://github.com/eea/volto-cca-policy/commit/5b3affee8401239de10097884c1b7f2349d15ec0)]
3024
3041
  - Refs #259267 - add When, lead image and title to files [Tripon Eugen - [`2cedb23`](https://github.com/eea/volto-cca-policy/commit/2cedb237f898af9057e13fba94b615ef71077204)]
3025
- - test: [JENKINS] Add cpu limit on cypress docker [valentinab25 - [`4d607a5`](https://github.com/eea/volto-cca-policy/commit/4d607a576e9d0a5c34e48c41b409e7df616ee3d6)]
3026
- - test: [JENKINS] Increase shm-size to cypress docker [valentinab25 - [`b7f74d5`](https://github.com/eea/volto-cca-policy/commit/b7f74d53513a6edbfbca5cb6d19687929bb1e5db)]
3027
- - test: [JENKINS] Improve cypress time [valentinab25 - [`db65617`](https://github.com/eea/volto-cca-policy/commit/db656173391f65157098d95d388c25f6429753d8)]
3028
3042
  - Refs #259267 - cca event blocks attachments and check not mandatoty fields [Tripon Eugen - [`3138e5a`](https://github.com/eea/volto-cca-policy/commit/3138e5afb5bfbdbed14e27ed457b16867b7fa414)]
3029
3043
  - 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)]
3030
3044
  - Refs #161485 - Fix ECDE name conflict. [GhitaB - [`8bfd99f`](https://github.com/eea/volto-cca-policy/commit/8bfd99ff68bb82a04d1c0ed625fa514fcf46289e)]
@@ -3241,7 +3255,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
3241
3255
 
3242
3256
  #### :house: Internal changes
3243
3257
 
3244
- - chore: [JENKINS] Remove alpha testing version [valentinab25 - [`ad1ced0`](https://github.com/eea/volto-cca-policy/commit/ad1ced0971ba116c13a3b5fcc039172cc915c919)]
3245
3258
 
3246
3259
  #### :hammer_and_wrench: Others
3247
3260
 
@@ -3722,7 +3735,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
3722
3735
  #### :hammer_and_wrench: Others
3723
3736
 
3724
3737
  - Refs #158294 - Update supported languages list. [GhitaB - [`0a4f91f`](https://github.com/eea/volto-cca-policy/commit/0a4f91f39b7edc367bd4c127d6a8f273c7788361)]
3725
- - Add Sonarqube tag using cca-frontend addons list [EEA Jenkins - [`8f1f9ce`](https://github.com/eea/volto-cca-policy/commit/8f1f9ce6c22805670cc0800d3c779b6d619d0f31)]
3726
3738
  ### [0.1.1](https://github.com/eea/volto-cca-policy/compare/0.1.0...0.1.1) - 13 December 2022
3727
3739
 
3728
3740
  #### :hammer_and_wrench: Others
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-cca-policy",
3
- "version": "0.3.85",
3
+ "version": "0.3.86",
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",
@@ -2,7 +2,12 @@ import '@testing-library/jest-dom';
2
2
  import { render, screen, fireEvent } from '@testing-library/react';
3
3
  import ImageGallery from './ImageGallery';
4
4
 
5
- jest.mock('react-slick', () => (props) => <div>React Slick Gallery</div>);
5
+ jest.mock('react-slick', () => {
6
+ const React = require('react');
7
+ return React.forwardRef((props, ref) => (
8
+ <div ref={ref}>React Slick Gallery</div>
9
+ ));
10
+ });
6
11
 
7
12
  const mockItems = [
8
13
  {
@@ -0,0 +1,70 @@
1
+ import '@testing-library/jest-dom';
2
+ import { render, screen } from '@testing-library/react';
3
+ import ItemsSection from './ItemsSection';
4
+ import defaultImage from '@eeacms/volto-cca-policy/../theme/assets/images/image-narrow.svg';
5
+
6
+ jest.mock('@eeacms/volto-cca-policy/utils', () => ({
7
+ normalizeImageFileName: (filename) => filename?.toLowerCase() || '',
8
+ }));
9
+
10
+ describe('ItemsSection', () => {
11
+ it('renders nothing when items is empty or undefined', () => {
12
+ const { container, rerender } = render(<ItemsSection items={[]} />);
13
+ expect(container.firstChild).toBeNull();
14
+
15
+ rerender(<ItemsSection />);
16
+ expect(container.firstChild).toBeNull();
17
+ });
18
+
19
+ it('renders default image when item has no icon', () => {
20
+ const mockItems = [{ Sector: 'Water' }];
21
+ render(<ItemsSection items={mockItems} field="Sector" iconPath="sector" />);
22
+
23
+ const image = screen.getByRole('img');
24
+ expect(image).toHaveAttribute('src', defaultImage);
25
+ expect(screen.getByText('Water')).toBeInTheDocument();
26
+ });
27
+
28
+ it('renders custom image when icon is provided', () => {
29
+ const mockItems = [{ Sector: 'Energy', Icon: 'Solar' }];
30
+ render(<ItemsSection items={mockItems} field="Sector" iconPath="sector" />);
31
+
32
+ const image = screen.getByRole('img');
33
+ expect(image).toHaveAttribute(
34
+ 'src',
35
+ '/en/mission/icons/signatory-reporting/sector/solar/@@images/image',
36
+ );
37
+ expect(screen.getByText('Energy')).toBeInTheDocument();
38
+ });
39
+
40
+ it('applies "column" class when more than 3 items exist', () => {
41
+ const mockItems = [
42
+ { Sector: 'Water' },
43
+ { Sector: 'Energy' },
44
+ { Sector: 'Agriculture' },
45
+ { Sector: 'Transport' },
46
+ ];
47
+
48
+ const { container } = render(
49
+ <ItemsSection items={mockItems} field="Sector" iconPath="sector" />,
50
+ );
51
+
52
+ const group = container.querySelector('.items-group');
53
+ expect(group).toHaveClass('column');
54
+ });
55
+
56
+ it('does not apply "column" class when 3 or fewer items', () => {
57
+ const mockItems = [
58
+ { Sector: 'Water' },
59
+ { Sector: 'Energy' },
60
+ { Sector: 'Agriculture' },
61
+ ];
62
+
63
+ const { container } = render(
64
+ <ItemsSection items={mockItems} field="Sector" iconPath="sector" />,
65
+ );
66
+
67
+ const group = container.querySelector('.items-group');
68
+ expect(group).not.toHaveClass('column');
69
+ });
70
+ });
@@ -1,4 +1,3 @@
1
- import { useLocation } from 'react-router-dom';
2
1
  import { Tab, Grid } from 'semantic-ui-react';
3
2
  import { Callout } from '@eeacms/volto-eea-design-system/ui';
4
3
  import { HTMLField } from '@eeacms/volto-cca-policy/helpers';
@@ -8,15 +7,10 @@ import NoDataReported from '../NoDataReported';
8
7
  import ItemsSection from '../ItemsSection';
9
8
 
10
9
  const ActionTabContent = ({ action }) => {
11
- const location = useLocation();
12
10
  const hasHazards = action?.Climate_Hazards?.length > 0;
13
11
  const hasSectors = action?.Sectors?.length > 0;
14
12
  const hasBenefits = action?.Co_Benefits?.length > 0;
15
13
 
16
- const isSandbox = location.pathname.includes(
17
- '/mission/sandbox/eea-sandbox/signatory-reporting',
18
- );
19
-
20
14
  return (
21
15
  <>
22
16
  <Grid columns="12">
@@ -34,7 +28,8 @@ const ActionTabContent = ({ action }) => {
34
28
  {hasSectors && (
35
29
  <>
36
30
  <h5 className="small-label">{action.Sectors_Label}</h5>
37
- {isSandbox ? (
31
+
32
+ {action.Sectors.some((s) => s.Icon) ? (
38
33
  <ItemsSection
39
34
  items={action.Sectors}
40
35
  field="Sector"
@@ -110,18 +110,52 @@ const AssessmentTab = ({ result, general_text }) => {
110
110
 
111
111
  <br />
112
112
 
113
- {assessment_hazards_sectors.length > 0 && (
113
+ {assessment_hazards_sectors?.length > 0 && (
114
114
  <AccordionList
115
- accordions={assessment_hazards_sectors.map((category) => ({
116
- title: category.Hazard,
117
- content: (
118
- <ul>
119
- {category.Sectors.map((sector, idx) => (
120
- <li key={idx}>{sector}</li>
121
- ))}
122
- </ul>
123
- ),
124
- }))}
115
+ accordions={assessment_hazards_sectors.flatMap((item) => {
116
+ // Beta data (Category > Hazards > Sectors)
117
+ if (item.Category && Array.isArray(item.Hazards)) {
118
+ return [
119
+ {
120
+ title: item.Category,
121
+ content: (
122
+ <ul>
123
+ {item.Hazards.map((hazard, index) => (
124
+ <li key={index}>
125
+ <strong>{hazard.Hazard}</strong>
126
+ {Array.isArray(hazard.Sectors) &&
127
+ hazard.Sectors.length > 0 && (
128
+ <ul>
129
+ {hazard.Sectors.map((sector, sIndex) => (
130
+ <li key={sIndex}>{sector}</li>
131
+ ))}
132
+ </ul>
133
+ )}
134
+ </li>
135
+ ))}
136
+ </ul>
137
+ ),
138
+ },
139
+ ];
140
+ }
141
+
142
+ // Production data (Category=null but has Hazards array)
143
+ if (item.Category === null && Array.isArray(item.Hazards)) {
144
+ return item.Hazards.map((hazard) => ({
145
+ title: hazard.Hazard,
146
+ content: (
147
+ <ul>
148
+ {Array.isArray(hazard.Sectors) &&
149
+ hazard.Sectors.map((sector, index) => (
150
+ <li key={index}>{sector}</li>
151
+ ))}
152
+ </ul>
153
+ ),
154
+ }));
155
+ }
156
+
157
+ return [];
158
+ })}
125
159
  />
126
160
  )}
127
161
  </Tab.Pane>
@@ -6,6 +6,14 @@ jest.mock('@eeacms/volto-eea-design-system/ui', () => ({
6
6
  Callout: ({ children }) => <div>{children}</div>,
7
7
  }));
8
8
 
9
+ jest.mock('@eeacms/volto-cca-policy/helpers', () => ({
10
+ HTMLField: ({ value }) => <div>{value.data}</div>,
11
+ }));
12
+
13
+ jest.mock('../NoDataReported', () => ({ label }) => (
14
+ <div data-testid="no-data">{label}</div>
15
+ ));
16
+
9
17
  jest.mock('@eeacms/volto-cca-policy/utils', () => ({
10
18
  isEmpty: (arr) => !arr || arr.length === 0,
11
19
  formatTextToHTML: (text) => text,
@@ -38,13 +46,28 @@ const mockData = {
38
46
  Please_Explain: 'Explanation content here.',
39
47
  },
40
48
  ],
49
+ assessment_hazards_sectors: [
50
+ {
51
+ Category: 'Water Management',
52
+ Hazards: [
53
+ { Hazard: 'Flooding', Sectors: ['Agriculture', 'Health'] },
54
+ { Hazard: 'Drought', Sectors: ['Energy'] },
55
+ ],
56
+ },
57
+ {
58
+ Category: null,
59
+ Hazards: [
60
+ { Hazard: 'Storms', Sectors: ['Transport', 'Tourism'] },
61
+ { Hazard: 'Heatwaves', Sectors: ['Health'] },
62
+ ],
63
+ },
64
+ ],
41
65
  };
42
66
 
43
67
  describe('AssessmentTab', () => {
44
- it('renders core sections and nested tabs', () => {
68
+ it('renders all major sections correctly', () => {
45
69
  render(<AssessmentTab result={mockData} />);
46
70
 
47
- // General headings
48
71
  expect(screen.getByText('Assessment Title')).toBeInTheDocument();
49
72
  expect(screen.getByText('Assessment Subheading')).toBeInTheDocument();
50
73
  expect(
@@ -56,8 +79,54 @@ describe('AssessmentTab', () => {
56
79
  expect(screen.getByText('Hazards')).toBeInTheDocument();
57
80
  expect(screen.getByText('Hazards abstract text.')).toBeInTheDocument();
58
81
 
59
- // Items section
60
82
  expect(screen.getByText('Factor A')).toBeInTheDocument();
61
83
  expect(screen.getByText('Factor B')).toBeInTheDocument();
62
84
  });
85
+
86
+ it('renders assessment risks accordion with link and details', () => {
87
+ render(<AssessmentTab result={mockData} />);
88
+
89
+ expect(
90
+ screen.getByText('1. Risk Attachment Title - 2023'),
91
+ ).toBeInTheDocument();
92
+
93
+ expect(screen.getByText('Explore this risk')).toBeInTheDocument();
94
+ expect(screen.getByText('Details')).toBeInTheDocument();
95
+ expect(screen.getByText('Explanation content here.')).toBeInTheDocument();
96
+
97
+ const link = screen.getByRole('link', { name: 'Explore this risk' });
98
+ expect(link).toHaveAttribute('href', 'https://example.com');
99
+ });
100
+
101
+ it('renders beta-style hazards (Category > Hazard > Sector)', () => {
102
+ render(<AssessmentTab result={mockData} />);
103
+
104
+ expect(screen.getByText('Water Management')).toBeInTheDocument();
105
+ expect(screen.getByText('Flooding')).toBeInTheDocument();
106
+ expect(screen.getByText('Drought')).toBeInTheDocument();
107
+
108
+ expect(screen.getByText('Agriculture')).toBeInTheDocument();
109
+ expect(screen.getAllByText('Health').length).toBeGreaterThanOrEqual(1);
110
+ expect(screen.getByText('Energy')).toBeInTheDocument();
111
+ });
112
+
113
+ it('renders production-style hazards (Hazard > Sectors)', () => {
114
+ render(<AssessmentTab result={mockData} />);
115
+
116
+ expect(screen.getByText('Storms')).toBeInTheDocument();
117
+ expect(screen.getByText('Heatwaves')).toBeInTheDocument();
118
+ expect(screen.getByText('Transport')).toBeInTheDocument();
119
+ expect(screen.getByText('Tourism')).toBeInTheDocument();
120
+ expect(screen.getAllByText('Health').length).toBeGreaterThanOrEqual(1);
121
+ });
122
+
123
+ it('renders NoDataReported when no data available', () => {
124
+ render(
125
+ <AssessmentTab
126
+ result={{}}
127
+ general_text={{ No_Data_Reported_Label: 'No info' }}
128
+ />,
129
+ );
130
+ expect(screen.getByTestId('no-data')).toHaveTextContent('No info');
131
+ });
63
132
  });
@@ -159,27 +159,27 @@ const PlanningTab = ({ result, general_text }) => {
159
159
  </p>
160
160
  )}
161
161
 
162
- {action?.Name_Of_Plan_And_Hyperlink && (
163
- <p>
164
- {(() => {
165
- const { name, url } = extractPlanNameAndURL(
166
- action.Name_Of_Plan_And_Hyperlink,
167
- );
168
- return url ? (
169
- <a href={url} title={name} target="_blank" rel="noreferrer">
170
- <strong>
171
- {action.Further_Information_Link_Text}
172
- {name && ` [${name}]`}
173
- </strong>
162
+ {(() => {
163
+ const { url } = extractPlanNameAndURL(
164
+ action?.Name_Of_Plan_And_Hyperlink,
165
+ );
166
+
167
+ if (url && action?.Further_Information_Link_Text) {
168
+ return (
169
+ <p>
170
+ <a href={url} target="_blank" rel="noreferrer">
171
+ <strong>{action.Further_Information_Link_Text}</strong>
174
172
  </a>
175
- ) : null;
176
- })()}
177
- </p>
178
- )}
173
+ </p>
174
+ );
175
+ }
176
+
177
+ return null;
178
+ })()}
179
179
 
180
180
  {action?.Attachment && (
181
181
  <p>
182
- <a href={action.Attachment}>
182
+ <a href={action.Attachment} target="_blank" rel="noreferrer">
183
183
  <strong>{action.Explore_Plan_Link_Text}</strong>
184
184
  </a>
185
185
  </p>
@@ -105,7 +105,7 @@ describe('PlanningTab', () => {
105
105
  it('renders hyperlink with extracted name and URL', () => {
106
106
  const { getByText } = render(<PlanningTab result={mockResult} />);
107
107
 
108
- const link = getByText(/More Info \[Plan Example\]/);
108
+ const link = getByText('More Info');
109
109
  expect(link).toBeInTheDocument();
110
110
  expect(link.closest('a')).toHaveAttribute('href', 'https://plan-link.com');
111
111
  });
package/src/utils.js CHANGED
@@ -113,24 +113,29 @@ const trimTrailingChars = (str) => {
113
113
  export const extractPlanNameAndURL = (text) => {
114
114
  if (!text) return { name: '', url: '' };
115
115
 
116
- // Match URL inside parentheses
117
- const parenthesisMatch = text.match(/\((https?:\/\/[^\s)]+)\)/);
118
- // Match first direct URL not inside parentheses
119
- const directMatch = text.match(/https?:\/\/[^\s,;)]+/);
120
- const url = parenthesisMatch?.[1] || directMatch?.[0] || '';
116
+ // Match all URLs
117
+ const urls = text.match(/https?:\/\/[^\s,;)]+/g) || [];
121
118
 
122
119
  let name = text;
120
+ let url = '';
123
121
 
124
- if (url) {
125
- // Remove URL and any punctuation before it
126
- name = name.replace(`(${url})`, '').replace(url, '');
127
- name = trimTrailingChars(name).trim();
122
+ if (urls.length > 1) {
123
+ // Edge case like Rotterdam: use second URL as the actual link
124
+ name = text.split(urls[0])[0] || urls[0]; // name is before or equals first URL
125
+ url = urls[1];
126
+ } else {
127
+ const parenthesisMatch = text.match(/\((https?:\/\/[^\s)]+)\)/);
128
+ const directMatch = text.match(/https?:\/\/[^\s,;)]+/);
129
+ url = parenthesisMatch?.[1] || directMatch?.[0] || '';
130
+
131
+ if (url) {
132
+ name = text.replace(`(${url})`, '').replace(url, '');
133
+ }
128
134
  }
129
135
 
130
- return {
131
- name: name,
132
- url,
133
- };
136
+ name = trimTrailingChars(name).trim();
137
+
138
+ return { name, url };
134
139
  };
135
140
 
136
141
  export const isEmpty = (arr) => !Array.isArray(arr) || arr.length === 0;
@@ -112,6 +112,11 @@ body.subsite-mkh {
112
112
  margin: 2em 0;
113
113
  }
114
114
 
115
+ ul > li > ul {
116
+ margin-top: 0.3em;
117
+ margin-bottom: 1em;
118
+ }
119
+
115
120
  .sectors-introduction {
116
121
  margin-top: 3em;
117
122
  }
@@ -178,6 +183,12 @@ body.subsite-mkh {
178
183
  margin-bottom: 1em;
179
184
  }
180
185
 
186
+ .ui.accordion {
187
+ .content.active {
188
+ padding: 1rem 1rem !important;
189
+ }
190
+ }
191
+
181
192
  .tab-section-wrapper {
182
193
  padding: 1.5em;
183
194
  margin: 2em 0;