@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.
- package/CHANGELOG.md +37 -0
- package/cypress/support/commands.js +51 -3
- package/package.json +4 -4
- package/razzle.config.js +14 -0
- package/src/actions/form/form.js +19 -0
- package/src/actions/form/form.test.js +14 -0
- package/src/actions/index.js +7 -1
- package/src/actions/sidebar/sidebar.js +45 -1
- package/src/actions/sidebar/sidebar.test.js +42 -2
- package/src/components/manage/Add/Add.jsx +1 -0
- package/src/components/manage/Blocks/Listing/getAsyncData.js +23 -8
- package/src/components/manage/Edit/Edit.jsx +1 -0
- package/src/components/manage/Form/Form.jsx +207 -76
- package/src/components/manage/Multilingual/TranslationObject.jsx +0 -1
- 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 +4 -0
- package/src/reducers/form/form.js +15 -3
- package/src/reducers/form/form.test.js +13 -1
- package/src/reducers/sidebar/sidebar.js +26 -1
- package/src/reducers/sidebar/sidebar.test.js +50 -1
- package/theme/themes/default/elements/step.overrides +2 -2
- package/theme/themes/pastanaga/elements/step.overrides +1 -1
- package/types/actions/form/form.d.ts +7 -0
- package/types/actions/form/form.test.d.ts +1 -0
- package/types/actions/index.d.ts +2 -1
- package/types/actions/sidebar/sidebar.d.ts +21 -0
- package/types/components/manage/Blocks/Listing/getAsyncData.d.ts +1 -7
- package/types/constants/ActionTypes.d.ts +4 -0
- 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 {
|
|
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 =
|
|
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
|
|
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(
|
|
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(
|
|
571
|
+
difference(formData, this.state.initialFormData),
|
|
502
572
|
);
|
|
503
573
|
return {
|
|
504
|
-
...pickBy(
|
|
505
|
-
|
|
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
|
|
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={
|
|
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
|
-
|
|
572
|
-
|
|
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
|
|
668
|
+
formData,
|
|
584
669
|
selected: this.state.selected,
|
|
585
670
|
multiSelected: this.state.multiSelected,
|
|
586
671
|
}}
|
|
587
672
|
enableHotKeys
|
|
588
|
-
onUndoRedo={({ 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={(
|
|
681
|
+
onChangeFormData={(newData) => {
|
|
682
|
+
const newFormData = {
|
|
683
|
+
...formData,
|
|
684
|
+
...newData,
|
|
685
|
+
};
|
|
592
686
|
this.setState({
|
|
593
|
-
formData:
|
|
594
|
-
|
|
595
|
-
|
|
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, (
|
|
624
|
-
<
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
className=
|
|
628
|
-
key={
|
|
717
|
+
map(schema.fieldsets, (fieldset) => (
|
|
718
|
+
<Accordion
|
|
719
|
+
fluid
|
|
720
|
+
styled
|
|
721
|
+
className="form"
|
|
722
|
+
key={fieldset.title}
|
|
629
723
|
>
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
<
|
|
635
|
-
{
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
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={
|
|
816
|
+
formData={formData}
|
|
704
817
|
fieldSet={item.title.toLowerCase()}
|
|
705
818
|
focus={this.state.inFocus[field]}
|
|
706
|
-
value={
|
|
819
|
+
value={formData?.[field]}
|
|
707
820
|
required={schema.required.indexOf(field) !== -1}
|
|
708
|
-
onChange={
|
|
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={
|
|
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(
|
|
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);
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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 {
|
|
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,
|