@plone/volto 17.0.0-alpha.17 → 17.0.0-alpha.18

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 (35) hide show
  1. package/.yarn/install-state.gz +0 -0
  2. package/CHANGELOG.md +37 -0
  3. package/cypress/support/commands.js +17 -0
  4. package/locales/it/LC_MESSAGES/volto.po +1 -1
  5. package/locales/it.json +1 -1
  6. package/package.json +2 -2
  7. package/packages/volto-slate/package.json +1 -1
  8. package/packages/volto-slate/src/blocks/Table/TableBlockEdit.jsx +21 -212
  9. package/packages/volto-slate/src/blocks/Table/schema.js +122 -0
  10. package/packages/volto-slate/src/editor/plugins/StyleMenu/utils.js +14 -5
  11. package/packages/volto-slate/src/utils/blocks.js +7 -0
  12. package/packages/volto-slate/src/widgets/RichTextWidget.jsx +15 -8
  13. package/src/components/manage/Blocks/Search/components/Facets.jsx +6 -2
  14. package/src/components/manage/Blocks/ToC/Schema.jsx +5 -1
  15. package/src/components/manage/Blocks/ToC/variations/HorizontalMenu.jsx +142 -8
  16. package/src/components/theme/Breadcrumbs/Breadcrumbs.jsx +52 -99
  17. package/src/components/theme/Breadcrumbs/Breadcrumbs.stories.jsx +14 -13
  18. package/src/components/theme/Comments/CommentEditModal.jsx +63 -115
  19. package/src/components/theme/ContactForm/ContactForm.jsx +108 -192
  20. package/src/components/theme/ContactForm/ContactForm.stories.jsx +1 -1
  21. package/src/components/theme/ContactForm/ContactForm.test.jsx +2 -3
  22. package/src/components/theme/Login/Login.jsx +1 -1
  23. package/src/components/theme/SearchWidget/SearchWidget.jsx +38 -98
  24. package/src/components/theme/View/LinkView.jsx +51 -79
  25. package/src/hooks/client/useClient.js +11 -0
  26. package/src/hooks/index.js +1 -0
  27. package/theme/themes/pastanaga/extras/main.less +2 -1
  28. package/theme/themes/pastanaga/extras/toc.less +29 -0
  29. package/news/4351.bugfix +0 -1
  30. package/news/4725.bugfix +0 -1
  31. package/news/4932.bugfix +0 -1
  32. package/news/4941.documentation +0 -1
  33. package/news/4951.breaking +0 -1
  34. package/news/4962.feature +0 -1
  35. package/news/4964.bugfix +0 -1
@@ -6,13 +6,13 @@
6
6
  import React, { Component } from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
  import { map, remove } from 'lodash';
9
- import { Button, Segment, Table, Form } from 'semantic-ui-react';
10
- import { Portal } from 'react-portal';
9
+ import { Button, Table } from 'semantic-ui-react';
11
10
  import cx from 'classnames';
12
- import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
11
+ import { defineMessages, injectIntl } from 'react-intl';
13
12
 
14
13
  import Cell from './Cell';
15
- import { Field, Icon } from '@plone/volto/components';
14
+ import { BlockDataForm, Icon, SidebarPortal } from '@plone/volto/components';
15
+ import TableSchema from './schema';
16
16
 
17
17
  import rowBeforeSVG from '@plone/volto/icons/row-before.svg';
18
18
  import rowAfterSVG from '@plone/volto/icons/row-after.svg';
@@ -130,42 +130,6 @@ const messages = defineMessages({
130
130
  id: 'Delete col',
131
131
  defaultMessage: 'Delete col',
132
132
  },
133
- hideHeaders: {
134
- id: 'Hide headers',
135
- defaultMessage: 'Hide headers',
136
- },
137
- sortable: {
138
- id: 'Make the table sortable',
139
- defaultMessage: 'Make the table sortable',
140
- },
141
- sortableDescription: {
142
- id: 'Visible only in view mode',
143
- defaultMessage: 'Visible only in view mode',
144
- },
145
- fixed: {
146
- id: 'Fixed width table cells',
147
- defaultMessage: 'Fixed width table cells',
148
- },
149
- compact: {
150
- id: 'Make the table compact',
151
- defaultMessage: 'Make the table compact',
152
- },
153
- basic: {
154
- id: 'Reduce complexity',
155
- defaultMessage: 'Reduce complexity',
156
- },
157
- celled: {
158
- id: 'Divide each row into separate cells',
159
- defaultMessage: 'Divide each row into separate cells',
160
- },
161
- inverted: {
162
- id: 'Table color inverted',
163
- defaultMessage: 'Table color inverted',
164
- },
165
- striped: {
166
- id: 'Stripe alternate rows with color',
167
- defaultMessage: 'Stripe alternate rows with color',
168
- },
169
133
  left: {
170
134
  id: 'Left',
171
135
  defaultMessage: 'Left',
@@ -255,15 +219,6 @@ class Edit extends Component {
255
219
  this.onDeleteCol = this.onDeleteCol.bind(this);
256
220
  this.onChangeCell = this.onChangeCell.bind(this);
257
221
  this.toggleCellType = this.toggleCellType.bind(this);
258
- this.toggleBool = this.toggleBool.bind(this);
259
- this.toggleHideHeaders = this.toggleHideHeaders.bind(this);
260
- this.toggleSortable = this.toggleSortable.bind(this);
261
- this.toggleFixed = this.toggleFixed.bind(this);
262
- this.toggleCompact = this.toggleCompact.bind(this);
263
- this.toggleBasic = this.toggleBasic.bind(this);
264
- this.toggleCelled = this.toggleCelled.bind(this);
265
- this.toggleInverted = this.toggleInverted.bind(this);
266
- this.toggleStriped = this.toggleStriped.bind(this);
267
222
  }
268
223
 
269
224
  /**
@@ -519,95 +474,6 @@ class Edit extends Component {
519
474
  });
520
475
  }
521
476
 
522
- /**
523
- * Toggles bool state data ('fixed', 'compact' etc. can be true or false).
524
- * @method toggleBool
525
- * @param {string} value Key in the table state to toggle.
526
- * @returns {undefined}
527
- */
528
- toggleBool(value) {
529
- const table = this.props.data.table;
530
- this.props.onChangeBlock(this.props.block, {
531
- ...this.props.data,
532
- table: {
533
- ...table,
534
- [value]: !table[value],
535
- },
536
- });
537
- }
538
-
539
- /**
540
- * Toggle fixed
541
- * @method toggleHideHeaders
542
- * @returns {undefined}
543
- */
544
- toggleHideHeaders() {
545
- this.toggleBool('hideHeaders');
546
- }
547
-
548
- /**
549
- * Toggle sortable
550
- * @method toggleSortable
551
- * @returns {undefined}
552
- */
553
- toggleSortable() {
554
- this.toggleBool('sortable');
555
- }
556
-
557
- /**
558
- * Toggle fixed
559
- * @method toggleFixed
560
- * @returns {undefined}
561
- */
562
- toggleFixed() {
563
- this.toggleBool('fixed');
564
- }
565
-
566
- /**
567
- * Toggle compact
568
- * @method toggleCompact
569
- * @returns {undefined}
570
- */
571
- toggleCompact() {
572
- this.toggleBool('compact');
573
- }
574
-
575
- /**
576
- * Toggle basic
577
- * @method toggleBasic
578
- * @returns {undefined}
579
- */
580
- toggleBasic() {
581
- this.toggleBool('basic');
582
- }
583
-
584
- /**
585
- * Toggle celled
586
- * @method toggleCelled
587
- * @returns {undefined}
588
- */
589
- toggleCelled() {
590
- this.toggleBool('celled');
591
- }
592
-
593
- /**
594
- * Toggle inverted
595
- * @method toggleInverted
596
- * @returns {undefined}
597
- */
598
- toggleInverted() {
599
- this.toggleBool('inverted');
600
- }
601
-
602
- /**
603
- * Toggle striped
604
- * @method toggleStriped
605
- * @returns {undefined}
606
- */
607
- toggleStriped() {
608
- this.toggleBool('striped');
609
- }
610
-
611
477
  componentDidUpdate(prevProps) {
612
478
  if (prevProps.selected && !this.props.selected) {
613
479
  this.setState({ selected: null });
@@ -623,6 +489,7 @@ class Edit extends Component {
623
489
  const headers = this.props.data.table?.rows?.[0]?.cells || [];
624
490
  const rows =
625
491
  this.props.data.table?.rows?.filter((_, index) => index > 0) || [];
492
+ const schema = TableSchema(this.props);
626
493
 
627
494
  return (
628
495
  // TODO: use slate-table instead of table, but first copy the CSS styles
@@ -802,80 +669,22 @@ class Edit extends Component {
802
669
  </Table>
803
670
  )}
804
671
  {this.props.selected && this.state.selected && this.state.isClient && (
805
- <Portal node={document.getElementById('sidebar-properties')}>
806
- <Form method="post" onSubmit={(event) => event.preventDefault()}>
807
- <Segment secondary attached>
808
- <FormattedMessage id="Table" defaultMessage="Table" />
809
- </Segment>
810
- <Segment attached>
811
- <Field
812
- id="hideHeaders"
813
- title={this.props.intl.formatMessage(messages.hideHeaders)}
814
- type="boolean"
815
- value={
816
- this.props.data.table && this.props.data.table.hideHeaders
817
- }
818
- onChange={() => this.toggleHideHeaders()}
819
- />
820
- <Field
821
- id="sortable"
822
- title={this.props.intl.formatMessage(messages.sortable)}
823
- description={this.props.intl.formatMessage(
824
- messages.sortableDescription,
825
- )}
826
- type="boolean"
827
- value={
828
- this.props.data.table && this.props.data.table.sortable
829
- }
830
- onChange={() => this.toggleSortable()}
831
- />
832
- <Field
833
- id="fixed"
834
- title={this.props.intl.formatMessage(messages.fixed)}
835
- type="boolean"
836
- value={this.props.data.table && this.props.data.table.fixed}
837
- onChange={() => this.toggleFixed()}
838
- />
839
- <Field
840
- id="celled"
841
- title={this.props.intl.formatMessage(messages.celled)}
842
- type="boolean"
843
- value={this.props.data.table && this.props.data.table.celled}
844
- onChange={this.toggleCelled}
845
- />
846
- <Field
847
- id="striped"
848
- title={this.props.intl.formatMessage(messages.striped)}
849
- type="boolean"
850
- value={this.props.data.table && this.props.data.table.striped}
851
- onChange={this.toggleStriped}
852
- />
853
- <Field
854
- id="compact"
855
- title={this.props.intl.formatMessage(messages.compact)}
856
- type="boolean"
857
- value={this.props.data.table && this.props.data.table.compact}
858
- onChange={() => this.toggleCompact()}
859
- />
860
- <Field
861
- id="basic"
862
- title={this.props.intl.formatMessage(messages.basic)}
863
- type="boolean"
864
- value={this.props.data.table && this.props.data.table.basic}
865
- onChange={this.toggleBasic}
866
- />
867
- <Field
868
- id="inverted"
869
- title={this.props.intl.formatMessage(messages.inverted)}
870
- type="boolean"
871
- value={
872
- this.props.data.table && this.props.data.table.inverted
873
- }
874
- onChange={this.toggleInverted}
875
- />
876
- </Segment>
877
- </Form>
878
- </Portal>
672
+ <SidebarPortal selected={this.props.selected}>
673
+ <BlockDataForm
674
+ schema={schema}
675
+ title={schema.title}
676
+ onChangeField={(id, value) => {
677
+ this.props.onChangeBlock(this.props.block, {
678
+ ...this.props.data,
679
+ [id]: value,
680
+ });
681
+ }}
682
+ onChangeBlock={this.props.onChangeBlock}
683
+ formData={this.props.data}
684
+ block={this.props.block}
685
+ blocksConfig={this.props.blocksConfig}
686
+ />
687
+ </SidebarPortal>
879
688
  )}
880
689
  </div>
881
690
  );
@@ -0,0 +1,122 @@
1
+ import { defineMessages } from 'react-intl';
2
+
3
+ const messages = defineMessages({
4
+ hideHeaders: {
5
+ id: 'Hide headers',
6
+ defaultMessage: 'Hide headers',
7
+ },
8
+ sortable: {
9
+ id: 'Make the table sortable',
10
+ defaultMessage: 'Make the table sortable',
11
+ },
12
+ sortableDescription: {
13
+ id: 'Visible only in view mode',
14
+ defaultMessage: 'Visible only in view mode',
15
+ },
16
+ fixed: {
17
+ id: 'Fixed width table cells',
18
+ defaultMessage: 'Fixed width table cells',
19
+ },
20
+ compact: {
21
+ id: 'Make the table compact',
22
+ defaultMessage: 'Make the table compact',
23
+ },
24
+ basic: {
25
+ id: 'Reduce complexity',
26
+ defaultMessage: 'Reduce complexity',
27
+ },
28
+ celled: {
29
+ id: 'Divide each row into separate cells',
30
+ defaultMessage: 'Divide each row into separate cells',
31
+ },
32
+ inverted: {
33
+ id: 'Table color inverted',
34
+ defaultMessage: 'Table color inverted',
35
+ },
36
+ striped: {
37
+ id: 'Stripe alternate rows with color',
38
+ defaultMessage: 'Stripe alternate rows with color',
39
+ },
40
+ });
41
+
42
+ function TableSchema(props) {
43
+ const { intl } = props;
44
+ return {
45
+ title: 'Table block',
46
+ fieldsets: [
47
+ {
48
+ id: 'default',
49
+ title: 'Default',
50
+ fields: [
51
+ 'hideHeaders',
52
+ 'sortable',
53
+ 'fixed',
54
+ 'celled',
55
+ 'striped',
56
+ 'compact',
57
+ 'basic',
58
+ 'inverted',
59
+ ],
60
+ },
61
+ ],
62
+ properties: {
63
+ hideHeaders: {
64
+ title: intl.formatMessage(messages.hideHeaders),
65
+ type: 'boolean',
66
+ },
67
+ sortable: {
68
+ title: intl.formatMessage(messages.sortable),
69
+ type: 'boolean',
70
+ },
71
+ fixed: {
72
+ title: intl.formatMessage(messages.fixed),
73
+ type: 'boolean',
74
+ },
75
+ celled: {
76
+ title: intl.formatMessage(messages.celled),
77
+ type: 'boolean',
78
+ },
79
+ striped: {
80
+ title: intl.formatMessage(messages.striped),
81
+ type: 'boolean',
82
+ },
83
+ compact: {
84
+ title: intl.formatMessage(messages.compact),
85
+ type: 'boolean',
86
+ },
87
+ basic: {
88
+ title: intl.formatMessage(messages.basic),
89
+ type: 'boolean',
90
+ },
91
+ inverted: {
92
+ title: intl.formatMessage(messages.inverted),
93
+ type: 'boolean',
94
+ },
95
+ },
96
+ required: [],
97
+ };
98
+ }
99
+
100
+ export function TableBlockSchema(props) {
101
+ return {
102
+ title: 'Table block',
103
+ fieldsets: [
104
+ {
105
+ id: 'default',
106
+ title: 'Default',
107
+ fields: ['table'],
108
+ },
109
+ ],
110
+ properties: {
111
+ table: {
112
+ title: 'Table block',
113
+ widget: 'object',
114
+ schema: TableSchema(props),
115
+ },
116
+ },
117
+
118
+ required: [],
119
+ };
120
+ }
121
+
122
+ export default TableBlockSchema;
@@ -72,19 +72,28 @@ export const toggleInlineStyle = (editor, style) => {
72
72
  };
73
73
 
74
74
  export const isBlockStyleActive = (editor, style) => {
75
+ const keyName = `style-${style}`;
75
76
  const sn = Array.from(
76
77
  Editor.nodes(editor, {
77
- match: (n) => !Editor.isEditor(n) && typeof n.styleName === 'string',
78
- mode: 'highest',
78
+ match: (n) => {
79
+ const isStyle = typeof n.styleName === 'string' || n[keyName];
80
+ return !Editor.isEditor(n) && isStyle;
81
+ },
82
+ mode: 'all',
79
83
  }),
80
84
  );
81
85
 
82
86
  for (const [n] of sn) {
83
- if (n.styleName.split(' ').filter((x) => x === style).length > 0) {
87
+ if (typeof n.styleName === 'string') {
88
+ if (n.styleName.split(' ').filter((x) => x === style).length > 0) {
89
+ return true;
90
+ }
91
+ } else if (
92
+ n[keyName] &&
93
+ keyName.split('-').filter((x) => x === style).length > 0
94
+ )
84
95
  return true;
85
- }
86
96
  }
87
-
88
97
  return false;
89
98
  };
90
99
 
@@ -125,6 +125,13 @@ export function createEmptyParagraph() {
125
125
  };
126
126
  }
127
127
 
128
+ export function createParagraph(text) {
129
+ return {
130
+ type: config.settings.slate.defaultBlockType,
131
+ children: [{ text }],
132
+ };
133
+ }
134
+
128
135
  export const isSingleBlockTypeActive = (editor, format) => {
129
136
  const [match] = Editor.nodes(editor, {
130
137
  match: (n) => n.type === format,
@@ -4,13 +4,26 @@
4
4
  */
5
5
 
6
6
  import React from 'react';
7
+ import isUndefined from 'lodash/isUndefined';
8
+ import isString from 'lodash/isString';
7
9
  import { FormFieldWrapper } from '@plone/volto/components';
8
10
  import SlateEditor from '@plone/volto-slate/editor/SlateEditor';
9
11
 
10
- import { createEmptyParagraph } from '../utils/blocks';
12
+ import { createEmptyParagraph, createParagraph } from '../utils/blocks';
11
13
 
12
14
  import './style.css';
13
15
 
16
+ const getValue = (value) => {
17
+ if (isUndefined(value) || !isUndefined(value?.data)) {
18
+ return [createEmptyParagraph()];
19
+ }
20
+ // Previously this was a text field
21
+ if (isString(value)) {
22
+ return [createParagraph(value)];
23
+ }
24
+ return value;
25
+ };
26
+
14
27
  const SlateRichTextWidget = (props) => {
15
28
  const {
16
29
  id,
@@ -42,13 +55,7 @@ const SlateRichTextWidget = (props) => {
42
55
  readOnly={readOnly}
43
56
  id={id}
44
57
  name={id}
45
- value={
46
- typeof value === 'undefined' ||
47
- typeof value?.data !==
48
- 'undefined' /* previously this was a Draft block */
49
- ? [createEmptyParagraph()]
50
- : value
51
- }
58
+ value={getValue(value)}
52
59
  onChange={(newValue) => {
53
60
  onChange(id, newValue);
54
61
  }}
@@ -12,7 +12,7 @@ const messages = defineMessages({
12
12
  hideFilters: { id: 'Hide filters', defaultMessage: 'Hide filters' },
13
13
  });
14
14
 
15
- const showFacet = (index) => {
15
+ const defaultShowFacet = (index) => {
16
16
  const { values } = index;
17
17
  return index
18
18
  ? hasNonValueOperation(index.operations || []) ||
@@ -91,7 +91,11 @@ const Facets = (props) => {
91
91
 
92
92
  // TODO :handle changing the type of facet (multi/nonmulti)
93
93
 
94
- const { view: FacetWidget, stateToValue } = resolveExtension(
94
+ const {
95
+ view: FacetWidget,
96
+ stateToValue,
97
+ showFacet = defaultShowFacet,
98
+ } = resolveExtension(
95
99
  'type',
96
100
  search.extensions.facetWidgets.types,
97
101
  facetSettings,
@@ -10,7 +10,7 @@ const TableOfContentsSchema = ({ data }) => {
10
10
  fields: [
11
11
  'title',
12
12
  'hide_title',
13
- ...(variation === 'default' ? ['ordered'] : []),
13
+ ...(variation === 'default' ? ['ordered'] : ['sticky']),
14
14
  'levels',
15
15
  ],
16
16
  },
@@ -39,6 +39,10 @@ const TableOfContentsSchema = ({ data }) => {
39
39
  title: 'Ordered',
40
40
  type: 'boolean',
41
41
  },
42
+ sticky: {
43
+ title: 'Sticky',
44
+ type: 'boolean',
45
+ },
42
46
  },
43
47
  required: [],
44
48
  };