@wordpress/block-editor 12.11.0 → 12.12.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 (215) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/components/block-controls/hook.js +4 -1
  3. package/build/components/block-controls/hook.js.map +1 -1
  4. package/build/components/block-list/block-outline.native.js +1 -1
  5. package/build/components/block-list/block-outline.native.js.map +1 -1
  6. package/build/components/block-list/block.js +2 -0
  7. package/build/components/block-list/block.js.map +1 -1
  8. package/build/components/block-preview/index.js +4 -1
  9. package/build/components/block-preview/index.js.map +1 -1
  10. package/build/components/block-styles/index.js +1 -2
  11. package/build/components/block-styles/index.js.map +1 -1
  12. package/build/components/block-switcher/pattern-transformations-menu.js +2 -4
  13. package/build/components/block-switcher/pattern-transformations-menu.js.map +1 -1
  14. package/build/components/block-tools/block-contextual-toolbar.js +1 -0
  15. package/build/components/block-tools/block-contextual-toolbar.js.map +1 -1
  16. package/build/components/dimensions-tool/index.js +22 -22
  17. package/build/components/dimensions-tool/index.js.map +1 -1
  18. package/build/components/dimensions-tool/scale-tool.js +1 -1
  19. package/build/components/dimensions-tool/scale-tool.js.map +1 -1
  20. package/build/components/height-control/index.js +90 -2
  21. package/build/components/height-control/index.js.map +1 -1
  22. package/build/components/iframe/index.js +9 -1
  23. package/build/components/iframe/index.js.map +1 -1
  24. package/build/components/image-editor/aspect-ratio-dropdown.js +0 -6
  25. package/build/components/image-editor/aspect-ratio-dropdown.js.map +1 -1
  26. package/build/components/index.native.js +8 -0
  27. package/build/components/index.native.js.map +1 -1
  28. package/build/components/link-control/link-preview.js +5 -2
  29. package/build/components/link-control/link-preview.js.map +1 -1
  30. package/build/components/link-control/use-internal-value.js +14 -8
  31. package/build/components/link-control/use-internal-value.js.map +1 -1
  32. package/build/components/list-view/index.js +6 -5
  33. package/build/components/list-view/index.js.map +1 -1
  34. package/build/components/media-replace-flow/index.js +2 -4
  35. package/build/components/media-replace-flow/index.js.map +1 -1
  36. package/build/components/provider/use-block-sync.js +2 -2
  37. package/build/components/provider/use-block-sync.js.map +1 -1
  38. package/build/components/rich-text/index.native.js +14 -3
  39. package/build/components/rich-text/index.native.js.map +1 -1
  40. package/build/components/rich-text/use-delete.js +1 -1
  41. package/build/components/rich-text/use-delete.js.map +1 -1
  42. package/build/components/rich-text/use-paste-handler.js +25 -22
  43. package/build/components/rich-text/use-paste-handler.js.map +1 -1
  44. package/build/components/spacing-sizes-control/input-controls/spacing-input-control.js +88 -0
  45. package/build/components/spacing-sizes-control/input-controls/spacing-input-control.js.map +1 -1
  46. package/build/components/tool-selector/index.js +1 -2
  47. package/build/components/tool-selector/index.js.map +1 -1
  48. package/build/components/use-block-commands/index.js +90 -46
  49. package/build/components/use-block-commands/index.js.map +1 -1
  50. package/build/components/use-setting/index.js +4 -2
  51. package/build/components/use-setting/index.js.map +1 -1
  52. package/build/components/writing-flow/use-arrow-nav.js +4 -0
  53. package/build/components/writing-flow/use-arrow-nav.js.map +1 -1
  54. package/build/hooks/block-rename-ui.js +5 -5
  55. package/build/hooks/block-rename-ui.js.map +1 -1
  56. package/build/hooks/{metadata-name.js → block-renaming.js} +3 -7
  57. package/build/hooks/block-renaming.js.map +1 -0
  58. package/build/hooks/custom-class-name.js +28 -21
  59. package/build/hooks/custom-class-name.js.map +1 -1
  60. package/build/hooks/duotone.js +28 -1
  61. package/build/hooks/duotone.js.map +1 -1
  62. package/build/hooks/index.js +1 -1
  63. package/build/hooks/index.js.map +1 -1
  64. package/build/hooks/layout.js +6 -6
  65. package/build/hooks/layout.js.map +1 -1
  66. package/build/hooks/metadata.js +6 -27
  67. package/build/hooks/metadata.js.map +1 -1
  68. package/build/hooks/utils.js +1 -1
  69. package/build/hooks/utils.js.map +1 -1
  70. package/build/layouts/grid.js +25 -3
  71. package/build/layouts/grid.js.map +1 -1
  72. package/build/layouts/utils.js +1 -1
  73. package/build/layouts/utils.js.map +1 -1
  74. package/build/lock-unlock.js +1 -1
  75. package/build/lock-unlock.js.map +1 -1
  76. package/build/store/actions.js +13 -5
  77. package/build/store/actions.js.map +1 -1
  78. package/build/utils/parse-css-unit-to-px.js +20 -0
  79. package/build/utils/parse-css-unit-to-px.js.map +1 -1
  80. package/build/utils/pasting.js +1 -1
  81. package/build/utils/pasting.js.map +1 -1
  82. package/build-module/components/block-controls/hook.js +4 -1
  83. package/build-module/components/block-controls/hook.js.map +1 -1
  84. package/build-module/components/block-list/block-outline.native.js +1 -1
  85. package/build-module/components/block-list/block-outline.native.js.map +1 -1
  86. package/build-module/components/block-list/block.js +2 -0
  87. package/build-module/components/block-list/block.js.map +1 -1
  88. package/build-module/components/block-preview/index.js +4 -1
  89. package/build-module/components/block-preview/index.js.map +1 -1
  90. package/build-module/components/block-styles/index.js +1 -2
  91. package/build-module/components/block-styles/index.js.map +1 -1
  92. package/build-module/components/block-switcher/pattern-transformations-menu.js +2 -4
  93. package/build-module/components/block-switcher/pattern-transformations-menu.js.map +1 -1
  94. package/build-module/components/block-tools/block-contextual-toolbar.js +1 -0
  95. package/build-module/components/block-tools/block-contextual-toolbar.js.map +1 -1
  96. package/build-module/components/dimensions-tool/index.js +22 -22
  97. package/build-module/components/dimensions-tool/index.js.map +1 -1
  98. package/build-module/components/dimensions-tool/scale-tool.js +1 -1
  99. package/build-module/components/dimensions-tool/scale-tool.js.map +1 -1
  100. package/build-module/components/height-control/index.js +90 -2
  101. package/build-module/components/height-control/index.js.map +1 -1
  102. package/build-module/components/iframe/index.js +9 -1
  103. package/build-module/components/iframe/index.js.map +1 -1
  104. package/build-module/components/image-editor/aspect-ratio-dropdown.js +0 -6
  105. package/build-module/components/image-editor/aspect-ratio-dropdown.js.map +1 -1
  106. package/build-module/components/index.native.js +1 -0
  107. package/build-module/components/index.native.js.map +1 -1
  108. package/build-module/components/link-control/link-preview.js +6 -3
  109. package/build-module/components/link-control/link-preview.js.map +1 -1
  110. package/build-module/components/link-control/use-internal-value.js +14 -9
  111. package/build-module/components/link-control/use-internal-value.js.map +1 -1
  112. package/build-module/components/list-view/index.js +7 -6
  113. package/build-module/components/list-view/index.js.map +1 -1
  114. package/build-module/components/media-replace-flow/index.js +3 -5
  115. package/build-module/components/media-replace-flow/index.js.map +1 -1
  116. package/build-module/components/provider/use-block-sync.js +2 -2
  117. package/build-module/components/provider/use-block-sync.js.map +1 -1
  118. package/build-module/components/rich-text/index.native.js +15 -4
  119. package/build-module/components/rich-text/index.native.js.map +1 -1
  120. package/build-module/components/rich-text/use-delete.js +1 -1
  121. package/build-module/components/rich-text/use-delete.js.map +1 -1
  122. package/build-module/components/rich-text/use-paste-handler.js +25 -22
  123. package/build-module/components/rich-text/use-paste-handler.js.map +1 -1
  124. package/build-module/components/spacing-sizes-control/input-controls/spacing-input-control.js +88 -0
  125. package/build-module/components/spacing-sizes-control/input-controls/spacing-input-control.js.map +1 -1
  126. package/build-module/components/tool-selector/index.js +1 -2
  127. package/build-module/components/tool-selector/index.js.map +1 -1
  128. package/build-module/components/use-block-commands/index.js +90 -46
  129. package/build-module/components/use-block-commands/index.js.map +1 -1
  130. package/build-module/components/use-setting/index.js +4 -2
  131. package/build-module/components/use-setting/index.js.map +1 -1
  132. package/build-module/components/writing-flow/use-arrow-nav.js +4 -0
  133. package/build-module/components/writing-flow/use-arrow-nav.js.map +1 -1
  134. package/build-module/hooks/block-rename-ui.js +6 -6
  135. package/build-module/hooks/block-rename-ui.js.map +1 -1
  136. package/build-module/hooks/{metadata-name.js → block-renaming.js} +3 -6
  137. package/build-module/hooks/block-renaming.js.map +1 -0
  138. package/build-module/hooks/custom-class-name.js +28 -21
  139. package/build-module/hooks/custom-class-name.js.map +1 -1
  140. package/build-module/hooks/duotone.js +28 -1
  141. package/build-module/hooks/duotone.js.map +1 -1
  142. package/build-module/hooks/index.js +1 -1
  143. package/build-module/hooks/index.js.map +1 -1
  144. package/build-module/hooks/layout.js +6 -6
  145. package/build-module/hooks/layout.js.map +1 -1
  146. package/build-module/hooks/metadata.js +6 -25
  147. package/build-module/hooks/metadata.js.map +1 -1
  148. package/build-module/hooks/utils.js +1 -1
  149. package/build-module/hooks/utils.js.map +1 -1
  150. package/build-module/layouts/grid.js +25 -3
  151. package/build-module/layouts/grid.js.map +1 -1
  152. package/build-module/layouts/utils.js +1 -1
  153. package/build-module/layouts/utils.js.map +1 -1
  154. package/build-module/lock-unlock.js +1 -1
  155. package/build-module/lock-unlock.js.map +1 -1
  156. package/build-module/store/actions.js +14 -6
  157. package/build-module/store/actions.js.map +1 -1
  158. package/build-module/utils/parse-css-unit-to-px.js +20 -0
  159. package/build-module/utils/parse-css-unit-to-px.js.map +1 -1
  160. package/build-module/utils/pasting.js +1 -1
  161. package/build-module/utils/pasting.js.map +1 -1
  162. package/build-style/style-rtl.css +11 -6
  163. package/build-style/style.css +11 -6
  164. package/package.json +31 -31
  165. package/src/components/block-controls/hook.js +6 -3
  166. package/src/components/block-list/block-outline.native.js +1 -1
  167. package/src/components/block-list/block.js +2 -0
  168. package/src/components/block-preview/index.js +7 -1
  169. package/src/components/block-styles/index.js +1 -4
  170. package/src/components/block-switcher/pattern-transformations-menu.js +1 -4
  171. package/src/components/block-switcher/style.scss +6 -0
  172. package/src/components/block-tools/block-contextual-toolbar.js +1 -0
  173. package/src/components/block-tools/style.scss +0 -1
  174. package/src/components/color-palette/test/__snapshots__/control.js.snap +3 -1
  175. package/src/components/colors-gradients/style.scss +4 -2
  176. package/src/components/dimensions-tool/index.js +25 -25
  177. package/src/components/dimensions-tool/scale-tool.js +1 -1
  178. package/src/components/height-control/index.js +50 -2
  179. package/src/components/iframe/index.js +8 -1
  180. package/src/components/image-editor/aspect-ratio-dropdown.js +0 -8
  181. package/src/components/index.native.js +1 -0
  182. package/src/components/link-control/README.md +43 -0
  183. package/src/components/link-control/link-preview.js +11 -5
  184. package/src/components/link-control/style.scss +3 -3
  185. package/src/components/link-control/test/index.js +19 -0
  186. package/src/components/link-control/use-internal-value.js +14 -10
  187. package/src/components/list-view/index.js +13 -3
  188. package/src/components/media-replace-flow/index.js +9 -14
  189. package/src/components/provider/test/use-block-sync.js +1 -1
  190. package/src/components/provider/use-block-sync.js +2 -2
  191. package/src/components/rich-text/index.native.js +19 -3
  192. package/src/components/rich-text/use-delete.js +1 -1
  193. package/src/components/rich-text/use-paste-handler.js +27 -24
  194. package/src/components/spacing-sizes-control/input-controls/spacing-input-control.js +22 -0
  195. package/src/components/tool-selector/index.js +1 -1
  196. package/src/components/use-block-commands/index.js +91 -46
  197. package/src/components/use-setting/index.js +6 -1
  198. package/src/components/writing-flow/use-arrow-nav.js +4 -0
  199. package/src/hooks/block-rename-ui.js +5 -14
  200. package/src/hooks/{metadata-name.js → block-renaming.js} +4 -7
  201. package/src/hooks/custom-class-name.js +36 -31
  202. package/src/hooks/duotone.js +33 -0
  203. package/src/hooks/index.js +1 -1
  204. package/src/hooks/layout.js +6 -6
  205. package/src/hooks/metadata.js +6 -38
  206. package/src/hooks/utils.js +2 -0
  207. package/src/layouts/grid.js +50 -2
  208. package/src/layouts/utils.js +2 -1
  209. package/src/lock-unlock.js +1 -1
  210. package/src/store/actions.js +25 -8
  211. package/src/utils/parse-css-unit-to-px.js +20 -0
  212. package/src/utils/pasting.js +1 -4
  213. package/src/utils/test/pasting.js +12 -19
  214. package/build/hooks/metadata-name.js.map +0 -1
  215. package/build-module/hooks/metadata-name.js.map +0 -1
@@ -26,6 +26,28 @@ const RANGE_CONTROL_CUSTOM_SETTINGS = {
26
26
  vh: { max: 100, step: 1 },
27
27
  em: { max: 50, step: 0.1 },
28
28
  rem: { max: 50, step: 0.1 },
29
+ svw: { max: 100, step: 1 },
30
+ lvw: { max: 100, step: 1 },
31
+ dvw: { max: 100, step: 1 },
32
+ svh: { max: 100, step: 1 },
33
+ lvh: { max: 100, step: 1 },
34
+ dvh: { max: 100, step: 1 },
35
+ vi: { max: 100, step: 1 },
36
+ svi: { max: 100, step: 1 },
37
+ lvi: { max: 100, step: 1 },
38
+ dvi: { max: 100, step: 1 },
39
+ vb: { max: 100, step: 1 },
40
+ svb: { max: 100, step: 1 },
41
+ lvb: { max: 100, step: 1 },
42
+ dvb: { max: 100, step: 1 },
43
+ vmin: { max: 100, step: 1 },
44
+ svmin: { max: 100, step: 1 },
45
+ lvmin: { max: 100, step: 1 },
46
+ dvmin: { max: 100, step: 1 },
47
+ vmax: { max: 100, step: 1 },
48
+ svmax: { max: 100, step: 1 },
49
+ lvmax: { max: 100, step: 1 },
50
+ dvmax: { max: 100, step: 1 },
29
51
  };
30
52
 
31
53
  /**
@@ -86,10 +108,36 @@ export default function HeightControl( {
86
108
  // Convert to pixel value assuming a root size of 16px.
87
109
  onChange( Math.round( currentValue * 16 ) + newUnit );
88
110
  } else if (
89
- [ 'vh', 'vw', '%' ].includes( newUnit ) &&
111
+ [
112
+ '%',
113
+ 'vw',
114
+ 'svw',
115
+ 'lvw',
116
+ 'dvw',
117
+ 'vh',
118
+ 'svh',
119
+ 'lvh',
120
+ 'dvh',
121
+ 'vi',
122
+ 'svi',
123
+ 'lvi',
124
+ 'dvi',
125
+ 'vb',
126
+ 'svb',
127
+ 'lvb',
128
+ 'dvb',
129
+ 'vmin',
130
+ 'svmin',
131
+ 'lvmin',
132
+ 'dvmin',
133
+ 'vmax',
134
+ 'svmax',
135
+ 'lvmax',
136
+ 'dvmax',
137
+ ].includes( newUnit ) &&
90
138
  currentValue > 100
91
139
  ) {
92
- // When converting to `vh`, `vw`, or `%` units, cap the new value at 100.
140
+ // When converting to `%` or viewport-relative units, cap the new value at 100.
93
141
  onChange( 100 + newUnit );
94
142
  }
95
143
  };
@@ -38,7 +38,14 @@ function bubbleEvent( event, Constructor, frame ) {
38
38
  init[ key ] = event[ key ];
39
39
  }
40
40
 
41
- if ( event instanceof frame.ownerDocument.defaultView.MouseEvent ) {
41
+ // Check if the event is a MouseEvent generated within the iframe.
42
+ // If so, adjust the coordinates to be relative to the position of
43
+ // the iframe. This ensures that components such as Draggable
44
+ // receive coordinates relative to the window, instead of relative
45
+ // to the iframe. Without this, the Draggable event handler would
46
+ // result in components "jumping" position as soon as the user
47
+ // drags over the iframe.
48
+ if ( event instanceof frame.contentDocument.defaultView.MouseEvent ) {
42
49
  const rect = frame.getBoundingClientRect();
43
50
  init.clientX += rect.left;
44
51
  init.clientY += rect.top;
@@ -74,10 +74,6 @@ export default function AspectRatioDropdown( { toggleProps } ) {
74
74
  } }
75
75
  value={ aspect }
76
76
  aspectRatios={ [
77
- {
78
- title: __( '16:10' ),
79
- aspect: 16 / 10,
80
- },
81
77
  {
82
78
  title: __( '16:9' ),
83
79
  aspect: 16 / 9,
@@ -101,10 +97,6 @@ export default function AspectRatioDropdown( { toggleProps } ) {
101
97
  } }
102
98
  value={ aspect }
103
99
  aspectRatios={ [
104
- {
105
- title: __( '10:16' ),
106
- aspect: 10 / 16,
107
- },
108
100
  {
109
101
  title: __( '9:16' ),
110
102
  aspect: 9 / 16,
@@ -67,6 +67,7 @@ export {
67
67
  export { default as Warning } from './warning';
68
68
  export { default as ContrastChecker } from './contrast-checker';
69
69
  export { default as useMultipleOriginColorsAndGradients } from './colors-gradients/use-multiple-origin-colors-and-gradients';
70
+ export { default as UnsupportedBlockDetails } from './unsupported-block-details';
70
71
 
71
72
  export {
72
73
  BottomSheetSettings,
@@ -4,6 +4,26 @@ Renders a link control. A link control is a controlled input which maintains a v
4
4
 
5
5
  It is designed to provide a standardized UI for the creation of a link throughout the Editor, see History section at bottom for further background.
6
6
 
7
+ ## Best Practices
8
+
9
+ ### Ensuring unique instances
10
+
11
+ It is possible that a given editor may render multiple instances of the `<LinkControl>` component. As a result, it is important to ensure each instance is unique across the editor to avoid state "leaking" between components.
12
+
13
+ Why would this happen?
14
+
15
+ React's reconciliation algorithm means that if you return the same element from a component, it keeps the nodes around as an optimization, even if the props change. This means that if you render two (or more) `<LinkControl>`s, it is possible that the `value` from one will appear in the other as you move between them.
16
+
17
+ As a result it is recommended that consumers provide a `key` prop to each instance of `<LinkControl>`:
18
+
19
+ ```jsx
20
+ <LinkControl key="some-unique-key" { ...props } />
21
+ ```
22
+
23
+ This will cause React to return the same component/element type but force remount a new instance, thus avoiding the issues described above.
24
+
25
+ For more information see: https://github.com/WordPress/gutenberg/pull/34742.
26
+
7
27
  ## Relationship to `<URLInput>`
8
28
 
9
29
  As of Gutenberg 7.4, `<LinkControl>` became the default link creation interface within the Block Editor, replacing previous disparate uses of `<URLInput>` and standardizing the UI.
@@ -59,6 +79,29 @@ The resulting default properties of `value` include:
59
79
  - `title` (`string`, optional): Link title.
60
80
  - `opensInNewTab` (`boolean`, optional): Whether link should open in a new browser tab. This value is only assigned when not providing a custom `settings` prop.
61
81
 
82
+ Note: `<LinkControl>` maintains an internal state tracking temporary user edits to the link `value` prior to submission. To avoid unwanted synchronization of this internal value, it is advised that the `value` prop is stablized (likely via memozation) before it is passed to the component. This will avoid unwanted loss of any changes users have may made whilst interacting with the control.
83
+
84
+ ```jsx
85
+ const memoizedValue = useMemo(
86
+ () => ( {
87
+ url: attributes.url,
88
+ type: attributes.type,
89
+ opensInNewTab: attributes.target === '_blank',
90
+ title: attributes.text,
91
+ } ),
92
+ [
93
+ attributes.url,
94
+ attributes.type,
95
+ attributes.target,
96
+ attributes.text,
97
+ ]
98
+ );
99
+
100
+ <LinkControl
101
+ value={ memoizedValue }
102
+ >
103
+ ```
104
+
62
105
  ### settings
63
106
 
64
107
  - Type: `Array`
@@ -11,6 +11,7 @@ import {
11
11
  Button,
12
12
  ExternalLink,
13
13
  __experimentalText as Text,
14
+ Tooltip,
14
15
  } from '@wordpress/components';
15
16
  import { filterURLForDisplay, safeDecodeURI } from '@wordpress/url';
16
17
  import { Icon, globe, info, linkOff, edit } from '@wordpress/icons';
@@ -87,12 +88,17 @@ export default function LinkPreview( {
87
88
  <span className="block-editor-link-control__search-item-details">
88
89
  { ! isEmptyURL ? (
89
90
  <>
90
- <ExternalLink
91
- className="block-editor-link-control__search-item-title"
92
- href={ value.url }
91
+ <Tooltip
92
+ text={ value.url }
93
+ placement="bottom-start"
93
94
  >
94
- { displayTitle }
95
- </ExternalLink>
95
+ <ExternalLink
96
+ className="block-editor-link-control__search-item-title"
97
+ href={ value.url }
98
+ >
99
+ { displayTitle }
100
+ </ExternalLink>
101
+ </Tooltip>
96
102
 
97
103
  { value?.url && displayTitle !== displayURL && (
98
104
  <span className="block-editor-link-control__search-item-info">
@@ -297,9 +297,9 @@ $preview-image-height: 140px;
297
297
 
298
298
  img {
299
299
  display: block; // remove unwanted space below image
300
- max-width: 100%;
301
- height: $preview-image-height; // limit height
302
- max-height: $preview-image-height; // limit height
300
+ width: 100%;
301
+ height: 100%;
302
+ object-fit: contain;
303
303
  }
304
304
  }
305
305
  }
@@ -2056,6 +2056,25 @@ describe( 'Addition Settings UI', () => {
2056
2056
  } )
2057
2057
  );
2058
2058
  } );
2059
+
2060
+ it( 'should show tooltip with full URL alongside filtered display', async () => {
2061
+ const user = userEvent.setup();
2062
+ const url =
2063
+ 'http://www.wordpress.org/wp-content/uploads/a-document.pdf';
2064
+ render( <LinkControl value={ { url } } /> );
2065
+
2066
+ const link = screen.getByRole( 'link' );
2067
+
2068
+ expect( link ).toHaveTextContent( 'a-document.pdf' );
2069
+
2070
+ await user.hover( link );
2071
+
2072
+ expect( await screen.findByRole( 'tooltip' ) ).toHaveTextContent( url );
2073
+
2074
+ await user.unhover( link );
2075
+
2076
+ expect( screen.queryByRole( 'tooltip' ) ).not.toBeInTheDocument();
2077
+ } );
2059
2078
  } );
2060
2079
 
2061
2080
  describe( 'Post types', () => {
@@ -1,21 +1,25 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { useState, useEffect } from '@wordpress/element';
4
+ import { useState } from '@wordpress/element';
5
+
6
+ /**
7
+ * External dependencies
8
+ */
9
+ import fastDeepEqual from 'fast-deep-equal';
5
10
 
6
11
  export default function useInternalValue( value ) {
7
12
  const [ internalValue, setInternalValue ] = useState( value || {} );
13
+ const [ previousValue, setPreviousValue ] = useState( value );
8
14
 
9
15
  // If the value prop changes, update the internal state.
10
- useEffect( () => {
11
- setInternalValue( ( prevValue ) => {
12
- if ( value && value !== prevValue ) {
13
- return value;
14
- }
15
-
16
- return prevValue;
17
- } );
18
- }, [ value ] );
16
+ // See:
17
+ // - https://github.com/WordPress/gutenberg/pull/51387#issuecomment-1722927384.
18
+ // - https://react.dev/reference/react/useState#storing-information-from-previous-renders.
19
+ if ( ! fastDeepEqual( value, previousValue ) ) {
20
+ setPreviousValue( value );
21
+ setInternalValue( value );
22
+ }
19
23
 
20
24
  const setInternalURLInputValue = ( nextValue ) => {
21
25
  setInternalValue( {
@@ -6,7 +6,10 @@ import {
6
6
  useMergeRefs,
7
7
  __experimentalUseFixedWindowList as useFixedWindowList,
8
8
  } from '@wordpress/compose';
9
- import { __experimentalTreeGrid as TreeGrid } from '@wordpress/components';
9
+ import {
10
+ __experimentalTreeGrid as TreeGrid,
11
+ VisuallyHidden,
12
+ } from '@wordpress/components';
10
13
  import { AsyncModeProvider, useSelect } from '@wordpress/data';
11
14
  import deprecated from '@wordpress/deprecated';
12
15
  import {
@@ -257,12 +260,20 @@ function ListViewComponent(
257
260
  return null;
258
261
  }
259
262
 
263
+ const describedById =
264
+ description && `block-editor-list-view-description-${ instanceId }`;
265
+
260
266
  return (
261
267
  <AsyncModeProvider value={ true }>
262
268
  <ListViewDropIndicator
263
269
  listViewRef={ elementRef }
264
270
  blockDropTarget={ blockDropTarget }
265
271
  />
272
+ { description && (
273
+ <VisuallyHidden id={ describedById }>
274
+ { description }
275
+ </VisuallyHidden>
276
+ ) }
266
277
  <TreeGrid
267
278
  id={ id }
268
279
  className="block-editor-list-view-tree"
@@ -272,8 +283,7 @@ function ListViewComponent(
272
283
  onExpandRow={ expandRow }
273
284
  onFocusRow={ focusRow }
274
285
  applicationAriaLabel={ __( 'Block navigation structure' ) }
275
- // eslint-disable-next-line jsx-a11y/aria-props
276
- aria-description={ description }
286
+ aria-describedby={ describedById }
277
287
  >
278
288
  <ListViewContext.Provider value={ contextValue }>
279
289
  <ListViewBranch
@@ -16,7 +16,6 @@ import {
16
16
  ToolbarButton,
17
17
  Dropdown,
18
18
  withFilters,
19
- Tooltip,
20
19
  } from '@wordpress/components';
21
20
  import { useSelect, withDispatch } from '@wordpress/data';
22
21
  import { DOWN } from '@wordpress/keycodes';
@@ -220,19 +219,15 @@ const MediaReplaceFlow = ( {
220
219
  { __( 'Current media URL:' ) }
221
220
  </span>
222
221
 
223
- <Tooltip text={ mediaURL }>
224
- <div>
225
- <LinkControl
226
- value={ { url: mediaURL } }
227
- settings={ [] }
228
- showSuggestions={ false }
229
- onChange={ ( { url } ) => {
230
- onSelectURL( url );
231
- editMediaButtonRef.current.focus();
232
- } }
233
- />
234
- </div>
235
- </Tooltip>
222
+ <LinkControl
223
+ value={ { url: mediaURL } }
224
+ settings={ [] }
225
+ showSuggestions={ false }
226
+ onChange={ ( { url } ) => {
227
+ onSelectURL( url );
228
+ editMediaButtonRef.current.focus();
229
+ } }
230
+ />
236
231
  </form>
237
232
  ) }
238
233
  </>
@@ -31,7 +31,7 @@ const TestWrapper = withRegistryProvider( ( props ) => {
31
31
  props.setRegistry( props.registry );
32
32
  }
33
33
  useBlockSync( props );
34
- return <p>Test.</p>;
34
+ return null;
35
35
  } );
36
36
 
37
37
  describe( 'useBlockSync hook', () => {
@@ -180,7 +180,7 @@ export default function useBlockSync( {
180
180
  // bound sync, unset the outbound value to avoid considering it in
181
181
  // subsequent renders.
182
182
  pendingChanges.current.outgoing = [];
183
- const hadSelecton = hasSelectedBlock();
183
+ const hadSelection = hasSelectedBlock();
184
184
  const selectionAnchor = getSelectionStart();
185
185
  const selectionFocus = getSelectionEnd();
186
186
  setControlledBlocks();
@@ -195,7 +195,7 @@ export default function useBlockSync( {
195
195
  const selectionStillExists = getBlock(
196
196
  selectionAnchor.clientId
197
197
  );
198
- if ( hadSelecton && ! selectionStillExists ) {
198
+ if ( hadSelection && ! selectionStillExists ) {
199
199
  selectBlock( clientId );
200
200
  } else {
201
201
  resetSelection( selectionAnchor, selectionFocus );
@@ -21,6 +21,7 @@ import {
21
21
  __unstableCreateElement,
22
22
  isEmpty,
23
23
  insert,
24
+ remove,
24
25
  create,
25
26
  split,
26
27
  toHTMLString,
@@ -70,6 +71,7 @@ function RichTextWrapper(
70
71
  onSplit,
71
72
  __unstableOnSplitAtEnd: onSplitAtEnd,
72
73
  __unstableOnSplitMiddle: onSplitMiddle,
74
+ __unstableOnSplitAtDoubleLineEnd: onSplitAtDoubleLineEnd,
73
75
  identifier,
74
76
  preserveWhiteSpace,
75
77
  __unstablePastePlainText: pastePlainText,
@@ -339,14 +341,28 @@ function RichTextWrapper(
339
341
  splitStart === splitEnd &&
340
342
  splitEnd === text.length;
341
343
 
342
- if ( shiftKey || ( ! canSplit && ! canSplitAtEnd ) ) {
344
+ if ( shiftKey ) {
343
345
  if ( ! disableLineBreaks ) {
344
346
  onChange( insert( value, '\n' ) );
345
347
  }
346
- } else if ( ! canSplit && canSplitAtEnd ) {
347
- onSplitAtEnd();
348
348
  } else if ( canSplit ) {
349
349
  splitValue( value );
350
+ } else if ( canSplitAtEnd ) {
351
+ onSplitAtEnd();
352
+ } else if (
353
+ // For some blocks it's desirable to split at the end of the
354
+ // block when there are two line breaks at the end of the
355
+ // block, so triple Enter exits the block.
356
+ onSplitAtDoubleLineEnd &&
357
+ splitStart === splitEnd &&
358
+ splitEnd === text.length &&
359
+ text.slice( -2 ) === '\n\n'
360
+ ) {
361
+ value.start = value.end - 2;
362
+ onChange( remove( value ) );
363
+ onSplitAtDoubleLineEnd();
364
+ } else if ( ! disableLineBreaks ) {
365
+ onChange( insert( value, '\n' ) );
350
366
  }
351
367
  },
352
368
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -43,7 +43,7 @@ export function useDelete( props ) {
43
43
  // an intentional user interaction distinguishing between Backspace and
44
44
  // Delete to remove the empty field, but also to avoid merge & remove
45
45
  // causing destruction of two fields (merge, then removed merged).
46
- if ( onRemove && isEmpty( value ) && isReverse ) {
46
+ else if ( onRemove && isEmpty( value ) && isReverse ) {
47
47
  onRemove( ! isReverse );
48
48
  }
49
49
 
@@ -56,26 +56,6 @@ export function usePasteHandler( props ) {
56
56
  return;
57
57
  }
58
58
 
59
- const transformed = formatTypes.reduce(
60
- ( accumlator, { __unstablePasteRule } ) => {
61
- // Only allow one transform.
62
- if ( __unstablePasteRule && accumlator === value ) {
63
- accumlator = __unstablePasteRule( value, {
64
- html,
65
- plainText,
66
- } );
67
- }
68
-
69
- return accumlator;
70
- },
71
- value
72
- );
73
-
74
- if ( transformed !== value ) {
75
- onChange( transformed );
76
- return;
77
- }
78
-
79
59
  const isInternal =
80
60
  event.clipboardData.getData( 'rich-text' ) === 'true';
81
61
 
@@ -139,10 +119,14 @@ export function usePasteHandler( props ) {
139
119
 
140
120
  let mode = onReplace && onSplit ? 'AUTO' : 'INLINE';
141
121
 
122
+ const trimmedPlainText = plainText.trim();
123
+
142
124
  if (
143
125
  __unstableEmbedURLOnPaste &&
144
126
  isEmpty( value ) &&
145
- isURL( plainText.trim() )
127
+ isURL( trimmedPlainText ) &&
128
+ // For the link pasting feature, allow only http(s) protocols.
129
+ /^https?:/.test( trimmedPlainText )
146
130
  ) {
147
131
  mode = 'BLOCKS';
148
132
  }
@@ -156,9 +140,28 @@ export function usePasteHandler( props ) {
156
140
  } );
157
141
 
158
142
  if ( typeof content === 'string' ) {
159
- const valueToInsert = create( { html: content } );
160
- addActiveFormats( valueToInsert, value.activeFormats );
161
- onChange( insert( value, valueToInsert ) );
143
+ const transformed = formatTypes.reduce(
144
+ ( accumlator, { __unstablePasteRule } ) => {
145
+ // Only allow one transform.
146
+ if ( __unstablePasteRule && accumlator === value ) {
147
+ accumlator = __unstablePasteRule( value, {
148
+ html,
149
+ plainText,
150
+ } );
151
+ }
152
+
153
+ return accumlator;
154
+ },
155
+ value
156
+ );
157
+
158
+ if ( transformed !== value ) {
159
+ onChange( transformed );
160
+ } else {
161
+ const valueToInsert = create( { html: content } );
162
+ addActiveFormats( valueToInsert, value.activeFormats );
163
+ onChange( insert( value, valueToInsert ) );
164
+ }
162
165
  } else if ( content.length > 0 ) {
163
166
  if ( onReplace && isEmpty( value ) ) {
164
167
  onReplace( content, content.length - 1, -1 );
@@ -38,6 +38,28 @@ const CUSTOM_VALUE_SETTINGS = {
38
38
  vh: { max: 100, steps: 1 },
39
39
  em: { max: 10, steps: 0.1 },
40
40
  rm: { max: 10, steps: 0.1 },
41
+ svw: { max: 100, steps: 1 },
42
+ lvw: { max: 100, steps: 1 },
43
+ dvw: { max: 100, steps: 1 },
44
+ svh: { max: 100, steps: 1 },
45
+ lvh: { max: 100, steps: 1 },
46
+ dvh: { max: 100, steps: 1 },
47
+ vi: { max: 100, steps: 1 },
48
+ svi: { max: 100, steps: 1 },
49
+ lvi: { max: 100, steps: 1 },
50
+ dvi: { max: 100, steps: 1 },
51
+ vb: { max: 100, steps: 1 },
52
+ svb: { max: 100, steps: 1 },
53
+ lvb: { max: 100, steps: 1 },
54
+ dvb: { max: 100, steps: 1 },
55
+ vmin: { max: 100, steps: 1 },
56
+ svmin: { max: 100, steps: 1 },
57
+ lvmin: { max: 100, steps: 1 },
58
+ dvmin: { max: 100, steps: 1 },
59
+ vmax: { max: 100, steps: 1 },
60
+ svmax: { max: 100, steps: 1 },
61
+ lvmax: { max: 100, steps: 1 },
62
+ dvmax: { max: 100, steps: 1 },
41
63
  };
42
64
 
43
65
  export default function SpacingInputControl( {
@@ -51,7 +51,7 @@ function ToolSelector( props, ref ) {
51
51
  label={ __( 'Tools' ) }
52
52
  />
53
53
  ) }
54
- popoverProps={ { placement: 'bottom-start', variant: undefined } }
54
+ popoverProps={ { placement: 'bottom-start' } }
55
55
  renderContent={ () => (
56
56
  <>
57
57
  <NavigableMenu role="menu" aria-label={ __( 'Tools' ) }>