@eeacms/volto-eea-website-theme 3.19.0 → 4.0.0
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 +28 -0
- package/DEVELOP.md +19 -17
- package/README.md +27 -15
- 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/History/History.jsx +58 -52
- 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 +10 -10
- 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
|
@@ -2,8 +2,9 @@ import React, { useEffect } from 'react';
|
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import { Segment } from 'semantic-ui-react';
|
|
4
4
|
import { FormattedMessage, injectIntl } from 'react-intl';
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import Icon from '@plone/volto/components/theme/Icon/Icon';
|
|
6
|
+
import BlockDataForm from '@plone/volto/components/manage/Form/BlockDataForm';
|
|
7
|
+
import { flattenToAppURL } from '@plone/volto/helpers/Url/Url';
|
|
7
8
|
import { LeadImageSchema } from './schema';
|
|
8
9
|
import imageSVG from '@plone/volto/icons/image.svg';
|
|
9
10
|
|
|
@@ -48,6 +49,7 @@ const LeadImageSidebar = ({ properties, data, block, onChangeBlock, intl }) => {
|
|
|
48
49
|
<>
|
|
49
50
|
<Segment className="sidebar-metadata-container" secondary>
|
|
50
51
|
{properties.image.filename}
|
|
52
|
+
{/* eslint-disable-next-line no-restricted-syntax */}
|
|
51
53
|
<img
|
|
52
54
|
src={
|
|
53
55
|
properties.image.data
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
|
|
6
6
|
import React from 'react';
|
|
7
7
|
import PropTypes from 'prop-types';
|
|
8
|
-
import
|
|
8
|
+
import UniversalLink from '@plone/volto/components/manage/UniversalLink/UniversalLink';
|
|
9
9
|
import cx from 'classnames';
|
|
10
10
|
import config from '@plone/volto/registry';
|
|
11
|
-
import
|
|
11
|
+
import Copyright from '@eeacms/volto-eea-design-system/ui/Copyright/Copyright';
|
|
12
12
|
import { Icon } from 'semantic-ui-react';
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -8,7 +8,7 @@ import { FormattedMessage, injectIntl } from 'react-intl';
|
|
|
8
8
|
import { Dropdown, Table, Checkbox } from 'semantic-ui-react';
|
|
9
9
|
import trashSVG from '@plone/volto/icons/delete.svg';
|
|
10
10
|
import ploneSVG from '@plone/volto/icons/plone.svg';
|
|
11
|
-
import
|
|
11
|
+
import Icon from '@plone/volto/components/theme/Icon/Icon';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* UsersControlpanelGroups class.
|
package/src/customizations/volto/components/manage/Controlpanels/Groups/RenderGroups.test.jsx
CHANGED
|
@@ -1,58 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RenderGroups — unit tests
|
|
3
|
+
*
|
|
4
|
+
* Volto 17 / 18 dual-support notes
|
|
5
|
+
* ---------------------------------
|
|
6
|
+
* The EEA change is a single-line tweak on line 80:
|
|
7
|
+
* `{this.props.group.title || this.props.group.groupname}`
|
|
8
|
+
* instead of showing only `groupname`.
|
|
9
|
+
*
|
|
10
|
+
* V17 and V18 upstream are byte-for-byte identical, so this shadow is
|
|
11
|
+
* compatible with both versions without any rebase.
|
|
12
|
+
*
|
|
13
|
+
* The `@plone/volto/components` barrel is mocked to prevent the chain:
|
|
14
|
+
* TranslationObject.jsx → store.js → @root/reducers
|
|
15
|
+
* that cannot be resolved in this Jest environment. The previous snapshot
|
|
16
|
+
* test used `react-test-renderer` which produced a snapshot that contained
|
|
17
|
+
* the full icon SVG tree; the new tests use @testing-library/react and
|
|
18
|
+
* assert on the actual EEA-specific behavior instead.
|
|
19
|
+
*/
|
|
20
|
+
|
|
1
21
|
import React from 'react';
|
|
2
|
-
import
|
|
22
|
+
import { render, screen } from '@testing-library/react';
|
|
23
|
+
import '@testing-library/jest-dom';
|
|
3
24
|
import configureStore from 'redux-mock-store';
|
|
4
25
|
import { Provider } from 'react-intl-redux';
|
|
5
26
|
|
|
6
27
|
import RenderGroups from './RenderGroups';
|
|
7
28
|
|
|
29
|
+
jest.mock('@plone/volto/components', () => ({
|
|
30
|
+
// RenderGroups.jsx uses only Icon from the barrel
|
|
31
|
+
Icon: () => null,
|
|
32
|
+
}));
|
|
33
|
+
|
|
8
34
|
const mockStore = configureStore();
|
|
9
35
|
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
groupname: 'Administrators',
|
|
15
|
-
id: 'Administrators',
|
|
16
|
-
title: 'Administrators',
|
|
17
|
-
roles: ['Manager'],
|
|
18
|
-
};
|
|
36
|
+
const makeStore = () =>
|
|
37
|
+
mockStore({
|
|
38
|
+
intl: { locale: 'en', messages: {} },
|
|
39
|
+
});
|
|
19
40
|
|
|
20
41
|
const testRoles = [
|
|
21
|
-
{
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
id: 'Member',
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
'@id': 'http://localhost:8080/Plone/@roles/Reader',
|
|
28
|
-
'@type': 'role',
|
|
29
|
-
id: 'Reader',
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
'@id': 'http://localhost:8080/Plone/@roles/Manager',
|
|
33
|
-
'@type': 'role',
|
|
34
|
-
id: 'Manager',
|
|
35
|
-
},
|
|
42
|
+
{ '@id': 'http://localhost:8080/Plone/@roles/Member', id: 'Member' },
|
|
43
|
+
{ '@id': 'http://localhost:8080/Plone/@roles/Reader', id: 'Reader' },
|
|
44
|
+
{ '@id': 'http://localhost:8080/Plone/@roles/Manager', id: 'Manager' },
|
|
36
45
|
];
|
|
37
46
|
|
|
38
|
-
describe('
|
|
39
|
-
it('
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
describe('RenderGroups — EEA: title || groupname display', () => {
|
|
48
|
+
it('shows the group title when available (EEA change)', () => {
|
|
49
|
+
const group = {
|
|
50
|
+
groupname: 'editors',
|
|
51
|
+
title: 'Site Editors',
|
|
52
|
+
roles: [],
|
|
53
|
+
};
|
|
54
|
+
render(
|
|
55
|
+
<Provider store={makeStore()}>
|
|
56
|
+
<RenderGroups group={group} roles={testRoles} onDelete={() => {}} />
|
|
57
|
+
</Provider>,
|
|
58
|
+
);
|
|
59
|
+
// EEA customization: title takes precedence over groupname
|
|
60
|
+
expect(screen.getByText('Site Editors')).toBeInTheDocument();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('falls back to groupname when title is absent', () => {
|
|
64
|
+
const group = {
|
|
65
|
+
groupname: 'Administrators',
|
|
66
|
+
title: '',
|
|
67
|
+
roles: ['Manager'],
|
|
68
|
+
};
|
|
69
|
+
render(
|
|
70
|
+
<Provider store={makeStore()}>
|
|
71
|
+
<RenderGroups group={group} roles={testRoles} onDelete={() => {}} />
|
|
72
|
+
</Provider>,
|
|
73
|
+
);
|
|
74
|
+
expect(screen.getByText('Administrators')).toBeInTheDocument();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('falls back to groupname when title is undefined', () => {
|
|
78
|
+
const group = {
|
|
79
|
+
groupname: 'Reviewers',
|
|
80
|
+
// no title key at all
|
|
81
|
+
roles: [],
|
|
82
|
+
};
|
|
83
|
+
render(
|
|
84
|
+
<Provider store={makeStore()}>
|
|
85
|
+
<RenderGroups group={group} roles={testRoles} onDelete={() => {}} />
|
|
86
|
+
</Provider>,
|
|
87
|
+
);
|
|
88
|
+
expect(screen.getByText('Reviewers')).toBeInTheDocument();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('renders a checkbox for each role', () => {
|
|
92
|
+
const group = {
|
|
93
|
+
groupname: 'Administrators',
|
|
94
|
+
title: 'Administrators',
|
|
95
|
+
roles: ['Manager'],
|
|
96
|
+
};
|
|
97
|
+
const { container } = render(
|
|
98
|
+
<Provider store={makeStore()}>
|
|
99
|
+
<RenderGroups group={group} roles={testRoles} onDelete={() => {}} />
|
|
100
|
+
</Provider>,
|
|
101
|
+
);
|
|
102
|
+
// One checkbox per role
|
|
103
|
+
const checkboxes = container.querySelectorAll('input[type="checkbox"]');
|
|
104
|
+
expect(checkboxes).toHaveLength(testRoles.length);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('checks the checkbox for roles the group already has', () => {
|
|
108
|
+
const group = {
|
|
109
|
+
groupname: 'managers',
|
|
110
|
+
title: 'Managers',
|
|
111
|
+
roles: ['Manager'],
|
|
112
|
+
};
|
|
113
|
+
const { container } = render(
|
|
114
|
+
<Provider store={makeStore()}>
|
|
115
|
+
<RenderGroups group={group} roles={testRoles} onDelete={() => {}} />
|
|
53
116
|
</Provider>,
|
|
54
117
|
);
|
|
55
|
-
const
|
|
56
|
-
|
|
118
|
+
const checkboxes = container.querySelectorAll('input[type="checkbox"]');
|
|
119
|
+
// Member → unchecked, Reader → unchecked, Manager → checked
|
|
120
|
+
expect(checkboxes[0]).not.toBeChecked();
|
|
121
|
+
expect(checkboxes[1]).not.toBeChecked();
|
|
122
|
+
expect(checkboxes[2]).toBeChecked();
|
|
57
123
|
});
|
|
58
124
|
});
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React from 'react';
|
|
7
|
-
import
|
|
7
|
+
import join from 'lodash/join';
|
|
8
|
+
import map from 'lodash/map';
|
|
8
9
|
import PropTypes from 'prop-types';
|
|
9
10
|
import { Grid } from 'semantic-ui-react';
|
|
10
11
|
import ReactDOMServer from 'react-dom/server';
|
|
@@ -13,9 +14,9 @@ import { createBrowserHistory } from 'history';
|
|
|
13
14
|
import { ConnectedRouter } from 'connected-react-router';
|
|
14
15
|
import { useSelector, useStore } from 'react-redux';
|
|
15
16
|
import config from '@plone/volto/registry';
|
|
16
|
-
import
|
|
17
|
+
import Api from '@plone/volto/helpers/Api/Api';
|
|
17
18
|
import configureStore from '@plone/volto/store';
|
|
18
|
-
import
|
|
19
|
+
import RenderBlocks from '@plone/volto/components/theme/View/RenderBlocks';
|
|
19
20
|
import { serializeNodes } from '@plone/volto-slate/editor/render';
|
|
20
21
|
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
|
|
21
22
|
|
|
@@ -5,14 +5,15 @@ import { compose } from 'redux';
|
|
|
5
5
|
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
|
|
6
6
|
|
|
7
7
|
import jwtDecode from 'jwt-decode';
|
|
8
|
+
import { getSchema } from '@plone/volto/actions/schema/schema';
|
|
9
|
+
import { getUser } from '@plone/volto/actions/users/users';
|
|
8
10
|
import {
|
|
9
|
-
getSchema,
|
|
10
|
-
getUser,
|
|
11
11
|
updateContent,
|
|
12
12
|
getContent,
|
|
13
|
-
} from '@plone/volto/actions';
|
|
14
|
-
import { getLayoutFieldname } from '@plone/volto/helpers';
|
|
15
|
-
import
|
|
13
|
+
} from '@plone/volto/actions/content/content';
|
|
14
|
+
import { getLayoutFieldname } from '@plone/volto/helpers/Content/Content';
|
|
15
|
+
import Icon from '@plone/volto/components/theme/Icon/Icon';
|
|
16
|
+
import FormFieldWrapper from '@plone/volto/components/manage/Widgets/FormFieldWrapper';
|
|
16
17
|
import { defineMessages, injectIntl } from 'react-intl';
|
|
17
18
|
import config from '@plone/volto/registry';
|
|
18
19
|
|
|
@@ -101,8 +102,8 @@ const customSelectStyles = {
|
|
|
101
102
|
color: state.isSelected
|
|
102
103
|
? '#007bc1'
|
|
103
104
|
: state.isFocused
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
? '#4a4a4a'
|
|
106
|
+
: 'inherit',
|
|
106
107
|
':active': {
|
|
107
108
|
backgroundColor: null,
|
|
108
109
|
},
|
|
@@ -42,6 +42,14 @@ import config from '@plone/volto/registry';
|
|
|
42
42
|
|
|
43
43
|
import backSVG from '@plone/volto/icons/back.svg';
|
|
44
44
|
|
|
45
|
+
const getPathname = (url) => {
|
|
46
|
+
try {
|
|
47
|
+
return new URL(url).pathname;
|
|
48
|
+
} catch {
|
|
49
|
+
return typeof url === 'string' && url.startsWith('/') ? url : null;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
45
53
|
const messages = defineMessages({
|
|
46
54
|
back: {
|
|
47
55
|
id: 'Back',
|
|
@@ -213,32 +221,31 @@ class History extends Component {
|
|
|
213
221
|
defaultMessage="You can view the history of your item below."
|
|
214
222
|
/>
|
|
215
223
|
</Segment>
|
|
216
|
-
{
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
<Message
|
|
221
|
-
<
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
)}
|
|
224
|
+
{(() => {
|
|
225
|
+
const copiedToPath = getPathname(this.props.content?.copied_to);
|
|
226
|
+
return (
|
|
227
|
+
copiedToPath && (
|
|
228
|
+
<Message info icon attached="top">
|
|
229
|
+
<Icon name="arrow right" />
|
|
230
|
+
<Message.Content>
|
|
231
|
+
<Message.Header>
|
|
232
|
+
<FormattedMessage {...messages.newerVersionAvailable} />
|
|
233
|
+
</Message.Header>
|
|
234
|
+
<FormattedMessage
|
|
235
|
+
{...messages.thereIsNewerVersionAt}
|
|
236
|
+
values={{
|
|
237
|
+
link: (
|
|
238
|
+
<a href={`${copiedToPath}/historyview`}>
|
|
239
|
+
{copiedToPath.split('/').pop() || 'newer version'}
|
|
240
|
+
</a>
|
|
241
|
+
),
|
|
242
|
+
}}
|
|
243
|
+
/>
|
|
244
|
+
</Message.Content>
|
|
245
|
+
</Message>
|
|
246
|
+
)
|
|
247
|
+
);
|
|
248
|
+
})()}
|
|
242
249
|
<Table
|
|
243
250
|
selectable
|
|
244
251
|
compact
|
|
@@ -371,32 +378,31 @@ class History extends Component {
|
|
|
371
378
|
))}
|
|
372
379
|
</Table.Body>
|
|
373
380
|
</Table>
|
|
374
|
-
{
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
<Message
|
|
379
|
-
<
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
)}
|
|
381
|
+
{(() => {
|
|
382
|
+
const copiedFromPath = getPathname(this.props.content?.copied_from);
|
|
383
|
+
return (
|
|
384
|
+
copiedFromPath && (
|
|
385
|
+
<Message warning icon attached="bottom">
|
|
386
|
+
<Icon name="arrow left" />
|
|
387
|
+
<Message.Content>
|
|
388
|
+
<Message.Header>
|
|
389
|
+
<FormattedMessage {...messages.olderVersionAvailable} />
|
|
390
|
+
</Message.Header>
|
|
391
|
+
<FormattedMessage
|
|
392
|
+
{...messages.thereIsOlderVersionAt}
|
|
393
|
+
values={{
|
|
394
|
+
link: (
|
|
395
|
+
<a href={`${copiedFromPath}/historyview`}>
|
|
396
|
+
{copiedFromPath.split('/').pop() || 'older version'}
|
|
397
|
+
</a>
|
|
398
|
+
),
|
|
399
|
+
}}
|
|
400
|
+
/>
|
|
401
|
+
</Message.Content>
|
|
402
|
+
</Message>
|
|
403
|
+
)
|
|
404
|
+
);
|
|
405
|
+
})()}
|
|
400
406
|
</Segment.Group>
|
|
401
407
|
{this.state.isClient &&
|
|
402
408
|
createPortal(
|
|
@@ -7,7 +7,7 @@ import { connect } from 'react-redux';
|
|
|
7
7
|
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
|
8
8
|
import { Input, Segment, Breadcrumb } from 'semantic-ui-react';
|
|
9
9
|
|
|
10
|
-
import
|
|
10
|
+
import join from 'lodash/join';
|
|
11
11
|
|
|
12
12
|
// These absolute imports (without using the corresponding centralized index.js) are required
|
|
13
13
|
// to cut circular import problems, this file should never use them. This is because of
|
|
@@ -25,6 +25,11 @@ import linkSVG from '@plone/volto/icons/link.svg';
|
|
|
25
25
|
import homeSVG from '@plone/volto/icons/home.svg';
|
|
26
26
|
|
|
27
27
|
import ObjectBrowserNav from '@plone/volto/components/manage/Sidebar/ObjectBrowserNav';
|
|
28
|
+
import {
|
|
29
|
+
isItemAlreadySelected,
|
|
30
|
+
isSelectableObjectBrowserItem,
|
|
31
|
+
shouldCloseAfterObjectBrowserSelection,
|
|
32
|
+
} from './objectBrowserSelection';
|
|
28
33
|
|
|
29
34
|
const messages = defineMessages({
|
|
30
35
|
SearchInputPlaceholder: {
|
|
@@ -74,6 +79,7 @@ class ObjectBrowserBody extends Component {
|
|
|
74
79
|
maximumSelectionSize: PropTypes.number,
|
|
75
80
|
contextURL: PropTypes.string,
|
|
76
81
|
searchableTypes: PropTypes.arrayOf(PropTypes.string),
|
|
82
|
+
onlyFolderishSelectable: PropTypes.bool,
|
|
77
83
|
};
|
|
78
84
|
|
|
79
85
|
/**
|
|
@@ -89,6 +95,7 @@ class ObjectBrowserBody extends Component {
|
|
|
89
95
|
selectableTypes: [],
|
|
90
96
|
searchableTypes: null,
|
|
91
97
|
maximumSelectionSize: null,
|
|
98
|
+
onlyFolderishSelectable: false,
|
|
92
99
|
};
|
|
93
100
|
|
|
94
101
|
/**
|
|
@@ -106,27 +113,27 @@ class ObjectBrowserBody extends Component {
|
|
|
106
113
|
this.props.mode === 'multiple'
|
|
107
114
|
? '/'
|
|
108
115
|
: this.props.mode === 'image' && this.props.data?.url
|
|
109
|
-
|
|
110
|
-
|
|
116
|
+
? getParentURL(this.props.data.url)
|
|
117
|
+
: '/',
|
|
111
118
|
currentLinkFolder:
|
|
112
119
|
this.props.mode === 'multiple'
|
|
113
120
|
? '/'
|
|
114
121
|
: this.props.mode === 'link' && this.props.data?.href
|
|
115
|
-
|
|
116
|
-
|
|
122
|
+
? getParentURL(this.props.data.href)
|
|
123
|
+
: '/',
|
|
117
124
|
parentFolder: '',
|
|
118
125
|
selectedImage:
|
|
119
126
|
this.props.mode === 'multiple'
|
|
120
127
|
? ''
|
|
121
128
|
: this.props.mode === 'image' && this.props.data?.url
|
|
122
|
-
|
|
123
|
-
|
|
129
|
+
? flattenToAppURL(this.props.data.url)
|
|
130
|
+
: '',
|
|
124
131
|
selectedHref:
|
|
125
132
|
this.props.mode === 'multiple'
|
|
126
133
|
? ''
|
|
127
134
|
: this.props.mode === 'link' && this.props.data?.href
|
|
128
|
-
|
|
129
|
-
|
|
135
|
+
? flattenToAppURL(this.props.data.href)
|
|
136
|
+
: '',
|
|
130
137
|
showSearchInput: false,
|
|
131
138
|
// In image mode, the searchable types default to the image types which
|
|
132
139
|
// can be overridden with the property if specified.
|
|
@@ -152,8 +159,8 @@ class ObjectBrowserBody extends Component {
|
|
|
152
159
|
mode === 'multiple'
|
|
153
160
|
? ''
|
|
154
161
|
: mode === 'image'
|
|
155
|
-
|
|
156
|
-
|
|
162
|
+
? this.state.selectedImage
|
|
163
|
+
: this.state.selectedHref;
|
|
157
164
|
if (currentSelected && isInternalURL(currentSelected)) {
|
|
158
165
|
this.props.searchContent(
|
|
159
166
|
getParentURL(currentSelected),
|
|
@@ -299,9 +306,15 @@ class ObjectBrowserBody extends Component {
|
|
|
299
306
|
};
|
|
300
307
|
|
|
301
308
|
isSelectable = (item) => {
|
|
302
|
-
return
|
|
303
|
-
|
|
304
|
-
:
|
|
309
|
+
return isSelectableObjectBrowserItem({
|
|
310
|
+
item,
|
|
311
|
+
selectableTypes: this.props.selectableTypes,
|
|
312
|
+
onlyFolderishSelectable: this.props.onlyFolderishSelectable,
|
|
313
|
+
maximumSelectionSize: this.props.maximumSelectionSize,
|
|
314
|
+
data: this.props.data,
|
|
315
|
+
mode: this.props.mode,
|
|
316
|
+
normalize: flattenToAppURL,
|
|
317
|
+
});
|
|
305
318
|
};
|
|
306
319
|
|
|
307
320
|
handleClickOnItem = (item) => {
|
|
@@ -318,15 +331,23 @@ class ObjectBrowserBody extends Component {
|
|
|
318
331
|
!this.props.maximumSelectionSize ||
|
|
319
332
|
this.props.mode === 'multiple' ||
|
|
320
333
|
!this.props.data ||
|
|
321
|
-
this.props.data.length
|
|
334
|
+
this.props.data.length <= this.props.maximumSelectionSize
|
|
322
335
|
) {
|
|
323
|
-
|
|
324
|
-
|
|
336
|
+
const isDeselecting =
|
|
337
|
+
this.props.mode === 'multiple' &&
|
|
338
|
+
isItemAlreadySelected({
|
|
339
|
+
data: this.props.data,
|
|
340
|
+
item,
|
|
341
|
+
normalize: flattenToAppURL,
|
|
342
|
+
});
|
|
325
343
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
344
|
+
this.onSelectItem(item);
|
|
345
|
+
const stopSelecting = shouldCloseAfterObjectBrowserSelection({
|
|
346
|
+
mode: this.props.mode,
|
|
347
|
+
maximumSelectionSize: this.props.maximumSelectionSize,
|
|
348
|
+
currentLength: this.props.data ? this.props.data.length : 0,
|
|
349
|
+
isDeselecting,
|
|
350
|
+
});
|
|
330
351
|
|
|
331
352
|
if (stopSelecting) {
|
|
332
353
|
this.props.closeObjectBrowser();
|
|
@@ -3,7 +3,8 @@ import { Button, Segment, Popup } from 'semantic-ui-react';
|
|
|
3
3
|
import { useIntl, defineMessages } from 'react-intl';
|
|
4
4
|
import cx from 'classnames';
|
|
5
5
|
import Icon from '@plone/volto/components/theme/Icon/Icon';
|
|
6
|
-
import { flattenToAppURL
|
|
6
|
+
import { flattenToAppURL } from '@plone/volto/helpers/Url/Url';
|
|
7
|
+
import { getContentIcon } from '@plone/volto/helpers/Content/Content';
|
|
7
8
|
import { Image } from 'semantic-ui-react';
|
|
8
9
|
import config from '@plone/volto/registry';
|
|
9
10
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Backport of https://github.com/plone/volto/pull/5520 for Volto 17.
|
|
2
|
+
// On Volto 18 this shadow is identical to upstream — keep while V17 is supported.
|
|
2
3
|
import React from 'react';
|
|
3
|
-
import {
|
|
4
|
+
import { createPortal } from 'react-dom';
|
|
4
5
|
import { CSSTransition } from 'react-transition-group';
|
|
5
6
|
import PropTypes from 'prop-types';
|
|
6
|
-
import
|
|
7
|
+
import doesNodeContainClick from 'semantic-ui-react/dist/commonjs/lib/doesNodeContainClick';
|
|
7
8
|
|
|
8
9
|
const DEFAULT_TIMEOUT = 500;
|
|
9
10
|
|
|
@@ -17,10 +18,24 @@ const SidebarPopup = (props) => {
|
|
|
17
18
|
onClose();
|
|
18
19
|
};
|
|
19
20
|
|
|
21
|
+
const handleEscapeKey = (e) => {
|
|
22
|
+
if (open && e.key === 'Escape') {
|
|
23
|
+
onClose();
|
|
24
|
+
e.stopPropagation();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const [isClient, setIsClient] = React.useState(false);
|
|
29
|
+
React.useEffect(() => {
|
|
30
|
+
setIsClient(true);
|
|
31
|
+
}, []);
|
|
32
|
+
|
|
20
33
|
React.useEffect(() => {
|
|
21
34
|
document.addEventListener('mousedown', handleClickOutside, false);
|
|
35
|
+
document.addEventListener('keyup', handleEscapeKey, false);
|
|
22
36
|
return () => {
|
|
23
37
|
document.removeEventListener('mousedown', handleClickOutside, false);
|
|
38
|
+
document.removeEventListener('keyup', handleEscapeKey, false);
|
|
24
39
|
};
|
|
25
40
|
});
|
|
26
41
|
|
|
@@ -33,9 +48,13 @@ const SidebarPopup = (props) => {
|
|
|
33
48
|
classNames="overlay-container"
|
|
34
49
|
unmountOnExit
|
|
35
50
|
>
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
51
|
+
<>
|
|
52
|
+
{document?.body &&
|
|
53
|
+
createPortal(
|
|
54
|
+
<div className="overlay-container"></div>,
|
|
55
|
+
document?.body,
|
|
56
|
+
)}
|
|
57
|
+
</>
|
|
39
58
|
</CSSTransition>
|
|
40
59
|
)}
|
|
41
60
|
<CSSTransition
|
|
@@ -44,24 +63,27 @@ const SidebarPopup = (props) => {
|
|
|
44
63
|
classNames="sidebar-container"
|
|
45
64
|
unmountOnExit
|
|
46
65
|
>
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
66
|
+
<>
|
|
67
|
+
{isClient &&
|
|
68
|
+
createPortal(
|
|
69
|
+
<aside
|
|
70
|
+
role="presentation"
|
|
71
|
+
onClick={(e) => {
|
|
72
|
+
e.stopPropagation();
|
|
73
|
+
}}
|
|
74
|
+
onKeyDown={(e) => {
|
|
75
|
+
e.stopPropagation();
|
|
76
|
+
}}
|
|
77
|
+
ref={asideElement}
|
|
78
|
+
key="sidebarpopup"
|
|
79
|
+
className="sidebar-container"
|
|
80
|
+
style={{ overflowY: 'auto' }}
|
|
81
|
+
>
|
|
82
|
+
{children}
|
|
83
|
+
</aside>,
|
|
84
|
+
document.body,
|
|
85
|
+
)}
|
|
86
|
+
</>
|
|
65
87
|
</CSSTransition>
|
|
66
88
|
</>
|
|
67
89
|
);
|