@eeacms/volto-cca-policy 0.2.34 → 0.2.35

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,11 +4,19 @@ 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.2.34](https://github.com/eea/volto-cca-policy/compare/0.2.33...0.2.34) - 24 May 2024
7
+ ### [0.2.35](https://github.com/eea/volto-cca-policy/compare/0.2.34...0.2.35) - 24 May 2024
8
+
9
+ #### :bug: Bug Fixes
10
+
11
+ - fix: don't break view without blocks [kreafox - [`0a63710`](https://github.com/eea/volto-cca-policy/commit/0a63710bb682f033b4a9f0f8df51720c10dc7644)]
8
12
 
9
13
  #### :hammer_and_wrench: Others
10
14
 
11
- - Fix type in event listing [Tiberiu Ichim - [`09b2456`](https://github.com/eea/volto-cca-policy/commit/09b24565c456fc622d46b4d32029bc74c62e619b)]
15
+ - test: increase coverage [kreafox - [`5a349cf`](https://github.com/eea/volto-cca-policy/commit/5a349cf8f2d04f4e491537c3c6d0ed442ac6481f)]
16
+ - test: fix issues reported by sonarqube [kreafox - [`a84e309`](https://github.com/eea/volto-cca-policy/commit/a84e309de1d8da69832a28c0a9bc037a57cd4ddb)]
17
+ - update snapshot [kreafox - [`0e1b2cc`](https://github.com/eea/volto-cca-policy/commit/0e1b2ccf6d3088d5c3867287050d99adcd93f655)]
18
+ ### [0.2.34](https://github.com/eea/volto-cca-policy/compare/0.2.33...0.2.34) - 24 May 2024
19
+
12
20
  ### [0.2.33](https://github.com/eea/volto-cca-policy/compare/0.2.32...0.2.33) - 23 May 2024
13
21
 
14
22
  #### :bug: Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-cca-policy",
3
- "version": "0.2.34",
3
+ "version": "0.2.35",
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",
@@ -1,13 +1,15 @@
1
1
  import React from 'react';
2
- import {
3
- BannerTitle,
4
- PortalMessage,
5
- } from '@eeacms/volto-cca-policy/components';
6
- import RenderBlocks from '@plone/volto/components/theme/View/RenderBlocks';
7
- import { Grid, Container, Segment, Button, Icon } from 'semantic-ui-react';
8
- import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
9
- import { SubjectTags, EventDetails } from '@eeacms/volto-cca-policy/helpers';
10
2
  import { expandToBackendURL } from '@plone/volto/helpers';
3
+ import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
4
+ import { Grid, Container, Segment, Button, Icon } from 'semantic-ui-react';
5
+ import RenderBlocks from '@plone/volto/components/theme/View/RenderBlocks';
6
+ import {
7
+ SubjectTags,
8
+ EventDetails,
9
+ HTMLField,
10
+ } from '@eeacms/volto-cca-policy/helpers';
11
+ import { filterBlocks } from '@eeacms/volto-cca-policy/utils';
12
+ import { PortalMessage } from '@eeacms/volto-cca-policy/components';
11
13
 
12
14
  const messages = defineMessages({
13
15
  downloadEvent: {
@@ -16,20 +18,39 @@ const messages = defineMessages({
16
18
  },
17
19
  });
18
20
 
19
- function CcaEventView(props) {
20
- const { content } = props;
21
+ function EventView(props) {
21
22
  const intl = useIntl();
23
+ const { content } = props;
24
+ const {
25
+ blocks: filtered_blocks,
26
+ blocks_layout: filtered_blocks_layout,
27
+ hasBlockType,
28
+ } = filterBlocks(content, 'tabs_block');
22
29
 
23
30
  return (
24
31
  <div className="cca-event-view">
25
- <BannerTitle content={content} />
32
+ <PortalMessage content={content} />
26
33
 
27
34
  <Container>
28
- <PortalMessage content={content} />
29
35
  <Grid columns="12">
30
36
  <Grid.Row>
31
37
  <Grid.Column mobile={12} tablet={12} computer={8}>
32
- <RenderBlocks {...props} />
38
+ {hasBlockType && (
39
+ <>
40
+ <p className="documentDescription">{content.description}</p>
41
+ <HTMLField value={content.text} className="content-text" />
42
+ </>
43
+ )}
44
+
45
+ <RenderBlocks
46
+ {...props}
47
+ content={{
48
+ ...content,
49
+ blocks: filtered_blocks,
50
+ blocks_layout: filtered_blocks_layout,
51
+ }}
52
+ />
53
+
33
54
  <SubjectTags {...props} />
34
55
  </Grid.Column>
35
56
  <Grid.Column mobile={12} tablet={12} computer={4}>
@@ -37,9 +58,9 @@ function CcaEventView(props) {
37
58
  <EventDetails {...props} />
38
59
  {content?.event_url && (
39
60
  <>
40
- <h3>
61
+ <h4>
41
62
  <FormattedMessage id="Web" defaultMessage="Web" />
42
- </h3>
63
+ </h4>
43
64
  <p>
44
65
  <a href={content.event_url} target="_blank">
45
66
  <FormattedMessage
@@ -79,4 +100,4 @@ function CcaEventView(props) {
79
100
  );
80
101
  }
81
102
 
82
- export default CcaEventView;
103
+ export default EventView;
@@ -0,0 +1,143 @@
1
+ import React from 'react';
2
+ import renderer from 'react-test-renderer';
3
+ import { Provider } from 'react-intl-redux';
4
+ import configureStore from 'redux-mock-store';
5
+ import EventView from './EventView';
6
+ import config from '@plone/volto/registry';
7
+
8
+ config.blocks = {
9
+ blocksConfig: {
10
+ title: {
11
+ view: () => <div>Title Block Component</div>,
12
+ },
13
+ },
14
+ };
15
+
16
+ const mockStore = configureStore();
17
+
18
+ const store = mockStore({
19
+ intl: {
20
+ locale: 'en',
21
+ messages: {},
22
+ },
23
+ });
24
+
25
+ jest.mock('react-router-dom', () => ({
26
+ ...jest.requireActual('react-router-dom'),
27
+ useLocation: () => ({
28
+ pathname: '/',
29
+ hash: '',
30
+ search: '',
31
+ state: undefined,
32
+ }),
33
+ }));
34
+
35
+ jest.mock('@plone/volto/helpers/Loadable/Loadable');
36
+ beforeAll(
37
+ async () =>
38
+ await require('@plone/volto/helpers/Loadable/Loadable').__setLoadables(),
39
+ );
40
+
41
+ const { settings } = config;
42
+
43
+ test('renders an event view component with all props', () => {
44
+ const component = renderer.create(
45
+ <Provider store={store}>
46
+ <EventView
47
+ content={{
48
+ '@id': 'http://localhost:8080/Plone/my-page',
49
+ title: 'Hello World!',
50
+ description: 'Hi',
51
+ text: {
52
+ data: '<p>Hello World!</p>',
53
+ },
54
+ attendees: ['John Doe', 'Mario Rossi'],
55
+ contact_email: 'test@example.com',
56
+ contact_name: 'John Doe',
57
+ contact_phone: '0123456789',
58
+ end: '2019-06-24T15:20:00+00:00',
59
+ event_url: 'https://www.example.com',
60
+ location: 'Volto, Plone',
61
+ open_end: false,
62
+ recurrence: 'RRULE:FREQ=DAILY;INTERVAL=7;COUNT=7',
63
+ start: '2019-06-23T15:20:00+00:00',
64
+ subjects: ['Volto'],
65
+ whole_day: false,
66
+ blocks: {
67
+ '6cc87646-cbd3-40ea-8b18-8a737b4ec803': {
68
+ '@type': 'title',
69
+ copyrightIcon: 'ri-copyright-line',
70
+ styles: {},
71
+ },
72
+ },
73
+ blocks_layout: {
74
+ items: ['6cc87646-cbd3-40ea-8b18-8a737b4ec803'],
75
+ },
76
+ }}
77
+ />
78
+ </Provider>,
79
+ );
80
+ const json = component.toJSON();
81
+ expect(json).toMatchSnapshot();
82
+ });
83
+
84
+ test('renders an event view component with only required props', () => {
85
+ const component = renderer.create(
86
+ <Provider store={store}>
87
+ <EventView
88
+ content={{
89
+ '@id': 'http://localhost:8080/Plone/my-page',
90
+ title: 'Hello World!',
91
+ attendees: [],
92
+ end: '2019-06-23T16:20:00+00:00',
93
+ start: '2019-06-23T15:20:00+00:00',
94
+ subjects: ['Volto'],
95
+ blocks: {
96
+ '6cc87646-cbd3-40ea-8b18-8a737b4ec803': {
97
+ '@type': 'title',
98
+ copyrightIcon: 'ri-copyright-line',
99
+ styles: {},
100
+ },
101
+ },
102
+ blocks_layout: {
103
+ items: ['6cc87646-cbd3-40ea-8b18-8a737b4ec803'],
104
+ },
105
+ }}
106
+ />
107
+ </Provider>,
108
+ );
109
+ const json = component.toJSON();
110
+ expect(json).toMatchSnapshot();
111
+ });
112
+
113
+ test('renders an event view component without links to api in the text', () => {
114
+ const component = renderer.create(
115
+ <Provider store={store}>
116
+ <EventView
117
+ content={{
118
+ '@id': 'http://localhost:8080/Plone/my-page',
119
+ title: 'Hello World!',
120
+ attendees: [],
121
+ end: '2019-06-23T16:20:00+00:00',
122
+ start: '2019-06-23T15:20:00+00:00',
123
+ subjects: ['Volto'],
124
+ text: {
125
+ data: `<p>Hello World!</p><p>This is an <a href="${settings.apiPath}/foo/bar">internal link</a> and a <a href="${settings.apiPath}/foo/baz">second link</a></p>`,
126
+ },
127
+ blocks: {
128
+ '6cc87646-cbd3-40ea-8b18-8a737b4ec803': {
129
+ '@type': 'title',
130
+ copyrightIcon: 'ri-copyright-line',
131
+ styles: {},
132
+ },
133
+ },
134
+ blocks_layout: {
135
+ items: ['6cc87646-cbd3-40ea-8b18-8a737b4ec803'],
136
+ },
137
+ }}
138
+ />
139
+ </Provider>,
140
+ );
141
+ const json = component.toJSON();
142
+ expect(json).toMatchSnapshot();
143
+ });
@@ -1,33 +1,51 @@
1
1
  import React from 'react';
2
- import {
3
- BannerTitle,
4
- PortalMessage,
5
- } from '@eeacms/volto-cca-policy/components';
6
2
  import { Container } from 'semantic-ui-react';
7
- import { SubjectTags } from '@eeacms/volto-cca-policy/helpers';
3
+ import { filterBlocks } from '@eeacms/volto-cca-policy/utils';
4
+ import { PortalMessage } from '@eeacms/volto-cca-policy/components';
5
+ import { HTMLField, SubjectTags } from '@eeacms/volto-cca-policy/helpers';
8
6
  import RenderBlocks from '@plone/volto/components/theme/View/RenderBlocks';
9
- // import { When } from '@plone/volto/components/theme/View/EventDatesInfo';
7
+ import { When } from '@plone/volto/components/theme/View/EventDatesInfo';
10
8
 
11
- // const Date = (props) => {
12
- // const date = props.content?.effective;
13
- // return date ? (
14
- // <>
15
- // <When start={date} end={date} whole_day={true} open_end={false} />
16
- // </>
17
- // ) : null;
18
- // };
9
+ const PublicationDate = (props) => {
10
+ const date = props.content?.effective;
11
+ return date ? (
12
+ <div className="news-date-info">
13
+ Date:
14
+ <When start={date} end={date} whole_day={true} open_end={false} />
15
+ </div>
16
+ ) : null;
17
+ };
19
18
 
20
19
  function NewsItemView(props) {
21
20
  const { content } = props;
21
+ const {
22
+ blocks: filtered_blocks,
23
+ blocks_layout: filtered_blocks_layout,
24
+ hasBlockType,
25
+ } = filterBlocks(content, 'tabs_block');
22
26
 
23
27
  return (
24
28
  <div className="cca-newsitem-view">
25
- <BannerTitle content={content} />
29
+ <PortalMessage content={content} />
26
30
 
27
31
  <Container>
28
- <PortalMessage content={content} />
29
- <RenderBlocks {...props} />
30
- {/* <Date {...props} /> */}
32
+ {hasBlockType && (
33
+ <>
34
+ <p className="documentDescription">{content.description}</p>
35
+ <HTMLField value={content.text} className="content-text" />
36
+ </>
37
+ )}
38
+
39
+ <RenderBlocks
40
+ {...props}
41
+ content={{
42
+ ...content,
43
+ blocks: filtered_blocks,
44
+ blocks_layout: filtered_blocks_layout,
45
+ }}
46
+ />
47
+
48
+ <PublicationDate {...props} />
31
49
  <SubjectTags {...props} />
32
50
  </Container>
33
51
  </div>
@@ -454,9 +454,9 @@ export const EventDetails = (props) => {
454
454
 
455
455
  return (
456
456
  <>
457
- <h3>
457
+ <h4>
458
458
  <FormattedMessage id="When" defaultMessage="When" />
459
- </h3>
459
+ </h4>
460
460
  <When
461
461
  start={content.start}
462
462
  end={content.end}
@@ -465,17 +465,17 @@ export const EventDetails = (props) => {
465
465
  />
466
466
  {content?.location !== null && (
467
467
  <>
468
- <h3>
468
+ <h4>
469
469
  <FormattedMessage id="Where" defaultMessage="Where" />
470
- </h3>
470
+ </h4>
471
471
  <p>{content.location}</p>
472
472
  </>
473
473
  )}
474
474
  {!!content.contact_email && (
475
475
  <>
476
- <h3>
476
+ <h4>
477
477
  <FormattedMessage id="Info" defaultMessage="Info" />
478
- </h3>
478
+ </h4>
479
479
  <p>{content.contact_email}</p>
480
480
  </>
481
481
  )}
package/src/utils.js CHANGED
@@ -50,3 +50,23 @@ export const hasTypeOfBlock = (obj, type, name) => {
50
50
 
51
51
  return false;
52
52
  };
53
+
54
+ export const filterBlocks = (content, block_type) => {
55
+ const filteredBlocks = { ...content.blocks };
56
+ const filteredBlocksLayout = { ...content.blocks_layout };
57
+
58
+ const filteredBlockUID = Object.keys(filteredBlocks)?.filter(
59
+ (key) => filteredBlocks[key]['@type'] === block_type,
60
+ );
61
+ filteredBlockUID.forEach((key) => delete filteredBlocks[key]);
62
+ filteredBlocksLayout.items = filteredBlocksLayout?.items?.filter(
63
+ (item) => !filteredBlockUID.includes(item),
64
+ );
65
+ const hasBlockType = filteredBlockUID.length > 0;
66
+
67
+ return {
68
+ blocks: filteredBlocks,
69
+ blocks_layout: filteredBlocksLayout,
70
+ hasBlockType,
71
+ };
72
+ };
@@ -302,3 +302,17 @@ hr {
302
302
  }
303
303
  }
304
304
  }
305
+
306
+ .cca-newsitem-view,
307
+ .cca-event-view {
308
+ .news-date-info {
309
+ display: flex;
310
+ gap: 3px;
311
+ justify-content: flex-end;
312
+ }
313
+
314
+ .content-text p {
315
+ margin: 0em 0em 1em;
316
+ line-height: 1.5;
317
+ }
318
+ }