@eeacms/volto-cca-policy 0.3.23 → 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,52 @@ 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)]
31
+ ### [0.3.24](https://github.com/eea/volto-cca-policy/compare/0.3.23...0.3.24) - 11 April 2025
32
+
33
+ #### :rocket: Dependency updates
34
+
35
+ - Release @eeacms/volto-globalsearch@2.0.11 [EEA Jenkins - [`e96979c`](https://github.com/eea/volto-cca-policy/commit/e96979c8b0bd19a67055f0a1c1e9fd8eeec41e69)]
36
+ - Release @eeacms/volto-searchlib@2.0.15 [EEA Jenkins - [`35919dd`](https://github.com/eea/volto-cca-policy/commit/35919dd309a32ab58704d90291db6b812d4a9d1b)]
37
+
38
+ #### :house: Internal changes
39
+
40
+ - style: Automated code fix [eea-jenkins - [`3630c9a`](https://github.com/eea/volto-cca-policy/commit/3630c9ab97418376c1d4a75a546d4eeb3c8d32e8)]
41
+ - style: Automated code fix [eea-jenkins - [`d085ddd`](https://github.com/eea/volto-cca-policy/commit/d085dddcb1ee2967ecfebd1f956082d2f9bfd16f)]
42
+ - style: Automated code fix [eea-jenkins - [`687b4a2`](https://github.com/eea/volto-cca-policy/commit/687b4a20546b72d6c95810f97f24b08cb46b13b4)]
43
+
44
+ #### :hammer_and_wrench: Others
45
+
46
+ - More langs [Tiberiu Ichim - [`0cea414`](https://github.com/eea/volto-cca-policy/commit/0cea4147d6ae72f4f4ca6cb87177745dbdb79259)]
47
+ - More langs [Tiberiu Ichim - [`9010d13`](https://github.com/eea/volto-cca-policy/commit/9010d132d2b9aad96fa6a297ffd25bdf329ce124)]
48
+ - More langs [Tiberiu Ichim - [`d7a9036`](https://github.com/eea/volto-cca-policy/commit/d7a9036d8341204026fd62aadc748cf71324fe21)]
49
+ - Rename norwegian [Tiberiu Ichim - [`15aaf95`](https://github.com/eea/volto-cca-policy/commit/15aaf9515c86131efebffb9a280e4ea913adebb9)]
50
+ - Add norwegian [Tiberiu Ichim - [`1c2410b`](https://github.com/eea/volto-cca-policy/commit/1c2410b7e590b1ba409d698f3ef2a943ba1fabe5)]
51
+ - Enable extra languages [Tiberiu Ichim - [`3210236`](https://github.com/eea/volto-cca-policy/commit/321023608d5ae8294490929e2def26686ea63cd8)]
52
+ - Add error component [Tiberiu Ichim - [`8a9d6dc`](https://github.com/eea/volto-cca-policy/commit/8a9d6dc3d90b67463b0f3356ce887c7a15996e67)]
7
53
  ### [0.3.23](https://github.com/eea/volto-cca-policy/compare/0.3.22...0.3.23) - 7 April 2025
8
54
 
9
55
  #### :bug: Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-cca-policy",
3
- "version": "0.3.23",
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",
@@ -32,10 +32,10 @@
32
32
  "@eeacms/volto-eea-design-system": "<=1.36.3",
33
33
  "@eeacms/volto-eea-website-theme": "^1.35.0",
34
34
  "@eeacms/volto-embed": "^9.1.1",
35
- "@eeacms/volto-globalsearch": "2.0.10",
35
+ "@eeacms/volto-globalsearch": "2.0.11",
36
36
  "@eeacms/volto-hero-block": "^7.1.0",
37
37
  "@eeacms/volto-openlayers-map": "*",
38
- "@eeacms/volto-searchlib": "2.0.14",
38
+ "@eeacms/volto-searchlib": "2.0.15",
39
39
  "@eeacms/volto-slate-label": "^0.6.0",
40
40
  "@eeacms/volto-tabs-block": "^7.5.1",
41
41
  "@elastic/search-ui": "1.21.2",
@@ -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
+ ];
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Error Page.
3
+ * @module Error
4
+ */
5
+
6
+ import React from 'react';
7
+ import { FormattedMessage, injectIntl } from 'react-intl';
8
+ import PropTypes from 'prop-types';
9
+ import { useHistory } from 'react-router-dom';
10
+
11
+ const styles = {
12
+ container: {
13
+ display: 'flex',
14
+ flexDirection: 'column',
15
+ alignItems: 'center',
16
+ justifyContent: 'center',
17
+ height: '100vh',
18
+ textAlign: 'center',
19
+ // backgroundColor: '#f8f9fa',
20
+ color: '#343a40',
21
+ fontFamily: 'Arial, sans-serif',
22
+ },
23
+ title: {
24
+ fontSize: '2rem',
25
+ marginBottom: '1rem',
26
+ },
27
+ message: {
28
+ fontSize: '1.2rem',
29
+ marginBottom: '2rem',
30
+ },
31
+ button: {
32
+ padding: '10px 20px',
33
+ fontSize: '1rem',
34
+ color: '#fff',
35
+ backgroundColor: '#004B7F',
36
+ border: 'none',
37
+ borderRadius: '5px',
38
+ cursor: 'pointer',
39
+ marginBottom: '1rem',
40
+ },
41
+ link: {
42
+ color: '#004B7F',
43
+ textDecoration: 'none',
44
+ fontSize: '1rem',
45
+ },
46
+ };
47
+
48
+ /**
49
+ * Error page.
50
+ * @function Error
51
+ * @returns {string} Markup of the error page.
52
+ */
53
+ const Error = ({ message, stackTrace }) => {
54
+ let history = useHistory();
55
+
56
+ return (
57
+ <div
58
+ style={{
59
+ fontFamily: 'Helvetica, sans-serif',
60
+ fontSize: '20px',
61
+ display: 'flex',
62
+ flexDirection: 'column',
63
+ justifyContent: 'center',
64
+ alignItems: 'center',
65
+ }}
66
+ >
67
+ <div style={styles.container}>
68
+ <h1 style={styles.title}>
69
+ <FormattedMessage
70
+ id="Sorry, something went wrong with your request"
71
+ defaultMessage="Sorry, something went wrong with your request"
72
+ />
73
+ </h1>
74
+ <p style={styles.message}>We're sorry, but an error has occurred.</p>
75
+
76
+ <button style={styles.button} onClick={() => history.goBack()}>
77
+ <FormattedMessage id="Navigate back" defaultMessage="Navigate back" />
78
+ </button>
79
+ <a style={styles.link} href="/">
80
+ <FormattedMessage
81
+ id="return to the site root"
82
+ defaultMessage="return to the site root"
83
+ />
84
+ </a>
85
+ <p>
86
+ <FormattedMessage
87
+ id="or try a different page."
88
+ defaultMessage="or try a different page."
89
+ />
90
+ </p>
91
+ <div style={{ display: 'none' }}>
92
+ <strong style={{ color: 'red' }}>{message}</strong>
93
+ <pre>{stackTrace}</pre>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ );
98
+ };
99
+
100
+ Error.propTypes = {
101
+ message: PropTypes.string.isRequired,
102
+ stackTrace: PropTypes.string,
103
+ };
104
+
105
+ Error.defaultProps = {
106
+ stackTrace: null,
107
+ };
108
+
109
+ export default injectIntl(Error);
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}))`;
@@ -105,19 +107,42 @@ const applyConfig = (config) => {
105
107
  config.settings.isMultilingual = true;
106
108
  config.settings.hasLanguageDropdown = true;
107
109
  config.settings.defaultLanguage = 'en';
108
- config.settings.supportedLanguages = ['en', 'de', 'fr', 'es', 'it', 'pl'];
110
+ config.settings.supportedLanguages = [
111
+ 'en',
112
+
113
+ 'bg', // bulgarian
114
+ 'es', // spanish
115
+ 'cs', // czech
116
+ 'da', // danish
117
+ 'de', // german
118
+ 'et', // estonian
119
+ 'el', // greek
120
+ 'fr', // french
121
+ 'ga', // irish
122
+ 'hr', // croatian
123
+ 'it', // italian
124
+ 'lv', // latvia
125
+ 'lt', // lituania
126
+ 'hu', // hungarian
127
+ 'mt', // malta
128
+ 'nl', // dutch
129
+ 'pl', // polish
130
+ 'pt', // portuguese
131
+ 'ro', // romanian
132
+ 'sk', // slovakian
133
+ 'sl', // slovenian
134
+ 'fi', // suomi (finish)
135
+ 'sv', // swedish
136
+
137
+ 'is', // islenska, for iceland
138
+ 'nn', // norwegean (one of 2)
139
+ 'tr', // turkish
140
+ ];
109
141
 
110
142
  // EEA customizations
111
143
  config.settings.eea = {
112
144
  ...(config.settings.eea || {}),
113
- languages: [
114
- { name: 'English', code: 'en' },
115
- { name: 'Deutsch', code: 'de' },
116
- { name: 'Français', code: 'fr' },
117
- { name: 'Español', code: 'es' },
118
- { name: 'Italiano', code: 'it' },
119
- { name: 'Polski', code: 'pl' },
120
- ],
145
+ languages: eea_languages,
121
146
  headerOpts: {
122
147
  ...(config.settings.eea?.headerOpts || {}),
123
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;
File without changes