@plone/volto 17.1.1 → 17.3.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.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,46 @@
8
8
 
9
9
  <!-- towncrier release notes start -->
10
10
 
11
+ ## 17.3.0 (2023-10-27)
12
+
13
+ ### Feature
14
+
15
+ - Updated aria-label for landmarks @ichim-david
16
+ Added landmark on sidebar @ichim-david
17
+ Added Pluggable section for skiplinks @ichim-david [#5290](https://github.com/plone/volto/issues/5290)
18
+
19
+ ### Bugfix
20
+
21
+ - (FIX): put padding so the text is not clipped #5305 @dobri1408 [#5305](https://github.com/plone/volto/issues/5305)
22
+ - Fix compare translations view @sneridagh [#5327](https://github.com/plone/volto/issues/5327)
23
+ - Fix DatetimeWidget on FF, the button default if no type is set is sending the form. @sneridagh
24
+ See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#formmethod [#5343](https://github.com/plone/volto/issues/5343)
25
+
26
+ ### Internal
27
+
28
+ - For blocks that define their `blockSchema`, call `applyBlockDefaults` when creating the initial data for the blocks form.
29
+ It is now possible to define a block configuration function, `initialValue` that returns the initial value for a block. This is useful in use cases such as container blocks that want to create a complex initial data structure, to avoid the need to call `React.useEffect` on their initial block rendering and thus, avoid complex async "concurent" state mutations.
30
+ The `addBlock`, `mutateBlock`, `insertBlock` now allow passing a `blocksConfig` configuration object
31
+
32
+ @tiberiuichim [#5320](https://github.com/plone/volto/issues/5320)
33
+ - Add a new set of acceptance tests with the multilingual fixture using seamless mode. @sneridagh [#5332](https://github.com/plone/volto/issues/5332)
34
+
35
+ ### Documentation
36
+
37
+ - Fix reference link to installation. @stevepiercy [#5328](https://github.com/plone/volto/issues/5328)
38
+ - Add upgrade docs for users of `@kitconcept/volto-blocks-grid` addon @sneridagh [#5333](https://github.com/plone/volto/issues/5333)
39
+
40
+ ## 17.2.0 (2023-10-16)
41
+
42
+ ### Feature
43
+
44
+ - add cypress test for search block via url - @ionlizarazu [#5298](https://github.com/plone/volto/issues/5298)
45
+
46
+ ### Bugfix
47
+
48
+ - Fix adding multiple path criteria in search and listing blocks. @davisagli [#5317](https://github.com/plone/volto/issues/5317)
49
+
50
+
11
51
  ## 17.1.1 (2023-10-13)
12
52
 
13
53
  ### Bugfix
@@ -0,0 +1 @@
1
+ fix the search block for search via url - @ionlizarazu
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  }
10
10
  ],
11
11
  "license": "MIT",
12
- "version": "17.1.1",
12
+ "version": "17.3.0",
13
13
  "repository": {
14
14
  "type": "git",
15
15
  "url": "git@github.com:plone/volto.git"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plone/volto-slate",
3
- "version": "17.1.1",
3
+ "version": "17.3.0",
4
4
  "description": "Slate.js integration with Volto",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -31,8 +31,7 @@ export const customSelectStyles = {
31
31
  }),
32
32
  valueContainer: (styles) => ({
33
33
  ...styles,
34
- padding: '0px',
35
- paddingLeft: 0,
34
+ padding: '8px',
36
35
  }),
37
36
  dropdownIndicator: (styles) => ({
38
37
  paddingRight: 0,
@@ -28,7 +28,14 @@ const PAQO = 'plone.app.querystring.operation';
28
28
  * @function getInitialState
29
29
  *
30
30
  */
31
- function getInitialState(data, facets, urlSearchText, id) {
31
+ function getInitialState(
32
+ data,
33
+ facets,
34
+ urlSearchText,
35
+ id,
36
+ sortOnParam,
37
+ sortOrderParam,
38
+ ) {
32
39
  const { types: facetWidgetTypes } =
33
40
  config.blocks.blocksConfig.search.extensions.facetWidgets;
34
41
  const facetSettings = data?.facets || [];
@@ -62,8 +69,8 @@ function getInitialState(data, facets, urlSearchText, id) {
62
69
  ]
63
70
  : []),
64
71
  ],
65
- sort_on: data.query?.sort_on,
66
- sort_order: data.query?.sort_order,
72
+ sort_on: sortOnParam || data.query?.sort_on,
73
+ sort_order: sortOrderParam || data.query?.sort_order,
67
74
  b_size: data.query?.b_size,
68
75
  limit: data.query?.limit,
69
76
  block: id,
@@ -257,7 +264,28 @@ const withSearch = (options) => (WrappedComponent) => {
257
264
  const multiFacets = data.facets
258
265
  ?.filter((facet) => facet?.multiple)
259
266
  .map((facet) => facet?.field?.value);
260
- const [facets, setFacets] = React.useState({});
267
+ const [facets, setFacets] = React.useState(
268
+ Object.assign(
269
+ {},
270
+ ...urlQuery.map(({ i, v }) => ({ [i]: v })), // TODO: the 'o' should be kept. This would be a major refactoring of the facets
271
+
272
+ // support for simple filters like ?Subject=something
273
+ // TODO: since the move to hash params this is no longer working.
274
+ // We'd have to treat the location.search and manage it just like the
275
+ // hash, to support it. We can read it, but we'd have to reset it as
276
+ // well, so at that point what's the difference to the hash?
277
+ ...configuredFacets.map((f) =>
278
+ locationSearchData[f]
279
+ ? {
280
+ [f]:
281
+ multiFacets.indexOf(f) > -1
282
+ ? [locationSearchData[f]]
283
+ : locationSearchData[f],
284
+ }
285
+ : {},
286
+ ),
287
+ ),
288
+ );
261
289
  const previousUrlQuery = usePrevious(urlQuery);
262
290
 
263
291
  React.useEffect(() => {
@@ -296,11 +324,16 @@ const withSearch = (options) => (WrappedComponent) => {
296
324
  const [sortOn, setSortOn] = React.useState(data?.query?.sort_on);
297
325
  const [sortOrder, setSortOrder] = React.useState(data?.query?.sort_order);
298
326
 
299
- const [searchData, setSearchData] = React.useState({});
327
+ const [searchData, setSearchData] = React.useState(
328
+ getInitialState(data, facets, urlSearchText, id),
329
+ );
300
330
 
331
+ const deepFacets = JSON.stringify(facets);
301
332
  React.useEffect(() => {
302
- setSearchData(getInitialState(data, facets, urlSearchText, id));
303
- }, [facets, data, urlSearchText, id]);
333
+ setSearchData(
334
+ getInitialState(data, facets, urlSearchText, id, sortOn, sortOrder),
335
+ );
336
+ }, [deepFacets, facets, data, urlSearchText, id, sortOn, sortOrder]);
304
337
 
305
338
  const timeoutRef = React.useRef();
306
339
  const facetSettings = data?.facets;
@@ -316,7 +349,7 @@ const withSearch = (options) => (WrappedComponent) => {
316
349
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
317
350
  timeoutRef.current = setTimeout(
318
351
  () => {
319
- const searchData = normalizeState({
352
+ const newSearchData = normalizeState({
320
353
  id,
321
354
  query: data.query || {},
322
355
  facets: toSearchFacets || facets,
@@ -328,8 +361,8 @@ const withSearch = (options) => (WrappedComponent) => {
328
361
  if (toSearchFacets) setFacets(toSearchFacets);
329
362
  if (toSortOn) setSortOn(toSortOn);
330
363
  if (toSortOrder) setSortOrder(toSortOrder);
331
- setSearchData(searchData);
332
- setLocationSearchData(getSearchFields(searchData));
364
+ setSearchData(newSearchData);
365
+ setLocationSearchData(getSearchFields(newSearchData));
333
366
  },
334
367
  toSearchFacets ? inputDelay / 3 : inputDelay,
335
368
  );
@@ -349,13 +382,14 @@ const withSearch = (options) => (WrappedComponent) => {
349
382
  );
350
383
 
351
384
  const removeSearchQuery = () => {
352
- searchData.query = searchData.query.reduce(
385
+ let newSearchData = { ...searchData };
386
+ newSearchData.query = searchData.query.reduce(
353
387
  // Remove SearchableText from query
354
388
  (acc, kvp) => (kvp.i === 'SearchableText' ? acc : [...acc, kvp]),
355
389
  [],
356
390
  );
357
- setSearchData(searchData);
358
- setLocationSearchData(getSearchFields(searchData));
391
+ setSearchData(newSearchData);
392
+ setLocationSearchData(getSearchFields(newSearchData));
359
393
  };
360
394
 
361
395
  const querystringResults = useSelector(
@@ -35,7 +35,11 @@ import {
35
35
  getSchema,
36
36
  listActions,
37
37
  } from '@plone/volto/actions';
38
- import { getBaseUrl, hasBlocksData } from '@plone/volto/helpers';
38
+ import {
39
+ flattenToAppURL,
40
+ getBaseUrl,
41
+ hasBlocksData,
42
+ } from '@plone/volto/helpers';
39
43
  import { preloadLazyLibs } from '@plone/volto/helpers/Loadable';
40
44
 
41
45
  import saveSVG from '@plone/volto/icons/save.svg';
@@ -260,7 +264,12 @@ class Edit extends Component {
260
264
 
261
265
  setComparingLanguage(lang, content_id) {
262
266
  this.setState({ comparingLanguage: lang });
263
- this.props.getContent(content_id, null, 'compare_to', null);
267
+ this.props.getContent(
268
+ flattenToAppURL(content_id),
269
+ null,
270
+ 'compare_to',
271
+ null,
272
+ );
264
273
  }
265
274
 
266
275
  form = React.createRef();
@@ -240,6 +240,8 @@ export class DatetimeWidgetComponent extends Component {
240
240
  )}
241
241
  {resettable && (
242
242
  <button
243
+ // FF needs that the type is "button" in order to not POST the form
244
+ type="button"
243
245
  disabled={this.props.isDisabled || !datetime}
244
246
  onClick={() => this.onResetDates()}
245
247
  className="item ui noborder button"
@@ -314,7 +314,8 @@ export class QuerystringWidgetComponent extends Component {
314
314
  label: field[1].title,
315
315
  value: field[0],
316
316
  isDisabled: (value || []).some(
317
- (v) => v['i'] === field[0],
317
+ (v) =>
318
+ v['i'] !== 'path' && v['i'] === field[0],
318
319
  ),
319
320
  }),
320
321
  ),
@@ -444,8 +445,11 @@ export class QuerystringWidgetComponent extends Component {
444
445
  (field) => ({
445
446
  label: field[1].title,
446
447
  value: field[0],
448
+ // disable selecting indexes that are already used,
449
+ // except for path, which has explicit support
450
+ // in the backend for multipath queries
447
451
  isDisabled: (value || []).some(
448
- (v) => v['i'] === field[0],
452
+ (v) => v['i'] !== 'path' && v['i'] === field[0],
449
453
  ),
450
454
  }),
451
455
  ),
@@ -41,6 +41,7 @@ const Footer = ({ intl }) => {
41
41
  color="grey"
42
42
  textAlign="center"
43
43
  id="footer"
44
+ aria-label="Footer"
44
45
  >
45
46
  <Container>
46
47
  <Segment basic inverted color="grey" className="discreet">
@@ -50,7 +50,7 @@ const Navigation = (props) => {
50
50
  };
51
51
 
52
52
  return (
53
- <nav className="navigation" id="navigation" aria-label="navigation">
53
+ <nav className="navigation" id="navigation" aria-label="Site">
54
54
  <div className="hamburger-wrapper mobile tablet only">
55
55
  <button
56
56
  className={cx('hamburger hamburger--spin', {
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import { useIntl, defineMessages } from 'react-intl';
3
+ import { Pluggable } from '@plone/volto/components/manage/Pluggable';
3
4
 
4
5
  const messages = defineMessages({
5
6
  mainView: {
@@ -23,7 +24,7 @@ const SkipLinks = () => {
23
24
  <div
24
25
  className="skiplinks-wrapper"
25
26
  role="complementary"
26
- aria-label="skiplinks"
27
+ aria-label="Skiplinks"
27
28
  >
28
29
  <a className="skiplink" href="#view">
29
30
  {intl.formatMessage(messages.mainView)}
@@ -34,6 +35,7 @@ const SkipLinks = () => {
34
35
  <a className="skiplink" href="#footer">
35
36
  {intl.formatMessage(messages.footer)}
36
37
  </a>
38
+ <Pluggable name="main.skiplinks" />
37
39
  </div>
38
40
  );
39
41
  };
@@ -3,6 +3,7 @@ import renderer from 'react-test-renderer';
3
3
  import configureStore from 'redux-mock-store';
4
4
  import { Provider } from 'react-intl-redux';
5
5
  import { MemoryRouter } from 'react-router-dom';
6
+ import { PluggablesProvider } from '@plone/volto/components/manage/Pluggable';
6
7
 
7
8
  import SkipLinks from './SkipLinks';
8
9
 
@@ -18,9 +19,11 @@ describe('SkipLinks', () => {
18
19
  });
19
20
  const component = renderer.create(
20
21
  <Provider store={store}>
21
- <MemoryRouter>
22
- <SkipLinks />
23
- </MemoryRouter>
22
+ <PluggablesProvider>
23
+ <MemoryRouter>
24
+ <SkipLinks />
25
+ </MemoryRouter>
26
+ </PluggablesProvider>
24
27
  </Provider>,
25
28
  );
26
29
  const json = component.toJSON();
@@ -132,14 +132,14 @@ export function deleteBlock(formData, blockId) {
132
132
  }
133
133
 
134
134
  /**
135
- * Add block
135
+ * Adds a block to the blocks form
136
136
  * @function addBlock
137
137
  * @param {Object} formData Form data
138
138
  * @param {string} type Block type
139
139
  * @param {number} index Destination index
140
140
  * @return {Array} New block id, New form data
141
141
  */
142
- export function addBlock(formData, type, index) {
142
+ export function addBlock(formData, type, index, blocksConfig) {
143
143
  const { settings } = config;
144
144
  const id = uuid();
145
145
  const idTrailingBlock = uuid();
@@ -148,81 +148,133 @@ export function addBlock(formData, type, index) {
148
148
  const totalItems = formData[blocksLayoutFieldname].items.length;
149
149
  const insert = index === -1 ? totalItems : index;
150
150
 
151
+ let value = applyBlockDefaults({
152
+ data: {
153
+ '@type': type,
154
+ },
155
+ intl: _dummyIntl,
156
+ });
157
+
151
158
  return [
152
159
  id,
153
- {
154
- ...formData,
155
- [blocksLayoutFieldname]: {
156
- items: [
157
- ...formData[blocksLayoutFieldname].items.slice(0, insert),
158
- id,
159
- ...(type !== settings.defaultBlockType ? [idTrailingBlock] : []),
160
- ...formData[blocksLayoutFieldname].items.slice(insert),
161
- ],
162
- },
163
- [blocksFieldname]: {
164
- ...formData[blocksFieldname],
165
- [id]: {
166
- '@type': type,
160
+ _applyBlockInitialValue({
161
+ id,
162
+ value,
163
+ blocksConfig,
164
+ formData: {
165
+ ...formData,
166
+ [blocksLayoutFieldname]: {
167
+ items: [
168
+ ...formData[blocksLayoutFieldname].items.slice(0, insert),
169
+ id,
170
+ ...(type !== settings.defaultBlockType ? [idTrailingBlock] : []),
171
+ ...formData[blocksLayoutFieldname].items.slice(insert),
172
+ ],
173
+ },
174
+ [blocksFieldname]: {
175
+ ...formData[blocksFieldname],
176
+ [id]: value,
177
+ ...(type !== settings.defaultBlockType && {
178
+ [idTrailingBlock]: {
179
+ '@type': settings.defaultBlockType,
180
+ },
181
+ }),
167
182
  },
168
- ...(type !== settings.defaultBlockType && {
169
- [idTrailingBlock]: {
170
- '@type': settings.defaultBlockType,
171
- },
172
- }),
183
+ selected: id,
173
184
  },
174
- selected: id,
175
- },
185
+ }),
176
186
  ];
177
187
  }
178
188
 
179
189
  /**
180
- * Mutate block
190
+ * Gets an initial value for a block, based on configuration
191
+ *
192
+ * This allows blocks that need complex initial data structures to avoid having
193
+ * to call `onChangeBlock` at their creation time, as this is prone to racing
194
+ * issue on block data storage.
195
+ */
196
+ const _applyBlockInitialValue = ({ id, value, blocksConfig, formData }) => {
197
+ const blocksFieldname = getBlocksFieldname(formData);
198
+ const type = value['@type'];
199
+ blocksConfig = blocksConfig || config.blocks.blocksConfig;
200
+
201
+ if (blocksConfig[type]?.initialValue) {
202
+ value = blocksConfig[type].initialValue({
203
+ id,
204
+ value,
205
+ formData,
206
+ });
207
+ formData[blocksFieldname][id] = value;
208
+ }
209
+
210
+ return formData;
211
+ };
212
+
213
+ /**
214
+ * Mutate block, changes the block @type
181
215
  * @function mutateBlock
182
216
  * @param {Object} formData Form data
183
217
  * @param {string} id Block uid to mutate
184
218
  * @param {number} value Block's new value
185
219
  * @return {Object} New form data
186
220
  */
187
- export function mutateBlock(formData, id, value) {
221
+ export function mutateBlock(formData, id, value, blocksConfig) {
188
222
  const { settings } = config;
189
223
  const blocksFieldname = getBlocksFieldname(formData);
190
224
  const blocksLayoutFieldname = getBlocksLayoutFieldname(formData);
191
225
  const index = formData[blocksLayoutFieldname].items.indexOf(id) + 1;
192
226
 
227
+ value = applyBlockDefaults({
228
+ data: value,
229
+ intl: _dummyIntl,
230
+ });
231
+ let newFormData;
232
+
193
233
  // Test if block at index is already a placeholder (trailing) block
194
234
  const trailId = formData[blocksLayoutFieldname].items[index];
195
235
  if (trailId) {
196
236
  const block = formData[blocksFieldname][trailId];
197
- if (!blockHasValue(block)) {
198
- return {
237
+ newFormData = _applyBlockInitialValue({
238
+ id,
239
+ value,
240
+ blocksConfig,
241
+ formData: {
199
242
  ...formData,
200
243
  [blocksFieldname]: {
201
244
  ...formData[blocksFieldname],
202
245
  [id]: value || null,
203
246
  },
204
- };
247
+ },
248
+ });
249
+ if (!blockHasValue(block)) {
250
+ return newFormData;
205
251
  }
206
252
  }
207
253
 
208
254
  const idTrailingBlock = uuid();
209
- return {
210
- ...formData,
211
- [blocksFieldname]: {
212
- ...formData[blocksFieldname],
213
- [id]: value || null,
214
- [idTrailingBlock]: {
215
- '@type': settings.defaultBlockType,
255
+ newFormData = _applyBlockInitialValue({
256
+ id,
257
+ value,
258
+ blocksConfig,
259
+ formData: {
260
+ ...formData,
261
+ [blocksFieldname]: {
262
+ ...formData[blocksFieldname],
263
+ [id]: value || null,
264
+ [idTrailingBlock]: {
265
+ '@type': settings.defaultBlockType,
266
+ },
267
+ },
268
+ [blocksLayoutFieldname]: {
269
+ items: [
270
+ ...formData[blocksLayoutFieldname].items.slice(0, index),
271
+ idTrailingBlock,
272
+ ...formData[blocksLayoutFieldname].items.slice(index),
273
+ ],
216
274
  },
217
275
  },
218
- [blocksLayoutFieldname]: {
219
- items: [
220
- ...formData[blocksLayoutFieldname].items.slice(0, index),
221
- idTrailingBlock,
222
- ...formData[blocksLayoutFieldname].items.slice(index),
223
- ],
224
- },
225
- };
276
+ });
277
+ return newFormData;
226
278
  }
227
279
 
228
280
  /**
@@ -233,15 +285,29 @@ export function mutateBlock(formData, id, value) {
233
285
  * @param {number} value New block's value
234
286
  * @return {Array} New block id, New form data
235
287
  */
236
- export function insertBlock(formData, id, value, current = {}, offset = 0) {
288
+ export function insertBlock(
289
+ formData,
290
+ id,
291
+ value,
292
+ current = {},
293
+ offset = 0,
294
+ blocksConfig,
295
+ ) {
237
296
  const blocksFieldname = getBlocksFieldname(formData);
238
297
  const blocksLayoutFieldname = getBlocksLayoutFieldname(formData);
239
298
  const index = formData[blocksLayoutFieldname].items.indexOf(id);
240
299
 
300
+ value = applyBlockDefaults({
301
+ data: value,
302
+ intl: _dummyIntl,
303
+ });
304
+
241
305
  const newBlockId = uuid();
242
- return [
243
- newBlockId,
244
- {
306
+ const newFormData = _applyBlockInitialValue({
307
+ id,
308
+ value,
309
+ blocksConfig,
310
+ formData: {
245
311
  ...formData,
246
312
  [blocksFieldname]: {
247
313
  ...formData[blocksFieldname],
@@ -259,7 +325,9 @@ export function insertBlock(formData, id, value, current = {}, offset = 0) {
259
325
  ],
260
326
  },
261
327
  },
262
- ];
328
+ });
329
+
330
+ return [newBlockId, newFormData];
263
331
  }
264
332
 
265
333
  /**
@@ -570,3 +638,7 @@ export function findBlocks(blocks, types, result = []) {
570
638
 
571
639
  return result;
572
640
  }
641
+
642
+ const _dummyIntl = {
643
+ formatMessage() {},
644
+ };
@@ -64,6 +64,24 @@ config.blocks.blocksConfig.text = {
64
64
  }),
65
65
  };
66
66
 
67
+ config.blocks.blocksConfig.dummyText = {
68
+ id: 'dummyText',
69
+ title: 'Text',
70
+ group: 'text',
71
+ restricted: false,
72
+ mostUsed: false,
73
+ blockHasOwnFocusManagement: true,
74
+ blockHasValue: (data) => {
75
+ const isEmpty =
76
+ !data.text ||
77
+ (data.text?.blocks?.length === 1 && data.text.blocks[0].text === '');
78
+ return !isEmpty;
79
+ },
80
+ initialValue: ({ value, id, formData }) => {
81
+ return { ...value, marker: true };
82
+ },
83
+ };
84
+
67
85
  config.blocks.blocksConfig.enhancedBlock = {
68
86
  id: 'enhancedBlock',
69
87
  title: 'Text',
@@ -474,6 +492,63 @@ describe('Blocks', () => {
474
492
  );
475
493
  expect(form.blocks_layout.items).toStrictEqual(['a', newId, 'b']);
476
494
  });
495
+
496
+ it('initializes data for new block with initialValue', () => {
497
+ const [newId, form] = addBlock(
498
+ {
499
+ blocks: { a: { value: 1 }, b: { value: 2 } },
500
+ blocks_layout: { items: ['a', 'b'] },
501
+ },
502
+ 'dummyText',
503
+ 1,
504
+ );
505
+ expect(form.blocks[newId]).toStrictEqual({
506
+ '@type': 'dummyText',
507
+ marker: true,
508
+ });
509
+ });
510
+
511
+ it('initializes data for new block based on schema defaults', () => {
512
+ const [newId, form] = addBlock(
513
+ {
514
+ blocks: { a: { value: 1 }, b: { value: 2 } },
515
+ blocks_layout: { items: ['a', 'b'] },
516
+ },
517
+ 'text',
518
+ 1,
519
+ );
520
+ expect(form.blocks[newId]).toStrictEqual({
521
+ '@type': 'text',
522
+ booleanField: false,
523
+ description: 'Default description',
524
+ title: 'Default title',
525
+ });
526
+ });
527
+
528
+ it('initializes data for new block based on schema defaults and initialValue', () => {
529
+ config.blocks.blocksConfig.text.initialValue = ({ value }) => ({
530
+ ...value,
531
+ marker: true,
532
+ });
533
+ const [newId, form] = addBlock(
534
+ {
535
+ blocks: { a: { value: 1 }, b: { value: 2 } },
536
+ blocks_layout: { items: ['a', 'b'] },
537
+ },
538
+ 'text',
539
+ 1,
540
+ );
541
+
542
+ delete config.blocks.blocksConfig.text.initialValue;
543
+
544
+ expect(form.blocks[newId]).toStrictEqual({
545
+ '@type': 'text',
546
+ booleanField: false,
547
+ description: 'Default description',
548
+ title: 'Default title',
549
+ marker: true,
550
+ });
551
+ });
477
552
  });
478
553
 
479
554
  describe('moveBlock', () => {
@@ -177,7 +177,7 @@ class Html extends Component {
177
177
  <body className={bodyClass}>
178
178
  <div role="navigation" aria-label="Toolbar" id="toolbar" />
179
179
  <div id="main" dangerouslySetInnerHTML={{ __html: markup }} />
180
- <div id="sidebar" />
180
+ <div role="complementary" aria-label="Sidebar" id="sidebar" />
181
181
  <script
182
182
  dangerouslySetInnerHTML={{
183
183
  __html: `window.__data=${serialize(