@plone/volto 17.0.0-alpha.15 → 17.0.0-alpha.16

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 (68) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/cypress/support/commands.js +18 -0
  3. package/locales/ca/LC_MESSAGES/volto.po +41 -0
  4. package/locales/ca.json +1 -1
  5. package/locales/de/LC_MESSAGES/volto.po +41 -0
  6. package/locales/de.json +1 -1
  7. package/locales/en/LC_MESSAGES/volto.po +41 -0
  8. package/locales/en.json +1 -1
  9. package/locales/es/LC_MESSAGES/volto.po +41 -0
  10. package/locales/es.json +1 -1
  11. package/locales/eu/LC_MESSAGES/volto.po +41 -0
  12. package/locales/eu.json +1 -1
  13. package/locales/fi/LC_MESSAGES/volto.po +41 -0
  14. package/locales/fi.json +1 -1
  15. package/locales/fr/LC_MESSAGES/volto.po +41 -0
  16. package/locales/fr.json +1 -1
  17. package/locales/it/LC_MESSAGES/volto.po +41 -0
  18. package/locales/it.json +1 -1
  19. package/locales/ja/LC_MESSAGES/volto.po +41 -0
  20. package/locales/ja.json +1 -1
  21. package/locales/nl/LC_MESSAGES/volto.po +41 -0
  22. package/locales/nl.json +1 -1
  23. package/locales/pt/LC_MESSAGES/volto.po +41 -0
  24. package/locales/pt.json +1 -1
  25. package/locales/pt_BR/LC_MESSAGES/volto.po +41 -0
  26. package/locales/pt_BR.json +1 -1
  27. package/locales/ro/LC_MESSAGES/volto.po +41 -0
  28. package/locales/ro.json +1 -1
  29. package/locales/volto.pot +42 -1
  30. package/locales/zh_CN/LC_MESSAGES/volto.po +41 -0
  31. package/locales/zh_CN.json +1 -1
  32. package/package.json +1 -1
  33. package/packages/volto-slate/package.json +1 -1
  34. package/packages/volto-slate/src/blocks/Text/index.js +8 -0
  35. package/src/components/manage/Blocks/Block/BlocksForm.jsx +19 -2
  36. package/src/components/manage/Blocks/Block/Edit.jsx +1 -1
  37. package/src/components/manage/Blocks/Container/Data.jsx +32 -0
  38. package/src/components/manage/Blocks/Container/Edit.jsx +174 -0
  39. package/src/components/manage/Blocks/Container/EditBlockWrapper.jsx +120 -0
  40. package/src/components/manage/Blocks/Container/NewBlockAddButton.jsx +84 -0
  41. package/src/components/manage/Blocks/Container/SimpleContainerToolbar.jsx +54 -0
  42. package/src/components/manage/Blocks/Grid/Edit.jsx +33 -0
  43. package/src/components/manage/Blocks/Grid/View.jsx +42 -0
  44. package/src/components/manage/Blocks/Grid/adapter.js +14 -0
  45. package/src/components/manage/Blocks/Grid/grid-1.svg +6 -0
  46. package/src/components/manage/Blocks/Grid/grid-2.svg +9 -0
  47. package/src/components/manage/Blocks/Grid/grid-3.svg +10 -0
  48. package/src/components/manage/Blocks/Grid/grid-4.svg +11 -0
  49. package/src/components/manage/Blocks/Grid/schema.js +35 -0
  50. package/src/components/manage/Blocks/Grid/templates.js +47 -0
  51. package/src/components/manage/Blocks/Image/ImageSidebar.jsx +2 -1
  52. package/src/components/manage/Blocks/Image/schema.js +11 -0
  53. package/src/components/manage/Blocks/Teaser/schema.js +5 -0
  54. package/src/components/manage/DragDropList/DragDropList.jsx +18 -13
  55. package/src/components/manage/TemplateChooser/TemplateChooser.jsx +38 -0
  56. package/src/components/manage/TemplateChooser/TemplateChooser.test.jsx +34 -0
  57. package/src/components/manage/TemplateChooser/template.svg +10 -0
  58. package/src/components/theme/Component/Component.jsx +1 -1
  59. package/src/components/theme/View/RenderBlocks.jsx +56 -27
  60. package/src/components/theme/View/RenderEmptyBlock.jsx +5 -0
  61. package/src/config/Blocks.jsx +44 -0
  62. package/src/helpers/Blocks/Blocks.js +26 -0
  63. package/src/helpers/Blocks/Blocks.test.js +21 -0
  64. package/src/helpers/Utils/Utils.js +25 -0
  65. package/src/helpers/index.js +2 -0
  66. package/src/icons/grid-block.svg +11 -0
  67. package/theme/themes/pastanaga/extras/grid.less +426 -0
  68. package/theme/themes/pastanaga/extras/main.less +1 -0
@@ -1,7 +1,8 @@
1
1
  import React from 'react';
2
2
  import { getBaseUrl, applyBlockDefaults } from '@plone/volto/helpers';
3
- import { defineMessages, injectIntl } from 'react-intl';
3
+ import { defineMessages, useIntl } from 'react-intl';
4
4
  import { map } from 'lodash';
5
+ import { MaybeWrap } from '@plone/volto/components';
5
6
  import {
6
7
  getBlocksFieldname,
7
8
  getBlocksLayoutFieldname,
@@ -10,6 +11,7 @@ import {
10
11
  import StyleWrapper from '@plone/volto/components/manage/Blocks/Block/StyleWrapper';
11
12
  import config from '@plone/volto/registry';
12
13
  import { ViewDefaultBlock } from '@plone/volto/components';
14
+ import RenderEmptyBlock from './RenderEmptyBlock';
13
15
 
14
16
  const messages = defineMessages({
15
17
  unknownBlock: {
@@ -23,7 +25,8 @@ const messages = defineMessages({
23
25
  });
24
26
 
25
27
  const RenderBlocks = (props) => {
26
- const { content, intl, location, metadata } = props;
28
+ const { content, location, metadata, blockWrapperTag } = props;
29
+ const intl = useIntl();
27
30
  const blocksFieldname = getBlocksFieldname(content);
28
31
  const blocksLayoutFieldname = getBlocksLayoutFieldname(content);
29
32
  const blocksConfig = props.blocksConfig || config.blocks.blocksConfig;
@@ -43,30 +46,56 @@ const RenderBlocks = (props) => {
43
46
  properties: content,
44
47
  });
45
48
 
46
- return Block ? (
47
- <StyleWrapper
48
- key={block}
49
- {...props}
50
- id={block}
51
- block={block}
52
- data={blockData}
53
- >
54
- <Block
55
- id={block}
56
- metadata={metadata}
57
- properties={content}
58
- data={blockData}
59
- path={getBaseUrl(location?.pathname || '')}
60
- blocksConfig={blocksConfig}
61
- />
62
- </StyleWrapper>
63
- ) : blockData ? (
64
- <div key={block}>
65
- {intl.formatMessage(messages.unknownBlock, {
66
- block: content[blocksFieldname]?.[block]?.['@type'],
67
- })}
68
- </div>
69
- ) : (
49
+ if (content[blocksFieldname]?.[block]?.['@type'] === 'empty') {
50
+ return (
51
+ <MaybeWrap
52
+ key={block}
53
+ condition={blockWrapperTag}
54
+ as={blockWrapperTag}
55
+ >
56
+ <RenderEmptyBlock />
57
+ </MaybeWrap>
58
+ );
59
+ }
60
+
61
+ if (Block) {
62
+ return (
63
+ <MaybeWrap
64
+ key={block}
65
+ condition={blockWrapperTag}
66
+ as={blockWrapperTag}
67
+ >
68
+ <StyleWrapper
69
+ key={block}
70
+ {...props}
71
+ id={block}
72
+ block={block}
73
+ data={blockData}
74
+ >
75
+ <Block
76
+ id={block}
77
+ metadata={metadata}
78
+ properties={content}
79
+ data={blockData}
80
+ path={getBaseUrl(location?.pathname || '')}
81
+ blocksConfig={blocksConfig}
82
+ />
83
+ </StyleWrapper>
84
+ </MaybeWrap>
85
+ );
86
+ }
87
+
88
+ if (blockData) {
89
+ return (
90
+ <div key={block}>
91
+ {intl.formatMessage(messages.unknownBlock, {
92
+ block: content[blocksFieldname]?.[block]?.['@type'],
93
+ })}
94
+ </div>
95
+ );
96
+ }
97
+
98
+ return (
70
99
  <div key={block}>{intl.formatMessage(messages.invalidBlock)}</div>
71
100
  );
72
101
  })}
@@ -76,4 +105,4 @@ const RenderBlocks = (props) => {
76
105
  );
77
106
  };
78
107
 
79
- export default injectIntl(RenderBlocks);
108
+ export default RenderBlocks;
@@ -0,0 +1,5 @@
1
+ const RenderEmptyBlock = () => {
2
+ return null;
3
+ };
4
+
5
+ export default RenderEmptyBlock;
@@ -42,6 +42,7 @@ import tableSVG from '@plone/volto/icons/table.svg';
42
42
  import listingBlockSVG from '@plone/volto/icons/content-listing.svg';
43
43
  import tocSVG from '@plone/volto/icons/list-bullet.svg';
44
44
  import searchSVG from '@plone/volto/icons/zoom.svg';
45
+ import gridSVG from '@plone/volto/icons/grid-block.svg';
45
46
  import imagesSVG from '@plone/volto/icons/images.svg';
46
47
 
47
48
  import ImageGalleryListingBlockTemplate from '@plone/volto/components/manage/Blocks/Listing/ImageGallery';
@@ -50,6 +51,14 @@ import TextSettingsSchema from '@plone/volto/components/manage/Blocks/Text/Schem
50
51
  import ImageSettingsSchema from '@plone/volto/components/manage/Blocks/Image/LayoutSchema';
51
52
  import ToCSettingsSchema from '@plone/volto/components/manage/Blocks/ToC/Schema';
52
53
 
54
+ import GridBlockView from '@plone/volto/components/manage/Blocks/Grid/View';
55
+ import GridBlockEdit from '@plone/volto/components/manage/Blocks/Grid/Edit';
56
+ import { GridBlockDataAdapter } from '@plone/volto/components/manage/Blocks/Grid/adapter';
57
+ import { GridBlockSchema } from '@plone/volto/components/manage/Blocks/Grid/schema';
58
+ import GridTemplates from '@plone/volto/components/manage/Blocks/Grid/templates';
59
+ import { gridTeaserDisableStylingSchema } from '@plone/volto/components/manage/Blocks/Teaser/schema';
60
+ import { gridImageDisableSizeAndPositionHandlersSchema } from '@plone/volto/components/manage/Blocks/Image/schema';
61
+
53
62
  import SearchBlockView from '@plone/volto/components/manage/Blocks/Search/SearchBlockView';
54
63
  import SearchBlockEdit from '@plone/volto/components/manage/Blocks/Search/SearchBlockEdit';
55
64
 
@@ -458,6 +467,27 @@ const blocksConfig = {
458
467
  },
459
468
  },
460
469
  },
470
+ // This next block is not named just grid for some reasons:
471
+ // 1.- Naming it grid will collide with the SemanticUI CSS of the Grid component
472
+ // 2.- It would prevent the transition from the old grid
473
+ // (based on @kitconcept/volto-blocks-grid) without having to perform any migration.
474
+ // This way, both can co-exist at the same time.
475
+ gridBlock: {
476
+ id: 'gridBlock',
477
+ title: 'Grid',
478
+ icon: gridSVG,
479
+ group: 'common',
480
+ view: GridBlockView,
481
+ edit: GridBlockEdit,
482
+ blockSchema: GridBlockSchema,
483
+ dataAdapter: GridBlockDataAdapter,
484
+ restricted: false,
485
+ mostUsed: true,
486
+ sidebarTab: 1,
487
+ templates: GridTemplates,
488
+ maxLength: 4,
489
+ allowedBlocks: ['image', 'listing', 'slate', 'teaser'],
490
+ },
461
491
  teaser: {
462
492
  id: 'teaser',
463
493
  title: 'Teaser',
@@ -481,6 +511,20 @@ const blocksConfig = {
481
511
  },
482
512
  };
483
513
 
514
+ // This is required in order to initialize the inner blocksConfig
515
+ // for the grid block, since we need to modify how the inner teaser
516
+ // block behave in it (= no schemaEnhancer fields for teasers inside a grid)
517
+ // Afterwards, it can be further customized in add-ons using the same technique.
518
+ blocksConfig.gridBlock.blocksConfig = { ...blocksConfig };
519
+ blocksConfig.gridBlock.blocksConfig.teaser = {
520
+ ...blocksConfig.teaser,
521
+ schemaEnhancer: gridTeaserDisableStylingSchema,
522
+ };
523
+ blocksConfig.gridBlock.blocksConfig.image = {
524
+ ...blocksConfig.image,
525
+ schemaEnhancer: gridImageDisableSizeAndPositionHandlersSchema,
526
+ };
527
+
484
528
  const requiredBlocks = ['title'];
485
529
 
486
530
  const initialBlocks = {};
@@ -343,6 +343,32 @@ export function emptyBlocksForm() {
343
343
  };
344
344
  }
345
345
 
346
+ /**
347
+ * Generate empty blocks blocks/blocks_layout pair given the type
348
+ * (could be empty, if not type given) and the number of blocks
349
+ * @function blocksFormGenerator
350
+ * @param {number} number How many blocks to generate of the type (could be "empty", if no type provided)
351
+ * @param {number} type The type of the blocks
352
+ * @return {Object} blocks/blocks_layout pair filled with the generated blocks
353
+ */
354
+ export function blocksFormGenerator(number, type) {
355
+ const idMap = [...Array(number).keys()].map(() => uuid());
356
+ const start = {
357
+ blocks: {},
358
+ blocks_layout: { items: idMap },
359
+ };
360
+
361
+ return {
362
+ ...start,
363
+ blocks: Object.fromEntries(
364
+ start.blocks_layout.items.map((item) => [
365
+ item,
366
+ { '@type': type || 'empty' },
367
+ ]),
368
+ ),
369
+ };
370
+ }
371
+
346
372
  /**
347
373
  * Recursively discover blocks in data and call the provided callback
348
374
  * @function visitBlocks
@@ -19,6 +19,7 @@ import {
19
19
  buildStyleClassNamesFromData,
20
20
  buildStyleClassNamesExtenders,
21
21
  getPreviousNextBlock,
22
+ blocksFormGenerator,
22
23
  } from './Blocks';
23
24
 
24
25
  import config from '@plone/volto/registry';
@@ -1275,4 +1276,24 @@ describe('Blocks', () => {
1275
1276
  ]);
1276
1277
  });
1277
1278
  });
1279
+
1280
+ describe('blocksFormGenerator', () => {
1281
+ it('Returns an empty blocks/blocks_layout pair', () => {
1282
+ expect(blocksFormGenerator(0, '')).toEqual({
1283
+ blocks: {},
1284
+ blocks_layout: { items: [] },
1285
+ });
1286
+ });
1287
+ it.only('Returns a filled blocks/blocks_layout pair with type block', () => {
1288
+ const result = blocksFormGenerator(2, 'teaser');
1289
+ expect(Object.keys(result.blocks).length).toEqual(2);
1290
+ expect(result.blocks_layout.items.length).toEqual(2);
1291
+ expect(result.blocks[result.blocks_layout.items[0]]['@type']).toEqual(
1292
+ 'teaser',
1293
+ );
1294
+ expect(result.blocks[result.blocks_layout.items[1]]['@type']).toEqual(
1295
+ 'teaser',
1296
+ );
1297
+ });
1298
+ });
1278
1299
  });
@@ -324,3 +324,28 @@ export const arrayRange = (start, stop, step) =>
324
324
  { length: (stop - start) / step + 1 },
325
325
  (value, index) => start + index * step,
326
326
  );
327
+
328
+ /**
329
+ * Given an event target element returns if it's an interactive element
330
+ * of the one in the list.
331
+ * @param {node} element event.target element type
332
+ * @returns {boolean} If it's an interactive element of the list
333
+ */
334
+ export function isInteractiveElement(
335
+ element,
336
+ interactiveElements = [
337
+ 'button',
338
+ 'input',
339
+ 'textarea',
340
+ 'select',
341
+ 'option',
342
+ 'svg',
343
+ 'path',
344
+ ],
345
+ ) {
346
+ if (interactiveElements.includes(element.tagName.toLowerCase())) {
347
+ return true;
348
+ }
349
+
350
+ return false;
351
+ }
@@ -54,6 +54,7 @@ export {
54
54
  previousBlockId,
55
55
  applyBlockDefaults,
56
56
  applySchemaDefaults,
57
+ blocksFormGenerator,
57
58
  buildStyleClassNamesFromData,
58
59
  buildStyleClassNamesExtenders,
59
60
  getPreviousNextBlock,
@@ -91,6 +92,7 @@ export {
91
92
  cloneDeepSchema,
92
93
  arrayRange,
93
94
  reorderArray,
95
+ isInteractiveElement,
94
96
  } from '@plone/volto/helpers/Utils/Utils';
95
97
  export { messages } from './MessageLabels/MessageLabels';
96
98
  export {
@@ -0,0 +1,11 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 96 96">
2
+ <g fill="none" fill-rule="evenodd">
3
+ <rect width="96" height="96" fill="currentcolor" rx="3"/>
4
+ <g fill="#FFF" opacity=".9" transform="translate(8 22)">
5
+ <rect width="18" height="53" x="42"/>
6
+ <rect width="18" height="53" x="63"/>
7
+ <rect width="18" height="53"/>
8
+ <rect width="18" height="53" x="21"/>
9
+ </g>
10
+ </g>
11
+ </svg>