@plone/volto 16.30.3 → 16.31.1

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 (40) hide show
  1. package/.changelog.draft +3 -4
  2. package/.yarn/install-state.gz +0 -0
  3. package/CHANGELOG.md +14 -0
  4. package/locales/ca/LC_MESSAGES/volto.po +899 -902
  5. package/locales/de/LC_MESSAGES/volto.po +899 -900
  6. package/locales/en/LC_MESSAGES/volto.po +899 -899
  7. package/locales/en.json +1 -1
  8. package/locales/es/LC_MESSAGES/volto.po +899 -903
  9. package/locales/eu/LC_MESSAGES/volto.po +899 -899
  10. package/locales/fi/LC_MESSAGES/volto.po +899 -905
  11. package/locales/fr/LC_MESSAGES/volto.po +899 -909
  12. package/locales/it/LC_MESSAGES/volto.po +899 -899
  13. package/locales/ja/LC_MESSAGES/volto.po +899 -902
  14. package/locales/nl/LC_MESSAGES/volto.po +899 -902
  15. package/locales/pt/LC_MESSAGES/volto.po +899 -902
  16. package/locales/pt_BR/LC_MESSAGES/volto.po +899 -903
  17. package/locales/ro/LC_MESSAGES/volto.po +899 -899
  18. package/locales/volto.pot +901 -901
  19. package/locales/zh_CN/LC_MESSAGES/volto.po +899 -899
  20. package/package.json +1 -1
  21. package/packages/volto-slate/news/5779.bugfix +1 -0
  22. package/packages/volto-slate/package.json +1 -1
  23. package/packages/volto-slate/src/blocks/Table/TableBlockEdit.jsx +5 -11
  24. package/src/actions/form/form.js +19 -0
  25. package/src/actions/form/form.test.js +14 -0
  26. package/src/actions/index.js +1 -0
  27. package/src/components/manage/Add/Add.jsx +1 -0
  28. package/src/components/manage/Contents/ContentsUploadModal.jsx +31 -4
  29. package/src/components/manage/Edit/Edit.jsx +1 -0
  30. package/src/components/manage/Form/Form.jsx +92 -48
  31. package/src/config/index.js +1 -0
  32. package/src/constants/ActionTypes.js +1 -0
  33. package/src/helpers/Blocks/Blocks.js +25 -0
  34. package/src/helpers/Blocks/Blocks.test.js +37 -0
  35. package/src/helpers/Utils/usePagination.js +2 -7
  36. package/src/helpers/index.js +2 -0
  37. package/src/reducers/form/form.js +15 -3
  38. package/src/reducers/form/form.test.js +13 -1
  39. package/test-setup-config.js +1 -0
  40. package/theme/themes/pastanaga/extras/contents.less +11 -0
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  }
10
10
  ],
11
11
  "license": "MIT",
12
- "version": "16.30.3",
12
+ "version": "16.31.1",
13
13
  "repository": {
14
14
  "type": "git",
15
15
  "url": "git@github.com:plone/volto.git"
@@ -0,0 +1 @@
1
+ Fix sidebar form update. @robgietema
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plone/volto-slate",
3
- "version": "16.30.3",
3
+ "version": "16.31.1",
4
4
  "description": "Slate.js integration with Volto",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -5,7 +5,7 @@
5
5
 
6
6
  import React, { Component } from 'react';
7
7
  import PropTypes from 'prop-types';
8
- import { map, remove } from 'lodash';
8
+ import { isEmpty, map, remove } from 'lodash';
9
9
  import { Button, Table } from 'semantic-ui-react';
10
10
  import cx from 'classnames';
11
11
  import { defineMessages, injectIntl } from 'react-intl';
@@ -227,7 +227,7 @@ class Edit extends Component {
227
227
  * @returns {undefined}
228
228
  */
229
229
  componentDidMount() {
230
- if (!this.props.data.table) {
230
+ if (!this.props.data.table || isEmpty(this.props.data.table)) {
231
231
  this.props.onChangeBlock(this.props.block, {
232
232
  ...this.props.data,
233
233
  table: initialTable,
@@ -243,7 +243,7 @@ class Edit extends Component {
243
243
  * @returns {undefined}
244
244
  */
245
245
  UNSAFE_componentWillReceiveProps(nextProps) {
246
- if (!nextProps.data.table) {
246
+ if (!nextProps.data.table || isEmpty(nextProps.data.table)) {
247
247
  this.props.onChangeBlock(nextProps.block, {
248
248
  ...nextProps.data,
249
249
  table: initialTable,
@@ -528,10 +528,7 @@ class Edit extends Component {
528
528
  icon
529
529
  basic
530
530
  onClick={this.onDeleteRow}
531
- disabled={
532
- this.props.data.table &&
533
- this.props.data.table.rows.length === 1
534
- }
531
+ disabled={this.props.data.table?.rows?.length === 1}
535
532
  title={this.props.intl.formatMessage(messages.deleteRow)}
536
533
  aria-label={this.props.intl.formatMessage(messages.deleteRow)}
537
534
  >
@@ -569,10 +566,7 @@ class Edit extends Component {
569
566
  icon
570
567
  basic
571
568
  onClick={this.onDeleteCol}
572
- disabled={
573
- this.props.data.table &&
574
- this.props.data.table.rows[0].cells.length === 1
575
- }
569
+ disabled={this.props.data.table?.rows?.[0].cells.length === 1}
576
570
  title={this.props.intl.formatMessage(messages.deleteCol)}
577
571
  aria-label={this.props.intl.formatMessage(messages.deleteCol)}
578
572
  >
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Form actions.
3
+ * @module actions/form/form
4
+ */
5
+
6
+ import { SET_FORM_DATA } from '@plone/volto/constants/ActionTypes';
7
+
8
+ /**
9
+ * Set form data function.
10
+ * @function setFormData
11
+ * @param {Object} data New form data.
12
+ * @returns {Object} Set sidebar action.
13
+ */
14
+ export function setFormData(data) {
15
+ return {
16
+ type: SET_FORM_DATA,
17
+ data,
18
+ };
19
+ }
@@ -0,0 +1,14 @@
1
+ import { setFormData } from './form';
2
+ import { SET_FORM_DATA } from '@plone/volto/constants/ActionTypes';
3
+
4
+ describe('Form action', () => {
5
+ describe('setFormData', () => {
6
+ it('should create an action to set the form data', () => {
7
+ const data = { foo: 'bar' };
8
+ const action = setFormData(data);
9
+
10
+ expect(action.type).toEqual(SET_FORM_DATA);
11
+ expect(action.data).toEqual(data);
12
+ });
13
+ });
14
+ });
@@ -150,6 +150,7 @@ export {
150
150
  export { getQuerystring } from '@plone/volto/actions/querystring/querystring';
151
151
  export { getQueryStringResults } from '@plone/volto/actions/querystringsearch/querystringsearch';
152
152
  export { setSidebarTab } from '@plone/volto/actions/sidebar/sidebar';
153
+ export { setFormData } from '@plone/volto/actions/form/form';
153
154
  export {
154
155
  deleteLinkTranslation,
155
156
  getTranslationLocator,
@@ -359,6 +359,7 @@ class Add extends Component {
359
359
  onSelectForm={() => {
360
360
  this.setState({ formSelected: 'addForm' });
361
361
  }}
362
+ global
362
363
  />
363
364
  {this.state.isClient && (
364
365
  <Portal node={document.getElementById('toolbar')}>
@@ -17,6 +17,7 @@ import {
17
17
  Modal,
18
18
  Table,
19
19
  Segment,
20
+ Input,
20
21
  } from 'semantic-ui-react';
21
22
  import loadable from '@loadable/component';
22
23
  import { concat, filter, map } from 'lodash';
@@ -150,7 +151,27 @@ class ContentsUploadModal extends Component {
150
151
  }
151
152
 
152
153
  /**
153
- * Submit handler
154
+ * Name change handler
155
+ * @method onChangeFileName
156
+ * @returns {undefined}
157
+ */
158
+
159
+ onChangeFileName(e, index) {
160
+ let copyOfFiles = [...this.state.files];
161
+ let originalFile = this.state.files[index];
162
+ let newFile = new File([originalFile], e.target.value, {
163
+ type: originalFile.type,
164
+ });
165
+ newFile.preview = originalFile.preview;
166
+ newFile.path = e.target.value;
167
+ copyOfFiles[index] = newFile;
168
+ this.setState({
169
+ files: copyOfFiles,
170
+ });
171
+ }
172
+
173
+ /**
174
+ * Submit handlers
154
175
  * @method onSubmit
155
176
  * @returns {undefined}
156
177
  */
@@ -187,7 +208,7 @@ class ContentsUploadModal extends Component {
187
208
  render() {
188
209
  return (
189
210
  this.props.open && (
190
- <Modal open={this.props.open}>
211
+ <Modal className="contents-upload-modal" open={this.props.open}>
191
212
  <Header>
192
213
  <FormattedMessage id="Upload files" defaultMessage="Upload files" />
193
214
  </Header>
@@ -269,8 +290,14 @@ class ContentsUploadModal extends Component {
269
290
  </Table.Header>
270
291
  <Table.Body>
271
292
  {map(this.state.files, (file, index) => (
272
- <Table.Row className="upload-row" key={file.name}>
273
- <Table.Cell>{file.name}</Table.Cell>
293
+ <Table.Row className="upload-row" key={index}>
294
+ <Table.Cell>
295
+ <Input
296
+ className="file-name"
297
+ value={file.name}
298
+ onChange={(e) => this.onChangeFileName(e, index)}
299
+ />
300
+ </Table.Cell>
274
301
  <Table.Cell>
275
302
  {file.lastModifiedDate && (
276
303
  <FormattedRelativeDate date={file.lastModifiedDate} />
@@ -307,6 +307,7 @@ class Edit extends Component {
307
307
  onSelectForm={() => {
308
308
  this.setState({ formSelected: 'editForm' });
309
309
  }}
310
+ global
310
311
  />
311
312
  );
312
313
 
@@ -16,6 +16,7 @@ import clearSVG from '@plone/volto/icons/clear.svg';
16
16
  import {
17
17
  findIndex,
18
18
  isEmpty,
19
+ isEqual,
19
20
  keys,
20
21
  map,
21
22
  mapValues,
@@ -40,7 +41,7 @@ import {
40
41
  import { v4 as uuid } from 'uuid';
41
42
  import { toast } from 'react-toastify';
42
43
  import { BlocksToolbar, UndoToolbar } from '@plone/volto/components';
43
- import { setSidebarTab } from '@plone/volto/actions';
44
+ import { setSidebarTab, setFormData } from '@plone/volto/actions';
44
45
  import { compose } from 'redux';
45
46
  import config from '@plone/volto/registry';
46
47
 
@@ -69,6 +70,7 @@ class Form extends Component {
69
70
  required: PropTypes.arrayOf(PropTypes.string),
70
71
  }),
71
72
  formData: PropTypes.objectOf(PropTypes.any),
73
+ globalData: PropTypes.objectOf(PropTypes.any),
72
74
  pathname: PropTypes.string,
73
75
  onSubmit: PropTypes.func,
74
76
  onCancel: PropTypes.func,
@@ -93,6 +95,7 @@ class Form extends Component {
93
95
  requestError: PropTypes.string,
94
96
  allowedBlocks: PropTypes.arrayOf(PropTypes.string),
95
97
  showRestricted: PropTypes.bool,
98
+ global: PropTypes.bool,
96
99
  };
97
100
 
98
101
  /**
@@ -123,6 +126,7 @@ class Form extends Component {
123
126
  editable: true,
124
127
  requestError: null,
125
128
  allowedBlocks: null,
129
+ global: false,
126
130
  };
127
131
 
128
132
  /**
@@ -198,6 +202,12 @@ class Form extends Component {
198
202
  }
199
203
  }
200
204
 
205
+ // Sync state to global state
206
+ if (this.props.global) {
207
+ this.props.setFormData(formData);
208
+ }
209
+
210
+ // Set initial state
201
211
  this.state = {
202
212
  formData,
203
213
  initialFormData: cloneDeep(formData),
@@ -244,14 +254,18 @@ class Form extends Component {
244
254
  }
245
255
 
246
256
  if (this.props.onChangeFormData) {
247
- if (
248
- // TODO: use fast-deep-equal
249
- JSON.stringify(prevState?.formData) !==
250
- JSON.stringify(this.state.formData)
251
- ) {
257
+ if (!isEqual(prevState?.formData, this.state.formData)) {
252
258
  this.props.onChangeFormData(this.state.formData);
253
259
  }
254
260
  }
261
+ if (
262
+ this.props.global &&
263
+ !isEqual(this.props.globalData, prevProps.globalData)
264
+ ) {
265
+ this.setState({
266
+ formData: this.props.globalData,
267
+ });
268
+ }
255
269
  }
256
270
 
257
271
  /**
@@ -325,15 +339,18 @@ class Form extends Component {
325
339
  onChangeField(id, value) {
326
340
  this.setState((prevState) => {
327
341
  const { errors, formData } = prevState;
342
+ const newFormData = {
343
+ ...formData,
344
+ // We need to catch also when the value equals false this fixes #888
345
+ [id]: value || (value !== undefined && isBoolean(value)) ? value : null,
346
+ };
328
347
  delete errors[id];
348
+ if (this.props.global) {
349
+ this.props.setFormData(newFormData);
350
+ }
329
351
  return {
330
352
  errors,
331
- formData: {
332
- ...formData,
333
- // We need to catch also when the value equals false this fixes #888
334
- [id]:
335
- value || (value !== undefined && isBoolean(value)) ? value : null,
336
- },
353
+ formData: newFormData,
337
354
  // Changing the form data re-renders the select widget which causes the
338
355
  // focus to get lost. To circumvent this, we set the focus back to
339
356
  // the input.
@@ -355,14 +372,13 @@ class Form extends Component {
355
372
  onSelectBlock(id, isMultipleSelection, event) {
356
373
  let multiSelected = [];
357
374
  let selected = id;
375
+ const formData = this.state.formData;
358
376
 
359
377
  if (isMultipleSelection) {
360
378
  selected = null;
361
- const blocksLayoutFieldname = getBlocksLayoutFieldname(
362
- this.state.formData,
363
- );
379
+ const blocksLayoutFieldname = getBlocksLayoutFieldname(formData);
364
380
 
365
- const blocks_layout = this.state.formData[blocksLayoutFieldname].items;
381
+ const blocks_layout = formData[blocksLayoutFieldname].items;
366
382
 
367
383
  if (event.shiftKey) {
368
384
  const anchor =
@@ -422,6 +438,9 @@ class Form extends Component {
422
438
  this.setState({
423
439
  formData: this.props.formData,
424
440
  });
441
+ if (this.props.global) {
442
+ this.props.setFormData(this.props.formData);
443
+ }
425
444
  }
426
445
  this.props.onCancel(event);
427
446
  }
@@ -433,6 +452,8 @@ class Form extends Component {
433
452
  * @returns {undefined}
434
453
  */
435
454
  onSubmit(event) {
455
+ const formData = this.state.formData;
456
+
436
457
  if (event) {
437
458
  event.preventDefault();
438
459
  }
@@ -440,7 +461,7 @@ class Form extends Component {
440
461
  const errors = this.props.schema
441
462
  ? FormValidation.validateFieldsPerFieldset({
442
463
  schema: this.props.schema,
443
- formData: this.state.formData,
464
+ formData,
444
465
  formatMessage: this.props.intl.formatMessage,
445
466
  })
446
467
  : {};
@@ -475,12 +496,15 @@ class Form extends Component {
475
496
  if (this.props.isEditForm) {
476
497
  this.props.onSubmit(this.getOnlyFormModifiedValues());
477
498
  } else {
478
- this.props.onSubmit(this.state.formData);
499
+ this.props.onSubmit(formData);
479
500
  }
480
501
  if (this.props.resetAfterSubmit) {
481
502
  this.setState({
482
503
  formData: this.props.formData,
483
504
  });
505
+ if (this.props.global) {
506
+ this.props.setFormData(this.props.formData);
507
+ }
484
508
  }
485
509
  }
486
510
  }
@@ -495,15 +519,15 @@ class Form extends Component {
495
519
  * @returns {undefined}
496
520
  */
497
521
  getOnlyFormModifiedValues = () => {
522
+ const formData = this.state.formData;
523
+
498
524
  const fieldsModified = Object.keys(
499
- difference(this.state.formData, this.state.initialFormData),
525
+ difference(formData, this.state.initialFormData),
500
526
  );
501
527
  return {
502
- ...pickBy(this.state.formData, (value, key) =>
503
- fieldsModified.includes(key),
504
- ),
505
- ...(this.state.formData['@static_behaviors'] && {
506
- '@static_behaviors': this.state.formData['@static_behaviors'],
528
+ ...pickBy(formData, (value, key) => fieldsModified.includes(key)),
529
+ ...(formData['@static_behaviors'] && {
530
+ '@static_behaviors': formData['@static_behaviors'],
507
531
  }),
508
532
  };
509
533
  };
@@ -549,7 +573,7 @@ class Form extends Component {
549
573
  navRoot,
550
574
  type,
551
575
  } = this.props;
552
- const { formData } = this.state;
576
+ const formData = this.state.formData;
553
577
  const schema = this.removeBlocksLayoutFields(originalSchema);
554
578
  const Container =
555
579
  config.getComponent({ name: 'Container' }).component || SemanticContainer;
@@ -560,17 +584,21 @@ class Form extends Component {
560
584
  this.state.isClient && (
561
585
  <Container>
562
586
  <BlocksToolbar
563
- formData={this.state.formData}
587
+ formData={formData}
564
588
  selectedBlock={this.state.selected}
565
589
  selectedBlocks={this.state.multiSelected}
566
- onChangeBlocks={(newBlockData) =>
590
+ onChangeBlocks={(newBlockData) => {
591
+ const newFormData = {
592
+ ...formData,
593
+ ...newBlockData,
594
+ };
567
595
  this.setState({
568
- formData: {
569
- ...formData,
570
- ...newBlockData,
571
- },
572
- })
573
- }
596
+ formData: newFormData,
597
+ });
598
+ if (this.props.global) {
599
+ this.props.setFormData(newFormData);
600
+ }
601
+ }}
574
602
  onSetSelectedBlocks={(blockIds) =>
575
603
  this.setState({ multiSelected: blockIds })
576
604
  }
@@ -578,22 +606,31 @@ class Form extends Component {
578
606
  />
579
607
  <UndoToolbar
580
608
  state={{
581
- formData: this.state.formData,
609
+ formData,
582
610
  selected: this.state.selected,
583
611
  multiSelected: this.state.multiSelected,
584
612
  }}
585
613
  enableHotKeys
586
- onUndoRedo={({ state }) => this.setState(state)}
614
+ onUndoRedo={({ state }) => {
615
+ if (this.props.global) {
616
+ this.props.setFormData(state.formData);
617
+ }
618
+ return this.setState(state);
619
+ }}
587
620
  />
588
621
  <BlocksForm
589
- onChangeFormData={(newFormData) =>
622
+ onChangeFormData={(newData) => {
623
+ const newFormData = {
624
+ ...formData,
625
+ ...newData,
626
+ };
590
627
  this.setState({
591
- formData: {
592
- ...formData,
593
- ...newFormData,
594
- },
595
- })
596
- }
628
+ formData: newFormData,
629
+ });
630
+ if (this.props.global) {
631
+ this.props.setFormData(newFormData);
632
+ }
633
+ }}
597
634
  onChangeField={this.onChangeField}
598
635
  onSelectBlock={this.onSelectBlock}
599
636
  properties={formData}
@@ -633,9 +670,9 @@ class Form extends Component {
633
670
  {...schema.properties[field]}
634
671
  id={field}
635
672
  fieldSet={item.title.toLowerCase()}
636
- formData={this.state.formData}
673
+ formData={formData}
637
674
  focus={this.state.inFocus[field]}
638
- value={this.state.formData?.[field]}
675
+ value={formData?.[field]}
639
676
  required={schema.required.indexOf(field) !== -1}
640
677
  onChange={this.onChangeField}
641
678
  onBlur={this.onBlurField}
@@ -698,10 +735,10 @@ class Form extends Component {
698
735
  {...schema.properties[field]}
699
736
  isDisabled={!this.props.editable}
700
737
  id={field}
701
- formData={this.state.formData}
738
+ formData={formData}
702
739
  fieldSet={item.title.toLowerCase()}
703
740
  focus={this.state.inFocus[field]}
704
- value={this.state.formData?.[field]}
741
+ value={formData?.[field]}
705
742
  required={schema.required.indexOf(field) !== -1}
706
743
  onChange={this.onChangeField}
707
744
  onBlur={this.onBlurField}
@@ -749,7 +786,7 @@ class Form extends Component {
749
786
  <Field
750
787
  {...schema.properties[field]}
751
788
  id={field}
752
- value={this.state.formData?.[field]}
789
+ value={formData?.[field]}
753
790
  required={schema.required.indexOf(field) !== -1}
754
791
  onChange={this.onChangeField}
755
792
  onBlur={this.onBlurField}
@@ -810,5 +847,12 @@ class Form extends Component {
810
847
  const FormIntl = injectIntl(Form, { forwardRef: true });
811
848
 
812
849
  export default compose(
813
- connect(null, { setSidebarTab }, null, { forwardRef: true }),
850
+ connect(
851
+ (state, props) => ({
852
+ globalData: state.form?.global,
853
+ }),
854
+ { setSidebarTab, setFormData },
855
+ null,
856
+ { forwardRef: true },
857
+ ),
814
858
  )(FormIntl);
@@ -185,6 +185,7 @@ let config = {
185
185
  querystringSearchGet: false,
186
186
  blockSettingsTabFieldsetsInitialStateOpen: true,
187
187
  excludeLinksAndReferencesMenuItem: true,
188
+ containerBlockTypes: [],
188
189
  },
189
190
  experimental: {
190
191
  addBlockButton: {
@@ -138,3 +138,4 @@ export const REMOVE_ALIASES = 'REMOVE_ALIASES';
138
138
  export const GET_USERSCHEMA = 'GET_USERSCHEMA';
139
139
  export const GET_UPGRADE = 'GET_UPGRADE';
140
140
  export const POST_UPGRADE = 'POST_UPGRADE';
141
+ export const SET_FORM_DATA = 'SET_FORM_DATA';
@@ -533,3 +533,28 @@ export const getPreviousNextBlock = ({ content, block }) => {
533
533
 
534
534
  return [previousBlock, nextBlock];
535
535
  };
536
+
537
+ /**
538
+ * Given a `block` object and a list of block types, return a list of block ids matching the types
539
+ *
540
+ * @function findBlocks
541
+ * @param {Object} types A list with the list of types to be matched
542
+ * @return {Array} An array of block ids
543
+ */
544
+ export function findBlocks(blocks, types, result = []) {
545
+ const containerBlockTypes = config.settings.containerBlockTypes;
546
+
547
+ Object.keys(blocks).forEach((blockId) => {
548
+ const block = blocks[blockId];
549
+ // check blocks from data as well since some add-ons use that
550
+ // such as @eeacms/volto-tabs-block
551
+ const child_blocks = block.blocks || block.data?.blocks;
552
+ if (types.includes(block['@type'])) {
553
+ result.push(blockId);
554
+ } else if (containerBlockTypes.includes(block['@type']) || child_blocks) {
555
+ findBlocks(child_blocks, types, result);
556
+ }
557
+ });
558
+
559
+ return result;
560
+ }
@@ -19,6 +19,7 @@ import {
19
19
  buildStyleClassNamesFromData,
20
20
  buildStyleClassNamesExtenders,
21
21
  getPreviousNextBlock,
22
+ findBlocks,
22
23
  } from './Blocks';
23
24
 
24
25
  import config from '@plone/volto/registry';
@@ -1226,3 +1227,39 @@ describe('Blocks', () => {
1226
1227
  });
1227
1228
  });
1228
1229
  });
1230
+
1231
+ describe('findBlocks', () => {
1232
+ it('Get all blocks in the first level (main block container)', () => {
1233
+ const blocks = {
1234
+ '1': { title: 'title', '@type': 'title' },
1235
+ '2': { title: 'an image', '@type': 'image' },
1236
+ '3': { title: 'description', '@type': 'description' },
1237
+ '4': { title: 'a text', '@type': 'slate' },
1238
+ };
1239
+ const types = ['description'];
1240
+ expect(findBlocks(blocks, types)).toStrictEqual(['3']);
1241
+ });
1242
+
1243
+ it('Get all blocks in the first level (main block container) given a list', () => {
1244
+ const blocks = {
1245
+ '1': { title: 'title', '@type': 'title' },
1246
+ '2': { title: 'an image', '@type': 'image' },
1247
+ '3': { title: 'description', '@type': 'description' },
1248
+ '4': { title: 'a text', '@type': 'slate' },
1249
+ };
1250
+ const types = ['description', 'slate'];
1251
+ expect(findBlocks(blocks, types)).toStrictEqual(['3', '4']);
1252
+ });
1253
+
1254
+ it('Get all blocks in the first level (main block container) given a list', () => {
1255
+ const blocks = {
1256
+ '1': { title: 'title', '@type': 'title' },
1257
+ '2': { title: 'an image', '@type': 'image' },
1258
+ '3': { title: 'description', '@type': 'description' },
1259
+ '4': { title: 'a text', '@type': 'slate' },
1260
+ '5': { title: 'a text', '@type': 'slate' },
1261
+ };
1262
+ const types = ['description', 'slate'];
1263
+ expect(findBlocks(blocks, types)).toStrictEqual(['3', '4', '5']);
1264
+ });
1265
+ });
@@ -2,7 +2,7 @@ import React, { useRef, useEffect } from 'react';
2
2
  import { useHistory, useLocation } from 'react-router-dom';
3
3
  import qs from 'query-string';
4
4
  import { useSelector } from 'react-redux';
5
- import { slugify } from '@plone/volto/helpers/Utils/Utils';
5
+ import { findBlocks, slugify } from '@plone/volto/helpers';
6
6
 
7
7
  /**
8
8
  * @function useCreatePageQueryStringKey
@@ -12,13 +12,8 @@ import { slugify } from '@plone/volto/helpers/Utils/Utils';
12
12
  const useCreatePageQueryStringKey = (id) => {
13
13
  const blockTypesWithPagination = ['search', 'listing'];
14
14
  const blocks = useSelector((state) => state?.content?.data?.blocks) || [];
15
- const blocksLayout =
16
- useSelector((state) => state?.content?.data?.blocks_layout?.items) || [];
17
- const displayedBlocks = blocksLayout?.map((item) => blocks[item]);
18
15
  const hasMultiplePaginations =
19
- displayedBlocks.filter((item) =>
20
- blockTypesWithPagination.includes(item['@type']),
21
- ).length > 1 || false;
16
+ findBlocks(blocks, blockTypesWithPagination).length > 1;
22
17
 
23
18
  return hasMultiplePaginations ? slugify(`page-${id}`) : 'page';
24
19
  };
@@ -58,6 +58,7 @@ export {
58
58
  buildStyleClassNamesFromData,
59
59
  buildStyleClassNamesExtenders,
60
60
  getPreviousNextBlock,
61
+ findBlocks,
61
62
  } from '@plone/volto/helpers/Blocks/Blocks';
62
63
  export { default as BodyClass } from '@plone/volto/helpers/BodyClass/BodyClass';
63
64
  export { default as ScrollToTop } from '@plone/volto/helpers/ScrollToTop/ScrollToTop';
@@ -91,6 +92,7 @@ export {
91
92
  replaceItemOfArray,
92
93
  cloneDeepSchema,
93
94
  reorderArray,
95
+ slugify,
94
96
  } from '@plone/volto/helpers/Utils/Utils';
95
97
  export { messages } from './MessageLabels/MessageLabels';
96
98
  export {
@@ -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
  }