@eeacms/volto-cca-policy 1.0.6 → 1.0.7

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 (121) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/locales/bg/LC_MESSAGES/volto.po +63 -63
  3. package/locales/cs/LC_MESSAGES/volto.po +63 -63
  4. package/locales/da/LC_MESSAGES/volto.po +63 -63
  5. package/locales/de/LC_MESSAGES/volto.po +63 -63
  6. package/locales/el/LC_MESSAGES/volto.po +63 -63
  7. package/locales/en/LC_MESSAGES/volto.po +63 -63
  8. package/locales/es/LC_MESSAGES/volto.po +63 -63
  9. package/locales/et/LC_MESSAGES/volto.po +63 -63
  10. package/locales/fi/LC_MESSAGES/volto.po +63 -63
  11. package/locales/fr/LC_MESSAGES/volto.po +63 -63
  12. package/locales/ga/LC_MESSAGES/volto.po +63 -63
  13. package/locales/hr/LC_MESSAGES/volto.po +63 -63
  14. package/locales/hu/LC_MESSAGES/volto.po +63 -63
  15. package/locales/is/LC_MESSAGES/volto.po +63 -63
  16. package/locales/it/LC_MESSAGES/volto.po +63 -63
  17. package/locales/lt/LC_MESSAGES/volto.po +63 -63
  18. package/locales/lv/LC_MESSAGES/volto.po +63 -63
  19. package/locales/mt/LC_MESSAGES/volto.po +63 -63
  20. package/locales/nl/LC_MESSAGES/volto.po +63 -63
  21. package/locales/nn/LC_MESSAGES/volto.po +63 -63
  22. package/locales/pl/LC_MESSAGES/volto.po +63 -63
  23. package/locales/pt/LC_MESSAGES/volto.po +63 -63
  24. package/locales/ro/LC_MESSAGES/volto.po +63 -63
  25. package/locales/sk/LC_MESSAGES/volto.po +63 -63
  26. package/locales/sl/LC_MESSAGES/volto.po +63 -63
  27. package/locales/sv/LC_MESSAGES/volto.po +63 -63
  28. package/locales/tr/LC_MESSAGES/volto.po +63 -63
  29. package/locales/volto.pot +64 -64
  30. package/package.json +2 -2
  31. package/src/components/Search/NavigatorCatalogue/NavigatorCatalogueMapView.jsx +3 -2
  32. package/src/components/index.js +26 -10
  33. package/src/components/manage/Blocks/CollectionStatistics/CollectionStatsEdit.jsx +1 -1
  34. package/src/components/manage/Blocks/CountryMapHeatIndex/View.jsx +2 -2
  35. package/src/components/manage/Blocks/CountryMapObservatory/CountryMapObservatoryOLView.jsx +2 -2
  36. package/src/components/manage/Blocks/CountryMapProfile/OLView.jsx +2 -2
  37. package/src/components/{theme → manage}/Widgets/GeolocationWidget.jsx +1 -1
  38. package/src/components/{theme → manage}/Widgets/GeolocationWidget.test.jsx +1 -1
  39. package/src/components/{theme → manage}/Widgets/ImageWidget.jsx +2 -2
  40. package/src/components/manage/Widgets/ImageWidget.test.jsx +187 -0
  41. package/src/components/{theme → manage}/Widgets/PromotionalImageWidget.test.jsx +9 -3
  42. package/src/components/theme/Content/Fields/BooleanField.jsx +7 -0
  43. package/src/components/theme/Content/Fields/HTMLField.jsx +14 -0
  44. package/src/components/theme/Content/Fields/TextField.jsx +12 -0
  45. package/src/components/theme/Content/Fields/VocabularyField.jsx +26 -0
  46. package/src/components/theme/Content/Fields/index.js +4 -0
  47. package/src/components/theme/Content/Sections/ContentBannerTitle.jsx +26 -0
  48. package/src/{helpers → components/theme/Content/Sections}/ContentMetadata.jsx +11 -312
  49. package/src/components/theme/Content/Sections/ContentRelatedItems.jsx +35 -0
  50. package/src/components/theme/Content/Sections/DocumentsList.jsx +80 -0
  51. package/src/components/theme/Content/Sections/EventDetails.jsx +90 -0
  52. package/src/components/theme/Content/Sections/GeographicMetadata.jsx +83 -0
  53. package/src/components/theme/Content/Sections/ImageGallery/index.js +1 -0
  54. package/src/components/theme/Content/Sections/ItemLogo.jsx +29 -0
  55. package/src/components/theme/Content/Sections/LinkedMetadataItemList.jsx +63 -0
  56. package/src/components/theme/Content/Sections/LinksList.jsx +48 -0
  57. package/src/components/theme/Content/Sections/MetadataItemList.jsx +43 -0
  58. package/src/components/theme/Content/Sections/PublicationDateInfo.jsx +62 -0
  59. package/src/components/theme/Content/Sections/PublishedModifiedInfo.jsx +33 -0
  60. package/src/components/theme/Content/Sections/ReferenceInfo.jsx +137 -0
  61. package/src/components/theme/Content/Sections/ShareInfoButton.jsx +24 -0
  62. package/src/components/theme/Content/Sections/SubjectTags.jsx +19 -0
  63. package/src/components/theme/Content/Sections/Versioning/index.js +3 -0
  64. package/src/components/theme/Content/Sections/geographicMetadataUtils.js +177 -0
  65. package/src/components/theme/Content/Sections/index.js +17 -0
  66. package/src/components/theme/Content/index.js +20 -0
  67. package/src/components/theme/Views/AdaptationOptionView.jsx +6 -8
  68. package/src/components/theme/Views/AdaptationOptionView.test.jsx +5 -8
  69. package/src/components/theme/Views/C3SIndicatorView.jsx +5 -5
  70. package/src/components/theme/Views/CaseStudyView.jsx +6 -8
  71. package/src/components/theme/Views/CcaEventView.jsx +4 -4
  72. package/src/components/theme/Views/DatabaseItemView.jsx +14 -18
  73. package/src/components/theme/Views/EventView.jsx +3 -3
  74. package/src/components/theme/Views/ExtendedToolView.jsx +6 -8
  75. package/src/components/theme/Views/ExtendedToolView.test.jsx +10 -16
  76. package/src/components/theme/{MissionSignatoryProfile → Views/MissionSignatoryProfile}/MissionSignatoryProfileView.jsx +5 -2
  77. package/src/components/theme/{MissionSignatoryProfile → Views/MissionSignatoryProfile}/MissionSignatoryProfileView.test.jsx +2 -2
  78. package/src/components/theme/{MissionSignatoryProfile → Views/MissionSignatoryProfile}/TabSections/ActionTab.jsx +1 -2
  79. package/src/components/theme/{MissionSignatoryProfile → Views/MissionSignatoryProfile}/TabSections/ActionTab.test.jsx +3 -0
  80. package/src/components/theme/{MissionSignatoryProfile → Views/MissionSignatoryProfile}/TabSections/AssessmentTab.jsx +1 -2
  81. package/src/components/theme/{MissionSignatoryProfile → Views/MissionSignatoryProfile}/TabSections/AssessmentTab.test.jsx +11 -1
  82. package/src/components/theme/{MissionSignatoryProfile → Views/MissionSignatoryProfile}/TabSections/GovernanceTab.jsx +1 -2
  83. package/src/components/theme/{MissionSignatoryProfile → Views/MissionSignatoryProfile}/TabSections/PlanningTab.jsx +1 -2
  84. package/src/components/theme/{MissionSignatoryProfile → Views/MissionSignatoryProfile}/TabSections/PlanningTab.test.jsx +11 -1
  85. package/src/components/theme/Views/NewsItemView.jsx +5 -2
  86. package/src/components/theme/Views/ProjectView.jsx +4 -6
  87. package/src/constants.js +7 -0
  88. package/src/helpers/countryMap.js +1 -4
  89. package/src/helpers/embed.js +11 -0
  90. package/src/helpers/index.js +13 -101
  91. package/src/helpers/string.js +4 -0
  92. package/src/hocs/index.js +1 -0
  93. package/src/{helpers/clientOnly.jsx → hocs/withClientOnly.jsx} +1 -1
  94. package/src/index.js +3 -3
  95. package/src/search/queryUtils.js +64 -0
  96. package/src/components/theme/ShareInfoButton/ShareInfoButton.jsx +0 -26
  97. package/src/components/theme/Widgets/ImageWidget.test.jsx +0 -90
  98. package/src/helpers/Utils.jsx +0 -620
  99. /package/src/components/manage/{TransparentOverlay.jsx → Blocks/CollectionStatistics/TransparentOverlay.jsx} +0 -0
  100. /package/src/components/{theme → manage}/Widgets/GeocharsWidget.jsx +0 -0
  101. /package/src/components/{theme → manage}/Widgets/GeolocationWidgetMapContainer.jsx +0 -0
  102. /package/src/components/{theme → manage}/Widgets/PromotionalImageWidget.jsx +0 -0
  103. /package/src/components/{theme → manage}/Widgets/RASTWidgetView.jsx +0 -0
  104. /package/src/components/{theme → manage}/Widgets/__mocks__/GeolocationWidgetMapContainer.jsx +0 -0
  105. /package/src/components/{theme → manage}/Widgets/utils.js +0 -0
  106. /package/src/components/theme/{ImageGallery → Content/Sections/ImageGallery}/ImageGallery.jsx +0 -0
  107. /package/src/components/theme/{ImageGallery → Content/Sections/ImageGallery}/ImageGallery.test.jsx +0 -0
  108. /package/src/components/theme/{ImageGallery → Content/Sections/ImageGallery}/styles.less +0 -0
  109. /package/src/components/theme/{Views → Content/Sections/Versioning}/ArchivedVersionListing.jsx +0 -0
  110. /package/src/components/theme/{Views → Content/Sections/Versioning}/ArchivedVersionListing.test.jsx +0 -0
  111. /package/src/components/theme/{Views → Content/Sections/Versioning}/ArchivedVersionNotice.jsx +0 -0
  112. /package/src/components/theme/{Views → Content/Sections/Versioning}/ArchivedVersionNotice.test.jsx +0 -0
  113. /package/src/components/theme/{Views → Content/Sections/Versioning}/VersionsGroup.jsx +0 -0
  114. /package/src/components/theme/{Views → Content/Sections/Versioning}/VersionsGroup.test.jsx +0 -0
  115. /package/src/components/theme/{MissionSignatoryProfile → Views/MissionSignatoryProfile}/ItemsSection.jsx +0 -0
  116. /package/src/components/theme/{MissionSignatoryProfile → Views/MissionSignatoryProfile}/ItemsSection.test.jsx +0 -0
  117. /package/src/components/theme/{MissionSignatoryProfile → Views/MissionSignatoryProfile}/NoDataReported.jsx +0 -0
  118. /package/src/components/theme/{MissionSignatoryProfile → Views/MissionSignatoryProfile}/NoDataReported.test.jsx +0 -0
  119. /package/src/components/theme/{MissionSignatoryProfile → Views/MissionSignatoryProfile}/StatisticSection.jsx +0 -0
  120. /package/src/components/theme/{MissionSignatoryProfile → Views/MissionSignatoryProfile}/StatisticSection.test.jsx +0 -0
  121. /package/src/components/theme/{MissionSignatoryProfile → Views/MissionSignatoryProfile}/TabSections/GovernanceTab.test.jsx +0 -0
@@ -2,7 +2,6 @@ import React from 'react';
2
2
  import { compose } from 'redux';
3
3
  import { Grid } from 'semantic-ui-react';
4
4
  import {
5
- clientOnly,
6
5
  getImageUrl,
7
6
  tooltipStyle,
8
7
  adjustEuCountryNames,
@@ -15,6 +14,7 @@ import { Interactions } from './Interactions';
15
14
  import { useCountriesMetadata } from './hooks';
16
15
  import Filter from './Filter';
17
16
  import {
17
+ withClientOnly,
18
18
  withResponsiveContainer,
19
19
  withVisibilitySensor,
20
20
  withGeoJsonData,
@@ -156,7 +156,7 @@ const View = (props) => {
156
156
  };
157
157
 
158
158
  export default compose(
159
- clientOnly,
159
+ withClientOnly,
160
160
  withGeoJsonData(true),
161
161
  withResponsiveContainer('countryMapProfileBlock'),
162
162
  withVisibilitySensor(),
@@ -5,7 +5,7 @@ import config from '@plone/volto/registry';
5
5
 
6
6
  import { injectIntl } from 'react-intl';
7
7
  import FormFieldWrapper from '@plone/volto/components/manage/Widgets/FormFieldWrapper';
8
- import MapContainer from '@eeacms/volto-cca-policy/components/theme/Widgets/GeolocationWidgetMapContainer';
8
+ import MapContainer from '@eeacms/volto-cca-policy/components/manage/Widgets/GeolocationWidgetMapContainer';
9
9
 
10
10
  const defaultValue = {
11
11
  latitude: 55.6761,
@@ -10,7 +10,7 @@ import GeolocationWidget from './GeolocationWidget';
10
10
  global.__SERVER__ = false;
11
11
 
12
12
  jest.mock(
13
- '@eeacms/volto-cca-policy/components/theme/Widgets/GeolocationWidgetMapContainer',
13
+ '@eeacms/volto-cca-policy/components/manage/Widgets/GeolocationWidgetMapContainer',
14
14
  );
15
15
  const mockStore = configureStore();
16
16
 
@@ -187,7 +187,7 @@ const resolveSelectedImageUrl = (url, item, imageSize) => {
187
187
  return null;
188
188
  };
189
189
 
190
- export const ImageToolbar = ({ id, onChange }) => (
190
+ const ImageToolbar = ({ id, onChange }) => (
191
191
  <div className="image-upload-widget-toolbar">
192
192
  <Button
193
193
  icon
@@ -497,7 +497,7 @@ const UnconnectedImageInput = (props) => {
497
497
  );
498
498
  };
499
499
 
500
- export const ImageInput = withObjectBrowser(UnconnectedImageInput);
500
+ const ImageInput = withObjectBrowser(UnconnectedImageInput);
501
501
 
502
502
  const ImageUploadWidget = (props) => {
503
503
  if (props.block && props.widget === 'attachedimage') {
@@ -0,0 +1,187 @@
1
+ import React from 'react';
2
+ import { MemoryRouter } from 'react-router-dom';
3
+ import { act, fireEvent, render, waitFor } from '@testing-library/react';
4
+ import '@testing-library/jest-dom';
5
+ import { Provider } from 'react-intl-redux';
6
+ import configureStore from 'redux-mock-store';
7
+
8
+ import ImageWidget from './ImageWidget';
9
+
10
+ global.__SERVER__ = false;
11
+
12
+ const mockOpenObjectBrowser = jest.fn();
13
+ const mockReadAsDataURL = jest.fn();
14
+ let mockDropzoneProps;
15
+
16
+ jest.mock('promise-file-reader', () => ({
17
+ readAsDataURL: (...args) => mockReadAsDataURL(...args),
18
+ }));
19
+
20
+ jest.mock('@plone/volto/helpers/FormValidation/FormValidation', () => ({
21
+ validateFileUploadSize: jest.fn(() => true),
22
+ }));
23
+
24
+ jest.mock(
25
+ '@plone/volto/components/manage/Sidebar/ObjectBrowser',
26
+ () => (Component) => (props) => (
27
+ <Component {...props} openObjectBrowser={mockOpenObjectBrowser} />
28
+ ),
29
+ );
30
+
31
+ jest.mock(
32
+ '@plone/volto/components/manage/Widgets/ImageWidget',
33
+ () => (props) => (
34
+ <div data-testid="native-image-widget" data-value={props.value} />
35
+ ),
36
+ );
37
+
38
+ // mock Dropzone (avoids drag/drop complexity)
39
+ jest.mock('react-dropzone', () => (props) => {
40
+ mockDropzoneProps = props;
41
+ return props.children({
42
+ getRootProps: () => ({}),
43
+ getInputProps: () => ({}),
44
+ });
45
+ });
46
+
47
+ // mock loadable
48
+ jest.mock('@loadable/component', () => (importFn) => {
49
+ const Component = (props) => {
50
+ const Loaded = require('react-dropzone');
51
+ return <Loaded {...props} />;
52
+ };
53
+ return Component;
54
+ });
55
+
56
+ const mockStore = configureStore();
57
+
58
+ const renderWidget = (props) => {
59
+ const store = mockStore({
60
+ intl: {
61
+ locale: 'en',
62
+ messages: {},
63
+ },
64
+ });
65
+
66
+ return render(
67
+ <Provider store={store}>
68
+ <MemoryRouter initialEntries={['/current-page']}>
69
+ <ImageWidget id="image" title="Image" onChange={jest.fn()} {...props} />
70
+ </MemoryRouter>
71
+ </Provider>,
72
+ );
73
+ };
74
+
75
+ describe('ImageUploadWidget', () => {
76
+ it('renders empty state', () => {
77
+ const { container } = renderWidget({ value: null });
78
+
79
+ expect(container).toBeTruthy();
80
+ });
81
+
82
+ it('renders with image value', () => {
83
+ const { getByText } = renderWidget({
84
+ value: {
85
+ data: 'fakebase64',
86
+ encoding: 'base64',
87
+ 'content-type': 'image/png',
88
+ filename: 'test.png',
89
+ },
90
+ });
91
+
92
+ expect(getByText('test.png')).toBeInTheDocument();
93
+ });
94
+
95
+ it('renders external image URLs and clears them from the toolbar', () => {
96
+ const onChange = jest.fn();
97
+ const { container } = renderWidget({
98
+ value: 'https://example.com/images/photo.jpg',
99
+ onChange,
100
+ });
101
+
102
+ expect(container.querySelector('img')).toHaveAttribute(
103
+ 'src',
104
+ 'https://example.com/images/photo.jpg',
105
+ );
106
+ expect(container).toHaveTextContent('photo.jpg');
107
+
108
+ fireEvent.click(container.querySelector('.delete-btn'));
109
+ expect(onChange).toHaveBeenCalledWith('image', null);
110
+ });
111
+
112
+ it.each([
113
+ ['download metadata', { download: '/photo/@@images/image' }, '/photo'],
114
+ ['linked metadata', { '@id': '/photo' }, '/photo'],
115
+ [
116
+ 'inline image data',
117
+ {
118
+ data: 'fakebase64',
119
+ encoding: 'base64',
120
+ 'content-type': 'image/png',
121
+ },
122
+ '',
123
+ ],
124
+ ])(
125
+ 'sanitizes %s for the native attached-image widget',
126
+ (_, value, expected) => {
127
+ const { getByTestId } = renderWidget({
128
+ block: 'block-id',
129
+ widget: 'attachedimage',
130
+ value,
131
+ });
132
+
133
+ expect(getByTestId('native-image-widget')).toHaveAttribute(
134
+ 'data-value',
135
+ expected,
136
+ );
137
+ },
138
+ );
139
+
140
+ it('uploads a dropped file as an inline image value', async () => {
141
+ const onChange = jest.fn();
142
+ const file = new File(['image'], 'climate.png', { type: 'image/png' });
143
+ mockReadAsDataURL.mockResolvedValueOnce('data:image/png;base64,aW1hZ2U=');
144
+
145
+ renderWidget({ value: null, onChange });
146
+
147
+ await act(async () => {
148
+ mockDropzoneProps.onDrop([file]);
149
+ });
150
+
151
+ await waitFor(() =>
152
+ expect(onChange).toHaveBeenCalledWith('image', {
153
+ data: 'aW1hZ2U=',
154
+ encoding: 'base64',
155
+ 'content-type': 'image/png',
156
+ filename: 'climate.png',
157
+ }),
158
+ );
159
+ });
160
+
161
+ it('updates drag state and delegates an existing image selection', async () => {
162
+ const onSelectItem = jest.fn();
163
+ const { getByLabelText, container } = renderWidget({
164
+ value: null,
165
+ onSelectItem,
166
+ });
167
+
168
+ act(() => mockDropzoneProps.onDragEnter());
169
+ expect(container.querySelector('.active.dimmer')).toBeInTheDocument();
170
+
171
+ act(() => mockDropzoneProps.onDragLeave());
172
+ expect(container.querySelector('.active.dimmer')).not.toBeInTheDocument();
173
+
174
+ fireEvent.click(getByLabelText('Pick an existing image'));
175
+ expect(mockOpenObjectBrowser).toHaveBeenCalledWith(
176
+ expect.objectContaining({
177
+ mode: 'image',
178
+ currentPath: '/current-page',
179
+ }),
180
+ );
181
+
182
+ const { onSelectItem: selectFromBrowser } =
183
+ mockOpenObjectBrowser.mock.calls[0][0];
184
+ await selectFromBrowser('/photo', { title: 'Photo' });
185
+ expect(onSelectItem).toHaveBeenCalledWith('/photo', { title: 'Photo' });
186
+ });
187
+ });
@@ -29,7 +29,9 @@ describe('FileWidget', () => {
29
29
  </Provider>,
30
30
  );
31
31
 
32
- await waitFor(() => {});
32
+ await waitFor(() =>
33
+ expect(container.querySelector('.file-widget-dropzone')).not.toBeNull(),
34
+ );
33
35
  expect(container).toMatchSnapshot();
34
36
  });
35
37
  test('renders a file widget component with value', async () => {
@@ -57,7 +59,9 @@ describe('FileWidget', () => {
57
59
  </Provider>,
58
60
  );
59
61
 
60
- await waitFor(() => {});
62
+ await waitFor(() =>
63
+ expect(container.querySelector('.file-widget-dropzone')).not.toBeNull(),
64
+ );
61
65
  expect(container).toMatchSnapshot();
62
66
  });
63
67
  test('renders a file widget component with value in raw data', async () => {
@@ -85,7 +89,9 @@ describe('FileWidget', () => {
85
89
  </Provider>,
86
90
  );
87
91
 
88
- await waitFor(() => {});
92
+ await waitFor(() =>
93
+ expect(container.querySelector('.file-widget-dropzone')).not.toBeNull(),
94
+ );
89
95
  expect(container).toMatchSnapshot();
90
96
  });
91
97
  });
@@ -0,0 +1,7 @@
1
+ import TextField from './TextField';
2
+
3
+ const BooleanField = ({ label, value, yesLabel, noLabel }) => (
4
+ <TextField label={label} value={value ? yesLabel : noLabel} />
5
+ );
6
+
7
+ export default BooleanField;
@@ -0,0 +1,14 @@
1
+ const HTMLField = ({ value, className }) => {
2
+ if (value === null) {
3
+ return <></>;
4
+ }
5
+
6
+ return (
7
+ <div
8
+ className={className}
9
+ dangerouslySetInnerHTML={{ __html: value?.data }}
10
+ ></div>
11
+ );
12
+ };
13
+
14
+ export default HTMLField;
@@ -0,0 +1,12 @@
1
+ const TextField = ({ label, value }) => {
2
+ if (value === null || value === undefined || value === '') return null;
3
+
4
+ return (
5
+ <>
6
+ <h5>{label}</h5>
7
+ <p>{value}</p>
8
+ </>
9
+ );
10
+ };
11
+
12
+ export default TextField;
@@ -0,0 +1,26 @@
1
+ import TextField from './TextField';
2
+
3
+ const VocabularyField = ({ label, values, asList }) => {
4
+ const items = values
5
+ ?.map((value) => value?.title || value?.token || value)
6
+ .filter(Boolean);
7
+
8
+ if (!items?.length) return null;
9
+
10
+ if (asList) {
11
+ return (
12
+ <>
13
+ <h5>{label}</h5>
14
+ <ul>
15
+ {items.map((item, index) => (
16
+ <li key={`${item}-${index}`}>{item}</li>
17
+ ))}
18
+ </ul>
19
+ </>
20
+ );
21
+ }
22
+
23
+ return <TextField label={label} value={items.join(', ')} />;
24
+ };
25
+
26
+ export default VocabularyField;
@@ -0,0 +1,4 @@
1
+ export { default as BooleanField } from './BooleanField';
2
+ export { default as HTMLField } from './HTMLField';
3
+ export { default as TextField } from './TextField';
4
+ export { default as VocabularyField } from './VocabularyField';
@@ -0,0 +1,26 @@
1
+ import config from '@plone/volto/registry';
2
+
3
+ const ContentBannerTitle = (props) => {
4
+ const { content, data } = props;
5
+ const {
6
+ blocks: { blocksConfig },
7
+ } = config;
8
+ const TitleBlockView = blocksConfig?.title?.view;
9
+
10
+ const blockData = {
11
+ '@type': 'title',
12
+ ...data,
13
+ };
14
+
15
+ return (
16
+ <TitleBlockView
17
+ {...props}
18
+ data={blockData}
19
+ metadata={content}
20
+ properties={content}
21
+ blocksConfig={blocksConfig}
22
+ />
23
+ );
24
+ };
25
+
26
+ export default ContentBannerTitle;