@kitconcept/core 1.0.0-alpha.3 → 1.0.0-alpha.31

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.
package/locales/volto.pot CHANGED
@@ -1,7 +1,7 @@
1
1
  msgid ""
2
2
  msgstr ""
3
3
  "Project-Id-Version: Plone\n"
4
- "POT-Creation-Date: 2025-04-28T17:31:27.834Z\n"
4
+ "POT-Creation-Date: 2025-05-23T14:22:05.270Z\n"
5
5
  "Last-Translator: Plone i18n <plone-i18n@lists.sourceforge.net>\n"
6
6
  "Language-Team: Plone i18n <plone-i18n@lists.sourceforge.net>\n"
7
7
  "Content-Type: text/plain; charset=utf-8\n"
@@ -18,6 +18,36 @@ msgstr ""
18
18
  msgid "Add-ons"
19
19
  msgstr ""
20
20
 
21
+ #. Default: "Back"
22
+ #: components/Controlpanels/UpgradeControlPanel
23
+ msgid "Back"
24
+ msgstr ""
25
+
26
+ #. Default: "Configuration Versions"
27
+ #: components/Controlpanels/UpgradeControlPanel
28
+ msgid "Configuration Versions"
29
+ msgstr ""
30
+
31
+ #. Default: "Current active configuration"
32
+ #: components/Controlpanels/UpgradeControlPanel
33
+ msgid "Current active configuration"
34
+ msgstr ""
35
+
36
+ #. Default: "Dry run selected, transaction aborted."
37
+ #: components/Controlpanels/UpgradeControlPanel
38
+ msgid "Dry run selected, transaction aborted."
39
+ msgstr ""
40
+
41
+ #. Default: "Latest available configuration"
42
+ #: components/Controlpanels/UpgradeControlPanel
43
+ msgid "Latest available configuration"
44
+ msgstr ""
45
+
46
+ #. Default: "More information about the upgrade procedure can be found in the documentation section of plone.org in the Upgrade Guide."
47
+ #: components/Controlpanels/UpgradeControlPanel
48
+ msgid "More information about the upgrade procedure can be found in the documentation section of plone.org in the Upgrade Guide."
49
+ msgstr ""
50
+
21
51
  #. Default: "No addons found"
22
52
  #: components/Controlpanels/VersionOverview
23
53
  msgid "No addons found"
@@ -28,6 +58,11 @@ msgstr ""
28
58
  msgid "Package Version"
29
59
  msgstr ""
30
60
 
61
+ #. Default: "Please ensure you have a backup of your site before performing the upgrade."
62
+ #: components/Controlpanels/UpgradeControlPanel
63
+ msgid "Please ensure you have a backup of your site before performing the upgrade."
64
+ msgstr ""
65
+
31
66
  #. Default: "Profile Version (File system)"
32
67
  #: components/Controlpanels/VersionOverview
33
68
  msgid "Profile Version (File system)"
@@ -38,7 +73,62 @@ msgstr ""
38
73
  msgid "Profile Version (Installed)"
39
74
  msgstr ""
40
75
 
76
+ #. Default: "The following list shows which upgrade steps are going to be run. Upgrading sometimes performs a catalog/security update, which may take a long time on large sites. Be patient."
77
+ #: components/Controlpanels/UpgradeControlPanel
78
+ msgid "The following list shows which upgrade steps are going to be run. Upgrading sometimes performs a catalog/security update, which may take a long time on large sites. Be patient."
79
+ msgstr ""
80
+
81
+ #. Default: "The site configuration is outdated and needs to be upgraded."
82
+ #: components/Controlpanels/UpgradeControlPanel
83
+ msgid "The site configuration is outdated and needs to be upgraded."
84
+ msgstr ""
85
+
86
+ #. Default: "There was an error with the upgrade."
87
+ #: components/Controlpanels/UpgradeControlPanel
88
+ msgid "There was an error with the upgrade."
89
+ msgstr ""
90
+
91
+ #. Default: "Upgrade"
92
+ #: components/Controlpanels/UpgradeControlPanel
93
+ msgid "Upgrade"
94
+ msgstr ""
95
+
96
+ #. Default: "Upgrade Plone Site"
97
+ #: components/Controlpanels/UpgradeControlPanel
98
+ msgid "Upgrade Plone Site"
99
+ msgstr ""
100
+
101
+ #. Default: "Upgrade Report"
102
+ #: components/Controlpanels/UpgradeControlPanel
103
+ msgid "Upgrade Report"
104
+ msgstr ""
105
+
106
+ #. Default: "Upgrade Steps"
107
+ #: components/Controlpanels/UpgradeControlPanel
108
+ msgid "Upgrade Steps"
109
+ msgstr ""
110
+
111
+ #. Default: "Upgrade kitconcept.core"
112
+ #: components/Controlpanels/UpgradeControlPanel
113
+ msgid "Upgrade kitconcept.core"
114
+ msgstr ""
115
+
116
+ #. Default: "Upgrade your Site"
117
+ #: components/Controlpanels/UpgradeControlPanel
118
+ msgid "Upgrade your Site"
119
+ msgstr ""
120
+
121
+ #. Default: "Version Overview"
122
+ #: components/Controlpanels/UpgradeControlPanel
123
+ msgid "Version Overview"
124
+ msgstr ""
125
+
41
126
  #. Default: "You are running in 'debug mode'. This mode is intended for sites that are under development. This allows many configuration changes to be immediately visible, but will make your site run more slowly. To turn off debug mode, stop the server, set 'debug-mode=off' in your buildout.cfg, re-run bin/buildout and then restart the server process."
42
127
  #: components/Controlpanels/VersionOverview
43
128
  msgid "Warning Regarding debug mode"
44
129
  msgstr ""
130
+
131
+ #. Default: "Your site is up to date."
132
+ #: components/Controlpanels/UpgradeControlPanel
133
+ msgid "Your site is up to date."
134
+ msgstr ""
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kitconcept/core",
3
- "version": "1.0.0-alpha.3",
3
+ "version": "1.0.0-alpha.31",
4
4
  "description": "Core setup for kitconcept GmbH distributions built on top of Plone",
5
5
  "main": "src/index.ts",
6
6
  "license": "MIT",
@@ -23,9 +23,11 @@
23
23
  "@eeacms/volto-accordion-block",
24
24
  "@kitconcept/volto-banner-block",
25
25
  "@kitconcept/volto-button-block",
26
+ "@kitconcept/volto-carousel-block",
26
27
  "@kitconcept/volto-heading-block",
27
28
  "@kitconcept/volto-highlight-block",
28
29
  "@kitconcept/volto-introduction-block",
30
+ "@kitconcept/volto-logos-block",
29
31
  "@kitconcept/volto-separator-block",
30
32
  "@kitconcept/volto-slider-block",
31
33
  "@mbarde/volto-image-crop-widget",
@@ -34,22 +36,28 @@
34
36
  "@kitconcept/volto-light-theme"
35
37
  ],
36
38
  "dependencies": {
37
- "@plone-collective/volto-authomatic": "3.0.0-alpha.1",
38
- "@kitconcept/volto-light-theme": "6.0.1",
39
+ "@plone-collective/volto-authomatic": "3.0.0-alpha.4",
40
+ "@kitconcept/volto-light-theme": "7.0.0-beta.7",
39
41
  "@mbarde/volto-image-crop-widget": "^0.5.1",
40
- "@kitconcept/volto-banner-block": "^1.0.1",
41
42
  "@plone/components": "3.0.2"
42
43
  },
43
44
  "peerDependencies": {
45
+ "@plonegovbr/volto-social-media": "^2.0.0-alpha.8",
44
46
  "react": "18.2.0",
45
- "react-dom": "18.2.0"
47
+ "react-dom": "18.2.0",
48
+ "react-intl": "^3.12.1",
49
+ "react-redux": "^8.1.2"
46
50
  },
47
51
  "devDependencies": {
48
52
  "@plone/scripts": "^3.6.1",
53
+ "@types/lodash": "^4.14.201",
49
54
  "@types/react": "^18.3.12",
50
55
  "@types/react-dom": "^18.3.1",
51
- "release-it": "^18.1.2",
52
- "@plone/types": "1.4.4"
56
+ "lodash": "4.17.21",
57
+ "release-it": "^19.0.4",
58
+ "typescript": "^5.7.3",
59
+ "vitest": "^3.1.2",
60
+ "@plone/types": "1.4.5"
53
61
  },
54
62
  "scripts": {
55
63
  "i18n": "rm -rf build/messages && NODE_ENV=production i18n --addon",
@@ -0,0 +1,355 @@
1
+ /**
2
+ * Upgrade controlpanel container.
3
+ * @module components/manage/Controlpanels/Upgrade
4
+ */
5
+ import React, { Component } from 'react';
6
+ import PropTypes from 'prop-types';
7
+ import { connect } from 'react-redux';
8
+ import { compose } from 'redux';
9
+ import { Link } from 'react-router-dom';
10
+ import { createPortal } from 'react-dom';
11
+ import {
12
+ Button,
13
+ Container,
14
+ Form,
15
+ Message,
16
+ Segment,
17
+ Table,
18
+ } from 'semantic-ui-react';
19
+ import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
20
+ import map from 'lodash/map';
21
+
22
+ import { getSystemInformation } from '@plone/volto/actions/controlpanels/controlpanels';
23
+ import {
24
+ getUpgradeInformation,
25
+ runUpgrade,
26
+ } from '@plone/volto/actions/upgrade/upgrade';
27
+ import Helmet from '@plone/volto/helpers/Helmet/Helmet';
28
+ import Icon from '@plone/volto/components/theme/Icon/Icon';
29
+ import Toast from '@plone/volto/components/manage/Toast/Toast';
30
+ import Toolbar from '@plone/volto/components/manage/Toolbar/Toolbar';
31
+ import VersionOverview from '@kitconcept/core/components/Controlpanels/VersionOverview';
32
+ import backSVG from '@plone/volto/icons/back.svg';
33
+ import { toast } from 'react-toastify';
34
+
35
+ const messages = defineMessages({
36
+ back: {
37
+ id: 'Back',
38
+ defaultMessage: 'Back',
39
+ },
40
+ upgradeInformation: {
41
+ id: 'Upgrade kitconcept.core',
42
+ defaultMessage: 'Upgrade kitconcept.core',
43
+ },
44
+ upgrade: {
45
+ id: 'Upgrade your Site',
46
+ defaultMessage: 'Upgrade your Site',
47
+ },
48
+ upgradeSuccess: {
49
+ id: 'Your site is up to date.',
50
+ defaultMessage: 'Your site is up to date.',
51
+ },
52
+ dryRunSuccess: {
53
+ id: 'Dry run selected, transaction aborted.',
54
+ defaultMessage: 'Dry run selected, transaction aborted.',
55
+ },
56
+ upgradeError: {
57
+ id: 'There was an error with the upgrade.',
58
+ defaultMessage: 'There was an error with the upgrade.',
59
+ },
60
+ });
61
+
62
+ const UpgradeStep = ({ title, steps }) => {
63
+ const stepTitles = steps.map((step) => {
64
+ return step.title;
65
+ });
66
+ return (
67
+ <Message>
68
+ <Message.Header>{title}</Message.Header>
69
+ <Message.List items={stepTitles} />
70
+ </Message>
71
+ );
72
+ };
73
+
74
+ /**
75
+ * UpgradeControlPanel class.
76
+ * @class UpgradeControlPanel
77
+ * @extends Component
78
+ */
79
+ class UpgradeControlPanel extends Component {
80
+ /**
81
+ * Property types.
82
+ * @property {Object} propTypes Property types.
83
+ * @static
84
+ */
85
+ static propTypes = {
86
+ getUpgradeInformation: PropTypes.func.isRequired,
87
+ getSystemInformation: PropTypes.func.isRequired,
88
+ runUpgrade: PropTypes.func.isRequired,
89
+ };
90
+
91
+ /**
92
+ * Constructor
93
+ * @method constructor
94
+ * @param {Object} props Component properties
95
+ * @constructs DiffComponent
96
+ */
97
+ constructor(props) {
98
+ super(props);
99
+ this.runUpgrade = this.runUpgrade.bind(this);
100
+ this.state = {
101
+ isClient: false,
102
+ dryRun: false,
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Component did mount
108
+ * @method componentDidMount
109
+ * @returns {undefined}
110
+ */
111
+ componentDidMount() {
112
+ this.props.getUpgradeInformation();
113
+ this.props.getSystemInformation();
114
+ this.setState({ isClient: true, dryRun: false });
115
+ }
116
+
117
+ /**
118
+ * Run Upgrade handler
119
+ * @method runUpgrade
120
+ * @param {boolean} dryRun Id of package to install.
121
+ * @returns {undefined}
122
+ */
123
+ runUpgrade(dryRun) {
124
+ this.props
125
+ .runUpgrade(dryRun)
126
+ .then(() => {
127
+ if (!dryRun) {
128
+ // Upgrade completed
129
+ this.props.getUpgradeInformation();
130
+ toast.success(
131
+ <Toast
132
+ success
133
+ title={this.props.intl.formatMessage(messages.upgrade)}
134
+ content={this.props.intl.formatMessage(messages.upgradeSuccess)}
135
+ />,
136
+ );
137
+ } else {
138
+ toast.success(
139
+ <Toast
140
+ success
141
+ title={this.props.intl.formatMessage(messages.upgrade)}
142
+ content={this.props.intl.formatMessage(messages.dryRunSuccess)}
143
+ />,
144
+ );
145
+ }
146
+ })
147
+ .catch(() => {
148
+ toast.error(
149
+ <Toast
150
+ error
151
+ title={this.props.intl.formatMessage(messages.upgrade)}
152
+ content={this.props.intl.formatMessage(messages.upgradeError)}
153
+ />,
154
+ );
155
+ });
156
+ }
157
+
158
+ /**
159
+ * Render method.
160
+ * @method render
161
+ * @returns {string} Markup for the component.
162
+ */
163
+ render() {
164
+ const upgradeInformation = this.props.upgradeInformation;
165
+ const upgradeSteps = upgradeInformation
166
+ ? Object.entries(upgradeInformation.upgrade_steps)
167
+ : null;
168
+ return upgradeInformation ? (
169
+ <Container id="upgrade-page" className="controlpanel-upgrade">
170
+ <Helmet
171
+ title={this.props.intl.formatMessage(messages.upgradeInformation)}
172
+ />
173
+ <Segment.Group raised>
174
+ <Segment className="primary">
175
+ <FormattedMessage
176
+ id="Upgrade Plone Site"
177
+ defaultMessage="Upgrade Plone Site"
178
+ />
179
+ </Segment>
180
+ <Segment className="secondary">
181
+ <FormattedMessage
182
+ id="More information about the upgrade procedure can be found in the documentation section of plone.org in the Upgrade Guide."
183
+ defaultMessage="More information about the upgrade procedure can be found in the documentation section of plone.org in the Upgrade Guide."
184
+ />
185
+ </Segment>
186
+ <Message attached>
187
+ <Message.Header>
188
+ <FormattedMessage
189
+ id="Configuration Versions"
190
+ defaultMessage="Configuration Versions"
191
+ />
192
+ </Message.Header>
193
+ </Message>
194
+ <Segment>
195
+ {upgradeInformation.versions.fs ===
196
+ upgradeInformation.versions.instance ? (
197
+ <Message attached success>
198
+ <FormattedMessage
199
+ id="Your site is up to date."
200
+ defaultMessage="Your site is up to date."
201
+ />
202
+ </Message>
203
+ ) : (
204
+ <Message attached warning>
205
+ <FormattedMessage
206
+ id="The site configuration is outdated and needs to be upgraded."
207
+ defaultMessage="The site configuration is outdated and needs to be upgraded."
208
+ />
209
+ </Message>
210
+ )}
211
+ <Table celled padded columns="2">
212
+ <Table.Body>
213
+ <Table.Row>
214
+ <Table.Cell>
215
+ <FormattedMessage
216
+ id="Current active configuration"
217
+ defaultMessage="Current active configuration"
218
+ />
219
+ </Table.Cell>
220
+ <Table.Cell>
221
+ {upgradeInformation.versions.instance}
222
+ </Table.Cell>
223
+ </Table.Row>
224
+ <Table.Row>
225
+ <Table.Cell>
226
+ <FormattedMessage
227
+ id="Latest available configuration"
228
+ defaultMessage="Latest available configuration"
229
+ />
230
+ </Table.Cell>
231
+ <Table.Cell>{upgradeInformation.versions.fs}</Table.Cell>
232
+ </Table.Row>
233
+ </Table.Body>
234
+ </Table>
235
+ </Segment>
236
+ {upgradeSteps.length > 0 && (
237
+ <>
238
+ <Message attached>
239
+ <Message.Header>
240
+ <FormattedMessage
241
+ id="Upgrade Steps"
242
+ defaultMessage="Upgrade Steps"
243
+ />
244
+ </Message.Header>
245
+ <Message.Content>
246
+ <FormattedMessage
247
+ id="The following list shows which upgrade steps are going to be run. Upgrading sometimes performs a catalog/security update, which may take a long time on large sites. Be patient."
248
+ defaultMessage="The following list shows which upgrade steps are going to be run. Upgrading sometimes performs a catalog/security update, which may take a long time on large sites. Be patient."
249
+ />
250
+ </Message.Content>
251
+ </Message>
252
+ <Segment>
253
+ <Container>
254
+ {map(upgradeSteps, (upgradeGroup) => [
255
+ <UpgradeStep
256
+ key={upgradeGroup[0]}
257
+ title={upgradeGroup[0]}
258
+ steps={upgradeGroup[1]}
259
+ />,
260
+ ])}
261
+ </Container>
262
+ <Message negative>
263
+ <FormattedMessage
264
+ id="Please ensure you have a backup of your site before performing the upgrade."
265
+ defaultMessage="Please ensure you have a backup of your site before performing the upgrade."
266
+ />
267
+ </Message>
268
+ <Form.Checkbox
269
+ onChange={(e, data) =>
270
+ this.setState({ dryRun: data.checked })
271
+ }
272
+ checked={this.state.dryRun}
273
+ label="Dry run mode"
274
+ />
275
+ <br />
276
+ <Button
277
+ aria-label={this.props.intl.formatMessage({
278
+ id: 'Upgrade',
279
+ defaultMessage: 'Upgrade',
280
+ })}
281
+ onClick={() => this.runUpgrade(this.state.dryRun)}
282
+ primary
283
+ >
284
+ <FormattedMessage id="Upgrade" defaultMessage="Upgrade" />
285
+ </Button>
286
+ </Segment>
287
+ </>
288
+ )}
289
+ {this.props.upgradeReport ? (
290
+ <>
291
+ <Message attached>
292
+ <Message.Header>
293
+ <FormattedMessage
294
+ id="Upgrade Report"
295
+ defaultMessage="Upgrade Report"
296
+ />
297
+ </Message.Header>
298
+ </Message>
299
+ <Segment>
300
+ <pre>{this.props.upgradeReport.report}</pre>
301
+ </Segment>
302
+ </>
303
+ ) : null}
304
+ <Message attached>
305
+ <Message.Header>
306
+ <FormattedMessage
307
+ id="Version Overview"
308
+ defaultMessage="Version Overview"
309
+ />
310
+ </Message.Header>
311
+ </Message>
312
+ <Segment>
313
+ {this.props.systemInformation ? (
314
+ <VersionOverview {...this.props.systemInformation} />
315
+ ) : null}
316
+ </Segment>
317
+ </Segment.Group>
318
+ {this.state.isClient &&
319
+ createPortal(
320
+ <Toolbar
321
+ pathname={this.props.pathname}
322
+ hideDefaultViewButtons
323
+ inner={
324
+ <>
325
+ <Link to="/controlpanel" className="item">
326
+ <Icon
327
+ name={backSVG}
328
+ aria-label={this.props.intl.formatMessage(messages.back)}
329
+ className="contents circled"
330
+ size="30px"
331
+ title={this.props.intl.formatMessage(messages.back)}
332
+ />
333
+ </Link>
334
+ </>
335
+ }
336
+ />,
337
+ document.getElementById('toolbar'),
338
+ )}
339
+ </Container>
340
+ ) : null;
341
+ }
342
+ }
343
+
344
+ export default compose(
345
+ injectIntl,
346
+ connect(
347
+ (state, props) => ({
348
+ upgradeInformation: state.upgrade.upgradeinformation,
349
+ pathname: props.location.pathname,
350
+ systemInformation: state.controlpanels.systeminformation,
351
+ upgradeReport: state.upgrade.upgradereport,
352
+ }),
353
+ { getUpgradeInformation, getSystemInformation, runUpgrade },
354
+ ),
355
+ )(UpgradeControlPanel);
@@ -0,0 +1,19 @@
1
+ import type { ConfigType } from '@plone/registry';
2
+ import TTWCustomCSS from '../slots/TTWCustomCSS/TTWCustomCSS';
3
+ import ConfigInjector from '../slots/ConfigInjector/ConfigInjector';
4
+
5
+ export default function install(config: ConfigType) {
6
+ config.registerSlotComponent({
7
+ slot: 'aboveHeader',
8
+ name: 'ConfigInjector',
9
+ component: ConfigInjector,
10
+ });
11
+
12
+ config.registerSlotComponent({
13
+ slot: 'aboveHeader',
14
+ name: 'TTWCustomCSS',
15
+ component: TTWCustomCSS,
16
+ });
17
+
18
+ return config;
19
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * OVERRIDE UpgradeControlPanel.jsx
3
+ * REASON: Add distribution info support
4
+ */
5
+ import UpgradeControlPanel from '../../../../../components/Controlpanels/UpgradeControlPanel';
6
+
7
+ export default UpgradeControlPanel;
@@ -0,0 +1,98 @@
1
+ import { BlocksConfigMerger } from './BlocksConfigMerger';
2
+ import { describe, it, expect } from 'vitest';
3
+
4
+ const baseBlocksConfig = {
5
+ teaser: {
6
+ restricted: false,
7
+ variations: [
8
+ { id: 'variation1', label: 'Variation 1' },
9
+ { id: 'variation2', label: 'Variation 2' },
10
+ { id: 'variation3', label: 'Variation 3' },
11
+ ],
12
+ themes: [],
13
+ },
14
+ gridBlock: {
15
+ restricted: false,
16
+ variations: [
17
+ { id: 'variationA', label: 'Variation A' },
18
+ { id: 'variationB', label: 'Variation B' },
19
+ ],
20
+ themes: [],
21
+ },
22
+ };
23
+
24
+ const mutator = {
25
+ teaser: {
26
+ disable: true,
27
+ variations: ['variation1', 'variation2'],
28
+ themes: [
29
+ {
30
+ style: {
31
+ '--theme-color': '#fff',
32
+ '--theme-high-contrast-color': '#ecebeb',
33
+ '--theme-foreground-color': '#000',
34
+ '--theme-low-contrast-foreground-color': '#555555',
35
+ },
36
+ name: 'default',
37
+ label: 'Default',
38
+ },
39
+ ],
40
+ },
41
+ gridBlock: {
42
+ variations: ['variationB'],
43
+ },
44
+ description: {
45
+ disable: true,
46
+ },
47
+ };
48
+
49
+ describe('BlocksConfigMerger', () => {
50
+ it('disables the block if disable is true', () => {
51
+ const result = BlocksConfigMerger(baseBlocksConfig, mutator);
52
+ expect(result.teaser.restricted).toBe(true);
53
+ });
54
+
55
+ it('filters variations according to mutator', () => {
56
+ const result = BlocksConfigMerger(baseBlocksConfig, mutator);
57
+ expect(result.teaser.variations.map((v) => v.id)).toEqual([
58
+ 'variation1',
59
+ 'variation2',
60
+ ]);
61
+ expect(result.gridBlock.variations.map((v) => v.id)).toEqual([
62
+ 'variationB',
63
+ ]);
64
+ });
65
+
66
+ it('assigns themes from mutator', () => {
67
+ const result = BlocksConfigMerger(baseBlocksConfig, mutator);
68
+ expect(result.teaser.themes).toEqual([
69
+ {
70
+ style: {
71
+ '--theme-color': '#fff',
72
+ '--theme-high-contrast-color': '#ecebeb',
73
+ '--theme-foreground-color': '#000',
74
+ '--theme-low-contrast-foreground-color': '#555555',
75
+ },
76
+ name: 'default',
77
+ label: 'Default',
78
+ },
79
+ ]);
80
+ });
81
+
82
+ it('does not modify blocks not present in mutator', () => {
83
+ const result = BlocksConfigMerger(baseBlocksConfig, mutator);
84
+ expect(result.teaser.variations.length).toBe(2);
85
+ expect(result.gridBlock.restricted).toBe(false);
86
+ });
87
+
88
+ it('ignores blocks in mutator that do not exist in blocksConfig', () => {
89
+ const result = BlocksConfigMerger(baseBlocksConfig, mutator);
90
+ expect(result.description).toBeUndefined();
91
+ });
92
+
93
+ it('does not mutate the original blocksConfig', () => {
94
+ const original = JSON.parse(JSON.stringify(baseBlocksConfig));
95
+ BlocksConfigMerger(baseBlocksConfig, mutator);
96
+ expect(baseBlocksConfig).toEqual(original);
97
+ });
98
+ });