@wordpress/edit-site 4.9.0 → 4.10.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 (105) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/components/add-new-template/add-custom-generic-template-modal.js +84 -0
  3. package/build/components/add-new-template/add-custom-generic-template-modal.js.map +1 -0
  4. package/build/components/add-new-template/add-custom-template-modal.js +92 -53
  5. package/build/components/add-new-template/add-custom-template-modal.js.map +1 -1
  6. package/build/components/add-new-template/new-template.js +77 -81
  7. package/build/components/add-new-template/new-template.js.map +1 -1
  8. package/build/components/add-new-template/utils.js +310 -44
  9. package/build/components/add-new-template/utils.js.map +1 -1
  10. package/build/components/code-editor/index.js +17 -4
  11. package/build/components/code-editor/index.js.map +1 -1
  12. package/build/components/editor/index.js +16 -0
  13. package/build/components/editor/index.js.map +1 -1
  14. package/build/components/error-boundary/index.js +6 -0
  15. package/build/components/error-boundary/index.js.map +1 -1
  16. package/build/components/global-styles/dimensions-panel.js +2 -6
  17. package/build/components/global-styles/dimensions-panel.js.map +1 -1
  18. package/build/components/global-styles/global-styles-provider.js +4 -2
  19. package/build/components/global-styles/global-styles-provider.js.map +1 -1
  20. package/build/components/global-styles/hooks.js +10 -1
  21. package/build/components/global-styles/hooks.js.map +1 -1
  22. package/build/components/global-styles/screen-color-palette.js +13 -17
  23. package/build/components/global-styles/screen-color-palette.js.map +1 -1
  24. package/build/components/global-styles/screen-colors.js +9 -1
  25. package/build/components/global-styles/screen-colors.js.map +1 -1
  26. package/build/components/global-styles/screen-link-color.js +48 -14
  27. package/build/components/global-styles/screen-link-color.js.map +1 -1
  28. package/build/components/global-styles/use-global-styles-output.js +171 -33
  29. package/build/components/global-styles/use-global-styles-output.js.map +1 -1
  30. package/build/components/global-styles/utils.js +1 -1
  31. package/build/components/global-styles/utils.js.map +1 -1
  32. package/build/components/keyboard-shortcut-help-modal/index.js +1 -3
  33. package/build/components/keyboard-shortcut-help-modal/index.js.map +1 -1
  34. package/build/components/template-details/edit-template-title.js +11 -3
  35. package/build/components/template-details/edit-template-title.js.map +1 -1
  36. package/build/components/template-details/index.js +1 -20
  37. package/build/components/template-details/index.js.map +1 -1
  38. package/build/store/selectors.js +4 -1
  39. package/build/store/selectors.js.map +1 -1
  40. package/build-module/components/add-new-template/add-custom-generic-template-modal.js +77 -0
  41. package/build-module/components/add-new-template/add-custom-generic-template-modal.js.map +1 -0
  42. package/build-module/components/add-new-template/add-custom-template-modal.js +92 -53
  43. package/build-module/components/add-new-template/add-custom-template-modal.js.map +1 -1
  44. package/build-module/components/add-new-template/new-template.js +80 -83
  45. package/build-module/components/add-new-template/new-template.js.map +1 -1
  46. package/build-module/components/add-new-template/utils.js +286 -40
  47. package/build-module/components/add-new-template/utils.js.map +1 -1
  48. package/build-module/components/code-editor/index.js +18 -5
  49. package/build-module/components/code-editor/index.js.map +1 -1
  50. package/build-module/components/editor/index.js +16 -0
  51. package/build-module/components/editor/index.js.map +1 -1
  52. package/build-module/components/error-boundary/index.js +5 -0
  53. package/build-module/components/error-boundary/index.js.map +1 -1
  54. package/build-module/components/global-styles/dimensions-panel.js +2 -6
  55. package/build-module/components/global-styles/dimensions-panel.js.map +1 -1
  56. package/build-module/components/global-styles/global-styles-provider.js +4 -2
  57. package/build-module/components/global-styles/global-styles-provider.js.map +1 -1
  58. package/build-module/components/global-styles/hooks.js +10 -1
  59. package/build-module/components/global-styles/hooks.js.map +1 -1
  60. package/build-module/components/global-styles/screen-color-palette.js +14 -19
  61. package/build-module/components/global-styles/screen-color-palette.js.map +1 -1
  62. package/build-module/components/global-styles/screen-colors.js +9 -1
  63. package/build-module/components/global-styles/screen-colors.js.map +1 -1
  64. package/build-module/components/global-styles/screen-link-color.js +47 -14
  65. package/build-module/components/global-styles/screen-link-color.js.map +1 -1
  66. package/build-module/components/global-styles/use-global-styles-output.js +171 -35
  67. package/build-module/components/global-styles/use-global-styles-output.js.map +1 -1
  68. package/build-module/components/global-styles/utils.js +2 -2
  69. package/build-module/components/global-styles/utils.js.map +1 -1
  70. package/build-module/components/keyboard-shortcut-help-modal/index.js +1 -2
  71. package/build-module/components/keyboard-shortcut-help-modal/index.js.map +1 -1
  72. package/build-module/components/template-details/edit-template-title.js +12 -3
  73. package/build-module/components/template-details/edit-template-title.js.map +1 -1
  74. package/build-module/components/template-details/index.js +2 -21
  75. package/build-module/components/template-details/index.js.map +1 -1
  76. package/build-module/store/selectors.js +5 -2
  77. package/build-module/store/selectors.js.map +1 -1
  78. package/build-style/style-rtl.css +21 -23
  79. package/build-style/style.css +21 -23
  80. package/package.json +29 -29
  81. package/src/components/add-new-template/add-custom-generic-template-modal.js +97 -0
  82. package/src/components/add-new-template/add-custom-template-modal.js +92 -58
  83. package/src/components/add-new-template/new-template.js +142 -94
  84. package/src/components/add-new-template/style.scss +21 -0
  85. package/src/components/add-new-template/utils.js +290 -46
  86. package/src/components/code-editor/index.js +15 -5
  87. package/src/components/editor/index.js +11 -0
  88. package/src/components/error-boundary/index.js +5 -0
  89. package/src/components/global-styles/dimensions-panel.js +2 -7
  90. package/src/components/global-styles/global-styles-provider.js +8 -9
  91. package/src/components/global-styles/hooks.js +15 -0
  92. package/src/components/global-styles/screen-color-palette.js +25 -27
  93. package/src/components/global-styles/screen-colors.js +9 -3
  94. package/src/components/global-styles/screen-link-color.js +65 -23
  95. package/src/components/global-styles/style.scss +7 -11
  96. package/src/components/global-styles/test/use-global-styles-output.js +168 -0
  97. package/src/components/global-styles/use-global-styles-output.js +234 -59
  98. package/src/components/global-styles/utils.js +2 -2
  99. package/src/components/keyboard-shortcut-help-modal/index.js +1 -2
  100. package/src/components/keyboard-shortcut-help-modal/style.scss +0 -5
  101. package/src/components/list/style.scss +0 -8
  102. package/src/components/template-details/edit-template-title.js +10 -2
  103. package/src/components/template-details/index.js +4 -21
  104. package/src/components/test/error-boundary.js +38 -0
  105. package/src/store/selectors.js +11 -5
@@ -6,7 +6,6 @@ import {
6
6
  forEach,
7
7
  get,
8
8
  isEmpty,
9
- isString,
10
9
  kebabCase,
11
10
  pickBy,
12
11
  reduce,
@@ -24,7 +23,10 @@ import {
24
23
  } from '@wordpress/blocks';
25
24
  import { useEffect, useState, useContext } from '@wordpress/element';
26
25
  import { getCSSRules } from '@wordpress/style-engine';
27
- import { __unstablePresetDuotoneFilter as PresetDuotoneFilter } from '@wordpress/block-editor';
26
+ import {
27
+ __unstablePresetDuotoneFilter as PresetDuotoneFilter,
28
+ __experimentalGetGapCSSValue as getGapCSSValue,
29
+ } from '@wordpress/block-editor';
28
30
 
29
31
  /**
30
32
  * Internal dependencies
@@ -185,7 +187,7 @@ function getStylesDeclarations( blockStyles = {} ) {
185
187
 
186
188
  const styleValue = get( blockStyles, pathToValue );
187
189
 
188
- if ( !! properties && ! isString( styleValue ) ) {
190
+ if ( !! properties && typeof styleValue !== 'string' ) {
189
191
  Object.entries( properties ).forEach( ( entry ) => {
190
192
  const [ name, prop ] = entry;
191
193
 
@@ -231,6 +233,135 @@ function getStylesDeclarations( blockStyles = {} ) {
231
233
  return output;
232
234
  }
233
235
 
236
+ /**
237
+ * Get generated CSS for layout styles by looking up layout definitions provided
238
+ * in theme.json, and outputting common layout styles, and specific blockGap values.
239
+ *
240
+ * @param {Object} props
241
+ * @param {Object} props.tree A theme.json tree containing layout definitions.
242
+ * @param {Object} props.style A style object containing spacing values.
243
+ * @param {string} props.selector Selector used to group together layout styling rules.
244
+ * @param {boolean} props.hasBlockGapSupport Whether or not the theme opts-in to blockGap support.
245
+ * @param {boolean} props.hasFallbackGapSupport Whether or not the theme allows fallback gap styles.
246
+ * @param {?string} props.fallbackGapValue An optional fallback gap value if no real gap value is available.
247
+ * @return {string} Generated CSS rules for the layout styles.
248
+ */
249
+ export function getLayoutStyles( {
250
+ tree,
251
+ style,
252
+ selector,
253
+ hasBlockGapSupport,
254
+ hasFallbackGapSupport,
255
+ fallbackGapValue,
256
+ } ) {
257
+ let ruleset = '';
258
+ let gapValue = hasBlockGapSupport
259
+ ? getGapCSSValue( style?.spacing?.blockGap )
260
+ : '';
261
+
262
+ // Ensure a fallback gap value for the root layout definitions,
263
+ // and use a fallback value if one is provided for the current block.
264
+ if ( hasFallbackGapSupport ) {
265
+ if ( selector === ROOT_BLOCK_SELECTOR ) {
266
+ gapValue = ! gapValue ? '0.5em' : gapValue;
267
+ } else if ( ! hasBlockGapSupport && fallbackGapValue ) {
268
+ gapValue = fallbackGapValue;
269
+ }
270
+ }
271
+
272
+ if ( gapValue && tree?.settings?.layout?.definitions ) {
273
+ Object.values( tree.settings.layout.definitions ).forEach(
274
+ ( { className, name, spacingStyles } ) => {
275
+ // Allow skipping default layout for themes that opt-in to block styles, but opt-out of blockGap.
276
+ if ( ! hasBlockGapSupport && 'default' === name ) {
277
+ return;
278
+ }
279
+
280
+ if ( spacingStyles?.length ) {
281
+ spacingStyles.forEach( ( spacingStyle ) => {
282
+ const declarations = [];
283
+
284
+ if ( spacingStyle.rules ) {
285
+ Object.entries( spacingStyle.rules ).forEach(
286
+ ( [ cssProperty, cssValue ] ) => {
287
+ declarations.push(
288
+ `${ cssProperty }: ${
289
+ cssValue ? cssValue : gapValue
290
+ }`
291
+ );
292
+ }
293
+ );
294
+ }
295
+
296
+ if ( declarations.length ) {
297
+ const combinedSelector =
298
+ selector === ROOT_BLOCK_SELECTOR
299
+ ? `${ selector } .${ className }${
300
+ spacingStyle?.selector || ''
301
+ }`
302
+ : `${ selector }.${ className }${
303
+ spacingStyle?.selector || ''
304
+ }`;
305
+ ruleset += `${ combinedSelector } { ${ declarations.join(
306
+ '; '
307
+ ) }; }`;
308
+ }
309
+ } );
310
+ }
311
+ }
312
+ );
313
+ // For backwards compatibility, ensure the legacy block gap CSS variable is still available.
314
+ if ( selector === ROOT_BLOCK_SELECTOR && hasBlockGapSupport ) {
315
+ ruleset += `${ selector } { --wp--style--block-gap: ${ gapValue }; }`;
316
+ }
317
+ }
318
+
319
+ // Output base styles
320
+ if (
321
+ selector === ROOT_BLOCK_SELECTOR &&
322
+ tree?.settings?.layout?.definitions
323
+ ) {
324
+ const validDisplayModes = [ 'block', 'flex', 'grid' ];
325
+ Object.values( tree.settings.layout.definitions ).forEach(
326
+ ( { className, displayMode, baseStyles } ) => {
327
+ if (
328
+ displayMode &&
329
+ validDisplayModes.includes( displayMode )
330
+ ) {
331
+ ruleset += `${ selector } .${ className } { display:${ displayMode }; }`;
332
+ }
333
+
334
+ if ( baseStyles?.length ) {
335
+ baseStyles.forEach( ( baseStyle ) => {
336
+ const declarations = [];
337
+
338
+ if ( baseStyle.rules ) {
339
+ Object.entries( baseStyle.rules ).forEach(
340
+ ( [ cssProperty, cssValue ] ) => {
341
+ declarations.push(
342
+ `${ cssProperty }: ${ cssValue }`
343
+ );
344
+ }
345
+ );
346
+ }
347
+
348
+ if ( declarations.length ) {
349
+ const combinedSelector = `${ selector } .${ className }${
350
+ baseStyle?.selector || ''
351
+ }`;
352
+ ruleset += `${ combinedSelector } { ${ declarations.join(
353
+ '; '
354
+ ) }; }`;
355
+ }
356
+ } );
357
+ }
358
+ }
359
+ );
360
+ }
361
+
362
+ return ruleset;
363
+ }
364
+
234
365
  export const getNodesWithStyles = ( tree, blockSelectors ) => {
235
366
  const nodes = [];
236
367
 
@@ -267,9 +398,11 @@ export const getNodesWithStyles = ( tree, blockSelectors ) => {
267
398
  const blockStyles = pickStyleKeys( node );
268
399
  if ( !! blockStyles && !! blockSelectors?.[ blockName ]?.selector ) {
269
400
  nodes.push( {
270
- styles: blockStyles,
271
- selector: blockSelectors[ blockName ].selector,
272
401
  duotoneSelector: blockSelectors[ blockName ].duotoneSelector,
402
+ fallbackGapValue: blockSelectors[ blockName ].fallbackGapValue,
403
+ hasLayoutSupport: blockSelectors[ blockName ].hasLayoutSupport,
404
+ selector: blockSelectors[ blockName ].selector,
405
+ styles: blockStyles,
273
406
  } );
274
407
  }
275
408
 
@@ -364,7 +497,12 @@ export const toCustomProperties = ( tree, blockSelectors ) => {
364
497
  return ruleset;
365
498
  };
366
499
 
367
- export const toStyles = ( tree, blockSelectors, hasBlockGapSupport ) => {
500
+ export const toStyles = (
501
+ tree,
502
+ blockSelectors,
503
+ hasBlockGapSupport,
504
+ hasFallbackGapSupport
505
+ ) => {
368
506
  const nodesWithStyles = getNodesWithStyles( tree, blockSelectors );
369
507
  const nodesWithSettings = getNodesWithSettings( tree, blockSelectors );
370
508
 
@@ -377,63 +515,90 @@ export const toStyles = ( tree, blockSelectors, hasBlockGapSupport ) => {
377
515
  * @link https://github.com/WordPress/gutenberg/issues/36147.
378
516
  */
379
517
  let ruleset = 'body {margin: 0;}';
380
- nodesWithStyles.forEach( ( { selector, duotoneSelector, styles } ) => {
381
- const duotoneStyles = {};
382
- if ( styles?.filter ) {
383
- duotoneStyles.filter = styles.filter;
384
- delete styles.filter;
385
- }
518
+ nodesWithStyles.forEach(
519
+ ( {
520
+ selector,
521
+ duotoneSelector,
522
+ styles,
523
+ fallbackGapValue,
524
+ hasLayoutSupport,
525
+ } ) => {
526
+ const duotoneStyles = {};
527
+ if ( styles?.filter ) {
528
+ duotoneStyles.filter = styles.filter;
529
+ delete styles.filter;
530
+ }
386
531
 
387
- // Process duotone styles (they use color.__experimentalDuotone selector).
388
- if ( duotoneSelector ) {
389
- const duotoneDeclarations = getStylesDeclarations( duotoneStyles );
390
- if ( duotoneDeclarations.length === 0 ) {
391
- return;
532
+ // Process duotone styles (they use color.__experimentalDuotone selector).
533
+ if ( duotoneSelector ) {
534
+ const duotoneDeclarations =
535
+ getStylesDeclarations( duotoneStyles );
536
+ if ( duotoneDeclarations.length === 0 ) {
537
+ return;
538
+ }
539
+ ruleset =
540
+ ruleset +
541
+ `${ duotoneSelector }{${ duotoneDeclarations.join(
542
+ ';'
543
+ ) };}`;
392
544
  }
393
- ruleset =
394
- ruleset +
395
- `${ duotoneSelector }{${ duotoneDeclarations.join( ';' ) };}`;
396
- }
397
545
 
398
- // Process the remaning block styles (they use either normal block class or __experimentalSelector).
399
- const declarations = getStylesDeclarations( styles );
400
- if ( declarations?.length ) {
401
- ruleset = ruleset + `${ selector }{${ declarations.join( ';' ) };}`;
402
- }
546
+ // Process blockGap and layout styles.
547
+ if ( ROOT_BLOCK_SELECTOR === selector || hasLayoutSupport ) {
548
+ ruleset += getLayoutStyles( {
549
+ tree,
550
+ style: styles,
551
+ selector,
552
+ hasBlockGapSupport,
553
+ hasFallbackGapSupport,
554
+ fallbackGapValue,
555
+ } );
556
+ }
403
557
 
404
- // Check for pseudo selector in `styles` and handle separately.
405
- const psuedoSelectorStyles = Object.entries( styles ).filter(
406
- ( [ key ] ) => key.startsWith( ':' )
407
- );
558
+ // Process the remaining block styles (they use either normal block class or __experimentalSelector).
559
+ const declarations = getStylesDeclarations( styles );
560
+ if ( declarations?.length ) {
561
+ ruleset =
562
+ ruleset + `${ selector }{${ declarations.join( ';' ) };}`;
563
+ }
408
564
 
409
- if ( psuedoSelectorStyles?.length ) {
410
- psuedoSelectorStyles.forEach( ( [ pseudoKey, pseudoRule ] ) => {
411
- const pseudoDeclarations = getStylesDeclarations( pseudoRule );
565
+ // Check for pseudo selector in `styles` and handle separately.
566
+ const pseudoSelectorStyles = Object.entries( styles ).filter(
567
+ ( [ key ] ) => key.startsWith( ':' )
568
+ );
412
569
 
413
- if ( ! pseudoDeclarations?.length ) {
414
- return;
415
- }
570
+ if ( pseudoSelectorStyles?.length ) {
571
+ pseudoSelectorStyles.forEach(
572
+ ( [ pseudoKey, pseudoStyle ] ) => {
573
+ const pseudoDeclarations =
574
+ getStylesDeclarations( pseudoStyle );
416
575
 
417
- // `selector` maybe provided in a form
418
- // where block level selectors have sub element
419
- // selectors appended to them as a comma seperated
420
- // string.
421
- // e.g. `h1 a,h2 a,h3 a,h4 a,h5 a,h6 a`;
422
- // Split and append pseudo selector to create
423
- // the proper rules to target the elements.
424
- const _selector = selector
425
- .split( ',' )
426
- .map( ( sel ) => sel + pseudoKey )
427
- .join( ',' );
428
-
429
- const psuedoRule = `${ _selector }{${ pseudoDeclarations.join(
430
- ';'
431
- ) };}`;
432
-
433
- ruleset = ruleset + psuedoRule;
434
- } );
576
+ if ( ! pseudoDeclarations?.length ) {
577
+ return;
578
+ }
579
+
580
+ // `selector` maybe provided in a form
581
+ // where block level selectors have sub element
582
+ // selectors appended to them as a comma seperated
583
+ // string.
584
+ // e.g. `h1 a,h2 a,h3 a,h4 a,h5 a,h6 a`;
585
+ // Split and append pseudo selector to create
586
+ // the proper rules to target the elements.
587
+ const _selector = selector
588
+ .split( ',' )
589
+ .map( ( sel ) => sel + pseudoKey )
590
+ .join( ',' );
591
+
592
+ const pseudoRule = `${ _selector }{${ pseudoDeclarations.join(
593
+ ';'
594
+ ) };}`;
595
+
596
+ ruleset = ruleset + pseudoRule;
597
+ }
598
+ );
599
+ }
435
600
  }
436
- } );
601
+ );
437
602
 
438
603
  /* Add alignment / layout styles */
439
604
  ruleset =
@@ -447,12 +612,15 @@ export const toStyles = ( tree, blockSelectors, hasBlockGapSupport ) => {
447
612
  '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }';
448
613
 
449
614
  if ( hasBlockGapSupport ) {
615
+ // Use fallback of `0.5em` just in case, however if there is blockGap support, there should nearly always be a real value.
616
+ const gapValue =
617
+ getGapCSSValue( tree?.styles?.spacing?.blockGap ) || '0.5em';
450
618
  ruleset =
451
619
  ruleset +
452
620
  '.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }';
453
621
  ruleset =
454
622
  ruleset +
455
- '.wp-site-blocks > * + * { margin-block-start: var( --wp--style--block-gap ); }';
623
+ `.wp-site-blocks > * + * { margin-block-start: ${ gapValue }; }`;
456
624
  }
457
625
 
458
626
  nodesWithSettings.forEach( ( { selector, presets } ) => {
@@ -486,10 +654,15 @@ const getBlockSelectors = ( blockTypes ) => {
486
654
  '.wp-block-' + name.replace( 'core/', '' ).replace( '/', '-' );
487
655
  const duotoneSelector =
488
656
  blockType?.supports?.color?.__experimentalDuotone ?? null;
657
+ const hasLayoutSupport = !! blockType?.supports?.__experimentalLayout;
658
+ const fallbackGapValue =
659
+ blockType?.supports?.spacing?.blockGap?.__experimentalDefault;
489
660
  result[ name ] = {
661
+ duotoneSelector,
662
+ fallbackGapValue,
663
+ hasLayoutSupport,
490
664
  name,
491
665
  selector,
492
- duotoneSelector,
493
666
  };
494
667
  } );
495
668
 
@@ -503,6 +676,7 @@ export function useGlobalStylesOutput() {
503
676
  const { merged: mergedConfig } = useContext( GlobalStylesContext );
504
677
  const [ blockGap ] = useSetting( 'spacing.blockGap' );
505
678
  const hasBlockGapSupport = blockGap !== null;
679
+ const hasFallbackGapSupport = ! hasBlockGapSupport; // This setting isn't useful yet: it exists as a placeholder for a future explicit fallback styles support.
506
680
 
507
681
  useEffect( () => {
508
682
  if ( ! mergedConfig?.styles || ! mergedConfig?.settings ) {
@@ -517,7 +691,8 @@ export function useGlobalStylesOutput() {
517
691
  const globalStyles = toStyles(
518
692
  mergedConfig,
519
693
  blockSelectors,
520
- hasBlockGapSupport
694
+ hasBlockGapSupport,
695
+ hasFallbackGapSupport
521
696
  );
522
697
  const filters = toSvgFilters( mergedConfig, blockSelectors );
523
698
  setStylesheets( [
@@ -532,7 +707,7 @@ export function useGlobalStylesOutput() {
532
707
  ] );
533
708
  setSettings( mergedConfig.settings );
534
709
  setSvgFilters( filters );
535
- }, [ mergedConfig ] );
710
+ }, [ hasBlockGapSupport, hasFallbackGapSupport, mergedConfig ] );
536
711
 
537
712
  return [ stylesheets, settings, svgFilters, hasBlockGapSupport ];
538
713
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import { get, find, isString } from 'lodash';
4
+ import { get, find } from 'lodash';
5
5
 
6
6
  /* Supporting data. */
7
7
  export const ROOT_BLOCK_NAME = 'root';
@@ -213,7 +213,7 @@ function getValueFromCustomVariable( features, blockName, variable, path ) {
213
213
  }
214
214
 
215
215
  export function getValueFromVariable( features, blockName, variable ) {
216
- if ( ! variable || ! isString( variable ) ) {
216
+ if ( ! variable || typeof variable !== 'string' ) {
217
217
  return variable;
218
218
  }
219
219
  const USER_VALUE_PREFIX = 'var:';
@@ -2,7 +2,6 @@
2
2
  * External dependencies
3
3
  */
4
4
  import classnames from 'classnames';
5
- import { isString } from 'lodash';
6
5
 
7
6
  /**
8
7
  * WordPress dependencies
@@ -34,7 +33,7 @@ const ShortcutList = ( { shortcuts } ) => (
34
33
  className="edit-site-keyboard-shortcut-help-modal__shortcut"
35
34
  key={ index }
36
35
  >
37
- { isString( shortcut ) ? (
36
+ { typeof shortcut === 'string' ? (
38
37
  <DynamicShortcut name={ shortcut } />
39
38
  ) : (
40
39
  <Shortcut { ...shortcut } />
@@ -3,11 +3,6 @@
3
3
  margin: 0 0 2rem 0;
4
4
  }
5
5
 
6
- &__main-shortcuts .edit-site-keyboard-shortcut-help-modal__shortcut-list {
7
- // Push the shortcut to be flush with top modal header.
8
- margin-top: -$grid-unit-30 -$border-width;
9
- }
10
-
11
6
  &__section-title {
12
7
  font-size: 0.9rem;
13
8
  font-weight: 600;
@@ -131,14 +131,6 @@
131
131
  width: $grid-unit * 40;
132
132
  }
133
133
  }
134
-
135
- .components-modal__header {
136
- border-bottom: none;
137
- }
138
-
139
- .components-modal__content::before {
140
- margin-bottom: $grid-unit-05;
141
- }
142
134
  }
143
135
 
144
136
  .edit-site-list__rename-modal-actions {
@@ -4,8 +4,10 @@
4
4
  import { __ } from '@wordpress/i18n';
5
5
  import { TextControl } from '@wordpress/components';
6
6
  import { useEntityProp } from '@wordpress/core-data';
7
+ import { useState } from '@wordpress/element';
7
8
 
8
9
  export default function EditTemplateTitle( { template } ) {
10
+ const [ forceEmpty, setForceEmpty ] = useState( false );
9
11
  const [ title, setTitle ] = useEntityProp(
10
12
  'postType',
11
13
  template.type,
@@ -16,13 +18,19 @@ export default function EditTemplateTitle( { template } ) {
16
18
  return (
17
19
  <TextControl
18
20
  label={ __( 'Title' ) }
19
- value={ title }
21
+ value={ forceEmpty ? '' : title }
20
22
  help={ __(
21
23
  'Give the template a title that indicates its purpose, e.g. "Full Width".'
22
24
  ) }
23
25
  onChange={ ( newTitle ) => {
24
- setTitle( newTitle || template.slug );
26
+ if ( ! newTitle && ! forceEmpty ) {
27
+ setForceEmpty( true );
28
+ return;
29
+ }
30
+ setForceEmpty( false );
31
+ setTitle( newTitle );
25
32
  } }
33
+ onBlur={ () => setForceEmpty( false ) }
26
34
  />
27
35
  );
28
36
  }
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { useMemo } from '@wordpress/element';
5
- import { sprintf, __ } from '@wordpress/i18n';
4
+ import { __ } from '@wordpress/i18n';
6
5
  import {
7
6
  Button,
8
7
  MenuGroup,
@@ -17,10 +16,6 @@ import { store as editorStore } from '@wordpress/editor';
17
16
  * Internal dependencies
18
17
  */
19
18
  import isTemplateRevertable from '../../utils/is-template-revertable';
20
- import {
21
- MENU_TEMPLATES,
22
- TEMPLATE_PARTS_SUB_MENUS,
23
- } from '../navigation-sidebar/navigation-panel/constants';
24
19
  import { store as editSiteStore } from '../../store';
25
20
  import TemplateAreas from './template-areas';
26
21
  import EditTemplateTitle from './edit-template-title';
@@ -34,16 +29,6 @@ export default function TemplateDetails( { template, onClose } ) {
34
29
  );
35
30
  const { revertTemplate } = useDispatch( editSiteStore );
36
31
 
37
- const templateSubMenu = useMemo( () => {
38
- if ( template?.type === 'wp_template' ) {
39
- return { title: __( 'templates' ), menu: MENU_TEMPLATES };
40
- }
41
-
42
- return TEMPLATE_PARTS_SUB_MENUS.find(
43
- ( { area } ) => area === template?.area
44
- );
45
- }, [ template ] );
46
-
47
32
  const browseAllLinkProps = useLink( {
48
33
  // TODO: We should update this to filter by template part's areas as well.
49
34
  postType: template.type,
@@ -106,11 +91,9 @@ export default function TemplateDetails( { template, onClose } ) {
106
91
  className="edit-site-template-details__show-all-button"
107
92
  { ...browseAllLinkProps }
108
93
  >
109
- { sprintf(
110
- /* translators: the template part's area name ("Headers", "Sidebars") or "templates". */
111
- __( 'Browse all %s' ),
112
- templateSubMenu.title
113
- ) }
94
+ { template?.type === 'wp_template'
95
+ ? __( 'Browse all templates' )
96
+ : __( 'Browse all template parts' ) }
114
97
  </Button>
115
98
  </div>
116
99
  );
@@ -0,0 +1,38 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import * as wpHooks from '@wordpress/hooks';
5
+ /**
6
+ * Internal dependencies
7
+ */
8
+ import ErrorBoundary from '../error-boundary';
9
+ /**
10
+ * External dependencies
11
+ */
12
+ import { render } from '@testing-library/react';
13
+
14
+ const theError = new Error( 'Kaboom' );
15
+
16
+ const ChildComponent = () => {
17
+ throw theError;
18
+ };
19
+
20
+ describe( 'Error Boundary', () => {
21
+ describe( 'when error is thrown from a Child component', () => {
22
+ it( 'calls the `editor.ErrorBoundary.errorLogged` hook action with the error object', () => {
23
+ const doAction = jest.spyOn( wpHooks, 'doAction' );
24
+
25
+ render(
26
+ <ErrorBoundary>
27
+ <ChildComponent />
28
+ </ErrorBoundary>
29
+ );
30
+
31
+ expect( doAction ).toHaveBeenCalledWith(
32
+ 'editor.ErrorBoundary.errorLogged',
33
+ theError
34
+ );
35
+ expect( console ).toHaveErrored();
36
+ } );
37
+ } );
38
+ } );
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import { map, keyBy } from 'lodash';
4
+ import { map } from 'lodash';
5
5
  import createSelector from 'rememo';
6
6
 
7
7
  /**
@@ -341,10 +341,16 @@ export const getCurrentTemplateTemplateParts = createRegistrySelector(
341
341
  'wp_template_part',
342
342
  { per_page: -1 }
343
343
  );
344
- const templatePartsById = keyBy(
345
- templateParts,
346
- ( templatePart ) => templatePart.id
347
- );
344
+ const templatePartsById = templateParts
345
+ ? // Key template parts by their ID.
346
+ templateParts.reduce(
347
+ ( newTemplateParts, part ) => ( {
348
+ ...newTemplateParts,
349
+ [ part.id ]: part,
350
+ } ),
351
+ {}
352
+ )
353
+ : {};
348
354
 
349
355
  return ( template.blocks ?? [] )
350
356
  .filter( ( block ) => isTemplatePart( block ) )