@plone/volto 18.0.0-alpha.11 → 18.0.0-alpha.12

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 CHANGED
@@ -17,6 +17,12 @@ myst:
17
17
 
18
18
  <!-- towncrier release notes start -->
19
19
 
20
+ ## 18.0.0-alpha.12 (2024-02-21)
21
+
22
+ ### Feature
23
+
24
+ - Add accordion to metadata form. @robgietema [#5760](https://github.com/plone/volto/issues/5760)
25
+
20
26
  ## 18.0.0-alpha.11 (2024-02-18)
21
27
 
22
28
  ### Breaking
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  }
10
10
  ],
11
11
  "license": "MIT",
12
- "version": "18.0.0-alpha.11",
12
+ "version": "18.0.0-alpha.12",
13
13
  "repository": {
14
14
  "type": "git",
15
15
  "url": "git@github.com:plone/volto.git"
@@ -312,9 +312,9 @@
312
312
  "webpack-node-externals": "3.0.0",
313
313
  "xmlrpc": "1.3.2",
314
314
  "yarnhook": "0.5.1",
315
- "@plone/registry": "1.3.0",
316
- "@plone/volto-slate": "18.0.0-alpha.6",
317
- "@plone/scripts": "3.3.2"
315
+ "@plone/registry": "1.3.1",
316
+ "@plone/scripts": "3.3.2",
317
+ "@plone/volto-slate": "18.0.0-alpha.6"
318
318
  },
319
319
  "devDependencies": {
320
320
  "@jest/globals": "^29.7.0",
@@ -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 { setSidebarTab } from '@plone/volto/actions/sidebar/sidebar';
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 { SET_SIDEBAR_TAB } from '@plone/volto/constants/ActionTypes';
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 { setSidebarTab } from './sidebar';
2
- import { SET_SIDEBAR_TAB } from '@plone/volto/constants/ActionTypes';
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;
@@ -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 { setSidebarTab, setFormData } from '@plone/volto/actions';
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, (item) => [
661
- <Segment
662
- secondary
663
- attached
664
- className={`fieldset-${item.id}`}
665
- key={item.title}
717
+ map(schema.fieldsets, (fieldset) => (
718
+ <Accordion
719
+ fluid
720
+ styled
721
+ className="form"
722
+ key={fieldset.title}
666
723
  >
667
- {item.title}
668
- </Segment>,
669
- <Segment attached key={`fieldset-contents-${item.title}`}>
670
- {map(item.fields, (field, index) => (
671
- <Field
672
- {...schema.properties[field]}
673
- id={field}
674
- fieldSet={item.title.toLowerCase()}
675
- formData={formData}
676
- focus={this.state.inFocus[field]}
677
- value={formData?.[field]}
678
- required={schema.required.indexOf(field) !== -1}
679
- onChange={this.onChangeField}
680
- onBlur={this.onBlurField}
681
- onClick={this.onClickInput}
682
- key={field}
683
- error={this.state.errors[field]}
684
- />
685
- ))}
686
- </Segment>,
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
  )}
@@ -855,8 +932,15 @@ export default compose(
855
932
  connect(
856
933
  (state, props) => ({
857
934
  globalData: state.form?.global,
935
+ metadataFieldsets: state.sidebar?.metadataFieldsets,
936
+ metadataFieldFocus: state.sidebar?.metadataFieldFocus,
858
937
  }),
859
- { setSidebarTab, setFormData },
938
+ {
939
+ setMetadataFieldsets,
940
+ setSidebarTab,
941
+ setFormData,
942
+ resetMetadataFocus,
943
+ },
860
944
  null,
861
945
  { forwardRef: true },
862
946
  ),
@@ -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 { SET_SIDEBAR_TAB } from '@plone/volto/constants/ActionTypes';
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 { SET_SIDEBAR_TAB } from '@plone/volto/constants/ActionTypes';
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
  });
@@ -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
@@ -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";