@eeacms/volto-eea-website-theme 3.19.1 → 4.0.1
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/.eslintrc.js +7 -6
- package/CHANGELOG.md +26 -0
- package/DEVELOP.md +19 -17
- package/README.md +19 -7
- package/docker-compose.yml +1 -1
- package/jest-addon.config.js +8 -4
- package/package.json +1 -1
- package/src/actions/navigation.js +1 -1
- package/src/components/manage/Blocks/ContextNavigation/ContextNavigationEdit.jsx +4 -2
- package/src/components/manage/Blocks/ContextNavigation/ContextNavigationEdit.test.jsx +25 -19
- package/src/components/manage/Blocks/ContextNavigation/ContextNavigationView.jsx +2 -1
- package/src/components/manage/Blocks/ContextNavigation/ContextNavigationView.test.jsx +6 -4
- package/src/components/manage/Blocks/ContextNavigation/variations/Accordion.jsx +2 -2
- package/src/components/manage/Blocks/ContextNavigation/variations/ReportNavigation.jsx +4 -2
- package/src/components/manage/Blocks/ContextNavigation/variations/ReportNavigation.test.jsx +1 -1
- package/src/components/manage/Blocks/GroupBlockTemplate/FlexGroup/FlexGroup.jsx +12 -44
- package/src/components/manage/Blocks/GroupBlockTemplate/FlexGroup/RenderBlocks.jsx +5 -4
- package/src/components/manage/Blocks/GroupBlockTemplate/FlexGroup/editor-flex.less +45 -4
- package/src/components/manage/Blocks/LayoutSettings/LayoutSettingsEdit.jsx +2 -1
- package/src/components/manage/Blocks/LayoutSettings/LayoutSettingsEdit.test.jsx +12 -4
- package/src/components/manage/Blocks/LayoutSettings/LayoutSettingsView.jsx +1 -1
- package/src/components/manage/Blocks/Title/Edit.jsx +3 -3
- package/src/components/manage/Blocks/Title/View.jsx +2 -1
- package/src/components/manage/Blocks/Title/variations/WebReport.jsx +2 -2
- package/src/components/manage/Blocks/Title/variations/WebReport.test.jsx +6 -4
- package/src/components/manage/Blocks/Title/variations/WebReportPage.jsx +2 -2
- package/src/components/manage/Blocks/Title/variations/WebReportPage.test.jsx +6 -4
- package/src/components/theme/Banner/View.jsx +1 -1
- package/src/components/theme/BaseTag.jsx +2 -1
- package/src/components/theme/BaseTag.test.jsx +7 -2
- package/src/components/theme/DraftBackground/DraftBackground.jsx +2 -1
- package/src/components/theme/Homepage/HomePageInverseView.jsx +3 -3
- package/src/components/theme/Homepage/HomePageInverseView.test.jsx +1 -1
- package/src/components/theme/Homepage/HomePageView.jsx +3 -3
- package/src/components/theme/Homepage/HomePageView.test.jsx +1 -1
- package/src/components/theme/Logo.jsx +3 -3
- package/src/components/theme/NotFound/GoneView.jsx +3 -2
- package/src/components/theme/NotFound/GoneView.test.jsx +5 -4
- package/src/components/theme/NotFound/NotFound.jsx +1 -1
- package/src/components/theme/NotFound/NotFound.test.jsx +3 -2
- package/src/components/theme/PrintLoader/PrintLoader.test.jsx +1 -1
- package/src/components/theme/SubsiteClass.jsx +6 -4
- package/src/components/theme/SubsiteClass.test.jsx +3 -2
- package/src/components/theme/WebReport/WebReportSectionView.jsx +2 -2
- package/src/components/theme/WebReport/WebReportSectionView.test.jsx +10 -5
- package/src/components/theme/Widgets/ADUserGroupSelectWidget.jsx +2 -2
- package/src/components/theme/Widgets/ContributorsViewWidget.jsx +1 -1
- package/src/components/theme/Widgets/CreatableSelectWidget.jsx +7 -4
- package/src/components/theme/Widgets/CreatorsViewWidget.jsx +1 -1
- package/src/components/theme/Widgets/DateWidget.jsx +1 -1
- package/src/components/theme/Widgets/DateWidget.test.js +1 -1
- package/src/components/theme/Widgets/DatetimeWidget.jsx +1 -1
- package/src/components/theme/Widgets/DatetimeWidget.test.js +1 -1
- package/src/components/theme/Widgets/ImageViewWidget.jsx +1 -0
- package/src/components/theme/Widgets/NavigationBehaviorWidget.jsx +7 -3
- package/src/components/theme/Widgets/NavigationBehaviorWidget.test.jsx +51 -46
- package/src/components/theme/Widgets/UserSelectWidget.jsx +13 -10
- package/src/customizations/@plone/volto-slate/blocks/Table/TableBlockView.jsx +3 -3
- package/src/customizations/@plone/volto-slate/blocks/Text/TextBlockView.jsx +2 -2
- package/src/customizations/@plone/volto-slate/editor/SlateEditor.jsx +23 -10
- package/src/customizations/@plone/volto-slate/editor/render.jsx +7 -3
- package/src/customizations/@plone/volto-slate/utils/blocks.js +11 -8
- package/src/customizations/volto/components/manage/Blocks/Grid/View.jsx +2 -2
- package/src/customizations/volto/components/manage/Blocks/Image/Edit.jsx +30 -27
- package/src/customizations/volto/components/manage/Blocks/Image/Edit.test.jsx +244 -246
- package/src/customizations/volto/components/manage/Blocks/Image/View.jsx +23 -25
- package/src/customizations/volto/components/manage/Blocks/LeadImage/Edit.jsx +6 -4
- package/src/customizations/volto/components/manage/Blocks/LeadImage/LeadImageSidebar.jsx +4 -2
- package/src/customizations/volto/components/manage/Blocks/LeadImage/View.jsx +2 -2
- package/src/customizations/volto/components/manage/Controlpanels/Groups/RenderGroups.jsx +1 -1
- package/src/customizations/volto/components/manage/Controlpanels/Groups/RenderGroups.test.jsx +108 -42
- package/src/customizations/volto/components/manage/Diff/DiffField.jsx +4 -3
- package/src/customizations/volto/components/manage/Display/Display.jsx +8 -7
- package/src/customizations/volto/components/manage/Sidebar/ObjectBrowserBody.jsx +42 -21
- package/src/customizations/volto/components/manage/Sidebar/ObjectBrowserNav.jsx +2 -1
- package/src/customizations/volto/components/manage/Sidebar/SidebarPopup.jsx +46 -24
- package/src/customizations/volto/components/manage/Sidebar/objectBrowserSelection.js +58 -0
- package/src/customizations/volto/components/manage/Toolbar/More.jsx +8 -10
- package/src/customizations/volto/components/manage/Widgets/NumberWidget.jsx +1 -1
- package/src/customizations/volto/components/manage/Widgets/NumberWidget.test.jsx +6 -1
- package/src/customizations/volto/components/manage/Widgets/ObjectBrowserWidget.jsx +66 -12
- package/src/customizations/volto/components/manage/Workflow/Workflow.jsx +10 -9
- package/src/customizations/volto/components/theme/Breadcrumbs/Breadcrumbs.jsx +3 -2
- package/src/customizations/volto/components/theme/Comments/Comments.jsx +9 -8
- package/src/customizations/volto/components/theme/Comments/Comments.test.jsx +29 -7
- package/src/customizations/volto/components/theme/ContactForm/ContactForm.jsx +1 -1
- package/src/customizations/volto/components/theme/ContactForm/ContactForm.test.js +5 -0
- package/src/customizations/volto/components/theme/ContentMetadataTags/ContentMetadataTags.jsx +5 -7
- package/src/customizations/volto/components/theme/EventDetails/EventDetails.jsx +2 -2
- package/src/customizations/volto/components/theme/Footer/Footer.jsx +1 -1
- package/src/customizations/volto/components/theme/Header/Header.jsx +10 -8
- package/src/customizations/volto/components/theme/Header/Header.test.jsx +1 -1
- package/src/customizations/volto/components/theme/Header/LanguageSwitcher.jsx +3 -3
- package/src/customizations/volto/components/theme/Image/Image.jsx +4 -3
- package/src/customizations/volto/components/theme/Unauthorized/Unauthorized.jsx +1 -1
- package/src/customizations/volto/components/theme/View/DefaultView.jsx +4 -3
- package/src/customizations/volto/components/theme/View/EventView.jsx +3 -2
- package/src/customizations/volto/helpers/Html/Html.jsx +16 -6
- package/src/customizations/volto/helpers/Html/Readme.md +7 -1
- package/src/customizations/volto/reducers/breadcrumbs/breadcrumbs.js +3 -6
- package/src/customizations/volto/server.jsx +13 -15
- package/src/helpers/schema-utils.js +1 -1
- package/src/helpers/schema-utils.test.js +1 -1
- package/src/hocs/withErrorBoundary.jsx +1 -1
- package/src/hocs/withErrorBoundary.test.jsx +4 -11
- package/src/hocs/withRootNavigation.jsx +3 -2
- package/src/hocs/withRootNavigation.test.jsx +18 -14
- package/src/index.js +3 -3
- package/src/slate.js +1 -1
- package/src/customizations/volto/components/manage/Blocks/LeadImage/AlignChooser.jsx +0 -76
- package/src/customizations/volto/components/manage/Blocks/LeadImage/AlignChooser.test.js +0 -50
- package/src/customizations/volto/components/manage/Sidebar/SidebarPopup copy.jsx +0 -82
|
@@ -1,26 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ObjectBrowserWidget component.
|
|
3
3
|
* @module components/manage/Widgets/ObjectBrowserWidget
|
|
4
|
+
*
|
|
5
|
+
* EEA customization: preserves hash anchors in pasted internal URLs.
|
|
6
|
+
* When a URL like `/path/to/page#section` is entered, the hash is stored in
|
|
7
|
+
* `linkWithHash` on the selected item so the link renders with the anchor.
|
|
4
8
|
*/
|
|
5
9
|
|
|
6
10
|
import React, { Component } from 'react';
|
|
7
11
|
import PropTypes from 'prop-types';
|
|
8
12
|
import { compose } from 'redux';
|
|
9
|
-
import
|
|
13
|
+
import compact from 'lodash/compact';
|
|
14
|
+
import includes from 'lodash/includes';
|
|
15
|
+
import isArray from 'lodash/isArray';
|
|
16
|
+
import isEmpty from 'lodash/isEmpty';
|
|
17
|
+
import remove from 'lodash/remove';
|
|
10
18
|
import { connect } from 'react-redux';
|
|
11
19
|
import { Label, Popup, Button } from 'semantic-ui-react';
|
|
12
20
|
import {
|
|
13
21
|
flattenToAppURL,
|
|
14
22
|
isInternalURL,
|
|
15
|
-
isUrl,
|
|
16
23
|
normalizeUrl,
|
|
17
24
|
removeProtocol,
|
|
18
25
|
} from '@plone/volto/helpers/Url/Url';
|
|
26
|
+
import { messages as validationMessages } from '@plone/volto/helpers/MessageLabels/MessageLabels';
|
|
19
27
|
import { searchContent } from '@plone/volto/actions/search/search';
|
|
20
28
|
import withObjectBrowser from '@plone/volto/components/manage/Sidebar/ObjectBrowser';
|
|
21
29
|
import { defineMessages, injectIntl } from 'react-intl';
|
|
22
30
|
import Icon from '@plone/volto/components/theme/Icon/Icon';
|
|
23
31
|
import FormFieldWrapper from '@plone/volto/components/manage/Widgets/FormFieldWrapper';
|
|
32
|
+
import config from '@plone/volto/registry';
|
|
24
33
|
|
|
25
34
|
import navTreeSVG from '@plone/volto/icons/nav.svg';
|
|
26
35
|
import clearSVG from '@plone/volto/icons/clear.svg';
|
|
@@ -28,6 +37,7 @@ import homeSVG from '@plone/volto/icons/home.svg';
|
|
|
28
37
|
import aheadSVG from '@plone/volto/icons/ahead.svg';
|
|
29
38
|
import blankSVG from '@plone/volto/icons/blank.svg';
|
|
30
39
|
import { withRouter } from 'react-router';
|
|
40
|
+
import Image from '@plone/volto/components/theme/Image/Image';
|
|
31
41
|
|
|
32
42
|
const messages = defineMessages({
|
|
33
43
|
placeholder: {
|
|
@@ -48,6 +58,24 @@ const messages = defineMessages({
|
|
|
48
58
|
},
|
|
49
59
|
});
|
|
50
60
|
|
|
61
|
+
// Volto 17 does not expose the Volto 18 core helper
|
|
62
|
+
// `urlValidator` from `@plone/volto/helpers/FormValidation/validators`.
|
|
63
|
+
// When Volto 17 support is dropped, replace this fallback with that import.
|
|
64
|
+
const validateExternalUrl = ({ value, formatMessage }) => {
|
|
65
|
+
const urlRegex = new RegExp(
|
|
66
|
+
'^(https?:\\/\\/)?' +
|
|
67
|
+
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' +
|
|
68
|
+
'((\\d{1,3}\\.){3}\\d{1,3}))|' +
|
|
69
|
+
'(localhost)' +
|
|
70
|
+
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' +
|
|
71
|
+
'(\\?[;&a-z\\d%_.~+=-]*)?' +
|
|
72
|
+
'(\\#[-a-z\\d_]*)?$',
|
|
73
|
+
'i',
|
|
74
|
+
);
|
|
75
|
+
const isValid = urlRegex.test(value);
|
|
76
|
+
return !isValid ? formatMessage(validationMessages.isValidURL) : null;
|
|
77
|
+
};
|
|
78
|
+
|
|
51
79
|
/**
|
|
52
80
|
* ObjectBrowserWidget component class.
|
|
53
81
|
* @class ObjectBrowserWidget
|
|
@@ -76,6 +104,7 @@ export class ObjectBrowserWidgetComponent extends Component {
|
|
|
76
104
|
openObjectBrowser: PropTypes.func.isRequired,
|
|
77
105
|
allowExternals: PropTypes.bool,
|
|
78
106
|
placeholder: PropTypes.string,
|
|
107
|
+
onlyFolderishSelectable: PropTypes.bool,
|
|
79
108
|
};
|
|
80
109
|
|
|
81
110
|
/**
|
|
@@ -92,11 +121,13 @@ export class ObjectBrowserWidgetComponent extends Component {
|
|
|
92
121
|
return: 'multiple',
|
|
93
122
|
initialPath: '',
|
|
94
123
|
allowExternals: false,
|
|
124
|
+
onlyFolderishSelectable: false,
|
|
95
125
|
};
|
|
96
126
|
|
|
97
127
|
state = {
|
|
98
128
|
manualLinkInput: '',
|
|
99
129
|
validURL: false,
|
|
130
|
+
errors: [],
|
|
100
131
|
};
|
|
101
132
|
|
|
102
133
|
constructor(props) {
|
|
@@ -105,7 +136,7 @@ export class ObjectBrowserWidgetComponent extends Component {
|
|
|
105
136
|
this.placeholderRef = React.createRef();
|
|
106
137
|
}
|
|
107
138
|
renderLabel(item) {
|
|
108
|
-
//
|
|
139
|
+
// EEA: use linkWithHash if available so the anchor is preserved in the label
|
|
109
140
|
const href = item['linkWithHash'] || item['@id'];
|
|
110
141
|
return (
|
|
111
142
|
<Popup
|
|
@@ -123,7 +154,16 @@ export class ObjectBrowserWidgetComponent extends Component {
|
|
|
123
154
|
}
|
|
124
155
|
trigger={
|
|
125
156
|
<Label>
|
|
126
|
-
<div className="item-title">
|
|
157
|
+
<div className="item-title">
|
|
158
|
+
{includes(config.settings.imageObjects, item['@type']) ? (
|
|
159
|
+
<Image
|
|
160
|
+
className="small ui image"
|
|
161
|
+
src={`${item['@id']}/@@images/image/thumb`}
|
|
162
|
+
/>
|
|
163
|
+
) : (
|
|
164
|
+
item.title
|
|
165
|
+
)}
|
|
166
|
+
</div>
|
|
127
167
|
<div>
|
|
128
168
|
{this.props.mode === 'multiple' && (
|
|
129
169
|
<Icon
|
|
@@ -164,7 +204,6 @@ export class ObjectBrowserWidgetComponent extends Component {
|
|
|
164
204
|
}
|
|
165
205
|
let exists = false;
|
|
166
206
|
let index = -1;
|
|
167
|
-
|
|
168
207
|
value.forEach((_item, _index) => {
|
|
169
208
|
if (flattenToAppURL(_item['@id']) === flattenToAppURL(item['@id'])) {
|
|
170
209
|
exists = true;
|
|
@@ -183,8 +222,8 @@ export class ObjectBrowserWidgetComponent extends Component {
|
|
|
183
222
|
...this.props.selectedItemAttrs,
|
|
184
223
|
// Add the required attributes for the widget to work
|
|
185
224
|
'@id',
|
|
186
|
-
'linkWithHash', // add linkWithHash to the allowed attributes
|
|
187
225
|
'title',
|
|
226
|
+
'linkWithHash', // EEA: preserve anchor hash stored by onSubmitManualLink
|
|
188
227
|
];
|
|
189
228
|
resultantItem = Object.keys(item)
|
|
190
229
|
.filter((key) => allowedItemKeys.includes(key))
|
|
@@ -218,17 +257,25 @@ export class ObjectBrowserWidgetComponent extends Component {
|
|
|
218
257
|
};
|
|
219
258
|
|
|
220
259
|
validateManualLink = (url) => {
|
|
221
|
-
if (this.props.allowExternals) {
|
|
222
|
-
|
|
260
|
+
if (this.props.allowExternals && !url.startsWith('/')) {
|
|
261
|
+
const error = validateExternalUrl({
|
|
262
|
+
value: url,
|
|
263
|
+
formatMessage: this.props.intl.formatMessage,
|
|
264
|
+
});
|
|
265
|
+
if (error && url !== '') {
|
|
266
|
+
this.setState({ errors: [error] });
|
|
267
|
+
} else {
|
|
268
|
+
this.setState({ errors: [] });
|
|
269
|
+
}
|
|
270
|
+
return !Boolean(error);
|
|
223
271
|
} else {
|
|
224
272
|
return isInternalURL(url);
|
|
225
273
|
}
|
|
226
274
|
};
|
|
227
275
|
|
|
228
276
|
/**
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
* @returns {[string, string]} - An array containing the link and hash components of the URL.
|
|
277
|
+
* EEA: splits a URL into its path and hash components.
|
|
278
|
+
* e.g. '/path/to/page#section' → ['/path/to/page', 'section']
|
|
232
279
|
*/
|
|
233
280
|
getHashAndLinkFromUrl = (url) => {
|
|
234
281
|
return url.split('#');
|
|
@@ -237,6 +284,7 @@ export class ObjectBrowserWidgetComponent extends Component {
|
|
|
237
284
|
onSubmitManualLink = () => {
|
|
238
285
|
if (this.validateManualLink(this.state.manualLinkInput)) {
|
|
239
286
|
if (isInternalURL(this.state.manualLinkInput)) {
|
|
287
|
+
// EEA: split off any hash anchor before searching
|
|
240
288
|
const [link, hash] = this.getHashAndLinkFromUrl(
|
|
241
289
|
this.state.manualLinkInput,
|
|
242
290
|
);
|
|
@@ -256,7 +304,7 @@ export class ObjectBrowserWidgetComponent extends Component {
|
|
|
256
304
|
)
|
|
257
305
|
.then((resp) => {
|
|
258
306
|
if (resp.items?.length > 0) {
|
|
259
|
-
// if there is a hash
|
|
307
|
+
// EEA: if there is a hash, store it as linkWithHash on the item
|
|
260
308
|
if (hash) {
|
|
261
309
|
resp.items[0]['linkWithHash'] = `${relative_link}#${hash}`;
|
|
262
310
|
}
|
|
@@ -309,6 +357,9 @@ export class ObjectBrowserWidgetComponent extends Component {
|
|
|
309
357
|
maximumSelectionSize:
|
|
310
358
|
this.props.widgetOptions?.pattern_options?.maximumSelectionSize ||
|
|
311
359
|
this.props.maximumSelectionSize,
|
|
360
|
+
onlyFolderishSelectable:
|
|
361
|
+
this.props.widgetOptions?.pattern_options?.onlyFolderishSelectable ||
|
|
362
|
+
this.props.onlyFolderishSelectable,
|
|
312
363
|
});
|
|
313
364
|
};
|
|
314
365
|
|
|
@@ -349,6 +400,8 @@ export class ObjectBrowserWidgetComponent extends Component {
|
|
|
349
400
|
return (
|
|
350
401
|
<FormFieldWrapper
|
|
351
402
|
{...this.props}
|
|
403
|
+
// At the moment, OBW handles its own errors and validation
|
|
404
|
+
error={this.state.errors}
|
|
352
405
|
className={description ? 'help text' : 'text'}
|
|
353
406
|
>
|
|
354
407
|
<div
|
|
@@ -377,6 +430,7 @@ export class ObjectBrowserWidgetComponent extends Component {
|
|
|
377
430
|
items.length === 0 &&
|
|
378
431
|
this.props.mode !== 'multiple' && (
|
|
379
432
|
<input
|
|
433
|
+
onBlur={this.onSubmitManualLink}
|
|
380
434
|
onKeyDown={this.onKeyDownManualLink}
|
|
381
435
|
onChange={this.onManualLinkInput}
|
|
382
436
|
value={this.state.manualLinkInput}
|
|
@@ -2,24 +2,25 @@ import React, { useEffect } from 'react';
|
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import { compose } from 'redux';
|
|
4
4
|
import { useDispatch, useSelector, shallowEqual } from 'react-redux';
|
|
5
|
-
import
|
|
5
|
+
import uniqBy from 'lodash/uniqBy';
|
|
6
6
|
import { toast } from 'react-toastify';
|
|
7
7
|
import { defineMessages, useIntl } from 'react-intl';
|
|
8
8
|
import { useHistory } from 'react-router-dom';
|
|
9
|
-
import
|
|
10
|
-
import
|
|
9
|
+
import Icon from '@plone/volto/components/theme/Icon/Icon';
|
|
10
|
+
import Toast from '@plone/volto/components/manage/Toast/Toast';
|
|
11
|
+
import FormFieldWrapper from '@plone/volto/components/manage/Widgets/FormFieldWrapper';
|
|
12
|
+
import { flattenToAppURL } from '@plone/volto/helpers/Url/Url';
|
|
11
13
|
import {
|
|
12
|
-
flattenToAppURL,
|
|
13
14
|
getWorkflowOptions,
|
|
14
15
|
getCurrentStateMapping,
|
|
15
|
-
} from '@plone/volto/helpers';
|
|
16
|
+
} from '@plone/volto/helpers//Workflows/Workflows';
|
|
16
17
|
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
|
|
17
18
|
|
|
19
|
+
import { getContent } from '@plone/volto/actions/content/content';
|
|
18
20
|
import {
|
|
19
|
-
getContent,
|
|
20
21
|
getWorkflow,
|
|
21
22
|
transitionWorkflow,
|
|
22
|
-
} from '@plone/volto/actions';
|
|
23
|
+
} from '@plone/volto/actions/workflow/workflow';
|
|
23
24
|
import downSVG from '@plone/volto/icons/down-key.svg';
|
|
24
25
|
import upSVG from '@plone/volto/icons/up-key.svg';
|
|
25
26
|
import checkSVG from '@plone/volto/icons/check.svg';
|
|
@@ -150,8 +151,8 @@ const customSelectStyles = {
|
|
|
150
151
|
color: state.isSelected
|
|
151
152
|
? '#007bc1'
|
|
152
153
|
: state.isFocused
|
|
153
|
-
|
|
154
|
-
|
|
154
|
+
? '#4a4a4a'
|
|
155
|
+
: 'inherit',
|
|
155
156
|
':active': {
|
|
156
157
|
backgroundColor: null,
|
|
157
158
|
},
|
|
@@ -7,8 +7,9 @@ import React, { useEffect } from 'react';
|
|
|
7
7
|
import { useDispatch, useSelector } from 'react-redux';
|
|
8
8
|
|
|
9
9
|
import { useLocation } from 'react-router';
|
|
10
|
-
import { getBaseUrl
|
|
11
|
-
import {
|
|
10
|
+
import { getBaseUrl } from '@plone/volto/helpers/Url/Url';
|
|
11
|
+
import { hasApiExpander } from '@plone/volto/helpers/Utils/Utils';
|
|
12
|
+
import { getBreadcrumbs } from '@plone/volto/actions/breadcrumbs/breadcrumbs';
|
|
12
13
|
import config from '@plone/volto/registry';
|
|
13
14
|
|
|
14
15
|
import EEABreadcrumbs from '@eeacms/volto-eea-design-system/ui/Breadcrumbs/Breadcrumbs';
|
|
@@ -3,14 +3,15 @@
|
|
|
3
3
|
* @module components/theme/Comments/Comments
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import
|
|
13
|
-
import { flattenToAppURL, getBaseUrl
|
|
6
|
+
import { addComment } from '@plone/volto/actions/comments/comments';
|
|
7
|
+
import { deleteComment } from '@plone/volto/actions/comments/comments';
|
|
8
|
+
import { listComments } from '@plone/volto/actions/comments/comments';
|
|
9
|
+
import { listMoreComments } from '@plone/volto/actions/comments/comments';
|
|
10
|
+
import Avatar from '@plone/volto/components/theme/Avatar/Avatar';
|
|
11
|
+
import CommentEditModal from '@plone/volto/components/theme/Comments/CommentEditModal';
|
|
12
|
+
import Form from '@plone/volto/components/manage/Form/Form';
|
|
13
|
+
import { flattenToAppURL, getBaseUrl } from '@plone/volto/helpers/Url/Url';
|
|
14
|
+
import { getColor } from '@plone/volto/helpers/Utils/Utils';
|
|
14
15
|
import PropTypes from 'prop-types';
|
|
15
16
|
import React, { Component } from 'react';
|
|
16
17
|
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
|
@@ -5,7 +5,7 @@ import configureStore from 'redux-mock-store';
|
|
|
5
5
|
import Comments from './Comments';
|
|
6
6
|
import thunk from 'redux-thunk';
|
|
7
7
|
import renderer from 'react-test-renderer';
|
|
8
|
-
import '@testing-library/jest-dom
|
|
8
|
+
import '@testing-library/jest-dom';
|
|
9
9
|
|
|
10
10
|
const middleware = [thunk];
|
|
11
11
|
const mockStore = configureStore(middleware);
|
|
@@ -17,6 +17,31 @@ jest.mock('moment', () =>
|
|
|
17
17
|
})),
|
|
18
18
|
);
|
|
19
19
|
|
|
20
|
+
jest.mock('@plone/volto/components/theme/Avatar/Avatar', () => ({
|
|
21
|
+
__esModule: true,
|
|
22
|
+
default: ({ title }) => <div className="avatar">{title}</div>,
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
jest.mock('@plone/volto/components/theme/Comments/CommentEditModal', () => ({
|
|
26
|
+
__esModule: true,
|
|
27
|
+
default: ({ open }) => (open ? <div className="comment-edit-modal" /> : null),
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
jest.mock('@plone/volto/components/manage/Form/Form', () => ({
|
|
31
|
+
__esModule: true,
|
|
32
|
+
default: ({ onSubmit, submitLabel }) => (
|
|
33
|
+
<form onSubmit={(event) => event.preventDefault()}>
|
|
34
|
+
<button
|
|
35
|
+
type="button"
|
|
36
|
+
aria-label={submitLabel}
|
|
37
|
+
onClick={(event) => onSubmit?.(event)}
|
|
38
|
+
>
|
|
39
|
+
{submitLabel}
|
|
40
|
+
</button>
|
|
41
|
+
</form>
|
|
42
|
+
),
|
|
43
|
+
}));
|
|
44
|
+
|
|
20
45
|
jest.mock('@plone/volto/helpers/Loadable/Loadable');
|
|
21
46
|
beforeAll(
|
|
22
47
|
async () =>
|
|
@@ -97,8 +122,7 @@ describe('Comments', () => {
|
|
|
97
122
|
<Comments {...props} />
|
|
98
123
|
</Provider>,
|
|
99
124
|
);
|
|
100
|
-
|
|
101
|
-
expect(json).toMatchSnapshot();
|
|
125
|
+
expect(component.toJSON()).toBeTruthy();
|
|
102
126
|
});
|
|
103
127
|
|
|
104
128
|
it('renders a comments component withour viewing the comments', () => {
|
|
@@ -174,8 +198,7 @@ describe('Comments', () => {
|
|
|
174
198
|
<Comments {...props} />
|
|
175
199
|
</Provider>,
|
|
176
200
|
);
|
|
177
|
-
|
|
178
|
-
expect(json).toMatchSnapshot();
|
|
201
|
+
expect(component.toJSON()).toBeFalsy();
|
|
179
202
|
});
|
|
180
203
|
|
|
181
204
|
it('renders a comments component without permissions', () => {
|
|
@@ -248,8 +271,7 @@ describe('Comments', () => {
|
|
|
248
271
|
<Comments {...props} />
|
|
249
272
|
</Provider>,
|
|
250
273
|
);
|
|
251
|
-
|
|
252
|
-
expect(json).toMatchSnapshot();
|
|
274
|
+
expect(component.toJSON()).toBeFalsy();
|
|
253
275
|
});
|
|
254
276
|
|
|
255
277
|
it('renders a comments component, fires onClick events on comment and rerenders', () => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { Component } from 'react';
|
|
2
|
-
import
|
|
2
|
+
import UniversalLink from '@plone/volto/components/manage/UniversalLink/UniversalLink';
|
|
3
3
|
import { FormattedMessage } from 'react-intl';
|
|
4
4
|
import { Container } from 'semantic-ui-react';
|
|
5
5
|
import config from '@plone/volto/registry';
|
|
@@ -9,6 +9,11 @@ jest.mock('react-portal', () => ({
|
|
|
9
9
|
Portal: jest.fn(() => <div id="Portal" />),
|
|
10
10
|
}));
|
|
11
11
|
|
|
12
|
+
jest.mock('@plone/volto/components/manage/UniversalLink/UniversalLink', () => ({
|
|
13
|
+
__esModule: true,
|
|
14
|
+
default: ({ href, children }) => <a href={href}>{children}</a>,
|
|
15
|
+
}));
|
|
16
|
+
|
|
12
17
|
const mockStore = configureStore();
|
|
13
18
|
|
|
14
19
|
describe('Contact form', () => {
|
package/src/customizations/volto/components/theme/ContentMetadataTags/ContentMetadataTags.jsx
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import React, { useEffect } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from '@plone/volto/helpers';
|
|
8
|
-
import { getNavroot } from '@plone/volto/actions';
|
|
2
|
+
import { toPublicURL } from '@plone/volto/helpers/Url/Url';
|
|
3
|
+
import Helmet from '@plone/volto/helpers/Helmet/Helmet';
|
|
4
|
+
import { hasApiExpander } from '@plone/volto/helpers/Utils/Utils';
|
|
5
|
+
import { getBaseUrl } from '@plone/volto/helpers/Url/Url';
|
|
6
|
+
import { getNavroot } from '@plone/volto/actions//navroot/navroot';
|
|
9
7
|
import config from '@plone/volto/registry';
|
|
10
8
|
import { useDispatch, useSelector } from 'react-redux';
|
|
11
9
|
|
|
@@ -5,8 +5,8 @@ import {
|
|
|
5
5
|
When,
|
|
6
6
|
Recurrence,
|
|
7
7
|
} from '@plone/volto/components/theme/View/EventDatesInfo';
|
|
8
|
-
import
|
|
9
|
-
import { expandToBackendURL } from '@plone/volto/helpers';
|
|
8
|
+
import Icon from '@plone/volto/components/theme/Icon/Icon';
|
|
9
|
+
import { expandToBackendURL } from '@plone/volto/helpers/Url/Url';
|
|
10
10
|
|
|
11
11
|
import calendarSVG from '@plone/volto/icons/calendar.svg';
|
|
12
12
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import React from 'react';
|
|
7
7
|
import { useSelector, shallowEqual } from 'react-redux';
|
|
8
|
-
import { flattenToAppURL } from '@plone/volto/helpers';
|
|
8
|
+
import { flattenToAppURL } from '@plone/volto/helpers/Url/Url';
|
|
9
9
|
import EEAFooter from '@eeacms/volto-eea-design-system/ui/Footer/Footer';
|
|
10
10
|
import config from '@plone/volto/registry';
|
|
11
11
|
import isArray from 'lodash/isArray';
|
|
@@ -8,22 +8,24 @@ import { Dropdown, Image } from 'semantic-ui-react';
|
|
|
8
8
|
import { connect, useDispatch, useSelector } from 'react-redux';
|
|
9
9
|
|
|
10
10
|
import { withRouter } from 'react-router-dom';
|
|
11
|
-
import
|
|
12
|
-
import { getBaseUrl
|
|
13
|
-
import {
|
|
11
|
+
import UniversalLink from '@plone/volto/components/manage/UniversalLink/UniversalLink';
|
|
12
|
+
import { getBaseUrl } from '@plone/volto/helpers/Url/Url';
|
|
13
|
+
import { hasApiExpander } from '@plone/volto/helpers/Utils/Utils';
|
|
14
|
+
import { getNavigation } from '@plone/volto/actions/navigation/navigation';
|
|
14
15
|
import { getNavigationSettings } from '@eeacms/volto-eea-website-theme/actions';
|
|
15
|
-
import
|
|
16
|
+
import Header from '@eeacms/volto-eea-design-system/ui/Header/Header';
|
|
16
17
|
import EEALogo from '@eeacms/volto-eea-website-theme/components/theme/Logo';
|
|
17
18
|
import { usePrevious } from '@eeacms/volto-eea-design-system/helpers';
|
|
18
19
|
import eeaFlag from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/images/Header/eea.png';
|
|
19
20
|
|
|
20
21
|
import config from '@plone/volto/registry';
|
|
21
|
-
import { compose } from '
|
|
22
|
+
import { compose } from 'redux';
|
|
22
23
|
|
|
23
24
|
import cx from 'classnames';
|
|
24
25
|
import loadable from '@loadable/component';
|
|
25
26
|
|
|
26
27
|
const LazyLanguageSwitcher = loadable(() => import('./LanguageSwitcher'));
|
|
28
|
+
const EMPTY_NAVIGATION_SETTINGS = {};
|
|
27
29
|
|
|
28
30
|
function removeTrailingSlash(path) {
|
|
29
31
|
return path.replace(/\/+$/, '');
|
|
@@ -60,9 +62,9 @@ const EEAHeader = ({ pathname, token, items, history, subsite }) => {
|
|
|
60
62
|
const width = useSelector((state) => state.screen?.width);
|
|
61
63
|
const dispatch = useDispatch();
|
|
62
64
|
const previousToken = usePrevious(token);
|
|
63
|
-
const navigationSettings =
|
|
64
|
-
(state) => state.navigationSettings?.settings ||
|
|
65
|
-
|
|
65
|
+
const navigationSettings =
|
|
66
|
+
useSelector((state) => state.navigationSettings?.settings) ||
|
|
67
|
+
EMPTY_NAVIGATION_SETTINGS;
|
|
66
68
|
const updateRequest = useSelector((state) => state.content.update);
|
|
67
69
|
|
|
68
70
|
// Combine navigation settings from backend with config fallback
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { render, fireEvent, getByText } from '@testing-library/react';
|
|
3
|
-
import '@testing-library/jest-dom
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
4
4
|
import configureStore from 'redux-mock-store';
|
|
5
5
|
import { Router } from 'react-router-dom';
|
|
6
6
|
import { createMemoryHistory } from 'history';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { useSelector } from 'react-redux';
|
|
3
3
|
import { Dropdown, Image } from 'semantic-ui-react';
|
|
4
|
-
import { flattenToAppURL } from '@plone/volto/helpers';
|
|
5
|
-
import
|
|
4
|
+
import { flattenToAppURL } from '@plone/volto/helpers/Url/Url';
|
|
5
|
+
import find from 'lodash/find';
|
|
6
6
|
import globeIcon from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/images/Header/global-line.svg';
|
|
7
7
|
import config from '@plone/volto/registry';
|
|
8
|
-
import
|
|
8
|
+
import Header from '@eeacms/volto-eea-design-system/ui/Header/Header';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* LanguageSwitcher component.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import cx from 'classnames';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import { flattenScales
|
|
3
|
+
import { flattenScales } from '@plone/volto/helpers/Url/Url';
|
|
4
|
+
import { flattenToAppURL } from '@plone/volto/helpers/Url/Url';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Determines the image scale name based on the provided data.
|
|
@@ -42,10 +43,10 @@ export default function Image({
|
|
|
42
43
|
// TypeScript hints for editor autocomplete :)
|
|
43
44
|
/** @type {React.ImgHTMLAttributes<HTMLImageElement>} */
|
|
44
45
|
const attrs = {};
|
|
46
|
+
attrs.className = cx(className, { responsive }) || undefined;
|
|
45
47
|
|
|
46
48
|
if (!item && src) {
|
|
47
49
|
attrs.src = src;
|
|
48
|
-
attrs.className = cx(className, { responsive });
|
|
49
50
|
} else {
|
|
50
51
|
const isFromRealObject = !item.image_scales;
|
|
51
52
|
const imageFieldWithDefault = imageField || item.image_field || 'image';
|
|
@@ -68,7 +69,6 @@ export default function Image({
|
|
|
68
69
|
attrs.src = `${relativeBasePath}/${image.download}`;
|
|
69
70
|
attrs.width = image.width;
|
|
70
71
|
attrs.height = image.height;
|
|
71
|
-
attrs.className = cx(className, { responsive });
|
|
72
72
|
|
|
73
73
|
const original = {
|
|
74
74
|
download: `${image.download}`,
|
|
@@ -112,6 +112,7 @@ export default function Image({
|
|
|
112
112
|
attrs.fetchpriority = 'high';
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
115
116
|
return <img {...attrs} alt={alt} {...imageProps} />;
|
|
116
117
|
}
|
|
117
118
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { useEffect } from 'react';
|
|
6
6
|
import { useLocation, Link, useHistory } from 'react-router-dom';
|
|
7
|
-
import { getBaseUrl } from '@plone/volto/helpers';
|
|
7
|
+
import { getBaseUrl } from '@plone/volto/helpers/Url/Url';
|
|
8
8
|
import { Container, Button } from 'semantic-ui-react';
|
|
9
9
|
|
|
10
10
|
import { FormattedMessage } from 'react-intl';
|
|
@@ -13,11 +13,12 @@ import {
|
|
|
13
13
|
Label,
|
|
14
14
|
} from 'semantic-ui-react';
|
|
15
15
|
import config from '@plone/volto/registry';
|
|
16
|
-
import { getSchema } from '@plone/volto/actions';
|
|
16
|
+
import { getSchema } from '@plone/volto/actions/schema/schema';
|
|
17
17
|
import { getWidget } from '@plone/volto/helpers/Widget/utils';
|
|
18
|
-
import
|
|
18
|
+
import RenderBlocks from '@plone/volto/components/theme/View/RenderBlocks';
|
|
19
19
|
|
|
20
|
-
import { hasBlocksData
|
|
20
|
+
import { hasBlocksData } from '@plone/volto/helpers/Blocks/Blocks';
|
|
21
|
+
import { getBaseUrl } from '@plone/volto/helpers/Url/Url';
|
|
21
22
|
import { useDispatch, shallowEqual, useSelector } from 'react-redux';
|
|
22
23
|
|
|
23
24
|
import isEqual from 'lodash/isEqual';
|
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
import React from 'react';
|
|
7
7
|
import PropTypes from 'prop-types';
|
|
8
|
-
import { hasBlocksData
|
|
8
|
+
import { hasBlocksData } from '@plone/volto/helpers/Blocks/Blocks';
|
|
9
|
+
import { flattenHTMLToAppURL } from '@plone/volto/helpers/Url/Url';
|
|
9
10
|
import { Image } from 'semantic-ui-react';
|
|
10
11
|
import RenderBlocks from '@plone/volto/components/theme/View/RenderBlocks';
|
|
11
|
-
import
|
|
12
|
+
import EventDetails from '@plone/volto/components/theme/EventDetails/EventDetails';
|
|
12
13
|
import './style.less';
|
|
13
14
|
|
|
14
15
|
const EventTextfieldView = ({ content }) => (
|
|
@@ -7,7 +7,7 @@ import React, { Component } from 'react';
|
|
|
7
7
|
import PropTypes from 'prop-types';
|
|
8
8
|
import Helmet from '@plone/volto/helpers/Helmet/Helmet';
|
|
9
9
|
import serialize from 'serialize-javascript';
|
|
10
|
-
import
|
|
10
|
+
import join from 'lodash/join';
|
|
11
11
|
import BodyClass from '@plone/volto/helpers/BodyClass/BodyClass';
|
|
12
12
|
import { runtimeConfig } from '@plone/volto/runtime_config';
|
|
13
13
|
import config from '@plone/volto/registry';
|
|
@@ -104,6 +104,13 @@ class Html extends Component {
|
|
|
104
104
|
{head.link.toComponent()}
|
|
105
105
|
{head.script.toComponent()}
|
|
106
106
|
|
|
107
|
+
{config.settings.cssLayers && (
|
|
108
|
+
// Load the CSS layers from config, if any
|
|
109
|
+
<style>{`@layer ${config.settings.cssLayers.join(', ')};`}</style>
|
|
110
|
+
)}
|
|
111
|
+
|
|
112
|
+
{head.style.toComponent()}
|
|
113
|
+
|
|
107
114
|
{React.createElement('script', {
|
|
108
115
|
nonce: nonce,
|
|
109
116
|
dangerouslySetInnerHTML: {
|
|
@@ -118,6 +125,9 @@ class Html extends Component {
|
|
|
118
125
|
...(publicURL && {
|
|
119
126
|
publicURL,
|
|
120
127
|
}),
|
|
128
|
+
...(process.env.SITE_DEFAULT_LANGUAGE && {
|
|
129
|
+
defaultLanguage: process.env.SITE_DEFAULT_LANGUAGE,
|
|
130
|
+
}),
|
|
121
131
|
},
|
|
122
132
|
{ space: 2 },
|
|
123
133
|
)};`,
|
|
@@ -134,7 +144,7 @@ class Html extends Component {
|
|
|
134
144
|
<link rel="manifest" href="/site.webmanifest" />
|
|
135
145
|
<meta name="generator" content="Plone 6 - https://plone.org" />
|
|
136
146
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
137
|
-
<meta name="
|
|
147
|
+
<meta name="mobile-web-app-capable" content="yes" />
|
|
138
148
|
{process.env.NODE_ENV === 'production' && criticalCss && (
|
|
139
149
|
<style
|
|
140
150
|
dangerouslySetInnerHTML={{ __html: this.props.criticalCss }}
|
|
@@ -148,8 +158,8 @@ class Html extends Component {
|
|
|
148
158
|
rel: !criticalCss
|
|
149
159
|
? elem.props.rel
|
|
150
160
|
: elem.props.as === 'style'
|
|
151
|
-
|
|
152
|
-
|
|
161
|
+
? 'prefetch'
|
|
162
|
+
: elem.props.rel,
|
|
153
163
|
}),
|
|
154
164
|
)}
|
|
155
165
|
{/* Styles in development are loaded with Webpack's style-loader, in production,
|
|
@@ -162,8 +172,8 @@ class Html extends Component {
|
|
|
162
172
|
__html: CRITICAL_CSS_TEMPLATE,
|
|
163
173
|
}}
|
|
164
174
|
></script>
|
|
165
|
-
{extractor.getStyleElements().map((elem) => (
|
|
166
|
-
<noscript>
|
|
175
|
+
{extractor.getStyleElements().map((elem, index) => (
|
|
176
|
+
<noscript key={elem.key ?? `noscript-style-${index}`}>
|
|
167
177
|
{React.cloneElement(elem, {
|
|
168
178
|
rel: 'stylesheet',
|
|
169
179
|
crossOrigin:
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
Customized for CSP support
|
|
1
|
+
Customized for CSP support: adds a `nonce` prop that is applied to both inline
|
|
2
|
+
`<script>` tags (`window.env` and `window.__data`) and to all extracted script
|
|
3
|
+
elements, enabling a strict Content-Security-Policy `script-src 'nonce-...'`
|
|
4
|
+
header generated by the paired `server.jsx` customization.
|
|
5
|
+
|
|
6
|
+
Rebased against Volto 18 — also includes cssLayers support, head.style.toComponent(),
|
|
7
|
+
SITE_DEFAULT_LANGUAGE in window.env, and mobile-web-app-capable meta tag.
|