@plone/volto 18.29.0 → 18.30.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/.eslintignore +1 -0
- package/.eslintrc +26 -0
- package/CHANGELOG.md +32 -0
- package/cypress/support/guillotina.js +1 -0
- package/cypress.config.js +1 -0
- package/locales/af/LC_MESSAGES/volto.po +5 -0
- package/locales/af.json +1 -1
- package/locales/ar/LC_MESSAGES/volto.po +5 -0
- package/locales/ar.json +1 -1
- package/locales/bg/LC_MESSAGES/volto.po +5 -0
- package/locales/bg.json +1 -1
- package/locales/bn/LC_MESSAGES/volto.po +5 -0
- package/locales/bn.json +1 -1
- package/locales/ca/LC_MESSAGES/volto.po +5 -0
- package/locales/ca.json +1 -1
- package/locales/cs/LC_MESSAGES/volto.po +5 -0
- package/locales/cs.json +1 -1
- package/locales/cy/LC_MESSAGES/volto.po +5 -0
- package/locales/cy.json +1 -1
- package/locales/da/LC_MESSAGES/volto.po +5 -0
- package/locales/da.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +5 -0
- package/locales/de.json +1 -1
- package/locales/el/LC_MESSAGES/volto.po +5 -0
- package/locales/el.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +5 -0
- package/locales/en.json +1 -1
- package/locales/en_AU/LC_MESSAGES/volto.po +5 -0
- package/locales/en_AU.json +1 -1
- package/locales/en_GB/LC_MESSAGES/volto.po +5 -0
- package/locales/en_GB.json +1 -1
- package/locales/eo/LC_MESSAGES/volto.po +5 -0
- package/locales/eo.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +19 -14
- package/locales/es.json +1 -1
- package/locales/et/LC_MESSAGES/volto.po +5 -0
- package/locales/et.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +8 -3
- package/locales/eu.json +1 -1
- package/locales/fa/LC_MESSAGES/volto.po +5 -0
- package/locales/fa.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +5 -0
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +5 -0
- package/locales/fr.json +1 -1
- package/locales/fu/LC_MESSAGES/volto.po +5 -0
- package/locales/fu.json +1 -1
- package/locales/gl/LC_MESSAGES/volto.po +5 -0
- package/locales/gl.json +1 -1
- package/locales/he/LC_MESSAGES/volto.po +5 -0
- package/locales/he.json +1 -1
- package/locales/hi/LC_MESSAGES/volto.po +5 -0
- package/locales/hi.json +1 -1
- package/locales/hr/LC_MESSAGES/volto.po +5 -0
- package/locales/hr.json +1 -1
- package/locales/hu/LC_MESSAGES/volto.po +5 -0
- package/locales/hu.json +1 -1
- package/locales/hy/LC_MESSAGES/volto.po +5 -0
- package/locales/hy.json +1 -1
- package/locales/id/LC_MESSAGES/volto.po +5 -0
- package/locales/id.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +5 -0
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +5 -0
- package/locales/ja.json +1 -1
- package/locales/ka/LC_MESSAGES/volto.po +5 -0
- package/locales/ka.json +1 -1
- package/locales/kn/LC_MESSAGES/volto.po +5 -0
- package/locales/kn.json +1 -1
- package/locales/ko/LC_MESSAGES/volto.po +5 -0
- package/locales/ko.json +1 -1
- package/locales/lt/LC_MESSAGES/volto.po +5 -0
- package/locales/lt.json +1 -1
- package/locales/lv/LC_MESSAGES/volto.po +5 -0
- package/locales/lv.json +1 -1
- package/locales/mi/LC_MESSAGES/volto.po +5 -0
- package/locales/mi.json +1 -1
- package/locales/mk/LC_MESSAGES/volto.po +5 -0
- package/locales/mk.json +1 -1
- package/locales/my/LC_MESSAGES/volto.po +5 -0
- package/locales/my.json +1 -1
- package/locales/nb_NO/LC_MESSAGES/volto.po +5 -0
- package/locales/nb_NO.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +8 -3
- package/locales/nl.json +1 -1
- package/locales/nn/LC_MESSAGES/volto.po +5 -0
- package/locales/nn.json +1 -1
- package/locales/pl/LC_MESSAGES/volto.po +5 -0
- package/locales/pl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +5 -0
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +5 -0
- package/locales/pt_BR.json +1 -1
- package/locales/rm/LC_MESSAGES/volto.po +5 -0
- package/locales/rm.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +5 -0
- package/locales/ro.json +1 -1
- package/locales/ru/LC_MESSAGES/volto.po +5 -0
- package/locales/ru.json +1 -1
- package/locales/sk/LC_MESSAGES/volto.po +5 -0
- package/locales/sk.json +1 -1
- package/locales/sl/LC_MESSAGES/volto.po +5 -0
- package/locales/sl.json +1 -1
- package/locales/sm/LC_MESSAGES/volto.po +5 -0
- package/locales/sm.json +1 -1
- package/locales/sq/LC_MESSAGES/volto.po +5 -0
- package/locales/sq.json +1 -1
- package/locales/sr/LC_MESSAGES/volto.po +5 -0
- package/locales/sr.json +1 -1
- package/locales/sr@cyrl/LC_MESSAGES/volto.po +5 -0
- package/locales/sr@cyrl.json +1 -1
- package/locales/sr@latn/LC_MESSAGES/volto.po +5 -0
- package/locales/sr@latn.json +1 -1
- package/locales/sv/LC_MESSAGES/volto.po +5 -0
- package/locales/sv.json +1 -1
- package/locales/ta/LC_MESSAGES/volto.po +59 -53
- package/locales/ta.json +1 -1
- package/locales/te/LC_MESSAGES/volto.po +5 -0
- package/locales/te.json +1 -1
- package/locales/th/LC_MESSAGES/volto.po +5 -0
- package/locales/th.json +1 -1
- package/locales/to/LC_MESSAGES/volto.po +5 -0
- package/locales/to.json +1 -1
- package/locales/tr/LC_MESSAGES/volto.po +5 -0
- package/locales/tr.json +1 -1
- package/locales/uk/LC_MESSAGES/volto.po +5 -0
- package/locales/uk.json +1 -1
- package/locales/vi/LC_MESSAGES/volto.po +5 -0
- package/locales/vi.json +1 -1
- package/locales/volto.pot +6 -1
- package/locales/zh_CN/LC_MESSAGES/volto.po +5 -0
- package/locales/zh_CN.json +1 -1
- package/locales/zh_Hant/LC_MESSAGES/volto.po +5 -0
- package/locales/zh_Hant.json +1 -1
- package/locales/zh_Hant_HK/LC_MESSAGES/volto.po +5 -0
- package/locales/zh_Hant_HK.json +1 -1
- package/news/7428.feat +1 -0
- package/package.json +12 -11
- package/src/components/manage/BlockChooser/BlockChooser.jsx +1 -0
- package/src/components/manage/Controlpanels/Relations/Relations.jsx +1 -1
- package/src/components/manage/Controlpanels/Users/UsersControlpanel.jsx +575 -630
- package/src/components/manage/Controlpanels/Users/UsersControlpanel.test.jsx +4 -3
- package/src/components/manage/Sidebar/SidebarPortal.test.tsx +42 -0
- package/src/components/manage/Sidebar/SidebarPortal.tsx +48 -0
- package/src/components/manage/Toolbar/Toolbar.jsx +14 -4
- package/src/components/manage/Widgets/ImageWidget.jsx +11 -4
- package/src/helpers/Extensions/withBlockSchemaEnhancer.jsx +4 -1
- package/src/helpers/Url/Url.js +1 -0
- package/theme/themes/pastanaga/extras/toolbar.less +22 -5
- package/types/components/manage/Controlpanels/Users/UsersControlpanel.d.ts +6 -2
- package/types/components/manage/Controlpanels/index.d.ts +1 -1
- package/types/components/manage/Sidebar/SidebarPortal.d.ts +7 -15
- package/types/helpers/Extensions/withBlockSchemaEnhancer.d.ts +4 -5
- package/src/components/manage/Sidebar/SidebarPortal.jsx +0 -47
- package/src/components/manage/Sidebar/SidebarPortal.test.jsx +0 -26
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { render, act } from '@testing-library/react';
|
|
3
3
|
import configureStore from 'redux-mock-store';
|
|
4
4
|
import { Provider } from 'react-intl-redux';
|
|
5
|
+
import { MemoryRouter } from 'react-router-dom';
|
|
5
6
|
import jwt from 'jsonwebtoken';
|
|
6
7
|
|
|
7
8
|
import UsersControlpanel from './UsersControlpanel';
|
|
@@ -41,10 +42,10 @@ describe('UsersControlpanel', () => {
|
|
|
41
42
|
const { container } = await act(async () => {
|
|
42
43
|
return render(
|
|
43
44
|
<Provider store={store}>
|
|
44
|
-
|
|
45
|
-
<UsersControlpanel
|
|
45
|
+
<MemoryRouter initialEntries={['/controlpanel/users']}>
|
|
46
|
+
<UsersControlpanel />
|
|
46
47
|
<div id="toolbar"></div>
|
|
47
|
-
|
|
48
|
+
</MemoryRouter>
|
|
48
49
|
</Provider>,
|
|
49
50
|
);
|
|
50
51
|
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { cleanup, render, screen } from '@testing-library/react';
|
|
3
|
+
|
|
4
|
+
import SidebarPortal from './SidebarPortal';
|
|
5
|
+
|
|
6
|
+
describe('SidebarPortal', () => {
|
|
7
|
+
let portalRoot: HTMLDivElement | null;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
portalRoot = document.createElement('div');
|
|
11
|
+
portalRoot.setAttribute('id', 'sidebar-properties');
|
|
12
|
+
document.body.appendChild(portalRoot);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
cleanup();
|
|
17
|
+
portalRoot?.remove();
|
|
18
|
+
portalRoot = null;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('renders the sidebar portal when the block is selected', async () => {
|
|
22
|
+
render(
|
|
23
|
+
<SidebarPortal selected={true}>
|
|
24
|
+
<p>Tested!</p>
|
|
25
|
+
</SidebarPortal>,
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
expect(await screen.findByText('Tested!')).toBeInTheDocument();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('does not render the sidebar portal when the block is not selected', () => {
|
|
32
|
+
render(
|
|
33
|
+
<SidebarPortal selected={false}>
|
|
34
|
+
<p>Tested, but you should not see this!</p>
|
|
35
|
+
</SidebarPortal>,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
expect(
|
|
39
|
+
screen.queryByText('Tested, but you should not see this!'),
|
|
40
|
+
).not.toBeInTheDocument();
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { createPortal } from 'react-dom';
|
|
3
|
+
|
|
4
|
+
type SidebarPortalProps = {
|
|
5
|
+
children?: React.ReactNode;
|
|
6
|
+
selected: boolean;
|
|
7
|
+
tab?: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const SidebarPortal = ({
|
|
11
|
+
children,
|
|
12
|
+
selected,
|
|
13
|
+
tab = 'sidebar-properties',
|
|
14
|
+
}: SidebarPortalProps) => {
|
|
15
|
+
const [isClient, setIsClient] = React.useState(false);
|
|
16
|
+
|
|
17
|
+
React.useEffect(() => setIsClient(true), []);
|
|
18
|
+
|
|
19
|
+
if (!isClient || !selected) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const target = document.getElementById(tab);
|
|
24
|
+
|
|
25
|
+
if (!target) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return createPortal(
|
|
30
|
+
<div role="form" style={{ height: '100%' }}>
|
|
31
|
+
<div
|
|
32
|
+
style={{ height: '100%' }}
|
|
33
|
+
role="presentation"
|
|
34
|
+
onClick={(e) => {
|
|
35
|
+
e.stopPropagation();
|
|
36
|
+
}}
|
|
37
|
+
onKeyDown={(e) => {
|
|
38
|
+
e.stopPropagation();
|
|
39
|
+
}}
|
|
40
|
+
>
|
|
41
|
+
{children}
|
|
42
|
+
</div>
|
|
43
|
+
</div>,
|
|
44
|
+
target,
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export default SidebarPortal;
|
|
@@ -68,6 +68,10 @@ const messages = defineMessages({
|
|
|
68
68
|
id: 'Shrink toolbar',
|
|
69
69
|
defaultMessage: 'Shrink toolbar',
|
|
70
70
|
},
|
|
71
|
+
expandToolbar: {
|
|
72
|
+
id: 'Expand toolbar',
|
|
73
|
+
defaultMessage: 'Expand toolbar',
|
|
74
|
+
},
|
|
71
75
|
personalInformation: {
|
|
72
76
|
id: 'Personal Information',
|
|
73
77
|
defaultMessage: 'Personal Information',
|
|
@@ -455,6 +459,7 @@ class Toolbar extends Component {
|
|
|
455
459
|
</div>
|
|
456
460
|
</div>
|
|
457
461
|
<div
|
|
462
|
+
id="toolbar-body"
|
|
458
463
|
className={this.state.expanded ? 'toolbar expanded' : 'toolbar'}
|
|
459
464
|
ref={this.toolbarRef}
|
|
460
465
|
>
|
|
@@ -617,15 +622,20 @@ class Toolbar extends Component {
|
|
|
617
622
|
</div>
|
|
618
623
|
<div className="toolbar-handler">
|
|
619
624
|
<button
|
|
620
|
-
aria-label={this.props.intl.formatMessage(
|
|
621
|
-
messages.shrinkToolbar,
|
|
622
|
-
)}
|
|
623
625
|
className={cx('toolbar-handler-button', {
|
|
624
626
|
[this.props.content?.review_state]:
|
|
625
627
|
this.props.content?.review_state,
|
|
626
628
|
})}
|
|
627
629
|
onClick={this.handleShrink}
|
|
628
|
-
|
|
630
|
+
aria-expanded={expanded}
|
|
631
|
+
aria-controls="toolbar-body"
|
|
632
|
+
>
|
|
633
|
+
<span aria-live="assertive" className="visually-hidden">
|
|
634
|
+
{expanded
|
|
635
|
+
? this.props.intl.formatMessage(messages.shrinkToolbar)
|
|
636
|
+
: this.props.intl.formatMessage(messages.expandToolbar)}
|
|
637
|
+
</span>
|
|
638
|
+
</button>
|
|
629
639
|
</div>
|
|
630
640
|
</div>
|
|
631
641
|
<div className="pusher" />
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useEffect, useRef } from 'react';
|
|
2
2
|
import { Button, Dimmer, Loader, Message } from 'semantic-ui-react';
|
|
3
3
|
import { useIntl, defineMessages } from 'react-intl';
|
|
4
|
-
import { useDispatch } from 'react-redux';
|
|
4
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
5
5
|
import { useLocation } from 'react-router-dom';
|
|
6
6
|
import loadable from '@loadable/component';
|
|
7
7
|
import { connect } from 'react-redux';
|
|
@@ -14,6 +14,7 @@ import config from '@plone/volto/registry';
|
|
|
14
14
|
import {
|
|
15
15
|
flattenToAppURL,
|
|
16
16
|
getBaseUrl,
|
|
17
|
+
getParentUrl,
|
|
17
18
|
isInternalURL,
|
|
18
19
|
normalizeUrl,
|
|
19
20
|
removeProtocol,
|
|
@@ -109,6 +110,9 @@ const UnconnectedImageInput = (props) => {
|
|
|
109
110
|
const linkEditor = useLinkEditor();
|
|
110
111
|
const location = useLocation();
|
|
111
112
|
const dispatch = useDispatch();
|
|
113
|
+
const isFolderish = useSelector(
|
|
114
|
+
(state) => state?.content?.data?.is_folderish,
|
|
115
|
+
);
|
|
112
116
|
const contextUrl = location.pathname;
|
|
113
117
|
|
|
114
118
|
const [uploading, setUploading] = React.useState(false);
|
|
@@ -156,6 +160,8 @@ const UnconnectedImageInput = (props) => {
|
|
|
156
160
|
|
|
157
161
|
const handleUpload = React.useCallback(
|
|
158
162
|
(eventOrFile) => {
|
|
163
|
+
let uploadUrl = getBaseUrl(contextUrl);
|
|
164
|
+
if (!isFolderish) uploadUrl = getParentUrl(uploadUrl);
|
|
159
165
|
if (restrictFileUpload === true) return;
|
|
160
166
|
eventOrFile.target && eventOrFile.stopPropagation();
|
|
161
167
|
|
|
@@ -171,7 +177,7 @@ const UnconnectedImageInput = (props) => {
|
|
|
171
177
|
const fields = fileData.match(/^data:(.*);(.*),(.*)$/);
|
|
172
178
|
dispatch(
|
|
173
179
|
createContent(
|
|
174
|
-
|
|
180
|
+
uploadUrl,
|
|
175
181
|
{
|
|
176
182
|
'@type': 'Image',
|
|
177
183
|
title: file.name,
|
|
@@ -188,11 +194,12 @@ const UnconnectedImageInput = (props) => {
|
|
|
188
194
|
});
|
|
189
195
|
},
|
|
190
196
|
[
|
|
197
|
+
contextUrl,
|
|
198
|
+
isFolderish,
|
|
191
199
|
restrictFileUpload,
|
|
192
200
|
intl.formatMessage,
|
|
193
201
|
dispatch,
|
|
194
|
-
props,
|
|
195
|
-
contextUrl,
|
|
202
|
+
props.block,
|
|
196
203
|
requestId,
|
|
197
204
|
],
|
|
198
205
|
);
|
|
@@ -305,8 +305,11 @@ export const EMPTY_STYLES_SCHEMA = {
|
|
|
305
305
|
|
|
306
306
|
/**
|
|
307
307
|
* Adds the `styles` field and 'styling' fieldset in a given schema
|
|
308
|
+
* @param {object} params Helper params
|
|
309
|
+
* @param {import('@plone/types').JSONSchema} params.schema Schema to enhance
|
|
310
|
+
* @param {import('react-intl').IntlShape} params.intl intl helper for translations
|
|
308
311
|
*/
|
|
309
|
-
export const addStyling = ({ schema,
|
|
312
|
+
export const addStyling = ({ schema, intl }) => {
|
|
310
313
|
if (isEmpty(find(schema.fieldsets, { id: 'styling' }))) {
|
|
311
314
|
schema.fieldsets.push({
|
|
312
315
|
id: 'styling',
|
package/src/helpers/Url/Url.js
CHANGED
|
@@ -129,10 +129,6 @@ body:not(.has-sidebar):not(.has-sidebar-collapsed) {
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
.toolbar-handler {
|
|
132
|
-
.toolbar-handler-button {
|
|
133
|
-
opacity: 0.3;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
132
|
// State colors
|
|
137
133
|
.published:before {
|
|
138
134
|
background: @teal-blue;
|
|
@@ -252,6 +248,18 @@ body:not(.has-sidebar):not(.has-sidebar-collapsed) {
|
|
|
252
248
|
cursor: pointer;
|
|
253
249
|
transition: opacity 0.3s;
|
|
254
250
|
|
|
251
|
+
.visually-hidden {
|
|
252
|
+
position: absolute !important;
|
|
253
|
+
overflow: hidden !important;
|
|
254
|
+
width: 1px !important;
|
|
255
|
+
height: 1px !important;
|
|
256
|
+
padding: 0 !important;
|
|
257
|
+
border: 0 !important;
|
|
258
|
+
margin: -1px !important;
|
|
259
|
+
clip: rect(0 0 0 0) !important;
|
|
260
|
+
white-space: nowrap !important;
|
|
261
|
+
}
|
|
262
|
+
|
|
255
263
|
&::before {
|
|
256
264
|
position: relative;
|
|
257
265
|
display: block;
|
|
@@ -259,6 +267,15 @@ body:not(.has-sidebar):not(.has-sidebar-collapsed) {
|
|
|
259
267
|
height: 4px;
|
|
260
268
|
background-color: red;
|
|
261
269
|
content: '';
|
|
270
|
+
opacity: 0.65;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
&:hover,
|
|
274
|
+
&:focus {
|
|
275
|
+
&::before {
|
|
276
|
+
opacity: 1;
|
|
277
|
+
transition: opacity 0.3s;
|
|
278
|
+
}
|
|
262
279
|
}
|
|
263
280
|
}
|
|
264
281
|
}
|
|
@@ -714,7 +731,7 @@ body:not(.has-sidebar):not(.has-sidebar-collapsed) {
|
|
|
714
731
|
}
|
|
715
732
|
|
|
716
733
|
.compare-languages {
|
|
717
|
-
@media (max-width: @largestMobileScreen -
|
|
734
|
+
@media (max-width: calc(@largestMobileScreen - 1px)) {
|
|
718
735
|
position: fixed;
|
|
719
736
|
top: 101px;
|
|
720
737
|
left: 0;
|
|
@@ -4,7 +4,7 @@ export declare const RulesControlpanel: import("@loadable/component").LoadableCl
|
|
|
4
4
|
export declare const AddRuleControlpanel: import("@loadable/component").LoadableClassComponent<any>;
|
|
5
5
|
export declare const EditRuleControlpanel: import("@loadable/component").LoadableClassComponent<any>;
|
|
6
6
|
export declare const ConfigureRuleControlpanel: import("@loadable/component").LoadableClassComponent<any>;
|
|
7
|
-
export declare const UsersControlpanel: import("@loadable/component").
|
|
7
|
+
export declare const UsersControlpanel: import("@loadable/component").LoadableComponent<unknown>;
|
|
8
8
|
export declare const RenderUsers: import("@loadable/component").LoadableComponent<any>;
|
|
9
9
|
export declare const UserGroupMembershipControlPanel: import("@loadable/component").LoadableComponent<unknown>;
|
|
10
10
|
export declare const GroupsControlpanel: import("@loadable/component").LoadableClassComponent<any>;
|
|
@@ -1,16 +1,8 @@
|
|
|
1
|
-
export default SidebarPortal;
|
|
2
|
-
/**
|
|
3
|
-
* Portal that wraps Sidebar components
|
|
4
|
-
* @param {React.ReactNode} children Sidebar content
|
|
5
|
-
* @param {bool} selected Sidebar needs to know when the related block is selected
|
|
6
|
-
* @param {string} tab Element id where to insert sidebar content, default: sidebar-properties
|
|
7
|
-
* @returns {string} Rendered sidebar
|
|
8
|
-
*/
|
|
9
|
-
declare function SidebarPortal({ children, selected, tab }: React.ReactNode): string;
|
|
10
|
-
declare namespace SidebarPortal {
|
|
11
|
-
namespace propTypes {
|
|
12
|
-
let children: any;
|
|
13
|
-
let selected: any;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
1
|
import React from 'react';
|
|
2
|
+
type SidebarPortalProps = {
|
|
3
|
+
children?: React.ReactNode;
|
|
4
|
+
selected: boolean;
|
|
5
|
+
tab?: string;
|
|
6
|
+
};
|
|
7
|
+
declare const SidebarPortal: ({ children, selected, tab, }: SidebarPortalProps) => React.ReactPortal;
|
|
8
|
+
export default SidebarPortal;
|
|
@@ -39,11 +39,10 @@ export namespace EMPTY_STYLES_SCHEMA {
|
|
|
39
39
|
let properties: {};
|
|
40
40
|
let required: any[];
|
|
41
41
|
}
|
|
42
|
-
export function addStyling({ schema,
|
|
43
|
-
schema:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}): any;
|
|
42
|
+
export function addStyling({ schema, intl }: {
|
|
43
|
+
schema: import("@plone/types").JSONSchema;
|
|
44
|
+
intl: import("react-intl").IntlShape;
|
|
45
|
+
}): import("@plone/types").JSONSchema;
|
|
47
46
|
/**
|
|
48
47
|
* Sets the field name as first field in schema
|
|
49
48
|
*/
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
|
-
import { createPortal } from 'react-dom';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Portal that wraps Sidebar components
|
|
7
|
-
* @param {React.ReactNode} children Sidebar content
|
|
8
|
-
* @param {bool} selected Sidebar needs to know when the related block is selected
|
|
9
|
-
* @param {string} tab Element id where to insert sidebar content, default: sidebar-properties
|
|
10
|
-
* @returns {string} Rendered sidebar
|
|
11
|
-
*/
|
|
12
|
-
const SidebarPortal = ({ children, selected, tab = 'sidebar-properties' }) => {
|
|
13
|
-
const [isClient, setIsClient] = React.useState(null);
|
|
14
|
-
|
|
15
|
-
React.useEffect(() => setIsClient(true), []);
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<>
|
|
19
|
-
{isClient &&
|
|
20
|
-
selected &&
|
|
21
|
-
createPortal(
|
|
22
|
-
<div role="form" style={{ height: '100%' }}>
|
|
23
|
-
<div
|
|
24
|
-
style={{ height: '100%' }}
|
|
25
|
-
role="presentation"
|
|
26
|
-
onClick={(e) => {
|
|
27
|
-
e.stopPropagation();
|
|
28
|
-
}}
|
|
29
|
-
onKeyDown={(e) => {
|
|
30
|
-
e.stopPropagation();
|
|
31
|
-
}}
|
|
32
|
-
>
|
|
33
|
-
{children}
|
|
34
|
-
</div>
|
|
35
|
-
</div>,
|
|
36
|
-
document.getElementById(tab),
|
|
37
|
-
)}
|
|
38
|
-
</>
|
|
39
|
-
);
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
SidebarPortal.propTypes = {
|
|
43
|
-
children: PropTypes.any,
|
|
44
|
-
selected: PropTypes.bool.isRequired,
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export default SidebarPortal;
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import ShallowRenderer from 'react-test-renderer/shallow';
|
|
3
|
-
|
|
4
|
-
import SidebarPortal from './SidebarPortal';
|
|
5
|
-
|
|
6
|
-
test('sidebar portal is rendered when the block is selected', () => {
|
|
7
|
-
const renderer = new ShallowRenderer();
|
|
8
|
-
renderer.render(
|
|
9
|
-
<SidebarPortal selected={true}>
|
|
10
|
-
<p>Tested!</p>
|
|
11
|
-
</SidebarPortal>,
|
|
12
|
-
);
|
|
13
|
-
const component = renderer.getRenderOutput();
|
|
14
|
-
expect(component).toMatchSnapshot();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
test('sidebar portal is not rendered when the block is not selected', () => {
|
|
18
|
-
const renderer = new ShallowRenderer();
|
|
19
|
-
renderer.render(
|
|
20
|
-
<SidebarPortal selected={false}>
|
|
21
|
-
<p>Tested, but you shouldn't see this in the snapshot!</p>
|
|
22
|
-
</SidebarPortal>,
|
|
23
|
-
);
|
|
24
|
-
const component = renderer.getRenderOutput();
|
|
25
|
-
expect(component).toMatchSnapshot();
|
|
26
|
-
});
|