@wordpress/block-editor 10.4.0 → 11.0.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 (638) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +0 -1
  3. package/build/autocompleters/block.js +2 -6
  4. package/build/autocompleters/block.js.map +1 -1
  5. package/build/autocompleters/link.js +2 -0
  6. package/build/autocompleters/link.js.map +1 -1
  7. package/build/components/block-card/index.js +51 -3
  8. package/build/components/block-card/index.js.map +1 -1
  9. package/build/components/block-draggable/index.native.js +46 -39
  10. package/build/components/block-draggable/index.native.js.map +1 -1
  11. package/build/components/block-edit/edit.js +4 -3
  12. package/build/components/block-edit/edit.js.map +1 -1
  13. package/build/components/block-edit/edit.native.js +4 -7
  14. package/build/components/block-edit/edit.native.js.map +1 -1
  15. package/build/components/block-inspector/index.js +35 -33
  16. package/build/components/block-inspector/index.js.map +1 -1
  17. package/build/components/block-list/block-list-context.native.js +5 -8
  18. package/build/components/block-list/block-list-context.native.js.map +1 -1
  19. package/build/components/block-list/block.js +55 -24
  20. package/build/components/block-list/block.js.map +1 -1
  21. package/build/components/block-list/block.native.js +61 -28
  22. package/build/components/block-list/block.native.js.map +1 -1
  23. package/build/components/block-lock/menu-item.js +1 -1
  24. package/build/components/block-lock/menu-item.js.map +1 -1
  25. package/build/components/block-lock/modal.js +16 -9
  26. package/build/components/block-lock/modal.js.map +1 -1
  27. package/build/components/block-mobile-toolbar/block-actions-menu.native.js +12 -4
  28. package/build/components/block-mobile-toolbar/block-actions-menu.native.js.map +1 -1
  29. package/build/components/block-pattern-setup/index.js +3 -2
  30. package/build/components/block-pattern-setup/index.js.map +1 -1
  31. package/build/components/block-patterns-list/index.js +33 -11
  32. package/build/components/block-patterns-list/index.js.map +1 -1
  33. package/build/components/block-preview/auto.js +9 -3
  34. package/build/components/block-preview/auto.js.map +1 -1
  35. package/build/components/block-preview/index.js +5 -9
  36. package/build/components/block-preview/index.js.map +1 -1
  37. package/build/components/block-settings-menu/block-settings-dropdown.js +5 -2
  38. package/build/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  39. package/build/components/block-styles/utils.js +3 -3
  40. package/build/components/block-styles/utils.js.map +1 -1
  41. package/build/components/block-switcher/index.js +19 -4
  42. package/build/components/block-switcher/index.js.map +1 -1
  43. package/build/components/block-toolbar/index.js +5 -1
  44. package/build/components/block-toolbar/index.js.map +1 -1
  45. package/build/components/block-tools/insertion-point.js +8 -49
  46. package/build/components/block-tools/insertion-point.js.map +1 -1
  47. package/build/components/block-tools/selected-block-popover.js +27 -4
  48. package/build/components/block-tools/selected-block-popover.js.map +1 -1
  49. package/build/components/block-variation-picker/index.js +1 -2
  50. package/build/components/block-variation-picker/index.js.map +1 -1
  51. package/build/components/colors/with-colors.js +4 -3
  52. package/build/components/colors/with-colors.js.map +1 -1
  53. package/build/components/font-sizes/fluid-utils.js +24 -40
  54. package/build/components/font-sizes/fluid-utils.js.map +1 -1
  55. package/build/components/font-sizes/with-font-sizes.js +7 -5
  56. package/build/components/font-sizes/with-font-sizes.js.map +1 -1
  57. package/build/components/height-control/index.js +115 -0
  58. package/build/components/height-control/index.js.map +1 -0
  59. package/build/components/iframe/index.js +11 -8
  60. package/build/components/iframe/index.js.map +1 -1
  61. package/build/components/image-editor/use-save-image.js +2 -0
  62. package/build/components/image-editor/use-save-image.js.map +1 -1
  63. package/build/components/image-editor/zoom-dropdown.js +1 -0
  64. package/build/components/image-editor/zoom-dropdown.js.map +1 -1
  65. package/build/components/index.js +18 -0
  66. package/build/components/index.js.map +1 -1
  67. package/build/components/inner-blocks/index.js +25 -9
  68. package/build/components/inner-blocks/index.js.map +1 -1
  69. package/build/components/inner-blocks/use-inner-block-template-sync.js +25 -10
  70. package/build/components/inner-blocks/use-inner-block-template-sync.js.map +1 -1
  71. package/build/components/inserter/block-patterns-explorer/sidebar.js +1 -0
  72. package/build/components/inserter/block-patterns-explorer/sidebar.js.map +1 -1
  73. package/build/components/inserter/block-patterns-tab.js +25 -46
  74. package/build/components/inserter/block-patterns-tab.js.map +1 -1
  75. package/build/components/inserter/block-types-tab.js +3 -1
  76. package/build/components/inserter/block-types-tab.js.map +1 -1
  77. package/build/components/inserter/hooks/use-debounced-input.js +27 -0
  78. package/build/components/inserter/hooks/use-debounced-input.js.map +1 -0
  79. package/build/components/inserter/index.js +8 -3
  80. package/build/components/inserter/index.js.map +1 -1
  81. package/build/components/inserter/index.native.js +3 -4
  82. package/build/components/inserter/index.native.js.map +1 -1
  83. package/build/components/inserter/media-tab/hooks.js +103 -0
  84. package/build/components/inserter/media-tab/hooks.js.map +1 -0
  85. package/build/components/inserter/media-tab/index.js +32 -0
  86. package/build/components/inserter/media-tab/index.js.map +1 -0
  87. package/build/components/inserter/media-tab/media-list.js +100 -0
  88. package/build/components/inserter/media-tab/media-list.js.map +1 -0
  89. package/build/components/inserter/media-tab/media-panel.js +96 -0
  90. package/build/components/inserter/media-tab/media-panel.js.map +1 -0
  91. package/build/components/inserter/media-tab/media-tab.js +120 -0
  92. package/build/components/inserter/media-tab/media-tab.js.map +1 -0
  93. package/build/components/inserter/media-tab/utils.js +54 -0
  94. package/build/components/inserter/media-tab/utils.js.map +1 -0
  95. package/build/components/inserter/menu.js +35 -12
  96. package/build/components/inserter/menu.js.map +1 -1
  97. package/build/components/inserter/mobile-tab-navigation.js +70 -0
  98. package/build/components/inserter/mobile-tab-navigation.js.map +1 -0
  99. package/build/components/inserter/quick-inserter.js +1 -0
  100. package/build/components/inserter/quick-inserter.js.map +1 -1
  101. package/build/components/inserter/reusable-blocks-tab.js +4 -1
  102. package/build/components/inserter/reusable-blocks-tab.js.map +1 -1
  103. package/build/components/inserter/search-results.js +3 -1
  104. package/build/components/inserter/search-results.js.map +1 -1
  105. package/build/components/inserter/tabs.js +16 -2
  106. package/build/components/inserter/tabs.js.map +1 -1
  107. package/build/components/inserter-list-item/index.js +4 -1
  108. package/build/components/inserter-list-item/index.js.map +1 -1
  109. package/build/components/inspector-controls/groups.js +2 -0
  110. package/build/components/inspector-controls/groups.js.map +1 -1
  111. package/build/components/inspector-controls-tabs/advanced-controls-panel.js +46 -0
  112. package/build/components/inspector-controls-tabs/advanced-controls-panel.js.map +1 -0
  113. package/build/components/inspector-controls-tabs/index.js +71 -0
  114. package/build/components/inspector-controls-tabs/index.js.map +1 -0
  115. package/build/components/inspector-controls-tabs/settings-tab.js +28 -0
  116. package/build/components/inspector-controls-tabs/settings-tab.js.map +1 -0
  117. package/build/components/inspector-controls-tabs/styles-tab.js +61 -0
  118. package/build/components/inspector-controls-tabs/styles-tab.js.map +1 -0
  119. package/build/components/inspector-controls-tabs/use-inspector-controls-tabs.js +97 -0
  120. package/build/components/inspector-controls-tabs/use-inspector-controls-tabs.js.map +1 -0
  121. package/build/components/inspector-controls-tabs/use-is-list-view-tab-disabled.js +18 -0
  122. package/build/components/inspector-controls-tabs/use-is-list-view-tab-disabled.js.map +1 -0
  123. package/build/components/inspector-controls-tabs/utils.js +37 -0
  124. package/build/components/inspector-controls-tabs/utils.js.map +1 -0
  125. package/build/components/link-control/index.js +19 -34
  126. package/build/components/link-control/index.js.map +1 -1
  127. package/build/components/link-control/search-input.js +1 -2
  128. package/build/components/link-control/search-input.js.map +1 -1
  129. package/build/components/link-control/use-internal-input-value.js +26 -0
  130. package/build/components/link-control/use-internal-input-value.js.map +1 -0
  131. package/build/components/list-view/block.js +10 -5
  132. package/build/components/list-view/block.js.map +1 -1
  133. package/build/components/list-view/branch.js +22 -15
  134. package/build/components/list-view/branch.js.map +1 -1
  135. package/build/components/media-upload/index.native.js +2 -3
  136. package/build/components/media-upload/index.native.js.map +1 -1
  137. package/build/components/off-canvas-editor/appender.js +104 -0
  138. package/build/components/off-canvas-editor/appender.js.map +1 -0
  139. package/build/components/off-canvas-editor/block-contents.js +100 -0
  140. package/build/components/off-canvas-editor/block-contents.js.map +1 -0
  141. package/build/components/off-canvas-editor/block-edit-button.js +50 -0
  142. package/build/components/off-canvas-editor/block-edit-button.js.map +1 -0
  143. package/build/components/off-canvas-editor/block-select-button.js +119 -0
  144. package/build/components/off-canvas-editor/block-select-button.js.map +1 -0
  145. package/build/components/off-canvas-editor/block.js +324 -0
  146. package/build/components/off-canvas-editor/block.js.map +1 -0
  147. package/build/components/off-canvas-editor/branch.js +179 -0
  148. package/build/components/off-canvas-editor/branch.js.map +1 -0
  149. package/build/components/off-canvas-editor/context.js +19 -0
  150. package/build/components/off-canvas-editor/context.js.map +1 -0
  151. package/build/components/off-canvas-editor/drop-indicator.js +118 -0
  152. package/build/components/off-canvas-editor/drop-indicator.js.map +1 -0
  153. package/build/components/off-canvas-editor/expander.js +41 -0
  154. package/build/components/off-canvas-editor/expander.js.map +1 -0
  155. package/build/components/off-canvas-editor/index.js +213 -0
  156. package/build/components/off-canvas-editor/index.js.map +1 -0
  157. package/build/components/off-canvas-editor/leaf.js +60 -0
  158. package/build/components/off-canvas-editor/leaf.js.map +1 -0
  159. package/build/components/off-canvas-editor/link-ui.js +185 -0
  160. package/build/components/off-canvas-editor/link-ui.js.map +1 -0
  161. package/build/components/off-canvas-editor/update-attributes.js +108 -0
  162. package/build/components/off-canvas-editor/update-attributes.js.map +1 -0
  163. package/build/components/off-canvas-editor/use-block-selection.js +139 -0
  164. package/build/components/off-canvas-editor/use-block-selection.js.map +1 -0
  165. package/build/components/off-canvas-editor/use-list-view-client-ids.js +33 -0
  166. package/build/components/off-canvas-editor/use-list-view-client-ids.js.map +1 -0
  167. package/build/components/off-canvas-editor/use-list-view-drop-zone.js +235 -0
  168. package/build/components/off-canvas-editor/use-list-view-drop-zone.js.map +1 -0
  169. package/build/components/off-canvas-editor/use-list-view-expand-selected-item.js +60 -0
  170. package/build/components/off-canvas-editor/use-list-view-expand-selected-item.js.map +1 -0
  171. package/build/components/off-canvas-editor/utils.js +60 -0
  172. package/build/components/off-canvas-editor/utils.js.map +1 -0
  173. package/build/components/rich-text/format-toolbar/index.js +8 -4
  174. package/build/components/rich-text/format-toolbar/index.js.map +1 -1
  175. package/build/components/rich-text/index.js +3 -3
  176. package/build/components/rich-text/index.js.map +1 -1
  177. package/build/components/rich-text/index.native.js +0 -2
  178. package/build/components/rich-text/index.native.js.map +1 -1
  179. package/build/components/rich-text/use-insert-replacement-text.js +43 -0
  180. package/build/components/rich-text/use-insert-replacement-text.js.map +1 -0
  181. package/build/components/rich-text/use-undo-automatic-change.js +9 -1
  182. package/build/components/rich-text/use-undo-automatic-change.js.map +1 -1
  183. package/build/components/rich-text/utils.js +1 -19
  184. package/build/components/rich-text/utils.js.map +1 -1
  185. package/build/components/spacing-sizes-control/spacing-input-control.js +12 -3
  186. package/build/components/spacing-sizes-control/spacing-input-control.js.map +1 -1
  187. package/build/components/ungroup-button/index.native.js +4 -2
  188. package/build/components/ungroup-button/index.native.js.map +1 -1
  189. package/build/components/url-input/index.js +46 -43
  190. package/build/components/url-input/index.js.map +1 -1
  191. package/build/components/url-popover/index.js +31 -2
  192. package/build/components/url-popover/index.js.map +1 -1
  193. package/build/components/use-block-display-information/index.js +8 -4
  194. package/build/components/use-block-display-information/index.js.map +1 -1
  195. package/build/components/use-setting/index.js +10 -2
  196. package/build/components/use-setting/index.js.map +1 -1
  197. package/build/hooks/child-layout.js +209 -0
  198. package/build/hooks/child-layout.js.map +1 -0
  199. package/build/hooks/color-panel.js +17 -1
  200. package/build/hooks/color-panel.js.map +1 -1
  201. package/build/hooks/color.js +1 -1
  202. package/build/hooks/color.js.map +1 -1
  203. package/build/hooks/content-lock-ui.js +14 -7
  204. package/build/hooks/content-lock-ui.js.map +1 -1
  205. package/build/hooks/dimensions.js +65 -16
  206. package/build/hooks/dimensions.js.map +1 -1
  207. package/build/hooks/layout.js +59 -3
  208. package/build/hooks/layout.js.map +1 -1
  209. package/build/hooks/margin.js +4 -2
  210. package/build/hooks/margin.js.map +1 -1
  211. package/build/hooks/min-height.js +139 -0
  212. package/build/hooks/min-height.js.map +1 -0
  213. package/build/hooks/padding.js +4 -2
  214. package/build/hooks/padding.js.map +1 -1
  215. package/build/hooks/style.js +3 -2
  216. package/build/hooks/style.js.map +1 -1
  217. package/build/layouts/flex.js +22 -21
  218. package/build/layouts/flex.js.map +1 -1
  219. package/build/store/actions.js +26 -0
  220. package/build/store/actions.js.map +1 -1
  221. package/build/store/reducer.js +420 -265
  222. package/build/store/reducer.js.map +1 -1
  223. package/build/store/selectors.js +73 -49
  224. package/build/store/selectors.js.map +1 -1
  225. package/build/utils/sorting.js +63 -0
  226. package/build/utils/sorting.js.map +1 -0
  227. package/build-module/autocompleters/block.js +2 -6
  228. package/build-module/autocompleters/block.js.map +1 -1
  229. package/build-module/autocompleters/link.js +2 -0
  230. package/build-module/autocompleters/link.js.map +1 -1
  231. package/build-module/components/block-card/index.js +45 -3
  232. package/build-module/components/block-card/index.js.map +1 -1
  233. package/build-module/components/block-draggable/index.native.js +40 -31
  234. package/build-module/components/block-draggable/index.native.js.map +1 -1
  235. package/build-module/components/block-edit/edit.js +4 -2
  236. package/build-module/components/block-edit/edit.js.map +1 -1
  237. package/build-module/components/block-edit/edit.native.js +4 -6
  238. package/build-module/components/block-edit/edit.native.js.map +1 -1
  239. package/build-module/components/block-inspector/index.js +32 -30
  240. package/build-module/components/block-inspector/index.js.map +1 -1
  241. package/build-module/components/block-list/block-list-context.native.js +5 -8
  242. package/build-module/components/block-list/block-list-context.native.js.map +1 -1
  243. package/build-module/components/block-list/block.js +55 -25
  244. package/build-module/components/block-list/block.js.map +1 -1
  245. package/build-module/components/block-list/block.native.js +61 -28
  246. package/build-module/components/block-list/block.native.js.map +1 -1
  247. package/build-module/components/block-lock/menu-item.js +2 -2
  248. package/build-module/components/block-lock/menu-item.js.map +1 -1
  249. package/build-module/components/block-lock/modal.js +17 -10
  250. package/build-module/components/block-lock/modal.js.map +1 -1
  251. package/build-module/components/block-mobile-toolbar/block-actions-menu.native.js +13 -6
  252. package/build-module/components/block-mobile-toolbar/block-actions-menu.native.js.map +1 -1
  253. package/build-module/components/block-pattern-setup/index.js +3 -2
  254. package/build-module/components/block-pattern-setup/index.js.map +1 -1
  255. package/build-module/components/block-patterns-list/index.js +35 -13
  256. package/build-module/components/block-patterns-list/index.js.map +1 -1
  257. package/build-module/components/block-preview/auto.js +9 -3
  258. package/build-module/components/block-preview/auto.js.map +1 -1
  259. package/build-module/components/block-preview/index.js +5 -8
  260. package/build-module/components/block-preview/index.js.map +1 -1
  261. package/build-module/components/block-settings-menu/block-settings-dropdown.js +5 -2
  262. package/build-module/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  263. package/build-module/components/block-styles/utils.js +3 -3
  264. package/build-module/components/block-styles/utils.js.map +1 -1
  265. package/build-module/components/block-switcher/index.js +19 -4
  266. package/build-module/components/block-switcher/index.js.map +1 -1
  267. package/build-module/components/block-toolbar/index.js +6 -2
  268. package/build-module/components/block-toolbar/index.js.map +1 -1
  269. package/build-module/components/block-tools/insertion-point.js +8 -49
  270. package/build-module/components/block-tools/insertion-point.js.map +1 -1
  271. package/build-module/components/block-tools/selected-block-popover.js +27 -5
  272. package/build-module/components/block-tools/selected-block-popover.js.map +1 -1
  273. package/build-module/components/block-variation-picker/index.js +1 -2
  274. package/build-module/components/block-variation-picker/index.js.map +1 -1
  275. package/build-module/components/colors/with-colors.js +5 -4
  276. package/build-module/components/colors/with-colors.js.map +1 -1
  277. package/build-module/components/font-sizes/fluid-utils.js +24 -40
  278. package/build-module/components/font-sizes/fluid-utils.js.map +1 -1
  279. package/build-module/components/font-sizes/with-font-sizes.js +8 -6
  280. package/build-module/components/font-sizes/with-font-sizes.js.map +1 -1
  281. package/build-module/components/height-control/index.js +103 -0
  282. package/build-module/components/height-control/index.js.map +1 -0
  283. package/build-module/components/iframe/index.js +11 -8
  284. package/build-module/components/iframe/index.js.map +1 -1
  285. package/build-module/components/image-editor/use-save-image.js +2 -0
  286. package/build-module/components/image-editor/use-save-image.js.map +1 -1
  287. package/build-module/components/image-editor/zoom-dropdown.js +1 -0
  288. package/build-module/components/image-editor/zoom-dropdown.js.map +1 -1
  289. package/build-module/components/index.js +2 -0
  290. package/build-module/components/index.js.map +1 -1
  291. package/build-module/components/inner-blocks/index.js +27 -11
  292. package/build-module/components/inner-blocks/index.js.map +1 -1
  293. package/build-module/components/inner-blocks/use-inner-block-template-sync.js +23 -10
  294. package/build-module/components/inner-blocks/use-inner-block-template-sync.js.map +1 -1
  295. package/build-module/components/inserter/block-patterns-explorer/sidebar.js +1 -0
  296. package/build-module/components/inserter/block-patterns-explorer/sidebar.js.map +1 -1
  297. package/build-module/components/inserter/block-patterns-tab.js +27 -49
  298. package/build-module/components/inserter/block-patterns-tab.js.map +1 -1
  299. package/build-module/components/inserter/block-types-tab.js +3 -2
  300. package/build-module/components/inserter/block-types-tab.js.map +1 -1
  301. package/build-module/components/inserter/hooks/use-debounced-input.js +18 -0
  302. package/build-module/components/inserter/hooks/use-debounced-input.js.map +1 -0
  303. package/build-module/components/inserter/index.js +8 -3
  304. package/build-module/components/inserter/index.js.map +1 -1
  305. package/build-module/components/inserter/index.native.js +3 -5
  306. package/build-module/components/inserter/index.native.js.map +1 -1
  307. package/build-module/components/inserter/media-tab/hooks.js +89 -0
  308. package/build-module/components/inserter/media-tab/hooks.js.map +1 -0
  309. package/build-module/components/inserter/media-tab/index.js +4 -0
  310. package/build-module/components/inserter/media-tab/index.js.map +1 -0
  311. package/build-module/components/inserter/media-tab/media-list.js +86 -0
  312. package/build-module/components/inserter/media-tab/media-list.js.map +1 -0
  313. package/build-module/components/inserter/media-tab/media-panel.js +77 -0
  314. package/build-module/components/inserter/media-tab/media-panel.js.map +1 -0
  315. package/build-module/components/inserter/media-tab/media-tab.js +100 -0
  316. package/build-module/components/inserter/media-tab/media-tab.js.map +1 -0
  317. package/build-module/components/inserter/media-tab/utils.js +45 -0
  318. package/build-module/components/inserter/media-tab/utils.js.map +1 -0
  319. package/build-module/components/inserter/menu.js +33 -12
  320. package/build-module/components/inserter/menu.js.map +1 -1
  321. package/build-module/components/inserter/mobile-tab-navigation.js +61 -0
  322. package/build-module/components/inserter/mobile-tab-navigation.js.map +1 -0
  323. package/build-module/components/inserter/quick-inserter.js +1 -0
  324. package/build-module/components/inserter/quick-inserter.js.map +1 -1
  325. package/build-module/components/inserter/reusable-blocks-tab.js +3 -1
  326. package/build-module/components/inserter/reusable-blocks-tab.js.map +1 -1
  327. package/build-module/components/inserter/search-results.js +3 -2
  328. package/build-module/components/inserter/search-results.js.map +1 -1
  329. package/build-module/components/inserter/tabs.js +15 -2
  330. package/build-module/components/inserter/tabs.js.map +1 -1
  331. package/build-module/components/inserter-list-item/index.js +5 -2
  332. package/build-module/components/inserter-list-item/index.js.map +1 -1
  333. package/build-module/components/inspector-controls/groups.js +2 -0
  334. package/build-module/components/inspector-controls/groups.js.map +1 -1
  335. package/build-module/components/inspector-controls-tabs/advanced-controls-panel.js +32 -0
  336. package/build-module/components/inspector-controls-tabs/advanced-controls-panel.js.map +1 -0
  337. package/build-module/components/inspector-controls-tabs/index.js +56 -0
  338. package/build-module/components/inspector-controls-tabs/index.js.map +1 -0
  339. package/build-module/components/inspector-controls-tabs/settings-tab.js +17 -0
  340. package/build-module/components/inspector-controls-tabs/settings-tab.js.map +1 -0
  341. package/build-module/components/inspector-controls-tabs/styles-tab.js +46 -0
  342. package/build-module/components/inspector-controls-tabs/styles-tab.js.map +1 -0
  343. package/build-module/components/inspector-controls-tabs/use-inspector-controls-tabs.js +81 -0
  344. package/build-module/components/inspector-controls-tabs/use-inspector-controls-tabs.js.map +1 -0
  345. package/build-module/components/inspector-controls-tabs/use-is-list-view-tab-disabled.js +8 -0
  346. package/build-module/components/inspector-controls-tabs/use-is-list-view-tab-disabled.js.map +1 -0
  347. package/build-module/components/inspector-controls-tabs/utils.js +26 -0
  348. package/build-module/components/inspector-controls-tabs/utils.js.map +1 -0
  349. package/build-module/components/link-control/index.js +18 -34
  350. package/build-module/components/link-control/index.js.map +1 -1
  351. package/build-module/components/link-control/search-input.js +1 -2
  352. package/build-module/components/link-control/search-input.js.map +1 -1
  353. package/build-module/components/link-control/use-internal-input-value.js +18 -0
  354. package/build-module/components/link-control/use-internal-input-value.js.map +1 -0
  355. package/build-module/components/list-view/block.js +10 -5
  356. package/build-module/components/list-view/block.js.map +1 -1
  357. package/build-module/components/list-view/branch.js +21 -14
  358. package/build-module/components/list-view/branch.js.map +1 -1
  359. package/build-module/components/media-upload/index.native.js +2 -4
  360. package/build-module/components/media-upload/index.native.js.map +1 -1
  361. package/build-module/components/off-canvas-editor/appender.js +89 -0
  362. package/build-module/components/off-canvas-editor/appender.js.map +1 -0
  363. package/build-module/components/off-canvas-editor/block-contents.js +85 -0
  364. package/build-module/components/off-canvas-editor/block-contents.js.map +1 -0
  365. package/build-module/components/off-canvas-editor/block-edit-button.js +35 -0
  366. package/build-module/components/off-canvas-editor/block-edit-button.js.map +1 -0
  367. package/build-module/components/off-canvas-editor/block-select-button.js +101 -0
  368. package/build-module/components/off-canvas-editor/block-select-button.js.map +1 -0
  369. package/build-module/components/off-canvas-editor/block.js +298 -0
  370. package/build-module/components/off-canvas-editor/block.js.map +1 -0
  371. package/build-module/components/off-canvas-editor/branch.js +164 -0
  372. package/build-module/components/off-canvas-editor/branch.js.map +1 -0
  373. package/build-module/components/off-canvas-editor/context.js +7 -0
  374. package/build-module/components/off-canvas-editor/context.js.map +1 -0
  375. package/build-module/components/off-canvas-editor/drop-indicator.js +111 -0
  376. package/build-module/components/off-canvas-editor/drop-indicator.js.map +1 -0
  377. package/build-module/components/off-canvas-editor/expander.js +32 -0
  378. package/build-module/components/off-canvas-editor/expander.js.map +1 -0
  379. package/build-module/components/off-canvas-editor/index.js +189 -0
  380. package/build-module/components/off-canvas-editor/index.js.map +1 -0
  381. package/build-module/components/off-canvas-editor/leaf.js +45 -0
  382. package/build-module/components/off-canvas-editor/leaf.js.map +1 -0
  383. package/build-module/components/off-canvas-editor/link-ui.js +165 -0
  384. package/build-module/components/off-canvas-editor/link-ui.js.map +1 -0
  385. package/build-module/components/off-canvas-editor/update-attributes.js +97 -0
  386. package/build-module/components/off-canvas-editor/update-attributes.js.map +1 -0
  387. package/build-module/components/off-canvas-editor/use-block-selection.js +124 -0
  388. package/build-module/components/off-canvas-editor/use-block-selection.js.map +1 -0
  389. package/build-module/components/off-canvas-editor/use-list-view-client-ids.js +24 -0
  390. package/build-module/components/off-canvas-editor/use-list-view-client-ids.js.map +1 -0
  391. package/build-module/components/off-canvas-editor/use-list-view-drop-zone.js +220 -0
  392. package/build-module/components/off-canvas-editor/use-list-view-drop-zone.js.map +1 -0
  393. package/build-module/components/off-canvas-editor/use-list-view-expand-selected-item.js +50 -0
  394. package/build-module/components/off-canvas-editor/use-list-view-expand-selected-item.js.map +1 -0
  395. package/build-module/components/off-canvas-editor/utils.js +44 -0
  396. package/build-module/components/off-canvas-editor/utils.js.map +1 -0
  397. package/build-module/components/rich-text/format-toolbar/index.js +6 -2
  398. package/build-module/components/rich-text/format-toolbar/index.js.map +1 -1
  399. package/build-module/components/rich-text/index.js +2 -3
  400. package/build-module/components/rich-text/index.js.map +1 -1
  401. package/build-module/components/rich-text/index.native.js +0 -2
  402. package/build-module/components/rich-text/index.native.js.map +1 -1
  403. package/build-module/components/rich-text/use-insert-replacement-text.js +33 -0
  404. package/build-module/components/rich-text/use-insert-replacement-text.js.map +1 -0
  405. package/build-module/components/rich-text/use-undo-automatic-change.js +9 -1
  406. package/build-module/components/rich-text/use-undo-automatic-change.js.map +1 -1
  407. package/build-module/components/rich-text/utils.js +1 -16
  408. package/build-module/components/rich-text/utils.js.map +1 -1
  409. package/build-module/components/spacing-sizes-control/spacing-input-control.js +12 -3
  410. package/build-module/components/spacing-sizes-control/spacing-input-control.js.map +1 -1
  411. package/build-module/components/ungroup-button/index.native.js +3 -2
  412. package/build-module/components/ungroup-button/index.native.js.map +1 -1
  413. package/build-module/components/url-input/index.js +46 -43
  414. package/build-module/components/url-input/index.js.map +1 -1
  415. package/build-module/components/url-popover/index.js +30 -3
  416. package/build-module/components/url-popover/index.js.map +1 -1
  417. package/build-module/components/use-block-display-information/index.js +9 -5
  418. package/build-module/components/use-block-display-information/index.js.map +1 -1
  419. package/build-module/components/use-setting/index.js +9 -2
  420. package/build-module/components/use-setting/index.js.map +1 -1
  421. package/build-module/hooks/child-layout.js +189 -0
  422. package/build-module/hooks/child-layout.js.map +1 -0
  423. package/build-module/hooks/color-panel.js +17 -1
  424. package/build-module/hooks/color-panel.js.map +1 -1
  425. package/build-module/hooks/color.js +1 -1
  426. package/build-module/hooks/color.js.map +1 -1
  427. package/build-module/hooks/content-lock-ui.js +16 -9
  428. package/build-module/hooks/content-lock-ui.js.map +1 -1
  429. package/build-module/hooks/dimensions.js +60 -16
  430. package/build-module/hooks/dimensions.js.map +1 -1
  431. package/build-module/hooks/layout.js +57 -2
  432. package/build-module/hooks/layout.js.map +1 -1
  433. package/build-module/hooks/margin.js +4 -2
  434. package/build-module/hooks/margin.js.map +1 -1
  435. package/build-module/hooks/min-height.js +116 -0
  436. package/build-module/hooks/min-height.js.map +1 -0
  437. package/build-module/hooks/padding.js +4 -2
  438. package/build-module/hooks/padding.js.map +1 -1
  439. package/build-module/hooks/style.js +4 -3
  440. package/build-module/hooks/style.js.map +1 -1
  441. package/build-module/layouts/flex.js +23 -22
  442. package/build-module/layouts/flex.js.map +1 -1
  443. package/build-module/store/actions.js +22 -0
  444. package/build-module/store/actions.js.map +1 -1
  445. package/build-module/store/reducer.js +415 -265
  446. package/build-module/store/reducer.js.map +1 -1
  447. package/build-module/store/selectors.js +66 -48
  448. package/build-module/store/selectors.js.map +1 -1
  449. package/build-module/utils/sorting.js +56 -0
  450. package/build-module/utils/sorting.js.map +1 -0
  451. package/build-style/content-rtl.css +701 -0
  452. package/build-style/content.css +701 -0
  453. package/build-style/default-editor-styles-rtl.css +14 -0
  454. package/build-style/default-editor-styles.css +14 -0
  455. package/build-style/style-rtl.css +305 -668
  456. package/build-style/style.css +305 -668
  457. package/package.json +32 -30
  458. package/src/autocompleters/block.js +2 -6
  459. package/src/autocompleters/link.js +2 -0
  460. package/src/components/alignment-control/README.md +1 -1
  461. package/src/components/alignment-control/test/index.js +4 -1
  462. package/src/components/block-alignment-control/test/index.js +4 -1
  463. package/src/components/block-alignment-control/test/index.native.js +4 -4
  464. package/src/components/block-card/index.js +46 -2
  465. package/src/components/block-card/style.scss +4 -0
  466. package/src/components/block-content-overlay/{style.scss → content.scss} +7 -1
  467. package/src/components/block-draggable/content.scss +20 -0
  468. package/src/components/block-draggable/index.native.js +54 -40
  469. package/src/components/block-draggable/style.scss +0 -21
  470. package/src/components/block-draggable/test/helpers.native.js +7 -9
  471. package/src/components/block-draggable/test/index.native.js +35 -45
  472. package/src/components/block-edit/edit.js +5 -2
  473. package/src/components/block-edit/edit.native.js +5 -6
  474. package/src/components/block-inspector/index.js +96 -81
  475. package/src/components/block-inspector/style.scss +9 -1
  476. package/src/components/block-list/block-list-context.native.js +5 -8
  477. package/src/components/block-list/block.js +74 -23
  478. package/src/components/block-list/block.native.js +78 -23
  479. package/src/components/block-list/{style.scss → content.scss} +11 -20
  480. package/src/components/block-list-appender/{style.scss → content.scss} +0 -0
  481. package/src/components/block-lock/menu-item.js +5 -2
  482. package/src/components/block-lock/modal.js +19 -36
  483. package/src/components/block-lock/style.scss +8 -17
  484. package/src/components/block-mobile-toolbar/block-actions-menu.native.js +24 -6
  485. package/src/components/block-mover/style.scss +0 -1
  486. package/src/components/block-mover/test/__snapshots__/index.native.js.snap +0 -2
  487. package/src/components/block-pattern-setup/index.js +2 -1
  488. package/src/components/block-patterns-list/index.js +47 -24
  489. package/src/components/block-popover/style.scss +1 -1
  490. package/src/components/block-preview/README.md +15 -10
  491. package/src/components/block-preview/auto.js +7 -1
  492. package/src/components/block-preview/content.scss +4 -0
  493. package/src/components/block-preview/index.js +7 -12
  494. package/src/components/block-preview/style.scss +0 -7
  495. package/src/components/block-preview/test/index.js +18 -35
  496. package/src/components/block-selection-clearer/test/index.js +12 -12
  497. package/src/components/block-settings-menu/block-settings-dropdown.js +32 -20
  498. package/src/components/block-styles/utils.js +3 -3
  499. package/src/components/block-switcher/index.js +19 -4
  500. package/src/components/block-switcher/test/index.js +4 -0
  501. package/src/components/block-toolbar/index.js +12 -5
  502. package/src/components/block-toolbar/style.scss +10 -0
  503. package/src/components/block-tools/insertion-point.js +3 -47
  504. package/src/components/block-tools/selected-block-popover.js +80 -34
  505. package/src/components/block-tools/style.scss +27 -5
  506. package/src/components/block-variation-picker/index.js +1 -4
  507. package/src/components/block-vertical-alignment-control/test/index.js +4 -1
  508. package/src/components/colors/with-colors.js +13 -23
  509. package/src/components/default-block-appender/{style.scss → content.scss} +1 -0
  510. package/src/components/font-sizes/fluid-utils.js +37 -64
  511. package/src/components/font-sizes/test/fluid-utils.js +5 -5
  512. package/src/components/font-sizes/with-font-sizes.js +14 -11
  513. package/src/components/height-control/index.js +123 -0
  514. package/src/components/height-control/stories/index.js +21 -0
  515. package/src/components/height-control/style.scss +5 -0
  516. package/src/components/iframe/index.js +25 -18
  517. package/src/components/image-editor/use-save-image.js +2 -0
  518. package/src/components/image-editor/zoom-dropdown.js +1 -0
  519. package/src/components/index.js +2 -0
  520. package/src/components/inner-blocks/{style.scss → content.scss} +0 -0
  521. package/src/components/inner-blocks/index.js +30 -10
  522. package/src/components/inner-blocks/use-inner-block-template-sync.js +28 -10
  523. package/src/components/inserter/block-patterns-explorer/sidebar.js +1 -0
  524. package/src/components/inserter/block-patterns-tab.js +28 -71
  525. package/src/components/inserter/block-types-tab.js +3 -2
  526. package/src/components/inserter/hooks/use-debounced-input.js +17 -0
  527. package/src/components/inserter/index.js +10 -2
  528. package/src/components/inserter/index.native.js +1 -1
  529. package/src/components/inserter/media-tab/hooks.js +88 -0
  530. package/src/components/inserter/media-tab/index.js +3 -0
  531. package/src/components/inserter/media-tab/media-list.js +93 -0
  532. package/src/components/inserter/media-tab/media-panel.js +83 -0
  533. package/src/components/inserter/media-tab/media-tab.js +135 -0
  534. package/src/components/inserter/media-tab/utils.js +37 -0
  535. package/src/components/inserter/menu.js +55 -13
  536. package/src/components/inserter/mobile-tab-navigation.js +85 -0
  537. package/src/components/inserter/quick-inserter.js +1 -0
  538. package/src/components/inserter/reusable-blocks-tab.js +4 -2
  539. package/src/components/inserter/search-results.js +3 -2
  540. package/src/components/inserter/stories/index.js +1 -1
  541. package/src/components/inserter/stories/{fixtures.js → utils/fixtures.js} +0 -0
  542. package/src/components/inserter/style.scss +184 -18
  543. package/src/components/inserter/tabs.js +12 -1
  544. package/src/components/inserter/test/reusable-blocks-tab.js +14 -57
  545. package/src/components/inserter-list-item/index.js +11 -1
  546. package/src/components/inserter-list-item/style.scss +26 -0
  547. package/src/components/inspector-controls/groups.js +2 -0
  548. package/src/components/inspector-controls-tabs/advanced-controls-panel.js +37 -0
  549. package/src/components/inspector-controls-tabs/index.js +62 -0
  550. package/src/components/inspector-controls-tabs/settings-tab.js +18 -0
  551. package/src/components/inspector-controls-tabs/styles-tab.js +51 -0
  552. package/src/components/inspector-controls-tabs/use-inspector-controls-tabs.js +89 -0
  553. package/src/components/inspector-controls-tabs/use-is-list-view-tab-disabled.js +9 -0
  554. package/src/components/inspector-controls-tabs/utils.js +28 -0
  555. package/src/components/line-height-control/test/index.js +5 -5
  556. package/src/components/link-control/README.md +1 -1
  557. package/src/components/link-control/index.js +24 -39
  558. package/src/components/link-control/search-input.js +1 -2
  559. package/src/components/link-control/test/index.js +400 -582
  560. package/src/components/link-control/use-internal-input-value.js +22 -0
  561. package/src/components/list-view/block.js +7 -3
  562. package/src/components/list-view/branch.js +21 -14
  563. package/src/components/list-view/style.scss +20 -9
  564. package/src/components/media-placeholder/{style.scss → content.scss} +0 -0
  565. package/src/components/media-replace-flow/test/index.js +37 -9
  566. package/src/components/media-upload/test/index.native.js +2 -0
  567. package/src/components/off-canvas-editor/README.md +5 -0
  568. package/src/components/off-canvas-editor/appender.js +93 -0
  569. package/src/components/off-canvas-editor/block-contents.js +89 -0
  570. package/src/components/off-canvas-editor/block-edit-button.js +27 -0
  571. package/src/components/off-canvas-editor/block-select-button.js +113 -0
  572. package/src/components/off-canvas-editor/block.js +401 -0
  573. package/src/components/off-canvas-editor/branch.js +208 -0
  574. package/src/components/off-canvas-editor/context.js +8 -0
  575. package/src/components/off-canvas-editor/drop-indicator.js +126 -0
  576. package/src/components/off-canvas-editor/expander.js +26 -0
  577. package/src/components/off-canvas-editor/index.js +242 -0
  578. package/src/components/off-canvas-editor/leaf.js +52 -0
  579. package/src/components/off-canvas-editor/link-ui.js +166 -0
  580. package/src/components/off-canvas-editor/style.scss +26 -0
  581. package/src/components/off-canvas-editor/test/utils.js +50 -0
  582. package/src/components/off-canvas-editor/update-attributes.js +99 -0
  583. package/src/components/off-canvas-editor/use-block-selection.js +169 -0
  584. package/src/components/off-canvas-editor/use-list-view-client-ids.js +29 -0
  585. package/src/components/off-canvas-editor/use-list-view-drop-zone.js +260 -0
  586. package/src/components/off-canvas-editor/use-list-view-expand-selected-item.js +58 -0
  587. package/src/components/off-canvas-editor/utils.js +58 -0
  588. package/src/components/plain-text/{style.scss → content.scss} +0 -0
  589. package/src/components/recursion-provider/test/index.js +27 -29
  590. package/src/components/responsive-block-control/test/index.js +69 -92
  591. package/src/components/rich-text/content.scss +42 -0
  592. package/src/components/rich-text/format-toolbar/index.js +6 -4
  593. package/src/components/rich-text/index.js +2 -2
  594. package/src/components/rich-text/index.native.js +0 -2
  595. package/src/components/rich-text/style.scss +0 -43
  596. package/src/components/rich-text/use-insert-replacement-text.js +31 -0
  597. package/src/components/rich-text/use-undo-automatic-change.js +7 -1
  598. package/src/components/rich-text/utils.js +2 -21
  599. package/src/components/spacing-sizes-control/spacing-input-control.js +9 -0
  600. package/src/components/ungroup-button/index.native.js +6 -2
  601. package/src/components/url-input/index.js +57 -73
  602. package/src/components/url-popover/README.md +12 -3
  603. package/src/components/url-popover/index.js +33 -3
  604. package/src/components/url-popover/test/__snapshots__/index.js.snap +8 -6
  605. package/src/components/url-popover/test/index.js +21 -9
  606. package/src/components/use-block-display-information/index.js +14 -5
  607. package/src/components/use-setting/index.js +20 -2
  608. package/src/components/use-setting/test/index.js +99 -0
  609. package/src/content.scss +10 -0
  610. package/src/hooks/child-layout.js +190 -0
  611. package/src/hooks/color-panel.js +13 -1
  612. package/src/hooks/color.js +2 -0
  613. package/src/hooks/content-lock-ui.js +47 -35
  614. package/src/hooks/dimensions.js +119 -21
  615. package/src/hooks/layout.js +62 -3
  616. package/src/hooks/margin.js +4 -3
  617. package/src/hooks/min-height.js +104 -0
  618. package/src/hooks/padding.js +4 -3
  619. package/src/hooks/style.js +10 -2
  620. package/src/hooks/test/style.js +4 -0
  621. package/src/hooks/test/use-typography-props.js +1 -1
  622. package/src/layouts/flex.js +43 -38
  623. package/src/store/actions.js +22 -0
  624. package/src/store/reducer.js +480 -434
  625. package/src/store/selectors.js +70 -64
  626. package/src/store/test/actions.js +18 -0
  627. package/src/store/test/performance.js +71 -0
  628. package/src/store/test/reducer.js +662 -490
  629. package/src/store/test/selectors.js +1839 -1306
  630. package/src/style.scss +4 -7
  631. package/src/utils/sorting.js +54 -0
  632. package/src/utils/test/sorting.js +49 -0
  633. package/tsconfig.tsbuildinfo +1 -1
  634. package/build/components/block-preview/live.js +0 -30
  635. package/build/components/block-preview/live.js.map +0 -1
  636. package/build-module/components/block-preview/live.js +0 -20
  637. package/build-module/components/block-preview/live.js.map +0 -1
  638. package/src/components/block-preview/live.js +0 -19
@@ -1,18 +1,21 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import { act, fireEvent, render, screen } from '@testing-library/react';
4
+ import {
5
+ fireEvent,
6
+ render,
7
+ screen,
8
+ waitFor,
9
+ within,
10
+ } from '@testing-library/react';
5
11
  import userEvent from '@testing-library/user-event';
6
12
 
7
13
  /**
8
14
  * WordPress dependencies
9
15
  */
10
16
  import { useState } from '@wordpress/element';
11
-
12
- /**
13
- * WordPress dependencies
14
- */
15
17
  import { useSelect } from '@wordpress/data';
18
+
16
19
  /**
17
20
  * Internal dependencies
18
21
  */
@@ -23,14 +26,6 @@ import {
23
26
  uniqueId,
24
27
  } from './fixtures';
25
28
 
26
- // Mock debounce() so that it runs instantly.
27
- jest.mock( '@wordpress/compose/src/utils/debounce', () => ( {
28
- debounce: ( fn ) => {
29
- fn.cancel = jest.fn();
30
- return fn;
31
- },
32
- } ) );
33
-
34
29
  const mockFetchSearchSuggestions = jest.fn();
35
30
 
36
31
  /**
@@ -58,18 +53,6 @@ jest.mock( '@wordpress/data/src/components/use-dispatch', () => ( {
58
53
 
59
54
  jest.useRealTimers();
60
55
 
61
- /**
62
- * Wait for next tick of event loop. This is required
63
- * because the `fetchSearchSuggestions` Promise will
64
- * resolve on the next tick of the event loop (this is
65
- * inline with the Promise spec). As a result we need to
66
- * wait on this loop to "tick" before we can expect the UI
67
- * to have updated.
68
- */
69
- function eventLoopTick() {
70
- return new Promise( ( resolve ) => setImmediate( resolve ) );
71
- }
72
-
73
56
  beforeEach( () => {
74
57
  // Setup a DOM element as a render target.
75
58
  mockFetchSearchSuggestions.mockImplementation( fetchFauxEntitySuggestions );
@@ -81,30 +64,6 @@ afterEach( () => {
81
64
  mockFetchRichUrlData?.mockReset(); // Conditionally reset as it may NOT be a mock.
82
65
  } );
83
66
 
84
- function getURLInput() {
85
- return screen.queryByRole( 'combobox', { name: 'URL' } );
86
- }
87
-
88
- function getSearchResults( container ) {
89
- const input = getURLInput();
90
- // The input has `aria-controls` to indicate that it owns (and is related to)
91
- // the search results with `role="listbox"`.
92
- const relatedSelector = input.getAttribute( 'aria-controls' );
93
-
94
- // Select by relationship as well as role.
95
- return container.querySelectorAll(
96
- `#${ relatedSelector }[role="listbox"] [role="option"]`
97
- );
98
- }
99
-
100
- function getCurrentLink() {
101
- return screen.queryByLabelText( 'Currently selected' );
102
- }
103
-
104
- function getSelectedResultElement() {
105
- return screen.queryByRole( 'option', { selected: true } );
106
- }
107
-
108
67
  /**
109
68
  * Workaround to trigger an arrow up keypress event.
110
69
  *
@@ -176,7 +135,7 @@ describe( 'Basic rendering', () => {
176
135
  render( <LinkControl /> );
177
136
 
178
137
  // Search Input UI.
179
- const searchInput = getURLInput();
138
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
180
139
 
181
140
  expect( searchInput ).toBeInTheDocument();
182
141
  } );
@@ -207,11 +166,10 @@ describe( 'Basic rendering', () => {
207
166
  render( <LinkControl /> );
208
167
 
209
168
  // Search Input UI.
210
- const searchInput = getURLInput();
169
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
211
170
 
212
171
  // Simulate searching for a term.
213
- searchInput.focus();
214
- await user.keyboard( searchTerm );
172
+ await user.type( searchInput, searchTerm );
215
173
 
216
174
  expect( screen.queryByText( '://' ) ).not.toBeInTheDocument();
217
175
  } );
@@ -220,7 +178,9 @@ describe( 'Basic rendering', () => {
220
178
  it( 'undefined', () => {
221
179
  render( <LinkControl value={ { url: 'https://example.com' } } /> );
222
180
 
223
- expect( getURLInput() ).not.toBeInTheDocument();
181
+ expect(
182
+ screen.queryByRole( 'combobox', { name: 'URL' } )
183
+ ).not.toBeInTheDocument();
224
184
  } );
225
185
 
226
186
  it( 'true', () => {
@@ -231,7 +191,9 @@ describe( 'Basic rendering', () => {
231
191
  />
232
192
  );
233
193
 
234
- expect( getURLInput() ).toBeVisible();
194
+ expect(
195
+ screen.getByRole( 'combobox', { name: 'URL' } )
196
+ ).toBeVisible();
235
197
  } );
236
198
 
237
199
  it( 'false', async () => {
@@ -247,7 +209,9 @@ describe( 'Basic rendering', () => {
247
209
 
248
210
  await user.click( editButton );
249
211
 
250
- expect( getURLInput() ).toBeVisible();
212
+ expect(
213
+ screen.getByRole( 'combobox', { name: 'URL' } )
214
+ ).toBeVisible();
251
215
 
252
216
  // If passed `forceIsEditingLink` of `false` while editing, should
253
217
  // forcefully reset to the preview state.
@@ -258,7 +222,9 @@ describe( 'Basic rendering', () => {
258
222
  />
259
223
  );
260
224
 
261
- expect( getURLInput() ).not.toBeInTheDocument();
225
+ expect(
226
+ screen.queryByRole( 'combobox', { name: 'URL' } )
227
+ ).not.toBeInTheDocument();
262
228
  } );
263
229
 
264
230
  it( 'should display human friendly error message if value URL prop is empty when component is forced into no-editing (preview) mode', async () => {
@@ -282,7 +248,7 @@ describe( 'Basic rendering', () => {
282
248
  />
283
249
  );
284
250
 
285
- const linkPreview = getCurrentLink();
251
+ const linkPreview = screen.getByLabelText( 'Currently selected' );
286
252
 
287
253
  const isPreviewError = linkPreview.classList.contains( 'is-error' );
288
254
  expect( isPreviewError ).toBe( true );
@@ -305,6 +271,7 @@ describe( 'Basic rendering', () => {
305
271
  it( 'should show "Unlink" button if a onRemove handler is provided', async () => {
306
272
  const user = userEvent.setup();
307
273
  const mockOnRemove = jest.fn();
274
+
308
275
  render(
309
276
  <LinkControl
310
277
  value={ { url: 'https://example.com' } }
@@ -330,63 +297,49 @@ describe( 'Searching for a link', () => {
330
297
  const searchTerm = 'Hello';
331
298
 
332
299
  let resolver;
300
+ mockFetchSearchSuggestions.mockImplementation(
301
+ () =>
302
+ new Promise( ( resolve ) => {
303
+ resolver = resolve;
304
+ } )
305
+ );
333
306
 
334
- const fauxRequest = () =>
335
- new Promise( ( resolve ) => {
336
- resolver = resolve;
337
- } );
338
-
339
- mockFetchSearchSuggestions.mockImplementation( fauxRequest );
340
-
341
- const { container } = render( <LinkControl /> );
307
+ render( <LinkControl /> );
342
308
 
343
309
  // Search Input UI.
344
- const searchInput = getURLInput();
310
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
345
311
 
346
312
  // Simulate searching for a term.
347
- searchInput.focus();
348
- await user.keyboard( searchTerm );
349
-
350
- // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
351
- await eventLoopTick();
352
-
353
- const searchResultElements = getSearchResults( container );
354
-
355
- let loadingUI = screen.queryByRole( 'presentation' );
356
-
357
- expect( searchResultElements ).toHaveLength( 0 );
358
-
359
- expect( loadingUI ).toBeVisible();
360
-
361
- act( () => {
362
- resolver( fauxEntitySuggestions );
363
- } );
313
+ await user.type( searchInput, searchTerm );
364
314
 
365
- await eventLoopTick();
315
+ expect( await screen.findByRole( 'presentation' ) ).toBeVisible();
316
+ expect( screen.queryByRole( 'listbox' ) ).not.toBeInTheDocument();
366
317
 
367
- loadingUI = screen.queryByRole( 'presentation' );
318
+ // make the search suggestions fetch return a response
319
+ resolver( fauxEntitySuggestions );
368
320
 
369
- expect( loadingUI ).not.toBeInTheDocument();
321
+ expect( await screen.findByRole( 'listbox' ) ).toBeVisible();
322
+ expect( screen.queryByRole( 'presentation' ) ).not.toBeInTheDocument();
370
323
  } );
371
324
 
372
325
  it( 'should display only search suggestions when current input value is not URL-like', async () => {
373
326
  const user = userEvent.setup();
374
327
  const searchTerm = 'Hello world';
375
- const firstFauxSuggestion = fauxEntitySuggestions[ 0 ];
328
+ const firstSuggestion = fauxEntitySuggestions[ 0 ];
376
329
 
377
- const { container } = render( <LinkControl /> );
330
+ render( <LinkControl /> );
378
331
 
379
332
  // Search Input UI.
380
- const searchInput = getURLInput();
333
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
381
334
 
382
335
  // Simulate searching for a term.
383
- searchInput.focus();
384
- await user.keyboard( searchTerm );
385
-
386
- // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
387
- await eventLoopTick();
336
+ await user.type( searchInput, searchTerm );
388
337
 
389
- const searchResultElements = getSearchResults( container );
338
+ const searchResultElements = within(
339
+ await screen.findByRole( 'listbox', {
340
+ name: /Search results for.*/,
341
+ } )
342
+ ).getAllByRole( 'option' );
390
343
 
391
344
  expect( searchResultElements ).toHaveLength(
392
345
  fauxEntitySuggestions.length
@@ -394,12 +347,12 @@ describe( 'Searching for a link', () => {
394
347
 
395
348
  expect( searchInput ).toHaveAttribute( 'aria-expanded', 'true' );
396
349
 
397
- // Sanity check that a search suggestion shows up corresponding to the data.
350
+ // Check that a search suggestion shows up corresponding to the data.
398
351
  expect( searchResultElements[ 0 ] ).toHaveTextContent(
399
- firstFauxSuggestion.title
352
+ firstSuggestion.title
400
353
  );
401
354
  expect( searchResultElements[ 0 ] ).toHaveTextContent(
402
- firstFauxSuggestion.type
355
+ firstSuggestion.type
403
356
  );
404
357
 
405
358
  // The fallback URL suggestion should not be shown when input is not URL-like.
@@ -412,26 +365,19 @@ describe( 'Searching for a link', () => {
412
365
  const user = userEvent.setup();
413
366
  const searchTerm = ' Hello ';
414
367
 
415
- const { container } = render( <LinkControl /> );
368
+ render( <LinkControl /> );
416
369
 
417
370
  // Search Input UI.
418
- const searchInput = getURLInput();
371
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
419
372
 
420
373
  // Simulate searching for a term.
421
- searchInput.focus();
422
- await user.keyboard( searchTerm );
423
-
424
- // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
425
- await eventLoopTick();
374
+ await user.type( searchInput, searchTerm );
426
375
 
376
+ const searchResults = await screen.findByRole( 'listbox', {
377
+ name: /Search results for.*/,
378
+ } );
427
379
  const searchResultTextHighlightElements = Array.from(
428
- container.querySelectorAll(
429
- '[role="listbox"] button[role="option"] mark'
430
- )
431
- );
432
-
433
- const invalidResults = searchResultTextHighlightElements.find(
434
- ( mark ) => mark.innerHTML !== 'Hello'
380
+ searchResults.querySelectorAll( 'button[role="option"] mark' )
435
381
  );
436
382
 
437
383
  // Given we're mocking out the results we should always have 4 mark elements.
@@ -439,7 +385,11 @@ describe( 'Searching for a link', () => {
439
385
 
440
386
  // Make sure there are no `mark` elements which contain anything other
441
387
  // than the trimmed search term (ie: no whitespace).
442
- expect( invalidResults ).toBeFalsy();
388
+ expect(
389
+ searchResultTextHighlightElements.every(
390
+ ( mark ) => mark.innerHTML === 'Hello'
391
+ )
392
+ ).toBe( true );
443
393
 
444
394
  // Implementation detail test to ensure that the fetch handler is called
445
395
  // with the trimmed search value. We do this because we are mocking out
@@ -453,23 +403,17 @@ describe( 'Searching for a link', () => {
453
403
 
454
404
  it( 'should not call search handler when showSuggestions is false', async () => {
455
405
  const user = userEvent.setup();
456
- const { container } = render(
457
- <LinkControl showSuggestions={ false } />
458
- );
406
+ render( <LinkControl showSuggestions={ false } /> );
459
407
 
460
408
  // Search Input UI.
461
- const searchInput = getURLInput();
409
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
462
410
 
463
411
  // Simulate searching for a term.
464
- searchInput.focus();
465
- await user.keyboard( 'anything' );
466
-
467
- const searchResultElements = getSearchResults( container );
412
+ await user.type( searchInput, 'anything' );
468
413
 
469
- // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
470
- await eventLoopTick();
414
+ const searchResultsField = screen.queryByRole( 'listbox' );
471
415
 
472
- expect( searchResultElements ).toHaveLength( 0 );
416
+ expect( searchResultsField ).not.toBeInTheDocument();
473
417
  expect( mockFetchSearchSuggestions ).not.toHaveBeenCalled();
474
418
  } );
475
419
 
@@ -480,19 +424,19 @@ describe( 'Searching for a link', () => {
480
424
  'should display a URL suggestion as a default fallback for the search term "%s" which could potentially be a valid url.',
481
425
  async ( searchTerm ) => {
482
426
  const user = userEvent.setup();
483
- const { container } = render( <LinkControl /> );
427
+ render( <LinkControl /> );
484
428
 
485
429
  // Search Input UI.
486
- const searchInput = getURLInput();
430
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
487
431
 
488
432
  // Simulate searching for a term.
489
- searchInput.focus();
490
- await user.keyboard( searchTerm );
491
-
492
- // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
493
- await eventLoopTick();
433
+ await user.type( searchInput, searchTerm );
494
434
 
495
- const searchResultElements = getSearchResults( container );
435
+ const searchResultElements = within(
436
+ await screen.findByRole( 'listbox', {
437
+ name: /Search results for.*/,
438
+ } )
439
+ ).getAllByRole( 'option' );
496
440
 
497
441
  const lastSearchResultItem =
498
442
  searchResultElements[ searchResultElements.length - 1 ];
@@ -514,19 +458,19 @@ describe( 'Searching for a link', () => {
514
458
 
515
459
  it( 'should not display a URL suggestion as a default fallback when noURLSuggestion is passed.', async () => {
516
460
  const user = userEvent.setup();
517
- const { container } = render( <LinkControl noURLSuggestion /> );
461
+ render( <LinkControl noURLSuggestion /> );
518
462
 
519
463
  // Search Input UI.
520
- const searchInput = getURLInput();
464
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
521
465
 
522
466
  // Simulate searching for a term.
523
- searchInput.focus();
524
- await user.keyboard( 'couldbeurlorentitysearchterm' );
525
-
526
- // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
527
- await eventLoopTick();
467
+ await user.type( searchInput, 'couldbeurlorentitysearchterm' );
528
468
 
529
- const searchResultElements = getSearchResults( container );
469
+ const searchResultElements = within(
470
+ await screen.findByRole( 'listbox', {
471
+ name: /Search results for.*/,
472
+ } )
473
+ ).getAllByRole( 'option' );
530
474
 
531
475
  // We should see a search result for each of the expect search suggestions and nothing else.
532
476
  expect( searchResultElements ).toHaveLength(
@@ -544,24 +488,24 @@ describe( 'Manual link entry', () => {
544
488
  'should display a single suggestion result when the current input value is URL-like (eg: %s)',
545
489
  async ( searchTerm ) => {
546
490
  const user = userEvent.setup();
547
- const { container } = render( <LinkControl /> );
491
+ render( <LinkControl /> );
548
492
 
549
493
  // Search Input UI.
550
- const searchInput = getURLInput();
494
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
551
495
 
552
496
  // Simulate searching for a term.
553
- searchInput.focus();
554
- await user.keyboard( searchTerm );
555
-
556
- // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
557
- await eventLoopTick();
497
+ await user.type( searchInput, searchTerm );
558
498
 
559
- const searchResultElements = getSearchResults( container );
499
+ const searchResultElements = within(
500
+ await screen.findByRole( 'listbox', {
501
+ name: /Search results for.*/,
502
+ } )
503
+ ).getByRole( 'option' );
560
504
 
561
- expect( searchResultElements ).toHaveLength( 1 );
562
- expect( searchResultElements[ 0 ] ).toHaveTextContent( searchTerm );
563
- expect( searchResultElements[ 0 ] ).toHaveTextContent( 'URL' );
564
- expect( searchResultElements[ 0 ] ).toHaveTextContent(
505
+ expect( searchResultElements ).toBeVisible();
506
+ expect( searchResultElements ).toHaveTextContent( searchTerm );
507
+ expect( searchResultElements ).toHaveTextContent( 'URL' );
508
+ expect( searchResultElements ).toHaveTextContent(
565
509
  'Press ENTER to add this link'
566
510
  );
567
511
  }
@@ -582,31 +526,29 @@ describe( 'Manual link entry', () => {
582
526
  render( <LinkControl /> );
583
527
 
584
528
  // Search Input UI.
585
- const searchInput = getURLInput();
529
+ const searchInput = screen.getByRole( 'combobox', {
530
+ name: 'URL',
531
+ } );
586
532
 
587
- let submitButton = screen.queryByRole( 'button', {
533
+ let submitButton = screen.getByRole( 'button', {
588
534
  name: 'Submit',
589
535
  } );
590
536
 
591
537
  expect( submitButton ).toBeDisabled();
592
538
  expect( submitButton ).toBeVisible();
593
539
 
594
- searchInput.focus();
595
540
  if ( searchString.length ) {
596
541
  // Simulate searching for a term.
597
- await user.keyboard( searchString );
542
+ await user.type( searchInput, searchString );
598
543
  } else {
599
544
  // Simulate clearing the search term.
600
- await userEvent.clear( searchInput );
545
+ await user.clear( searchInput );
601
546
  }
602
547
 
603
- // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
604
- await eventLoopTick();
605
-
606
548
  // Attempt to submit the empty search value in the input.
607
549
  await user.keyboard( '[Enter]' );
608
550
 
609
- submitButton = screen.queryByRole( 'button', {
551
+ submitButton = screen.getByRole( 'button', {
610
552
  name: 'Submit',
611
553
  } );
612
554
 
@@ -625,7 +567,9 @@ describe( 'Manual link entry', () => {
625
567
  render( <LinkControl /> );
626
568
 
627
569
  // Search Input UI.
628
- const searchInput = getURLInput();
570
+ const searchInput = screen.getByRole( 'combobox', {
571
+ name: 'URL',
572
+ } );
629
573
 
630
574
  let submitButton = screen.queryByRole( 'button', {
631
575
  name: 'Submit',
@@ -635,18 +579,14 @@ describe( 'Manual link entry', () => {
635
579
  expect( submitButton ).toBeVisible();
636
580
 
637
581
  // Simulate searching for a term.
638
- searchInput.focus();
639
582
  if ( searchString.length ) {
640
583
  // Simulate searching for a term.
641
- await user.keyboard( searchString );
584
+ await user.type( searchInput, searchString );
642
585
  } else {
643
586
  // Simulate clearing the search term.
644
- await userEvent.clear( searchInput );
587
+ await user.clear( searchInput );
645
588
  }
646
589
 
647
- // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
648
- await eventLoopTick();
649
-
650
590
  // Attempt to submit the empty search value in the input.
651
591
  await user.click( submitButton );
652
592
 
@@ -671,28 +611,26 @@ describe( 'Manual link entry', () => {
671
611
  'should recognise "%s" as a %s link and handle as manual entry by displaying a single suggestion',
672
612
  async ( searchTerm, searchType ) => {
673
613
  const user = userEvent.setup();
674
- const { container } = render( <LinkControl /> );
614
+ render( <LinkControl /> );
675
615
 
676
616
  // Search Input UI.
677
- const searchInput = getURLInput();
617
+ const searchInput = screen.getByRole( 'combobox', {
618
+ name: 'URL',
619
+ } );
678
620
 
679
621
  // Simulate searching for a term.
680
- searchInput.focus();
681
- await user.keyboard( searchTerm );
682
-
683
- // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
684
- await eventLoopTick();
685
-
686
- const searchResultElements = getSearchResults( container );
687
-
688
- expect( searchResultElements ).toHaveLength( 1 );
689
- expect( searchResultElements[ 0 ] ).toHaveTextContent(
690
- searchTerm
691
- );
692
- expect( searchResultElements[ 0 ] ).toHaveTextContent(
693
- searchType
694
- );
695
- expect( searchResultElements[ 0 ] ).toHaveTextContent(
622
+ await user.type( searchInput, searchTerm );
623
+
624
+ const searchResultElements = within(
625
+ await screen.findByRole( 'listbox', {
626
+ name: /Search results for.*/,
627
+ } )
628
+ ).getByRole( 'option' );
629
+
630
+ expect( searchResultElements ).toBeVisible();
631
+ expect( searchResultElements ).toHaveTextContent( searchTerm );
632
+ expect( searchResultElements ).toHaveTextContent( searchType );
633
+ expect( searchResultElements ).toHaveTextContent(
696
634
  'Press ENTER to add this link'
697
635
  );
698
636
  }
@@ -702,14 +640,10 @@ describe( 'Manual link entry', () => {
702
640
 
703
641
  describe( 'Default search suggestions', () => {
704
642
  it( 'should display a list of initial search suggestions when there is no search value or suggestions', async () => {
705
- const expectedResultsLength = 3; // Set within `LinkControl`.
706
-
707
643
  render( <LinkControl showInitialSuggestions /> );
708
644
 
709
- await eventLoopTick();
710
-
711
645
  expect(
712
- screen.queryByRole( 'listbox', {
646
+ await screen.findByRole( 'listbox', {
713
647
  name: 'Recently updated',
714
648
  } )
715
649
  ).toBeVisible();
@@ -717,7 +651,9 @@ describe( 'Default search suggestions', () => {
717
651
  // Verify input has no value has default suggestions should only show
718
652
  // when this does not have a value.
719
653
  // Search Input UI.
720
- expect( getURLInput() ).toHaveValue( '' );
654
+ expect( screen.getByRole( 'combobox', { name: 'URL' } ) ).toHaveValue(
655
+ ''
656
+ );
721
657
 
722
658
  // Ensure only called once as a guard against potential infinite
723
659
  // re-render loop within `componentDidUpdate` calling `updateSuggestions`
@@ -725,43 +661,39 @@ describe( 'Default search suggestions', () => {
725
661
  expect( mockFetchSearchSuggestions ).toHaveBeenCalledTimes( 1 );
726
662
 
727
663
  // Verify the search results already display the initial suggestions.
728
- expect( screen.queryAllByRole( 'option' ) ).toHaveLength(
729
- expectedResultsLength
730
- );
664
+ // `LinkControl` internally always limits the number of initial suggestions to 3.
665
+ expect( screen.queryAllByRole( 'option' ) ).toHaveLength( 3 );
731
666
  } );
732
667
 
733
668
  it( 'should not display initial suggestions when input value is present', async () => {
734
669
  const user = userEvent.setup();
735
670
 
736
- // Render with an initial value an ensure that no initial suggestions
737
- // are shown.
738
- const { container } = render(
739
- <LinkControl
740
- showInitialSuggestions
741
- value={ fauxEntitySuggestions[ 0 ] }
742
- />
743
- );
744
-
745
- await eventLoopTick();
671
+ // Render with an initial value an ensure that no initial suggestions are shown.
672
+ const initialValue = fauxEntitySuggestions[ 0 ];
673
+ render( <LinkControl showInitialSuggestions value={ initialValue } /> );
746
674
 
747
675
  expect( mockFetchSearchSuggestions ).not.toHaveBeenCalled();
748
676
 
749
677
  // Click the "Edit/Change" button and check initial suggestions are not
750
678
  // shown.
751
- const currentLinkUI = getCurrentLink();
752
- const currentLinkBtn = currentLinkUI.querySelector( 'button' );
753
-
679
+ const currentLinkUI = screen.getByLabelText( 'Currently selected' );
680
+ const currentLinkBtn = within( currentLinkUI ).getByRole( 'button', {
681
+ name: 'Edit',
682
+ } );
754
683
  await user.click( currentLinkBtn );
755
684
 
756
- const searchInput = getURLInput();
757
- searchInput.focus();
758
-
759
- await eventLoopTick();
685
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
686
+ // Search input is set to the URL value.
687
+ expect( searchInput ).toHaveValue( initialValue.url );
760
688
 
761
- const searchResultElements = getSearchResults( container );
689
+ // Focus the search input to display suggestions
690
+ await user.click( searchInput );
762
691
 
763
- // Search input is set to the URL value.
764
- expect( searchInput ).toHaveValue( fauxEntitySuggestions[ 0 ].url );
692
+ const searchResultElements = within(
693
+ await screen.findByRole( 'listbox', {
694
+ name: /Search results for.*/,
695
+ } )
696
+ ).getAllByRole( 'option' );
765
697
 
766
698
  // It should match any url that's like ?p= and also include a URL option.
767
699
  expect( searchResultElements ).toHaveLength( 5 );
@@ -775,68 +707,54 @@ describe( 'Default search suggestions', () => {
775
707
  const user = userEvent.setup();
776
708
  const searchTerm = 'Hello world';
777
709
 
778
- const { container } = render( <LinkControl showInitialSuggestions /> );
779
-
780
- let searchResultElements;
781
- let searchInput;
710
+ render( <LinkControl showInitialSuggestions /> );
782
711
 
783
712
  // Search Input UI.
784
- searchInput = getURLInput();
713
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
785
714
 
786
715
  // Simulate searching for a term.
787
- searchInput.focus();
788
- await user.keyboard( searchTerm );
789
-
790
- // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
791
- await eventLoopTick();
716
+ await user.type( searchInput, searchTerm );
792
717
 
793
718
  expect( searchInput ).toHaveValue( searchTerm );
794
719
 
795
- searchResultElements = getSearchResults( container );
796
-
797
- // Delete the text.
798
- await userEvent.clear( searchInput );
720
+ const searchResultsList = await screen.findByRole( 'listbox', {
721
+ name: /Search results for.*/,
722
+ } );
799
723
 
800
- await eventLoopTick();
724
+ expect( searchResultsList ).toBeVisible();
801
725
 
802
- searchResultElements = getSearchResults( container );
726
+ expect(
727
+ within( searchResultsList ).getAllByRole( 'option' )
728
+ ).toHaveLength( 4 );
803
729
 
804
- searchInput = getURLInput();
730
+ // Delete the text.
731
+ await userEvent.clear( searchInput );
805
732
 
806
733
  // Check the input is empty now.
807
734
  expect( searchInput ).toHaveValue( '' );
808
735
 
809
- expect(
810
- screen.queryByRole( 'listbox', {
811
- name: 'Recently updated',
812
- } )
813
- ).toBeVisible();
736
+ const initialResultsList = await screen.findByRole( 'listbox', {
737
+ name: 'Recently updated',
738
+ } );
814
739
 
815
- expect( searchResultElements ).toHaveLength( 3 );
740
+ expect(
741
+ within( initialResultsList ).getAllByRole( 'option' )
742
+ ).toHaveLength( 3 );
816
743
  } );
817
744
 
818
745
  it( 'should not display initial suggestions when there are no recently updated pages/posts', async () => {
819
- const noResults = [];
820
746
  // Force API returning empty results for recently updated Pages.
821
- mockFetchSearchSuggestions.mockImplementation( () =>
822
- Promise.resolve( noResults )
823
- );
747
+ mockFetchSearchSuggestions.mockImplementation( async () => [] );
824
748
 
825
- const { container } = render( <LinkControl showInitialSuggestions /> );
826
-
827
- await eventLoopTick();
828
-
829
- const searchInput = getURLInput();
749
+ render( <LinkControl showInitialSuggestions /> );
830
750
 
831
- const searchResultElements = getSearchResults( container );
751
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
832
752
 
833
- const searchResultLabel = container.querySelector(
834
- '.block-editor-link-control__search-results-label'
835
- );
836
-
837
- expect( searchResultLabel ).not.toBeInTheDocument();
753
+ const searchResultsField = screen.queryByRole( 'listbox', {
754
+ name: 'Recently updated',
755
+ } );
838
756
 
839
- expect( searchResultElements ).toHaveLength( 0 );
757
+ expect( searchResultsField ).not.toBeInTheDocument();
840
758
 
841
759
  expect( searchInput ).toHaveAttribute( 'aria-expanded', 'false' );
842
760
  } );
@@ -860,19 +778,15 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
860
778
  async ( entityNameText ) => {
861
779
  const user = userEvent.setup();
862
780
  let resolver;
863
- let resolvedEntity;
864
-
865
781
  const createSuggestion = ( title ) =>
866
782
  new Promise( ( resolve ) => {
867
- resolver = ( arg ) => {
868
- resolve( arg );
869
- };
870
- resolvedEntity = {
871
- title,
872
- id: 123,
873
- url: '/?p=123',
874
- type: 'page',
875
- };
783
+ resolver = () =>
784
+ resolve( {
785
+ title,
786
+ id: 123,
787
+ url: '/?p=123',
788
+ type: 'page',
789
+ } );
876
790
  } );
877
791
 
878
792
  const LinkControlConsumer = () => {
@@ -889,22 +803,21 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
889
803
  );
890
804
  };
891
805
 
892
- const { container } = render( <LinkControlConsumer /> );
806
+ render( <LinkControlConsumer /> );
893
807
 
894
808
  // Search Input UI.
895
- const searchInput = getURLInput();
809
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
896
810
 
897
811
  // Simulate searching for a term.
898
- searchInput.focus();
899
- await user.keyboard( entityNameText );
812
+ await user.type( searchInput, entityNameText );
900
813
 
901
- await eventLoopTick();
902
-
903
- const searchResultElements = screen.queryAllByRole( 'option' );
814
+ const searchResults = await screen.findByRole( 'listbox', {
815
+ name: /Search results for.*/,
816
+ } );
904
817
 
905
- const createButton = Array.from( searchResultElements ).filter(
906
- ( result ) => result.innerHTML.includes( 'Create:' )
907
- )[ 0 ];
818
+ const createButton = within( searchResults ).getByRole( 'option', {
819
+ name: /^Create:/,
820
+ } );
908
821
 
909
822
  expect( createButton ).toBeVisible();
910
823
  expect( createButton ).toHaveTextContent( entityNameText );
@@ -913,25 +826,23 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
913
826
  // resolution manually via the `resolver` reference.
914
827
  await user.click( createButton );
915
828
 
916
- await eventLoopTick();
917
-
918
829
  // Check for loading indicator.
919
- const loadingIndicator = container.querySelector(
920
- '.block-editor-link-control__loading'
921
- );
922
- const currentLinkLabel = getCurrentLink();
830
+ const loadingIndicator = screen.getByText( 'Creating…' );
831
+ const currentLinkLabel =
832
+ screen.queryByLabelText( 'Currently selected' );
923
833
 
924
834
  expect( currentLinkLabel ).not.toBeInTheDocument();
925
- expect( loadingIndicator ).toHaveTextContent( 'Creating' );
835
+ expect( loadingIndicator ).toBeVisible();
836
+ expect( loadingIndicator ).toHaveClass(
837
+ 'block-editor-link-control__loading'
838
+ );
926
839
 
927
840
  // Resolve the `createSuggestion` promise.
928
- await act( async () => {
929
- resolver( resolvedEntity );
930
- } );
841
+ resolver();
931
842
 
932
- await eventLoopTick();
933
-
934
- const currentLink = getCurrentLink();
843
+ const currentLink = await screen.findByLabelText(
844
+ 'Currently selected'
845
+ );
935
846
 
936
847
  expect( currentLink ).toHaveTextContent( entityNameText );
937
848
  expect( currentLink ).toHaveTextContent( '/?p=123' );
@@ -959,31 +870,25 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
959
870
  );
960
871
  };
961
872
 
962
- const { container } = render( <LinkControlConsumer /> );
873
+ render( <LinkControlConsumer /> );
963
874
 
964
875
  // Search Input UI.
965
- const searchInput = getURLInput();
876
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
966
877
 
967
878
  // Simulate searching for a term.
968
- searchInput.focus();
969
- await user.keyboard( 'Some new page to create' );
970
-
971
- await eventLoopTick();
879
+ await user.type( searchInput, 'Some new page to create' );
972
880
 
973
- // TODO: select these by aria relationship to autocomplete rather than arbitrary selector.
974
- const searchResultElements = container.querySelectorAll(
975
- '[role="listbox"] [role="option"]'
976
- );
881
+ const searchResults = await screen.findByRole( 'listbox', {
882
+ name: /Search results for.*/,
883
+ } );
977
884
 
978
- const createButton = Array.from( searchResultElements ).filter(
979
- ( result ) => result.innerHTML.includes( 'Create:' )
980
- )[ 0 ];
885
+ const createButton = within( searchResults ).getByRole( 'option', {
886
+ name: /^Create:/,
887
+ } );
981
888
 
982
889
  await user.click( createButton );
983
890
 
984
- await eventLoopTick();
985
-
986
- const currentLink = getCurrentLink();
891
+ const currentLink = screen.getByLabelText( 'Currently selected' );
987
892
 
988
893
  expect( currentLink ).toHaveTextContent( 'Some new page to create' );
989
894
  expect( currentLink ).toHaveTextContent( '/?p=123' );
@@ -1014,37 +919,34 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1014
919
  );
1015
920
  };
1016
921
 
1017
- const { container } = render( <LinkControlConsumer /> );
922
+ render( <LinkControlConsumer /> );
1018
923
 
1019
924
  // Search Input UI.
1020
- const searchInput = getURLInput();
925
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
1021
926
 
1022
927
  // Simulate searching for a term.
1023
- searchInput.focus();
1024
- await user.keyboard( entityNameText );
928
+ await user.type( searchInput, entityNameText );
1025
929
 
1026
- await eventLoopTick();
1027
-
1028
- // TODO: select these by aria relationship to autocomplete rather than arbitrary selector.
1029
- const searchResultElements = container.querySelectorAll(
1030
- '[role="listbox"] [role="option"]'
1031
- );
1032
- const createButton = Array.from( searchResultElements ).filter(
1033
- ( result ) => result.innerHTML.includes( 'Create:' )
1034
- )[ 0 ];
930
+ const searchResults = await screen.findByRole( 'listbox', {
931
+ name: /Search results for.*/,
932
+ } );
1035
933
 
1036
- // Step down into the search results, highlighting the first result item.
934
+ // Step down into the search results, selecting the first result item.
1037
935
  triggerArrowDown( searchInput );
1038
936
 
1039
- createButton.focus();
1040
- await user.keyboard( '[Enter]' );
1041
-
1042
- searchInput.focus();
1043
- await user.keyboard( '[Enter]' );
937
+ // Check that the create button is in the results and that it's selected
938
+ const createButton = within( searchResults ).getByRole( 'option', {
939
+ name: /^Create:/,
940
+ selected: true,
941
+ } );
942
+ expect( createButton ).toBeVisible();
1044
943
 
1045
- await eventLoopTick();
944
+ expect( searchInput ).toHaveFocus();
945
+ triggerEnter( searchInput );
1046
946
 
1047
- expect( getCurrentLink() ).toHaveTextContent( entityNameText );
947
+ expect(
948
+ await screen.findByLabelText( 'Currently selected' )
949
+ ).toHaveTextContent( entityNameText );
1048
950
  } );
1049
951
 
1050
952
  it( 'should allow customisation of button text', async () => {
@@ -1060,25 +962,21 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1060
962
  );
1061
963
  };
1062
964
 
1063
- const { container } = render( <LinkControlConsumer /> );
965
+ render( <LinkControlConsumer /> );
1064
966
 
1065
967
  // Search Input UI.
1066
- const searchInput = getURLInput();
968
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
1067
969
 
1068
970
  // Simulate searching for a term.
1069
- searchInput.focus();
1070
- await user.keyboard( entityNameText );
1071
-
1072
- await eventLoopTick();
971
+ await user.type( searchInput, entityNameText );
1073
972
 
1074
- // TODO: select these by aria relationship to autocomplete rather than arbitrary selector.
1075
- const searchResultElements = container.querySelectorAll(
1076
- '[role="listbox"] [role="option"]'
1077
- );
973
+ const searchResults = await screen.findByRole( 'listbox', {
974
+ name: /Search results for.*/,
975
+ } );
1078
976
 
1079
- const createButton = Array.from( searchResultElements ).filter(
1080
- ( result ) => result.innerHTML.includes( 'Custom suggestion text' )
1081
- )[ 0 ];
977
+ const createButton = within( searchResults ).getByRole( 'option', {
978
+ name: /Custom suggestion text/,
979
+ } );
1082
980
 
1083
981
  expect( createButton ).toBeVisible();
1084
982
  } );
@@ -1087,55 +985,37 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1087
985
  it.each( [ [ undefined ], [ null ], [ false ] ] )(
1088
986
  'should not show not show an option to create an entity when "createSuggestion" handler is %s',
1089
987
  async ( handler ) => {
1090
- const { container } = render(
1091
- <LinkControl createSuggestion={ handler } />
1092
- );
1093
-
1094
- // Await the initial suggestions to be fetched.
1095
- await eventLoopTick();
988
+ render( <LinkControl createSuggestion={ handler } /> );
1096
989
 
1097
990
  // Search Input UI.
1098
- const searchInput = getURLInput();
991
+ const searchInput = screen.getByRole( 'combobox', {
992
+ name: 'URL',
993
+ } );
1099
994
 
1100
- // TODO: select these by aria relationship to autocomplete rather than arbitrary selector.
1101
- const searchResultElements = container.querySelectorAll(
1102
- '[role="listbox"] [role="option"]'
1103
- );
1104
- const createButton = Array.from( searchResultElements ).filter(
1105
- ( result ) => result.innerHTML.includes( 'Create:' )
1106
- )[ 0 ];
995
+ const searchResultsField = screen.queryByRole( 'listbox' );
1107
996
 
1108
997
  // Verify input has no value.
1109
998
  expect( searchInput ).toHaveValue( '' );
1110
- expect( createButton ).toBeFalsy(); // Shouldn't exist!
999
+ expect( searchResultsField ).not.toBeInTheDocument(); // Shouldn't exist!
1111
1000
  }
1112
1001
  );
1113
1002
 
1114
- it( 'should not show not show an option to create an entity when input is empty', async () => {
1115
- const { container } = render(
1003
+ it( 'should not show an option to create an entity when input is empty', async () => {
1004
+ render(
1116
1005
  <LinkControl
1117
- showInitialSuggestions={ true } // Should show even if we're not showing initial suggestions.
1006
+ showInitialSuggestions // Should show even if we're not showing initial suggestions.
1118
1007
  createSuggestion={ jest.fn() }
1119
1008
  />
1120
1009
  );
1121
1010
 
1122
- // Await the initial suggestions to be fetched.
1123
- await eventLoopTick();
1124
-
1125
1011
  // Search Input UI.
1126
- const searchInput = getURLInput();
1012
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
1127
1013
 
1128
- // TODO: select these by aria relationship to autocomplete rather than arbitrary selector.
1129
- const searchResultElements = container.querySelectorAll(
1130
- '[role="listbox"] [role="option"]'
1131
- );
1132
- const createButton = Array.from( searchResultElements ).filter(
1133
- ( result ) => result.innerHTML.includes( 'New page' )
1134
- )[ 0 ];
1014
+ const searchResultsField = screen.queryByRole( 'listbox' );
1135
1015
 
1136
1016
  // Verify input has no value.
1137
1017
  expect( searchInput ).toHaveValue( '' );
1138
- expect( createButton ).toBeFalsy(); // Shouldn't exist!
1018
+ expect( searchResultsField ).not.toBeInTheDocument(); // Shouldn't exist!
1139
1019
  } );
1140
1020
 
1141
1021
  it.each( [
@@ -1148,29 +1028,25 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1148
1028
  'should not show option to "Create Page" when text is a form of direct entry (eg: %s)',
1149
1029
  async ( inputText ) => {
1150
1030
  const user = userEvent.setup();
1151
- const { container } = render(
1152
- <LinkControl createSuggestion={ jest.fn() } />
1153
- );
1031
+ render( <LinkControl createSuggestion={ jest.fn() } /> );
1154
1032
 
1155
1033
  // Search Input UI.
1156
- const searchInput = getURLInput();
1034
+ const searchInput = screen.getByRole( 'combobox', {
1035
+ name: 'URL',
1036
+ } );
1157
1037
 
1158
1038
  // Simulate searching for a term.
1159
- searchInput.focus();
1160
- await user.keyboard( inputText );
1039
+ await user.type( searchInput, inputText );
1161
1040
 
1162
- await eventLoopTick();
1041
+ const searchResults = await screen.findByRole( 'listbox', {
1042
+ name: /Search results for.*/,
1043
+ } );
1163
1044
 
1164
- // TODO: select these by aria relationship to autocomplete rather than arbitrary selector.
1165
- const searchResultElements = container.querySelectorAll(
1166
- '[role="listbox"] [role="option"]'
1045
+ const createButton = within( searchResults ).queryByRole(
1046
+ 'option',
1047
+ { name: /New page/ }
1167
1048
  );
1168
-
1169
- const createButton = Array.from( searchResultElements ).filter(
1170
- ( result ) => result.innerHTML.includes( 'New page' )
1171
- )[ 0 ];
1172
-
1173
- expect( createButton ).toBeFalsy(); // Shouldn't exist!
1049
+ expect( createButton ).not.toBeInTheDocument(); // Shouldn't exist!
1174
1050
  }
1175
1051
  );
1176
1052
  } );
@@ -1187,61 +1063,42 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1187
1063
 
1188
1064
  const createSuggestion = () => Promise.reject( throwsError() );
1189
1065
 
1190
- const { container } = render(
1191
- <LinkControl createSuggestion={ createSuggestion } />
1192
- );
1066
+ render( <LinkControl createSuggestion={ createSuggestion } /> );
1193
1067
 
1194
1068
  // Search Input UI.
1195
- searchInput = getURLInput();
1069
+ searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
1196
1070
 
1197
1071
  // Simulate searching for a term.
1198
- searchInput.focus();
1199
- await user.keyboard( searchText );
1072
+ await user.type( searchInput, searchText );
1200
1073
 
1201
- await eventLoopTick();
1074
+ const searchResults = await screen.findByRole( 'listbox', {
1075
+ name: /Search results for.*/,
1076
+ } );
1202
1077
 
1203
- // TODO: select these by aria relationship to autocomplete rather than arbitrary selector.
1204
- let searchResultElements = container.querySelectorAll(
1205
- '[role="listbox"] [role="option"]'
1206
- );
1207
- let createButton = Array.from( searchResultElements ).filter(
1208
- ( result ) => result.innerHTML.includes( 'Create:' )
1209
- )[ 0 ];
1078
+ const createButton = within( searchResults ).getByRole( 'option', {
1079
+ name: /^Create:/,
1080
+ } );
1210
1081
 
1211
1082
  await user.click( createButton );
1212
1083
 
1213
- await eventLoopTick();
1214
-
1215
- searchInput = getURLInput();
1084
+ searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
1216
1085
 
1217
- // This is a Notice component
1218
- // we allow selecting by className here as an edge case because the
1219
- // a11y is handled via `speak`.
1220
- // See: https://github.com/WordPress/gutenberg/tree/HEAD/packages/a11y#speak.
1221
- const errorNotice = container.querySelector(
1222
- '.block-editor-link-control__search-error'
1223
- );
1086
+ const errorNotice = screen.getAllByText(
1087
+ 'API response returned invalid entity.'
1088
+ )[ 1 ];
1224
1089
 
1225
1090
  // Catch the error in the test to avoid test failures.
1226
1091
  expect( throwsError ).toThrow( Error );
1227
1092
 
1228
1093
  // Check human readable error notice is perceivable.
1229
1094
  expect( errorNotice ).toBeVisible();
1230
- expect( errorNotice ).toHaveTextContent(
1231
- 'API response returned invalid entity'
1095
+ expect( errorNotice.parentElement ).toHaveClass(
1096
+ 'block-editor-link-control__search-error'
1232
1097
  );
1233
1098
 
1234
1099
  // Verify input is repopulated with original search text.
1235
1100
  expect( searchInput ).toBeVisible();
1236
1101
  expect( searchInput ).toHaveValue( searchText );
1237
-
1238
- // Verify search results are re-shown and create button is available.
1239
- searchResultElements = container.querySelectorAll(
1240
- '[role="listbox"] [role="option"]'
1241
- );
1242
- createButton = Array.from( searchResultElements ).filter(
1243
- ( result ) => result.innerHTML.includes( 'New page' )
1244
- )[ 0 ];
1245
1102
  } );
1246
1103
  } );
1247
1104
  } );
@@ -1258,12 +1115,12 @@ describe( 'Selecting links', () => {
1258
1115
 
1259
1116
  render( <LinkControlConsumer /> );
1260
1117
 
1261
- const currentLink = getCurrentLink();
1262
- const currentLinkAnchor = currentLink.querySelector(
1263
- `[href="${ selectedLink.url }"]`
1264
- );
1118
+ const currentLink = screen.getByLabelText( 'Currently selected' );
1119
+ const currentLinkAnchor = screen.getByRole( 'link', {
1120
+ name: `${ selectedLink.title } (opens in a new tab)`,
1121
+ } );
1265
1122
 
1266
- expect( currentLink ).toHaveTextContent( selectedLink.title );
1123
+ expect( currentLink ).toBeVisible();
1267
1124
  expect(
1268
1125
  screen.queryByRole( 'button', { name: 'Edit' } )
1269
1126
  ).toBeVisible();
@@ -1288,14 +1145,16 @@ describe( 'Selecting links', () => {
1288
1145
  render( <LinkControlConsumer /> );
1289
1146
 
1290
1147
  // Required in order to select the button below.
1291
- let currentLinkUI = getCurrentLink();
1292
- const currentLinkBtn = currentLinkUI.querySelector( 'button' );
1148
+ let currentLinkUI = screen.getByLabelText( 'Currently selected' );
1149
+ const currentLinkBtn = within( currentLinkUI ).getByRole( 'button', {
1150
+ name: 'Edit',
1151
+ } );
1293
1152
 
1294
1153
  // Simulate searching for a term.
1295
1154
  await user.click( currentLinkBtn );
1296
1155
 
1297
- const searchInput = getURLInput();
1298
- currentLinkUI = getCurrentLink();
1156
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
1157
+ currentLinkUI = screen.queryByLabelText( 'Currently selected' );
1299
1158
 
1300
1159
  // We should be back to showing the search input.
1301
1160
  expect( searchInput ).toBeVisible();
@@ -1331,32 +1190,32 @@ describe( 'Selecting links', () => {
1331
1190
  );
1332
1191
  };
1333
1192
 
1334
- const { container } = render( <LinkControlConsumer /> );
1193
+ render( <LinkControlConsumer /> );
1335
1194
 
1336
1195
  // Search Input UI.
1337
- const searchInput = getURLInput();
1196
+ const searchInput = screen.getByRole( 'combobox', {
1197
+ name: 'URL',
1198
+ } );
1338
1199
 
1339
1200
  // Simulate searching for a term.
1340
- searchInput.focus();
1341
- await user.keyboard( searchTerm );
1342
-
1343
- // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
1344
- await eventLoopTick();
1201
+ await user.type( searchInput, searchTerm );
1345
1202
 
1346
- const searchResultElements = getSearchResults( container );
1203
+ const searchResultElements = within(
1204
+ await screen.findByRole( 'listbox', {
1205
+ name: /Search results for.*/,
1206
+ } )
1207
+ ).getAllByRole( 'option' );
1347
1208
 
1348
1209
  const firstSearchSuggestion = searchResultElements[ 0 ];
1349
1210
 
1350
1211
  // Simulate selecting the first of the search suggestions.
1351
1212
  await user.click( firstSearchSuggestion );
1352
1213
 
1353
- const currentLink = getCurrentLink();
1354
- const currentLinkAnchor = currentLink.querySelector(
1355
- `[href="${ selectedLink.url }"]`
1356
- );
1214
+ const currentLinkAnchor = screen.getByRole( 'link', {
1215
+ name: `${ selectedLink.title } (opens in a new tab)`,
1216
+ } );
1357
1217
 
1358
1218
  // Check that this suggestion is now shown as selected.
1359
- expect( currentLink ).toHaveTextContent( selectedLink.title );
1360
1219
  expect(
1361
1220
  screen.getByRole( 'button', { name: 'Edit' } )
1362
1221
  ).toBeVisible();
@@ -1396,27 +1255,32 @@ describe( 'Selecting links', () => {
1396
1255
  const { container } = render( <LinkControlConsumer /> );
1397
1256
 
1398
1257
  // Search Input UI.
1399
- const searchInput = getURLInput();
1258
+ const searchInput = screen.getByRole( 'combobox', {
1259
+ name: 'URL',
1260
+ } );
1400
1261
 
1401
1262
  // Simulate searching for a term.
1402
- searchInput.focus();
1403
- await user.keyboard( searchTerm );
1263
+ await user.type( searchInput, searchTerm );
1404
1264
 
1405
- // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
1406
- await eventLoopTick();
1265
+ const searchResults = await screen.findByRole( 'listbox', {
1266
+ name: /Search results for.*/,
1267
+ } );
1407
1268
 
1408
1269
  // Step down into the search results, highlighting the first result item.
1409
1270
  triggerArrowDown( searchInput );
1410
1271
 
1411
- const searchResultElements = getSearchResults( container );
1272
+ const searchResultElements =
1273
+ within( searchResults ).getAllByRole( 'option' );
1412
1274
 
1413
1275
  const firstSearchSuggestion = searchResultElements[ 0 ];
1414
1276
  const secondSearchSuggestion = searchResultElements[ 1 ];
1415
1277
 
1416
- let selectedSearchResultElement = getSelectedResultElement();
1278
+ let selectedSearchResultElement = screen.getByRole( 'option', {
1279
+ selected: true,
1280
+ } );
1417
1281
 
1418
1282
  // We should have highlighted the first item using the keyboard.
1419
- expect( selectedSearchResultElement ).toEqual(
1283
+ expect( selectedSearchResultElement ).toBe(
1420
1284
  firstSearchSuggestion
1421
1285
  );
1422
1286
 
@@ -1425,22 +1289,26 @@ describe( 'Selecting links', () => {
1425
1289
  // Check we can go down again using the down arrow.
1426
1290
  triggerArrowDown( searchInput );
1427
1291
 
1428
- selectedSearchResultElement = getSelectedResultElement();
1292
+ selectedSearchResultElement = screen.getByRole( 'option', {
1293
+ selected: true,
1294
+ } );
1429
1295
 
1430
1296
  // We should have highlighted the first item using the keyboard
1431
1297
  // eslint-disable-next-line jest/no-conditional-expect
1432
- expect( selectedSearchResultElement ).toEqual(
1298
+ expect( selectedSearchResultElement ).toBe(
1433
1299
  secondSearchSuggestion
1434
1300
  );
1435
1301
 
1436
1302
  // Check we can go back up via up arrow.
1437
1303
  triggerArrowUp( searchInput );
1438
1304
 
1439
- selectedSearchResultElement = getSelectedResultElement();
1305
+ selectedSearchResultElement = screen.getByRole( 'option', {
1306
+ selected: true,
1307
+ } );
1440
1308
 
1441
1309
  // We should be back to highlighting the first search result again
1442
1310
  // eslint-disable-next-line jest/no-conditional-expect
1443
- expect( selectedSearchResultElement ).toEqual(
1311
+ expect( selectedSearchResultElement ).toBe(
1444
1312
  firstSearchSuggestion
1445
1313
  );
1446
1314
  }
@@ -1449,15 +1317,16 @@ describe( 'Selecting links', () => {
1449
1317
  triggerEnter( searchInput );
1450
1318
 
1451
1319
  // Check that the suggestion selected via is now shown as selected.
1452
- const currentLink = getCurrentLink();
1453
- const currentLinkAnchor = currentLink.querySelector(
1454
- `[href="${ selectedLink.url }"]`
1455
- );
1320
+ const currentLink =
1321
+ screen.getByLabelText( 'Currently selected' );
1322
+ const currentLinkAnchor = screen.getByRole( 'link', {
1323
+ name: `${ selectedLink.title } (opens in a new tab)`,
1324
+ } );
1456
1325
 
1457
1326
  // Make sure focus is retained after submission.
1458
1327
  expect( container ).toContainElement( document.activeElement );
1459
1328
 
1460
- expect( currentLink ).toHaveTextContent( selectedLink.title );
1329
+ expect( currentLink ).toBeVisible();
1461
1330
  expect(
1462
1331
  screen.getByRole( 'button', { name: 'Edit' } )
1463
1332
  ).toBeVisible();
@@ -1466,32 +1335,32 @@ describe( 'Selecting links', () => {
1466
1335
  );
1467
1336
 
1468
1337
  it( 'should allow selection of initial search results via the keyboard', async () => {
1469
- const { container } = render(
1470
- <LinkControl showInitialSuggestions />
1471
- );
1472
-
1473
- await eventLoopTick();
1338
+ render( <LinkControl showInitialSuggestions /> );
1474
1339
 
1475
1340
  expect(
1476
- screen.queryByRole( 'listbox', {
1341
+ await screen.findByRole( 'listbox', {
1477
1342
  name: 'Recently updated',
1478
1343
  } )
1479
1344
  ).toBeVisible();
1480
1345
 
1481
1346
  // Search Input UI.
1482
- const searchInput = getURLInput();
1347
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
1483
1348
 
1484
1349
  // Step down into the search results, highlighting the first result item.
1485
1350
  triggerArrowDown( searchInput );
1486
1351
 
1487
- await eventLoopTick();
1488
-
1489
- const searchResultElements = getSearchResults( container );
1352
+ const searchResultElements = within(
1353
+ screen.getByRole( 'listbox', {
1354
+ name: 'Recently updated',
1355
+ } )
1356
+ ).getAllByRole( 'option' );
1490
1357
 
1491
1358
  const firstSearchSuggestion = searchResultElements[ 0 ];
1492
1359
  const secondSearchSuggestion = searchResultElements[ 1 ];
1493
1360
 
1494
- let selectedSearchResultElement = getSelectedResultElement();
1361
+ let selectedSearchResultElement = screen.getByRole( 'option', {
1362
+ selected: true,
1363
+ } );
1495
1364
 
1496
1365
  // We should have highlighted the first item using the keyboard.
1497
1366
  expect( selectedSearchResultElement ).toEqual(
@@ -1501,7 +1370,9 @@ describe( 'Selecting links', () => {
1501
1370
  // Check we can go down again using the down arrow.
1502
1371
  triggerArrowDown( searchInput );
1503
1372
 
1504
- selectedSearchResultElement = getSelectedResultElement();
1373
+ selectedSearchResultElement = screen.getByRole( 'option', {
1374
+ selected: true,
1375
+ } );
1505
1376
 
1506
1377
  // We should have highlighted the first item using the keyboard.
1507
1378
  expect( selectedSearchResultElement ).toEqual(
@@ -1511,7 +1382,9 @@ describe( 'Selecting links', () => {
1511
1382
  // Check we can go back up via up arrow.
1512
1383
  triggerArrowUp( searchInput );
1513
1384
 
1514
- selectedSearchResultElement = getSelectedResultElement();
1385
+ selectedSearchResultElement = screen.getByRole( 'option', {
1386
+ selected: true,
1387
+ } );
1515
1388
 
1516
1389
  // We should be back to highlighting the first search result again.
1517
1390
  expect( selectedSearchResultElement ).toEqual(
@@ -1534,18 +1407,17 @@ describe( 'Addition Settings UI', () => {
1534
1407
  return <LinkControl value={ link } />;
1535
1408
  };
1536
1409
 
1537
- const { container } = render( <LinkControlConsumer /> );
1410
+ render( <LinkControlConsumer /> );
1538
1411
 
1539
1412
  const newTabSettingLabel = screen.getByText( expectedSettingText );
1540
1413
  expect( newTabSettingLabel ).toBeVisible();
1541
1414
 
1542
- const newTabSettingLabelForAttr =
1543
- newTabSettingLabel.getAttribute( 'for' );
1544
- const newTabSettingInput = container.querySelector(
1545
- `#${ newTabSettingLabelForAttr }`
1546
- );
1415
+ const newTabSettingInput = screen.getByRole( 'checkbox', {
1416
+ name: expectedSettingText,
1417
+ checked: false,
1418
+ } );
1419
+
1547
1420
  expect( newTabSettingInput ).toBeVisible();
1548
- expect( newTabSettingInput ).not.toBeChecked();
1549
1421
  } );
1550
1422
 
1551
1423
  it( 'should display a setting control with correct default state for each of the custom settings provided', async () => {
@@ -1595,19 +1467,19 @@ describe( 'Post types', () => {
1595
1467
  const user = userEvent.setup();
1596
1468
  const searchTerm = 'Hello world';
1597
1469
 
1598
- const { container } = render( <LinkControl /> );
1470
+ render( <LinkControl /> );
1599
1471
 
1600
1472
  // Search Input UI.
1601
- const searchInput = getURLInput();
1473
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
1602
1474
 
1603
1475
  // Simulate searching for a term.
1604
- searchInput.focus();
1605
- await user.keyboard( searchTerm );
1476
+ await user.type( searchInput, searchTerm );
1606
1477
 
1607
- // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
1608
- await eventLoopTick();
1609
-
1610
- const searchResultElements = getSearchResults( container );
1478
+ const searchResultElements = within(
1479
+ await screen.findByRole( 'listbox', {
1480
+ name: /Search results for.*/,
1481
+ } )
1482
+ ).getAllByRole( 'option' );
1611
1483
 
1612
1484
  searchResultElements.forEach( ( resultItem, index ) => {
1613
1485
  expect( resultItem ).toHaveTextContent(
@@ -1622,21 +1494,19 @@ describe( 'Post types', () => {
1622
1494
  const user = userEvent.setup();
1623
1495
  const searchTerm = 'Hello world';
1624
1496
 
1625
- const { container } = render(
1626
- <LinkControl suggestionsQuery={ { type: postType } } />
1627
- );
1497
+ render( <LinkControl suggestionsQuery={ { type: postType } } /> );
1628
1498
 
1629
1499
  // Search Input UI.
1630
- const searchInput = getURLInput();
1500
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
1631
1501
 
1632
1502
  // Simulate searching for a term.
1633
- searchInput.focus();
1634
- await user.keyboard( searchTerm );
1635
-
1636
- // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
1637
- await eventLoopTick();
1503
+ await user.type( searchInput, searchTerm );
1638
1504
 
1639
- const searchResultElements = getSearchResults( container );
1505
+ const searchResultElements = within(
1506
+ await screen.findByRole( 'listbox', {
1507
+ name: /Search results for.*/,
1508
+ } )
1509
+ ).getAllByRole( 'option' );
1640
1510
 
1641
1511
  searchResultElements.forEach( ( resultItem, index ) => {
1642
1512
  expect(
@@ -1681,12 +1551,7 @@ describe( 'Rich link previews', () => {
1681
1551
 
1682
1552
  render( <LinkControl value={ selectedLink } /> );
1683
1553
 
1684
- // mockFetchRichUrlData resolves on next "tick" of event loop.
1685
- await act( async () => {
1686
- await eventLoopTick();
1687
- } );
1688
-
1689
- const linkPreview = getCurrentLink();
1554
+ const linkPreview = screen.getByLabelText( 'Currently selected' );
1690
1555
 
1691
1556
  const isRichLinkPreview = linkPreview.classList.contains( 'is-rich' );
1692
1557
 
@@ -1707,16 +1572,9 @@ describe( 'Rich link previews', () => {
1707
1572
 
1708
1573
  render( <LinkControl value={ selectedLink } hasRichPreviews /> );
1709
1574
 
1710
- // mockFetchRichUrlData resolves on next "tick" of event loop.
1711
- await act( async () => {
1712
- await eventLoopTick();
1713
- } );
1714
-
1715
- const linkPreview = getCurrentLink();
1575
+ const linkPreview = screen.getByLabelText( 'Currently selected' );
1716
1576
 
1717
- const isRichLinkPreview = linkPreview.classList.contains( 'is-rich' );
1718
-
1719
- expect( isRichLinkPreview ).toBe( true );
1577
+ await waitFor( () => expect( linkPreview ).toHaveClass( 'is-rich' ) );
1720
1578
  } );
1721
1579
 
1722
1580
  it( 'should not display placeholders for the image and description if neither is available in the data', async () => {
@@ -1731,12 +1589,9 @@ describe( 'Rich link previews', () => {
1731
1589
 
1732
1590
  render( <LinkControl value={ selectedLink } hasRichPreviews /> );
1733
1591
 
1734
- // mockFetchRichUrlData resolves on next "tick" of event loop.
1735
- await act( async () => {
1736
- await eventLoopTick();
1737
- } );
1592
+ const linkPreview = screen.getByLabelText( 'Currently selected' );
1738
1593
 
1739
- const linkPreview = getCurrentLink();
1594
+ await waitFor( () => expect( linkPreview ).toHaveClass( 'is-rich' ) );
1740
1595
 
1741
1596
  // Todo: refactor to use user-facing queries.
1742
1597
  const hasRichImagePreview = linkPreview.querySelector(
@@ -1764,21 +1619,15 @@ describe( 'Rich link previews', () => {
1764
1619
 
1765
1620
  render( <LinkControl value={ selectedLink } hasRichPreviews /> );
1766
1621
 
1767
- // mockFetchRichUrlData resolves on next "tick" of event loop.
1768
- await act( async () => {
1769
- await eventLoopTick();
1770
- } );
1622
+ const linkPreview = screen.getByLabelText( 'Currently selected' );
1771
1623
 
1772
- const linkPreview = getCurrentLink();
1624
+ await waitFor( () => expect( linkPreview ).toHaveClass( 'is-rich' ) );
1773
1625
 
1774
- const isRichLinkPreview = linkPreview.classList.contains( 'is-rich' );
1775
- expect( isRichLinkPreview ).toBe( true );
1626
+ const titlePreview = screen.getByText( selectedLink.title );
1776
1627
 
1777
- const titlePreview = linkPreview.querySelector(
1778
- '.block-editor-link-control__search-item-title'
1628
+ expect( titlePreview ).toHaveClass(
1629
+ 'block-editor-link-control__search-item-title'
1779
1630
  );
1780
-
1781
- expect( titlePreview ).toHaveTextContent( selectedLink.title );
1782
1631
  } );
1783
1632
 
1784
1633
  it( 'should display a fallback when icon is missing from rich data', async () => {
@@ -1793,15 +1642,9 @@ describe( 'Rich link previews', () => {
1793
1642
 
1794
1643
  render( <LinkControl value={ selectedLink } hasRichPreviews /> );
1795
1644
 
1796
- // mockFetchRichUrlData resolves on next "tick" of event loop.
1797
- await act( async () => {
1798
- await eventLoopTick();
1799
- } );
1645
+ const linkPreview = screen.getByLabelText( 'Currently selected' );
1800
1646
 
1801
- const linkPreview = getCurrentLink();
1802
-
1803
- const isRichLinkPreview = linkPreview.classList.contains( 'is-rich' );
1804
- expect( isRichLinkPreview ).toBe( true );
1647
+ await waitFor( () => expect( linkPreview ).toHaveClass( 'is-rich' ) );
1805
1648
 
1806
1649
  const iconPreview = linkPreview.querySelector(
1807
1650
  `.block-editor-link-control__search-item-icon`
@@ -1831,16 +1674,11 @@ describe( 'Rich link previews', () => {
1831
1674
 
1832
1675
  render( <LinkControl value={ selectedLink } hasRichPreviews /> );
1833
1676
 
1834
- // mockFetchRichUrlData resolves on next "tick" of event loop.
1835
- await act( async () => {
1836
- await eventLoopTick();
1837
- } );
1838
-
1839
- const linkPreview = getCurrentLink();
1677
+ const linkPreview = screen.getByLabelText( 'Currently selected' );
1840
1678
 
1841
- const isRichLinkPreview =
1842
- linkPreview.classList.contains( 'is-rich' );
1843
- expect( isRichLinkPreview ).toBe( true );
1679
+ await waitFor( () =>
1680
+ expect( linkPreview ).toHaveClass( 'is-rich' )
1681
+ );
1844
1682
 
1845
1683
  const missingDataItem = linkPreview.querySelector(
1846
1684
  `.block-editor-link-control__search-item-${ dataItem }`
@@ -1856,23 +1694,19 @@ describe( 'Rich link previews', () => {
1856
1694
  ] )(
1857
1695
  'should not display a rich preview when data is %s',
1858
1696
  async ( _descriptor, data ) => {
1859
- mockFetchRichUrlData.mockImplementation( () =>
1860
- Promise.resolve( data )
1861
- );
1697
+ mockFetchRichUrlData.mockImplementation( async () => data );
1862
1698
 
1863
1699
  render( <LinkControl value={ selectedLink } hasRichPreviews /> );
1864
1700
 
1865
- // mockFetchRichUrlData resolves on next "tick" of event loop.
1866
- await act( async () => {
1867
- await eventLoopTick();
1868
- } );
1701
+ const linkPreview = screen.getByLabelText( 'Currently selected' );
1869
1702
 
1870
- const linkPreview = getCurrentLink();
1703
+ expect( linkPreview ).toHaveClass( 'is-fetching' );
1871
1704
 
1872
- const isRichLinkPreview =
1873
- linkPreview.classList.contains( 'is-rich' );
1705
+ await waitFor( () =>
1706
+ expect( linkPreview ).not.toHaveClass( 'is-fetching' )
1707
+ );
1874
1708
 
1875
- expect( isRichLinkPreview ).toBe( false );
1709
+ expect( linkPreview ).not.toHaveClass( 'is-rich' );
1876
1710
  }
1877
1711
  );
1878
1712
 
@@ -1883,19 +1717,10 @@ describe( 'Rich link previews', () => {
1883
1717
 
1884
1718
  render( <LinkControl value={ selectedLink } hasRichPreviews /> );
1885
1719
 
1886
- // mockFetchRichUrlData resolves on next "tick" of event loop.
1887
- await act( async () => {
1888
- await eventLoopTick();
1889
- } );
1890
-
1891
- const linkPreview = getCurrentLink();
1892
-
1893
- const isFetchingRichPreview =
1894
- linkPreview.classList.contains( 'is-fetching' );
1895
- const isRichLinkPreview = linkPreview.classList.contains( 'is-rich' );
1720
+ const linkPreview = screen.getByLabelText( 'Currently selected' );
1896
1721
 
1897
- expect( isFetchingRichPreview ).toBe( true );
1898
- expect( isRichLinkPreview ).toBe( false );
1722
+ expect( linkPreview ).toHaveClass( 'is-fetching' );
1723
+ expect( linkPreview ).not.toHaveClass( 'is-rich' );
1899
1724
  } );
1900
1725
 
1901
1726
  it( 'should remove fetching UI indicators and fallback to standard preview if request for rich preview results in an error', async () => {
@@ -1905,20 +1730,15 @@ describe( 'Rich link previews', () => {
1905
1730
 
1906
1731
  render( <LinkControl value={ selectedLink } hasRichPreviews /> );
1907
1732
 
1908
- // mockFetchRichUrlData resolves on next "tick" of event loop.
1909
- await act( async () => {
1910
- await eventLoopTick();
1911
- } );
1912
-
1913
- const linkPreview = getCurrentLink();
1733
+ const linkPreview = screen.getByLabelText( 'Currently selected' );
1914
1734
 
1915
- const isFetchingRichPreview =
1916
- linkPreview.classList.contains( 'is-fetching' );
1735
+ expect( linkPreview ).toHaveClass( 'is-fetching' );
1917
1736
 
1918
- const isRichLinkPreview = linkPreview.classList.contains( 'is-rich' );
1737
+ await waitFor( () =>
1738
+ expect( linkPreview ).not.toHaveClass( 'is-fetching' )
1739
+ );
1919
1740
 
1920
- expect( isFetchingRichPreview ).toBe( false );
1921
- expect( isRichLinkPreview ).toBe( false );
1741
+ expect( linkPreview ).not.toHaveClass( 'is-rich' );
1922
1742
  } );
1923
1743
 
1924
1744
  afterAll( () => {
@@ -2019,8 +1839,7 @@ describe( 'Controlling link title text', () => {
2019
1839
 
2020
1840
  const textInput = screen.queryByRole( 'textbox', { name: 'Text' } );
2021
1841
 
2022
- textInput.focus();
2023
- await userEvent.clear( textInput );
1842
+ await user.clear( textInput );
2024
1843
  await user.keyboard( textValue );
2025
1844
 
2026
1845
  expect( textInput ).toHaveValue( textValue );
@@ -2056,8 +1875,7 @@ describe( 'Controlling link title text', () => {
2056
1875
 
2057
1876
  expect( textInput ).toBeVisible();
2058
1877
 
2059
- textInput.focus();
2060
- await userEvent.clear( textInput );
1878
+ await user.clear( textInput );
2061
1879
  await user.keyboard( textValue );
2062
1880
 
2063
1881
  // Attempt to submit the empty search value in the input.