@plone/volto 18.11.0 → 19.0.0-alpha.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.core.js +6 -0
- package/CHANGELOG.md +36 -3
- package/README.md +0 -3
- package/cypress/support/commands.js +19 -2
- package/locales/ca/LC_MESSAGES/volto.po +7 -5
- package/locales/ca.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +82 -80
- package/locales/de.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +7 -5
- package/locales/en.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +7 -5
- package/locales/es.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +7 -5
- package/locales/eu.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +7 -5
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +7 -5
- package/locales/fr.json +1 -1
- package/locales/hi/LC_MESSAGES/volto.po +7 -5
- package/locales/hi.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +7 -5
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +7 -5
- package/locales/ja.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +7 -5
- package/locales/nl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +7 -5
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +7 -5
- package/locales/pt_BR.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +7 -5
- package/locales/ro.json +1 -1
- package/locales/ru/LC_MESSAGES/volto.po +7 -5
- package/locales/ru.json +1 -1
- package/locales/volto.pot +8 -6
- package/locales/zh_CN/LC_MESSAGES/volto.po +7 -5
- package/locales/zh_CN.json +1 -1
- package/package.json +6 -6
- package/src/actions/querystring/querystring.js +11 -6
- package/src/actions/querystring/querystring.test.js +26 -5
- package/src/actions/vocabularies/vocabularies.js +5 -2
- package/src/actions/vocabularies/vocabularies.test.js +5 -5
- package/src/components/manage/Blocks/Block/BlocksForm.jsx +3 -3
- package/src/components/manage/Contents/ContentsDeleteModal.jsx +1 -1
- package/src/components/manage/Contents/ContentsDeleteModal.stories.jsx +137 -0
- package/src/components/manage/Contents/ContentsUploadModal.jsx +6 -2
- package/src/components/manage/Widgets/FileWidget.jsx +3 -3
- package/src/components/manage/Widgets/IdWidget.test.jsx +4 -2
- package/src/components/manage/Widgets/ImageWidget.jsx +27 -2
- package/src/components/manage/Widgets/ObjectBrowserWidget.jsx +3 -2
- package/src/components/manage/Widgets/RegistryImageWidget.jsx +3 -3
- package/src/components/theme/Logo/Logo.jsx +2 -1
- package/src/components/theme/Navigation/ContextNavigation.jsx +7 -2
- package/src/components/theme/View/LinkView.jsx +10 -4
- package/src/components/theme/View/LinkView.test.jsx +9 -1
- package/src/config/Blocks.jsx +1 -1
- package/src/helpers/Blocks/Blocks.js +1 -1
- package/theme/themes/pastanaga/extras/main.less +3 -0
- package/types/components/manage/Contents/ContentsDeleteModal.stories.d.ts +20 -0
- package/types/components/theme/View/LinkView.d.ts +1 -2
- package/types/config/Blocks.d.ts +1 -1
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { injectIntl } from 'react-intl';
|
|
2
|
+
import ContentsDeleteModalComponent from './ContentsDeleteModal';
|
|
3
|
+
import { RealStoreWrapper as Wrapper } from '@plone/volto/storybook';
|
|
4
|
+
|
|
5
|
+
const IntlContentsDeleteModal = injectIntl(ContentsDeleteModalComponent);
|
|
6
|
+
|
|
7
|
+
function StoryComponent(args) {
|
|
8
|
+
const { locale = 'en' } = args; // Default to 'en' if locale is not provided
|
|
9
|
+
const messages =
|
|
10
|
+
{
|
|
11
|
+
[locale]: require(`@plone/volto/../locales/${locale}.json`),
|
|
12
|
+
}[locale] || {};
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<Wrapper
|
|
16
|
+
customStore={{
|
|
17
|
+
linkIntegrity: {
|
|
18
|
+
result: args.linkIntegrityResult,
|
|
19
|
+
loading: args.loading,
|
|
20
|
+
},
|
|
21
|
+
intl: {
|
|
22
|
+
locale,
|
|
23
|
+
messages,
|
|
24
|
+
},
|
|
25
|
+
}}
|
|
26
|
+
>
|
|
27
|
+
<div id="toolbar" style={{ display: 'none' }} />
|
|
28
|
+
<div>{locale}</div>
|
|
29
|
+
<IntlContentsDeleteModal {...args} />
|
|
30
|
+
</Wrapper>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const Default = StoryComponent.bind({});
|
|
35
|
+
Default.args = {
|
|
36
|
+
locale: 'en',
|
|
37
|
+
open: true,
|
|
38
|
+
items: [
|
|
39
|
+
{ '@id': '/news', UID: '123', title: 'News' },
|
|
40
|
+
{ '@id': '/blog', UID: '456', title: 'Blog' },
|
|
41
|
+
{ '@id': '/extra', UID: '789', title: 'Extra' },
|
|
42
|
+
],
|
|
43
|
+
itemsToDelete: [{ UID: '456' }],
|
|
44
|
+
linkIntegrityResult: [
|
|
45
|
+
{
|
|
46
|
+
'@id': '/blog',
|
|
47
|
+
title: 'Blog',
|
|
48
|
+
items_total: 2,
|
|
49
|
+
breaches: [{ uid: '123', title: 'News', '@id': '/news' }],
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
loading: false,
|
|
53
|
+
onOk: () => {},
|
|
54
|
+
onCancel: () => {},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const DeleteMoreThanOne = StoryComponent.bind({});
|
|
58
|
+
DeleteMoreThanOne.args = {
|
|
59
|
+
...Default.args,
|
|
60
|
+
itemsToDelete: [{ UID: '456' }, { UID: '789' }],
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const NoContainedItems = StoryComponent.bind({});
|
|
64
|
+
NoContainedItems.args = {
|
|
65
|
+
...Default.args,
|
|
66
|
+
linkIntegrityResult: [
|
|
67
|
+
{
|
|
68
|
+
'@id': '/blog',
|
|
69
|
+
title: 'Blog',
|
|
70
|
+
items_total: 0,
|
|
71
|
+
breaches: [{ uid: '123', title: 'News', '@id': '/news' }],
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const MultipleLinkIntegrityResults = StoryComponent.bind({});
|
|
77
|
+
MultipleLinkIntegrityResults.args = {
|
|
78
|
+
...Default.args,
|
|
79
|
+
itemsToDelete: [{ UID: '456' }, { UID: '789' }],
|
|
80
|
+
linkIntegrityResult: [
|
|
81
|
+
{
|
|
82
|
+
'@id': '/blog',
|
|
83
|
+
title: 'Blog',
|
|
84
|
+
items_total: 1,
|
|
85
|
+
breaches: [{ uid: '123', title: 'News', '@id': '/news' }],
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
'@id': '/extra',
|
|
89
|
+
title: 'Extra',
|
|
90
|
+
items_total: 0,
|
|
91
|
+
breaches: [{ uid: '123', title: 'News', '@id': '/news' }],
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export const MultipleBreaches = StoryComponent.bind({});
|
|
97
|
+
MultipleBreaches.args = {
|
|
98
|
+
...Default.args,
|
|
99
|
+
itemsToDelete: [{ UID: '789' }],
|
|
100
|
+
linkIntegrityResult: [
|
|
101
|
+
{
|
|
102
|
+
'@id': '/extra',
|
|
103
|
+
title: 'Extra',
|
|
104
|
+
items_total: 0,
|
|
105
|
+
breaches: [
|
|
106
|
+
{ uid: '123', title: 'News', '@id': '/news' },
|
|
107
|
+
{ uid: '456', title: 'Blog', '@id': '/blog' },
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const WithoutLinkIntegrityBreaches = StoryComponent.bind({});
|
|
114
|
+
WithoutLinkIntegrityBreaches.args = {
|
|
115
|
+
...Default.args,
|
|
116
|
+
linkIntegrityResult: [],
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export default {
|
|
120
|
+
title: 'Public components/Contents/Contents Delete Modal',
|
|
121
|
+
component: Default,
|
|
122
|
+
decorators: [
|
|
123
|
+
(Story) => (
|
|
124
|
+
<div className="ui segment form attached" style={{ width: '400px' }}>
|
|
125
|
+
<Story />
|
|
126
|
+
</div>
|
|
127
|
+
),
|
|
128
|
+
],
|
|
129
|
+
argTypes: {
|
|
130
|
+
locale: {
|
|
131
|
+
options: ['en', 'fr', 'de', 'it', 'eu'],
|
|
132
|
+
control: {
|
|
133
|
+
type: 'radio',
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
};
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
Dimmer,
|
|
7
7
|
Header,
|
|
8
8
|
Icon,
|
|
9
|
-
Image,
|
|
10
9
|
Modal,
|
|
11
10
|
Table,
|
|
12
11
|
Segment,
|
|
@@ -24,6 +23,7 @@ import FormattedRelativeDate from '@plone/volto/components/theme/FormattedDate/F
|
|
|
24
23
|
import { createContent } from '@plone/volto/actions/content/content';
|
|
25
24
|
import { validateFileUploadSize } from '@plone/volto/helpers/FormValidation/FormValidation';
|
|
26
25
|
import { usePrevious } from '@plone/volto/helpers/Utils/usePrevious';
|
|
26
|
+
import Image from '@plone/volto/components/theme/Image/Image';
|
|
27
27
|
|
|
28
28
|
const Dropzone = loadable(() => import('react-dropzone'));
|
|
29
29
|
|
|
@@ -259,7 +259,11 @@ const ContentsUploadModal = (props) => {
|
|
|
259
259
|
<Table.Cell>{filesize(file.size, { round: 0 })}</Table.Cell>
|
|
260
260
|
<Table.Cell>
|
|
261
261
|
{file.type.split('/')[0] === 'image' && (
|
|
262
|
-
<Image
|
|
262
|
+
<Image
|
|
263
|
+
src={file.preview}
|
|
264
|
+
height={60}
|
|
265
|
+
className="ui image"
|
|
266
|
+
/>
|
|
263
267
|
)}
|
|
264
268
|
</Table.Cell>
|
|
265
269
|
<Table.Cell>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import React from 'react';
|
|
7
7
|
import PropTypes from 'prop-types';
|
|
8
|
-
import { Button,
|
|
8
|
+
import { Button, Dimmer } from 'semantic-ui-react';
|
|
9
9
|
import { readAsDataURL } from 'promise-file-reader';
|
|
10
10
|
import { injectIntl } from 'react-intl';
|
|
11
11
|
import deleteSVG from '@plone/volto/icons/delete.svg';
|
|
@@ -13,6 +13,7 @@ import Icon from '@plone/volto/components/theme/Icon/Icon';
|
|
|
13
13
|
import Toast from '@plone/volto/components/manage/Toast/Toast';
|
|
14
14
|
import UniversalLink from '@plone/volto/components/manage/UniversalLink/UniversalLink';
|
|
15
15
|
import FormFieldWrapper from '@plone/volto/components/manage/Widgets/FormFieldWrapper';
|
|
16
|
+
import Image from '@plone/volto/components/theme/Image/Image';
|
|
16
17
|
import loadable from '@loadable/component';
|
|
17
18
|
import { flattenToAppURL } from '@plone/volto/helpers/Url/Url';
|
|
18
19
|
import { validateFileUploadSize } from '@plone/volto/helpers/FormValidation/FormValidation';
|
|
@@ -172,9 +173,8 @@ const FileWidget = (props) => {
|
|
|
172
173
|
{isDragActive && <Dimmer active></Dimmer>}
|
|
173
174
|
{fileType ? (
|
|
174
175
|
<Image
|
|
175
|
-
className="image-preview"
|
|
176
|
+
className="image-preview small ui image"
|
|
176
177
|
id={`field-${id}-image`}
|
|
177
|
-
size="small"
|
|
178
178
|
src={imgsrc}
|
|
179
179
|
/>
|
|
180
180
|
) : (
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Provider } from 'react-intl-redux';
|
|
3
3
|
import { render, waitFor } from '@testing-library/react';
|
|
4
|
-
import
|
|
4
|
+
import configureMockStore from 'redux-mock-store';
|
|
5
|
+
import thunk from 'redux-thunk';
|
|
5
6
|
import config from '@plone/volto/registry';
|
|
6
7
|
|
|
7
8
|
import IdWidget from './IdWidget';
|
|
8
9
|
|
|
9
|
-
const
|
|
10
|
+
const middlewares = [thunk];
|
|
11
|
+
const mockStore = configureMockStore(middlewares);
|
|
10
12
|
|
|
11
13
|
describe('IdWidget', () => {
|
|
12
14
|
test('renders an empty id widget component', async () => {
|
|
@@ -6,6 +6,7 @@ import { useLocation } from 'react-router-dom';
|
|
|
6
6
|
import loadable from '@loadable/component';
|
|
7
7
|
import { connect } from 'react-redux';
|
|
8
8
|
import { compose } from 'redux';
|
|
9
|
+
import { toast } from 'react-toastify';
|
|
9
10
|
import useLinkEditor from '@plone/volto/components/manage/AnchorPlugin/useLinkEditor';
|
|
10
11
|
import withObjectBrowser from '@plone/volto/components/manage/Sidebar/ObjectBrowser';
|
|
11
12
|
|
|
@@ -20,6 +21,7 @@ import { createContent } from '@plone/volto/actions/content/content';
|
|
|
20
21
|
import { readAsDataURL } from 'promise-file-reader';
|
|
21
22
|
import FormFieldWrapper from '@plone/volto/components/manage/Widgets/FormFieldWrapper';
|
|
22
23
|
import Icon from '@plone/volto/components/theme/Icon/Icon';
|
|
24
|
+
import Toast from '@plone/volto/components/manage/Toast/Toast';
|
|
23
25
|
|
|
24
26
|
import imageBlockSVG from '@plone/volto/components/manage/Blocks/Image/block-image.svg';
|
|
25
27
|
import clearSVG from '@plone/volto/icons/clear.svg';
|
|
@@ -60,6 +62,14 @@ const messages = defineMessages({
|
|
|
60
62
|
id: 'Uploading image',
|
|
61
63
|
defaultMessage: 'Uploading image',
|
|
62
64
|
},
|
|
65
|
+
Error: {
|
|
66
|
+
id: 'Error',
|
|
67
|
+
defaultMessage: 'Error',
|
|
68
|
+
},
|
|
69
|
+
imageUploadErrorMessage: {
|
|
70
|
+
id: 'imageUploadErrorMessage',
|
|
71
|
+
defaultMessage: 'Please upload an image instead.',
|
|
72
|
+
},
|
|
63
73
|
});
|
|
64
74
|
|
|
65
75
|
const UnconnectedImageInput = (props) => {
|
|
@@ -184,7 +194,21 @@ const UnconnectedImageInput = (props) => {
|
|
|
184
194
|
>
|
|
185
195
|
<Dropzone
|
|
186
196
|
noClick
|
|
187
|
-
|
|
197
|
+
accept="image/*"
|
|
198
|
+
onDrop={(acceptedFiles) => {
|
|
199
|
+
if (acceptedFiles.length > 0) {
|
|
200
|
+
handleUpload(acceptedFiles);
|
|
201
|
+
} else {
|
|
202
|
+
setDragging(false);
|
|
203
|
+
toast.error(
|
|
204
|
+
<Toast
|
|
205
|
+
error
|
|
206
|
+
title={intl.formatMessage(messages.Error)}
|
|
207
|
+
content={intl.formatMessage(messages.imageUploadErrorMessage)}
|
|
208
|
+
/>,
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
}}
|
|
188
212
|
onDragEnter={onDragEnter}
|
|
189
213
|
onDragLeave={onDragLeave}
|
|
190
214
|
className="dropzone"
|
|
@@ -192,7 +216,7 @@ const UnconnectedImageInput = (props) => {
|
|
|
192
216
|
{({ getRootProps, getInputProps }) => (
|
|
193
217
|
<div {...getRootProps()}>
|
|
194
218
|
<Message>
|
|
195
|
-
{dragging && <Dimmer active
|
|
219
|
+
{dragging && <Dimmer active />}
|
|
196
220
|
{uploading && (
|
|
197
221
|
<Dimmer active>
|
|
198
222
|
<Loader indeterminate>
|
|
@@ -253,6 +277,7 @@ const UnconnectedImageInput = (props) => {
|
|
|
253
277
|
ref: imageUploadInputRef,
|
|
254
278
|
onChange: handleUpload,
|
|
255
279
|
style: { display: 'none' },
|
|
280
|
+
accept: 'image/*',
|
|
256
281
|
})}
|
|
257
282
|
/>
|
|
258
283
|
</Button.Group>
|
|
@@ -12,7 +12,7 @@ import isArray from 'lodash/isArray';
|
|
|
12
12
|
import isEmpty from 'lodash/isEmpty';
|
|
13
13
|
import remove from 'lodash/remove';
|
|
14
14
|
import { connect } from 'react-redux';
|
|
15
|
-
import {
|
|
15
|
+
import { Label, Popup, Button } from 'semantic-ui-react';
|
|
16
16
|
import {
|
|
17
17
|
flattenToAppURL,
|
|
18
18
|
isInternalURL,
|
|
@@ -33,6 +33,7 @@ import homeSVG from '@plone/volto/icons/home.svg';
|
|
|
33
33
|
import aheadSVG from '@plone/volto/icons/ahead.svg';
|
|
34
34
|
import blankSVG from '@plone/volto/icons/blank.svg';
|
|
35
35
|
import { withRouter } from 'react-router';
|
|
36
|
+
import Image from '@plone/volto/components/theme/Image/Image';
|
|
36
37
|
|
|
37
38
|
const messages = defineMessages({
|
|
38
39
|
placeholder: {
|
|
@@ -131,7 +132,7 @@ export class ObjectBrowserWidgetComponent extends Component {
|
|
|
131
132
|
<div className="item-title">
|
|
132
133
|
{includes(config.settings.imageObjects, item['@type']) ? (
|
|
133
134
|
<Image
|
|
134
|
-
|
|
135
|
+
className="small ui image"
|
|
135
136
|
src={`${item['@id']}/@@images/image/thumb`}
|
|
136
137
|
/>
|
|
137
138
|
) : (
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import React, { useState } from 'react';
|
|
7
7
|
import PropTypes from 'prop-types';
|
|
8
|
-
import { Button,
|
|
8
|
+
import { Button, Dimmer } from 'semantic-ui-react';
|
|
9
9
|
import { readAsDataURL } from 'promise-file-reader';
|
|
10
10
|
import { injectIntl } from 'react-intl';
|
|
11
11
|
import deleteSVG from '@plone/volto/icons/delete.svg';
|
|
@@ -15,6 +15,7 @@ import loadable from '@loadable/component';
|
|
|
15
15
|
import { defineMessages, useIntl } from 'react-intl';
|
|
16
16
|
import { toPublicURL } from '@plone/volto/helpers/Url/Url';
|
|
17
17
|
import { validateFileUploadSize } from '@plone/volto/helpers/FormValidation/FormValidation';
|
|
18
|
+
import Image from '@plone/volto/components/theme/Image/Image';
|
|
18
19
|
|
|
19
20
|
const imageMimetypes = [
|
|
20
21
|
'image/png',
|
|
@@ -119,9 +120,8 @@ const RegistryImageWidget = (props) => {
|
|
|
119
120
|
{isDragActive && <Dimmer active></Dimmer>}
|
|
120
121
|
{previewSrc ? (
|
|
121
122
|
<Image
|
|
122
|
-
className="image-preview"
|
|
123
|
+
className="image-preview small ui image"
|
|
123
124
|
id={`field-${id}-image`}
|
|
124
|
-
size="small"
|
|
125
125
|
src={previewSrc}
|
|
126
126
|
/>
|
|
127
127
|
) : (
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { defineMessages, useIntl } from 'react-intl';
|
|
6
6
|
import { useEffect } from 'react';
|
|
7
|
-
import { Image } from 'semantic-ui-react';
|
|
8
7
|
import LogoImage from '@plone/volto/components/theme/Logo/Logo.svg';
|
|
9
8
|
import { useSelector, useDispatch } from 'react-redux';
|
|
10
9
|
import { Link, useLocation } from 'react-router-dom';
|
|
11
10
|
import { getNavroot } from '@plone/volto/actions/navroot/navroot';
|
|
12
11
|
import { flattenToAppURL, getBaseUrl } from '@plone/volto/helpers/Url/Url';
|
|
13
12
|
import { hasApiExpander } from '@plone/volto/helpers/Utils/Utils';
|
|
13
|
+
import Image from '@plone/volto/components/theme/Image/Image';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Logo component class.
|
|
@@ -47,6 +47,7 @@ const Logo = () => {
|
|
|
47
47
|
return (
|
|
48
48
|
<Link to={navRootPath} aria-label={intl.formatMessage(messages.home)}>
|
|
49
49
|
<Image
|
|
50
|
+
className="ui image"
|
|
50
51
|
src={
|
|
51
52
|
site['plone.site_logo']
|
|
52
53
|
? flattenToAppURL(site['plone.site_logo'])
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import PropTypes from 'prop-types';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { List
|
|
3
|
+
import { List } from 'semantic-ui-react';
|
|
4
4
|
import { Link as RouterLink } from 'react-router-dom';
|
|
5
5
|
import cx from 'classnames';
|
|
6
6
|
import { compose } from 'redux';
|
|
@@ -13,6 +13,7 @@ import UniversalLink from '@plone/volto/components/manage/UniversalLink/Universa
|
|
|
13
13
|
import { withContentNavigation } from './withContentNavigation';
|
|
14
14
|
|
|
15
15
|
import leftIcon from '@plone/volto/icons/left-key.svg';
|
|
16
|
+
import Image from '@plone/volto/components/theme/Image/Image';
|
|
16
17
|
|
|
17
18
|
const messages = defineMessages({
|
|
18
19
|
navigation: {
|
|
@@ -38,7 +39,11 @@ function renderNode(node, parentLevel) {
|
|
|
38
39
|
in_path: node.is_in_path,
|
|
39
40
|
})}
|
|
40
41
|
>
|
|
41
|
-
{node.thumb ?
|
|
42
|
+
{node.thumb ? (
|
|
43
|
+
<Image src={flattenToAppURL(node.thumb)} className="ui image" />
|
|
44
|
+
) : (
|
|
45
|
+
''
|
|
46
|
+
)}
|
|
42
47
|
{node.title}
|
|
43
48
|
{node.is_current ? (
|
|
44
49
|
<List.Content className="active-indicator">
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
|
+
import { useSelector } from 'react-redux';
|
|
2
3
|
import PropTypes from 'prop-types';
|
|
3
4
|
import { useHistory } from 'react-router-dom';
|
|
4
5
|
import { isInternalURL, flattenToAppURL } from '@plone/volto/helpers/Url/Url';
|
|
@@ -8,10 +9,15 @@ import { Redirect } from 'react-router-dom';
|
|
|
8
9
|
import { FormattedMessage } from 'react-intl';
|
|
9
10
|
import config from '@plone/volto/registry';
|
|
10
11
|
|
|
11
|
-
const LinkView = ({
|
|
12
|
+
const LinkView = ({ content }) => {
|
|
12
13
|
const history = useHistory();
|
|
14
|
+
const userCanEdit = useSelector(
|
|
15
|
+
(state) =>
|
|
16
|
+
!!state.actions.actions.object.find((action) => action.id === 'edit'),
|
|
17
|
+
);
|
|
18
|
+
|
|
13
19
|
useEffect(() => {
|
|
14
|
-
if (!
|
|
20
|
+
if (!userCanEdit) {
|
|
15
21
|
const { remoteUrl } = content;
|
|
16
22
|
if (isInternalURL(remoteUrl)) {
|
|
17
23
|
history.replace(flattenToAppURL(remoteUrl));
|
|
@@ -19,8 +25,8 @@ const LinkView = ({ token, content }) => {
|
|
|
19
25
|
window.location.href = flattenToAppURL(remoteUrl);
|
|
20
26
|
}
|
|
21
27
|
}
|
|
22
|
-
}, [content, history,
|
|
23
|
-
if (__SERVER__ && !
|
|
28
|
+
}, [content, history, userCanEdit]);
|
|
29
|
+
if (__SERVER__ && !userCanEdit && content.remoteUrl) {
|
|
24
30
|
return <Redirect to={content.remoteUrl} />;
|
|
25
31
|
}
|
|
26
32
|
const { title, description, remoteUrl } = content;
|
|
@@ -17,6 +17,15 @@ const store = mockStore({
|
|
|
17
17
|
locale: 'en',
|
|
18
18
|
messages: {},
|
|
19
19
|
},
|
|
20
|
+
actions: {
|
|
21
|
+
actions: {
|
|
22
|
+
object: [
|
|
23
|
+
{
|
|
24
|
+
id: 'edit',
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
},
|
|
20
29
|
});
|
|
21
30
|
|
|
22
31
|
test('renders a link view component', () => {
|
|
@@ -24,7 +33,6 @@ test('renders a link view component', () => {
|
|
|
24
33
|
<Provider store={store}>
|
|
25
34
|
<MemoryRouter>
|
|
26
35
|
<LinkView
|
|
27
|
-
token="1234"
|
|
28
36
|
content={{
|
|
29
37
|
title: 'Hello World!',
|
|
30
38
|
description: 'Hi',
|
package/src/config/Blocks.jsx
CHANGED
|
@@ -527,7 +527,7 @@ blocksConfig.gridBlock.blocksConfig.teaser.schemaEnhancer =
|
|
|
527
527
|
blocksConfig.gridBlock.blocksConfig.image.schemaEnhancer =
|
|
528
528
|
gridImageDisableSizeAndPositionHandlersSchema;
|
|
529
529
|
|
|
530
|
-
const requiredBlocks = [
|
|
530
|
+
const requiredBlocks = [];
|
|
531
531
|
|
|
532
532
|
const initialBlocks = {};
|
|
533
533
|
const initialBlocksFocus = {}; //{Document:'title'}
|
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
|
|
18
18
|
body {
|
|
19
19
|
display: flex;
|
|
20
|
+
// react-aria-components popovers have problems with the default declaration
|
|
21
|
+
// in Semantic UI that forces `overflow-x: hidden` on the body.
|
|
22
|
+
overflow-x: initial;
|
|
20
23
|
|
|
21
24
|
@media only screen and (max-width: @largestMobileScreen) {
|
|
22
25
|
flex-direction: column;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const Default: any;
|
|
2
|
+
export const DeleteMoreThanOne: any;
|
|
3
|
+
export const NoContainedItems: any;
|
|
4
|
+
export const MultipleLinkIntegrityResults: any;
|
|
5
|
+
export const MultipleBreaches: any;
|
|
6
|
+
export const WithoutLinkIntegrityBreaches: any;
|
|
7
|
+
declare namespace _default {
|
|
8
|
+
export let title: string;
|
|
9
|
+
export { Default as component };
|
|
10
|
+
export let decorators: ((Story: any) => import("react/jsx-runtime").JSX.Element)[];
|
|
11
|
+
export namespace argTypes {
|
|
12
|
+
namespace locale {
|
|
13
|
+
let options: string[];
|
|
14
|
+
namespace control {
|
|
15
|
+
let type: string;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export default _default;
|
package/types/config/Blocks.d.ts
CHANGED