design-comuni-plone-theme 11.1.3 → 11.2.0

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 (38) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/RELEASE.md +20 -0
  3. package/locales/de/LC_MESSAGES/volto.po +5 -0
  4. package/locales/en/LC_MESSAGES/volto.po +5 -0
  5. package/locales/es/LC_MESSAGES/volto.po +5 -0
  6. package/locales/fr/LC_MESSAGES/volto.po +5 -0
  7. package/locales/it/LC_MESSAGES/volto.po +5 -0
  8. package/locales/volto.pot +6 -1
  9. package/package.json +2 -2
  10. package/publiccode.yml +2 -2
  11. package/src/components/Collapse.jsx +16 -11
  12. package/src/components/ItaliaTheme/Blocks/Alert/Edit.jsx +1 -1
  13. package/src/components/ItaliaTheme/Blocks/Alert/View.jsx +3 -1
  14. package/src/components/ItaliaTheme/Blocks/BandiSearch/Body.jsx +49 -43
  15. package/src/components/ItaliaTheme/Blocks/EventSearch/Body.jsx +49 -43
  16. package/src/components/ItaliaTheme/Blocks/TextCard/CardWithImage/Block.jsx +32 -44
  17. package/src/components/ItaliaTheme/Blocks/TextCard/SimpleCard/Block.jsx +28 -34
  18. package/src/components/ItaliaTheme/Blocks/UOSearch/Body.jsx +49 -43
  19. package/src/components/ItaliaTheme/Blocks/VideoGallery/Block/ViewBlock.jsx +33 -37
  20. package/src/components/ItaliaTheme/MegaMenu/MegaMenu.jsx +16 -6
  21. package/src/components/ItaliaTheme/MenuSecondary/MenuSecondary.jsx +3 -2
  22. package/src/components/ItaliaTheme/Search/ResultItem.jsx +30 -15
  23. package/src/components/ItaliaTheme/Search/Search.jsx +49 -19
  24. package/src/config/italiaConfig.js +5 -6
  25. package/src/customizations/volto/components/manage/Blocks/HTML/Edit.jsx +42 -16
  26. package/src/customizations/volto/components/manage/Blocks/Listing/ListingBody.jsx +6 -2
  27. package/src/customizations/volto/components/theme/Navigation/Navigation.jsx +65 -36
  28. package/src/theme/ItaliaTheme/Blocks/_alert.scss +6 -4
  29. package/src/theme/ItaliaTheme/Blocks/_countdown.scss +1 -1
  30. package/src/theme/ItaliaTheme/Blocks/_gridGalleryTemplate.scss +7 -2
  31. package/src/theme/ItaliaTheme/Blocks/_numbers.scss +1 -1
  32. package/src/theme/ItaliaTheme/Components/_card.scss +1 -1
  33. package/src/theme/ItaliaTheme/Components/_search.scss +1 -0
  34. package/src/theme/_site-variables.scss +7 -4
  35. package/src/theme/bootstrap-override/_bootstrap-italia-site.scss +1 -0
  36. package/src/theme/bootstrap-override/bootstrap-italia/_card.scss +1 -1
  37. package/src/theme/bootstrap-override/bootstrap-italia/_focus.scss +3 -0
  38. package/src/theme/extras/_modals.scss +0 -17
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable jsx-a11y/no-static-element-interactions */
2
2
  /* eslint-disable jsx-a11y/click-events-have-key-events */
3
- import React, { useState, useRef, useEffect } from 'react';
4
- import { Card, CardBody, CardTitle, CardText } from 'design-react-kit';
3
+ import React, { useState, useEffect } from 'react';
4
+ import { Card, CardBody, CardTitle } from 'design-react-kit';
5
5
  import { defineMessages, useIntl } from 'react-intl';
6
6
  import PropTypes from 'prop-types';
7
7
  import redraft from 'redraft';
@@ -98,28 +98,26 @@ const Block = ({
98
98
  setSelectedField('content');
99
99
  }}
100
100
  >
101
- <CardText>
102
- <TextEditorWidget
103
- data={data}
104
- fieldName="simple_card_content"
105
- selected={selectedField === 'content'}
106
- block={block}
107
- onChangeBlock={(data) =>
108
- onChange(data, 'simple_card_content')
109
- }
110
- placeholder={intl.formatMessage(
111
- messages.simple_card_content,
112
- )}
113
- showToolbar={true}
114
- onSelectBlock={onSelectBlock}
115
- onAddBlock={onAddBlock}
116
- index={index}
117
- onFocusNextBlock={onFocusNextBlock}
118
- onFocusPreviousBlock={() => {
119
- setSelectedField('title');
120
- }}
121
- />
122
- </CardText>
101
+ <TextEditorWidget
102
+ data={data}
103
+ fieldName="simple_card_content"
104
+ selected={selectedField === 'content'}
105
+ block={block}
106
+ onChangeBlock={(data) =>
107
+ onChange(data, 'simple_card_content')
108
+ }
109
+ placeholder={intl.formatMessage(
110
+ messages.simple_card_content,
111
+ )}
112
+ showToolbar={true}
113
+ onSelectBlock={onSelectBlock}
114
+ onAddBlock={onAddBlock}
115
+ index={index}
116
+ onFocusNextBlock={onFocusNextBlock}
117
+ onFocusPreviousBlock={() => {
118
+ setSelectedField('title');
119
+ }}
120
+ />
123
121
  </div>
124
122
  </>
125
123
  ) : (
@@ -127,16 +125,12 @@ const Block = ({
127
125
  <CardTitle tag="h4" id={block.id + '-title'}>
128
126
  {title}
129
127
  </CardTitle>
130
- <Divider />
131
- <div>
132
- <CardText>
133
- {redraft(
134
- content,
135
- config.settings.richtextViewSettings.ToHTMLRenderers,
136
- config.settings.richtextViewSettings.ToHTMLOptions,
137
- )}
138
- </CardText>
139
- </div>
128
+ <hr />
129
+ {redraft(
130
+ content,
131
+ config.settings.richtextViewSettings.ToHTMLRenderers,
132
+ config.settings.richtextViewSettings.ToHTMLOptions,
133
+ )}
140
134
  </>
141
135
  )}
142
136
  </div>
@@ -143,54 +143,60 @@ const Body = ({ data, inEditMode, path, onChangeBlock }) => {
143
143
  'public-ui': inEditMode,
144
144
  })}
145
145
  >
146
- <div className="d-flex justify-content-center">
147
- <div className="d-flex search-container align-items-center justify-content-center flex-wrap">
148
- {filterOne && (
149
- <>
150
- {React.createElement(filterOne.widget.component, {
151
- ...filterOne.widget?.props,
152
- id: 'filterOne',
153
- onChange: (filter, value) => {
146
+ <form
147
+ onSubmit={(event) => {
148
+ event.preventDefault();
149
+ doRequest(1);
150
+ }}
151
+ >
152
+ <div className="d-flex justify-content-center">
153
+ <div className="d-flex search-container align-items-center justify-content-center flex-wrap">
154
+ {filterOne && (
155
+ <>
156
+ {React.createElement(filterOne.widget.component, {
157
+ ...filterOne.widget?.props,
158
+ id: 'filterOne',
159
+ onChange: (filter, value) => {
160
+ dispatchFilter({
161
+ filter: filter,
162
+ value: value,
163
+ });
164
+ },
165
+ })}
166
+ </>
167
+ )}
168
+ {filterTwo &&
169
+ React.createElement(filterTwo.widget?.component, {
170
+ ...filterTwo.widget?.props,
171
+ id: 'filterTwo',
172
+ onChange: (filter, value) =>
154
173
  dispatchFilter({
155
174
  filter: filter,
156
175
  value: value,
157
- });
158
- },
176
+ }),
159
177
  })}
160
- </>
161
- )}
162
- {filterTwo &&
163
- React.createElement(filterTwo.widget?.component, {
164
- ...filterTwo.widget?.props,
165
- id: 'filterTwo',
166
- onChange: (filter, value) =>
167
- dispatchFilter({
168
- filter: filter,
169
- value: value,
170
- }),
171
- })}
172
- {filterThree &&
173
- React.createElement(filterThree.widget?.component, {
174
- ...filterThree.widget?.props,
175
- id: 'filterThree',
176
- onChange: (filter, value) =>
177
- dispatchFilter({
178
- filter: filter,
179
- value: value,
180
- }),
181
- })}
182
-
183
- <Button
184
- color={data.button_color || 'tertiary'}
185
- icon={false}
186
- tag="button"
187
- onClick={() => doRequest(1)}
188
- className="my-2 my-lg-1"
189
- >
190
- {intl.formatMessage(messages.find)}
191
- </Button>
178
+ {filterThree &&
179
+ React.createElement(filterThree.widget?.component, {
180
+ ...filterThree.widget?.props,
181
+ id: 'filterThree',
182
+ onChange: (filter, value) =>
183
+ dispatchFilter({
184
+ filter: filter,
185
+ value: value,
186
+ }),
187
+ })}
188
+
189
+ <Button
190
+ color={data.button_color || 'tertiary'}
191
+ icon={false}
192
+ tag="button"
193
+ className="my-2 my-lg-1"
194
+ >
195
+ {intl.formatMessage(messages.find)}
196
+ </Button>
197
+ </div>
192
198
  </div>
193
- </div>
199
+ </form>
194
200
  </div>
195
201
 
196
202
  {!loading ? (
@@ -6,13 +6,10 @@
6
6
  import React from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
  import { Embed } from 'semantic-ui-react';
9
- import {
10
- isInternalURL,
11
- getParentUrl,
12
- flattenToAppURL,
13
- } from '@plone/volto/helpers';
9
+ import { isInternalURL, getParentUrl } from '@plone/volto/helpers';
14
10
  import { ConditionalEmbed } from 'volto-gdpr-privacy';
15
11
  import { FontAwesomeIcon } from 'design-comuni-plone-theme/components/ItaliaTheme';
12
+ import { videoUrlHelper } from 'design-comuni-plone-theme/helpers';
16
13
  import { useIntl, defineMessages } from 'react-intl';
17
14
  import config from '@plone/volto/registry';
18
15
 
@@ -34,23 +31,36 @@ const messages = defineMessages({
34
31
  */
35
32
  const ViewBlock = ({ data, index, isEditMode = false }) => {
36
33
  const intl = useIntl();
37
- let placeholder = data.preview_image
38
- ? isInternalURL(data.preview_image)
39
- ? `${flattenToAppURL(data.preview_image)}/@@images/image`
40
- : data.preview_image
41
- : null;
42
34
 
43
- if (!placeholder && data.url) {
44
- if (data.url.match('youtu')) {
45
- //load video preview image from youtube
35
+ let placeholder = null;
36
+ let videoID = null;
37
+ let listID = null;
46
38
 
47
- const videoID = data.url.match(/.be\//)
48
- ? data.url.match(/^.*\.be\/(.*)/)?.[1]
49
- : data.url.match(/^.*\?v=(.*)$/)?.[1];
50
- placeholder = 'https://img.youtube.com/vi/' + videoID + '/hqdefault.jpg';
51
- } else if (data.url.match('vimeo')) {
52
- const videoID = data.url.match(/^.*\.com\/(.*)/)[1];
53
- placeholder = 'https://vumbnail.com/' + videoID + '.jpg';
39
+ if (data.url) {
40
+ const [computedID, computedPlaceholder] = videoUrlHelper(
41
+ data.url,
42
+ data?.preview_image,
43
+ );
44
+ if (computedID) {
45
+ videoID = computedID;
46
+ }
47
+ if (computedPlaceholder) {
48
+ placeholder = computedPlaceholder;
49
+ }
50
+
51
+ if (data.url.match('list')) {
52
+ const matches = data.url.match(/^.*\?list=(.*)|^.*&list=(.*)$/);
53
+ listID = matches[1] || matches[2];
54
+
55
+ let thumbnailID = null;
56
+ if (data.url.match(/\?v=(.*)&list/)) {
57
+ thumbnailID = data.url.match(/^.*\?v=(.*)&list(.*)/)[1];
58
+ }
59
+ if (data.url.match(/\?v=(.*)\?list/)) {
60
+ thumbnailID = data.url.match(/^.*\?v=(.*)\?list(.*)/)[1];
61
+ }
62
+ placeholder =
63
+ 'https://img.youtube.com/vi/' + thumbnailID + '/hqdefault.jpg';
54
64
  }
55
65
  }
56
66
 
@@ -90,31 +100,17 @@ const ViewBlock = ({ data, index, isEditMode = false }) => {
90
100
  <>
91
101
  {data.url.match('list') ? (
92
102
  <Embed
93
- url={`https://www.youtube.com/embed/videoseries?list=${
94
- data.url.match(/^.*\?list=(.*)$/)[1]
95
- }`}
103
+ url={`https://www.youtube.com/embed/videoseries?list=${listID}`}
96
104
  {...embedSettings}
97
105
  />
98
106
  ) : (
99
- <Embed
100
- id={
101
- data.url.match(/.be\//)
102
- ? data.url.match(/^.*\.be\/(.*)/)?.[1]
103
- : data.url.match(/^.*\?v=(.*)$/)?.[1]
104
- }
105
- source="youtube"
106
- {...embedSettings}
107
- />
107
+ <Embed id={videoID} source="youtube" {...embedSettings} />
108
108
  )}
109
109
  </>
110
110
  ) : (
111
111
  <>
112
112
  {data.url.match('vimeo') ? (
113
- <Embed
114
- id={data.url.match(/^.*\.com\/(.*)/)[1]}
115
- source="vimeo"
116
- {...embedSettings}
117
- />
113
+ <Embed id={videoID} source="vimeo" {...embedSettings} />
118
114
  ) : (
119
115
  <>
120
116
  {data.url.match('.mp4') ? (
@@ -112,13 +112,14 @@ const MegaMenu = ({ item, pathname }) => {
112
112
 
113
113
  if (item.mode === 'simpleLink') {
114
114
  return item.linkUrl?.length > 0 ? (
115
- <NavItem tag="li">
115
+ <NavItem tag="li" active={isItemActive} role="none">
116
116
  <NavLink
117
117
  href={item.linkUrl === '' ? '/' : null}
118
118
  item={item.linkUrl[0]?.['@id'] ? item.linkUrl[0] : '#'}
119
119
  tag={UniversalLink}
120
120
  active={isItemActive}
121
121
  data-element={item.id_lighthouse}
122
+ role="menuitem"
122
123
  >
123
124
  <span dangerouslySetInnerHTML={{ __html: item.title }}></span>
124
125
  {isItemActive && (
@@ -238,7 +239,7 @@ const MegaMenu = ({ item, pathname }) => {
238
239
  }
239
240
 
240
241
  return (
241
- <NavItem tag="li" className="megamenu" active={isItemActive}>
242
+ <NavItem tag="li" className="megamenu" active={isItemActive} role="none">
242
243
  <UncontrolledDropdown
243
244
  nav
244
245
  inNavbar
@@ -269,6 +270,9 @@ const MegaMenu = ({ item, pathname }) => {
269
270
  }
270
271
  }}
271
272
  title={intl.formatMessage(messages.closeMenu)}
273
+ // APG spec: on Tab menu closes, so remove it from focusable elements
274
+ // https://www.w3.org/WAI/ARIA/apg/patterns/menubar/examples/menubar-navigation/
275
+ tabIndex="-1"
272
276
  >
273
277
  <Icon icon="it-close" />
274
278
  </Button>
@@ -278,10 +282,14 @@ const MegaMenu = ({ item, pathname }) => {
278
282
  <Row>
279
283
  {childrenGroups.map((group, index) => (
280
284
  <Col lg={12 / max_cols} key={'group_' + index}>
281
- <LinkList className="bordered">
285
+ <LinkList
286
+ className="bordered"
287
+ role="menu"
288
+ aria-label={item.title ?? ''}
289
+ >
282
290
  {group.map((child, idx) => {
283
291
  return (
284
- <li key={child['@id'] + idx}>
292
+ <li key={child['@id'] + idx} role="none">
285
293
  {child.showAsHeader ? (
286
294
  <h3
287
295
  className={cx('list-item', {
@@ -298,6 +306,7 @@ const MegaMenu = ({ item, pathname }) => {
298
306
  key={child['@id']}
299
307
  onClick={() => setMenuStatus(false)}
300
308
  role="menuitem"
309
+ aria-current="page"
301
310
  >
302
311
  <span>{child.title}</span>
303
312
  </ConditionalLink>
@@ -362,12 +371,13 @@ const MegaMenu = ({ item, pathname }) => {
362
371
  <Row>
363
372
  <Col lg={8} />
364
373
  <Col lg={4}>
365
- <LinkList>
366
- <li className="it-more text-end">
374
+ <LinkList role="menu" aria-label={item.showMoreText ?? ''}>
375
+ <li className="it-more text-end" role="none">
367
376
  <UniversalLink
368
377
  className="list-item medium"
369
378
  item={item.showMoreLink[0]}
370
379
  onClick={() => setMenuStatus(false)}
380
+ role="menuitem"
371
381
  >
372
382
  <span>
373
383
  {item.showMoreText?.length > 0
@@ -44,17 +44,18 @@ const MenuSecondary = ({ pathname }) => {
44
44
 
45
45
  return (
46
46
  items?.length > 0 && (
47
- <Nav navbar className="navbar-secondary" role="navigation">
47
+ <Nav navbar className="navbar-secondary" role="menubar">
48
48
  {items.map((item, i) => {
49
49
  let url = item.href || item.linkUrl?.[0]?.['@id'] || '';
50
50
 
51
51
  return (
52
- <NavItem tag="li" key={i}>
52
+ <NavItem tag="li" active={isMenuActive(url)} key={i} role="none">
53
53
  <NavLink
54
54
  href={url === '' ? '/' : flattenToAppURL(url)}
55
55
  tag={UniversalLink}
56
56
  active={isMenuActive(url)}
57
57
  data-element={item.id_lighthouse}
58
+ role="menuitem"
58
59
  >
59
60
  <span
60
61
  className={item.inEvidence ? 'fw-bold' : ''}
@@ -1,32 +1,47 @@
1
1
  import React from 'react';
2
2
  import { Card, CardBody } from 'design-react-kit';
3
3
  import { UniversalLink } from '@plone/volto/components';
4
- import Highlighter from 'react-highlight-words';
5
4
 
6
- const ResultItem = ({ item, index, section, searchableText }) => {
7
- const filteredWords = searchableText.split(' ');
5
+ const Marker = ({ text = '', highlight = '' }) => {
6
+ if (!highlight.trim()) {
7
+ return text;
8
+ }
9
+ const regex = new RegExp(
10
+ `(${highlight
11
+ .split(' ')
12
+ // remove any characters not in these ranges
13
+ .map((s) => s.replace(/[^a-zA-Z0-9À-ÖØ-öø-ÿ]/g, ''))
14
+ .filter((s) => s !== '')
15
+ .join('|')})`,
16
+ 'gi',
17
+ );
18
+ // Split on highlight term and include term into parts, ignore case
19
+ const parts = text.split(regex);
20
+ return (
21
+ <>
22
+ {parts.map((part, i) =>
23
+ part.match(regex) ? (
24
+ <mark className="highlighted-text">{part}</mark>
25
+ ) : (
26
+ part
27
+ ),
28
+ )}
29
+ </>
30
+ );
31
+ };
8
32
 
33
+ const ResultItem = ({ item, index, section, searchableText }) => {
9
34
  return (
10
35
  <Card noWrapper={true} className="mt-3 mb-3">
11
36
  <CardBody className="shadow-sm px-4 pt-4 pb-4 rounded">
12
37
  {section}
13
38
  <h4 className="card-title">
14
39
  <UniversalLink item={item}>
15
- <Highlighter
16
- highlightClassName="highlighted-text"
17
- searchWords={filteredWords}
18
- autoEscape={true}
19
- textToHighlight={item.title}
20
- />
40
+ <Marker highlight={searchableText} text={item.title} />
21
41
  </UniversalLink>
22
42
  </h4>
23
43
  <p className="text-paragraph">
24
- <Highlighter
25
- highlightClassName="highlighted-text"
26
- searchWords={filteredWords}
27
- autoEscape={true}
28
- textToHighlight={item.description}
29
- />
44
+ <Marker highlight={searchableText} text={item.description} />
30
45
  </p>
31
46
  </CardBody>
32
47
  </Card>
@@ -151,6 +151,10 @@ const messages = defineMessages({
151
151
  id: 'search_skip_to_search_results',
152
152
  defaultMessage: 'Vai ai risultati di ricerca',
153
153
  },
154
+ active_filters: {
155
+ id: 'active_filters',
156
+ defaultMessage: '{filterNumber} filtri attivati',
157
+ },
154
158
  });
155
159
 
156
160
  const searchOrderDict = {
@@ -444,11 +448,21 @@ const Search = () => {
444
448
  <div className="pt-4 pt-lg-0">
445
449
  <h6 className="text-uppercase">
446
450
  {intl.formatMessage(messages.sections)}
447
- {activeSections > 0 && (
448
- <span className="badge bg-secondary ms-3">
449
- {activeSections}
450
- </span>
451
- )}
451
+
452
+ <span
453
+ className={cx('badge bg-secondary ms-3', {
454
+ 'visually-hidden': activeSections === 0,
455
+ })}
456
+ aria-live="polite"
457
+ aria-label={intl.formatMessage(
458
+ messages.active_filters,
459
+ {
460
+ filterNumber: activeSections,
461
+ },
462
+ )}
463
+ >
464
+ {activeSections}
465
+ </span>
452
466
  </h6>
453
467
  <div className="mt-4">
454
468
  <SearchSections
@@ -470,11 +484,21 @@ const Search = () => {
470
484
  >
471
485
  <h6 className="text-uppercase">
472
486
  {intl.formatMessage(messages.topics)}
473
- {activeTopics > 0 && (
474
- <span className="badge bg-secondary ms-3">
475
- {activeTopics}
476
- </span>
477
- )}
487
+
488
+ <span
489
+ className={cx('badge bg-secondary ms-3', {
490
+ 'visually-hidden': activeTopics === 0,
491
+ })}
492
+ aria-live="polite"
493
+ aria-label={intl.formatMessage(
494
+ messages.active_filters,
495
+ {
496
+ filterNumber: activeTopics,
497
+ },
498
+ )}
499
+ >
500
+ {activeTopics}
501
+ </span>
478
502
  </h6>
479
503
  <div className="form-check mt-4">
480
504
  <SearchTopics
@@ -507,14 +531,21 @@ const Search = () => {
507
531
  <div className="p-3 shadow-sm bg-white">
508
532
  <h6 className="text-uppercase">
509
533
  {intl.formatMessage(messages.content_types)}
510
- {activePortalTypes > 0 && (
511
- <span
512
- className="badge bg-secondary ms-3"
513
- aria-live="polite"
514
- >
515
- {activePortalTypes}
516
- </span>
517
- )}
534
+
535
+ <span
536
+ className={cx('badge bg-secondary ms-3', {
537
+ 'visually-hidden': activePortalTypes === 0,
538
+ })}
539
+ aria-live="polite"
540
+ aria-label={intl.formatMessage(
541
+ messages.active_filters,
542
+ {
543
+ filterNumber: activePortalTypes,
544
+ },
545
+ )}
546
+ >
547
+ {activePortalTypes}
548
+ </span>
518
549
  </h6>
519
550
  <div className="form-checck mt-4">
520
551
  <SearchCTs
@@ -613,7 +644,6 @@ const Search = () => {
613
644
  <Col lg={9} tag="section" className="py-lg-5">
614
645
  <div
615
646
  className="search-results-wrapper"
616
- role="region"
617
647
  id="search-results-region"
618
648
  aria-live="polite"
619
649
  >
@@ -65,8 +65,8 @@ import { schemaListing } from 'design-comuni-plone-theme/components/ItaliaTheme/
65
65
 
66
66
  import reducers from 'design-comuni-plone-theme/reducers';
67
67
 
68
- const ReleaseLog = loadable(
69
- () => import('design-comuni-plone-theme/components/ReleaseLog/ReleaseLog'),
68
+ const ReleaseLog = loadable(() =>
69
+ import('design-comuni-plone-theme/components/ReleaseLog/ReleaseLog'),
70
70
  );
71
71
 
72
72
  const messages = defineMessages({
@@ -486,10 +486,9 @@ export default function applyConfig(voltoConfig) {
486
486
  },
487
487
  };
488
488
  // Remove Horizontal Menu variation of TOC Block
489
- config.blocks.blocksConfig.toc.variations =
490
- config.blocks.blocksConfig.toc.variations.filter(
491
- (v) => v.id !== 'horizontalMenu',
492
- );
489
+ config.blocks.blocksConfig.toc.variations = config.blocks.blocksConfig.toc.variations.filter(
490
+ (v) => v.id !== 'horizontalMenu',
491
+ );
493
492
 
494
493
  // COMPONENTS
495
494
  config.components = {