@eeacms/volto-cca-policy 0.1.90 → 0.1.91

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,16 @@ 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.1.91](https://github.com/eea/volto-cca-policy/compare/0.1.90...0.1.91) - 5 March 2024
8
+
9
+ #### :bug: Bug Fixes
10
+
11
+ - fix(test): update tests for UniversalLink + generate snapshot [kreafox - [`4b2e0c2`](https://github.com/eea/volto-cca-policy/commit/4b2e0c265601f1420a95a9cf0056fc3ea73baf59)]
12
+
13
+ #### :hammer_and_wrench: Others
14
+
15
+ - Add test file [Tiberiu Ichim - [`282e32c`](https://github.com/eea/volto-cca-policy/commit/282e32cde07122150379df996b19d37eed3e0260)]
16
+ - Add override for UniversalLink, see #266263 [Tiberiu Ichim - [`63242f5`](https://github.com/eea/volto-cca-policy/commit/63242f5fc4b0e54d1170b8f8ee31dba46e997eee)]
7
17
  ### [0.1.90](https://github.com/eea/volto-cca-policy/compare/0.1.89...0.1.90) - 5 March 2024
8
18
 
9
19
  #### :hammer_and_wrench: Others
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-cca-policy",
3
- "version": "0.1.90",
3
+ "version": "0.1.91",
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,3 @@
1
+ Customization of https://github.com/eea/volto-eea-website-theme/blob/e12e85279d0973ed17ffadf9897f0bc8f4368227/src/customizations/volto/components/manage/UniversalLink/UniversalLink.jsx
2
+
3
+ Because we always want to open external links in new tabs
@@ -0,0 +1,152 @@
1
+ /*
2
+ * UniversalLink
3
+ * @module components/UniversalLink
4
+ * Removed noreferrer from rel attribute
5
+ */
6
+
7
+ import React from 'react';
8
+ import PropTypes from 'prop-types';
9
+ import { HashLink as Link } from 'react-router-hash-link';
10
+ import { useSelector } from 'react-redux';
11
+ import {
12
+ flattenToAppURL,
13
+ isInternalURL,
14
+ URLUtils,
15
+ } from '@plone/volto/helpers/Url/Url';
16
+
17
+ import config from '@plone/volto/registry';
18
+
19
+ const UniversalLink = ({
20
+ href,
21
+ item = null,
22
+ openLinkInNewTab,
23
+ download = false,
24
+ children,
25
+ className = null,
26
+ title = null,
27
+ ...props
28
+ }) => {
29
+ const token = useSelector((state) => state.userSession?.token);
30
+
31
+ let url = href;
32
+ if (!href && item) {
33
+ if (!item['@id']) {
34
+ // eslint-disable-next-line no-console
35
+ console.error(
36
+ 'Invalid item passed to UniversalLink',
37
+ item,
38
+ props,
39
+ children,
40
+ );
41
+ url = '#';
42
+ } else {
43
+ //case: generic item
44
+ url = flattenToAppURL(item['@id']);
45
+
46
+ //case: item like a Link
47
+ let remoteUrl = item.remoteUrl || item.getRemoteUrl;
48
+ if (!token && remoteUrl) {
49
+ url = remoteUrl;
50
+ }
51
+
52
+ //case: item of type 'File'
53
+ if (
54
+ !token &&
55
+ config.settings.downloadableObjects.includes(item['@type'])
56
+ ) {
57
+ url = `${url}/@@download/file`;
58
+ }
59
+
60
+ if (
61
+ !token &&
62
+ config.settings.viewableInBrowserObjects.includes(item['@type'])
63
+ ) {
64
+ url = `${url}/@@display-file/file`;
65
+ }
66
+ }
67
+ }
68
+
69
+ const isExternal = !isInternalURL(url);
70
+
71
+ const isDownload = (!isExternal && url.includes('@@download')) || download;
72
+ const isDisplayFile =
73
+ (!isExternal && url.includes('@@display-file')) || false;
74
+
75
+ const checkedURL = URLUtils.checkAndNormalizeUrl(url);
76
+
77
+ url = checkedURL.url;
78
+ let tag = (
79
+ <Link
80
+ to={flattenToAppURL(url)}
81
+ target={openLinkInNewTab ?? false ? '_blank' : null}
82
+ title={title}
83
+ className={className}
84
+ smooth={config.settings.hashLinkSmoothScroll}
85
+ {...props}
86
+ >
87
+ {children}
88
+ </Link>
89
+ );
90
+
91
+ const isBlank =
92
+ (isExternal || openLinkInNewTab) &&
93
+ (!checkedURL.isMail || !checkedURL.isTelephone);
94
+
95
+ if (isExternal) {
96
+ tag = (
97
+ <a
98
+ href={url}
99
+ title={title}
100
+ target={isBlank ? '_blank' : null}
101
+ rel="noopener"
102
+ className={className}
103
+ {...props}
104
+ >
105
+ {children}
106
+ </a>
107
+ );
108
+ } else if (isDownload) {
109
+ tag = (
110
+ <a
111
+ href={flattenToAppURL(url)}
112
+ download
113
+ title={title}
114
+ className={className}
115
+ {...props}
116
+ >
117
+ {children}
118
+ </a>
119
+ );
120
+ } else if (isDisplayFile) {
121
+ tag = (
122
+ <a
123
+ href={flattenToAppURL(url)}
124
+ title={title}
125
+ rel="noopener"
126
+ className={className}
127
+ {...props}
128
+ >
129
+ {children}
130
+ </a>
131
+ );
132
+ }
133
+ return tag;
134
+ };
135
+
136
+ UniversalLink.propTypes = {
137
+ href: PropTypes.string,
138
+ openLinkInNewTab: PropTypes.bool,
139
+ download: PropTypes.bool,
140
+ className: PropTypes.string,
141
+ title: PropTypes.string,
142
+ item: PropTypes.shape({
143
+ '@id': PropTypes.string.isRequired,
144
+ remoteUrl: PropTypes.string, //of plone @type 'Link'
145
+ }),
146
+ children: PropTypes.oneOfType([
147
+ PropTypes.arrayOf(PropTypes.node),
148
+ PropTypes.node,
149
+ ]),
150
+ };
151
+
152
+ export default UniversalLink;
@@ -0,0 +1,229 @@
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 { render } from '@testing-library/react';
6
+ import { MemoryRouter } from 'react-router-dom';
7
+ import UniversalLink from './UniversalLink';
8
+ import config from '@plone/volto/registry';
9
+
10
+ const mockStore = configureStore();
11
+ const store = mockStore({
12
+ userSession: {
13
+ token: null,
14
+ },
15
+ intl: {
16
+ locale: 'en',
17
+ messages: {},
18
+ },
19
+ });
20
+
21
+ global.console.error = jest.fn();
22
+
23
+ describe('UniversalLink', () => {
24
+ it('renders a UniversalLink component with internal link', () => {
25
+ const component = renderer.create(
26
+ <Provider store={store}>
27
+ <MemoryRouter>
28
+ <UniversalLink href={'/en/welcome-to-volto'}>
29
+ <h1>Title</h1>
30
+ </UniversalLink>
31
+ </MemoryRouter>
32
+ </Provider>,
33
+ );
34
+ const json = component.toJSON();
35
+ expect(json).toMatchSnapshot();
36
+ });
37
+
38
+ it('renders a UniversalLink component with external link', () => {
39
+ const component = renderer.create(
40
+ <Provider store={store}>
41
+ <MemoryRouter>
42
+ <UniversalLink href="https://github.com/plone/volto">
43
+ <h1>Title</h1>
44
+ </UniversalLink>
45
+ </MemoryRouter>
46
+ </Provider>,
47
+ );
48
+ const json = component.toJSON();
49
+ expect(json).toMatchSnapshot();
50
+ });
51
+
52
+ it('renders a UniversalLink component if no external(href) link passed', () => {
53
+ const component = renderer.create(
54
+ <Provider store={store}>
55
+ <MemoryRouter>
56
+ <UniversalLink
57
+ item={{
58
+ '@id': 'http://localhost:3000/en/welcome-to-volto',
59
+ }}
60
+ >
61
+ <h1>Title</h1>
62
+ </UniversalLink>
63
+ </MemoryRouter>
64
+ </Provider>,
65
+ );
66
+ const json = component.toJSON();
67
+ expect(json).toMatchSnapshot();
68
+ });
69
+
70
+ it('check UniversalLink set rel attribute for ext links', () => {
71
+ const { getByTitle } = render(
72
+ <Provider store={store}>
73
+ <MemoryRouter>
74
+ <UniversalLink
75
+ href="https://github.com/plone/volto"
76
+ title="Volto GitHub repository"
77
+ >
78
+ <h1>Title</h1>
79
+ </UniversalLink>
80
+ </MemoryRouter>
81
+ </Provider>,
82
+ );
83
+
84
+ expect(getByTitle('Volto GitHub repository').getAttribute('rel')).toBe(
85
+ 'noopener',
86
+ );
87
+ });
88
+
89
+ it('check UniversalLink set target attribute for ext links', () => {
90
+ const { getByTitle } = render(
91
+ <Provider store={store}>
92
+ <MemoryRouter>
93
+ <UniversalLink
94
+ href="https://github.com/plone/volto"
95
+ title="Volto GitHub repository"
96
+ >
97
+ <h1>Title</h1>
98
+ </UniversalLink>
99
+ </MemoryRouter>
100
+ </Provider>,
101
+ );
102
+
103
+ expect(getByTitle('Volto GitHub repository').getAttribute('target')).toBe(
104
+ '_blank',
105
+ );
106
+ });
107
+
108
+ it('check UniversalLink can unset target for ext links with prop', () => {
109
+ const { getByTitle } = render(
110
+ <Provider store={store}>
111
+ <MemoryRouter>
112
+ <UniversalLink
113
+ href="https://github.com/plone/volto"
114
+ title="Volto GitHub repository"
115
+ openLinkInNewTab={false}
116
+ >
117
+ <h1>Title</h1>
118
+ </UniversalLink>
119
+ </MemoryRouter>
120
+ </Provider>,
121
+ );
122
+
123
+ expect(getByTitle('Volto GitHub repository').getAttribute('target')).toBe(
124
+ '_blank',
125
+ );
126
+ });
127
+
128
+ it('check UniversalLink renders ext link for blacklisted urls', () => {
129
+ config.settings.externalRoutes = [
130
+ {
131
+ match: {
132
+ path: '/external-app',
133
+ exact: true,
134
+ strict: false,
135
+ },
136
+ url(payload) {
137
+ return payload.location.pathname;
138
+ },
139
+ },
140
+ ];
141
+
142
+ const { getByTitle } = render(
143
+ <Provider store={store}>
144
+ <MemoryRouter>
145
+ <UniversalLink
146
+ href="http://localhost:3000/external-app"
147
+ title="Blacklisted route"
148
+ >
149
+ <h1>Title</h1>
150
+ </UniversalLink>
151
+ </MemoryRouter>
152
+ </Provider>,
153
+ );
154
+
155
+ expect(getByTitle('Blacklisted route').getAttribute('target')).toBe(
156
+ '_blank',
157
+ );
158
+ });
159
+
160
+ it('UniversalLink renders external link where link is blacklisted', () => {
161
+ const notInEN = /^(?!.*(#|\/en|\/static|\/controlpanel|\/cypress|\/login|\/logout|\/contact-form)).*$/;
162
+ config.settings.externalRoutes = [
163
+ {
164
+ match: {
165
+ path: notInEN,
166
+ exact: false,
167
+ strict: false,
168
+ },
169
+ url(payload) {
170
+ return payload.location.pathname;
171
+ },
172
+ },
173
+ ];
174
+
175
+ const { getByTitle } = render(
176
+ <Provider store={store}>
177
+ <MemoryRouter>
178
+ <UniversalLink
179
+ href="http://localhost:3000/blacklisted-app"
180
+ title="External blacklisted app"
181
+ >
182
+ <h1>Title</h1>
183
+ </UniversalLink>
184
+ </MemoryRouter>
185
+ </Provider>,
186
+ );
187
+
188
+ expect(getByTitle('External blacklisted app').getAttribute('target')).toBe(
189
+ '_blank',
190
+ );
191
+ expect(getByTitle('External blacklisted app').getAttribute('rel')).toBe(
192
+ 'noopener',
193
+ );
194
+ });
195
+
196
+ it('check UniversalLink does not break with error in item', () => {
197
+ const component = renderer.create(
198
+ <Provider store={store}>
199
+ <MemoryRouter>
200
+ <UniversalLink
201
+ item={{
202
+ error: 'Error while fetching content',
203
+ message: 'Something went wrong',
204
+ }}
205
+ >
206
+ <h1>Title</h1>
207
+ </UniversalLink>
208
+ </MemoryRouter>
209
+ </Provider>,
210
+ );
211
+ const json = component.toJSON();
212
+ expect(json).toMatchSnapshot();
213
+ expect(global.console.error).toHaveBeenCalled();
214
+ });
215
+ });
216
+
217
+ it('renders a UniversalLink component when url ends with @@display-file', () => {
218
+ const component = renderer.create(
219
+ <Provider store={store}>
220
+ <MemoryRouter>
221
+ <UniversalLink href="http://localhost:3000/en/welcome-to-volto/@@display-file">
222
+ <h1>Title</h1>
223
+ </UniversalLink>
224
+ </MemoryRouter>
225
+ </Provider>,
226
+ );
227
+ const json = component.toJSON();
228
+ expect(json).toMatchSnapshot();
229
+ });