@plone/volto 18.0.0-alpha.10 → 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.
Files changed (31) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/cypress/support/commands.js +51 -3
  3. package/package.json +4 -4
  4. package/razzle.config.js +14 -0
  5. package/src/actions/form/form.js +19 -0
  6. package/src/actions/form/form.test.js +14 -0
  7. package/src/actions/index.js +7 -1
  8. package/src/actions/sidebar/sidebar.js +45 -1
  9. package/src/actions/sidebar/sidebar.test.js +42 -2
  10. package/src/components/manage/Add/Add.jsx +1 -0
  11. package/src/components/manage/Blocks/Listing/getAsyncData.js +23 -8
  12. package/src/components/manage/Edit/Edit.jsx +1 -0
  13. package/src/components/manage/Form/Form.jsx +207 -76
  14. package/src/components/manage/Multilingual/TranslationObject.jsx +0 -1
  15. package/src/components/theme/App/App.jsx +1 -0
  16. package/src/components/theme/AppExtras/AppExtras.jsx +2 -0
  17. package/src/components/theme/AppExtras/AppExtras.test.jsx +20 -0
  18. package/src/constants/ActionTypes.js +4 -0
  19. package/src/reducers/form/form.js +15 -3
  20. package/src/reducers/form/form.test.js +13 -1
  21. package/src/reducers/sidebar/sidebar.js +26 -1
  22. package/src/reducers/sidebar/sidebar.test.js +50 -1
  23. package/theme/themes/default/elements/step.overrides +2 -2
  24. package/theme/themes/pastanaga/elements/step.overrides +1 -1
  25. package/types/actions/form/form.d.ts +7 -0
  26. package/types/actions/form/form.test.d.ts +1 -0
  27. package/types/actions/index.d.ts +2 -1
  28. package/types/actions/sidebar/sidebar.d.ts +21 -0
  29. package/types/components/manage/Blocks/Listing/getAsyncData.d.ts +1 -7
  30. package/types/constants/ActionTypes.d.ts +4 -0
  31. package/types/reducers/form/form.d.ts +1 -1
@@ -13,15 +13,19 @@ 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,
21
+ isEqual,
19
22
  keys,
20
23
  map,
21
24
  mapValues,
22
25
  pickBy,
23
26
  without,
24
27
  cloneDeep,
28
+ xor,
25
29
  } from 'lodash';
26
30
  import isBoolean from 'lodash/isBoolean';
27
31
  import PropTypes from 'prop-types';
@@ -30,6 +34,7 @@ import { injectIntl } from 'react-intl';
30
34
  import { Portal } from 'react-portal';
31
35
  import { connect } from 'react-redux';
32
36
  import {
37
+ Accordion,
33
38
  Button,
34
39
  Container as SemanticContainer,
35
40
  Form as UiForm,
@@ -40,7 +45,12 @@ import {
40
45
  import { v4 as uuid } from 'uuid';
41
46
  import { toast } from 'react-toastify';
42
47
  import { BlocksToolbar, UndoToolbar } from '@plone/volto/components';
43
- import { setSidebarTab } from '@plone/volto/actions';
48
+ import {
49
+ setMetadataFieldsets,
50
+ resetMetadataFocus,
51
+ setSidebarTab,
52
+ setFormData,
53
+ } from '@plone/volto/actions';
44
54
  import { compose } from 'redux';
45
55
  import config from '@plone/volto/registry';
46
56
 
@@ -69,6 +79,9 @@ class Form extends Component {
69
79
  required: PropTypes.arrayOf(PropTypes.string),
70
80
  }),
71
81
  formData: PropTypes.objectOf(PropTypes.any),
82
+ globalData: PropTypes.objectOf(PropTypes.any),
83
+ metadataFieldsets: PropTypes.arrayOf(PropTypes.string),
84
+ metadataFieldFocus: PropTypes.string,
72
85
  pathname: PropTypes.string,
73
86
  onSubmit: PropTypes.func,
74
87
  onCancel: PropTypes.func,
@@ -93,6 +106,7 @@ class Form extends Component {
93
106
  requestError: PropTypes.string,
94
107
  allowedBlocks: PropTypes.arrayOf(PropTypes.string),
95
108
  showRestricted: PropTypes.bool,
109
+ global: PropTypes.bool,
96
110
  };
97
111
 
98
112
  /**
@@ -123,6 +137,7 @@ class Form extends Component {
123
137
  editable: true,
124
138
  requestError: null,
125
139
  allowedBlocks: null,
140
+ global: false,
126
141
  };
127
142
 
128
143
  /**
@@ -137,10 +152,16 @@ class Form extends Component {
137
152
  title: uuid(),
138
153
  text: uuid(),
139
154
  };
140
- let { formData } = props;
155
+ let { formData, schema: originalSchema } = props;
141
156
  const blocksFieldname = getBlocksFieldname(formData);
142
157
  const blocksLayoutFieldname = getBlocksLayoutFieldname(formData);
143
158
 
159
+ const schema = this.removeBlocksLayoutFields(originalSchema);
160
+
161
+ this.props.setMetadataFieldsets(
162
+ schema?.fieldsets ? schema.fieldsets.map((fieldset) => fieldset.id) : [],
163
+ );
164
+
144
165
  if (!props.isEditForm) {
145
166
  // It's a normal (add form), get defaults from schema
146
167
  formData = {
@@ -201,6 +222,12 @@ class Form extends Component {
201
222
  }
202
223
  }
203
224
 
225
+ // Sync state to global state
226
+ if (this.props.global) {
227
+ this.props.setFormData(formData);
228
+ }
229
+
230
+ // Set initial state
204
231
  this.state = {
205
232
  formData,
206
233
  initialFormData,
@@ -218,6 +245,7 @@ class Form extends Component {
218
245
  this.onTabChange = this.onTabChange.bind(this);
219
246
  this.onBlurField = this.onBlurField.bind(this);
220
247
  this.onClickInput = this.onClickInput.bind(this);
248
+ this.onToggleMetadataFieldset = this.onToggleMetadataFieldset.bind(this);
221
249
  }
222
250
 
223
251
  /**
@@ -246,14 +274,44 @@ class Form extends Component {
246
274
  }
247
275
 
248
276
  if (this.props.onChangeFormData) {
249
- if (
250
- // TODO: use fast-deep-equal
251
- JSON.stringify(prevState?.formData) !==
252
- JSON.stringify(this.state.formData)
253
- ) {
277
+ if (!isEqual(prevState?.formData, this.state.formData)) {
254
278
  this.props.onChangeFormData(this.state.formData);
255
279
  }
256
280
  }
281
+ if (
282
+ this.props.global &&
283
+ !isEqual(this.props.globalData, this.state.formData)
284
+ ) {
285
+ this.setState({
286
+ formData: this.props.globalData,
287
+ });
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
+ }
257
315
  }
258
316
 
259
317
  /**
@@ -327,15 +385,18 @@ class Form extends Component {
327
385
  onChangeField(id, value) {
328
386
  this.setState((prevState) => {
329
387
  const { errors, formData } = prevState;
388
+ const newFormData = {
389
+ ...formData,
390
+ // We need to catch also when the value equals false this fixes #888
391
+ [id]: value || (value !== undefined && isBoolean(value)) ? value : null,
392
+ };
330
393
  delete errors[id];
394
+ if (this.props.global) {
395
+ this.props.setFormData(newFormData);
396
+ }
331
397
  return {
332
398
  errors,
333
- formData: {
334
- ...formData,
335
- // We need to catch also when the value equals false this fixes #888
336
- [id]:
337
- value || (value !== undefined && isBoolean(value)) ? value : null,
338
- },
399
+ formData: newFormData,
339
400
  // Changing the form data re-renders the select widget which causes the
340
401
  // focus to get lost. To circumvent this, we set the focus back to
341
402
  // the input.
@@ -357,14 +418,13 @@ class Form extends Component {
357
418
  onSelectBlock(id, isMultipleSelection, event) {
358
419
  let multiSelected = [];
359
420
  let selected = id;
421
+ const formData = this.state.formData;
360
422
 
361
423
  if (isMultipleSelection) {
362
424
  selected = null;
363
- const blocksLayoutFieldname = getBlocksLayoutFieldname(
364
- this.state.formData,
365
- );
425
+ const blocksLayoutFieldname = getBlocksLayoutFieldname(formData);
366
426
 
367
- const blocks_layout = this.state.formData[blocksLayoutFieldname].items;
427
+ const blocks_layout = formData[blocksLayoutFieldname].items;
368
428
 
369
429
  if (event.shiftKey) {
370
430
  const anchor =
@@ -424,6 +484,9 @@ class Form extends Component {
424
484
  this.setState({
425
485
  formData: this.props.formData,
426
486
  });
487
+ if (this.props.global) {
488
+ this.props.setFormData(this.props.formData);
489
+ }
427
490
  }
428
491
  this.props.onCancel(event);
429
492
  }
@@ -435,6 +498,8 @@ class Form extends Component {
435
498
  * @returns {undefined}
436
499
  */
437
500
  onSubmit(event) {
501
+ const formData = this.state.formData;
502
+
438
503
  if (event) {
439
504
  event.preventDefault();
440
505
  }
@@ -442,7 +507,7 @@ class Form extends Component {
442
507
  const errors = this.props.schema
443
508
  ? FormValidation.validateFieldsPerFieldset({
444
509
  schema: this.props.schema,
445
- formData: this.state.formData,
510
+ formData,
446
511
  formatMessage: this.props.intl.formatMessage,
447
512
  })
448
513
  : {};
@@ -477,12 +542,15 @@ class Form extends Component {
477
542
  if (this.props.isEditForm) {
478
543
  this.props.onSubmit(this.getOnlyFormModifiedValues());
479
544
  } else {
480
- this.props.onSubmit(this.state.formData);
545
+ this.props.onSubmit(formData);
481
546
  }
482
547
  if (this.props.resetAfterSubmit) {
483
548
  this.setState({
484
549
  formData: this.props.formData,
485
550
  });
551
+ if (this.props.global) {
552
+ this.props.setFormData(this.props.formData);
553
+ }
486
554
  }
487
555
  }
488
556
  }
@@ -497,15 +565,15 @@ class Form extends Component {
497
565
  * @returns {undefined}
498
566
  */
499
567
  getOnlyFormModifiedValues = () => {
568
+ const formData = this.state.formData;
569
+
500
570
  const fieldsModified = Object.keys(
501
- difference(this.state.formData, this.state.initialFormData),
571
+ difference(formData, this.state.initialFormData),
502
572
  );
503
573
  return {
504
- ...pickBy(this.state.formData, (value, key) =>
505
- fieldsModified.includes(key),
506
- ),
507
- ...(this.state.formData['@static_behaviors'] && {
508
- '@static_behaviors': this.state.formData['@static_behaviors'],
574
+ ...pickBy(formData, (value, key) => fieldsModified.includes(key)),
575
+ ...(formData['@static_behaviors'] && {
576
+ '@static_behaviors': formData['@static_behaviors'],
509
577
  }),
510
578
  };
511
579
  };
@@ -537,6 +605,18 @@ class Form extends Component {
537
605
  return newSchema;
538
606
  };
539
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
+
540
620
  /**
541
621
  * Render method.
542
622
  * @method render
@@ -550,8 +630,9 @@ class Form extends Component {
550
630
  onSubmit,
551
631
  navRoot,
552
632
  type,
633
+ metadataFieldsets,
553
634
  } = this.props;
554
- const { formData } = this.state;
635
+ const formData = this.state.formData;
555
636
  const schema = this.removeBlocksLayoutFields(originalSchema);
556
637
  const Container =
557
638
  config.getComponent({ name: 'Container' }).component || SemanticContainer;
@@ -562,17 +643,21 @@ class Form extends Component {
562
643
  this.state.isClient && (
563
644
  <Container>
564
645
  <BlocksToolbar
565
- formData={this.state.formData}
646
+ formData={formData}
566
647
  selectedBlock={this.state.selected}
567
648
  selectedBlocks={this.state.multiSelected}
568
- onChangeBlocks={(newBlockData) =>
649
+ onChangeBlocks={(newBlockData) => {
650
+ const newFormData = {
651
+ ...formData,
652
+ ...newBlockData,
653
+ };
569
654
  this.setState({
570
- formData: {
571
- ...formData,
572
- ...newBlockData,
573
- },
574
- })
575
- }
655
+ formData: newFormData,
656
+ });
657
+ if (this.props.global) {
658
+ this.props.setFormData(newFormData);
659
+ }
660
+ }}
576
661
  onSetSelectedBlocks={(blockIds) =>
577
662
  this.setState({ multiSelected: blockIds })
578
663
  }
@@ -580,22 +665,31 @@ class Form extends Component {
580
665
  />
581
666
  <UndoToolbar
582
667
  state={{
583
- formData: this.state.formData,
668
+ formData,
584
669
  selected: this.state.selected,
585
670
  multiSelected: this.state.multiSelected,
586
671
  }}
587
672
  enableHotKeys
588
- onUndoRedo={({ state }) => this.setState(state)}
673
+ onUndoRedo={({ state }) => {
674
+ if (this.props.global) {
675
+ this.props.setFormData(state.formData);
676
+ }
677
+ return this.setState(state);
678
+ }}
589
679
  />
590
680
  <BlocksForm
591
- onChangeFormData={(newFormData) =>
681
+ onChangeFormData={(newData) => {
682
+ const newFormData = {
683
+ ...formData,
684
+ ...newData,
685
+ };
592
686
  this.setState({
593
- formData: {
594
- ...formData,
595
- ...newFormData,
596
- },
597
- })
598
- }
687
+ formData: newFormData,
688
+ });
689
+ if (this.props.global) {
690
+ this.props.setFormData(newFormData);
691
+ }
692
+ }}
599
693
  onChangeField={this.onChangeField}
600
694
  onSelectBlock={this.onSelectBlock}
601
695
  properties={formData}
@@ -620,34 +714,54 @@ class Form extends Component {
620
714
  error={keys(this.state.errors).length > 0}
621
715
  >
622
716
  {schema &&
623
- map(schema.fieldsets, (item) => [
624
- <Segment
625
- secondary
626
- attached
627
- className={`fieldset-${item.id}`}
628
- key={item.title}
717
+ map(schema.fieldsets, (fieldset) => (
718
+ <Accordion
719
+ fluid
720
+ styled
721
+ className="form"
722
+ key={fieldset.title}
629
723
  >
630
- {item.title}
631
- </Segment>,
632
- <Segment attached key={`fieldset-contents-${item.title}`}>
633
- {map(item.fields, (field, index) => (
634
- <Field
635
- {...schema.properties[field]}
636
- id={field}
637
- fieldSet={item.title.toLowerCase()}
638
- formData={this.state.formData}
639
- focus={this.state.inFocus[field]}
640
- value={this.state.formData?.[field]}
641
- required={schema.required.indexOf(field) !== -1}
642
- onChange={this.onChangeField}
643
- onBlur={this.onBlurField}
644
- onClick={this.onClickInput}
645
- key={field}
646
- error={this.state.errors[field]}
647
- />
648
- ))}
649
- </Segment>,
650
- ])}
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
+ ))}
651
765
  </UiForm>
652
766
  </Portal>
653
767
  )}
@@ -698,14 +812,17 @@ class Form extends Component {
698
812
  ...map(item.fields, (field, index) => (
699
813
  <Field
700
814
  {...schema.properties[field]}
701
- isDisabled={!this.props.editable}
702
815
  id={field}
703
- formData={this.state.formData}
816
+ formData={formData}
704
817
  fieldSet={item.title.toLowerCase()}
705
818
  focus={this.state.inFocus[field]}
706
- value={this.state.formData?.[field]}
819
+ value={formData?.[field]}
707
820
  required={schema.required.indexOf(field) !== -1}
708
- onChange={this.onChangeField}
821
+ onChange={
822
+ this.props.editable
823
+ ? this.onChangeField
824
+ : () => {}
825
+ }
709
826
  onBlur={this.onBlurField}
710
827
  onClick={this.onClickInput}
711
828
  key={field}
@@ -751,7 +868,7 @@ class Form extends Component {
751
868
  <Field
752
869
  {...schema.properties[field]}
753
870
  id={field}
754
- value={this.state.formData?.[field]}
871
+ value={formData?.[field]}
755
872
  required={schema.required.indexOf(field) !== -1}
756
873
  onChange={this.onChangeField}
757
874
  onBlur={this.onBlurField}
@@ -812,5 +929,19 @@ class Form extends Component {
812
929
  const FormIntl = injectIntl(Form, { forwardRef: true });
813
930
 
814
931
  export default compose(
815
- connect(null, { setSidebarTab }, null, { forwardRef: true }),
932
+ connect(
933
+ (state, props) => ({
934
+ globalData: state.form?.global,
935
+ metadataFieldsets: state.sidebar?.metadataFieldsets,
936
+ metadataFieldFocus: state.sidebar?.metadataFieldFocus,
937
+ }),
938
+ {
939
+ setMetadataFieldsets,
940
+ setSidebarTab,
941
+ setFormData,
942
+ resetMetadataFocus,
943
+ },
944
+ null,
945
+ { forwardRef: true },
946
+ ),
816
947
  )(FormIntl);
@@ -124,7 +124,6 @@ const TranslationObject = ({
124
124
  {map(item.fields, (field, index) => (
125
125
  <Field
126
126
  {...schema.properties[field]}
127
- isDisabled={true}
128
127
  id={field}
129
128
  formData={translationObject}
130
129
  focus={false}
@@ -242,6 +242,7 @@ export const fetchContent = async ({ store, location }) => {
242
242
  id,
243
243
  data,
244
244
  blocksConfig,
245
+ content,
245
246
  });
246
247
  if (!p?.length) {
247
248
  throw new Error(
@@ -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';
@@ -141,3 +144,4 @@ export const POST_UPGRADE = 'POST_UPGRADE';
141
144
  export const RESET_LOGIN_REQUEST = 'RESET_LOGIN_REQUEST';
142
145
  export const GET_SITE = 'GET_SITE';
143
146
  export const GET_NAVROOT = 'GET_NAVROOT';
147
+ export const SET_FORM_DATA = 'SET_FORM_DATA';
@@ -4,7 +4,11 @@
4
4
  * @module reducers/form/form
5
5
  */
6
6
 
7
- const initialState = {};
7
+ import { SET_FORM_DATA } from '@plone/volto/constants/ActionTypes';
8
+
9
+ const initialState = {
10
+ global: {},
11
+ };
8
12
 
9
13
  /**
10
14
  * Form reducer.
@@ -12,6 +16,14 @@ const initialState = {};
12
16
  * @param {Object} state Current state.
13
17
  * @returns {Object} New state.
14
18
  */
15
- export default function form(state = initialState) {
16
- return state;
19
+ export default function form(state = initialState, action = {}) {
20
+ switch (action.type) {
21
+ case SET_FORM_DATA:
22
+ return {
23
+ ...state,
24
+ global: action.data,
25
+ };
26
+ default:
27
+ return state;
28
+ }
17
29
  }
@@ -1,7 +1,19 @@
1
1
  import form from './form';
2
+ import { SET_FORM_DATA } from '@plone/volto/constants/ActionTypes';
2
3
 
3
4
  describe('Form reducer', () => {
4
5
  it('should return the initial state', () => {
5
- expect(form()).toEqual({});
6
+ expect(form()).toEqual({ global: {} });
7
+ });
8
+
9
+ it('should handle SET_FORM_DATA', () => {
10
+ expect(
11
+ form(undefined, {
12
+ type: SET_FORM_DATA,
13
+ data: { foo: 'bar' },
14
+ }),
15
+ ).toEqual({
16
+ global: { foo: 'bar' },
17
+ });
6
18
  });
7
19
  });
@@ -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,