@wordpress/block-editor 11.3.6 → 11.5.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 (357) hide show
  1. package/CHANGELOG.md +3 -1
  2. package/build/components/block-lock/modal.js +1 -0
  3. package/build/components/block-lock/modal.js.map +1 -1
  4. package/build/components/block-popover/inbetween.js +10 -33
  5. package/build/components/block-popover/inbetween.js.map +1 -1
  6. package/build/components/block-settings-menu/block-settings-dropdown.js +2 -2
  7. package/build/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  8. package/build/components/block-styles/index.js +2 -1
  9. package/build/components/block-styles/index.js.map +1 -1
  10. package/build/components/block-types-list/index.native.js +2 -0
  11. package/build/components/block-types-list/index.native.js.map +1 -1
  12. package/build/components/child-layout-control/index.js +107 -0
  13. package/build/components/child-layout-control/index.js.map +1 -0
  14. package/build/components/colors-gradients/control.js +6 -3
  15. package/build/components/colors-gradients/control.js.map +1 -1
  16. package/build/components/date-format-picker/index.js +3 -3
  17. package/build/components/date-format-picker/index.js.map +1 -1
  18. package/build/components/font-appearance-control/index.js +0 -3
  19. package/build/components/font-appearance-control/index.js.map +1 -1
  20. package/build/components/global-styles/dimensions-panel.js +594 -0
  21. package/build/components/global-styles/dimensions-panel.js.map +1 -0
  22. package/build/components/global-styles/hooks.js +142 -45
  23. package/build/components/global-styles/hooks.js.map +1 -1
  24. package/build/components/global-styles/index.js +38 -0
  25. package/build/components/global-styles/index.js.map +1 -1
  26. package/build/components/global-styles/typography-panel.js +434 -0
  27. package/build/components/global-styles/typography-panel.js.map +1 -0
  28. package/build/components/global-styles/use-global-styles-output.js +7 -3
  29. package/build/components/global-styles/use-global-styles-output.js.map +1 -1
  30. package/build/components/global-styles/utils.js +2 -0
  31. package/build/components/global-styles/utils.js.map +1 -1
  32. package/build/components/iframe/index.js +17 -11
  33. package/build/components/iframe/index.js.map +1 -1
  34. package/build/components/image-editor/aspect-ratio-dropdown.js +2 -1
  35. package/build/components/image-editor/aspect-ratio-dropdown.js.map +1 -1
  36. package/build/components/image-size-control/index.js +6 -11
  37. package/build/components/image-size-control/index.js.map +1 -1
  38. package/build/components/index.native.js +23 -0
  39. package/build/components/index.native.js.map +1 -1
  40. package/build/components/inserter/block-patterns-tab.js +9 -15
  41. package/build/components/inserter/block-patterns-tab.js.map +1 -1
  42. package/build/components/inserter/block-types-tab.native.js +4 -1
  43. package/build/components/inserter/block-types-tab.native.js.map +1 -1
  44. package/build/components/inserter/reusable-blocks-tab.native.js +4 -1
  45. package/build/components/inserter/reusable-blocks-tab.native.js.map +1 -1
  46. package/build/components/inserter/search-results.native.js +4 -1
  47. package/build/components/inserter/search-results.native.js.map +1 -1
  48. package/build/components/inspector-controls/block-support-tools-panel.js +1 -1
  49. package/build/components/inspector-controls/block-support-tools-panel.js.map +1 -1
  50. package/build/components/inspector-controls/fill.js +38 -9
  51. package/build/components/inspector-controls/fill.js.map +1 -1
  52. package/build/components/link-control/index.js +55 -28
  53. package/build/components/link-control/index.js.map +1 -1
  54. package/build/components/link-control/settings-drawer.js +72 -30
  55. package/build/components/link-control/settings-drawer.js.map +1 -1
  56. package/build/components/link-control/settings.js +53 -0
  57. package/build/components/link-control/settings.js.map +1 -0
  58. package/build/components/list-view/block.js +4 -2
  59. package/build/components/list-view/block.js.map +1 -1
  60. package/build/components/list-view/expander.js +2 -1
  61. package/build/components/list-view/expander.js.map +1 -1
  62. package/build/components/list-view/index.js +6 -1
  63. package/build/components/list-view/index.js.map +1 -1
  64. package/build/components/off-canvas-editor/appender.js +5 -10
  65. package/build/components/off-canvas-editor/appender.js.map +1 -1
  66. package/build/components/off-canvas-editor/block-contents.js +3 -2
  67. package/build/components/off-canvas-editor/block-contents.js.map +1 -1
  68. package/build/components/off-canvas-editor/branch.js +1 -0
  69. package/build/components/off-canvas-editor/branch.js.map +1 -1
  70. package/build/components/off-canvas-editor/index.js +7 -3
  71. package/build/components/off-canvas-editor/index.js.map +1 -1
  72. package/build/components/off-canvas-editor/leaf-more-menu.js +24 -4
  73. package/build/components/off-canvas-editor/leaf-more-menu.js.map +1 -1
  74. package/build/components/off-canvas-editor/link-ui.js +2 -0
  75. package/build/components/off-canvas-editor/link-ui.js.map +1 -1
  76. package/build/components/responsive-block-control/index.js +1 -0
  77. package/build/components/responsive-block-control/index.js.map +1 -1
  78. package/build/components/rich-text/index.js +9 -45
  79. package/build/components/rich-text/index.js.map +1 -1
  80. package/build/components/rich-text/use-delete.js +73 -0
  81. package/build/components/rich-text/use-delete.js.map +1 -0
  82. package/build/components/rich-text/use-input-rules.js +14 -6
  83. package/build/components/rich-text/use-input-rules.js.map +1 -1
  84. package/build/components/url-input/index.js +2 -2
  85. package/build/components/url-input/index.js.map +1 -1
  86. package/build/components/writing-flow/use-selection-observer.js +4 -1
  87. package/build/components/writing-flow/use-selection-observer.js.map +1 -1
  88. package/build/hooks/align.js +3 -1
  89. package/build/hooks/align.js.map +1 -1
  90. package/build/hooks/dimensions.js +78 -190
  91. package/build/hooks/dimensions.js.map +1 -1
  92. package/build/hooks/duotone.js +94 -25
  93. package/build/hooks/duotone.js.map +1 -1
  94. package/build/hooks/font-family.js +2 -76
  95. package/build/hooks/font-family.js.map +1 -1
  96. package/build/hooks/font-size.js +3 -51
  97. package/build/hooks/font-size.js.map +1 -1
  98. package/build/hooks/gap.js +0 -201
  99. package/build/hooks/gap.js.map +1 -1
  100. package/build/hooks/index.js +2 -0
  101. package/build/hooks/index.js.map +1 -1
  102. package/build/hooks/layout.js +14 -5
  103. package/build/hooks/layout.js.map +1 -1
  104. package/build/hooks/line-height.js +0 -42
  105. package/build/hooks/line-height.js.map +1 -1
  106. package/build/hooks/margin.js +7 -163
  107. package/build/hooks/margin.js.map +1 -1
  108. package/build/hooks/padding.js +7 -163
  109. package/build/hooks/padding.js.map +1 -1
  110. package/build/hooks/typography.js +98 -128
  111. package/build/hooks/typography.js.map +1 -1
  112. package/build/hooks/utils.js +75 -0
  113. package/build/hooks/utils.js.map +1 -1
  114. package/build/layouts/flex.js +1 -0
  115. package/build/layouts/flex.js.map +1 -1
  116. package/build/store/actions.js +24 -12
  117. package/build/store/actions.js.map +1 -1
  118. package/build/store/reducer.js +53 -47
  119. package/build/store/reducer.js.map +1 -1
  120. package/build/store/selectors.js +22 -1
  121. package/build/store/selectors.js.map +1 -1
  122. package/build/utils/parse-css-unit-to-px.js +36 -3
  123. package/build/utils/parse-css-unit-to-px.js.map +1 -1
  124. package/build-module/components/block-lock/modal.js +1 -0
  125. package/build-module/components/block-lock/modal.js.map +1 -1
  126. package/build-module/components/block-popover/inbetween.js +10 -33
  127. package/build-module/components/block-popover/inbetween.js.map +1 -1
  128. package/build-module/components/block-settings-menu/block-settings-dropdown.js +2 -2
  129. package/build-module/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  130. package/build-module/components/block-styles/index.js +2 -1
  131. package/build-module/components/block-styles/index.js.map +1 -1
  132. package/build-module/components/block-types-list/index.native.js +2 -0
  133. package/build-module/components/block-types-list/index.native.js.map +1 -1
  134. package/build-module/components/child-layout-control/index.js +98 -0
  135. package/build-module/components/child-layout-control/index.js.map +1 -0
  136. package/build-module/components/colors-gradients/control.js +6 -3
  137. package/build-module/components/colors-gradients/control.js.map +1 -1
  138. package/build-module/components/date-format-picker/index.js +4 -4
  139. package/build-module/components/date-format-picker/index.js.map +1 -1
  140. package/build-module/components/font-appearance-control/index.js +2 -1
  141. package/build-module/components/font-appearance-control/index.js.map +1 -1
  142. package/build-module/components/global-styles/dimensions-panel.js +574 -0
  143. package/build-module/components/global-styles/dimensions-panel.js.map +1 -0
  144. package/build-module/components/global-styles/hooks.js +138 -46
  145. package/build-module/components/global-styles/hooks.js.map +1 -1
  146. package/build-module/components/global-styles/index.js +3 -1
  147. package/build-module/components/global-styles/index.js.map +1 -1
  148. package/build-module/components/global-styles/typography-panel.js +415 -0
  149. package/build-module/components/global-styles/typography-panel.js.map +1 -0
  150. package/build-module/components/global-styles/use-global-styles-output.js +7 -3
  151. package/build-module/components/global-styles/use-global-styles-output.js.map +1 -1
  152. package/build-module/components/global-styles/utils.js +2 -0
  153. package/build-module/components/global-styles/utils.js.map +1 -1
  154. package/build-module/components/iframe/index.js +17 -11
  155. package/build-module/components/iframe/index.js.map +1 -1
  156. package/build-module/components/image-editor/aspect-ratio-dropdown.js +2 -1
  157. package/build-module/components/image-editor/aspect-ratio-dropdown.js.map +1 -1
  158. package/build-module/components/image-size-control/index.js +7 -12
  159. package/build-module/components/image-size-control/index.js.map +1 -1
  160. package/build-module/components/index.native.js +2 -1
  161. package/build-module/components/index.native.js.map +1 -1
  162. package/build-module/components/inserter/block-patterns-tab.js +9 -15
  163. package/build-module/components/inserter/block-patterns-tab.js.map +1 -1
  164. package/build-module/components/inserter/block-types-tab.native.js +3 -1
  165. package/build-module/components/inserter/block-types-tab.native.js.map +1 -1
  166. package/build-module/components/inserter/reusable-blocks-tab.native.js +3 -1
  167. package/build-module/components/inserter/reusable-blocks-tab.native.js.map +1 -1
  168. package/build-module/components/inserter/search-results.native.js +3 -1
  169. package/build-module/components/inserter/search-results.native.js.map +1 -1
  170. package/build-module/components/inspector-controls/block-support-tools-panel.js +1 -1
  171. package/build-module/components/inspector-controls/block-support-tools-panel.js.map +1 -1
  172. package/build-module/components/inspector-controls/fill.js +39 -9
  173. package/build-module/components/inspector-controls/fill.js.map +1 -1
  174. package/build-module/components/link-control/index.js +56 -28
  175. package/build-module/components/link-control/index.js.map +1 -1
  176. package/build-module/components/link-control/settings-drawer.js +68 -30
  177. package/build-module/components/link-control/settings-drawer.js.map +1 -1
  178. package/build-module/components/link-control/settings.js +44 -0
  179. package/build-module/components/link-control/settings.js.map +1 -0
  180. package/build-module/components/list-view/block.js +4 -2
  181. package/build-module/components/list-view/block.js.map +1 -1
  182. package/build-module/components/list-view/expander.js +2 -1
  183. package/build-module/components/list-view/expander.js.map +1 -1
  184. package/build-module/components/list-view/index.js +6 -1
  185. package/build-module/components/list-view/index.js.map +1 -1
  186. package/build-module/components/off-canvas-editor/appender.js +5 -10
  187. package/build-module/components/off-canvas-editor/appender.js.map +1 -1
  188. package/build-module/components/off-canvas-editor/block-contents.js +3 -2
  189. package/build-module/components/off-canvas-editor/block-contents.js.map +1 -1
  190. package/build-module/components/off-canvas-editor/branch.js +1 -0
  191. package/build-module/components/off-canvas-editor/branch.js.map +1 -1
  192. package/build-module/components/off-canvas-editor/index.js +7 -3
  193. package/build-module/components/off-canvas-editor/index.js.map +1 -1
  194. package/build-module/components/off-canvas-editor/leaf-more-menu.js +27 -7
  195. package/build-module/components/off-canvas-editor/leaf-more-menu.js.map +1 -1
  196. package/build-module/components/off-canvas-editor/link-ui.js +2 -0
  197. package/build-module/components/off-canvas-editor/link-ui.js.map +1 -1
  198. package/build-module/components/responsive-block-control/index.js +1 -0
  199. package/build-module/components/responsive-block-control/index.js.map +1 -1
  200. package/build-module/components/rich-text/index.js +9 -45
  201. package/build-module/components/rich-text/index.js.map +1 -1
  202. package/build-module/components/rich-text/use-delete.js +62 -0
  203. package/build-module/components/rich-text/use-delete.js.map +1 -0
  204. package/build-module/components/rich-text/use-input-rules.js +14 -6
  205. package/build-module/components/rich-text/use-input-rules.js.map +1 -1
  206. package/build-module/components/url-input/index.js +2 -2
  207. package/build-module/components/url-input/index.js.map +1 -1
  208. package/build-module/components/writing-flow/use-selection-observer.js +4 -1
  209. package/build-module/components/writing-flow/use-selection-observer.js.map +1 -1
  210. package/build-module/hooks/align.js +3 -1
  211. package/build-module/hooks/align.js.map +1 -1
  212. package/build-module/hooks/dimensions.js +81 -187
  213. package/build-module/hooks/dimensions.js.map +1 -1
  214. package/build-module/hooks/duotone.js +86 -24
  215. package/build-module/hooks/duotone.js.map +1 -1
  216. package/build-module/hooks/font-family.js +3 -69
  217. package/build-module/hooks/font-family.js.map +1 -1
  218. package/build-module/hooks/font-size.js +6 -47
  219. package/build-module/hooks/font-size.js.map +1 -1
  220. package/build-module/hooks/gap.js +0 -182
  221. package/build-module/hooks/gap.js.map +1 -1
  222. package/build-module/hooks/index.js +1 -0
  223. package/build-module/hooks/index.js.map +1 -1
  224. package/build-module/hooks/layout.js +14 -5
  225. package/build-module/hooks/layout.js.map +1 -1
  226. package/build-module/hooks/line-height.js +0 -38
  227. package/build-module/hooks/line-height.js.map +1 -1
  228. package/build-module/hooks/margin.js +4 -143
  229. package/build-module/hooks/margin.js.map +1 -1
  230. package/build-module/hooks/padding.js +4 -143
  231. package/build-module/hooks/padding.js.map +1 -1
  232. package/build-module/hooks/typography.js +97 -123
  233. package/build-module/hooks/typography.js.map +1 -1
  234. package/build-module/hooks/utils.js +70 -0
  235. package/build-module/hooks/utils.js.map +1 -1
  236. package/build-module/layouts/flex.js +1 -0
  237. package/build-module/layouts/flex.js.map +1 -1
  238. package/build-module/store/actions.js +24 -12
  239. package/build-module/store/actions.js.map +1 -1
  240. package/build-module/store/reducer.js +53 -45
  241. package/build-module/store/reducer.js.map +1 -1
  242. package/build-module/store/selectors.js +22 -1
  243. package/build-module/store/selectors.js.map +1 -1
  244. package/build-module/utils/parse-css-unit-to-px.js +36 -3
  245. package/build-module/utils/parse-css-unit-to-px.js.map +1 -1
  246. package/build-style/style-rtl.css +54 -64
  247. package/build-style/style.css +54 -64
  248. package/package.json +31 -31
  249. package/src/components/block-draggable/test/__snapshots__/index.native.js.snap +24 -24
  250. package/src/components/block-lock/modal.js +1 -0
  251. package/src/components/block-lock/style.scss +0 -9
  252. package/src/components/block-mobile-toolbar/test/__snapshots__/block-actions-menu.native.js.snap +20 -20
  253. package/src/components/block-mover/test/__snapshots__/index.native.js.snap +15 -15
  254. package/src/components/block-popover/inbetween.js +21 -53
  255. package/src/components/block-settings-menu/block-settings-dropdown.js +4 -1
  256. package/src/components/block-styles/index.js +5 -1
  257. package/src/components/block-types-list/index.native.js +2 -0
  258. package/src/components/child-layout-control/index.js +106 -0
  259. package/src/components/color-palette/test/__snapshots__/control.js.snap +1 -1
  260. package/src/components/colors-gradients/control.js +3 -0
  261. package/src/components/date-format-picker/index.js +6 -8
  262. package/src/components/date-format-picker/style.scss +0 -5
  263. package/src/components/font-appearance-control/index.js +1 -1
  264. package/src/components/global-styles/dimensions-panel.js +627 -0
  265. package/src/components/global-styles/hooks.js +229 -66
  266. package/src/components/global-styles/index.js +9 -0
  267. package/src/components/global-styles/typography-panel.js +428 -0
  268. package/src/components/global-styles/use-global-styles-output.js +10 -5
  269. package/src/components/global-styles/utils.js +2 -0
  270. package/src/components/iframe/index.js +20 -18
  271. package/src/components/image-editor/aspect-ratio-dropdown.js +1 -0
  272. package/src/components/image-size-control/index.js +10 -12
  273. package/src/components/image-size-control/style.scss +3 -21
  274. package/src/components/index.native.js +5 -0
  275. package/src/components/inner-blocks/test/__snapshots__/index.js.snap +1 -1
  276. package/src/components/inserter/block-patterns-tab.js +9 -23
  277. package/src/components/inserter/block-types-tab.native.js +2 -0
  278. package/src/components/inserter/reusable-blocks-tab.native.js +2 -0
  279. package/src/components/inserter/search-results.native.js +2 -0
  280. package/src/components/inserter/test/__snapshots__/index.native.js.snap +15 -15
  281. package/src/components/inspector-controls/block-support-tools-panel.js +0 -1
  282. package/src/components/inspector-controls/fill.js +32 -8
  283. package/src/components/link-control/index.js +69 -34
  284. package/src/components/link-control/settings-drawer.js +85 -29
  285. package/src/components/link-control/settings.js +42 -0
  286. package/src/components/link-control/style.scss +63 -37
  287. package/src/components/link-control/test/index.js +347 -9
  288. package/src/components/list-view/block.js +7 -1
  289. package/src/components/list-view/expander.js +1 -0
  290. package/src/components/list-view/index.js +5 -0
  291. package/src/components/media-replace-flow/style.scss +7 -9
  292. package/src/components/media-replace-flow/test/index.js +1 -1
  293. package/src/components/off-canvas-editor/appender.js +13 -16
  294. package/src/components/off-canvas-editor/block-contents.js +2 -1
  295. package/src/components/off-canvas-editor/branch.js +1 -0
  296. package/src/components/off-canvas-editor/index.js +8 -2
  297. package/src/components/off-canvas-editor/leaf-more-menu.js +52 -15
  298. package/src/components/off-canvas-editor/link-ui.js +2 -0
  299. package/src/components/responsive-block-control/index.js +1 -0
  300. package/src/components/rich-text/index.js +8 -46
  301. package/src/components/rich-text/use-delete.js +59 -0
  302. package/src/components/rich-text/use-input-rules.js +13 -5
  303. package/src/components/url-input/index.js +3 -2
  304. package/src/components/url-popover/stories/index.js +1 -0
  305. package/src/components/writing-flow/use-selection-observer.js +5 -1
  306. package/src/hooks/align.js +1 -1
  307. package/src/hooks/dimensions.js +97 -269
  308. package/src/hooks/duotone.js +100 -30
  309. package/src/hooks/font-family.js +0 -58
  310. package/src/hooks/font-size.js +1 -36
  311. package/src/hooks/gap.js +0 -201
  312. package/src/hooks/index.js +1 -0
  313. package/src/hooks/layout.js +19 -6
  314. package/src/hooks/line-height.js +0 -33
  315. package/src/hooks/margin.js +1 -164
  316. package/src/hooks/padding.js +1 -163
  317. package/src/hooks/test/__snapshots__/align.native.js.snap +24 -24
  318. package/src/hooks/test/duotone.js +102 -0
  319. package/src/hooks/typography.js +112 -213
  320. package/src/hooks/utils.js +90 -0
  321. package/src/layouts/flex.js +1 -0
  322. package/src/store/actions.js +12 -4
  323. package/src/store/reducer.js +68 -43
  324. package/src/store/selectors.js +20 -1
  325. package/src/store/test/actions.js +4 -2
  326. package/src/utils/parse-css-unit-to-px.js +35 -5
  327. package/src/utils/test/parse-css-unit-to-px.js +12 -0
  328. package/build/hooks/child-layout.js +0 -213
  329. package/build/hooks/child-layout.js.map +0 -1
  330. package/build/hooks/font-appearance.js +0 -188
  331. package/build/hooks/font-appearance.js.map +0 -1
  332. package/build/hooks/letter-spacing.js +0 -129
  333. package/build/hooks/letter-spacing.js.map +0 -1
  334. package/build/hooks/min-height.js +0 -139
  335. package/build/hooks/min-height.js.map +0 -1
  336. package/build/hooks/text-decoration.js +0 -130
  337. package/build/hooks/text-decoration.js.map +0 -1
  338. package/build/hooks/text-transform.js +0 -130
  339. package/build/hooks/text-transform.js.map +0 -1
  340. package/build-module/hooks/child-layout.js +0 -193
  341. package/build-module/hooks/child-layout.js.map +0 -1
  342. package/build-module/hooks/font-appearance.js +0 -161
  343. package/build-module/hooks/font-appearance.js.map +0 -1
  344. package/build-module/hooks/letter-spacing.js +0 -107
  345. package/build-module/hooks/letter-spacing.js.map +0 -1
  346. package/build-module/hooks/min-height.js +0 -116
  347. package/build-module/hooks/min-height.js.map +0 -1
  348. package/build-module/hooks/text-decoration.js +0 -108
  349. package/build-module/hooks/text-decoration.js.map +0 -1
  350. package/build-module/hooks/text-transform.js +0 -108
  351. package/build-module/hooks/text-transform.js.map +0 -1
  352. package/src/hooks/child-layout.js +0 -195
  353. package/src/hooks/font-appearance.js +0 -146
  354. package/src/hooks/letter-spacing.js +0 -101
  355. package/src/hooks/min-height.js +0 -104
  356. package/src/hooks/text-decoration.js +0 -102
  357. package/src/hooks/text-transform.js +0 -101
@@ -22,6 +22,20 @@ $preview-image-height: 140px;
22
22
  width: 90vw;
23
23
  max-width: $modal-min-width;
24
24
  }
25
+
26
+ .show-icon-labels & {
27
+ .components-button.has-icon {
28
+ // Hide the button icons when labels are set to display...
29
+ svg {
30
+ display: none;
31
+ }
32
+ // ... and display labels.
33
+ // Uses ::before as ::after is already used for active tab styling.
34
+ &::before {
35
+ content: attr(aria-label);
36
+ }
37
+ }
38
+ }
25
39
  }
26
40
 
27
41
  // Provides positioning context for reset button. Without this then when an
@@ -64,7 +78,6 @@ $preview-image-height: 140px;
64
78
  width: calc(100% - #{$grid-unit-20 * 2});
65
79
  display: block;
66
80
  padding: 11px $grid-unit-20;
67
- padding-right: ( $button-size * $block-editor-link-control-number-of-actions ); // width of reset and submit buttons
68
81
  margin: 0;
69
82
  position: relative;
70
83
  border: 1px solid $gray-300;
@@ -73,24 +86,15 @@ $preview-image-height: 140px;
73
86
  }
74
87
 
75
88
  .block-editor-link-control__search-error {
76
- margin: -$grid-unit-20*0.5 $grid-unit-20 $grid-unit-20; // negative margin to bring the error a bit closer to the button
89
+ margin: -$grid-unit-20 * 0.5 $grid-unit-20 $grid-unit-20; // negative margin to bring the error a bit closer to the button
77
90
  }
78
91
 
79
92
  .block-editor-link-control__search-actions {
80
- position: absolute;
81
- /*
82
- * Actions must be positioned on top of URLInput, since the input will grow
83
- * when suggestions are rendered.
84
- *
85
- * Compensate for:
86
- * - Border (1px)
87
- * - Vertically, for the difference in height between the input (40px) and
88
- * the icon buttons.
89
- * - Horizontally, pad to the minimum of: default input padding, or the
90
- * equivalent of the vertical padding.
91
- */
92
- top: 1px + ( ( 40px - $button-size ) * 0.5 );
93
- right: $grid-unit-20 + 1px + min($grid-unit-10, ( 40px - $button-size ) * 0.5);
93
+ display: flex;
94
+ flex-direction: row-reverse; // put "Cancel" on the left but retain DOM order.
95
+ justify-content: flex-start;
96
+ gap: $grid-unit-10;
97
+ order: 20;
94
98
  }
95
99
 
96
100
  .components-button .block-editor-link-control__search-submit .has-icon {
@@ -113,7 +117,7 @@ $preview-image-height: 140px;
113
117
  }
114
118
 
115
119
  &::before {
116
- height: $grid-unit-20*0.5;
120
+ height: $grid-unit-20 * 0.5;
117
121
  top: 0;
118
122
  bottom: auto;
119
123
  }
@@ -133,7 +137,7 @@ $preview-image-height: 140px;
133
137
 
134
138
  .block-editor-link-control__search-results {
135
139
  margin: 0;
136
- padding: $grid-unit-20*0.5 $grid-unit-20 $grid-unit-20*0.5;
140
+ padding: $grid-unit-20 * 0.5 $grid-unit-20 $grid-unit-20 * 0.5;
137
141
  max-height: 200px;
138
142
  overflow-y: auto; // allow results list to scroll
139
143
 
@@ -234,7 +238,6 @@ $preview-image-height: 140px;
234
238
  max-height: 32px;
235
239
  }
236
240
 
237
-
238
241
  .block-editor-link-control__search-item-info,
239
242
  .block-editor-link-control__search-item-title {
240
243
  overflow: hidden;
@@ -354,16 +357,13 @@ $preview-image-height: 140px;
354
357
  width: 100%;
355
358
  }
356
359
 
357
-
358
360
  .block-editor-link-control__search-item.is-fetching {
359
-
360
361
  .block-editor-link-control__search-item-description {
361
362
  &::before,
362
363
  &::after {
363
364
  animation: loadingpulse 1s linear infinite;
364
365
  animation-delay: 0.5s; // avoid animating for fast network responses
365
366
  }
366
-
367
367
  }
368
368
 
369
369
  .block-editor-link-control__search-item-image {
@@ -413,7 +413,7 @@ $preview-image-height: 140px;
413
413
  &::before {
414
414
  content: "";
415
415
  position: absolute;
416
- top: -#{$block-selected-child-margin*2};
416
+ top: -#{$block-selected-child-margin * 2};
417
417
  left: 0;
418
418
  display: block;
419
419
  width: 100%;
@@ -437,12 +437,49 @@ $preview-image-height: 140px;
437
437
  padding: 10px;
438
438
  }
439
439
 
440
+ .block-editor-link-control__drawer {
441
+ display: flex; // allow for ordering.
442
+ order: 30;
443
+ flex-direction: column;
444
+ flex-basis: 100%; // occupy full width.
445
+ }
446
+
447
+ // Inner div required to avoid padding/margin
448
+ // causing janky animation.
449
+ .block-editor-link-control__drawer-inner {
450
+ display: flex; // allow for ordering.
451
+ flex-direction: column;
452
+ flex-basis: 100%; // occupy full width.
453
+ margin-top: $grid-unit-20;
454
+ padding-top: $grid-unit-20;
455
+ position: relative;
456
+
457
+ &::after {
458
+ content: "";
459
+ display: block;
460
+ height: 1px;
461
+ background-color: $gray-300;
462
+ position: absolute;
463
+ left: -$grid-unit-20;
464
+ right: -$grid-unit-20;
465
+ top: 0;
466
+ }
467
+ }
468
+
440
469
  .block-editor-link-control__tools {
441
470
  display: flex;
471
+ flex-wrap: wrap;
442
472
  align-items: center;
443
- border-top: $border-width solid $gray-300;
473
+ justify-content: space-between;
444
474
  margin: 0;
445
475
  padding: $grid-unit-20;
476
+
477
+ // To hide the horizontal scrollbar on toggle.
478
+ // Margin and padding are needed to prevent cutoff of the toggle button focus outline.
479
+ // See: https://github.com/WordPress/gutenberg/pull/47986
480
+ margin-top: calc(var(--wp-admin-border-width-focus) * -1);
481
+ padding-top: var(--wp-admin-border-width-focus);
482
+ overflow: hidden;
446
483
  }
447
484
 
448
485
  .block-editor-link-control__unlink {
@@ -454,11 +491,6 @@ $preview-image-height: 140px;
454
491
  flex: 1;
455
492
  margin: 0;
456
493
 
457
-
458
- :last-child {
459
- margin-bottom: 0;
460
- }
461
-
462
494
  .is-alternate & {
463
495
  border-top: $border-width solid $gray-900;
464
496
  }
@@ -467,7 +499,7 @@ $preview-image-height: 140px;
467
499
  .block-editor-link-control__setting {
468
500
  margin-bottom: $grid-unit-20;
469
501
 
470
- :last-child {
502
+ &.block-editor-link-control__setting:last-child {
471
503
  margin-bottom: 0;
472
504
  }
473
505
  }
@@ -479,14 +511,8 @@ $preview-image-height: 140px;
479
511
  position: absolute;
480
512
  left: auto;
481
513
  bottom: auto;
482
- /*
483
- * Position spinner to the left of the actions.
484
- *
485
- * Compensate for:
486
- * - Input padding right ($button-size)
487
- */
488
514
  top: calc(50% - #{$spinner-size} / 2);
489
- right: $button-size;
515
+ right: $grid-unit-20;
490
516
  }
491
517
  }
492
518
 
@@ -53,6 +53,11 @@ jest.mock( '@wordpress/data/src/components/use-dispatch', () => ( {
53
53
 
54
54
  jest.useRealTimers();
55
55
 
56
+ jest.mock( '@wordpress/compose', () => ( {
57
+ ...jest.requireActual( '@wordpress/compose' ),
58
+ useReducedMotion: jest.fn( () => true ),
59
+ } ) );
60
+
56
61
  beforeEach( () => {
57
62
  // Setup a DOM element as a render target.
58
63
  mockFetchSearchSuggestions.mockImplementation( fetchFauxEntitySuggestions );
@@ -137,7 +142,122 @@ describe( 'Basic rendering', () => {
137
142
  // Search Input UI.
138
143
  const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
139
144
 
140
- expect( searchInput ).toBeInTheDocument();
145
+ expect( searchInput ).toBeVisible();
146
+ } );
147
+
148
+ it( 'should have aria-owns attribute to follow the ARIA 1.0 pattern', () => {
149
+ render( <LinkControl /> );
150
+
151
+ // Search Input UI.
152
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
153
+
154
+ expect( searchInput ).toBeVisible();
155
+ // Make sure we use the ARIA 1.0 pattern with aria-owns.
156
+ // See https://github.com/WordPress/gutenberg/issues/47147
157
+ expect( searchInput ).not.toHaveAttribute( 'aria-controls' );
158
+ expect( searchInput ).toHaveAttribute( 'aria-owns' );
159
+ } );
160
+
161
+ it( 'should have aria-selected attribute only on the highlighted item', async () => {
162
+ const user = userEvent.setup();
163
+
164
+ let resolver;
165
+ mockFetchSearchSuggestions.mockImplementation(
166
+ () =>
167
+ new Promise( ( resolve ) => {
168
+ resolver = resolve;
169
+ } )
170
+ );
171
+
172
+ render( <LinkControl /> );
173
+
174
+ // Search Input UI.
175
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
176
+
177
+ // Simulate searching for a term.
178
+ await user.type( searchInput, 'Hello' );
179
+
180
+ // Wait for the spinner SVG icon to be rendered.
181
+ expect( await screen.findByRole( 'presentation' ) ).toBeVisible();
182
+ // Check the suggestions list is not rendered yet.
183
+ expect( screen.queryByRole( 'listbox' ) ).not.toBeInTheDocument();
184
+
185
+ // Make the search suggestions fetch return a response.
186
+ resolver( fauxEntitySuggestions );
187
+
188
+ const resultsList = await screen.findByRole( 'listbox', {
189
+ name: 'Search results for "Hello"',
190
+ } );
191
+
192
+ // Check the suggestions list is rendered.
193
+ expect( resultsList ).toBeVisible();
194
+ // Check the spinner SVG icon is not rendered any longer.
195
+ expect( screen.queryByRole( 'presentation' ) ).not.toBeInTheDocument();
196
+
197
+ const searchResultElements =
198
+ within( resultsList ).getAllByRole( 'option' );
199
+
200
+ expect( searchResultElements ).toHaveLength(
201
+ // The fauxEntitySuggestions length plus the 'Press ENTER to add this link' button.
202
+ fauxEntitySuggestions.length + 1
203
+ );
204
+
205
+ // Step down into the search results, highlighting the first result item.
206
+ triggerArrowDown( searchInput );
207
+
208
+ const firstSearchSuggestion = searchResultElements[ 0 ];
209
+ const secondSearchSuggestion = searchResultElements[ 1 ];
210
+
211
+ let selectedSearchResultElement = screen.getByRole( 'option', {
212
+ selected: true,
213
+ } );
214
+
215
+ // We should have highlighted the first item using the keyboard.
216
+ expect( selectedSearchResultElement ).toEqual( firstSearchSuggestion );
217
+
218
+ // Check the aria-selected attribute is set only on the highlighted item.
219
+ expect( firstSearchSuggestion ).toHaveAttribute(
220
+ 'aria-selected',
221
+ 'true'
222
+ );
223
+ // Check the aria-selected attribute is omitted on the non-highlighted items.
224
+ expect( secondSearchSuggestion ).not.toHaveAttribute( 'aria-selected' );
225
+
226
+ // Step down into the search results, highlighting the second result item.
227
+ triggerArrowDown( searchInput );
228
+
229
+ selectedSearchResultElement = screen.getByRole( 'option', {
230
+ selected: true,
231
+ } );
232
+
233
+ // We should have highlighted the first item using the keyboard.
234
+ expect( selectedSearchResultElement ).toEqual( secondSearchSuggestion );
235
+
236
+ // Check the aria-selected attribute is omitted on non-highlighted items.
237
+ expect( firstSearchSuggestion ).not.toHaveAttribute( 'aria-selected' );
238
+ // Check the aria-selected attribute is set only on the highlighted item.
239
+ expect( secondSearchSuggestion ).toHaveAttribute(
240
+ 'aria-selected',
241
+ 'true'
242
+ );
243
+
244
+ // Step up into the search results, highlighting the first result item.
245
+ triggerArrowUp( searchInput );
246
+
247
+ selectedSearchResultElement = screen.getByRole( 'option', {
248
+ selected: true,
249
+ } );
250
+
251
+ // We should be back to highlighting the first search result again.
252
+ expect( selectedSearchResultElement ).toEqual( firstSearchSuggestion );
253
+
254
+ // Check the aria-selected attribute is set only on the highlighted item.
255
+ expect( firstSearchSuggestion ).toHaveAttribute(
256
+ 'aria-selected',
257
+ 'true'
258
+ );
259
+ // Check the aria-selected attribute is omitted on non-highlighted items.
260
+ expect( secondSearchSuggestion ).not.toHaveAttribute( 'aria-selected' );
141
261
  } );
142
262
 
143
263
  it( 'should not render protocol in links', async () => {
@@ -537,7 +657,7 @@ describe( 'Manual link entry', () => {
537
657
  } );
538
658
 
539
659
  let submitButton = screen.getByRole( 'button', {
540
- name: 'Submit',
660
+ name: 'Apply',
541
661
  } );
542
662
 
543
663
  expect( submitButton ).toBeDisabled();
@@ -555,11 +675,11 @@ describe( 'Manual link entry', () => {
555
675
  await user.keyboard( '[Enter]' );
556
676
 
557
677
  submitButton = screen.getByRole( 'button', {
558
- name: 'Submit',
678
+ name: 'Apply',
559
679
  } );
560
680
 
561
681
  // Verify the UI hasn't allowed submission.
562
- expect( searchInput ).toBeInTheDocument();
682
+ expect( searchInput ).toBeVisible();
563
683
  expect( submitButton ).toBeDisabled();
564
684
  expect( submitButton ).toBeVisible();
565
685
  }
@@ -578,7 +698,7 @@ describe( 'Manual link entry', () => {
578
698
  } );
579
699
 
580
700
  let submitButton = screen.queryByRole( 'button', {
581
- name: 'Submit',
701
+ name: 'Apply',
582
702
  } );
583
703
 
584
704
  expect( submitButton ).toBeDisabled();
@@ -597,17 +717,150 @@ describe( 'Manual link entry', () => {
597
717
  await user.click( submitButton );
598
718
 
599
719
  submitButton = screen.queryByRole( 'button', {
600
- name: 'Submit',
720
+ name: 'Apply',
601
721
  } );
602
722
 
603
723
  // Verify the UI hasn't allowed submission.
604
- expect( searchInput ).toBeInTheDocument();
724
+ expect( searchInput ).toBeVisible();
605
725
  expect( submitButton ).toBeDisabled();
606
726
  expect( submitButton ).toBeVisible();
607
727
  }
608
728
  );
609
729
  } );
610
730
 
731
+ describe( 'Handling cancellation', () => {
732
+ it( 'should allow cancellation of the link creation process and reset any entered values', async () => {
733
+ const user = userEvent.setup();
734
+ const mockOnRemove = jest.fn();
735
+ const mockOnCancel = jest.fn();
736
+
737
+ render( <LinkControl onRemove={ mockOnRemove } /> );
738
+
739
+ // Search Input UI.
740
+ const searchInput = screen.getByRole( 'combobox', {
741
+ name: 'URL',
742
+ } );
743
+
744
+ const cancelButton = screen.queryByRole( 'button', {
745
+ name: 'Cancel',
746
+ } );
747
+
748
+ expect( cancelButton ).toBeEnabled();
749
+ expect( cancelButton ).toBeVisible();
750
+
751
+ // Simulate adding a link for a term.
752
+ await user.type( searchInput, 'https://www.wordpress.org' );
753
+
754
+ // Attempt to submit the empty search value in the input.
755
+ await user.click( cancelButton );
756
+
757
+ // Verify the consumer can handle the cancellation.
758
+ expect( mockOnRemove ).toHaveBeenCalled();
759
+
760
+ // Ensure optional callback is not called.
761
+ expect( mockOnCancel ).not.toHaveBeenCalled();
762
+
763
+ expect( searchInput ).toHaveValue( '' );
764
+ } );
765
+
766
+ it( 'should allow cancellation of the link editing process and reset any entered values', async () => {
767
+ const user = userEvent.setup();
768
+ const initialLink = fauxEntitySuggestions[ 0 ];
769
+
770
+ const LinkControlConsumer = () => {
771
+ const [ link, setLink ] = useState( initialLink );
772
+
773
+ return (
774
+ <LinkControl
775
+ value={ link }
776
+ onChange={ ( suggestion ) => {
777
+ setLink( suggestion );
778
+ } }
779
+ hasTextControl
780
+ />
781
+ );
782
+ };
783
+
784
+ render( <LinkControlConsumer /> );
785
+
786
+ let linkPreview = screen.getByLabelText( 'Currently selected' );
787
+
788
+ expect( linkPreview ).toBeInTheDocument();
789
+
790
+ // Click the "Edit" button to trigger into the editing mode.
791
+ let editButton = screen.queryByRole( 'button', {
792
+ name: 'Edit',
793
+ } );
794
+
795
+ await user.click( editButton );
796
+
797
+ await toggleSettingsDrawer( user );
798
+
799
+ let searchInput = screen.getByRole( 'combobox', {
800
+ name: 'URL',
801
+ } );
802
+
803
+ let textInput = screen.getByRole( 'textbox', {
804
+ name: 'Text',
805
+ } );
806
+
807
+ // Make a change to the search input.
808
+ await user.type( searchInput, 'This URL value was changed!' );
809
+
810
+ // Make a change to the text input.
811
+ await user.type( textInput, 'This text value was changed!' );
812
+
813
+ const cancelButton = screen.queryByRole( 'button', {
814
+ name: 'Cancel',
815
+ } );
816
+
817
+ // Cancel the editing process.
818
+ await user.click( cancelButton );
819
+
820
+ linkPreview = screen.getByLabelText( 'Currently selected' );
821
+
822
+ expect( linkPreview ).toBeInTheDocument();
823
+
824
+ // Re-query the edit button as it's been replaced.
825
+ editButton = screen.queryByRole( 'button', {
826
+ name: 'Edit',
827
+ } );
828
+
829
+ await user.click( editButton );
830
+
831
+ await toggleSettingsDrawer( user );
832
+
833
+ // Re-query the inputs as they have been replaced.
834
+ searchInput = screen.getByRole( 'combobox', {
835
+ name: 'URL',
836
+ } );
837
+
838
+ textInput = screen.getByRole( 'textbox', {
839
+ name: 'Text',
840
+ } );
841
+
842
+ // Expect to see the original link values and **not** the changed values.
843
+ expect( searchInput ).toHaveValue( initialLink.url );
844
+ expect( textInput ).toHaveValue( initialLink.text );
845
+ } );
846
+
847
+ it( 'should call onCancel callback when cancelling if provided', async () => {
848
+ const user = userEvent.setup();
849
+ const mockOnCancel = jest.fn();
850
+
851
+ render( <LinkControl onCancel={ mockOnCancel } /> );
852
+
853
+ const cancelButton = screen.queryByRole( 'button', {
854
+ name: 'Cancel',
855
+ } );
856
+
857
+ await user.click( cancelButton );
858
+
859
+ // Verify the consumer can handle the cancellation.
860
+ expect( mockOnCancel ).toHaveBeenCalled();
861
+ } );
862
+ } );
863
+
611
864
  describe( 'Alternative link protocols and formats', () => {
612
865
  it.each( [
613
866
  [ 'mailto:example123456@wordpress.org', 'mailto' ],
@@ -1405,6 +1658,62 @@ describe( 'Selecting links', () => {
1405
1658
  } );
1406
1659
 
1407
1660
  describe( 'Addition Settings UI', () => {
1661
+ it( 'should not show a means to toggle the link settings when not editing a link', async () => {
1662
+ const selectedLink = fauxEntitySuggestions[ 0 ];
1663
+
1664
+ const LinkControlConsumer = () => {
1665
+ const [ link ] = useState( selectedLink );
1666
+
1667
+ return <LinkControl value={ link } />;
1668
+ };
1669
+
1670
+ render( <LinkControlConsumer /> );
1671
+
1672
+ const settingsToggle = screen.queryByRole( 'button', {
1673
+ name: 'Link Settings',
1674
+ ariaControls: 'link-settings-1',
1675
+ } );
1676
+
1677
+ expect( settingsToggle ).not.toBeInTheDocument();
1678
+ } );
1679
+ it( 'should provides a means to toggle the link settings', async () => {
1680
+ const selectedLink = fauxEntitySuggestions[ 0 ];
1681
+
1682
+ const LinkControlConsumer = () => {
1683
+ const [ link ] = useState( selectedLink );
1684
+
1685
+ return <LinkControl value={ link } forceIsEditingLink />;
1686
+ };
1687
+
1688
+ render( <LinkControlConsumer /> );
1689
+
1690
+ const user = userEvent.setup();
1691
+
1692
+ const settingsToggle = screen.queryByRole( 'button', {
1693
+ name: 'Link Settings',
1694
+ ariaControls: 'link-settings-1',
1695
+ } );
1696
+
1697
+ expect( settingsToggle ).toHaveAttribute( 'aria-expanded', 'false' );
1698
+
1699
+ expect( settingsToggle ).toBeVisible();
1700
+
1701
+ await user.click( settingsToggle );
1702
+
1703
+ expect( settingsToggle ).toHaveAttribute( 'aria-expanded', 'true' );
1704
+
1705
+ const newTabSettingInput = screen.getByRole( 'checkbox', {
1706
+ name: 'Open in new tab',
1707
+ } );
1708
+
1709
+ expect( newTabSettingInput ).toBeVisible();
1710
+
1711
+ await user.click( settingsToggle );
1712
+
1713
+ expect( settingsToggle ).toHaveAttribute( 'aria-expanded', 'false' );
1714
+ expect( newTabSettingInput ).not.toBeVisible();
1715
+ } );
1716
+
1408
1717
  it( 'should display "New Tab" setting (in "off" mode) by default when a link is selected', async () => {
1409
1718
  const selectedLink = fauxEntitySuggestions[ 0 ];
1410
1719
  const expectedSettingText = 'Open in new tab';
@@ -1412,11 +1721,15 @@ describe( 'Addition Settings UI', () => {
1412
1721
  const LinkControlConsumer = () => {
1413
1722
  const [ link ] = useState( selectedLink );
1414
1723
 
1415
- return <LinkControl value={ link } />;
1724
+ return <LinkControl value={ link } forceIsEditingLink />;
1416
1725
  };
1417
1726
 
1418
1727
  render( <LinkControlConsumer /> );
1419
1728
 
1729
+ const user = userEvent.setup();
1730
+
1731
+ await toggleSettingsDrawer( user );
1732
+
1420
1733
  const newTabSettingLabel = screen.getByText( expectedSettingText );
1421
1734
  expect( newTabSettingLabel ).toBeVisible();
1422
1735
 
@@ -1449,12 +1762,17 @@ describe( 'Addition Settings UI', () => {
1449
1762
  <LinkControl
1450
1763
  value={ { ...link, newTab: false, noFollow: true } }
1451
1764
  settings={ customSettings }
1765
+ forceIsEditingLink
1452
1766
  />
1453
1767
  );
1454
1768
  };
1455
1769
 
1456
1770
  render( <LinkControlConsumer /> );
1457
1771
 
1772
+ const user = userEvent.setup();
1773
+
1774
+ await toggleSettingsDrawer( user );
1775
+
1458
1776
  expect( screen.queryAllByRole( 'checkbox' ) ).toHaveLength( 2 );
1459
1777
 
1460
1778
  expect(
@@ -1803,6 +2121,10 @@ describe( 'Controlling link title text', () => {
1803
2121
  />
1804
2122
  );
1805
2123
 
2124
+ const user = userEvent.setup();
2125
+
2126
+ await toggleSettingsDrawer( user );
2127
+
1806
2128
  expect(
1807
2129
  screen.queryByRole( 'textbox', { name: 'Text' } )
1808
2130
  ).toBeVisible();
@@ -1829,6 +2151,10 @@ describe( 'Controlling link title text', () => {
1829
2151
  />
1830
2152
  );
1831
2153
 
2154
+ const user = userEvent.setup();
2155
+
2156
+ await toggleSettingsDrawer( user );
2157
+
1832
2158
  const textInput = screen.queryByRole( 'textbox', {
1833
2159
  name: 'Text',
1834
2160
  } );
@@ -1851,6 +2177,8 @@ describe( 'Controlling link title text', () => {
1851
2177
  />
1852
2178
  );
1853
2179
 
2180
+ await toggleSettingsDrawer( user );
2181
+
1854
2182
  const textInput = screen.queryByRole( 'textbox', { name: 'Text' } );
1855
2183
 
1856
2184
  await user.clear( textInput );
@@ -1859,7 +2187,7 @@ describe( 'Controlling link title text', () => {
1859
2187
  expect( textInput ).toHaveValue( textValue );
1860
2188
 
1861
2189
  const submitButton = screen.queryByRole( 'button', {
1862
- name: 'Submit',
2190
+ name: 'Apply',
1863
2191
  } );
1864
2192
 
1865
2193
  await user.click( submitButton );
@@ -1885,6 +2213,8 @@ describe( 'Controlling link title text', () => {
1885
2213
  />
1886
2214
  );
1887
2215
 
2216
+ await toggleSettingsDrawer( user );
2217
+
1888
2218
  const textInput = screen.queryByRole( 'textbox', { name: 'Text' } );
1889
2219
 
1890
2220
  expect( textInput ).toBeVisible();
@@ -1908,3 +2238,11 @@ describe( 'Controlling link title text', () => {
1908
2238
  ).not.toBeInTheDocument();
1909
2239
  } );
1910
2240
  } );
2241
+
2242
+ async function toggleSettingsDrawer( user ) {
2243
+ const settingsToggle = screen.queryByRole( 'button', {
2244
+ name: 'Link Settings',
2245
+ } );
2246
+
2247
+ await user.click( settingsToggle );
2248
+ }
@@ -231,6 +231,10 @@ function ListViewBlock( {
231
231
  selectedClientIds,
232
232
  } );
233
233
 
234
+ // Detect if there is a block in the canvas currently being edited and multi-selection is not happening.
235
+ const currentlyEditingBlockInCanvas =
236
+ isSelected && selectedClientIds.length === 1;
237
+
234
238
  return (
235
239
  <ListViewLeaf
236
240
  className={ classes }
@@ -268,7 +272,9 @@ function ListViewBlock( {
268
272
  siblingBlockCount={ siblingBlockCount }
269
273
  level={ level }
270
274
  ref={ ref }
271
- tabIndex={ tabIndex }
275
+ tabIndex={
276
+ currentlyEditingBlockInCanvas ? 0 : tabIndex
277
+ }
272
278
  onFocus={ onFocus }
273
279
  isExpanded={ isExpanded }
274
280
  selectedClientIds={ selectedClientIds }
@@ -19,6 +19,7 @@ export default function ListViewExpander( { onClick } ) {
19
19
  className="block-editor-list-view__expander"
20
20
  onClick={ ( event ) => onClick( event, { forceToggle: true } ) }
21
21
  aria-hidden="true"
22
+ data-testid="list-view-expander"
22
23
  >
23
24
  <Icon icon={ isRTL() ? chevronLeftSmall : chevronRightSmall } />
24
25
  </span>