@plone/volto 17.13.0 → 17.15.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.md +19 -0
- package/addon-registry.js +7 -1
- package/cypress/support/commands.js +51 -3
- package/package.json +1 -1
- package/packages/volto-slate/package.json +1 -1
- package/src/actions/index.js +6 -1
- package/src/actions/sidebar/sidebar.js +45 -1
- package/src/actions/sidebar/sidebar.test.js +42 -2
- package/src/components/manage/Blocks/Listing/getAsyncData.js +23 -8
- package/src/components/manage/Form/Form.jsx +114 -30
- package/src/components/theme/App/App.jsx +1 -0
- package/src/components/theme/AppExtras/AppExtras.jsx +2 -0
- package/src/components/theme/AppExtras/AppExtras.test.jsx +20 -0
- package/src/constants/ActionTypes.js +3 -0
- package/src/reducers/sidebar/sidebar.js +26 -1
- package/src/reducers/sidebar/sidebar.test.js +50 -1
- package/types/actions/index.d.ts +1 -1
- package/types/actions/sidebar/sidebar.d.ts +21 -0
- package/types/components/manage/Blocks/Listing/getAsyncData.d.ts +1 -7
- package/types/components/manage/Form/Form.d.ts +1 -1
- package/types/constants/ActionTypes.d.ts +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -17,6 +17,25 @@ myst:
|
|
|
17
17
|
|
|
18
18
|
<!-- towncrier release notes start -->
|
|
19
19
|
|
|
20
|
+
## 17.15.0 (2024-02-21)
|
|
21
|
+
|
|
22
|
+
### Feature
|
|
23
|
+
|
|
24
|
+
- Add accordion to metadata form. @robgietema [#5760](https://github.com/plone/volto/issues/5760)
|
|
25
|
+
|
|
26
|
+
## 17.14.0 (2024-02-18)
|
|
27
|
+
|
|
28
|
+
### Feature
|
|
29
|
+
|
|
30
|
+
- Added the `ignore` property to allow exceptions to rules that are applied to all routes. @dobri1408 [#5621](https://github.com/plone/volto/issues/5621)
|
|
31
|
+
|
|
32
|
+
### Bugfix
|
|
33
|
+
|
|
34
|
+
- Fixed listing SSR rendering by sending `subrequestId` instead of `id` only within `getAsyncData`, similar to calling `getQueryStringResults` directly. @ichim-david
|
|
35
|
+
Fixed listing SSR pagination rendering by sending `currentPage` value besides the `subrequestId`. @ichim-david
|
|
36
|
+
Added testing for SSR rendering for all of the listing block tests. @ichim-david [#5688](https://github.com/plone/volto/issues/5688)
|
|
37
|
+
- Add extra wait calls to listing block tests to avoid sporadic failures. @ichim-david [#5753](https://github.com/plone/volto/issues/5753)
|
|
38
|
+
|
|
20
39
|
## 17.13.0 (2024-02-09)
|
|
21
40
|
|
|
22
41
|
### Feature
|
package/addon-registry.js
CHANGED
|
@@ -105,7 +105,13 @@ class AddonConfigurationRegistry {
|
|
|
105
105
|
path.join(projectRootPath, 'package.json'),
|
|
106
106
|
));
|
|
107
107
|
// Loads the dynamic config, if any
|
|
108
|
-
if (
|
|
108
|
+
if (process.env.VOLTOCONFIG) {
|
|
109
|
+
if (fs.existsSync(path.resolve(process.env.VOLTOCONFIG))) {
|
|
110
|
+
const voltoConfigPath = path.resolve(process.env.VOLTOCONFIG);
|
|
111
|
+
console.log(`Using volto.config.js in: ${voltoConfigPath}`);
|
|
112
|
+
this.voltoConfigJS = require(voltoConfigPath);
|
|
113
|
+
}
|
|
114
|
+
} else if (fs.existsSync(path.join(projectRootPath, 'volto.config.js'))) {
|
|
109
115
|
this.voltoConfigJS = require(
|
|
110
116
|
path.join(projectRootPath, 'volto.config.js'),
|
|
111
117
|
);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
1
2
|
import { getIfExists } from '../helpers';
|
|
2
3
|
import { ploneAuth } from './constants';
|
|
3
4
|
|
|
@@ -39,6 +40,30 @@ Cypress.Commands.add('isInViewport', (element) => {
|
|
|
39
40
|
});
|
|
40
41
|
});
|
|
41
42
|
|
|
43
|
+
// --- isInHTML ----------------------------------------------------------
|
|
44
|
+
Cypress.Commands.add('isInHTML', ({ parent = 'body', content }) => {
|
|
45
|
+
cy.url().then((currentUrl) => {
|
|
46
|
+
// sometimes the cy command is called when the url is still at content/edit
|
|
47
|
+
// we want to query the html markup of the content, not the edit form
|
|
48
|
+
const url =
|
|
49
|
+
currentUrl.indexOf('/edit') !== -1
|
|
50
|
+
? currentUrl.split('/edit')[0]
|
|
51
|
+
: currentUrl;
|
|
52
|
+
cy.request({
|
|
53
|
+
method: 'GET',
|
|
54
|
+
url: url,
|
|
55
|
+
}).then((response) => {
|
|
56
|
+
const html = Cypress.$(response.body);
|
|
57
|
+
if (content.startsWith('.') || content.startsWith('#')) {
|
|
58
|
+
return expect(html.find(parent)).to.have.descendants(content);
|
|
59
|
+
} else {
|
|
60
|
+
// check if parent contains the content text string in its HTML output
|
|
61
|
+
return expect(html.find(parent)).to.contain(content);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
42
67
|
// --- AUTOLOGIN -------------------------------------------------------------
|
|
43
68
|
Cypress.Commands.add('autologin', (usr, pass) => {
|
|
44
69
|
let api_url, user, password;
|
|
@@ -695,7 +720,7 @@ Cypress.Commands.add('clearSlate', (selector) => {
|
|
|
695
720
|
return cy
|
|
696
721
|
.get(selector)
|
|
697
722
|
.focus()
|
|
698
|
-
.click()
|
|
723
|
+
.click({ force: true }) // fix sporadic failure this element is currently animating
|
|
699
724
|
.wait(1000)
|
|
700
725
|
.type('{selectAll}')
|
|
701
726
|
.wait(1000)
|
|
@@ -819,7 +844,6 @@ function getTextNode(el, match) {
|
|
|
819
844
|
return walk.nextNode();
|
|
820
845
|
}
|
|
821
846
|
|
|
822
|
-
const nodes = [];
|
|
823
847
|
let node;
|
|
824
848
|
while ((node = walk.nextNode())) {
|
|
825
849
|
if (node.wholeText.includes(match)) {
|
|
@@ -848,7 +872,7 @@ function createHtmlPasteEvent(htmlContent) {
|
|
|
848
872
|
|
|
849
873
|
Cypress.Commands.add('addNewBlock', (blockName, createNewSlate = false) => {
|
|
850
874
|
let block;
|
|
851
|
-
block = cy.getSlate(createNewSlate).type(`/${blockName}{enter}`);
|
|
875
|
+
block = cy.getSlate(createNewSlate).click().type(`/${blockName}{enter}`);
|
|
852
876
|
return block;
|
|
853
877
|
});
|
|
854
878
|
|
|
@@ -902,9 +926,33 @@ Cypress.Commands.add('configureListingWith', (contentType) => {
|
|
|
902
926
|
'.querystring-widget .fields:first-of-type > .field .react-select__menu .react-select__option',
|
|
903
927
|
)
|
|
904
928
|
.contains(contentType)
|
|
929
|
+
|
|
905
930
|
.click();
|
|
906
931
|
});
|
|
907
932
|
|
|
933
|
+
Cypress.Commands.add(
|
|
934
|
+
'addLocationQuerystring',
|
|
935
|
+
(option = 'Relative path', value) => {
|
|
936
|
+
cy.get('.block-editor-listing').click();
|
|
937
|
+
cy.get('.querystring-widget .fields').contains('Add criteria').click();
|
|
938
|
+
cy.get('.querystring-widget .react-select__menu .react-select__option')
|
|
939
|
+
.contains('Location')
|
|
940
|
+
.click();
|
|
941
|
+
|
|
942
|
+
cy.get('.querystring-widget .fields').contains('Absolute path').click();
|
|
943
|
+
cy.get(
|
|
944
|
+
'.querystring-widget .fields .react-select__menu .react-select__option',
|
|
945
|
+
)
|
|
946
|
+
.contains(option)
|
|
947
|
+
.click();
|
|
948
|
+
if (value) {
|
|
949
|
+
cy.get('.querystring-widget .fields .input')
|
|
950
|
+
.click()
|
|
951
|
+
.type(`${value}{enter}`);
|
|
952
|
+
}
|
|
953
|
+
},
|
|
954
|
+
);
|
|
955
|
+
|
|
908
956
|
Cypress.Commands.add('queryCounter', (path, steps, number = 1) => {
|
|
909
957
|
cy.intercept(path, cy.spy().as('counterName'));
|
|
910
958
|
steps.forEach((element) => {
|
package/package.json
CHANGED
package/src/actions/index.js
CHANGED
|
@@ -150,7 +150,12 @@ export {
|
|
|
150
150
|
} from '@plone/volto/actions/workflow/workflow';
|
|
151
151
|
export { getQuerystring } from '@plone/volto/actions/querystring/querystring';
|
|
152
152
|
export { getQueryStringResults } from '@plone/volto/actions/querystringsearch/querystringsearch';
|
|
153
|
-
export {
|
|
153
|
+
export {
|
|
154
|
+
setMetadataFieldsets,
|
|
155
|
+
setMetadataFocus,
|
|
156
|
+
resetMetadataFocus,
|
|
157
|
+
setSidebarTab,
|
|
158
|
+
} from '@plone/volto/actions/sidebar/sidebar';
|
|
154
159
|
export { setFormData } from '@plone/volto/actions/form/form';
|
|
155
160
|
export {
|
|
156
161
|
deleteLinkTranslation,
|
|
@@ -3,7 +3,51 @@
|
|
|
3
3
|
* @module actions/sidebar/sidebar
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
SET_METADATA_FIELDSETS,
|
|
8
|
+
SET_METADATA_FOCUS,
|
|
9
|
+
RESET_METADATA_FOCUS,
|
|
10
|
+
SET_SIDEBAR_TAB,
|
|
11
|
+
} from '@plone/volto/constants/ActionTypes';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Set metadata fieldsets function.
|
|
15
|
+
* @function setMetadataFieldsets
|
|
16
|
+
* @param {Array} fieldsets New fieldsets.
|
|
17
|
+
* @returns {Object} Set metadata fieldsets action.
|
|
18
|
+
*/
|
|
19
|
+
export function setMetadataFieldsets(fieldsets) {
|
|
20
|
+
return {
|
|
21
|
+
type: SET_METADATA_FIELDSETS,
|
|
22
|
+
fieldsets,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Set metadata focus function.
|
|
28
|
+
* @function setMetadataFocus
|
|
29
|
+
* @param {String} fieldset Fieldset of the field.
|
|
30
|
+
* @param {String} field Field to set focus too.
|
|
31
|
+
* @returns {Object} Set metadata focus action.
|
|
32
|
+
*/
|
|
33
|
+
export function setMetadataFocus(fieldset, field) {
|
|
34
|
+
return {
|
|
35
|
+
type: SET_METADATA_FOCUS,
|
|
36
|
+
fieldset,
|
|
37
|
+
field,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Resets metadata focus function.
|
|
43
|
+
* @function resetMetadataFocus
|
|
44
|
+
* @returns {Object} Set metadata focus action.
|
|
45
|
+
*/
|
|
46
|
+
export function resetMetadataFocus() {
|
|
47
|
+
return {
|
|
48
|
+
type: RESET_METADATA_FOCUS,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
7
51
|
|
|
8
52
|
/**
|
|
9
53
|
* Set sidebar tab function.
|
|
@@ -1,7 +1,47 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
setMetadataFieldsets,
|
|
3
|
+
setMetadataFocus,
|
|
4
|
+
resetMetadataFocus,
|
|
5
|
+
setSidebarTab,
|
|
6
|
+
} from './sidebar';
|
|
7
|
+
import {
|
|
8
|
+
SET_METADATA_FIELDSETS,
|
|
9
|
+
SET_METADATA_FOCUS,
|
|
10
|
+
RESET_METADATA_FOCUS,
|
|
11
|
+
SET_SIDEBAR_TAB,
|
|
12
|
+
} from '@plone/volto/constants/ActionTypes';
|
|
3
13
|
|
|
4
14
|
describe('Sidebar action', () => {
|
|
15
|
+
describe('setMetadataFieldsets', () => {
|
|
16
|
+
it('should create an action to set the metadata fieldsets', () => {
|
|
17
|
+
const fieldsets = ['default'];
|
|
18
|
+
const action = setMetadataFieldsets(fieldsets);
|
|
19
|
+
|
|
20
|
+
expect(action.type).toEqual(SET_METADATA_FIELDSETS);
|
|
21
|
+
expect(action.fieldsets).toEqual(fieldsets);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('setMetadataFocus', () => {
|
|
26
|
+
it('should create an action to set the metadata focus', () => {
|
|
27
|
+
const fieldset = ['default'];
|
|
28
|
+
const field = ['title'];
|
|
29
|
+
const action = setMetadataFocus(fieldset, field);
|
|
30
|
+
|
|
31
|
+
expect(action.type).toEqual(SET_METADATA_FOCUS);
|
|
32
|
+
expect(action.fieldset).toEqual(fieldset);
|
|
33
|
+
expect(action.field).toEqual(field);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('resetMetadataFocus', () => {
|
|
38
|
+
it('should create an action to reset the metadata focus', () => {
|
|
39
|
+
const action = resetMetadataFocus();
|
|
40
|
+
|
|
41
|
+
expect(action.type).toEqual(RESET_METADATA_FOCUS);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
5
45
|
describe('setSidebarTab', () => {
|
|
6
46
|
it('should create an action to set the sidebar', () => {
|
|
7
47
|
const index = 1;
|
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
import { getQueryStringResults } from '@plone/volto/actions';
|
|
2
2
|
import { resolveBlockExtensions } from '@plone/volto/helpers';
|
|
3
|
+
import qs from 'query-string';
|
|
4
|
+
import { slugify } from '@plone/volto/helpers/Utils/Utils';
|
|
5
|
+
|
|
6
|
+
const getCurrentPage = (location, id) => {
|
|
7
|
+
const pageQueryParam = qs.parse(location.search);
|
|
8
|
+
switch (Object.keys(pageQueryParam).length) {
|
|
9
|
+
case 0:
|
|
10
|
+
return 1;
|
|
11
|
+
case 1:
|
|
12
|
+
// when there is only one query param, it could be the simple page number or the sluggified block id
|
|
13
|
+
return pageQueryParam['page'] || pageQueryParam[slugify(`page-${id}`)];
|
|
14
|
+
default:
|
|
15
|
+
return pageQueryParam[slugify(`page-${id}`)];
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default function getListingBlockAsyncData(props) {
|
|
20
|
+
const { data, path, location, id, dispatch, blocksConfig, content } = props;
|
|
3
21
|
|
|
4
|
-
export default function getListingBlockAsyncData({
|
|
5
|
-
dispatch,
|
|
6
|
-
id,
|
|
7
|
-
data,
|
|
8
|
-
path,
|
|
9
|
-
blocksConfig,
|
|
10
|
-
}) {
|
|
11
22
|
const { resolvedExtensions } = resolveBlockExtensions(data, blocksConfig);
|
|
12
23
|
|
|
24
|
+
const subrequestID = content?.UID ? `${content?.UID}-${id}` : id;
|
|
25
|
+
const currentPage = getCurrentPage(location, id);
|
|
26
|
+
|
|
13
27
|
return [
|
|
14
28
|
dispatch(
|
|
15
29
|
getQueryStringResults(
|
|
@@ -20,7 +34,8 @@ export default function getListingBlockAsyncData({
|
|
|
20
34
|
? { fullobjects: 1 }
|
|
21
35
|
: { metadata_fields: '_all' }),
|
|
22
36
|
},
|
|
23
|
-
|
|
37
|
+
subrequestID,
|
|
38
|
+
currentPage,
|
|
24
39
|
),
|
|
25
40
|
),
|
|
26
41
|
];
|
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
} from '@plone/volto/helpers';
|
|
14
14
|
import aheadSVG from '@plone/volto/icons/ahead.svg';
|
|
15
15
|
import clearSVG from '@plone/volto/icons/clear.svg';
|
|
16
|
+
import upSVG from '@plone/volto/icons/up-key.svg';
|
|
17
|
+
import downSVG from '@plone/volto/icons/down-key.svg';
|
|
16
18
|
import {
|
|
17
19
|
findIndex,
|
|
18
20
|
isEmpty,
|
|
@@ -23,6 +25,7 @@ import {
|
|
|
23
25
|
pickBy,
|
|
24
26
|
without,
|
|
25
27
|
cloneDeep,
|
|
28
|
+
xor,
|
|
26
29
|
} from 'lodash';
|
|
27
30
|
import isBoolean from 'lodash/isBoolean';
|
|
28
31
|
import PropTypes from 'prop-types';
|
|
@@ -31,6 +34,7 @@ import { injectIntl } from 'react-intl';
|
|
|
31
34
|
import { Portal } from 'react-portal';
|
|
32
35
|
import { connect } from 'react-redux';
|
|
33
36
|
import {
|
|
37
|
+
Accordion,
|
|
34
38
|
Button,
|
|
35
39
|
Container as SemanticContainer,
|
|
36
40
|
Form as UiForm,
|
|
@@ -41,7 +45,12 @@ import {
|
|
|
41
45
|
import { v4 as uuid } from 'uuid';
|
|
42
46
|
import { toast } from 'react-toastify';
|
|
43
47
|
import { BlocksToolbar, UndoToolbar } from '@plone/volto/components';
|
|
44
|
-
import {
|
|
48
|
+
import {
|
|
49
|
+
setMetadataFieldsets,
|
|
50
|
+
resetMetadataFocus,
|
|
51
|
+
setSidebarTab,
|
|
52
|
+
setFormData,
|
|
53
|
+
} from '@plone/volto/actions';
|
|
45
54
|
import { compose } from 'redux';
|
|
46
55
|
import config from '@plone/volto/registry';
|
|
47
56
|
|
|
@@ -71,6 +80,8 @@ class Form extends Component {
|
|
|
71
80
|
}),
|
|
72
81
|
formData: PropTypes.objectOf(PropTypes.any),
|
|
73
82
|
globalData: PropTypes.objectOf(PropTypes.any),
|
|
83
|
+
metadataFieldsets: PropTypes.arrayOf(PropTypes.string),
|
|
84
|
+
metadataFieldFocus: PropTypes.string,
|
|
74
85
|
pathname: PropTypes.string,
|
|
75
86
|
onSubmit: PropTypes.func,
|
|
76
87
|
onCancel: PropTypes.func,
|
|
@@ -141,10 +152,16 @@ class Form extends Component {
|
|
|
141
152
|
title: uuid(),
|
|
142
153
|
text: uuid(),
|
|
143
154
|
};
|
|
144
|
-
let { formData } = props;
|
|
155
|
+
let { formData, schema: originalSchema } = props;
|
|
145
156
|
const blocksFieldname = getBlocksFieldname(formData);
|
|
146
157
|
const blocksLayoutFieldname = getBlocksLayoutFieldname(formData);
|
|
147
158
|
|
|
159
|
+
const schema = this.removeBlocksLayoutFields(originalSchema);
|
|
160
|
+
|
|
161
|
+
this.props.setMetadataFieldsets(
|
|
162
|
+
schema?.fieldsets ? schema.fieldsets.map((fieldset) => fieldset.id) : [],
|
|
163
|
+
);
|
|
164
|
+
|
|
148
165
|
if (!props.isEditForm) {
|
|
149
166
|
// It's a normal (add form), get defaults from schema
|
|
150
167
|
formData = {
|
|
@@ -228,6 +245,7 @@ class Form extends Component {
|
|
|
228
245
|
this.onTabChange = this.onTabChange.bind(this);
|
|
229
246
|
this.onBlurField = this.onBlurField.bind(this);
|
|
230
247
|
this.onClickInput = this.onClickInput.bind(this);
|
|
248
|
+
this.onToggleMetadataFieldset = this.onToggleMetadataFieldset.bind(this);
|
|
231
249
|
}
|
|
232
250
|
|
|
233
251
|
/**
|
|
@@ -268,6 +286,32 @@ class Form extends Component {
|
|
|
268
286
|
formData: this.props.globalData,
|
|
269
287
|
});
|
|
270
288
|
}
|
|
289
|
+
|
|
290
|
+
if (!isEqual(prevProps.schema, this.props.schema)) {
|
|
291
|
+
this.props.setMetadataFieldsets(
|
|
292
|
+
this.removeBlocksLayoutFields(this.props.schema).fieldsets.map(
|
|
293
|
+
(fieldset) => fieldset.id,
|
|
294
|
+
),
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (
|
|
299
|
+
this.props.metadataFieldFocus !== '' &&
|
|
300
|
+
!isEqual(prevProps.metadataFieldFocus, this.props.metadataFieldFocus)
|
|
301
|
+
) {
|
|
302
|
+
// Scroll into view
|
|
303
|
+
document
|
|
304
|
+
.querySelector(`.field-wrapper-${this.props.metadataFieldFocus}`)
|
|
305
|
+
.scrollIntoView();
|
|
306
|
+
|
|
307
|
+
// Set focus to first input if available
|
|
308
|
+
document
|
|
309
|
+
.querySelector(`.field-wrapper-${this.props.metadataFieldFocus} input`)
|
|
310
|
+
.focus();
|
|
311
|
+
|
|
312
|
+
// Reset focus field
|
|
313
|
+
this.props.resetMetadataFocus();
|
|
314
|
+
}
|
|
271
315
|
}
|
|
272
316
|
|
|
273
317
|
/**
|
|
@@ -561,6 +605,18 @@ class Form extends Component {
|
|
|
561
605
|
return newSchema;
|
|
562
606
|
};
|
|
563
607
|
|
|
608
|
+
/**
|
|
609
|
+
* Toggle metadata fieldset handler
|
|
610
|
+
* @method onToggleMetadataFieldset
|
|
611
|
+
* @param {Object} event Event object.
|
|
612
|
+
* @param {Object} blockProps Block properties.
|
|
613
|
+
* @returns {undefined}
|
|
614
|
+
*/
|
|
615
|
+
onToggleMetadataFieldset(event, blockProps) {
|
|
616
|
+
const { index } = blockProps;
|
|
617
|
+
this.props.setMetadataFieldsets(xor(this.props.metadataFieldsets, [index]));
|
|
618
|
+
}
|
|
619
|
+
|
|
564
620
|
/**
|
|
565
621
|
* Render method.
|
|
566
622
|
* @method render
|
|
@@ -574,6 +630,7 @@ class Form extends Component {
|
|
|
574
630
|
onSubmit,
|
|
575
631
|
navRoot,
|
|
576
632
|
type,
|
|
633
|
+
metadataFieldsets,
|
|
577
634
|
} = this.props;
|
|
578
635
|
const formData = this.state.formData;
|
|
579
636
|
const schema = this.removeBlocksLayoutFields(originalSchema);
|
|
@@ -657,34 +714,54 @@ class Form extends Component {
|
|
|
657
714
|
error={keys(this.state.errors).length > 0}
|
|
658
715
|
>
|
|
659
716
|
{schema &&
|
|
660
|
-
map(schema.fieldsets, (
|
|
661
|
-
<
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
className=
|
|
665
|
-
key={
|
|
717
|
+
map(schema.fieldsets, (fieldset) => (
|
|
718
|
+
<Accordion
|
|
719
|
+
fluid
|
|
720
|
+
styled
|
|
721
|
+
className="form"
|
|
722
|
+
key={fieldset.title}
|
|
666
723
|
>
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
<
|
|
672
|
-
{
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
724
|
+
<div
|
|
725
|
+
key={fieldset.id}
|
|
726
|
+
id={`metadataform-fieldset-${fieldset.id}`}
|
|
727
|
+
>
|
|
728
|
+
<Accordion.Title
|
|
729
|
+
active={metadataFieldsets.includes(fieldset.id)}
|
|
730
|
+
index={fieldset.id}
|
|
731
|
+
onClick={this.onToggleMetadataFieldset}
|
|
732
|
+
>
|
|
733
|
+
{fieldset.title}
|
|
734
|
+
{metadataFieldsets.includes(fieldset.id) ? (
|
|
735
|
+
<Icon name={upSVG} size="20px" />
|
|
736
|
+
) : (
|
|
737
|
+
<Icon name={downSVG} size="20px" />
|
|
738
|
+
)}
|
|
739
|
+
</Accordion.Title>
|
|
740
|
+
<Accordion.Content
|
|
741
|
+
active={metadataFieldsets.includes(fieldset.id)}
|
|
742
|
+
>
|
|
743
|
+
<Segment className="attached">
|
|
744
|
+
{map(fieldset.fields, (field, index) => (
|
|
745
|
+
<Field
|
|
746
|
+
{...schema.properties[field]}
|
|
747
|
+
id={field}
|
|
748
|
+
fieldSet={fieldset.title.toLowerCase()}
|
|
749
|
+
formData={formData}
|
|
750
|
+
focus={this.state.inFocus[field]}
|
|
751
|
+
value={formData?.[field]}
|
|
752
|
+
required={schema.required.indexOf(field) !== -1}
|
|
753
|
+
onChange={this.onChangeField}
|
|
754
|
+
onBlur={this.onBlurField}
|
|
755
|
+
onClick={this.onClickInput}
|
|
756
|
+
key={field}
|
|
757
|
+
error={this.state.errors[field]}
|
|
758
|
+
/>
|
|
759
|
+
))}
|
|
760
|
+
</Segment>
|
|
761
|
+
</Accordion.Content>
|
|
762
|
+
</div>
|
|
763
|
+
</Accordion>
|
|
764
|
+
))}
|
|
688
765
|
</UiForm>
|
|
689
766
|
</Portal>
|
|
690
767
|
)}
|
|
@@ -852,8 +929,15 @@ export default compose(
|
|
|
852
929
|
connect(
|
|
853
930
|
(state, props) => ({
|
|
854
931
|
globalData: state.form?.global,
|
|
932
|
+
metadataFieldsets: state.sidebar?.metadataFieldsets,
|
|
933
|
+
metadataFieldFocus: state.sidebar?.metadataFieldFocus,
|
|
855
934
|
}),
|
|
856
|
-
{
|
|
935
|
+
{
|
|
936
|
+
setMetadataFieldsets,
|
|
937
|
+
setSidebarTab,
|
|
938
|
+
setFormData,
|
|
939
|
+
resetMetadataFocus,
|
|
940
|
+
},
|
|
857
941
|
null,
|
|
858
942
|
{ forwardRef: true },
|
|
859
943
|
),
|
|
@@ -8,6 +8,8 @@ const AppExtras = (props) => {
|
|
|
8
8
|
const { pathname } = props;
|
|
9
9
|
const active = appExtras
|
|
10
10
|
.map((reg) => {
|
|
11
|
+
const ignored = matchPath(pathname, reg.ignore);
|
|
12
|
+
if (ignored) return null;
|
|
11
13
|
const match = matchPath(pathname, reg.match);
|
|
12
14
|
return match ? { reg, match } : null;
|
|
13
15
|
})
|
|
@@ -46,6 +46,13 @@ beforeAll(() => {
|
|
|
46
46
|
<div className="something">{JSON.stringify(props.match)}</div>
|
|
47
47
|
)),
|
|
48
48
|
},
|
|
49
|
+
{
|
|
50
|
+
match: '/frontpage',
|
|
51
|
+
ignore: '/frontpage/images',
|
|
52
|
+
component: jest.fn((props) => (
|
|
53
|
+
<div className="frontpage-content">{JSON.stringify(props.match)}</div>
|
|
54
|
+
)),
|
|
55
|
+
},
|
|
49
56
|
];
|
|
50
57
|
});
|
|
51
58
|
|
|
@@ -85,4 +92,17 @@ describe('AppExtras', () => {
|
|
|
85
92
|
const json = component.toJSON();
|
|
86
93
|
expect(json).toMatchSnapshot();
|
|
87
94
|
});
|
|
95
|
+
it('ignore property works', () => {
|
|
96
|
+
const componentView = renderer.create(
|
|
97
|
+
<AppExtras pathname="/frontpage"></AppExtras>,
|
|
98
|
+
);
|
|
99
|
+
const componentEdit = renderer.create(
|
|
100
|
+
<AppExtras pathname="/frontpage/images"></AppExtras>,
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const jsonView = componentView.toJSON();
|
|
104
|
+
expect(jsonView).toMatchSnapshot();
|
|
105
|
+
const jsonEdit = componentEdit.toJSON();
|
|
106
|
+
expect(jsonEdit).toMatchSnapshot();
|
|
107
|
+
});
|
|
88
108
|
});
|
|
@@ -82,6 +82,9 @@ export const REVERT_HISTORY = 'REVERT_HISTORY';
|
|
|
82
82
|
export const REVERT_TRANSACTIONS = 'REVERT_TRANSACTIONS';
|
|
83
83
|
export const SEARCH_CONTENT = 'SEARCH_CONTENT';
|
|
84
84
|
export const SET_SIDEBAR_TAB = 'SET_SIDEBAR_TAB';
|
|
85
|
+
export const SET_METADATA_FIELDSETS = 'SET_METADATA_FIELDSETS';
|
|
86
|
+
export const SET_METADATA_FOCUS = 'SET_METADATA_FOCUS';
|
|
87
|
+
export const RESET_METADATA_FOCUS = 'RESET_METADATA_FOCUS';
|
|
85
88
|
export const TRANSITION_WORKFLOW = 'TRANSITION_WORKFLOW';
|
|
86
89
|
export const UNINSTALL_ADDON = 'UNINSTALL_ADDON';
|
|
87
90
|
export const UPDATE_CONTENT = 'UPDATE_CONTENT';
|
|
@@ -3,10 +3,19 @@
|
|
|
3
3
|
* @module reducers/sidebar/sidebar
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { union } from 'lodash';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
SET_METADATA_FIELDSETS,
|
|
10
|
+
SET_METADATA_FOCUS,
|
|
11
|
+
RESET_METADATA_FOCUS,
|
|
12
|
+
SET_SIDEBAR_TAB,
|
|
13
|
+
} from '@plone/volto/constants/ActionTypes';
|
|
7
14
|
|
|
8
15
|
const initialState = {
|
|
9
16
|
tab: 0,
|
|
17
|
+
metadataFieldsets: [],
|
|
18
|
+
metadataFieldFocus: '',
|
|
10
19
|
};
|
|
11
20
|
|
|
12
21
|
/**
|
|
@@ -18,6 +27,22 @@ const initialState = {
|
|
|
18
27
|
*/
|
|
19
28
|
export default function sidebar(state = initialState, action = {}) {
|
|
20
29
|
switch (action.type) {
|
|
30
|
+
case SET_METADATA_FIELDSETS:
|
|
31
|
+
return {
|
|
32
|
+
...state,
|
|
33
|
+
metadataFieldsets: action.fieldsets,
|
|
34
|
+
};
|
|
35
|
+
case SET_METADATA_FOCUS:
|
|
36
|
+
return {
|
|
37
|
+
...state,
|
|
38
|
+
metadataFieldsets: union(state.metadataFieldsets, [action.fieldset]),
|
|
39
|
+
metadataFieldFocus: action.field,
|
|
40
|
+
};
|
|
41
|
+
case RESET_METADATA_FOCUS:
|
|
42
|
+
return {
|
|
43
|
+
...state,
|
|
44
|
+
metadataFieldFocus: '',
|
|
45
|
+
};
|
|
21
46
|
case SET_SIDEBAR_TAB:
|
|
22
47
|
return {
|
|
23
48
|
...state,
|
|
@@ -1,13 +1,60 @@
|
|
|
1
1
|
import sidebar from './sidebar';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
SET_METADATA_FIELDSETS,
|
|
4
|
+
SET_METADATA_FOCUS,
|
|
5
|
+
RESET_METADATA_FOCUS,
|
|
6
|
+
SET_SIDEBAR_TAB,
|
|
7
|
+
} from '@plone/volto/constants/ActionTypes';
|
|
3
8
|
|
|
4
9
|
describe('Sidebar reducer', () => {
|
|
5
10
|
it('should return the initial state', () => {
|
|
6
11
|
expect(sidebar()).toEqual({
|
|
12
|
+
metadataFieldFocus: '',
|
|
13
|
+
metadataFieldsets: [],
|
|
7
14
|
tab: 0,
|
|
8
15
|
});
|
|
9
16
|
});
|
|
10
17
|
|
|
18
|
+
it('should handle SET_METADATA_FIELDSETS', () => {
|
|
19
|
+
expect(
|
|
20
|
+
sidebar(undefined, {
|
|
21
|
+
type: SET_METADATA_FIELDSETS,
|
|
22
|
+
fieldsets: ['default'],
|
|
23
|
+
}),
|
|
24
|
+
).toEqual({
|
|
25
|
+
metadataFieldFocus: '',
|
|
26
|
+
metadataFieldsets: ['default'],
|
|
27
|
+
tab: 0,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should handle SET_METADATA_FOCUS', () => {
|
|
32
|
+
expect(
|
|
33
|
+
sidebar(undefined, {
|
|
34
|
+
type: SET_METADATA_FOCUS,
|
|
35
|
+
fieldset: 'default',
|
|
36
|
+
field: 'title',
|
|
37
|
+
}),
|
|
38
|
+
).toEqual({
|
|
39
|
+
metadataFieldFocus: 'title',
|
|
40
|
+
metadataFieldsets: ['default'],
|
|
41
|
+
tab: 0,
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should handle RESET_METADATA_FOCUS', () => {
|
|
46
|
+
expect(
|
|
47
|
+
sidebar(
|
|
48
|
+
{ metadataFieldFocus: 'title' },
|
|
49
|
+
{
|
|
50
|
+
type: RESET_METADATA_FOCUS,
|
|
51
|
+
},
|
|
52
|
+
),
|
|
53
|
+
).toEqual({
|
|
54
|
+
metadataFieldFocus: '',
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
11
58
|
it('should handle SET_SIDEBAR_TAB', () => {
|
|
12
59
|
expect(
|
|
13
60
|
sidebar(undefined, {
|
|
@@ -15,6 +62,8 @@ describe('Sidebar reducer', () => {
|
|
|
15
62
|
index: 1,
|
|
16
63
|
}),
|
|
17
64
|
).toEqual({
|
|
65
|
+
metadataFieldFocus: '',
|
|
66
|
+
metadataFieldsets: [],
|
|
18
67
|
tab: 1,
|
|
19
68
|
});
|
|
20
69
|
});
|
package/types/actions/index.d.ts
CHANGED
|
@@ -10,7 +10,6 @@ export { listRoles } from "@plone/volto/actions/roles/roles";
|
|
|
10
10
|
export { getTypes } from "@plone/volto/actions/types/types";
|
|
11
11
|
export { getQuerystring } from "@plone/volto/actions/querystring/querystring";
|
|
12
12
|
export { getQueryStringResults } from "@plone/volto/actions/querystringsearch/querystringsearch";
|
|
13
|
-
export { setSidebarTab } from "@plone/volto/actions/sidebar/sidebar";
|
|
14
13
|
export { setFormData } from "@plone/volto/actions/form/form";
|
|
15
14
|
export { loadLazyLibrary } from "@plone/volto/actions/lazyLibraries/lazyLibraries";
|
|
16
15
|
export { getContextNavigation } from "@plone/volto/actions/contextNavigation/contextNavigation";
|
|
@@ -38,6 +37,7 @@ export { createUser, deleteUser, getUser, listUsers, setInitialPassword, resetPa
|
|
|
38
37
|
export { login, loginRenew, logout, resetLoginRequest } from "@plone/volto/actions/userSession/userSession";
|
|
39
38
|
export { getVocabulary, getVocabularyTokenTitle } from "@plone/volto/actions/vocabularies/vocabularies";
|
|
40
39
|
export { getWorkflow, transitionWorkflow } from "@plone/volto/actions/workflow/workflow";
|
|
40
|
+
export { setMetadataFieldsets, setMetadataFocus, resetMetadataFocus, setSidebarTab } from "@plone/volto/actions/sidebar/sidebar";
|
|
41
41
|
export { deleteLinkTranslation, getTranslationLocator, linkTranslation } from "@plone/volto/actions/translations/translations";
|
|
42
42
|
export { setBlocksClipboard, resetBlocksClipboard } from "@plone/volto/actions/blocksClipboard/blocksClipboard";
|
|
43
43
|
export { changeLanguage, changeLanguageCookies } from "./language/language";
|
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Set metadata fieldsets function.
|
|
3
|
+
* @function setMetadataFieldsets
|
|
4
|
+
* @param {Array} fieldsets New fieldsets.
|
|
5
|
+
* @returns {Object} Set metadata fieldsets action.
|
|
6
|
+
*/
|
|
7
|
+
export function setMetadataFieldsets(fieldsets: any[]): any;
|
|
8
|
+
/**
|
|
9
|
+
* Set metadata focus function.
|
|
10
|
+
* @function setMetadataFocus
|
|
11
|
+
* @param {String} fieldset Fieldset of the field.
|
|
12
|
+
* @param {String} field Field to set focus too.
|
|
13
|
+
* @returns {Object} Set metadata focus action.
|
|
14
|
+
*/
|
|
15
|
+
export function setMetadataFocus(fieldset: string, field: string): any;
|
|
16
|
+
/**
|
|
17
|
+
* Resets metadata focus function.
|
|
18
|
+
* @function resetMetadataFocus
|
|
19
|
+
* @returns {Object} Set metadata focus action.
|
|
20
|
+
*/
|
|
21
|
+
export function resetMetadataFocus(): any;
|
|
1
22
|
/**
|
|
2
23
|
* Set sidebar tab function.
|
|
3
24
|
* @function setSidebarTab
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
declare const _default: import("react-redux").ConnectedComponent<React.ForwardRefExoticComponent<Pick<import("react-intl").WithIntlProps<any>, string | number | symbol> & React.RefAttributes<any>> & {
|
|
2
2
|
WrappedComponent: React.ComponentType<any>;
|
|
3
|
-
}, import("react-redux").Omit<Pick<import("react-intl").WithIntlProps<any>, string | number | symbol> & React.RefAttributes<any>, "setSidebarTab" | "setFormData" | "globalData">>;
|
|
3
|
+
}, import("react-redux").Omit<Pick<import("react-intl").WithIntlProps<any>, string | number | symbol> & React.RefAttributes<any>, "setMetadataFieldsets" | "setSidebarTab" | "setFormData" | "resetMetadataFocus" | "globalData" | "metadataFieldsets" | "metadataFieldFocus">>;
|
|
4
4
|
export default _default;
|
|
5
5
|
import React from 'react';
|
|
@@ -81,6 +81,9 @@ export const REVERT_HISTORY: "REVERT_HISTORY";
|
|
|
81
81
|
export const REVERT_TRANSACTIONS: "REVERT_TRANSACTIONS";
|
|
82
82
|
export const SEARCH_CONTENT: "SEARCH_CONTENT";
|
|
83
83
|
export const SET_SIDEBAR_TAB: "SET_SIDEBAR_TAB";
|
|
84
|
+
export const SET_METADATA_FIELDSETS: "SET_METADATA_FIELDSETS";
|
|
85
|
+
export const SET_METADATA_FOCUS: "SET_METADATA_FOCUS";
|
|
86
|
+
export const RESET_METADATA_FOCUS: "RESET_METADATA_FOCUS";
|
|
84
87
|
export const TRANSITION_WORKFLOW: "TRANSITION_WORKFLOW";
|
|
85
88
|
export const UNINSTALL_ADDON: "UNINSTALL_ADDON";
|
|
86
89
|
export const UPDATE_CONTENT: "UPDATE_CONTENT";
|