@eeacms/volto-cca-policy 0.3.24 → 0.3.25

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,30 @@ 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.25](https://github.com/eea/volto-cca-policy/compare/0.3.24...0.3.25) - 14 April 2025
8
+
9
+ #### :bug: Bug Fixes
10
+
11
+ - fix: code cleanup [kreafox - [`6608927`](https://github.com/eea/volto-cca-policy/commit/660892792e10ef6f38c66d8f2d6b7f2993973bdf)]
12
+
13
+ #### :nail_care: Enhancements
14
+
15
+ - change: update planning section, add more tests - refs #285361 [kreafox - [`1d148c8`](https://github.com/eea/volto-cca-policy/commit/1d148c87f0f523effdf7399d7f8a1ddc4620e03e)]
16
+ - change: update planning section - refs #285361 [kreafox - [`50de23f`](https://github.com/eea/volto-cca-policy/commit/50de23f8e75053602b7d0bc1441ae1c59fe0944d)]
17
+
18
+ #### :house: Internal changes
19
+
20
+ - style: Automated code fix [eea-jenkins - [`e4f700e`](https://github.com/eea/volto-cca-policy/commit/e4f700ea7bcb425ae168308d47729ec59b15b2c4)]
21
+
22
+ #### :hammer_and_wrench: Others
23
+
24
+ - Remove console log [Tiberiu Ichim - [`a5ae067`](https://github.com/eea/volto-cca-policy/commit/a5ae06713b54cb1f5fce475827db0a8cd678ecf0)]
25
+ - More langs [Tiberiu Ichim - [`e17b887`](https://github.com/eea/volto-cca-policy/commit/e17b8870eb9e0cdadcab3eca126119287fd2bac2)]
26
+ - test: increase code coverage [kreafox - [`7aeb818`](https://github.com/eea/volto-cca-policy/commit/7aeb818b31fcf2c5225f0b4a6d0294664313b8aa)]
27
+ - increase code coverage [kreafox - [`f82df41`](https://github.com/eea/volto-cca-policy/commit/f82df41ed61fbd901fc5013d966f745c2a97f315)]
28
+ - fix tests [kreafox - [`cf4d267`](https://github.com/eea/volto-cca-policy/commit/cf4d2677c523609b0387f6d8142cb1ad58f0353e)]
29
+ - fix tests [kreafox - [`2151ffc`](https://github.com/eea/volto-cca-policy/commit/2151ffc3d27ff41bd7e72c6c0f373274925e11e8)]
30
+ - fix tests [kreafox - [`3971083`](https://github.com/eea/volto-cca-policy/commit/3971083f85a79d6f99d15b622ca8140a67b1a665)]
7
31
  ### [0.3.24](https://github.com/eea/volto-cca-policy/compare/0.3.23...0.3.24) - 11 April 2025
8
32
 
9
33
  #### :rocket: Dependency updates
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-cca-policy",
3
- "version": "0.3.24",
3
+ "version": "0.3.25",
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",
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import '@testing-library/jest-dom';
3
+ import { render, fireEvent } from '@testing-library/react';
4
+ import AccordionList from './AccordionList';
5
+
6
+ describe('AccordionList', () => {
7
+ const mockAccordions = [
8
+ { title: 'Section 1', content: 'Content for section 1' },
9
+ { title: 'Section 2', content: 'Content for section 2' },
10
+ ];
11
+
12
+ it('renders all accordion titles', () => {
13
+ const { getByText } = render(<AccordionList accordions={mockAccordions} />);
14
+ expect(getByText('Section 1')).toBeInTheDocument();
15
+ expect(getByText('Section 2')).toBeInTheDocument();
16
+ });
17
+
18
+ it('toggles accordion content on title click', () => {
19
+ const { getByText, container } = render(
20
+ <AccordionList accordions={mockAccordions} />,
21
+ );
22
+
23
+ const contentDiv = container.querySelector('.content');
24
+
25
+ expect(contentDiv).not.toHaveClass('active');
26
+
27
+ fireEvent.click(getByText('Section 1'));
28
+ expect(contentDiv).toHaveClass('active');
29
+
30
+ fireEvent.click(getByText('Section 1'));
31
+ expect(contentDiv).not.toHaveClass('active');
32
+ });
33
+ });
@@ -11,7 +11,9 @@ import './style.less';
11
11
 
12
12
  const MissionSignatoriesProfileView = (props) => {
13
13
  const { data } = props;
14
- const result = data?._v_results?.[0] || {};
14
+ const result = data?._v_results || {};
15
+ const governance = result?.governance?.[0] || {};
16
+
15
17
  // const dataJson = JSON.parse(result?.Cooperation_Experience);
16
18
  const [activeIndex, setActiveIndex] = React.useState(0);
17
19
 
@@ -23,7 +25,9 @@ const MissionSignatoriesProfileView = (props) => {
23
25
  'DescribeDetailCooperationEnhance'
24
26
  ]
25
27
  } */}
26
- <h1>{result?.Signatory}</h1>
28
+ <h2>{result?.planning_titles?.[0].Signatory}</h2>
29
+
30
+ <br />
27
31
 
28
32
  <Tab
29
33
  menu={{
@@ -42,19 +46,28 @@ const MissionSignatoriesProfileView = (props) => {
42
46
  },
43
47
  {
44
48
  menuItem: 'Governance',
45
- render: () => <GovernanceTab result={result} />,
49
+ render: () => <GovernanceTab result={governance} />,
46
50
  },
47
51
  {
48
52
  menuItem: 'Assessment',
49
- render: () => <AssessmentTab result={result} />,
53
+ render: () => <AssessmentTab />,
50
54
  },
51
55
  {
52
56
  menuItem: 'Planning',
53
- render: () => <PlanningTab result={result} />,
57
+ render: () => (
58
+ <PlanningTab
59
+ result={{
60
+ planning_goals: result?.planning_goals,
61
+ planning_titles: result?.planning_titles,
62
+ planning_climate_action: result?.planning_climate_action,
63
+ planning_climate_sectors: result?.planning_climate_sectors,
64
+ }}
65
+ />
66
+ ),
54
67
  },
55
68
  {
56
69
  menuItem: 'Action Pages',
57
- render: () => <ActionPagesTab result={result} />,
70
+ render: () => <ActionPagesTab />,
58
71
  },
59
72
  ]}
60
73
  />
@@ -1,43 +1,107 @@
1
1
  import React from 'react';
2
2
  import '@testing-library/jest-dom/extend-expect';
3
- import { render, fireEvent } from '@testing-library/react';
3
+ import { render, fireEvent, waitFor } from '@testing-library/react';
4
4
  import MissionSignatoriesProfileView from './MissionSignatoriesProfileView';
5
5
 
6
+ // Mock components for tabs
7
+ jest.mock('./TabSections/IntroductionTab', () => () => (
8
+ <div>Introduction Content</div>
9
+ ));
10
+ jest.mock('./TabSections/GovernanceTab', () => () => (
11
+ <div>Governance Content</div>
12
+ ));
13
+ jest.mock('./TabSections/AssessmentTab', () => () => (
14
+ <div>Assessment Content</div>
15
+ ));
16
+ jest.mock('./TabSections/PlanningTab', () => () => <div>Planning Content</div>);
17
+ jest.mock('./TabSections/ActionPagesTab', () => () => (
18
+ <div>Action Pages Content</div>
19
+ ));
20
+
6
21
  describe('MissionSignatoriesProfileView', () => {
7
- it('should render the component with data', () => {
8
- const data = {
9
- _v_results: [
10
- {
11
- Signatory: 'Test Signatory',
12
- Describe: 'Test description',
13
- Provide: 'Test evidence',
14
- },
15
- ],
16
- };
22
+ const data = {
23
+ _v_results: {
24
+ planning_titles: [{ Signatory: 'Test Signatory Title' }],
25
+ planning_goals: [],
26
+ planning_climate_action: [],
27
+ planning_climate_sectors: [],
28
+ governance: [{}],
29
+ },
30
+ };
17
31
 
32
+ it('should render the component with data and tabs', () => {
18
33
  const { getByText } = render(<MissionSignatoriesProfileView data={data} />);
19
34
 
20
- expect(getByText('Test Signatory')).toBeInTheDocument();
21
35
  expect(getByText('Governance')).toBeInTheDocument();
22
36
  expect(getByText('Assessment')).toBeInTheDocument();
23
37
  expect(getByText('Planning')).toBeInTheDocument();
24
38
  expect(getByText('Action Pages')).toBeInTheDocument();
39
+ expect(getByText('Introduction')).toBeInTheDocument();
25
40
  });
26
41
 
27
- it('should render tabs and switch content', () => {
28
- const data = {
29
- _v_results: [
30
- {
31
- Signatory: 'Test Signatory',
32
- },
33
- ],
34
- };
42
+ it('should render Signatory title', () => {
43
+ const { getByText } = render(<MissionSignatoriesProfileView data={data} />);
44
+ expect(getByText('Test Signatory Title')).toBeInTheDocument();
45
+ });
46
+
47
+ it('should handle missing planning_titles gracefully', () => {
48
+ const data = { _v_results: { governance: [{}] } };
49
+ const { container } = render(<MissionSignatoriesProfileView data={data} />);
50
+ expect(container).toBeInTheDocument();
51
+ });
35
52
 
53
+ it('should handle empty _v_results object gracefully', () => {
54
+ const { getByText } = render(
55
+ <MissionSignatoriesProfileView data={{ _v_results: {} }} />,
56
+ );
57
+ expect(getByText('Introduction')).toBeInTheDocument();
58
+ });
59
+
60
+ it('should handle completely missing data prop', () => {
61
+ const { getByText } = render(<MissionSignatoriesProfileView data={{}} />);
62
+ expect(getByText('Planning')).toBeInTheDocument();
63
+ expect(getByText('Introduction')).toBeInTheDocument();
64
+ });
65
+
66
+ it('should render all tab labels', () => {
67
+ const { getByText } = render(<MissionSignatoriesProfileView data={{}} />);
68
+ [
69
+ 'Introduction',
70
+ 'Governance',
71
+ 'Assessment',
72
+ 'Planning',
73
+ 'Action Pages',
74
+ ].forEach((label) => {
75
+ expect(getByText(label)).toBeInTheDocument();
76
+ });
77
+ });
78
+
79
+ it('should switch between tabs and display correct content', async () => {
36
80
  const { getByText } = render(<MissionSignatoriesProfileView data={data} />);
37
- const governanceTab = getByText('Governance');
38
- fireEvent.click(governanceTab);
39
- expect(
40
- getByText('Opportunities and benefits of climate action'),
41
- ).toBeInTheDocument();
81
+
82
+ fireEvent.click(getByText('Governance'));
83
+ await waitFor(() =>
84
+ expect(getByText('Governance Content')).toBeInTheDocument(),
85
+ );
86
+
87
+ fireEvent.click(getByText('Introduction'));
88
+ await waitFor(() =>
89
+ expect(getByText('Introduction Content')).toBeInTheDocument(),
90
+ );
91
+
92
+ fireEvent.click(getByText('Assessment'));
93
+ await waitFor(() =>
94
+ expect(getByText('Assessment Content')).toBeInTheDocument(),
95
+ );
96
+
97
+ fireEvent.click(getByText('Planning'));
98
+ await waitFor(() =>
99
+ expect(getByText('Planning Content')).toBeInTheDocument(),
100
+ );
101
+
102
+ fireEvent.click(getByText('Action Pages'));
103
+ await waitFor(() =>
104
+ expect(getByText('Action Pages Content')).toBeInTheDocument(),
105
+ );
42
106
  });
43
107
  });
@@ -10,10 +10,29 @@ import {
10
10
  } from 'semantic-ui-react';
11
11
  import { Callout } from '@eeacms/volto-eea-design-system/ui';
12
12
  import AccordionList from './../AccordionList';
13
- import ItemsSection from './../ItemsSection';
14
13
 
15
14
  import image from '@eeacms/volto-cca-policy/../theme//assets/images/image-narrow.svg';
16
15
 
16
+ const ItemsSection = ({ items }) => {
17
+ return (
18
+ <ItemGroup className="items-group">
19
+ <Item>
20
+ <Image size="small" src={image} />
21
+ <ItemContent verticalAlign="middle">
22
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit
23
+ </ItemContent>
24
+ </Item>
25
+
26
+ <Item>
27
+ <Image size="small" src={image} />
28
+ <ItemContent verticalAlign="middle">
29
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit
30
+ </ItemContent>
31
+ </Item>
32
+ </ItemGroup>
33
+ );
34
+ };
35
+
17
36
  const AssessmentTab = () => {
18
37
  const [activeIndex, setActiveIndex] = React.useState(0);
19
38
  return (
@@ -1,117 +1,188 @@
1
1
  import React from 'react';
2
- import { Tab, Message } from 'semantic-ui-react';
2
+ import {
3
+ Tab,
4
+ Message,
5
+ Segment,
6
+ Grid,
7
+ Item,
8
+ ItemGroup,
9
+ ItemContent,
10
+ Image,
11
+ } from 'semantic-ui-react';
3
12
  import { Callout } from '@eeacms/volto-eea-design-system/ui';
13
+ import { HTMLField } from '@eeacms/volto-cca-policy/helpers';
14
+ import { formatTextToHTML } from '@eeacms/volto-cca-policy/utils';
4
15
  import AccordionList from './../AccordionList';
5
- import ItemsSection from './../ItemsSection';
16
+ import image from '@eeacms/volto-cca-policy/../theme/assets/images/image-narrow.svg';
17
+
18
+ const ItemsSection = ({ items }) => {
19
+ if (!items?.length) return null;
20
+
21
+ return (
22
+ <ItemGroup className="items-group">
23
+ {items.map((sector, index) => (
24
+ <Item key={index}>
25
+ <Image size="small" src={image} />
26
+ <ItemContent verticalAlign="middle">{sector}</ItemContent>
27
+ </Item>
28
+ ))}
29
+ </ItemGroup>
30
+ );
31
+ };
32
+
33
+ const PlanningGoalContent = ({ goal }) => {
34
+ const hasHazards = goal?.Climate_Hazards?.length > 0;
35
+ const hasComments = !!goal?.Comments;
36
+ const hasDescription = !!goal?.Description;
37
+
38
+ return (
39
+ <div>
40
+ <Grid columns="12">
41
+ {hasHazards && (
42
+ <Grid.Column mobile={12} tablet={12} computer={6}>
43
+ <h5>{goal.Climate_Hazards_Addressed_Label}</h5>
44
+ <ul>
45
+ {goal.Climate_Hazards.map((hazard, index) => (
46
+ <li key={index}>{hazard}</li>
47
+ ))}
48
+ </ul>
49
+ </Grid.Column>
50
+ )}
51
+ <Grid.Column mobile={12} tablet={12} computer={hasHazards ? 6 : 12}>
52
+ {hasComments && (
53
+ <>
54
+ <h5>{goal.Comments_Label}</h5>
55
+ <Segment>
56
+ <HTMLField value={{ data: formatTextToHTML(goal.Comments) }} />
57
+ </Segment>
58
+ </>
59
+ )}
60
+ {hasDescription && (
61
+ <>
62
+ <h5>{goal.Description_Label}</h5>
63
+ <Segment>
64
+ <HTMLField
65
+ value={{ data: formatTextToHTML(goal.Description) }}
66
+ />
67
+ </Segment>
68
+ </>
69
+ )}
70
+ </Grid.Column>
71
+ </Grid>
72
+ </div>
73
+ );
74
+ };
75
+
76
+ const PlanningTab = ({ result }) => {
77
+ const {
78
+ planning_goals = [],
79
+ planning_titles = [],
80
+ planning_climate_action = [],
81
+ } = result || {};
82
+
83
+ const titleData = planning_titles?.[0];
84
+ const goalData = planning_goals?.[0];
85
+
86
+ const sortedGoals = [...planning_goals].sort((a, b) => {
87
+ const aNum = parseInt(a.Adaptation_Goal_Id.replace(/\D/g, ''), 10);
88
+ const bNum = parseInt(b.Adaptation_Goal_Id.replace(/\D/g, ''), 10);
89
+ return aNum - bNum;
90
+ });
6
91
 
7
- const PlanningTab = () => {
8
92
  return (
9
93
  <Tab.Pane>
10
- <h2>Planning</h2>
11
- <Callout>
12
- <p>
13
- Vestibulum ante ipsum primis in faucibus orci luctus et ultrices
14
- posuere cubilia curae; Pellentesque sodales, velit nec euismod
15
- scelerisque, lectus est interdum eros, sit amet bibendum eros sapien
16
- in magna.
17
- </p>
18
- </Callout>
19
- <div className="section-wrapper">
20
- <h5>
21
- <span className="section-number">1. </span>
22
- Donec in laoreet leo. Quisque suscipit ligula eu turpis dignissim, a
23
- eleifend ipsum cursus.
24
- </h5>
25
- <AccordionList
26
- variation="secondary"
27
- accordions={[
28
- {
29
- title: 'Vestibulum ante ipsum primis',
30
- content: 'No additional details provided.',
31
- },
32
- ]}
33
- />
34
- </div>
35
- <div className="section-wrapper">
36
- <h5>
37
- <span className="section-number">2. </span>
38
- Donec in laoreet leo. Quisque suscipit ligula eu turpis dignissim, a
39
- eleifend ipsum cursus.
40
- </h5>
41
- <AccordionList
42
- variation="secondary"
43
- accordions={[
44
- {
45
- title: 'Vestibulum ante ipsum primis',
46
- content: 'No additional details provided.',
47
- },
48
- ]}
49
- />
50
- </div>
51
- <div className="section-wrapper">
52
- <h5>
53
- <span className="section-number">3. </span>
54
- Donec in laoreet leo. Quisque suscipit ligula eu turpis dignissim, a
55
- eleifend ipsum cursus.
56
- </h5>
57
- <AccordionList
58
- variation="secondary"
59
- accordions={[
60
- {
61
- title: 'Vestibulum ante ipsum primis',
62
- content: 'No additional details provided.',
63
- },
64
- ]}
65
- />
66
- </div>
67
- <h2>
68
- Quisque suscipit ligula eu turpis dignissim, a eleifend ipsum cursus.
69
- </h2>
70
-
71
- <Callout>
72
- <p>
73
- Vestibulum ante ipsum primis in faucibus orci luctus et ultrices
74
- posuere cubilia curae; Pellentesque sodales, velit nec euismod
75
- scelerisque, lectus est interdum eros, sit amet bibendum eros sapien
76
- in magna.
77
- </p>
78
- </Callout>
79
- <br />
80
- <Message>
81
- <p>
82
- Vestibulum ante ipsum primis in faucibus orci luctus et ultrices
83
- posuere cubilia curae. Nunc euismod bibendum augue. Cras nec ligula
84
- velit. Donec in laoreet leo. Quisque suscipit ligula eu turpis
85
- dignissim, a eleifend ipsum cursus.
86
- </p>
87
- </Message>
88
- <ItemsSection />
89
- <p>
90
- Curabitur at felis non libero suscipit fermentum. Duis volutpat, ante et
91
- scelerisque luctus, sem nulla placerat leo, at aliquet libero justo id
92
- nulla. Integer at dui nec magna posuere fringilla. Nunc euismod bibendum
93
- augue. Cras nec ligula velit. Donec in laoreet leo. Quisque suscipit
94
- ligula eu turpis dignissim, a eleifend ipsum cursus.
95
- </p>
96
- <p>
97
- Year of formal approval of plan: <strong className="date">2023</strong>{' '}
98
- End year of plan: {''}
99
- <strong className="date">2030</strong>
100
- </p>
101
-
102
- <p>
103
- <a href="/">
104
- <strong>Nunc euismod bibendum augue</strong>
105
- </a>
106
- </p>
107
-
108
- <p>
109
- <a href="/">
110
- <strong>
111
- Donec in laoreet leo. Quisque suscipit ligula eu turpis dignissim
112
- </strong>
113
- </a>
114
- </p>
94
+ {titleData?.Title && <h2>{titleData.Title}</h2>}
95
+ {titleData?.Abstract_Line && (
96
+ <Callout>
97
+ <p>{titleData.Abstract_Line}</p>
98
+ </Callout>
99
+ )}
100
+
101
+ {sortedGoals.map((goal, index) => {
102
+ const goalNumber = parseInt(
103
+ goal.Adaptation_Goal_Id.replace(/\D/g, ''),
104
+ 10,
105
+ );
106
+ return (
107
+ <div key={index} className="section-wrapper">
108
+ <h5>
109
+ <span className="section-number">{goalNumber}. </span>
110
+ {goal.Title}
111
+ </h5>
112
+ <AccordionList
113
+ variation="secondary"
114
+ accordions={[
115
+ {
116
+ title: goal.More_Details_Label || 'More details',
117
+ content: <PlanningGoalContent goal={goal} />,
118
+ },
119
+ ]}
120
+ />
121
+ </div>
122
+ );
123
+ })}
124
+
125
+ {goalData?.Climate_Action_Title && (
126
+ <>
127
+ <h2>{goalData.Climate_Action_Title}</h2>
128
+ {goalData?.Climate_Action_Abstract && (
129
+ <Callout>
130
+ <p>{goalData.Climate_Action_Abstract}</p>
131
+ </Callout>
132
+ )}
133
+ </>
134
+ )}
135
+
136
+ {planning_climate_action.map((action, index) => {
137
+ return (
138
+ <>
139
+ <br />
140
+ {action?.Sectors_Introduction && (
141
+ <Message>
142
+ <p>{action.Sectors_Introduction}</p>
143
+ </Message>
144
+ )}
145
+
146
+ <ItemsSection items={action.Sectors} />
147
+ {action?.Description && <p>{action.Description}</p>}
148
+
149
+ {(action?.Approval_Year || action?.End_Year) && (
150
+ <p>
151
+ {action?.Year_Of_Approval_Label}{' '}
152
+ <strong className="date">{action.Approval_Year}</strong>{' '}
153
+ {action?.End_Year_Of_Plan_Label}{' '}
154
+ <strong className="date">{action.End_Year}</strong>
155
+ </p>
156
+ )}
157
+
158
+ {action?.Name_Of_Plan_And_Hyperlink && (
159
+ <p>
160
+ {(() => {
161
+ const [
162
+ planName,
163
+ planUrl,
164
+ ] = action.Name_Of_Plan_And_Hyperlink.split(';').map((part) =>
165
+ part.trim(),
166
+ );
167
+ return (
168
+ <a href={planUrl} title={planName}>
169
+ <strong>{action.Further_Information_Link_Text}</strong>
170
+ </a>
171
+ );
172
+ })()}
173
+ </p>
174
+ )}
175
+
176
+ {action?.Attachment && (
177
+ <p>
178
+ <a href={action.Attachment}>
179
+ <strong>{action.Explore_Plan_Link_Text}</strong>
180
+ </a>
181
+ </p>
182
+ )}
183
+ </>
184
+ );
185
+ })}
115
186
  </Tab.Pane>
116
187
  );
117
188
  };
@@ -0,0 +1,97 @@
1
+ import React from 'react';
2
+ import { render } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
4
+ import PlanningTab from './PlanningTab';
5
+
6
+ // Mock child components
7
+ jest.mock('./../AccordionList', () => () => <div>Mock AccordionList</div>);
8
+ jest.mock('@eeacms/volto-cca-policy/helpers', () => ({
9
+ HTMLField: ({ value }) => (
10
+ <div dangerouslySetInnerHTML={{ __html: value.data }} />
11
+ ),
12
+ }));
13
+ jest.mock('@eeacms/volto-cca-policy/utils', () => ({
14
+ formatTextToHTML: (text) => text,
15
+ }));
16
+
17
+ describe('PlanningTab', () => {
18
+ const mockResult = {
19
+ planning_titles: [
20
+ { Title: 'Planning Title', Abstract_Line: 'Abstract info' },
21
+ ],
22
+ planning_goals: [
23
+ {
24
+ Adaptation_Goal_Id: 'AG-001',
25
+ Title: 'Goal Title 1',
26
+ More_Details_Label: 'Details',
27
+ Climate_Hazards: ['Heat', 'Flood'],
28
+ Climate_Hazards_Addressed_Label: 'Hazards',
29
+ Comments_Label: 'Comments',
30
+ Comments: 'Some comments',
31
+ Description_Label: 'Description',
32
+ Description: 'Detailed description',
33
+ Climate_Action_Title: 'Action Title',
34
+ Climate_Action_Abstract: 'Action abstract',
35
+ },
36
+ {
37
+ Adaptation_Goal_Id: 'AG-002',
38
+ Title: 'Goal Title 2',
39
+ More_Details_Label: 'Details',
40
+ Climate_Hazards: ['Drought', 'Storm'],
41
+ Climate_Hazards_Addressed_Label: 'Hazards',
42
+ Comments_Label: 'Comments',
43
+ Comments: 'Other comments',
44
+ Description_Label: 'Description',
45
+ Description: 'Another detailed description',
46
+ Climate_Action_Title: 'Action Title',
47
+ Climate_Action_Abstract: 'Another action abstract',
48
+ },
49
+ ],
50
+ planning_climate_action: [
51
+ {
52
+ Sectors_Introduction: 'Intro to sectors',
53
+ Description: 'Sector description',
54
+ Year_Of_Approval_Label: 'Approval Year:',
55
+ Approval_Year: '2023',
56
+ End_Year_Of_Plan_Label: 'End Year:',
57
+ End_Year: '2030',
58
+ Name_Of_Plan_And_Hyperlink: 'http://example.com; https://plan-link.com',
59
+ Further_Information_Link_Text: 'More Info',
60
+ Attachment: 'http://attachment.com',
61
+ Explore_Plan_Link_Text: 'Explore Plan',
62
+ Sectors: ['Agriculture'],
63
+ },
64
+ ],
65
+ };
66
+
67
+ it('renders planning tab with basic data', () => {
68
+ const { getByText, getAllByText } = render(
69
+ <PlanningTab result={mockResult} />,
70
+ );
71
+
72
+ expect(getByText('Planning Title')).toBeInTheDocument();
73
+ expect(getByText('Abstract info')).toBeInTheDocument();
74
+
75
+ expect(getByText('Goal Title 1')).toBeInTheDocument();
76
+ expect(getByText('Goal Title 2')).toBeInTheDocument();
77
+
78
+ expect(getAllByText('Mock AccordionList').length).toBe(2);
79
+
80
+ expect(getByText('Action Title')).toBeInTheDocument();
81
+ expect(getByText('Intro to sectors')).toBeInTheDocument();
82
+ expect(getByText('Sector description')).toBeInTheDocument();
83
+
84
+ expect(getByText(/Approval Year:/)).toBeInTheDocument();
85
+ expect(getByText(/2023/)).toBeInTheDocument();
86
+ expect(getByText(/End Year:/)).toBeInTheDocument();
87
+ expect(getByText(/2030/)).toBeInTheDocument();
88
+
89
+ expect(getByText('Explore Plan')).toBeInTheDocument();
90
+ });
91
+
92
+ it('renders ItemsSection if there are sectors', () => {
93
+ const { getByText } = render(<PlanningTab result={mockResult} />);
94
+
95
+ expect(getByText(/Agriculture/)).toBeInTheDocument();
96
+ });
97
+ });
@@ -8,6 +8,10 @@
8
8
  margin: 2em 0;
9
9
  }
10
10
 
11
+ .column > .ui.segment {
12
+ background-color: #fff !important;
13
+ }
14
+
11
15
  .section-wrapper-info {
12
16
  display: flex;
13
17
  justify-content: space-between;
@@ -0,0 +1,51 @@
1
+ export const download_fields = [
2
+ // { field: 'cca_uid', name: 'UID' },
3
+ { field: 'about', name: 'About' },
4
+ { field: 'title', name: 'Title' },
5
+ { field: 'created', name: 'Creation Date' },
6
+ { field: 'issued', name: 'Publication Date' },
7
+ { field: 'creators', name: 'Creator' },
8
+ { field: 'objectProvides', name: 'Content type' },
9
+ { field: 'cca_keywords', name: 'Keywords' },
10
+ { field: 'cca_adaptation_sectors', name: 'Sectors' },
11
+ { field: 'cca_climate_impacts', name: 'Climate impact' },
12
+ { field: 'transnational_regions', name: 'Transnational regions' },
13
+ { field: 'cca_adaptation_elements', name: 'Adaptation Approaches' },
14
+ { field: 'cca_funding_programme', name: 'Funding programme' },
15
+ { field: 'cca_key_type_measure', name: 'Key type measure' },
16
+ { field: 'cca_geographic_countries', name: 'Countries' },
17
+ { field: 'cca_origin_websites', name: 'Origin website' },
18
+ { field: 'cca_health_impacts', name: 'Health impacts' },
19
+ { field: 'cca_partner_contributors', name: 'Observatory impacts' },
20
+ // { field: 'fulltext', name: 'Description' },
21
+ ];
22
+
23
+ export const eea_languages = [
24
+ { name: 'English', code: 'en' },
25
+ { name: 'български', code: 'bg' },
26
+ { name: 'Español', code: 'es' },
27
+ // { name: 'Čeština', code: 'cs' },
28
+ { name: 'Dansk', code: 'da' },
29
+ { name: 'Deutsch', code: 'de' },
30
+ // { name: 'Eesti keel', code: 'et' },
31
+ { name: 'Ελληνικά', code: 'el' },
32
+ { name: 'Français', code: 'fr' },
33
+ // { name: 'Gaeilge', code: 'ga' },
34
+ { name: 'Hrvatski', code: 'hr' },
35
+ { name: 'Italiano', code: 'it' },
36
+ // { name: 'Latviešu valoda', code: 'lv' },
37
+ // { name: 'Lietuvių kalba', code: 'lt' },
38
+ // { name: 'Magyar', code: 'hu' },
39
+ // { name: 'Malti', code: 'mt' },
40
+ // { name: 'Nederlands', code: 'nl' },
41
+ { name: 'Polski', code: 'pl' },
42
+ { name: 'Português', code: 'pt' },
43
+ { name: 'Română', code: 'ro' },
44
+ // { name: 'Slovenčina', code: 'sk' },
45
+ // { name: 'Slovenščina', code: 'sl' },
46
+ { name: 'Suomi', code: 'fi' },
47
+ { name: 'Svenska', code: 'sv' },
48
+ { name: 'Íslenska', code: 'is' },
49
+ { name: 'Nynorsk', code: 'nn' },
50
+ // { name: 'Türkçe', code: 'tr' },
51
+ ];
package/src/index.js CHANGED
@@ -42,6 +42,8 @@ import eeaWhiteLogo from '@eeacms/volto-eea-design-system/../theme/themes/eea/as
42
42
  import './slate-styles.less';
43
43
  import BrokenLinks from './components/theme/Views/BrokenLinks';
44
44
 
45
+ import { eea_languages } from './constants';
46
+
45
47
  const getEnv = () => (typeof window !== 'undefined' ? window.env : process.env);
46
48
 
47
49
  const pathToNegRegex = (p) => `(?!(${p}))`;
@@ -140,35 +142,7 @@ const applyConfig = (config) => {
140
142
  // EEA customizations
141
143
  config.settings.eea = {
142
144
  ...(config.settings.eea || {}),
143
- languages: [
144
- { name: 'English', code: 'en' },
145
- // { name: 'български', code: 'bg' },
146
- { name: 'Español', code: 'es' },
147
- // { name: 'Čeština', code: 'cs' },
148
- { name: 'Dansk', code: 'da' },
149
- { name: 'Deutsch', code: 'de' },
150
- // { name: 'Eesti keel', code: 'et' },
151
- { name: 'Ελληνικά', code: 'el' },
152
- { name: 'Français', code: 'fr' },
153
- // { name: 'Gaeilge', code: 'ga' },
154
- { name: 'Hrvatski', code: 'hr' },
155
- { name: 'Italiano', code: 'it' },
156
- // { name: 'Latviešu valoda', code: 'lv' },
157
- // { name: 'Lietuvių kalba', code: 'lt' },
158
- // { name: 'Magyar', code: 'hu' },
159
- // { name: 'Malti', code: 'mt' },
160
- // { name: 'Nederlands', code: 'nl' },
161
- { name: 'Polski', code: 'pl' },
162
- { name: 'Português', code: 'pt' },
163
- // { name: 'Română', code: 'ro' },
164
- // { name: 'Slovenčina', code: 'sk' },
165
- // { name: 'Slovenščina', code: 'sl' },
166
- { name: 'Suomi', code: 'fi' },
167
- { name: 'Svenska', code: 'sv' },
168
- { name: 'Íslenska', code: 'is' },
169
- { name: 'Nynorsk', code: 'nn' },
170
- // { name: 'Türkçe', code: 'tr' },
171
- ],
145
+ languages: eea_languages,
172
146
  headerOpts: {
173
147
  ...(config.settings.eea?.headerOpts || {}),
174
148
  logo: ccaLogo,
@@ -1,9 +1,10 @@
1
1
  import { mergeConfig } from '@eeacms/search';
2
2
  import { getClientProxyAddress } from '../utils';
3
- import vocabs from '../vocabulary';
3
+ import { vocab } from '../vocabulary';
4
4
 
5
5
  import facets from './facets';
6
6
  import views from './views';
7
+ import { download_fields } from '../../constants';
7
8
 
8
9
  const ccaConfig = {
9
10
  title: 'ClimateAdapt Main',
@@ -76,30 +77,10 @@ export default function installMainSearch(config) {
76
77
  index_name: 'data_searchui',
77
78
  host: process.env.RAZZLE_ES_PROXY_ADDR || 'http://localhost:3000',
78
79
  runtime_mappings: cca_build_runtime_mappings,
79
- ...vocabs,
80
+ vocab,
80
81
  };
81
82
 
82
- config.searchui.ccaSearch.download_fields = [
83
- // { field: 'cca_uid', name: 'UID' },
84
- { field: 'about', name: 'About' },
85
- { field: 'title', name: 'Title' },
86
- { field: 'created', name: 'Creation Date' },
87
- { field: 'issued', name: 'Publication Date' },
88
- { field: 'creators', name: 'Creator' },
89
- { field: 'objectProvides', name: 'Content type' },
90
- { field: 'cca_keywords', name: 'Keywords' },
91
- { field: 'cca_adaptation_sectors', name: 'Sectors' },
92
- { field: 'cca_climate_impacts', name: 'Climate impact' },
93
- { field: 'transnational_regions', name: 'Transnational regions' },
94
- { field: 'cca_adaptation_elements', name: 'Adaptation Approaches' },
95
- { field: 'cca_funding_programme', name: 'Funding programme' },
96
- { field: 'cca_key_type_measure', name: 'Key type measure' },
97
- { field: 'cca_geographic_countries', name: 'Countries' },
98
- { field: 'cca_origin_websites', name: 'Origin website' },
99
- { field: 'cca_health_impacts', name: 'Health impacts' },
100
- { field: 'cca_partner_contributors', name: 'Observatory impacts' },
101
- // { field: 'fulltext', name: 'Description' },
102
- ];
83
+ config.searchui.ccaSearch.download_fields = download_fields;
103
84
 
104
85
  const { ccaSearch } = config.searchui;
105
86
 
@@ -1,3 +1,4 @@
1
+ import { eea_languages } from '@eeacms/volto-cca-policy/constants';
1
2
  import { booleanFacet } from '@eeacms/search';
2
3
  import { getTodayWithTime } from './utils';
3
4
 
@@ -192,42 +193,44 @@ export const language = {
192
193
  type: 'any',
193
194
  };
194
195
  },
195
- facetValues: [
196
- 'de',
197
- 'en',
198
- 'es',
199
- 'fr',
200
- 'it',
201
- 'pl',
202
- // 'ar',
203
- // 'bg',
204
- // 'bs',
205
- // 'cs',
206
- // 'da',
207
- // 'el',
208
- // 'et',
209
- // 'fi',
210
- // 'ga',
211
- // 'hr',
212
- // 'hu',
213
- // 'is',
214
- // 'lt',
215
- // 'lv',
216
- // 'mk',
217
- // 'mt',
218
- // 'nl',
219
- // 'no',
220
- // 'pt',
221
- // 'ro',
222
- // 'ru',
223
- // 'sh',
224
- // 'sk',
225
- // 'sl',
226
- // 'sq',
227
- // 'sr',
228
- // 'sv',
229
- // 'tr',
230
- ],
196
+ facetValues: eea_languages.map(({ code }) => code),
231
197
  sortOn: 'custom',
232
198
  sortOnCustomLabel: 'Alphabetical',
233
199
  };
200
+
201
+ // [
202
+ // 'de',
203
+ // 'en',
204
+ // 'es',
205
+ // 'fr',
206
+ // 'it',
207
+ // 'pl',
208
+ // // 'ar',
209
+ // // 'bg',
210
+ // // 'bs',
211
+ // // 'cs',
212
+ // // 'da',
213
+ // // 'el',
214
+ // // 'et',
215
+ // // 'fi',
216
+ // // 'ga',
217
+ // // 'hr',
218
+ // // 'hu',
219
+ // // 'is',
220
+ // // 'lt',
221
+ // // 'lv',
222
+ // // 'mk',
223
+ // // 'mt',
224
+ // // 'nl',
225
+ // // 'no',
226
+ // // 'pt',
227
+ // // 'ro',
228
+ // // 'ru',
229
+ // // 'sh',
230
+ // // 'sk',
231
+ // // 'sl',
232
+ // // 'sq',
233
+ // // 'sr',
234
+ // // 'sv',
235
+ // // 'tr',
236
+ // ]
@@ -1,7 +1,7 @@
1
1
  import { mergeConfig } from '@eeacms/search';
2
2
  import { build_runtime_mappings } from '@eeacms/volto-globalsearch/utils';
3
3
  import { getClientProxyAddress } from './../utils';
4
- import vocabs from './../vocabulary';
4
+ import { vocab } from './../vocabulary';
5
5
 
6
6
  import facets from './facets-health';
7
7
  import views from './views-health';
@@ -90,7 +90,7 @@ export default function installMainSearch(config) {
90
90
  elastic_index: '_es/globalsearch',
91
91
  index_name: 'data_searchui',
92
92
  host: process.env.RAZZLE_ES_PROXY_ADDR || 'http://localhost:3000',
93
- ...vocabs,
93
+ vocab,
94
94
  runtime_mappings: build_runtime_mappings(clusters),
95
95
  };
96
96
 
@@ -1,4 +1,6 @@
1
- const vocab = {
1
+ import { eea_languages } from '@eeacms/volto-cca-policy/constants';
2
+
3
+ export const vocab = {
2
4
  cluster_name: {
3
5
  cca: 'Climate-ADAPT',
4
6
  },
@@ -19,6 +21,8 @@ const vocab = {
19
21
  'Publication reference': 'Publications and reports',
20
22
  Video: 'Videos and podcasts',
21
23
  },
24
+ language: Object.assign(
25
+ {},
26
+ ...eea_languages.map(({ name, code }) => ({ [code]: name })),
27
+ ),
22
28
  };
23
-
24
- export default { vocab };
package/src/utils.js CHANGED
@@ -81,10 +81,17 @@ export const filterBlocks = (content, blockTypes = []) => {
81
81
  export const formatTextToHTML = (text) => {
82
82
  if (!text) return '';
83
83
 
84
- // Replace \\n\\n with </p><p> (separate paragraphs)
85
- let formattedText = text.replace(/\\n\\n/g, '</p><p>');
84
+ let formattedText = text;
86
85
 
87
- // Replace \\n with <br /> (line breaks within paragraphs)
86
+ // Handle common escape issues
87
+ formattedText = formattedText.replace(/\\\\/g, '\\'); // unescape backslashes
88
+ formattedText = formattedText.replace(/\\'/g, "'"); // unescape single quotes
89
+ formattedText = formattedText.replace(/\\"/g, '"'); // unescape double quotes
90
+
91
+ // Replace \\n\\n with </p><p> for paragraph separation
92
+ formattedText = formattedText.replace(/\\n\\n/g, '</p><p>');
93
+
94
+ // Replace \\n with <br /> for line breaks
88
95
  formattedText = formattedText.replace(/\\n/g, '<br />');
89
96
 
90
97
  return `<p>${formattedText}</p>`;
@@ -1,54 +0,0 @@
1
- import React from 'react';
2
- import { Item, ItemGroup, ItemContent, Image } from 'semantic-ui-react';
3
-
4
- import image from '@eeacms/volto-cca-policy/../theme//assets/images/image-narrow.svg';
5
-
6
- const ItemsSection = ({ items }) => {
7
- return (
8
- <ItemGroup className="items-group">
9
- <Item>
10
- <Image size="small" src={image} />
11
- <ItemContent verticalAlign="middle">
12
- Lorem ipsum dolor sit amet, consectetur adipiscing elit
13
- </ItemContent>
14
- </Item>
15
-
16
- <Item>
17
- <Image size="small" src={image} />
18
- <ItemContent verticalAlign="middle">
19
- Lorem ipsum dolor sit amet, consectetur adipiscing elit
20
- </ItemContent>
21
- </Item>
22
-
23
- <Item>
24
- <Image size="small" src={image} />
25
- <ItemContent verticalAlign="middle">
26
- Lorem ipsum dolor sit amet, consectetur adipiscing elit
27
- </ItemContent>
28
- </Item>
29
-
30
- <Item>
31
- <Image size="small" src={image} />
32
- <ItemContent verticalAlign="middle">
33
- Lorem ipsum dolor sit amet, consectetur adipiscing elit
34
- </ItemContent>
35
- </Item>
36
-
37
- <Item>
38
- <Image size="small" src={image} />
39
- <ItemContent verticalAlign="middle">
40
- Lorem ipsum dolor sit amet, consectetur adipiscing elit
41
- </ItemContent>
42
- </Item>
43
-
44
- <Item>
45
- <Image size="small" src={image} />
46
- <ItemContent verticalAlign="middle">
47
- Lorem ipsum dolor sit amet, consectetur adipiscing elit
48
- </ItemContent>
49
- </Item>
50
- </ItemGroup>
51
- );
52
- };
53
-
54
- export default ItemsSection;