@wordpress/block-library 9.46.0 → 9.48.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 (176) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/build/button/edit.cjs +7 -4
  3. package/build/button/edit.cjs.map +3 -3
  4. package/build/columns/edit.cjs +4 -10
  5. package/build/columns/edit.cjs.map +2 -2
  6. package/build/cover/edit/inspector-controls.cjs +20 -6
  7. package/build/cover/edit/inspector-controls.cjs.map +2 -2
  8. package/build/freeform/migration-notice.cjs +1 -1
  9. package/build/freeform/migration-notice.cjs.map +1 -1
  10. package/build/home-link/block.json +7 -0
  11. package/build/home-link/edit.cjs +167 -24
  12. package/build/home-link/edit.cjs.map +3 -3
  13. package/build/html/edit.cjs +2 -4
  14. package/build/html/edit.cjs.map +2 -2
  15. package/build/html/modal.cjs +0 -4
  16. package/build/html/modal.cjs.map +2 -2
  17. package/build/image/block.json +4 -0
  18. package/build/image/deprecated.cjs +202 -4
  19. package/build/image/deprecated.cjs.map +3 -3
  20. package/build/image/image.cjs +94 -30
  21. package/build/image/image.cjs.map +2 -2
  22. package/build/image/index.cjs +23 -4
  23. package/build/image/index.cjs.map +2 -2
  24. package/build/image/save.cjs +25 -10
  25. package/build/image/save.cjs.map +2 -2
  26. package/build/image/transforms.cjs +15 -3
  27. package/build/image/transforms.cjs.map +2 -2
  28. package/build/image/use-open-image-media-editor-modal.cjs +37 -14
  29. package/build/image/use-open-image-media-editor-modal.cjs.map +2 -2
  30. package/build/list-item/hooks/use-enter.cjs +8 -4
  31. package/build/list-item/hooks/use-enter.cjs.map +3 -3
  32. package/build/list-item/hooks/use-space.cjs +8 -4
  33. package/build/list-item/hooks/use-space.cjs.map +3 -3
  34. package/build/navigation-link/edit.cjs +2 -1
  35. package/build/navigation-link/edit.cjs.map +2 -2
  36. package/build/navigation-link/shared/use-handle-link-change.cjs +19 -3
  37. package/build/navigation-link/shared/use-handle-link-change.cjs.map +3 -3
  38. package/build/navigation-submenu/edit.cjs +8 -22
  39. package/build/navigation-submenu/edit.cjs.map +2 -2
  40. package/build/paragraph/use-enter.cjs +8 -4
  41. package/build/paragraph/use-enter.cjs.map +3 -3
  42. package/build/post-date/edit.cjs +9 -1
  43. package/build/post-date/edit.cjs.map +2 -2
  44. package/build/post-featured-image/edit.cjs +6 -5
  45. package/build/post-featured-image/edit.cjs.map +2 -2
  46. package/build/site-logo/edit.cjs +5 -2
  47. package/build/site-logo/edit.cjs.map +2 -2
  48. package/build/social-link/edit.cjs.map +3 -3
  49. package/build/tab-list/edit.cjs +2 -0
  50. package/build/tab-list/edit.cjs.map +2 -2
  51. package/build/tab-panels/edit.cjs +5 -1
  52. package/build/tab-panels/edit.cjs.map +2 -2
  53. package/build/table/edit.cjs +1 -0
  54. package/build/table/edit.cjs.map +2 -2
  55. package/build/tabs/edit.cjs +1 -36
  56. package/build/tabs/edit.cjs.map +2 -2
  57. package/build-module/button/edit.mjs +12 -5
  58. package/build-module/button/edit.mjs.map +2 -2
  59. package/build-module/columns/edit.mjs +4 -10
  60. package/build-module/columns/edit.mjs.map +2 -2
  61. package/build-module/cover/edit/inspector-controls.mjs +20 -7
  62. package/build-module/cover/edit/inspector-controls.mjs.map +2 -2
  63. package/build-module/freeform/migration-notice.mjs +1 -1
  64. package/build-module/freeform/migration-notice.mjs.map +1 -1
  65. package/build-module/home-link/block.json +7 -0
  66. package/build-module/home-link/edit.mjs +181 -26
  67. package/build-module/home-link/edit.mjs.map +2 -2
  68. package/build-module/html/edit.mjs +2 -4
  69. package/build-module/html/edit.mjs.map +2 -2
  70. package/build-module/html/modal.mjs +0 -4
  71. package/build-module/html/modal.mjs.map +2 -2
  72. package/build-module/image/block.json +4 -0
  73. package/build-module/image/deprecated.mjs +204 -5
  74. package/build-module/image/deprecated.mjs.map +2 -2
  75. package/build-module/image/image.mjs +96 -30
  76. package/build-module/image/image.mjs.map +2 -2
  77. package/build-module/image/index.mjs +23 -4
  78. package/build-module/image/index.mjs.map +2 -2
  79. package/build-module/image/save.mjs +25 -10
  80. package/build-module/image/save.mjs.map +2 -2
  81. package/build-module/image/transforms.mjs +15 -3
  82. package/build-module/image/transforms.mjs.map +2 -2
  83. package/build-module/image/use-open-image-media-editor-modal.mjs +37 -14
  84. package/build-module/image/use-open-image-media-editor-modal.mjs.map +2 -2
  85. package/build-module/list-item/hooks/use-enter.mjs +12 -5
  86. package/build-module/list-item/hooks/use-enter.mjs.map +2 -2
  87. package/build-module/list-item/hooks/use-space.mjs +12 -5
  88. package/build-module/list-item/hooks/use-space.mjs.map +2 -2
  89. package/build-module/navigation-link/edit.mjs +2 -1
  90. package/build-module/navigation-link/edit.mjs.map +2 -2
  91. package/build-module/navigation-link/shared/use-handle-link-change.mjs +19 -3
  92. package/build-module/navigation-link/shared/use-handle-link-change.mjs.map +2 -2
  93. package/build-module/navigation-submenu/edit.mjs +9 -23
  94. package/build-module/navigation-submenu/edit.mjs.map +2 -2
  95. package/build-module/paragraph/use-enter.mjs +12 -5
  96. package/build-module/paragraph/use-enter.mjs.map +2 -2
  97. package/build-module/post-date/edit.mjs +9 -1
  98. package/build-module/post-date/edit.mjs.map +2 -2
  99. package/build-module/post-featured-image/edit.mjs +6 -5
  100. package/build-module/post-featured-image/edit.mjs.map +2 -2
  101. package/build-module/site-logo/edit.mjs +6 -2
  102. package/build-module/site-logo/edit.mjs.map +2 -2
  103. package/build-module/social-link/edit.mjs +2 -2
  104. package/build-module/social-link/edit.mjs.map +2 -2
  105. package/build-module/tab-list/edit.mjs +2 -0
  106. package/build-module/tab-list/edit.mjs.map +2 -2
  107. package/build-module/tab-panels/edit.mjs +5 -1
  108. package/build-module/tab-panels/edit.mjs.map +2 -2
  109. package/build-module/table/edit.mjs +1 -0
  110. package/build-module/table/edit.mjs.map +2 -2
  111. package/build-module/tabs/edit.mjs +2 -37
  112. package/build-module/tabs/edit.mjs.map +2 -2
  113. package/build-style/breadcrumbs/style-rtl.css +1 -1
  114. package/build-style/breadcrumbs/style.css +1 -1
  115. package/build-style/editor-rtl.css +0 -11
  116. package/build-style/editor.css +0 -11
  117. package/build-style/gallery/editor-rtl.css +0 -11
  118. package/build-style/gallery/editor.css +0 -11
  119. package/build-style/style-rtl.css +1 -1
  120. package/build-style/style.css +1 -1
  121. package/package.json +40 -40
  122. package/src/block/edit-title.native.js +3 -3
  123. package/src/block/edit.native.js +2 -2
  124. package/src/breadcrumbs/style.scss +1 -1
  125. package/src/button/edit.js +14 -5
  126. package/src/columns/edit.js +3 -9
  127. package/src/cover/controls.native.js +2 -2
  128. package/src/cover/edit/inspector-controls.js +69 -52
  129. package/src/cover/edit.native.js +6 -4
  130. package/src/cover/focal-point-settings-button.native.js +2 -2
  131. package/src/cover/test/edit.js +70 -31
  132. package/src/embed/embed-no-preview.native.js +7 -3
  133. package/src/embed/embed-placeholder.native.js +2 -2
  134. package/src/file/edit.native.js +2 -2
  135. package/src/freeform/migration-notice.js +1 -1
  136. package/src/gallery/editor.scss +0 -14
  137. package/src/home-link/block.json +7 -0
  138. package/src/home-link/edit.js +185 -22
  139. package/src/home-link/index.php +14 -2
  140. package/src/html/edit.js +14 -12
  141. package/src/html/modal.js +0 -5
  142. package/src/image/block.json +4 -0
  143. package/src/image/deprecated.js +236 -4
  144. package/src/image/edit.native.js +2 -2
  145. package/src/image/image.js +166 -76
  146. package/src/image/index.js +20 -1
  147. package/src/image/index.php +1 -1
  148. package/src/image/save.js +39 -12
  149. package/src/image/test/use-open-image-media-editor-modal.js +101 -0
  150. package/src/image/transforms.js +21 -5
  151. package/src/image/use-open-image-media-editor-modal.js +41 -17
  152. package/src/latest-posts/edit.native.js +2 -2
  153. package/src/list-item/hooks/use-enter.js +15 -5
  154. package/src/list-item/hooks/use-space.js +15 -5
  155. package/src/list-item/list-style-type.native.js +2 -2
  156. package/src/media-text/media-container.native.js +7 -3
  157. package/src/missing/edit.native.js +4 -4
  158. package/src/missing/test/edit.native.js +3 -3
  159. package/src/navigation/test/use-navigation-menu.js +8 -2
  160. package/src/navigation-link/edit.js +1 -0
  161. package/src/navigation-link/shared/test/use-handle-link-change.test.js +212 -0
  162. package/src/navigation-link/shared/use-handle-link-change.js +36 -9
  163. package/src/navigation-submenu/edit.js +11 -28
  164. package/src/navigation-submenu/index.php +13 -0
  165. package/src/paragraph/use-enter.js +19 -5
  166. package/src/post-date/edit.js +7 -3
  167. package/src/post-featured-image/edit.js +15 -11
  168. package/src/search/edit.native.js +2 -2
  169. package/src/search/test/edit.native.js +2 -2
  170. package/src/site-logo/edit.js +7 -1
  171. package/src/social-link/edit.js +2 -2
  172. package/src/tab-list/edit.js +3 -0
  173. package/src/tab-panels/edit.js +10 -1
  174. package/src/table/edit.js +1 -0
  175. package/src/tabs/edit.js +14 -42
  176. package/src/video/edit.native.js +3 -3
@@ -423,6 +423,51 @@ describe( 'useHandleLinkChange', () => {
423
423
  } )
424
424
  );
425
425
  } );
426
+
427
+ it( 'should update text when editing text and changing a bound entity link to a custom URL in the link editing UI', () => {
428
+ useEntityBinding.mockReturnValue( {
429
+ hasUrlBinding: true,
430
+ createBinding: mockCreateBinding,
431
+ clearBinding: mockClearBinding,
432
+ } );
433
+
434
+ const attributes = {
435
+ id: 456,
436
+ url: 'https://example.com/my-page',
437
+ label: 'My Page',
438
+ kind: 'post-type',
439
+ type: 'page',
440
+ };
441
+
442
+ const { result } = renderHook( () =>
443
+ useHandleLinkChange( {
444
+ clientId,
445
+ attributes,
446
+ setAttributes: mockSetAttributes,
447
+ allowTextUpdate: true,
448
+ } )
449
+ );
450
+
451
+ const updatedLink = {
452
+ url: 'https://external-site.com',
453
+ title: 'Updated Navigation Text',
454
+ };
455
+
456
+ result.current( updatedLink );
457
+
458
+ expect( mockClearBinding ).toHaveBeenCalled();
459
+ expect( mockUpdateBlockAttributes ).toHaveBeenCalledWith(
460
+ clientId,
461
+ expect.objectContaining( {
462
+ url: 'https://external-site.com',
463
+ kind: 'custom',
464
+ type: 'custom',
465
+ id: undefined,
466
+ label: 'Updated Navigation Text',
467
+ } )
468
+ );
469
+ expect( updateAttributes ).not.toHaveBeenCalled();
470
+ } );
426
471
  } );
427
472
 
428
473
  describe( 'updating existing links', () => {
@@ -503,6 +548,87 @@ describe( 'useHandleLinkChange', () => {
503
548
  );
504
549
  } );
505
550
 
551
+ it( 'should preserve label when changing the link without editing text in the link editing UI', () => {
552
+ const attributes = {
553
+ id: 123,
554
+ url: 'https://example.com/page',
555
+ label: 'Custom Label',
556
+ kind: 'post-type',
557
+ type: 'page',
558
+ };
559
+
560
+ const { result } = renderHook( () =>
561
+ useHandleLinkChange( {
562
+ clientId,
563
+ attributes,
564
+ setAttributes: mockSetAttributes,
565
+ allowTextUpdate: true,
566
+ } )
567
+ );
568
+
569
+ const updatedLink = {
570
+ id: 456,
571
+ url: 'https://example.com/new-page',
572
+ title: 'Custom Label',
573
+ kind: 'post-type',
574
+ type: 'page',
575
+ };
576
+
577
+ result.current( updatedLink );
578
+
579
+ expect( updateAttributes ).toHaveBeenCalledWith(
580
+ expect.not.objectContaining( {
581
+ title: 'Custom Label',
582
+ } ),
583
+ mockSetAttributes,
584
+ attributes
585
+ );
586
+ expect( mockUpdateBlockAttributes ).not.toHaveBeenCalled();
587
+ } );
588
+
589
+ it( 'should include title when editing text and changing the entity link in the link editing UI', () => {
590
+ const attributes = {
591
+ id: 123,
592
+ url: 'https://example.com/page',
593
+ label: 'Sample Page',
594
+ kind: 'post-type',
595
+ type: 'page',
596
+ };
597
+
598
+ const { result } = renderHook( () =>
599
+ useHandleLinkChange( {
600
+ clientId,
601
+ attributes,
602
+ setAttributes: mockSetAttributes,
603
+ allowTextUpdate: true,
604
+ } )
605
+ );
606
+
607
+ const updatedLink = {
608
+ id: 456,
609
+ url: 'https://example.com/new-page',
610
+ title: 'Updated Navigation Text',
611
+ kind: 'post-type',
612
+ type: 'page',
613
+ };
614
+
615
+ result.current( updatedLink );
616
+
617
+ expect( updateAttributes ).toHaveBeenCalledWith(
618
+ expect.objectContaining( {
619
+ title: 'Updated Navigation Text',
620
+ } ),
621
+ mockSetAttributes,
622
+ attributes
623
+ );
624
+ expect( mockUpdateBlockAttributes ).toHaveBeenCalledWith(
625
+ clientId,
626
+ {
627
+ label: 'Updated Navigation Text',
628
+ }
629
+ );
630
+ } );
631
+
506
632
  it( 'should include title when creating link without existing label', () => {
507
633
  const attributes = {};
508
634
 
@@ -606,6 +732,92 @@ describe( 'useHandleLinkChange', () => {
606
732
  );
607
733
  } );
608
734
 
735
+ it( 'should include title when editing text for the same existing entity link', () => {
736
+ const attributes = {
737
+ id: 123,
738
+ url: 'https://example.com/page',
739
+ label: 'Sample Page',
740
+ kind: 'post-type',
741
+ type: 'page',
742
+ };
743
+
744
+ const { result } = renderHook( () =>
745
+ useHandleLinkChange( {
746
+ clientId,
747
+ attributes,
748
+ setAttributes: mockSetAttributes,
749
+ allowTextUpdate: true,
750
+ } )
751
+ );
752
+
753
+ const updatedLink = {
754
+ id: 123,
755
+ url: 'https://example.com/page',
756
+ title: 'Updated Sample Page',
757
+ kind: 'post-type',
758
+ type: 'page',
759
+ };
760
+
761
+ result.current( updatedLink );
762
+
763
+ expect( updateAttributes ).toHaveBeenCalledWith(
764
+ expect.objectContaining( {
765
+ title: 'Updated Sample Page',
766
+ } ),
767
+ mockSetAttributes,
768
+ attributes
769
+ );
770
+ expect( mockUpdateBlockAttributes ).toHaveBeenCalledWith(
771
+ clientId,
772
+ {
773
+ label: 'Updated Sample Page',
774
+ }
775
+ );
776
+ } );
777
+
778
+ it( 'should clear text when the inline link editing UI title is emptied', () => {
779
+ const attributes = {
780
+ id: 123,
781
+ url: 'https://example.com/page',
782
+ label: 'Sample Page',
783
+ kind: 'post-type',
784
+ type: 'page',
785
+ };
786
+
787
+ const { result } = renderHook( () =>
788
+ useHandleLinkChange( {
789
+ clientId,
790
+ attributes,
791
+ setAttributes: mockSetAttributes,
792
+ allowTextUpdate: true,
793
+ } )
794
+ );
795
+
796
+ const updatedLink = {
797
+ id: 123,
798
+ url: 'https://example.com/page',
799
+ title: '',
800
+ kind: 'post-type',
801
+ type: 'page',
802
+ };
803
+
804
+ result.current( updatedLink );
805
+
806
+ expect( updateAttributes ).toHaveBeenCalledWith(
807
+ expect.objectContaining( {
808
+ title: '',
809
+ } ),
810
+ mockSetAttributes,
811
+ attributes
812
+ );
813
+ expect( mockUpdateBlockAttributes ).toHaveBeenCalledWith(
814
+ clientId,
815
+ {
816
+ label: '',
817
+ }
818
+ );
819
+ } );
820
+
609
821
  it( 'should update custom link to another custom link', () => {
610
822
  updateAttributes.mockImplementation( ( attrs ) => ( {
611
823
  isEntityLink: false,
@@ -4,6 +4,8 @@
4
4
  import { useCallback } from '@wordpress/element';
5
5
  import { useDispatch } from '@wordpress/data';
6
6
  import { store as blockEditorStore } from '@wordpress/block-editor';
7
+ import { __unstableStripHTML as stripHTML } from '@wordpress/dom';
8
+ import { escapeHTML } from '@wordpress/escape-html';
7
9
 
8
10
  /**
9
11
  * Internal dependencies
@@ -16,13 +18,19 @@ import { useEntityBinding } from './use-entity-binding';
16
18
  * Manages the transition between entity links and custom links,
17
19
  * including proper binding creation and cleanup.
18
20
  *
19
- * @param {Object} options - Configuration options
20
- * @param {string} options.clientId - Block client ID
21
- * @param {Object} options.attributes - Current block attributes
22
- * @param {Function} options.setAttributes - Standard setAttribute function
21
+ * @param {Object} options - Configuration options
22
+ * @param {string} options.clientId - Block client ID
23
+ * @param {Object} options.attributes - Current block attributes
24
+ * @param {Function} options.setAttributes - Standard setAttribute function
25
+ * @param {boolean} options.allowTextUpdate - Whether this control can update the link text
23
26
  * @return {Function} Callback function to handle link changes
24
27
  */
25
- export function useHandleLinkChange( { clientId, attributes, setAttributes } ) {
28
+ export function useHandleLinkChange( {
29
+ clientId,
30
+ attributes,
31
+ setAttributes,
32
+ allowTextUpdate = false,
33
+ } ) {
26
34
  const { updateBlockAttributes } = useDispatch( blockEditorStore );
27
35
  const { hasUrlBinding, createBinding, clearBinding } = useEntityBinding( {
28
36
  clientId,
@@ -42,12 +50,25 @@ export function useHandleLinkChange( { clientId, attributes, setAttributes } ) {
42
50
  id: updatedLink.id,
43
51
  };
44
52
 
45
- // Only include title when there's no existing label
46
- // This preserves user-customized labels when updating links
47
- if ( ! attributes.label || attributes.label === '' ) {
53
+ const currentText = attributes.label
54
+ ? stripHTML( attributes.label )
55
+ : '';
56
+ const updatedText = updatedLink.title ?? '';
57
+ const hasTextUpdate =
58
+ allowTextUpdate &&
59
+ updatedLink.title !== undefined &&
60
+ updatedText !== currentText;
61
+ const textUpdateAttributes = hasTextUpdate
62
+ ? { label: escapeHTML( updatedText ) }
63
+ : {};
64
+
65
+ if (
66
+ ! attributes.label ||
67
+ attributes.label === '' ||
68
+ hasTextUpdate
69
+ ) {
48
70
  attrs.title = updatedLink.title;
49
71
  }
50
-
51
72
  // Check if transitioning from entity to custom link
52
73
  const willBeCustomLink = ! updatedLink.id && hasUrlBinding;
53
74
 
@@ -62,6 +83,7 @@ export function useHandleLinkChange( { clientId, attributes, setAttributes } ) {
62
83
  kind: 'custom',
63
84
  type: 'custom',
64
85
  id: undefined,
86
+ ...textUpdateAttributes,
65
87
  } );
66
88
  } else {
67
89
  // Normal flow for entity links or unbound custom links
@@ -76,10 +98,15 @@ export function useHandleLinkChange( { clientId, attributes, setAttributes } ) {
76
98
  } else {
77
99
  clearBinding();
78
100
  }
101
+
102
+ if ( Object.keys( textUpdateAttributes ).length ) {
103
+ updateBlockAttributes( clientId, textUpdateAttributes );
104
+ }
79
105
  }
80
106
  },
81
107
  [
82
108
  attributes,
109
+ allowTextUpdate,
83
110
  clientId,
84
111
  hasUrlBinding,
85
112
  createBinding,
@@ -35,8 +35,8 @@ import { ItemSubmenuIcon } from './icons';
35
35
  import {
36
36
  Controls,
37
37
  LinkUI,
38
- updateAttributes,
39
38
  useEntityBinding,
39
+ useHandleLinkChange,
40
40
  useIsInvalidLink,
41
41
  InvalidDraftDisplay,
42
42
  useEnableLinkStatusValidation,
@@ -92,15 +92,17 @@ export default function NavigationSubmenuEdit( {
92
92
  blockEditingMode !== 'default' ? true : submenuVisibility === 'click';
93
93
 
94
94
  // URL binding logic
95
- const {
96
- clearBinding,
97
- createBinding,
98
- hasUrlBinding,
99
- isBoundEntityAvailable,
100
- entityRecord,
101
- } = useEntityBinding( {
95
+ const { hasUrlBinding, isBoundEntityAvailable, entityRecord } =
96
+ useEntityBinding( {
97
+ clientId,
98
+ attributes,
99
+ } );
100
+
101
+ const handleLinkChange = useHandleLinkChange( {
102
102
  clientId,
103
103
  attributes,
104
+ setAttributes,
105
+ allowTextUpdate: true,
104
106
  } );
105
107
 
106
108
  const { __unstableMarkNextChangeAsNotPersistent, replaceBlock } =
@@ -398,26 +400,7 @@ export default function NavigationSubmenuEdit( {
398
400
  setAttributes( { url: '' } );
399
401
  speak( __( 'Link removed.' ), 'assertive' );
400
402
  } }
401
- onChange={ ( updatedValue ) => {
402
- // updateAttributes determines the final state and returns metadata
403
- const {
404
- isEntityLink,
405
- attributes: updatedAttributes,
406
- } = updateAttributes(
407
- updatedValue,
408
- setAttributes,
409
- attributes
410
- );
411
-
412
- // Handle URL binding based on the final computed state
413
- // Only create bindings for entity links (posts, pages, taxonomies)
414
- // Never create bindings for custom links (manual URLs)
415
- if ( isEntityLink ) {
416
- createBinding( updatedAttributes );
417
- } else {
418
- clearBinding();
419
- }
420
- } }
403
+ onChange={ handleLinkChange }
421
404
  />
422
405
  ) }
423
406
  </ParentElement>
@@ -9,6 +9,19 @@ require_once __DIR__ . '/navigation-link/shared/item-should-render.php';
9
9
  require_once __DIR__ . '/navigation-link/shared/render-submenu-icon.php';
10
10
  require_once __DIR__ . '/navigation-link/shared/build-css-font-sizes.php';
11
11
 
12
+ /**
13
+ * Renders the submenu icon SVG for the Navigation Submenu block.
14
+ *
15
+ * @since 5.9.0
16
+ * @deprecated 7.0.0 Use block_core_shared_navigation_render_submenu_icon() instead.
17
+ *
18
+ * @return string SVG markup for the submenu icon.
19
+ */
20
+ function block_core_navigation_submenu_render_submenu_icon() {
21
+ _deprecated_function( __FUNCTION__, '7.0.0', 'block_core_shared_navigation_render_submenu_icon()' );
22
+ return block_core_shared_navigation_render_submenu_icon();
23
+ }
24
+
12
25
  /**
13
26
  * Returns the submenu visibility value with backward compatibility
14
27
  * for the deprecated openSubmenusOnClick attribute.
@@ -2,7 +2,10 @@
2
2
  * WordPress dependencies
3
3
  */
4
4
  import { useRef } from '@wordpress/element';
5
- import { useRefEffect } from '@wordpress/compose';
5
+ import {
6
+ useRefEffect,
7
+ privateApis as composePrivateApis,
8
+ } from '@wordpress/compose';
6
9
  import { ENTER } from '@wordpress/keycodes';
7
10
  import { useSelect, useDispatch, useRegistry } from '@wordpress/data';
8
11
  import { store as blockEditorStore } from '@wordpress/block-editor';
@@ -13,6 +16,13 @@ import {
13
16
  getDefaultBlockName,
14
17
  } from '@wordpress/blocks';
15
18
 
19
+ /**
20
+ * Internal dependencies
21
+ */
22
+ import { unlock } from '../lock-unlock';
23
+
24
+ const { subscribeDelegatedListener } = unlock( composePrivateApis );
25
+
16
26
  export function useOnEnter( props ) {
17
27
  const { batch } = useRegistry();
18
28
  const { moveBlocksToPosition, replaceBlocks, selectionChange } =
@@ -119,9 +129,13 @@ export function useOnEnter( props ) {
119
129
  } );
120
130
  }
121
131
 
122
- element.addEventListener( 'keydown', onKeyDown );
123
- return () => {
124
- element.removeEventListener( 'keydown', onKeyDown );
125
- };
132
+ // Capture phase so we run before writing-flow's ancestor-bubble
133
+ // keydown handlers that gate on `event.defaultPrevented`.
134
+ return subscribeDelegatedListener(
135
+ element,
136
+ 'keydown',
137
+ onKeyDown,
138
+ true
139
+ );
126
140
  }, [] );
127
141
  }
@@ -99,11 +99,15 @@ export default function PostDateEdit( props ) {
99
99
 
100
100
  const blockEditingMode = useBlockEditingMode();
101
101
 
102
+ const validDatetime = datetime || new Date();
102
103
  let postDate = (
103
- <time dateTime={ dateI18n( 'c', datetime ) } ref={ setPopoverAnchor }>
104
+ <time
105
+ dateTime={ dateI18n( 'c', validDatetime ) }
106
+ ref={ setPopoverAnchor }
107
+ >
104
108
  { format === 'human-diff'
105
- ? humanTimeDiff( datetime )
106
- : dateI18n( format || siteFormat, datetime ) }
109
+ ? humanTimeDiff( validDatetime )
110
+ : dateI18n( format || siteFormat, validDatetime ) }
107
111
  </time>
108
112
  );
109
113
 
@@ -137,10 +137,12 @@ export default function PostFeaturedImageEdit( {
137
137
  return imageId;
138
138
  }, [ storedFeaturedImage, useFirstImageFromPost, postContent ] );
139
139
 
140
- const { media, postType, postPermalink } = useSelect(
140
+ const { media, postType, postPermalink, hasSelectedStyleState } = useSelect(
141
141
  ( select ) => {
142
142
  const { getEntityRecord, getPostType, getEditedEntityRecord } =
143
143
  select( coreStore );
144
+ const { hasSelectedStyleState: hasSelectedBlockStyleState } =
145
+ unlock( select( blockEditorStore ) );
144
146
  return {
145
147
  media:
146
148
  featuredImage &&
@@ -153,9 +155,10 @@ export default function PostFeaturedImageEdit( {
153
155
  postTypeSlug,
154
156
  postId
155
157
  )?.link,
158
+ hasSelectedStyleState: hasSelectedBlockStyleState( clientId ),
156
159
  };
157
160
  },
158
- [ featuredImage, postTypeSlug, postId ]
161
+ [ clientId, featuredImage, postTypeSlug, postId ]
159
162
  );
160
163
 
161
164
  const mediaUrl =
@@ -237,14 +240,16 @@ export default function PostFeaturedImageEdit( {
237
240
  clientId={ clientId }
238
241
  />
239
242
  </InspectorControls>
240
- <InspectorControls group="dimensions">
241
- <DimensionControls
242
- clientId={ clientId }
243
- attributes={ attributes }
244
- setAttributes={ setAttributes }
245
- media={ media }
246
- />
247
- </InspectorControls>
243
+ { ! hasSelectedStyleState && (
244
+ <InspectorControls group="dimensions">
245
+ <DimensionControls
246
+ clientId={ clientId }
247
+ attributes={ attributes }
248
+ setAttributes={ setAttributes }
249
+ media={ media }
250
+ />
251
+ </InspectorControls>
252
+ ) }
248
253
  { ( featuredImage || isDescendentOfQueryLoop || ! postId ) && (
249
254
  <InspectorControls>
250
255
  <ToolsPanel
@@ -278,7 +283,6 @@ export default function PostFeaturedImageEdit( {
278
283
  }
279
284
  >
280
285
  <ToggleControl
281
- __nextHasNoMarginBottom
282
286
  label={ __( 'Make image a link' ) }
283
287
  onChange={ () =>
284
288
  setAttributes( { isLink: ! isLink } )
@@ -17,7 +17,7 @@ import {
17
17
  PanelBody,
18
18
  SelectControl,
19
19
  ToggleControl,
20
- Icon,
20
+ Icon as WCIcon,
21
21
  } from '@wordpress/components';
22
22
  import { __ } from '@wordpress/i18n';
23
23
  import { search } from '@wordpress/icons';
@@ -374,7 +374,7 @@ export default function SearchEdit( {
374
374
  return (
375
375
  <View style={ richTextButtonContainerStyle }>
376
376
  { buttonUseIcon && (
377
- <Icon
377
+ <WCIcon
378
378
  icon={ search }
379
379
  { ...iconStyles }
380
380
  onLayout={ onLayoutButton }
@@ -6,7 +6,7 @@ import { render } from 'test/helpers';
6
6
  /**
7
7
  * WordPress dependencies
8
8
  */
9
- import { Icon } from '@wordpress/components';
9
+ import { Icon as WCIcon } from '@wordpress/components';
10
10
 
11
11
  /**
12
12
  * Internal dependencies
@@ -129,7 +129,7 @@ describe( 'Search Block', () => {
129
129
  } );
130
130
 
131
131
  it( 'search button uses icon', () => {
132
- const button = instance.UNSAFE_getByType( Icon );
132
+ const button = instance.UNSAFE_getByType( WCIcon );
133
133
  expect( button ).toBeTruthy();
134
134
  } );
135
135
 
@@ -10,6 +10,7 @@ import { isBlobURL } from '@wordpress/blob';
10
10
  import {
11
11
  createInterpolateElement,
12
12
  useEffect,
13
+ useRef,
13
14
  useState,
14
15
  } from '@wordpress/element';
15
16
  import { __, isRTL } from '@wordpress/i18n';
@@ -73,6 +74,7 @@ const SiteLogo = ( {
73
74
  const isResizable = ! isWideAligned && isLargeViewport;
74
75
  const [ { naturalWidth, naturalHeight }, setNaturalSize ] = useState( {} );
75
76
  const [ isEditingImage, setIsEditingImage ] = useState( false );
77
+ const cropButtonRef = useRef();
76
78
  const { toggleSelection } = useDispatch( blockEditorStore );
77
79
  const dropdownMenuProps = useToolsPanelDropdownMenuProps();
78
80
 
@@ -116,8 +118,9 @@ const SiteLogo = ( {
116
118
  }
117
119
  }, [ isSelected ] );
118
120
 
121
+ // Always apply modal updates as snackbar Undo may restore the original id.
119
122
  const handleMediaUpdate = ( { id: newId } ) => {
120
- if ( typeof newId === 'number' && newId !== logoId ) {
123
+ if ( typeof newId === 'number' ) {
121
124
  setLogo( newId );
122
125
  }
123
126
  };
@@ -400,12 +403,15 @@ const SiteLogo = ( {
400
403
  shouldShowCropAndDimensions && (
401
404
  <BlockControls group="block">
402
405
  <ToolbarButton
406
+ ref={ cropButtonRef }
403
407
  onClick={
404
408
  openMediaEditorModal && logoId
405
409
  ? () =>
406
410
  openMediaEditorModal( {
407
411
  id: logoId,
408
412
  onUpdate: handleMediaUpdate,
413
+ onClose: () =>
414
+ cropButtonRef.current?.focus(),
409
415
  } )
410
416
  : () => setIsEditingImage( true )
411
417
  }
@@ -19,7 +19,7 @@ import {
19
19
  } from '@wordpress/block-editor';
20
20
  import { useState, useRef, createInterpolateElement } from '@wordpress/element';
21
21
  import {
22
- Icon,
22
+ Icon as WCIcon,
23
23
  Button,
24
24
  Dropdown,
25
25
  TextControl,
@@ -277,7 +277,7 @@ const SocialLinkEdit = ( {
277
277
  */
278
278
  /* eslint-disable jsx-a11y/no-redundant-roles */ }
279
279
  <button aria-haspopup="dialog" { ...blockProps } role="button">
280
- <Icon icon={ icon } />
280
+ <WCIcon icon={ icon } />
281
281
  <span
282
282
  className={ clsx( 'wp-block-social-link-label', {
283
283
  'screen-reader-text': ! showLabels,
@@ -14,6 +14,8 @@ import { useSelect } from '@wordpress/data';
14
14
  import AddTabToolbarControl from '../tab-panel/add-tab-toolbar-control';
15
15
  import RemoveTabToolbarControl from '../tab-panel/remove-tab-toolbar-control';
16
16
 
17
+ const TAB_LIST_TEMPLATE = [ [ 'core/tab' ], [ 'core/tab' ] ];
18
+
17
19
  function Edit( { clientId } ) {
18
20
  const tabsClientId = useSelect(
19
21
  ( select ) =>
@@ -26,6 +28,7 @@ function Edit( { clientId } ) {
26
28
  const innerBlocksProps = useInnerBlocksProps( blockProps, {
27
29
  allowedBlocks: [ 'core/tab' ],
28
30
  orientation: 'horizontal',
31
+ template: TAB_LIST_TEMPLATE,
29
32
  templateLock: false,
30
33
  renderAppender: false,
31
34
  } );
@@ -7,6 +7,7 @@ import {
7
7
  store as blockEditorStore,
8
8
  } from '@wordpress/block-editor';
9
9
  import { useSelect } from '@wordpress/data';
10
+ import { __ } from '@wordpress/i18n';
10
11
 
11
12
  /**
12
13
  * Internal dependencies
@@ -14,7 +15,15 @@ import { useSelect } from '@wordpress/data';
14
15
  import AddTabToolbarControl from '../tab-panel/add-tab-toolbar-control';
15
16
  import RemoveTabToolbarControl from '../tab-panel/remove-tab-toolbar-control';
16
17
 
17
- const TAB_PANELS_TEMPLATE = [ [ 'core/tab-panel', {} ] ];
18
+ /**
19
+ * Initial template applied only when the block is first inserted (i.e. when
20
+ * inner blocks are empty). templateLock is false, so this is never applied to
21
+ * existing blocks that already have tab panels saved.
22
+ */
23
+ const TAB_PANELS_TEMPLATE = [
24
+ [ 'core/tab-panel', { label: __( 'Tab' ) } ],
25
+ [ 'core/tab-panel', { label: __( 'Tab' ) } ],
26
+ ];
18
27
 
19
28
  export default function Edit( { clientId } ) {
20
29
  const blockProps = useBlockProps();
package/src/table/edit.js CHANGED
@@ -631,6 +631,7 @@ const Cell = memo( function ( {
631
631
  ) }
632
632
  >
633
633
  <RichText
634
+ identifier={ `${ name }.${ rowIndex }.cells.${ columnIndex }.content` }
634
635
  value={ content }
635
636
  onChange={ onChange }
636
637
  onFocus={ () => {