@plone/volto 16.8.1 → 16.10.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/.changelog.draft +14 -2
- package/.yarn/install-state.gz +0 -0
- package/CHANGELOG.md +42 -0
- package/cypress/support/commands.js +6 -0
- package/package.json +4 -2
- package/packages/volto-slate/package.json +1 -1
- package/packages/volto-slate/src/blocks/Text/DetachedTextBlockEditor.jsx +1 -0
- package/packages/volto-slate/src/editor/SlateEditor.jsx +5 -1
- package/packages/volto-slate/src/editor/ui/InlineToolbar.jsx +3 -1
- package/packages/volto-slate/src/editor/ui/SlateToolbar.jsx +3 -1
- package/src/components/manage/Blocks/Block/Edit.jsx +1 -0
- package/src/components/manage/Blocks/Listing/ListingData.jsx +5 -2
- package/src/components/manage/Blocks/Search/widgets/SelectMetadataField.jsx +1 -1
- package/src/components/manage/Controlpanels/Rules/components/VariableModal.jsx +38 -11
- package/src/components/manage/Edit/Edit.jsx +5 -2
- package/src/components/manage/Form/Form.jsx +8 -4
- package/src/components/manage/Toolbar/Toolbar.jsx +34 -7
- package/src/components/manage/UniversalLink/UniversalLink.jsx +2 -2
- package/src/components/manage/Widgets/ArrayWidget.jsx +1 -0
- package/src/components/manage/Widgets/SelectWidget.jsx +1 -0
- package/src/components/manage/Widgets/TokenWidget.jsx +1 -0
- package/src/components/theme/App/App.jsx +39 -13
- package/src/components/theme/View/View.jsx +9 -2
- package/src/config/index.js +10 -2
- package/src/helpers/FormValidation/FormValidation.js +11 -9
- package/src/helpers/ScrollToTop/ScrollToTop.jsx +11 -1
- package/src/helpers/Utils/Utils.js +5 -3
- package/src/middleware/api.js +22 -15
- package/src/reducers/actions/actions.js +36 -8
- package/src/reducers/actions/actions.test.js +87 -1
- package/src/reducers/breadcrumbs/breadcrumbs.js +50 -13
- package/src/reducers/breadcrumbs/breadcrumbs.test.js +47 -1
- package/src/reducers/navigation/navigation.js +41 -9
- package/src/reducers/navigation/navigation.test.js +49 -1
- package/src/reducers/types/types.js +36 -8
- package/src/reducers/types/types.test.js +31 -1
- package/src/registry.js +18 -0
- package/src/registry.test.js +34 -0
- package/test-setup-config.js +3 -0
package/.changelog.draft
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
|
-
## 16.
|
|
1
|
+
## 16.10.0 (2023-02-06)
|
|
2
|
+
|
|
3
|
+
### Feature
|
|
4
|
+
|
|
5
|
+
- Option for opening /edit with the same vertical offset like the page in view mode before. @ksuess [#3662](https://github.com/plone/volto/issues/3662)
|
|
6
|
+
- Add option to add an action button to the top of the toolbar and to add a menu button to the bottom of the toolbar. @ksuess [#4333](https://github.com/plone/volto/issues/4333)
|
|
7
|
+
- Update to latest versions in the backend for testing and the convenience api folder @sneridagh [#4361](https://github.com/plone/volto/issues/4361)
|
|
8
|
+
- Content Rules: Support server-provided schema for condition and action @ericof [#4368](https://github.com/plone/volto/issues/4368)
|
|
2
9
|
|
|
3
10
|
### Bugfix
|
|
4
11
|
|
|
5
|
-
- Fix
|
|
12
|
+
- Fix react-error-overlay resolution @sneridagh [#4360](https://github.com/plone/volto/issues/4360)
|
|
13
|
+
|
|
14
|
+
### Documentation
|
|
15
|
+
|
|
16
|
+
- Add documentation for copy, cut, and paste blocks in Volto. @MAX-786 [#3827](https://github.com/plone/volto/issues/3827)
|
|
17
|
+
- Fixed Grammar error @SaiRev0 [#4272](https://github.com/plone/volto/issues/4272)
|
|
6
18
|
|
package/.yarn/install-state.gz
CHANGED
|
Binary file
|
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,48 @@
|
|
|
8
8
|
|
|
9
9
|
<!-- towncrier release notes start -->
|
|
10
10
|
|
|
11
|
+
## 16.10.0 (2023-02-06)
|
|
12
|
+
|
|
13
|
+
### Feature
|
|
14
|
+
|
|
15
|
+
- Option for opening /edit with the same vertical offset like the page in view mode before. @ksuess [#3662](https://github.com/plone/volto/issues/3662)
|
|
16
|
+
- Add option to add an action button to the top of the toolbar and to add a menu button to the bottom of the toolbar. @ksuess [#4333](https://github.com/plone/volto/issues/4333)
|
|
17
|
+
- Update to latest versions in the backend for testing and the convenience api folder @sneridagh [#4361](https://github.com/plone/volto/issues/4361)
|
|
18
|
+
- Content Rules: Support server-provided schema for condition and action @ericof [#4368](https://github.com/plone/volto/issues/4368)
|
|
19
|
+
|
|
20
|
+
### Bugfix
|
|
21
|
+
|
|
22
|
+
- Fix react-error-overlay resolution @sneridagh [#4360](https://github.com/plone/volto/issues/4360)
|
|
23
|
+
|
|
24
|
+
### Documentation
|
|
25
|
+
|
|
26
|
+
- Add documentation for copy, cut, and paste blocks in Volto. @MAX-786 [#3827](https://github.com/plone/volto/issues/3827)
|
|
27
|
+
- Fixed Grammar error @SaiRev0 [#4272](https://github.com/plone/volto/issues/4272)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## 16.9.0 (2023-01-27)
|
|
31
|
+
|
|
32
|
+
### Feature
|
|
33
|
+
|
|
34
|
+
- Enable scrolling to ids via hashes in internal links @jackahl [#4165](https://github.com/plone/volto/issues/4165)
|
|
35
|
+
- Read listing block schema from configuration registry @pnicolli [#4231](https://github.com/plone/volto/issues/4231)
|
|
36
|
+
- Add displayName when registering a component @sneridagh [#4282](https://github.com/plone/volto/issues/4282)
|
|
37
|
+
- Support for all default expanders (breadcrumbs, navigation, actions, types) in actions/reducers. Conditional loading of actions if the expanders are present. @sneridagh [#4285](https://github.com/plone/volto/issues/4285)
|
|
38
|
+
- Add `addNewBlock` Cypress support command @sneridagh [#4313](https://github.com/plone/volto/issues/4313)
|
|
39
|
+
|
|
40
|
+
### Bugfix
|
|
41
|
+
|
|
42
|
+
- Fixed maxLength validation for string type fields @pnicolli [#4189](https://github.com/plone/volto/issues/4189)
|
|
43
|
+
- bugfix : add pathname as required proptype in Blocks/Edit @akshatgarg12 [#4194](https://github.com/plone/volto/issues/4194)
|
|
44
|
+
- (Fix) Select Widgets scrolls the page when the options are not visible @dobri1408 [#4223](https://github.com/plone/volto/issues/4223)
|
|
45
|
+
- Updated volto-slate to check for slateSettings before falling back to config @danalvrz [#4311](https://github.com/plone/volto/issues/4311)
|
|
46
|
+
- Fix bug where label of search facet wasn't translated when the content object is being translated @robgietema [#4306](https://github.com/plone/volto/issues/4306)
|
|
47
|
+
|
|
48
|
+
### Internal
|
|
49
|
+
|
|
50
|
+
- Updated 4 Dependencies @SaiRev0 [#4104](https://github.com/plone/volto/issues/4104)
|
|
51
|
+
|
|
52
|
+
|
|
11
53
|
## 16.8.1 (2023-01-18)
|
|
12
54
|
|
|
13
55
|
### Bugfix
|
|
@@ -806,6 +806,12 @@ function createHtmlPasteEvent(htmlContent) {
|
|
|
806
806
|
);
|
|
807
807
|
}
|
|
808
808
|
|
|
809
|
+
Cypress.Commands.add('addNewBlock', (blockName, createNewSlate = false) => {
|
|
810
|
+
let block;
|
|
811
|
+
block = cy.getSlate(createNewSlate).type(`/${blockName}{enter}`);
|
|
812
|
+
return block;
|
|
813
|
+
});
|
|
814
|
+
|
|
809
815
|
Cypress.Commands.add('navigate', (route = '') => {
|
|
810
816
|
return cy.window().its('appHistory').invoke('push', route);
|
|
811
817
|
});
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
}
|
|
10
10
|
],
|
|
11
11
|
"license": "MIT",
|
|
12
|
-
"version": "16.
|
|
12
|
+
"version": "16.10.0",
|
|
13
13
|
"repository": {
|
|
14
14
|
"type": "git",
|
|
15
15
|
"url": "git@github.com:plone/volto.git"
|
|
@@ -281,7 +281,7 @@
|
|
|
281
281
|
"eslint-plugin-prettier": "3.1.3",
|
|
282
282
|
"eslint-plugin-react": "7.20.0",
|
|
283
283
|
"eslint-plugin-react-hooks": "4.0.2",
|
|
284
|
-
"express": "4.17.
|
|
284
|
+
"express": "4.17.3",
|
|
285
285
|
"filesize": "6",
|
|
286
286
|
"glob": "7.1.6",
|
|
287
287
|
"hamburgers": "1.1.3",
|
|
@@ -349,6 +349,7 @@
|
|
|
349
349
|
"react-router": "5.2.0",
|
|
350
350
|
"react-router-config": "5.1.1",
|
|
351
351
|
"react-router-dom": "5.2.0",
|
|
352
|
+
"react-router-hash-link": "2.4.3",
|
|
352
353
|
"react-select": "4.3.1",
|
|
353
354
|
"react-select-async-paginate": "0.5.3",
|
|
354
355
|
"react-share": "2.3.1",
|
|
@@ -421,6 +422,7 @@
|
|
|
421
422
|
"resolutions": {
|
|
422
423
|
"clean-css": "5.3.1",
|
|
423
424
|
"http-proxy": "^1.18.1",
|
|
425
|
+
"react-error-overlay": "6.0.9",
|
|
424
426
|
"ua-parser-js": "0.7.28"
|
|
425
427
|
},
|
|
426
428
|
"volta": {
|
|
@@ -262,7 +262,11 @@ class SlateEditor extends Component {
|
|
|
262
262
|
>
|
|
263
263
|
{selected ? (
|
|
264
264
|
<>
|
|
265
|
-
<InlineToolbar
|
|
265
|
+
<InlineToolbar
|
|
266
|
+
editor={editor}
|
|
267
|
+
className={className}
|
|
268
|
+
slateSettings={this.props.slateSettings}
|
|
269
|
+
/>
|
|
266
270
|
{Object.keys(slateSettings.elementToolbarButtons).map(
|
|
267
271
|
(t, i) => {
|
|
268
272
|
return (
|
|
@@ -17,7 +17,8 @@ const InlineToolbar = (props) => {
|
|
|
17
17
|
setShowExpandedToolbar,
|
|
18
18
|
} = props;
|
|
19
19
|
|
|
20
|
-
const
|
|
20
|
+
const slate = props.slateSettings || config.settings.slate;
|
|
21
|
+
|
|
21
22
|
const [showMainToolbar, setShowMainToolbar] = React.useState(
|
|
22
23
|
!!(editor.selection && hasRangeSelection(editor)),
|
|
23
24
|
);
|
|
@@ -58,6 +59,7 @@ const InlineToolbar = (props) => {
|
|
|
58
59
|
showExpandedToolbar={showExpandedToolbar}
|
|
59
60
|
setShowExpandedToolbar={setShowExpandedToolbar}
|
|
60
61
|
show={showMainToolbar}
|
|
62
|
+
slateSettings={slate}
|
|
61
63
|
/>
|
|
62
64
|
<SlateContextToolbar
|
|
63
65
|
editor={editor}
|
|
@@ -26,7 +26,9 @@ const SlateToolbar = (props) => {
|
|
|
26
26
|
enableExpando = false,
|
|
27
27
|
show,
|
|
28
28
|
} = props;
|
|
29
|
-
|
|
29
|
+
|
|
30
|
+
const slate = props.slateSettings || config.settings.slate;
|
|
31
|
+
|
|
30
32
|
const { toolbarButtons, expandedToolbarButtons, buttons } = slate;
|
|
31
33
|
|
|
32
34
|
function renderButton(name, index) {
|
|
@@ -2,12 +2,15 @@ import React from 'react';
|
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import { useIntl } from 'react-intl';
|
|
4
4
|
import { BlockDataForm } from '@plone/volto/components';
|
|
5
|
-
import
|
|
5
|
+
import config from '@plone/volto/registry';
|
|
6
6
|
|
|
7
7
|
const ListingData = (props) => {
|
|
8
8
|
const { data, block, onChangeBlock } = props;
|
|
9
9
|
const intl = useIntl();
|
|
10
|
-
const schema =
|
|
10
|
+
const schema = config.blocks.blocksConfig.listing.blockSchema({
|
|
11
|
+
...props,
|
|
12
|
+
intl,
|
|
13
|
+
});
|
|
11
14
|
|
|
12
15
|
return (
|
|
13
16
|
<BlockDataForm
|
|
@@ -195,7 +195,7 @@ class SelectWidget extends Component {
|
|
|
195
195
|
styles={customSelectStyles}
|
|
196
196
|
theme={selectTheme}
|
|
197
197
|
components={{ DropdownIndicator, Option }}
|
|
198
|
-
value={value}
|
|
198
|
+
value={{ value: value?.value, label: indexes[value?.value]?.title }}
|
|
199
199
|
onChange={(data) => {
|
|
200
200
|
let dataValue = [];
|
|
201
201
|
if (Array.isArray(data)) {
|
|
@@ -339,6 +339,23 @@ const detectVocabulary = (type) => {
|
|
|
339
339
|
}
|
|
340
340
|
};
|
|
341
341
|
|
|
342
|
+
const processSchema = (schema) => {
|
|
343
|
+
const properties = { ...schema.properties };
|
|
344
|
+
Object.keys(properties).forEach((key) => {
|
|
345
|
+
const value = properties[key];
|
|
346
|
+
const vocabId = value?.vocabulary?.['@id'];
|
|
347
|
+
if (vocabId?.includes('@sources')) {
|
|
348
|
+
value['widget'] = 'internal_url';
|
|
349
|
+
delete value['vocabulary'];
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
const newSchema = {
|
|
353
|
+
...schema,
|
|
354
|
+
properties,
|
|
355
|
+
};
|
|
356
|
+
return newSchema;
|
|
357
|
+
};
|
|
358
|
+
|
|
342
359
|
const VariableModal = ({
|
|
343
360
|
open,
|
|
344
361
|
type,
|
|
@@ -371,17 +388,27 @@ const VariableModal = ({
|
|
|
371
388
|
React.useEffect(() => {
|
|
372
389
|
//reset input schema to rehydrate options
|
|
373
390
|
setInputSchema('');
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
391
|
+
// On creation, @schema is provided by value
|
|
392
|
+
// On update, @schema comes from formData
|
|
393
|
+
const rawSchema = value['@schema'] || formData['@schema'];
|
|
394
|
+
let schema;
|
|
395
|
+
if (rawSchema) {
|
|
396
|
+
schema = processSchema(rawSchema);
|
|
397
|
+
} else {
|
|
398
|
+
const vocabularyOptions =
|
|
399
|
+
vocabularyName &&
|
|
400
|
+
vocabularies &&
|
|
401
|
+
vocabularies[vocabularyName] &&
|
|
402
|
+
vocabularies[vocabularyName].items
|
|
403
|
+
? vocabularies[vocabularyName].items.map((item, i) => {
|
|
404
|
+
return [item.value, item.label];
|
|
405
|
+
})
|
|
406
|
+
: [];
|
|
407
|
+
//set schema with the new options from vocabulary
|
|
408
|
+
schema = setSchema(value.title, vocabularyOptions);
|
|
409
|
+
}
|
|
410
|
+
setInputSchema(schema);
|
|
411
|
+
|
|
385
412
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
386
413
|
}, [vocabularies, open, formData, value]);
|
|
387
414
|
|
|
@@ -8,7 +8,7 @@ import PropTypes from 'prop-types';
|
|
|
8
8
|
import { Helmet } from '@plone/volto/helpers';
|
|
9
9
|
import { connect } from 'react-redux';
|
|
10
10
|
import { compose } from 'redux';
|
|
11
|
-
import { asyncConnect } from '@plone/volto/helpers';
|
|
11
|
+
import { asyncConnect, hasApiExpander } from '@plone/volto/helpers';
|
|
12
12
|
import { defineMessages, injectIntl } from 'react-intl';
|
|
13
13
|
import { Button, Grid, Menu } from 'semantic-ui-react';
|
|
14
14
|
import { Portal } from 'react-portal';
|
|
@@ -475,7 +475,10 @@ export default compose(
|
|
|
475
475
|
{
|
|
476
476
|
key: 'actions',
|
|
477
477
|
promise: async ({ location, store: { dispatch } }) => {
|
|
478
|
-
|
|
478
|
+
// Do not trigger the actions action if the expander is present
|
|
479
|
+
if (!hasApiExpander('actions', getBaseUrl(location.pathname))) {
|
|
480
|
+
return await dispatch(listActions(getBaseUrl(location.pathname)));
|
|
481
|
+
}
|
|
479
482
|
},
|
|
480
483
|
},
|
|
481
484
|
{
|
|
@@ -180,10 +180,11 @@ class Form extends Component {
|
|
|
180
180
|
formData.hasOwnProperty(blocksLayoutFieldname) &&
|
|
181
181
|
formData[blocksLayoutFieldname].items.length > 0
|
|
182
182
|
) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if (config.blocks?.initialBlocksFocus
|
|
186
|
-
//Default selected is not the first block, but the one from config.
|
|
183
|
+
if (config.blocks?.initialBlocksFocus === null) {
|
|
184
|
+
selectedBlock = null;
|
|
185
|
+
} else if (this.props.type in config.blocks?.initialBlocksFocus) {
|
|
186
|
+
// Default selected is not the first block, but the one from config.
|
|
187
|
+
// TODO Select first block and not an arbitrary one.
|
|
187
188
|
Object.keys(formData[blocksFieldname]).forEach((b_key) => {
|
|
188
189
|
if (
|
|
189
190
|
formData[blocksFieldname][b_key]['@type'] ===
|
|
@@ -192,8 +193,11 @@ class Form extends Component {
|
|
|
192
193
|
selectedBlock = b_key;
|
|
193
194
|
}
|
|
194
195
|
});
|
|
196
|
+
} else {
|
|
197
|
+
selectedBlock = formData[blocksLayoutFieldname].items[0];
|
|
195
198
|
}
|
|
196
199
|
}
|
|
200
|
+
|
|
197
201
|
this.state = {
|
|
198
202
|
formData,
|
|
199
203
|
initialFormData: cloneDeep(formData),
|
|
@@ -29,7 +29,12 @@ import {
|
|
|
29
29
|
unlockContent,
|
|
30
30
|
} from '@plone/volto/actions';
|
|
31
31
|
import { Icon } from '@plone/volto/components';
|
|
32
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
BodyClass,
|
|
34
|
+
getBaseUrl,
|
|
35
|
+
getCookieOptions,
|
|
36
|
+
hasApiExpander,
|
|
37
|
+
} from '@plone/volto/helpers';
|
|
33
38
|
import { Pluggable } from '@plone/volto/components/manage/Pluggable';
|
|
34
39
|
|
|
35
40
|
import penSVG from '@plone/volto/icons/pen.svg';
|
|
@@ -104,7 +109,7 @@ const messages = defineMessages({
|
|
|
104
109
|
},
|
|
105
110
|
});
|
|
106
111
|
|
|
107
|
-
|
|
112
|
+
let toolbarComponents = {
|
|
108
113
|
personalTools: { component: PersonalTools, wrapper: null },
|
|
109
114
|
more: { component: More, wrapper: null },
|
|
110
115
|
types: { component: Types, wrapper: null, contentAsProps: true },
|
|
@@ -197,8 +202,20 @@ class Toolbar extends Component {
|
|
|
197
202
|
* @returns {undefined}
|
|
198
203
|
*/
|
|
199
204
|
componentDidMount() {
|
|
200
|
-
|
|
201
|
-
|
|
205
|
+
// Do not trigger the actions action if the expander is present
|
|
206
|
+
if (!hasApiExpander('actions', getBaseUrl(this.props.pathname))) {
|
|
207
|
+
this.props.listActions(getBaseUrl(this.props.pathname));
|
|
208
|
+
}
|
|
209
|
+
// Do not trigger the types action if the expander is present
|
|
210
|
+
if (!hasApiExpander('types', getBaseUrl(this.props.pathname))) {
|
|
211
|
+
this.props.getTypes(getBaseUrl(this.props.pathname));
|
|
212
|
+
}
|
|
213
|
+
toolbarComponents = {
|
|
214
|
+
...(config.settings
|
|
215
|
+
? config.settings.additionalToolbarComponents || {}
|
|
216
|
+
: {}),
|
|
217
|
+
...toolbarComponents,
|
|
218
|
+
};
|
|
202
219
|
this.props.setExpandedToolbar(this.state.expanded);
|
|
203
220
|
document.addEventListener('mousedown', this.handleClickOutside, false);
|
|
204
221
|
}
|
|
@@ -211,8 +228,14 @@ class Toolbar extends Component {
|
|
|
211
228
|
*/
|
|
212
229
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
213
230
|
if (nextProps.pathname !== this.props.pathname) {
|
|
214
|
-
|
|
215
|
-
|
|
231
|
+
// Do not trigger the actions action if the expander is present
|
|
232
|
+
if (!hasApiExpander('actions', getBaseUrl(nextProps.pathname))) {
|
|
233
|
+
this.props.listActions(getBaseUrl(nextProps.pathname));
|
|
234
|
+
}
|
|
235
|
+
// Do not trigger the types action if the expander is present
|
|
236
|
+
if (!hasApiExpander('types', getBaseUrl(nextProps.pathname))) {
|
|
237
|
+
this.props.getTypes(getBaseUrl(nextProps.pathname));
|
|
238
|
+
}
|
|
216
239
|
}
|
|
217
240
|
|
|
218
241
|
// Unlock
|
|
@@ -534,9 +557,13 @@ class Toolbar extends Component {
|
|
|
534
557
|
</button>
|
|
535
558
|
</>
|
|
536
559
|
)}
|
|
560
|
+
<Pluggable name="main.toolbar.top" />
|
|
537
561
|
</div>
|
|
538
562
|
<div className="toolbar-bottom">
|
|
539
|
-
<Pluggable
|
|
563
|
+
<Pluggable
|
|
564
|
+
name="main.toolbar.bottom"
|
|
565
|
+
params={{ onClickHandler: this.toggleMenu }}
|
|
566
|
+
/>
|
|
540
567
|
{!this.props.hideDefaultViewButtons && (
|
|
541
568
|
<button
|
|
542
569
|
className="user"
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import React from 'react';
|
|
7
7
|
import PropTypes from 'prop-types';
|
|
8
|
-
import { Link } from 'react-router-
|
|
8
|
+
import { HashLink as Link } from 'react-router-hash-link';
|
|
9
9
|
import { useSelector } from 'react-redux';
|
|
10
10
|
import {
|
|
11
11
|
flattenToAppURL,
|
|
@@ -78,13 +78,13 @@ const UniversalLink = ({
|
|
|
78
78
|
const checkedURL = URLUtils.checkAndNormalizeUrl(url);
|
|
79
79
|
|
|
80
80
|
url = checkedURL.url;
|
|
81
|
-
|
|
82
81
|
let tag = (
|
|
83
82
|
<Link
|
|
84
83
|
to={flattenToAppURL(url)}
|
|
85
84
|
target={openLinkInNewTab ?? false ? '_blank' : null}
|
|
86
85
|
title={title}
|
|
87
86
|
className={className}
|
|
87
|
+
smooth={config.settings.hashLinkSmoothScroll}
|
|
88
88
|
{...props}
|
|
89
89
|
>
|
|
90
90
|
{children}
|
|
@@ -307,6 +307,7 @@ class ArrayWidget extends Component {
|
|
|
307
307
|
// react-sortable-hoc props:
|
|
308
308
|
axis="xy"
|
|
309
309
|
onSortEnd={this.onSortEnd}
|
|
310
|
+
menuShouldScrollIntoView={false}
|
|
310
311
|
distance={4}
|
|
311
312
|
// small fix for https://github.com/clauderic/react-sortable-hoc/pull/352:
|
|
312
313
|
getHelperDimensions={({ node }) => node.getBoundingClientRect()}
|
|
@@ -174,6 +174,7 @@ class TokenWidget extends Component {
|
|
|
174
174
|
<CreatableSelect
|
|
175
175
|
id={`field-${this.props.id}`}
|
|
176
176
|
key={this.props.id}
|
|
177
|
+
menuShouldScrollIntoView={false}
|
|
177
178
|
isDisabled={this.props.isDisabled}
|
|
178
179
|
className="react-select-container"
|
|
179
180
|
classNamePrefix="react-select"
|
|
@@ -32,7 +32,13 @@ import {
|
|
|
32
32
|
AppExtras,
|
|
33
33
|
SkipLinks,
|
|
34
34
|
} from '@plone/volto/components';
|
|
35
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
BodyClass,
|
|
37
|
+
getBaseUrl,
|
|
38
|
+
getView,
|
|
39
|
+
hasApiExpander,
|
|
40
|
+
isCmsUi,
|
|
41
|
+
} from '@plone/volto/helpers';
|
|
36
42
|
import {
|
|
37
43
|
getBreadcrumbs,
|
|
38
44
|
getContent,
|
|
@@ -257,8 +263,15 @@ export default compose(
|
|
|
257
263
|
asyncConnect([
|
|
258
264
|
{
|
|
259
265
|
key: 'breadcrumbs',
|
|
260
|
-
promise: ({ location, store: { dispatch } }) =>
|
|
261
|
-
|
|
266
|
+
promise: ({ location, store: { dispatch } }) => {
|
|
267
|
+
// Do not trigger the breadcrumbs action if the expander is present
|
|
268
|
+
if (
|
|
269
|
+
__SERVER__ &&
|
|
270
|
+
!hasApiExpander('breadcrumbs', getBaseUrl(location.pathname))
|
|
271
|
+
) {
|
|
272
|
+
return dispatch(getBreadcrumbs(getBaseUrl(location.pathname)));
|
|
273
|
+
}
|
|
274
|
+
},
|
|
262
275
|
},
|
|
263
276
|
{
|
|
264
277
|
key: 'content',
|
|
@@ -267,19 +280,32 @@ export default compose(
|
|
|
267
280
|
},
|
|
268
281
|
{
|
|
269
282
|
key: 'navigation',
|
|
270
|
-
promise: ({ location, store: { dispatch } }) =>
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
283
|
+
promise: ({ location, store: { dispatch } }) => {
|
|
284
|
+
// Do not trigger the navigation action if the expander is present
|
|
285
|
+
if (
|
|
286
|
+
__SERVER__ &&
|
|
287
|
+
!hasApiExpander('navigation', getBaseUrl(location.pathname))
|
|
288
|
+
) {
|
|
289
|
+
return dispatch(
|
|
290
|
+
getNavigation(
|
|
291
|
+
getBaseUrl(location.pathname),
|
|
292
|
+
config.settings.navDepth,
|
|
293
|
+
),
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
},
|
|
278
297
|
},
|
|
279
298
|
{
|
|
280
299
|
key: 'types',
|
|
281
|
-
promise: ({ location, store: { dispatch } }) =>
|
|
282
|
-
|
|
300
|
+
promise: ({ location, store: { dispatch } }) => {
|
|
301
|
+
// Do not trigger the types action if the expander is present
|
|
302
|
+
if (
|
|
303
|
+
__SERVER__ &&
|
|
304
|
+
!hasApiExpander('types', getBaseUrl(location.pathname))
|
|
305
|
+
) {
|
|
306
|
+
return dispatch(getTypes(getBaseUrl(location.pathname)));
|
|
307
|
+
}
|
|
308
|
+
},
|
|
283
309
|
},
|
|
284
310
|
{
|
|
285
311
|
key: 'workflow',
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
getBaseUrl,
|
|
25
25
|
flattenToAppURL,
|
|
26
26
|
getLayoutFieldname,
|
|
27
|
+
hasApiExpander,
|
|
27
28
|
} from '@plone/volto/helpers';
|
|
28
29
|
|
|
29
30
|
import config from '@plone/volto/registry';
|
|
@@ -118,7 +119,10 @@ class View extends Component {
|
|
|
118
119
|
};
|
|
119
120
|
|
|
120
121
|
componentDidMount() {
|
|
121
|
-
|
|
122
|
+
// Do not trigger the actions action if the expander is present
|
|
123
|
+
if (!hasApiExpander('actions', getBaseUrl(this.props.pathname))) {
|
|
124
|
+
this.props.listActions(getBaseUrl(this.props.pathname));
|
|
125
|
+
}
|
|
122
126
|
this.props.getContent(
|
|
123
127
|
getBaseUrl(this.props.pathname),
|
|
124
128
|
this.props.versionId,
|
|
@@ -134,7 +138,10 @@ class View extends Component {
|
|
|
134
138
|
*/
|
|
135
139
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
136
140
|
if (nextProps.pathname !== this.props.pathname) {
|
|
137
|
-
|
|
141
|
+
// Do not trigger the actions action if the expander is present
|
|
142
|
+
if (!hasApiExpander('actions', getBaseUrl(nextProps.pathname))) {
|
|
143
|
+
this.props.listActions(getBaseUrl(nextProps.pathname));
|
|
144
|
+
}
|
|
138
145
|
this.props.getContent(
|
|
139
146
|
getBaseUrl(nextProps.pathname),
|
|
140
147
|
this.props.versionId,
|
package/src/config/index.js
CHANGED
|
@@ -75,11 +75,18 @@ let config = {
|
|
|
75
75
|
// The URL Volto is going to be served (see sensible defaults above)
|
|
76
76
|
publicURL,
|
|
77
77
|
apiPath,
|
|
78
|
-
apiExpanders: [
|
|
78
|
+
apiExpanders: [
|
|
79
|
+
// Add the following expanders for only issuing a single request.
|
|
80
|
+
// https://6.docs.plone.org/volto/configuration/settings-reference.html#term-apiExpanders
|
|
81
|
+
// {
|
|
82
|
+
// match: '',
|
|
83
|
+
// GET_CONTENT: ['breadcrumbs', 'navigation', 'actions', 'types'],
|
|
84
|
+
// },
|
|
85
|
+
],
|
|
79
86
|
// Internal proxy to bypass CORS *while developing*. NOT intended for production use.
|
|
80
87
|
// In production is recommended you use a Seamless mode deployment using a web server in
|
|
81
88
|
// front of both the frontend and the backend so you can bypass CORS safely.
|
|
82
|
-
// https://docs.
|
|
89
|
+
// https://6.docs.plone.org/volto/deploying/seamless-mode.html
|
|
83
90
|
devProxyToApiPath:
|
|
84
91
|
process.env.RAZZLE_DEV_PROXY_API_PATH ||
|
|
85
92
|
process.env.RAZZLE_API_PATH ||
|
|
@@ -169,6 +176,7 @@ let config = {
|
|
|
169
176
|
workflowMapping,
|
|
170
177
|
errorHandlers: [], // callables for unhandled errors
|
|
171
178
|
styleClassNameConverters,
|
|
179
|
+
hashLinkSmoothScroll: false,
|
|
172
180
|
styleClassNameExtenders,
|
|
173
181
|
},
|
|
174
182
|
experimental: {
|
|
@@ -4,27 +4,29 @@ import { messages } from '../MessageLabels/MessageLabels';
|
|
|
4
4
|
/**
|
|
5
5
|
* Will return the intl message if invalid
|
|
6
6
|
* @param {boolean} isValid
|
|
7
|
-
* @param {string}
|
|
7
|
+
* @param {string} criterion
|
|
8
8
|
* @param {string | number} valueToCompare can compare '47' < 50
|
|
9
9
|
* @param {Function} intlFunc
|
|
10
10
|
*/
|
|
11
|
-
const validationMessage = (isValid,
|
|
11
|
+
const validationMessage = (isValid, criterion, valueToCompare, intlFunc) =>
|
|
12
12
|
!isValid
|
|
13
|
-
? intlFunc(messages[
|
|
13
|
+
? intlFunc(messages[criterion], {
|
|
14
14
|
len: valueToCompare,
|
|
15
15
|
})
|
|
16
16
|
: null;
|
|
17
|
+
|
|
17
18
|
/**
|
|
18
19
|
* Returns if based on the criterion the value is lower or equal
|
|
19
20
|
* @param {string | number} value can compare '47' < 50
|
|
20
21
|
* @param {string | number} valueToCompare can compare '47' < 50
|
|
21
|
-
* @param {string}
|
|
22
|
+
* @param {string} maxCriterion
|
|
22
23
|
* @param {Function} intlFunc
|
|
23
24
|
*/
|
|
24
|
-
const isMaxPropertyValid = (value, valueToCompare,
|
|
25
|
+
const isMaxPropertyValid = (value, valueToCompare, maxCriterion, intlFunc) => {
|
|
25
26
|
const isValid = valueToCompare !== undefined ? value <= valueToCompare : true;
|
|
26
|
-
return validationMessage(isValid,
|
|
27
|
+
return validationMessage(isValid, maxCriterion, valueToCompare, intlFunc);
|
|
27
28
|
};
|
|
29
|
+
|
|
28
30
|
/**
|
|
29
31
|
* Returns if based on the criterion the value is higher or equal
|
|
30
32
|
* @param {string | number} value can compare '47' < 50
|
|
@@ -32,9 +34,9 @@ const isMaxPropertyValid = (value, valueToCompare, minCriterion, intlFunc) => {
|
|
|
32
34
|
* @param {string} minCriterion
|
|
33
35
|
* @param {Function} intlFunc
|
|
34
36
|
*/
|
|
35
|
-
const isMinPropertyValid = (value, valueToCompare,
|
|
37
|
+
const isMinPropertyValid = (value, valueToCompare, minCriterion, intlFunc) => {
|
|
36
38
|
const isValid = valueToCompare !== undefined ? value >= valueToCompare : true;
|
|
37
|
-
return validationMessage(isValid,
|
|
39
|
+
return validationMessage(isValid, minCriterion, valueToCompare, intlFunc);
|
|
38
40
|
};
|
|
39
41
|
|
|
40
42
|
const widgetValidation = {
|
|
@@ -109,7 +111,7 @@ const widgetValidation = {
|
|
|
109
111
|
maxLength: (value, itemObj, intlFunc) =>
|
|
110
112
|
isMaxPropertyValid(
|
|
111
113
|
value.length,
|
|
112
|
-
itemObj.
|
|
114
|
+
itemObj.maxLength,
|
|
113
115
|
'maxLength',
|
|
114
116
|
intlFunc,
|
|
115
117
|
),
|