@eeacms/volto-cca-policy 0.3.83 → 0.3.85

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.83](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.1...0.3.83) - 10 September 2025
7
+ ### [0.3.85](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.1...0.3.85) - 14 October 2025
8
8
 
9
9
  #### :rocket: Dependency updates
10
10
 
@@ -14,17 +14,36 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
14
14
 
15
15
  #### :bug: Bug Fixes
16
16
 
17
+ - fix(mission): fix assessment risk numbering to match sorted order [kreafox - [`9ecea88`](https://github.com/eea/volto-cca-policy/commit/9ecea885394f1ae6ad63ffb03d8a10cd706caffb)]
18
+ - fix: duplicated code - make ItemsSection reusable [kreafox - [`52e2625`](https://github.com/eea/volto-cca-policy/commit/52e2625914f699c194f75d561f97959ab6c21fbf)]
17
19
  - fix: add missing props for title block [kreafox - [`1d8efa0`](https://github.com/eea/volto-cca-policy/commit/1d8efa0136960261196e7585f89f4ff7db75ae17)]
18
20
  - fix: trailing space issue in HtmlSlateWidget - refs #290137 [kreafox - [`34c1ff6`](https://github.com/eea/volto-cca-policy/commit/34c1ff6896a0fe3fae40527f31ea34220e945163)]
19
21
 
22
+ #### :nail_care: Enhancements
23
+
24
+ - change(mission): improve AccordionList [kreafox - [`cf1497f`](https://github.com/eea/volto-cca-policy/commit/cf1497ff50ff45cdb0bb8e995683969bb3e3c065)]
25
+ - refactor(mission): remove unnecessary line breaks and simplify message rendering - refs #291190 [kreafox - [`430a123`](https://github.com/eea/volto-cca-policy/commit/430a12371b6e1c6a77ea65b6a434eda883568c64)]
26
+ - refactor(mission): use ItemsSection in PlanningTab - refs #291190 [kreafox - [`9e96aaa`](https://github.com/eea/volto-cca-policy/commit/9e96aaac0686f6688b3406aca578b598882a9c29)]
27
+ - change: update sectors with icons for Planning tab - refs #291190 [kreafox - [`8b82168`](https://github.com/eea/volto-cca-policy/commit/8b821686fc6b9d98c542d9f0315734b446070a17)]
28
+ - change: update factors with icons for Assessment tab - refs #291189 [kreafox - [`d8b7afc`](https://github.com/eea/volto-cca-policy/commit/d8b7afc36b2129b60ef81d9a057c4c9be35cbb03)]
29
+ - change: update sectors with icons for Action tab - refs #291188 [kreafox - [`a9f15c7`](https://github.com/eea/volto-cca-policy/commit/a9f15c70fa261c9da5b0f01d0bf07ede0136085f)]
30
+ - change(mission): use result_beta in sandbox for signatory reporting - refs #292508 [kreafox - [`b425801`](https://github.com/eea/volto-cca-policy/commit/b42580100bdd2b4d79be53bbfa05098856deb4be)]
31
+ - change: update signatory profile action tab with icons [kreafox - [`0e72902`](https://github.com/eea/volto-cca-policy/commit/0e7290259b966ff72d2a4d77efca8fbeec8655f8)]
32
+
20
33
  #### :house: Internal changes
21
34
 
35
+ - style(mission): mobile fixes on signatory reporting pages [kreafox - [`cbeb584`](https://github.com/eea/volto-cca-policy/commit/cbeb5846c8047c164c29635bd30491b4f810246a)]
36
+ - chore: better component naming [kreafox - [`d61e462`](https://github.com/eea/volto-cca-policy/commit/d61e4629da184ec14d6ad0d056ac29751223e2f9)]
22
37
  - style: Automated code fix [eea-jenkins - [`f8934ba`](https://github.com/eea/volto-cca-policy/commit/f8934baf29317bb81cb5a816bfe66ee1724e040f)]
23
38
  - style: Automated code fix [eea-jenkins - [`0e75a69`](https://github.com/eea/volto-cca-policy/commit/0e75a6978a2b43e8ad4545ea5fb41191354fd9d0)]
24
39
  - style: fix item spacing [kreafox - [`7d4535d`](https://github.com/eea/volto-cca-policy/commit/7d4535dbc486e6cb80fc55d6ac8c7c01d9cf48af)]
25
40
 
26
41
  #### :hammer_and_wrench: Others
27
42
 
43
+ - test(mission): add unit tests for NoDataReported and StatisticSection components [kreafox - [`36c6e36`](https://github.com/eea/volto-cca-policy/commit/36c6e36278124759240c896786f2ac0c20674730)]
44
+ - test: resolve Jest errors in component tests [kreafox - [`6dbb735`](https://github.com/eea/volto-cca-policy/commit/6dbb73540e8301fcb42a4d52db7004f5a42db4d8)]
45
+ - test: fix PlanningTab mock data to match component structure [kreafox - [`48e76e1`](https://github.com/eea/volto-cca-policy/commit/48e76e1e50e0c6d8a6a500fec3d1831419faeee9)]
46
+ - test: resolve Jest errors in component tests [kreafox - [`f309b96`](https://github.com/eea/volto-cca-policy/commit/f309b96b0767358530d969e21102887a9db76b5a)]
28
47
  - Add customization for server.jsx [Tiberiu Ichim - [`c625d39`](https://github.com/eea/volto-cca-policy/commit/c625d397493ca9c1bcbd4083dd0b93969d89ef97)]
29
48
  - Remove customizations for server, to benefit from CSP headers implemented in eea-website-theme [Tiberiu Ichim - [`0c9ce15`](https://github.com/eea/volto-cca-policy/commit/0c9ce154c68a589ad6a0c553b36ea6e4f155ed09)]
30
49
  - Refs #290787 - sum of countries [Tripon Eugen - [`f1033cf`](https://github.com/eea/volto-cca-policy/commit/f1033cf0beca2d7cace3d1ee2bc93d03cbf7b3c8)]
@@ -110,8 +129,39 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
110
129
  - Add some loadable for components [Tiberiu Ichim - [`1793962`](https://github.com/eea/volto-cca-policy/commit/179396211c66a6a2465b2d1b6c0f2afc40fc7189)]
111
130
  - Refs #284961 - test [Tripon Eugen - [`c989f1f`](https://github.com/eea/volto-cca-policy/commit/c989f1f8638c0c5233c5c49f8673c9a2cdc7937e)]
112
131
  - Refs #284961 - add translations [Tripon Eugen - [`04ee988`](https://github.com/eea/volto-cca-policy/commit/04ee988c086d393b9b37ce1ea8d24f5e84f266aa)]
113
- ### [1.0.0-alpha.0](https://github.com/eea/volto-cca-policy/compare/0.3.82...1.0.0-alpha.0) - 15 July 2025
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
133
+
134
+ ### [0.3.84](https://github.com/eea/volto-cca-policy/compare/0.3.83...0.3.84) - 7 October 2025
135
+
136
+ #### :bug: Bug Fixes
137
+
138
+ - fix: duplicated code - make ItemsSection reusable [kreafox - [`52e2625`](https://github.com/eea/volto-cca-policy/commit/52e2625914f699c194f75d561f97959ab6c21fbf)]
139
+
140
+ #### :nail_care: Enhancements
141
+
142
+ - refactor(mission): use ItemsSection in PlanningTab - refs #291190 [kreafox - [`9e96aaa`](https://github.com/eea/volto-cca-policy/commit/9e96aaac0686f6688b3406aca578b598882a9c29)]
143
+ - change: update sectors with icons for Planning tab - refs #291190 [kreafox - [`8b82168`](https://github.com/eea/volto-cca-policy/commit/8b821686fc6b9d98c542d9f0315734b446070a17)]
144
+ - change: update factors with icons for Assessment tab - refs #291189 [kreafox - [`d8b7afc`](https://github.com/eea/volto-cca-policy/commit/d8b7afc36b2129b60ef81d9a057c4c9be35cbb03)]
145
+ - change: update sectors with icons for Action tab - refs #291188 [kreafox - [`a9f15c7`](https://github.com/eea/volto-cca-policy/commit/a9f15c70fa261c9da5b0f01d0bf07ede0136085f)]
146
+ - change(mission): use result_beta in sandbox for signatory reporting - refs #292508 [kreafox - [`b425801`](https://github.com/eea/volto-cca-policy/commit/b42580100bdd2b4d79be53bbfa05098856deb4be)]
147
+ - change: update signatory profile action tab with icons [kreafox - [`0e72902`](https://github.com/eea/volto-cca-policy/commit/0e7290259b966ff72d2a4d77efca8fbeec8655f8)]
114
148
 
149
+ #### :house: Internal changes
150
+
151
+ - style(mission): mobile fixes on signatory reporting pages [kreafox - [`cbeb584`](https://github.com/eea/volto-cca-policy/commit/cbeb5846c8047c164c29635bd30491b4f810246a)]
152
+ - chore: better component naming [kreafox - [`d61e462`](https://github.com/eea/volto-cca-policy/commit/d61e4629da184ec14d6ad0d056ac29751223e2f9)]
153
+
154
+ #### :hammer_and_wrench: Others
155
+
156
+ - test: resolve Jest errors in component tests [kreafox - [`6dbb735`](https://github.com/eea/volto-cca-policy/commit/6dbb73540e8301fcb42a4d52db7004f5a42db4d8)]
157
+ - test: fix PlanningTab mock data to match component structure [kreafox - [`48e76e1`](https://github.com/eea/volto-cca-policy/commit/48e76e1e50e0c6d8a6a500fec3d1831419faeee9)]
158
+ - test: resolve Jest errors in component tests [kreafox - [`f309b96`](https://github.com/eea/volto-cca-policy/commit/f309b96b0767358530d969e21102887a9db76b5a)]
159
+ ### [0.3.83](https://github.com/eea/volto-cca-policy/compare/0.3.82...0.3.83) - 10 September 2025
160
+
161
+ #### :hammer_and_wrench: Others
162
+
163
+ - Add customization for server.jsx [Tiberiu Ichim - [`c625d39`](https://github.com/eea/volto-cca-policy/commit/c625d397493ca9c1bcbd4083dd0b93969d89ef97)]
164
+ - Remove customizations for server, to benefit from CSP headers implemented in eea-website-theme [Tiberiu Ichim - [`0c9ce15`](https://github.com/eea/volto-cca-policy/commit/0c9ce154c68a589ad6a0c553b36ea6e4f155ed09)]
115
165
  ### [0.3.82](https://github.com/eea/volto-cca-policy/compare/0.3.81...0.3.82) - 5 September 2025
116
166
 
117
167
  #### :rocket: Dependency updates
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-cca-policy",
3
- "version": "0.3.83",
3
+ "version": "0.3.85",
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",
@@ -6,34 +6,44 @@ import {
6
6
  AccordionContent,
7
7
  } from 'semantic-ui-react';
8
8
 
9
- const AccordionList = ({ accordions, variation }) => {
10
- const [activeIndex, setActiveIndex] = useState(-1);
9
+ const AccordionList = ({ accordions, variation, multiple = false }) => {
10
+ const [activeIndexes, setActiveIndexes] = useState([]);
11
+
11
12
  const handleAccordionClick = (index) => {
12
- setActiveIndex(activeIndex === index ? -1 : index);
13
+ if (multiple) {
14
+ setActiveIndexes((prev) =>
15
+ prev.includes(index)
16
+ ? prev.filter((i) => i !== index)
17
+ : [...prev, index],
18
+ );
19
+ } else {
20
+ setActiveIndexes((prev) => (prev.includes(index) ? [] : [index]));
21
+ }
13
22
  };
14
23
 
15
24
  return (
16
25
  <Accordion className={variation}>
17
- {accordions.map((accordion, index) => (
18
- <React.Fragment key={index}>
19
- <AccordionTitle
20
- active={activeIndex === index}
21
- onClick={() => handleAccordionClick(index)}
22
- >
23
- <Icon
24
- className={
25
- activeIndex === index
26
- ? 'ri-arrow-up-s-line'
27
- : 'ri-arrow-down-s-line'
28
- }
29
- />
30
- {accordion.title}
31
- </AccordionTitle>
32
- <AccordionContent active={activeIndex === index}>
33
- {accordion.content}
34
- </AccordionContent>
35
- </React.Fragment>
36
- ))}
26
+ {accordions.map((accordion, index) => {
27
+ const isActive = activeIndexes.includes(index);
28
+ return (
29
+ <React.Fragment key={index}>
30
+ <AccordionTitle
31
+ active={isActive}
32
+ onClick={() => handleAccordionClick(index)}
33
+ >
34
+ <Icon
35
+ className={
36
+ isActive ? 'ri-arrow-up-s-line' : 'ri-arrow-down-s-line'
37
+ }
38
+ />
39
+ {accordion.title}
40
+ </AccordionTitle>
41
+ <AccordionContent active={isActive}>
42
+ {accordion.content}
43
+ </AccordionContent>
44
+ </React.Fragment>
45
+ );
46
+ })}
37
47
  </Accordion>
38
48
  );
39
49
  };
@@ -0,0 +1,35 @@
1
+ import cx from 'classnames';
2
+ import { Item, Image } from 'semantic-ui-react';
3
+ import { normalizeImageFileName } from '@eeacms/volto-cca-policy/utils';
4
+ import defaultImage from '@eeacms/volto-cca-policy/../theme/assets/images/image-narrow.svg';
5
+
6
+ const ItemsSection = ({ items, field, iconPath }) => {
7
+ if (!items?.length) return null;
8
+
9
+ return (
10
+ <Item.Group
11
+ unstackable
12
+ className={cx('items-group', { column: items.length > 3 })}
13
+ >
14
+ {items.map((item, index) => {
15
+ const normalizedIcon = normalizeImageFileName(item.Icon);
16
+ return (
17
+ <Item key={index}>
18
+ {item.Icon ? (
19
+ <Image
20
+ src={`/en/mission/icons/signatory-reporting/${iconPath}/${normalizedIcon}/@@images/image`}
21
+ />
22
+ ) : (
23
+ <Image size="small" src={defaultImage} />
24
+ )}
25
+ <Item.Content verticalAlign="middle">
26
+ <p>{item[field]}</p>
27
+ </Item.Content>
28
+ </Item>
29
+ );
30
+ })}
31
+ </Item.Group>
32
+ );
33
+ };
34
+
35
+ export default ItemsSection;
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { Link } from 'react-router-dom';
2
+ import { Link, useLocation } from 'react-router-dom';
3
3
  import { Tab, Container, Divider, Button, Icon } from 'semantic-ui-react';
4
4
  import { formatTextToHTML } from '@eeacms/volto-cca-policy/utils';
5
5
  import { BannerTitle, HTMLField } from '@eeacms/volto-cca-policy/helpers';
@@ -8,18 +8,24 @@ import { flattenToAppURL } from '@plone/volto/helpers';
8
8
  import GovernanceTab from './TabSections/GovernanceTab';
9
9
  import AssessmentTab from './TabSections/AssessmentTab';
10
10
  import PlanningTab from './TabSections/PlanningTab';
11
- import ActionPagesTab from './TabSections/ActionPagesTab';
11
+ import ActionTab from './TabSections/ActionTab';
12
12
 
13
13
  const tabRenderers = {
14
14
  Governance_Label: (props) => <GovernanceTab {...props} />,
15
15
  Assessment_Label: (props) => <AssessmentTab {...props} />,
16
16
  Planning_Label: (props) => <PlanningTab {...props} />,
17
- Action_Label: (props) => <ActionPagesTab {...props} />,
17
+ Action_Label: (props) => <ActionTab {...props} />,
18
18
  };
19
19
 
20
20
  const MissionSignatoryProfileView = ({ content }) => {
21
+ const location = useLocation();
22
+ const isSandbox = location.pathname.includes(
23
+ '/mission/sandbox/eea-sandbox/signatory-reporting',
24
+ );
21
25
  const signatoryData =
22
- content?.['@components']?.missionsignatoryprofile?.result || {};
26
+ content?.['@components']?.missionsignatoryprofile?.[
27
+ isSandbox ? 'result_beta' : 'result'
28
+ ] || {};
23
29
 
24
30
  const {
25
31
  governance = [{}],
@@ -10,7 +10,7 @@ jest.mock('./TabSections/AssessmentTab', () => () => (
10
10
  <div>Mocked Assessment</div>
11
11
  ));
12
12
  jest.mock('./TabSections/PlanningTab', () => () => <div>Mocked Planning</div>);
13
- jest.mock('./TabSections/ActionPagesTab', () => () => <div>Mocked Action</div>);
13
+ jest.mock('./TabSections/ActionTab', () => () => <div>Mocked Action</div>);
14
14
 
15
15
  jest.mock('@eeacms/volto-cca-policy/helpers', () => ({
16
16
  BannerTitle: ({ children }) => <div>{children}</div>,
@@ -0,0 +1,12 @@
1
+ import '@testing-library/jest-dom';
2
+ import { render } from '@testing-library/react';
3
+ import NoDataReported from './NoDataReported';
4
+
5
+ describe('NoDataReported', () => {
6
+ it('renders the provided label inside Tab.Pane', () => {
7
+ const testLabel = 'No data available';
8
+ const { getByText } = render(<NoDataReported label={testLabel} />);
9
+
10
+ expect(getByText(testLabel)).toBeInTheDocument();
11
+ });
12
+ });
@@ -0,0 +1,22 @@
1
+ import '@testing-library/jest-dom';
2
+ import { render } from '@testing-library/react';
3
+ import StatisticSection from './StatisticSection';
4
+
5
+ describe('StatisticSection', () => {
6
+ const mockStatistics = [
7
+ { label: 'Population year', value: 2023 },
8
+ { label: 'Population size', value: 362.133 },
9
+ ];
10
+
11
+ it('renders all statistic values and labels', () => {
12
+ const { getByText } = render(
13
+ <StatisticSection statistic={mockStatistics} />,
14
+ );
15
+
16
+ expect(getByText('2023')).toBeInTheDocument();
17
+ expect(getByText('362.133')).toBeInTheDocument();
18
+
19
+ expect(getByText('Population year')).toBeInTheDocument();
20
+ expect(getByText('Population size')).toBeInTheDocument();
21
+ });
22
+ });
@@ -1,19 +1,26 @@
1
+ import { useLocation } from 'react-router-dom';
1
2
  import { Tab, Grid } from 'semantic-ui-react';
2
3
  import { Callout } from '@eeacms/volto-eea-design-system/ui';
3
4
  import { HTMLField } from '@eeacms/volto-cca-policy/helpers';
4
5
  import { formatTextToHTML, isEmpty } from '@eeacms/volto-cca-policy/utils';
5
6
  import AccordionList from '../AccordionList';
6
7
  import NoDataReported from '../NoDataReported';
8
+ import ItemsSection from '../ItemsSection';
7
9
 
8
- const ActionsTabContent = ({ action }) => {
10
+ const ActionTabContent = ({ action }) => {
11
+ const location = useLocation();
9
12
  const hasHazards = action?.Climate_Hazards?.length > 0;
10
- const hasSectors = action?.Sectors.length > 0;
11
- const hasBenefits = action?.Co_Benefits.length > 0;
13
+ const hasSectors = action?.Sectors?.length > 0;
14
+ const hasBenefits = action?.Co_Benefits?.length > 0;
15
+
16
+ const isSandbox = location.pathname.includes(
17
+ '/mission/sandbox/eea-sandbox/signatory-reporting',
18
+ );
12
19
 
13
20
  return (
14
21
  <>
15
22
  <Grid columns="12">
16
- <Grid.Column mobile={12} tablet={12} computer={6}>
23
+ <Grid.Column mobile={12} tablet={12} computer={hasBenefits ? 6 : 9}>
17
24
  {hasHazards && (
18
25
  <>
19
26
  <h5 className="small-label">{action.Hazards_Addressed_Label}</h5>
@@ -27,17 +34,25 @@ const ActionsTabContent = ({ action }) => {
27
34
  {hasSectors && (
28
35
  <>
29
36
  <h5 className="small-label">{action.Sectors_Label}</h5>
30
- <ul>
31
- {action.Sectors.map((hazard, index) => (
32
- <li key={index}>{hazard}</li>
33
- ))}
34
- </ul>
37
+ {isSandbox ? (
38
+ <ItemsSection
39
+ items={action.Sectors}
40
+ field="Sector"
41
+ iconPath="sector"
42
+ />
43
+ ) : (
44
+ <ul>
45
+ {action.Sectors.map((item, index) => (
46
+ <li key={index}>{item.Sector}</li>
47
+ ))}
48
+ </ul>
49
+ )}
35
50
  </>
36
51
  )}
37
52
  </Grid.Column>
38
53
 
39
- <Grid.Column mobile={12} tablet={12} computer={6}>
40
- {hasBenefits && (
54
+ {hasBenefits && (
55
+ <Grid.Column mobile={12} tablet={12} computer={6}>
41
56
  <>
42
57
  <h5 className="small-label">{action.Co_Benefits_Label}</h5>
43
58
  <ul>
@@ -46,27 +61,24 @@ const ActionsTabContent = ({ action }) => {
46
61
  ))}
47
62
  </ul>
48
63
  </>
49
- )}
50
- </Grid.Column>
64
+ </Grid.Column>
65
+ )}
51
66
  </Grid>
52
67
  {action.Funding_Sources && (
53
- <>
54
- <br />
55
- <div className="funding-sources">
56
- <span>{action.Funding_Sources_Label} </span>
57
- <strong>
58
- <HTMLField
59
- value={{ data: formatTextToHTML(action.Funding_Sources) }}
60
- />
61
- </strong>
62
- </div>
63
- </>
68
+ <div className="funding-sources">
69
+ <span>{action.Funding_Sources_Label} </span>
70
+ <strong>
71
+ <HTMLField
72
+ value={{ data: formatTextToHTML(action.Funding_Sources) }}
73
+ />
74
+ </strong>
75
+ </div>
64
76
  )}
65
77
  </>
66
78
  );
67
79
  };
68
80
 
69
- const ActionPagesTab = ({ result, general_text }) => {
81
+ const ActionTab = ({ result, general_text }) => {
70
82
  const { action_text, actions } = result || {};
71
83
  const { No_Data_Reported_Label } = general_text || {};
72
84
  const { Title, Abstract, Abstract_Line } = action_text?.[0] || {};
@@ -80,7 +92,7 @@ const ActionPagesTab = ({ result, general_text }) => {
80
92
  }
81
93
 
82
94
  return (
83
- <Tab.Pane>
95
+ <Tab.Pane className="action-tab">
84
96
  {Title && <h2>{Title}</h2>}
85
97
  {Abstract && <HTMLField value={{ data: formatTextToHTML(Abstract) }} />}
86
98
  {Abstract_Line && (
@@ -102,7 +114,7 @@ const ActionPagesTab = ({ result, general_text }) => {
102
114
  accordions={[
103
115
  {
104
116
  title: action?.More_Details_Label,
105
- content: <ActionsTabContent action={action} />,
117
+ content: <ActionTabContent action={action} />,
106
118
  },
107
119
  ]}
108
120
  />
@@ -113,4 +125,4 @@ const ActionPagesTab = ({ result, general_text }) => {
113
125
  );
114
126
  };
115
127
 
116
- export default ActionPagesTab;
128
+ export default ActionTab;
@@ -1,6 +1,7 @@
1
1
  import '@testing-library/jest-dom';
2
+ import { MemoryRouter } from 'react-router-dom';
2
3
  import { render, within } from '@testing-library/react';
3
- import ActionPagesTab from './ActionPagesTab';
4
+ import ActionTab from './ActionTab';
4
5
 
5
6
  jest.mock('@eeacms/volto-eea-design-system/ui', () => ({
6
7
  Callout: ({ children }) => <div>{children}</div>,
@@ -65,7 +66,9 @@ describe('ActionPagesTab', () => {
65
66
 
66
67
  it('renders action tab content correctly', () => {
67
68
  const { getByText, container } = render(
68
- <ActionPagesTab result={mockResult} />,
69
+ <MemoryRouter>
70
+ <ActionTab result={mockResult} />
71
+ </MemoryRouter>,
69
72
  );
70
73
 
71
74
  expect(getByText('Adaptation Actions')).toBeInTheDocument();
@@ -76,7 +79,6 @@ describe('ActionPagesTab', () => {
76
79
 
77
80
  const sections = container.querySelectorAll('.section-wrapper');
78
81
 
79
- // First action
80
82
  const firstAction = within(sections[0]);
81
83
  expect(firstAction.getByText('First action')).toBeInTheDocument();
82
84
  expect(firstAction.getByText('Hazards addressed')).toBeInTheDocument();
@@ -84,7 +86,6 @@ describe('ActionPagesTab', () => {
84
86
  expect(firstAction.getByText('Funding source:')).toBeInTheDocument();
85
87
  expect(firstAction.getByText('National Funds')).toBeInTheDocument();
86
88
 
87
- // Second action
88
89
  const secondAction = within(sections[1]);
89
90
  expect(secondAction.getByText('Second action')).toBeInTheDocument();
90
91
  expect(secondAction.getByText('Hazards addressed')).toBeInTheDocument();
@@ -1,26 +1,10 @@
1
- import { Tab, Image, Segment, Item } from 'semantic-ui-react';
1
+ import { Tab, Segment } from 'semantic-ui-react';
2
2
  import { Callout } from '@eeacms/volto-eea-design-system/ui';
3
3
  import { HTMLField } from '@eeacms/volto-cca-policy/helpers';
4
- import { formatTextToHTML, isEmpty } from '@eeacms/volto-cca-policy/utils';
4
+ import { isEmpty, formatTextToHTML } from '@eeacms/volto-cca-policy/utils';
5
5
  import AccordionList from '../AccordionList';
6
6
  import NoDataReported from '../NoDataReported';
7
-
8
- import image from '@eeacms/volto-cca-policy/../theme//assets/images/image-narrow.svg';
9
-
10
- const ItemsSection = ({ items }) => {
11
- if (!items?.length) return null;
12
-
13
- return (
14
- <Item.Group className="items-group">
15
- {items.map((item, index) => (
16
- <Item key={index}>
17
- <Image size="small" src={image} />
18
- <Item.Content verticalAlign="middle">{item.Factor}</Item.Content>
19
- </Item>
20
- ))}
21
- </Item.Group>
22
- );
23
- };
7
+ import ItemsSection from '../ItemsSection';
24
8
 
25
9
  const AssessmentAccordionContent = ({ result }) => {
26
10
  return (
@@ -57,8 +41,9 @@ const AssessmentTab = ({ result, general_text }) => {
57
41
  Hazards_Title,
58
42
  Hazards_Abstract,
59
43
  } = result.assessment_text?.[0] || {};
60
- const assessment_risks = result.assessment_risks || [];
61
- const assessment_hazards_sectors = result.assessment_hazards_sectors || [];
44
+
45
+ const assessment_risks = result?.assessment_risks || [];
46
+ const assessment_hazards_sectors = result?.assessment_hazards_sectors || [];
62
47
  const { No_Data_Reported_Label } = general_text || {};
63
48
 
64
49
  const NoResults =
@@ -72,8 +57,9 @@ const AssessmentTab = ({ result, general_text }) => {
72
57
  }
73
58
 
74
59
  return (
75
- <Tab.Pane>
60
+ <Tab.Pane className="assessment-tab">
76
61
  {Title && <h2>{Title}</h2>}
62
+
77
63
  {Subheading && (
78
64
  <Callout>
79
65
  <HTMLField value={{ data: formatTextToHTML(Subheading) }} />
@@ -85,36 +71,33 @@ const AssessmentTab = ({ result, general_text }) => {
85
71
  <div className="tab-section-wrapper assessment">
86
72
  {Cra_Title && <h3>{Cra_Title}</h3>}
87
73
 
88
- {result.assessment_factors.length > 0 && (
74
+ {result.assessment_factors?.length > 0 && (
89
75
  <>
90
76
  {Cra_Abstract && <h5>{Cra_Abstract}</h5>}
91
- <ItemsSection items={result.assessment_factors} />
77
+ <ItemsSection
78
+ items={result.assessment_factors}
79
+ field="Factor"
80
+ iconPath="factors"
81
+ />
92
82
  </>
93
83
  )}
94
84
 
95
85
  {assessment_risks.length > 0 && (
96
86
  <>
97
87
  {Attachments && <h4>{Attachments}</h4>}
98
- {assessment_risks.map((risk, index) => {
99
- const title = risk?.Attachment_Title
100
- ? `${risk.Assessment_Id}. ${risk.Attachment_Title} - ${
101
- risk.Year_Of_Publication || ''
102
- }`
103
- : null;
104
- return (
105
- <div key={index}>
106
- <AccordionList
107
- variation="tertiary"
108
- accordions={[
109
- {
110
- title: title,
111
- content: <AssessmentAccordionContent result={risk} />,
112
- },
113
- ]}
114
- />
115
- </div>
116
- );
117
- })}
88
+
89
+ <AccordionList
90
+ variation="tertiary"
91
+ multiple={false}
92
+ accordions={assessment_risks.map((risk, index) => ({
93
+ title: risk?.Attachment_Title
94
+ ? `${index + 1}. ${risk.Attachment_Title} - ${
95
+ risk.Year_Of_Publication || ''
96
+ }`
97
+ : `Risk ${index + 1}`,
98
+ content: <AssessmentAccordionContent result={risk} />,
99
+ }))}
100
+ />
118
101
  </>
119
102
  )}
120
103
  </div>
@@ -127,7 +110,7 @@ const AssessmentTab = ({ result, general_text }) => {
127
110
 
128
111
  <br />
129
112
 
130
- {assessment_hazards_sectors && (
113
+ {assessment_hazards_sectors.length > 0 && (
131
114
  <AccordionList
132
115
  accordions={assessment_hazards_sectors.map((category) => ({
133
116
  title: category.Hazard,
@@ -9,6 +9,7 @@ jest.mock('@eeacms/volto-eea-design-system/ui', () => ({
9
9
  jest.mock('@eeacms/volto-cca-policy/utils', () => ({
10
10
  isEmpty: (arr) => !arr || arr.length === 0,
11
11
  formatTextToHTML: (text) => text,
12
+ normalizeImageFileName: (filename) => filename || '',
12
13
  }));
13
14
 
14
15
  const mockData = {
@@ -52,7 +52,7 @@ const GovernanceTab = ({ result, general_text }) => {
52
52
  }
53
53
 
54
54
  return (
55
- <Tab.Pane>
55
+ <Tab.Pane className="governance-tab">
56
56
  {Title && <h2>{Title}</h2>}
57
57
 
58
58
  {Introduction && <Callout>{Introduction}</Callout>}
@@ -63,19 +63,19 @@ const GovernanceTab = ({ result, general_text }) => {
63
63
 
64
64
  {Describe && <HTMLField value={{ data: formatTextToHTML(Describe) }} />}
65
65
 
66
- <br />
67
-
68
66
  {Provide && (
69
- <AccordionList
70
- accordions={[
71
- {
72
- title: Provide_Title,
73
- content: (
74
- <HTMLField value={{ data: formatTextToHTML(Provide) }} />
75
- ),
76
- },
77
- ]}
78
- />
67
+ <div className="provide-section">
68
+ <AccordionList
69
+ accordions={[
70
+ {
71
+ title: Provide_Title,
72
+ content: (
73
+ <HTMLField value={{ data: formatTextToHTML(Provide) }} />
74
+ ),
75
+ },
76
+ ]}
77
+ />
78
+ </div>
79
79
  )}
80
80
  </Tab.Pane>
81
81
  );
@@ -1,30 +1,15 @@
1
1
  import React from 'react';
2
- import { Tab, Message, Segment, Grid, Item, Image } from 'semantic-ui-react';
2
+ import { Tab, Message, Segment, Grid } from 'semantic-ui-react';
3
3
  import { Callout } from '@eeacms/volto-eea-design-system/ui';
4
4
  import { HTMLField } from '@eeacms/volto-cca-policy/helpers';
5
5
  import {
6
+ isEmpty,
6
7
  formatTextToHTML,
7
8
  extractPlanNameAndURL,
8
- isEmpty,
9
9
  } from '@eeacms/volto-cca-policy/utils';
10
10
  import AccordionList from '../AccordionList';
11
11
  import NoDataReported from '../NoDataReported';
12
- import image from '@eeacms/volto-cca-policy/../theme/assets/images/image-narrow.svg';
13
-
14
- const ItemsSection = ({ items }) => {
15
- if (!items?.length) return null;
16
-
17
- return (
18
- <Item.Group className="items-group">
19
- {items.map((sector, index) => (
20
- <Item key={index}>
21
- <Image size="small" src={image} />
22
- <Item.Content verticalAlign="middle">{sector}</Item.Content>
23
- </Item>
24
- ))}
25
- </Item.Group>
26
- );
27
- };
12
+ import ItemsSection from '../ItemsSection';
28
13
 
29
14
  const PlanningGoalContent = ({ goal }) => {
30
15
  const hasHazards = goal?.Climate_Hazards?.length > 0;
@@ -98,8 +83,9 @@ const PlanningTab = ({ result, general_text }) => {
98
83
  }
99
84
 
100
85
  return (
101
- <Tab.Pane>
86
+ <Tab.Pane className="planning-tab">
102
87
  {titleData?.Title && <h2>{titleData.Title}</h2>}
88
+
103
89
  {titleData?.Abstract_Line && (
104
90
  <Callout>
105
91
  <HTMLField
@@ -108,27 +94,24 @@ const PlanningTab = ({ result, general_text }) => {
108
94
  </Callout>
109
95
  )}
110
96
 
111
- {sortedGoals.map((goal, index) => {
112
- return (
113
- <div key={index} className="section-wrapper">
114
- <h5 className="section-title">
115
- <span className="section-number">{goal?.Title_Label} </span>
116
-
117
- <HTMLField value={{ data: formatTextToHTML(goal?.Title) }} />
118
- </h5>
119
-
120
- <AccordionList
121
- variation="secondary"
122
- accordions={[
123
- {
124
- title: goal?.More_Details_Label || 'More details',
125
- content: <PlanningGoalContent goal={goal} />,
126
- },
127
- ]}
128
- />
129
- </div>
130
- );
131
- })}
97
+ {sortedGoals.map((goal, index) => (
98
+ <div key={index} className="section-wrapper">
99
+ <h5 className="section-title">
100
+ <span className="section-number">{goal?.Title_Label} </span>
101
+ <HTMLField value={{ data: formatTextToHTML(goal?.Title) }} />
102
+ </h5>
103
+
104
+ <AccordionList
105
+ variation="secondary"
106
+ accordions={[
107
+ {
108
+ title: goal?.More_Details_Label || 'More details',
109
+ content: <PlanningGoalContent goal={goal} />,
110
+ },
111
+ ]}
112
+ />
113
+ </div>
114
+ ))}
132
115
 
133
116
  {goalData?.Climate_Action_Title && (
134
117
  <h2>{goalData.Climate_Action_Title}</h2>
@@ -142,71 +125,67 @@ const PlanningTab = ({ result, general_text }) => {
142
125
  </Callout>
143
126
  )}
144
127
 
145
- {planning_climate_action.map((action, index) => {
146
- return (
147
- <React.Fragment key={index}>
148
- <br />
149
- {action?.Sectors_Introduction && (
150
- <Message>
151
- <HTMLField
152
- value={{
153
- data: formatTextToHTML(action.Sectors_Introduction),
154
- }}
155
- />
156
- </Message>
157
- )}
158
-
159
- <ItemsSection items={action?.Sectors} />
160
-
161
- {action?.Description && (
128
+ {planning_climate_action.map((action, index) => (
129
+ <React.Fragment key={index}>
130
+ {action?.Sectors_Introduction && (
131
+ <Message className="sectors-introduction">
162
132
  <HTMLField
163
- className="description"
164
- value={{ data: formatTextToHTML(action.Description) }}
133
+ value={{
134
+ data: formatTextToHTML(action.Sectors_Introduction),
135
+ }}
165
136
  />
166
- )}
167
-
168
- {(action?.Approval_Year || action?.End_Year) && (
169
- <p>
170
- {action?.Year_Of_Approval_Label}{' '}
171
- <strong className="date">{action.Approval_Year}</strong>{' '}
172
- {action?.End_Year_Of_Plan_Label}{' '}
173
- <strong className="date">{action.End_Year}</strong>
174
- </p>
175
- )}
176
-
177
- {action?.Name_Of_Plan_And_Hyperlink && (
178
- <p>
179
- {(() => {
180
- const { name, url } = extractPlanNameAndURL(
181
- action.Name_Of_Plan_And_Hyperlink,
182
- );
183
-
184
- return url ? (
185
- <a href={url} title={name} target="_blank" rel="noreferrer">
186
- <strong>
187
- {action.Further_Information_Link_Text}
188
- {name && ` [${name}]`}
189
- </strong>
190
- </a>
191
- ) : (
137
+ </Message>
138
+ )}
139
+
140
+ <ItemsSection
141
+ items={action?.Sectors}
142
+ field="Sector"
143
+ iconPath="sector"
144
+ />
145
+
146
+ {action?.Description && (
147
+ <HTMLField
148
+ className="description"
149
+ value={{ data: formatTextToHTML(action.Description) }}
150
+ />
151
+ )}
152
+
153
+ {(action?.Approval_Year || action?.End_Year) && (
154
+ <p>
155
+ {action?.Year_Of_Approval_Label}{' '}
156
+ <strong className="date">{action.Approval_Year}</strong>{' '}
157
+ {action?.End_Year_Of_Plan_Label}{' '}
158
+ <strong className="date">{action.End_Year}</strong>
159
+ </p>
160
+ )}
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">
192
170
  <strong>
193
171
  {action.Further_Information_Link_Text}
194
172
  {name && ` [${name}]`}
195
173
  </strong>
196
- );
197
- })()}
198
- </p>
199
- )}
200
- {action?.Attachment && (
201
- <p>
202
- <a href={action.Attachment}>
203
- <strong>{action.Explore_Plan_Link_Text}</strong>
204
- </a>
205
- </p>
206
- )}
207
- </React.Fragment>
208
- );
209
- })}
174
+ </a>
175
+ ) : null;
176
+ })()}
177
+ </p>
178
+ )}
179
+
180
+ {action?.Attachment && (
181
+ <p>
182
+ <a href={action.Attachment}>
183
+ <strong>{action.Explore_Plan_Link_Text}</strong>
184
+ </a>
185
+ </p>
186
+ )}
187
+ </React.Fragment>
188
+ ))}
210
189
  </Tab.Pane>
211
190
  );
212
191
  };
@@ -15,6 +15,7 @@ jest.mock('@eeacms/volto-cca-policy/utils', () => ({
15
15
  name: 'Plan Example',
16
16
  url: 'https://plan-link.com',
17
17
  }),
18
+ normalizeImageFileName: (filename) => filename || '',
18
19
  }));
19
20
 
20
21
  describe('PlanningTab', () => {
@@ -65,7 +66,7 @@ describe('PlanningTab', () => {
65
66
  Further_Information_Link_Text: 'More Info',
66
67
  Attachment: 'https://attachment.com',
67
68
  Explore_Plan_Link_Text: 'Explore Plan',
68
- Sectors: ['Agriculture'],
69
+ Sectors: [{ Sector: 'Agriculture', Icon: '' }],
69
70
  },
70
71
  ],
71
72
  };
package/src/utils.js CHANGED
@@ -134,3 +134,22 @@ export const extractPlanNameAndURL = (text) => {
134
134
  };
135
135
 
136
136
  export const isEmpty = (arr) => !Array.isArray(arr) || arr.length === 0;
137
+
138
+ export const normalizeImageFileName = (filename) => {
139
+ if (!filename) return '';
140
+
141
+ const parts = filename.split('.');
142
+ if (parts.length < 2)
143
+ return filename.split('(').join('-').split(')').join('-');
144
+
145
+ const ext = parts.pop();
146
+ let name = parts.join('.');
147
+
148
+ name = name.split('(').join('-').split(')').join('-');
149
+
150
+ if (name.endsWith('-')) {
151
+ name = name.slice(0, -1);
152
+ }
153
+
154
+ return `${name}.${ext}`;
155
+ };
@@ -14,15 +14,11 @@
14
14
  }
15
15
  }
16
16
 
17
- body.view-viewview.contenttype-subsite.section-mission
18
- .block.gridBlock
19
- .image {
17
+ body.view-viewview.contenttype-subsite.section-mission .block.gridBlock .image {
20
18
  height: 260px;
21
19
  }
22
20
 
23
- body.view-viewview.contenttype-subsite.section-mission
24
- .block.gridBlock
25
- img {
21
+ body.view-viewview.contenttype-subsite.section-mission .block.gridBlock img {
26
22
  height: 260px;
27
23
  object-fit: contain;
28
24
  }
@@ -78,7 +74,6 @@ body.subsite-mkh {
78
74
  }
79
75
  }
80
76
 
81
-
82
77
  .search-action {
83
78
  background-color: @green-1;
84
79
  }
@@ -114,7 +109,34 @@ body.subsite-mkh {
114
109
  /* Mission signatory profile */
115
110
  .signatory-profile {
116
111
  .ui.items.items-group {
117
- margin: 2em 0 !important;
112
+ margin: 2em 0;
113
+ }
114
+
115
+ .sectors-introduction {
116
+ margin-top: 3em;
117
+ }
118
+
119
+ .funding-sources,
120
+ .provide-section {
121
+ margin-top: 1em;
122
+ }
123
+
124
+ .action-tab {
125
+ .ui.items.items-group {
126
+ margin: 1em 0;
127
+
128
+ .item {
129
+ .content {
130
+ font-size: 16px;
131
+ max-width: 300px;
132
+ }
133
+ }
134
+
135
+ .ui.image {
136
+ width: 35px;
137
+ height: 35px;
138
+ }
139
+ }
118
140
  }
119
141
 
120
142
  .ui.inverted.button.back-to-map {
@@ -211,14 +233,28 @@ body.subsite-mkh {
211
233
 
212
234
  .items-group {
213
235
  display: block !important;
214
- column-count: 2;
236
+
237
+ @media only screen and (min-width: 900px) {
238
+ &.column {
239
+ column-count: 2;
240
+ column-gap: 15px;
241
+ }
242
+ }
215
243
 
216
244
  .item {
217
245
  padding: 0.5em 0 !important;
218
246
  break-inside: avoid-column;
247
+ align-items: center;
248
+
249
+ .ui.image {
250
+ width: 40px;
251
+ height: 40px;
252
+ }
219
253
 
220
254
  .content {
221
- padding-left: 0.5rem !important;
255
+ font-size: 18px;
256
+ padding-left: 0.6rem !important;
257
+ max-width: 300px;
222
258
  }
223
259
  }
224
260
  }