@eeacms/volto-cca-policy 0.3.38 → 0.3.40

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,39 @@ 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.40](https://github.com/eea/volto-cca-policy/compare/0.3.39...0.3.40) - 13 May 2025
8
+
9
+ #### :bug: Bug Fixes
10
+
11
+ - fix: tests [kreafox - [`1852df4`](https://github.com/eea/volto-cca-policy/commit/1852df43cde6f0f173fcd28d79a0576407d9b313)]
12
+ - fix: class -> className [kreafox - [`56124bc`](https://github.com/eea/volto-cca-policy/commit/56124bc768fafcd59fc280d5ed9f8a6840969691)]
13
+ - fix: tests [kreafox - [`4f95457`](https://github.com/eea/volto-cca-policy/commit/4f954579aed4ed94e796be0de712ab1fc025d96a)]
14
+
15
+ #### :nail_care: Enhancements
16
+
17
+ - change: update Assessment section to render html tags [kreafox - [`011d752`](https://github.com/eea/volto-cca-policy/commit/011d752a949ca08ece75bed5aed46103216875ba)]
18
+ - change: update mission signatory profile templates - refs #286863, #286151 [kreafox - [`fde31e1`](https://github.com/eea/volto-cca-policy/commit/fde31e1ccb694a6109a49155010edf3fba34e512)]
19
+ - change(view): add footer text - refs #287671 [kreafox - [`864a247`](https://github.com/eea/volto-cca-policy/commit/864a247e754117b56a42608779ea60b0da8b71e2)]
20
+
21
+ #### :house: Internal changes
22
+
23
+ - style: Automated code fix [eea-jenkins - [`b301fff`](https://github.com/eea/volto-cca-policy/commit/b301fff27818a9317be2b6c4a992ddd9d1672663)]
24
+ - style: Automated code fix [eea-jenkins - [`3aa5289`](https://github.com/eea/volto-cca-policy/commit/3aa52897989a36dae14a0f0c00f4addd233eef18)]
25
+
26
+ #### :hammer_and_wrench: Others
27
+
28
+ - Refs #287520 - eslint [Tripon Eugen - [`fe3883e`](https://github.com/eea/volto-cca-policy/commit/fe3883ede70439de7f42cc817f9471996ad98696)]
29
+ - Refs #287520 - test resposition [Tripon Eugen - [`3114d1b`](https://github.com/eea/volto-cca-policy/commit/3114d1b8bb2d1017c590b1405f83cddeda5f5d35)]
30
+ ### [0.3.39](https://github.com/eea/volto-cca-policy/compare/0.3.38...0.3.39) - 9 May 2025
31
+
32
+ #### :nail_care: Enhancements
33
+
34
+ - change: update Assessment bottom section - refs #286757 [kreafox - [`de01613`](https://github.com/eea/volto-cca-policy/commit/de0161390443df084d058a4243492974240b0406)]
35
+
36
+ #### :house: Internal changes
37
+
38
+ - style: improvements [kreafox - [`f06cfb9`](https://github.com/eea/volto-cca-policy/commit/f06cfb9867963b304ac8be3cd306ca7eeb575290)]
39
+
7
40
  ### [0.3.38](https://github.com/eea/volto-cca-policy/compare/0.3.37...0.3.38) - 9 May 2025
8
41
 
9
42
  ### [0.3.37](https://github.com/eea/volto-cca-policy/compare/0.3.36...0.3.37) - 8 May 2025
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-cca-policy",
3
- "version": "0.3.38",
3
+ "version": "0.3.40",
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",
@@ -47,25 +47,38 @@ export function filterCases(cases, activeFilters) {
47
47
  impacts: false,
48
48
  };
49
49
 
50
- activeFilters.sectors.forEach((filter) => {
51
- if (_case.properties.sectors.includes(',' + filter + ','))
52
- flag.sectors = true;
53
- });
54
-
55
- activeFilters.elements.forEach((filter) => {
56
- if (_case.properties.elements.includes(',' + filter + ','))
57
- flag.elements = true;
58
- });
59
-
60
- activeFilters.impacts.forEach((filter) => {
61
- if (_case.properties.impacts.includes(',' + filter + ','))
62
- flag.impacts = true;
63
- });
64
-
65
- activeFilters.measures.forEach((filter) => {
66
- if (_case.properties.ktms.includes(',' + filter + ','))
67
- flag.measures = true;
68
- });
50
+ // activeFilters.sectors.forEach((filter) => {
51
+ // if (_case.properties.sectors.includes(',' + filter + ','))
52
+ // flag.sectors = true;
53
+ // });
54
+ flag.sectors = activeFilters.sectors.every((sub) =>
55
+ _case.properties.sectors.includes(sub),
56
+ );
57
+
58
+ // ADAPTATION APPROACHES
59
+ // activeFilters.elements.forEach((filter) => {
60
+ // if (_case.properties.elements.includes(',' + filter + ','))
61
+ // flag.elements = true;
62
+ // });
63
+ flag.elements = activeFilters.elements.every((sub) =>
64
+ _case.properties.elements.includes(sub),
65
+ );
66
+
67
+ // activeFilters.impacts.forEach((filter) => {
68
+ // if (_case.properties.impacts.includes(',' + filter + ','))
69
+ // flag.impacts = true;
70
+ // });
71
+ flag.impacts = activeFilters.impacts.every((sub) =>
72
+ _case.properties.impacts.includes(sub),
73
+ );
74
+
75
+ // activeFilters.measures.forEach((filter) => {
76
+ // if (_case.properties.ktms.includes(',' + filter + ','))
77
+ // flag.measures = true;
78
+ // });
79
+ flag.measures = activeFilters.measures.every((sub) =>
80
+ _case.properties.ktms.includes(sub),
81
+ );
69
82
 
70
83
  return (activeFilters.sectors.length ? flag.sectors : true) &&
71
84
  (activeFilters.elements.length ? flag.elements : true) &&
@@ -35,7 +35,7 @@ export default function View(props) {
35
35
  return (
36
36
  <>
37
37
  {isNonEN && (
38
- <Message class="eea callout">
38
+ <Message className="eea callout">
39
39
  <FormattedMessage
40
40
  id="officiallyInEnglish"
41
41
  defaultMessage="Officially reported governmental information is only available in English"
@@ -43,7 +43,7 @@ export default function View(props) {
43
43
  </Message>
44
44
  )}
45
45
  {dataJson.message_top ? (
46
- <div class="eea callout">{dataJson.message_top}</div>
46
+ <div className="eea callout">{dataJson.message_top}</div>
47
47
  ) : null}
48
48
  {dataJson.top_accordeon ? (
49
49
  <div className="top-accordion">
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { Tab, Container } from 'semantic-ui-react';
2
+ import { Tab, Container, Divider } from 'semantic-ui-react';
3
3
  import { BannerTitle } from '@eeacms/volto-cca-policy/helpers';
4
4
  import GovernanceTab from './TabSections/GovernanceTab';
5
5
  import AssessmentTab from './TabSections/AssessmentTab';
@@ -14,10 +14,11 @@ const MissionSignatoryProfileView = (props) => {
14
14
  props?.content?.['@components']?.missionsignatoryprofile || {};
15
15
 
16
16
  const result = dataJson?.result || {};
17
- const governance = result?.governance?.[0] || [];
17
+ const governance = result?.governance?.[0] || {};
18
18
  const planning = result?.planning || {};
19
19
  const assessment = result?.assessment || {};
20
20
  const action = result?.action || {};
21
+ const footer_text = result?.footer_text || {};
21
22
  const tab_labels = result?.tab_labels || {};
22
23
  const { Governance_Label, Assessment_Label, Planning_Label, Action_Label } =
23
24
  tab_labels || {};
@@ -70,6 +71,14 @@ const MissionSignatoryProfileView = (props) => {
70
71
  },
71
72
  ]}
72
73
  />
74
+
75
+ {footer_text.Disclaimer && (
76
+ <div className="footer-text">
77
+ <Divider />
78
+ <strong>{footer_text.Disclaimer_Title}</strong>
79
+ <p>{footer_text.Disclaimer}</p>
80
+ </div>
81
+ )}
73
82
  </div>
74
83
  </Container>
75
84
  );
@@ -1,19 +1,21 @@
1
- import React from 'react';
2
1
  import { Tab, Grid } from 'semantic-ui-react';
3
2
  import { Callout } from '@eeacms/volto-eea-design-system/ui';
3
+ import { HTMLField } from '@eeacms/volto-cca-policy/helpers';
4
+ import { formatTextToHTML } from '@eeacms/volto-cca-policy/utils';
4
5
  import AccordionList from '../AccordionList';
5
6
 
6
7
  const ActionsTabContent = ({ action }) => {
7
8
  const hasHazards = action?.Climate_Hazards?.length > 0;
8
- const hasSectors = !!action?.Sectors;
9
- const hasBenefits = !!action?.Co_Benefits;
9
+ const hasSectors = action?.Sectors.length > 0;
10
+ const hasBenefits = action?.Co_Benefits.length > 0;
11
+
10
12
  return (
11
13
  <>
12
14
  <Grid columns="12">
13
15
  <Grid.Column mobile={12} tablet={12} computer={6}>
14
16
  {hasHazards && (
15
17
  <>
16
- <h5>{action.Hazards_Addressed_Label}</h5>
18
+ <h5 className="small-label">{action.Hazards_Addressed_Label}</h5>
17
19
  <ul>
18
20
  {action.Climate_Hazards.map((hazard, index) => (
19
21
  <li key={index}>{hazard}</li>
@@ -23,7 +25,7 @@ const ActionsTabContent = ({ action }) => {
23
25
  )}
24
26
  {hasSectors && (
25
27
  <>
26
- <h5>{action.Sectors_Label}</h5>
28
+ <h5 className="small-label">{action.Sectors_Label}</h5>
27
29
  <ul>
28
30
  {action.Sectors.map((hazard, index) => (
29
31
  <li key={index}>{hazard}</li>
@@ -72,21 +74,23 @@ const ActionPagesTab = ({ result }) => {
72
74
  return (
73
75
  <Tab.Pane>
74
76
  {Title && <h2>{Title}</h2>}
75
- {Abstract && <p>{Abstract}</p>}
76
- {Abstract_Line && <Callout>{Abstract_Line}</Callout>}
77
-
78
- <br />
77
+ {Abstract && <HTMLField value={{ data: formatTextToHTML(Abstract) }} />}
78
+ {Abstract_Line && (
79
+ <Callout>
80
+ <HTMLField value={{ data: formatTextToHTML(Abstract_Line) }} />
81
+ </Callout>
82
+ )}
79
83
 
80
84
  {sortedActions.map((action, index) => {
81
85
  return (
82
86
  <div key={index} className="section-wrapper">
83
87
  <h5 className="section-title">
84
- <span className="section-number">{action.Order}. </span>
85
- <span>{action?.Action}</span>
88
+ <span className="section-number">{action.Action_Id}. </span>
89
+ <HTMLField value={{ data: formatTextToHTML(action?.Action) }} />
86
90
  </h5>
87
91
 
88
92
  <AccordionList
89
- variation="tertiary"
93
+ variation="secondary"
90
94
  accordions={[
91
95
  {
92
96
  title: action?.More_Details_Label || 'More details',
@@ -61,7 +61,7 @@ describe('ActionPagesTab', () => {
61
61
  };
62
62
 
63
63
  it('renders action tab content correctly', () => {
64
- const { getByText, getAllByText } = render(
64
+ const { getByText, container } = render(
65
65
  <ActionPagesTab result={mockResult} />,
66
66
  );
67
67
 
@@ -71,9 +71,7 @@ describe('ActionPagesTab', () => {
71
71
  ).toBeInTheDocument();
72
72
  expect(getByText('Summary line here.')).toBeInTheDocument();
73
73
 
74
- const sections = getAllByText(/^\d+\./).map((el) =>
75
- el.closest('.section-wrapper'),
76
- );
74
+ const sections = container.querySelectorAll('.section-wrapper');
77
75
 
78
76
  // First action
79
77
  const firstAction = within(sections[0]);
@@ -1,6 +1,8 @@
1
1
  import React from 'react';
2
2
  import { Tab, Image, Segment, Item } from 'semantic-ui-react';
3
3
  import { Callout } from '@eeacms/volto-eea-design-system/ui';
4
+ import { HTMLField } from '@eeacms/volto-cca-policy/helpers';
5
+ import { formatTextToHTML } from '@eeacms/volto-cca-policy/utils';
4
6
  import AccordionList from '../AccordionList';
5
7
 
6
8
  import image from '@eeacms/volto-cca-policy/../theme//assets/images/image-narrow.svg';
@@ -38,7 +40,7 @@ const AssessmentAccordionContent = ({ result }) => {
38
40
 
39
41
  <h5>{result.Further_Details_Label}</h5>
40
42
  <Segment className="border">
41
- <p>{result.Please_Explain}</p>
43
+ <HTMLField value={{ data: formatTextToHTML(result.Please_Explain) }} />
42
44
  </Segment>
43
45
  </>
44
46
  );
@@ -56,15 +58,20 @@ const AssessmentTab = ({ result }) => {
56
58
  Hazards_Abstract,
57
59
  } = result.assessment_text?.[0] || [];
58
60
  const assessment_risks = result.assessment_risks || [];
61
+ const assessment_sectors = result.assessment_sectors || [];
59
62
 
60
- const [activeIndex, setActiveIndex] = React.useState(0);
63
+ // const [activeIndex, setActiveIndex] = React.useState(0);
61
64
 
62
65
  return (
63
66
  <Tab.Pane>
64
67
  {Title && <h2>{Title}</h2>}
65
- {Subheading && <Callout>{Subheading}</Callout>}
68
+ {Subheading && (
69
+ <Callout>
70
+ <HTMLField value={{ data: formatTextToHTML(Subheading) }} />
71
+ </Callout>
72
+ )}
66
73
 
67
- {Abstract && <p>{Abstract}</p>}
74
+ {Abstract && <HTMLField value={{ data: formatTextToHTML(Abstract) }} />}
68
75
 
69
76
  <div className="tab-section-wrapper assessment">
70
77
  {Cra_Title && <h3>{Cra_Title}</h3>}
@@ -98,11 +105,28 @@ const AssessmentTab = ({ result }) => {
98
105
 
99
106
  {Hazards_Title && <h3>{Hazards_Title}</h3>}
100
107
 
101
- {Hazards_Abstract && <p>{Hazards_Abstract}</p>}
108
+ {Hazards_Abstract && (
109
+ <HTMLField value={{ data: formatTextToHTML(Hazards_Abstract) }} />
110
+ )}
102
111
 
103
112
  <br />
104
113
 
105
- <Tab
114
+ {assessment_sectors && (
115
+ <AccordionList
116
+ accordions={assessment_sectors.map((category) => ({
117
+ title: category.Category_Name,
118
+ content: (
119
+ <ul>
120
+ {category.Sectors.map((sector, idx) => (
121
+ <li key={idx}>{sector}</li>
122
+ ))}
123
+ </ul>
124
+ ),
125
+ }))}
126
+ />
127
+ )}
128
+
129
+ {/* <Tab
106
130
  menu={{
107
131
  fluid: true,
108
132
  className: 'secondary',
@@ -153,7 +177,7 @@ const AssessmentTab = ({ result }) => {
153
177
  ),
154
178
  },
155
179
  ]}
156
- />
180
+ /> */}
157
181
  </Tab.Pane>
158
182
  );
159
183
  };
@@ -54,10 +54,5 @@ describe('AssessmentTab', () => {
54
54
  // Items section
55
55
  expect(screen.getByText('Factor A')).toBeInTheDocument();
56
56
  expect(screen.getByText('Factor B')).toBeInTheDocument();
57
-
58
- // Hazard tabs
59
- expect(screen.getByText('Water related')).toBeInTheDocument();
60
- expect(screen.getByText('Heat related')).toBeInTheDocument();
61
- expect(screen.getByText('Other hazards')).toBeInTheDocument();
62
57
  });
63
58
  });
@@ -34,7 +34,9 @@ const PlanningGoalContent = ({ goal }) => {
34
34
  <Grid columns="12">
35
35
  {hasHazards && (
36
36
  <Grid.Column mobile={12} tablet={12} computer={6}>
37
- <h5>{goal.Climate_Hazards_Addressed_Label}</h5>
37
+ <h5 className="small-label">
38
+ {goal.Climate_Hazards_Addressed_Label}
39
+ </h5>
38
40
  <ul>
39
41
  {goal.Climate_Hazards.map((hazard, index) => (
40
42
  <li key={index}>{hazard}</li>
@@ -86,17 +88,25 @@ const PlanningTab = ({ result }) => {
86
88
  return (
87
89
  <Tab.Pane>
88
90
  {titleData?.Title && <h2>{titleData.Title}</h2>}
89
- {titleData?.Abstract_Line && <Callout>{titleData.Abstract_Line}</Callout>}
91
+ {titleData?.Abstract_Line && (
92
+ <Callout>
93
+ <HTMLField
94
+ value={{ data: formatTextToHTML(titleData?.Abstract_Line) }}
95
+ />
96
+ </Callout>
97
+ )}
90
98
 
91
99
  {sortedGoals.map((goal, index) => {
92
100
  return (
93
101
  <div key={index} className="section-wrapper">
94
- <span className="goal-title-label">{goal?.Title_Label}</span>
102
+ <h5 className="section-title">
103
+ <span className="section-number">{goal?.Title_Label} </span>
95
104
 
96
- <HTMLField value={{ data: formatTextToHTML(goal?.Title) }} />
105
+ <HTMLField value={{ data: formatTextToHTML(goal?.Title) }} />
106
+ </h5>
97
107
 
98
108
  <AccordionList
99
- variation="tertiary"
109
+ variation="secondary"
100
110
  accordions={[
101
111
  {
102
112
  title: goal?.More_Details_Label || 'More details',
@@ -113,7 +123,11 @@ const PlanningTab = ({ result }) => {
113
123
  )}
114
124
 
115
125
  {goalData?.Climate_Action_Abstract && (
116
- <Callout>{goalData.Climate_Action_Abstract}</Callout>
126
+ <Callout>
127
+ <HTMLField
128
+ value={{ data: formatTextToHTML(goalData.Climate_Action_Abstract) }}
129
+ />
130
+ </Callout>
117
131
  )}
118
132
 
119
133
  {planning_climate_action.map((action, index) => {
@@ -122,12 +136,22 @@ const PlanningTab = ({ result }) => {
122
136
  <br />
123
137
  {action?.Sectors_Introduction && (
124
138
  <Message>
125
- <p>{action.Sectors_Introduction}</p>
139
+ <HTMLField
140
+ value={{
141
+ data: formatTextToHTML(action.Sectors_Introduction),
142
+ }}
143
+ />
126
144
  </Message>
127
145
  )}
128
146
 
129
147
  <ItemsSection items={action?.Sectors} />
130
- {action?.Description && <p>{action.Description}</p>}
148
+
149
+ {action?.Description && (
150
+ <HTMLField
151
+ className="description"
152
+ value={{ data: formatTextToHTML(action.Description) }}
153
+ />
154
+ )}
131
155
 
132
156
  {(action?.Approval_Year || action?.End_Year) && (
133
157
  <p>
@@ -23,6 +23,10 @@
23
23
  }
24
24
  }
25
25
 
26
+ .description > p {
27
+ margin-bottom: 1em;
28
+ }
29
+
26
30
  .tab-section-wrapper {
27
31
  padding: 1.5em;
28
32
  margin: 2em 0;
@@ -45,20 +49,22 @@
45
49
  }
46
50
 
47
51
  .section-wrapper {
52
+ padding: 1em;
48
53
  margin: 1em 0;
54
+ background-color: #f9f9f9;
49
55
 
50
- .goal-title-label {
51
- display: inline-block;
52
- padding: 0.3em 0.5em;
53
- margin: 1em 0;
54
- background-color: #dbe7f4;
55
- font-size: 14px;
56
- font-weight: bold;
57
- text-transform: uppercase;
56
+ .section-title {
57
+ display: flex;
58
+ align-items: baseline;
59
+ gap: 0.5em;
58
60
  }
59
61
 
60
62
  .ui.accordion {
61
63
  margin-top: 1em;
64
+
65
+ .content.active {
66
+ padding-bottom: 1rem !important;
67
+ }
62
68
  }
63
69
 
64
70
  .small-label {
@@ -66,6 +72,10 @@
66
72
  }
67
73
  }
68
74
 
75
+ .section-number {
76
+ color: @pineGreen;
77
+ }
78
+
69
79
  strong.date {
70
80
  margin-right: 0.5em;
71
81
  }
@@ -83,4 +93,8 @@
83
93
  }
84
94
  }
85
95
  }
96
+
97
+ .footer-text {
98
+ margin-top: 3em;
99
+ }
86
100
  }
@@ -9,13 +9,13 @@ import {
9
9
  } from '@eeacms/volto-cca-policy/helpers';
10
10
  import {
11
11
  Container,
12
- Segment,
12
+ // Segment,
13
13
  Divider,
14
14
  Grid,
15
15
  ListItem,
16
16
  List,
17
17
  } from 'semantic-ui-react';
18
- import { UniversalLink } from '@plone/volto/components';
18
+ // import { UniversalLink } from '@plone/volto/components';
19
19
  import {
20
20
  ShareInfoButton,
21
21
  PortalMessage,
@@ -253,9 +253,12 @@ function AdaptationOptionView(props) {
253
253
  computer={4}
254
254
  className="col-right"
255
255
  >
256
- <ContentMetadata {...props} />
256
+ <ContentMetadata
257
+ {...props}
258
+ related_case_studies={related_case_studies}
259
+ />
257
260
 
258
- {related_case_studies?.length > 0 && (
261
+ {/* {related_case_studies?.length > 0 && (
259
262
  <Segment>
260
263
  <h5>
261
264
  <FormattedMessage
@@ -273,7 +276,7 @@ function AdaptationOptionView(props) {
273
276
  ))}
274
277
  </ul>
275
278
  </Segment>
276
- )}
279
+ )} */}
277
280
  </Grid.Column>
278
281
  </div>
279
282
  </Grid>
@@ -1,8 +1,7 @@
1
- import React from 'react';
2
1
  import { MemoryRouter } from 'react-router-dom';
3
2
  import configureStore from 'redux-mock-store';
4
3
  import '@testing-library/jest-dom/extend-expect';
5
- import { render } from '@testing-library/react';
4
+ import { render, waitFor } from '@testing-library/react';
6
5
  import { Provider } from 'react-intl-redux';
7
6
  import { BrokenLinksComponent } from './BrokenLinks';
8
7
 
@@ -17,7 +16,18 @@ describe('BrokenLinksComponent', () => {
17
16
  messages: {},
18
17
  },
19
18
  });
19
+
20
+ global.fetch = jest.fn(() =>
21
+ Promise.resolve({
22
+ json: () =>
23
+ Promise.resolve({
24
+ broken_links: {},
25
+ }),
26
+ }),
27
+ );
28
+
20
29
  const reactTable = await import('@tanstack/react-table');
30
+
21
31
  const { container } = render(
22
32
  <Provider store={store}>
23
33
  <MemoryRouter>
@@ -25,6 +35,9 @@ describe('BrokenLinksComponent', () => {
25
35
  </MemoryRouter>
26
36
  </Provider>,
27
37
  );
28
- expect(container).toBeTruthy();
38
+
39
+ await waitFor(() => {
40
+ expect(container).toBeTruthy();
41
+ });
29
42
  });
30
43
  });
@@ -13,6 +13,7 @@ import {
13
13
  PUBLICATION_REPORT,
14
14
  } from '@eeacms/volto-cca-policy/helpers/Constants';
15
15
  import { MetadataItemList } from '@eeacms/volto-cca-policy/helpers';
16
+ import { UniversalLink } from '@plone/volto/components';
16
17
 
17
18
  const messages = defineMessages({
18
19
  default_info_tooltip: {
@@ -342,6 +343,11 @@ function ContentMetadata(props) {
342
343
  key_type_measures,
343
344
  funding_programme,
344
345
  } = content;
346
+ // const related_case_studies = props.hasAttribute('related_case_studies')
347
+ // ? props['related_case_studies']
348
+ // : [];
349
+ const related_case_studies = props['related_case_studies'];
350
+
345
351
  const type = content['@type'];
346
352
  const hasGeoChars = geochars !== null || spatial_layer.length > 0;
347
353
 
@@ -364,6 +370,26 @@ function ContentMetadata(props) {
364
370
  portaltype={type}
365
371
  />
366
372
 
373
+ {related_case_studies?.length > 0 && (
374
+ <>
375
+ <h5>
376
+ <FormattedMessage
377
+ id="Case studies related to this option:"
378
+ defaultMessage="Case studies related to this option:"
379
+ />
380
+ </h5>
381
+ <ul className="related-case-studies">
382
+ {related_case_studies.map((item, index) => (
383
+ <li key={index}>
384
+ <UniversalLink key={index} href={item.url}>
385
+ {item.title}
386
+ </UniversalLink>
387
+ </li>
388
+ ))}
389
+ </ul>
390
+ </>
391
+ )}
392
+
367
393
  {keywords && keywords?.length > 0 && (
368
394
  <>
369
395
  <h5>