@wordpress/block-editor 10.4.0 → 10.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +0 -1
  3. package/build/components/block-lock/menu-item.js +1 -1
  4. package/build/components/block-lock/menu-item.js.map +1 -1
  5. package/build/components/block-lock/modal.js +16 -9
  6. package/build/components/block-lock/modal.js.map +1 -1
  7. package/build/components/block-styles/utils.js +3 -3
  8. package/build/components/block-styles/utils.js.map +1 -1
  9. package/build/components/block-switcher/index.js +19 -4
  10. package/build/components/block-switcher/index.js.map +1 -1
  11. package/build/components/block-tools/selected-block-popover.js +27 -4
  12. package/build/components/block-tools/selected-block-popover.js.map +1 -1
  13. package/build/components/colors/with-colors.js +4 -3
  14. package/build/components/colors/with-colors.js.map +1 -1
  15. package/build/components/font-sizes/fluid-utils.js +24 -40
  16. package/build/components/font-sizes/fluid-utils.js.map +1 -1
  17. package/build/components/font-sizes/with-font-sizes.js +7 -5
  18. package/build/components/font-sizes/with-font-sizes.js.map +1 -1
  19. package/build/components/index.js +9 -0
  20. package/build/components/index.js.map +1 -1
  21. package/build/components/inner-blocks/index.js +5 -3
  22. package/build/components/inner-blocks/index.js.map +1 -1
  23. package/build/components/inserter/reusable-blocks-tab.js +4 -1
  24. package/build/components/inserter/reusable-blocks-tab.js.map +1 -1
  25. package/build/components/link-control/index.js +18 -34
  26. package/build/components/link-control/index.js.map +1 -1
  27. package/build/components/link-control/search-input.js +1 -1
  28. package/build/components/link-control/search-input.js.map +1 -1
  29. package/build/components/link-control/use-internal-input-value.js +26 -0
  30. package/build/components/link-control/use-internal-input-value.js.map +1 -0
  31. package/build/components/list-view/block.js +5 -3
  32. package/build/components/list-view/block.js.map +1 -1
  33. package/build/components/list-view/branch.js +9 -3
  34. package/build/components/list-view/branch.js.map +1 -1
  35. package/build/components/off-canvas-editor/block-contents.js +100 -0
  36. package/build/components/off-canvas-editor/block-contents.js.map +1 -0
  37. package/build/components/off-canvas-editor/block-select-button.js +119 -0
  38. package/build/components/off-canvas-editor/block-select-button.js.map +1 -0
  39. package/build/components/off-canvas-editor/block.js +292 -0
  40. package/build/components/off-canvas-editor/block.js.map +1 -0
  41. package/build/components/off-canvas-editor/branch.js +181 -0
  42. package/build/components/off-canvas-editor/branch.js.map +1 -0
  43. package/build/components/off-canvas-editor/context.js +19 -0
  44. package/build/components/off-canvas-editor/context.js.map +1 -0
  45. package/build/components/off-canvas-editor/drop-indicator.js +118 -0
  46. package/build/components/off-canvas-editor/drop-indicator.js.map +1 -0
  47. package/build/components/off-canvas-editor/expander.js +41 -0
  48. package/build/components/off-canvas-editor/expander.js.map +1 -0
  49. package/build/components/off-canvas-editor/index.js +204 -0
  50. package/build/components/off-canvas-editor/index.js.map +1 -0
  51. package/build/components/off-canvas-editor/leaf.js +60 -0
  52. package/build/components/off-canvas-editor/leaf.js.map +1 -0
  53. package/build/components/off-canvas-editor/use-block-selection.js +139 -0
  54. package/build/components/off-canvas-editor/use-block-selection.js.map +1 -0
  55. package/build/components/off-canvas-editor/use-list-view-client-ids.js +33 -0
  56. package/build/components/off-canvas-editor/use-list-view-client-ids.js.map +1 -0
  57. package/build/components/off-canvas-editor/use-list-view-drop-zone.js +235 -0
  58. package/build/components/off-canvas-editor/use-list-view-drop-zone.js.map +1 -0
  59. package/build/components/off-canvas-editor/use-list-view-expand-selected-item.js +60 -0
  60. package/build/components/off-canvas-editor/use-list-view-expand-selected-item.js.map +1 -0
  61. package/build/components/off-canvas-editor/utils.js +60 -0
  62. package/build/components/off-canvas-editor/utils.js.map +1 -0
  63. package/build/components/url-popover/index.js +31 -2
  64. package/build/components/url-popover/index.js.map +1 -1
  65. package/build/components/use-setting/index.js +1 -1
  66. package/build/components/use-setting/index.js.map +1 -1
  67. package/build/hooks/color-panel.js +17 -1
  68. package/build/hooks/color-panel.js.map +1 -1
  69. package/build/hooks/color.js +1 -1
  70. package/build/hooks/color.js.map +1 -1
  71. package/build/hooks/content-lock-ui.js +13 -6
  72. package/build/hooks/content-lock-ui.js.map +1 -1
  73. package/build/hooks/dimensions.js +44 -13
  74. package/build/hooks/dimensions.js.map +1 -1
  75. package/build/hooks/layout.js +2 -2
  76. package/build/hooks/layout.js.map +1 -1
  77. package/build/hooks/margin.js +4 -2
  78. package/build/hooks/margin.js.map +1 -1
  79. package/build/hooks/min-height.js +145 -0
  80. package/build/hooks/min-height.js.map +1 -0
  81. package/build/hooks/padding.js +4 -2
  82. package/build/hooks/padding.js.map +1 -1
  83. package/build/hooks/style.js +3 -2
  84. package/build/hooks/style.js.map +1 -1
  85. package/build/layouts/flex.js +22 -21
  86. package/build/layouts/flex.js.map +1 -1
  87. package/build/store/actions.js +26 -0
  88. package/build/store/actions.js.map +1 -1
  89. package/build/store/reducer.js +46 -14
  90. package/build/store/reducer.js.map +1 -1
  91. package/build/store/selectors.js +16 -2
  92. package/build/store/selectors.js.map +1 -1
  93. package/build-module/components/block-lock/menu-item.js +2 -2
  94. package/build-module/components/block-lock/menu-item.js.map +1 -1
  95. package/build-module/components/block-lock/modal.js +17 -10
  96. package/build-module/components/block-lock/modal.js.map +1 -1
  97. package/build-module/components/block-styles/utils.js +3 -3
  98. package/build-module/components/block-styles/utils.js.map +1 -1
  99. package/build-module/components/block-switcher/index.js +19 -4
  100. package/build-module/components/block-switcher/index.js.map +1 -1
  101. package/build-module/components/block-tools/selected-block-popover.js +27 -5
  102. package/build-module/components/block-tools/selected-block-popover.js.map +1 -1
  103. package/build-module/components/colors/with-colors.js +5 -4
  104. package/build-module/components/colors/with-colors.js.map +1 -1
  105. package/build-module/components/font-sizes/fluid-utils.js +24 -40
  106. package/build-module/components/font-sizes/fluid-utils.js.map +1 -1
  107. package/build-module/components/font-sizes/with-font-sizes.js +8 -6
  108. package/build-module/components/font-sizes/with-font-sizes.js.map +1 -1
  109. package/build-module/components/index.js +1 -0
  110. package/build-module/components/index.js.map +1 -1
  111. package/build-module/components/inner-blocks/index.js +5 -3
  112. package/build-module/components/inner-blocks/index.js.map +1 -1
  113. package/build-module/components/inserter/reusable-blocks-tab.js +3 -1
  114. package/build-module/components/inserter/reusable-blocks-tab.js.map +1 -1
  115. package/build-module/components/link-control/index.js +17 -34
  116. package/build-module/components/link-control/index.js.map +1 -1
  117. package/build-module/components/link-control/search-input.js +1 -1
  118. package/build-module/components/link-control/search-input.js.map +1 -1
  119. package/build-module/components/link-control/use-internal-input-value.js +18 -0
  120. package/build-module/components/link-control/use-internal-input-value.js.map +1 -0
  121. package/build-module/components/list-view/block.js +5 -3
  122. package/build-module/components/list-view/block.js.map +1 -1
  123. package/build-module/components/list-view/branch.js +9 -3
  124. package/build-module/components/list-view/branch.js.map +1 -1
  125. package/build-module/components/off-canvas-editor/block-contents.js +85 -0
  126. package/build-module/components/off-canvas-editor/block-contents.js.map +1 -0
  127. package/build-module/components/off-canvas-editor/block-select-button.js +101 -0
  128. package/build-module/components/off-canvas-editor/block-select-button.js.map +1 -0
  129. package/build-module/components/off-canvas-editor/block.js +268 -0
  130. package/build-module/components/off-canvas-editor/block.js.map +1 -0
  131. package/build-module/components/off-canvas-editor/branch.js +165 -0
  132. package/build-module/components/off-canvas-editor/branch.js.map +1 -0
  133. package/build-module/components/off-canvas-editor/context.js +7 -0
  134. package/build-module/components/off-canvas-editor/context.js.map +1 -0
  135. package/build-module/components/off-canvas-editor/drop-indicator.js +111 -0
  136. package/build-module/components/off-canvas-editor/drop-indicator.js.map +1 -0
  137. package/build-module/components/off-canvas-editor/expander.js +32 -0
  138. package/build-module/components/off-canvas-editor/expander.js.map +1 -0
  139. package/build-module/components/off-canvas-editor/index.js +181 -0
  140. package/build-module/components/off-canvas-editor/index.js.map +1 -0
  141. package/build-module/components/off-canvas-editor/leaf.js +45 -0
  142. package/build-module/components/off-canvas-editor/leaf.js.map +1 -0
  143. package/build-module/components/off-canvas-editor/use-block-selection.js +124 -0
  144. package/build-module/components/off-canvas-editor/use-block-selection.js.map +1 -0
  145. package/build-module/components/off-canvas-editor/use-list-view-client-ids.js +24 -0
  146. package/build-module/components/off-canvas-editor/use-list-view-client-ids.js.map +1 -0
  147. package/build-module/components/off-canvas-editor/use-list-view-drop-zone.js +220 -0
  148. package/build-module/components/off-canvas-editor/use-list-view-drop-zone.js.map +1 -0
  149. package/build-module/components/off-canvas-editor/use-list-view-expand-selected-item.js +50 -0
  150. package/build-module/components/off-canvas-editor/use-list-view-expand-selected-item.js.map +1 -0
  151. package/build-module/components/off-canvas-editor/utils.js +44 -0
  152. package/build-module/components/off-canvas-editor/utils.js.map +1 -0
  153. package/build-module/components/url-popover/index.js +30 -3
  154. package/build-module/components/url-popover/index.js.map +1 -1
  155. package/build-module/components/use-setting/index.js +1 -1
  156. package/build-module/components/use-setting/index.js.map +1 -1
  157. package/build-module/hooks/color-panel.js +17 -1
  158. package/build-module/hooks/color-panel.js.map +1 -1
  159. package/build-module/hooks/color.js +1 -1
  160. package/build-module/hooks/color.js.map +1 -1
  161. package/build-module/hooks/content-lock-ui.js +15 -8
  162. package/build-module/hooks/content-lock-ui.js.map +1 -1
  163. package/build-module/hooks/dimensions.js +39 -12
  164. package/build-module/hooks/dimensions.js.map +1 -1
  165. package/build-module/hooks/layout.js +2 -2
  166. package/build-module/hooks/layout.js.map +1 -1
  167. package/build-module/hooks/margin.js +4 -2
  168. package/build-module/hooks/margin.js.map +1 -1
  169. package/build-module/hooks/min-height.js +122 -0
  170. package/build-module/hooks/min-height.js.map +1 -0
  171. package/build-module/hooks/padding.js +4 -2
  172. package/build-module/hooks/padding.js.map +1 -1
  173. package/build-module/hooks/style.js +4 -3
  174. package/build-module/hooks/style.js.map +1 -1
  175. package/build-module/layouts/flex.js +23 -22
  176. package/build-module/layouts/flex.js.map +1 -1
  177. package/build-module/store/actions.js +22 -0
  178. package/build-module/store/actions.js.map +1 -1
  179. package/build-module/store/reducer.js +44 -14
  180. package/build-module/store/reducer.js.map +1 -1
  181. package/build-module/store/selectors.js +13 -2
  182. package/build-module/store/selectors.js.map +1 -1
  183. package/build-style/style-rtl.css +39 -26
  184. package/build-style/style.css +39 -26
  185. package/package.json +28 -28
  186. package/src/components/alignment-control/README.md +1 -1
  187. package/src/components/block-alignment-control/test/index.native.js +4 -4
  188. package/src/components/block-draggable/test/helpers.native.js +3 -3
  189. package/src/components/block-draggable/test/index.native.js +27 -27
  190. package/src/components/block-list/style.scss +10 -5
  191. package/src/components/block-lock/menu-item.js +5 -2
  192. package/src/components/block-lock/modal.js +19 -36
  193. package/src/components/block-lock/style.scss +8 -17
  194. package/src/components/block-mover/style.scss +0 -1
  195. package/src/components/block-popover/style.scss +1 -1
  196. package/src/components/block-styles/utils.js +3 -3
  197. package/src/components/block-switcher/index.js +19 -4
  198. package/src/components/block-tools/selected-block-popover.js +80 -34
  199. package/src/components/block-tools/style.scss +15 -0
  200. package/src/components/colors/with-colors.js +13 -23
  201. package/src/components/default-block-appender/style.scss +1 -0
  202. package/src/components/font-sizes/fluid-utils.js +37 -64
  203. package/src/components/font-sizes/test/fluid-utils.js +5 -5
  204. package/src/components/font-sizes/with-font-sizes.js +14 -11
  205. package/src/components/index.js +1 -0
  206. package/src/components/inner-blocks/index.js +7 -4
  207. package/src/components/inserter/reusable-blocks-tab.js +4 -2
  208. package/src/components/inserter/style.scss +8 -7
  209. package/src/components/inserter/test/reusable-blocks-tab.js +14 -57
  210. package/src/components/link-control/index.js +23 -39
  211. package/src/components/link-control/search-input.js +1 -1
  212. package/src/components/link-control/test/index.js +272 -241
  213. package/src/components/link-control/use-internal-input-value.js +22 -0
  214. package/src/components/list-view/block.js +4 -3
  215. package/src/components/list-view/branch.js +11 -6
  216. package/src/components/off-canvas-editor/README.md +5 -0
  217. package/src/components/off-canvas-editor/block-contents.js +89 -0
  218. package/src/components/off-canvas-editor/block-select-button.js +113 -0
  219. package/src/components/off-canvas-editor/block.js +335 -0
  220. package/src/components/off-canvas-editor/branch.js +210 -0
  221. package/src/components/off-canvas-editor/context.js +8 -0
  222. package/src/components/off-canvas-editor/drop-indicator.js +126 -0
  223. package/src/components/off-canvas-editor/expander.js +26 -0
  224. package/src/components/off-canvas-editor/index.js +216 -0
  225. package/src/components/off-canvas-editor/leaf.js +48 -0
  226. package/src/components/off-canvas-editor/style.scss +397 -0
  227. package/src/components/off-canvas-editor/test/utils.js +50 -0
  228. package/src/components/off-canvas-editor/use-block-selection.js +169 -0
  229. package/src/components/off-canvas-editor/use-list-view-client-ids.js +29 -0
  230. package/src/components/off-canvas-editor/use-list-view-drop-zone.js +260 -0
  231. package/src/components/off-canvas-editor/use-list-view-expand-selected-item.js +58 -0
  232. package/src/components/off-canvas-editor/utils.js +58 -0
  233. package/src/components/responsive-block-control/test/index.js +69 -92
  234. package/src/components/url-popover/README.md +12 -3
  235. package/src/components/url-popover/index.js +33 -3
  236. package/src/components/use-setting/index.js +7 -1
  237. package/src/hooks/color-panel.js +13 -1
  238. package/src/hooks/color.js +2 -0
  239. package/src/hooks/content-lock-ui.js +46 -34
  240. package/src/hooks/dimensions.js +76 -16
  241. package/src/hooks/layout.js +2 -3
  242. package/src/hooks/margin.js +4 -3
  243. package/src/hooks/min-height.js +121 -0
  244. package/src/hooks/padding.js +4 -3
  245. package/src/hooks/style.js +10 -2
  246. package/src/hooks/test/style.js +4 -0
  247. package/src/hooks/test/use-typography-props.js +1 -1
  248. package/src/layouts/flex.js +43 -38
  249. package/src/store/actions.js +22 -0
  250. package/src/store/reducer.js +50 -40
  251. package/src/store/selectors.js +16 -9
  252. package/src/store/test/actions.js +18 -0
  253. package/src/store/test/reducer.js +40 -0
  254. package/src/store/test/selectors.js +19 -0
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import { act, fireEvent, render, screen } from '@testing-library/react';
4
+ import { act, fireEvent, render, screen, within } from '@testing-library/react';
5
5
  import userEvent from '@testing-library/user-event';
6
6
 
7
7
  /**
@@ -81,30 +81,6 @@ afterEach( () => {
81
81
  mockFetchRichUrlData?.mockReset(); // Conditionally reset as it may NOT be a mock.
82
82
  } );
83
83
 
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
84
  /**
109
85
  * Workaround to trigger an arrow up keypress event.
110
86
  *
@@ -176,7 +152,7 @@ describe( 'Basic rendering', () => {
176
152
  render( <LinkControl /> );
177
153
 
178
154
  // Search Input UI.
179
- const searchInput = getURLInput();
155
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
180
156
 
181
157
  expect( searchInput ).toBeInTheDocument();
182
158
  } );
@@ -207,7 +183,7 @@ describe( 'Basic rendering', () => {
207
183
  render( <LinkControl /> );
208
184
 
209
185
  // Search Input UI.
210
- const searchInput = getURLInput();
186
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
211
187
 
212
188
  // Simulate searching for a term.
213
189
  searchInput.focus();
@@ -220,7 +196,9 @@ describe( 'Basic rendering', () => {
220
196
  it( 'undefined', () => {
221
197
  render( <LinkControl value={ { url: 'https://example.com' } } /> );
222
198
 
223
- expect( getURLInput() ).not.toBeInTheDocument();
199
+ expect(
200
+ screen.queryByRole( 'combobox', { name: 'URL' } )
201
+ ).not.toBeInTheDocument();
224
202
  } );
225
203
 
226
204
  it( 'true', () => {
@@ -231,7 +209,9 @@ describe( 'Basic rendering', () => {
231
209
  />
232
210
  );
233
211
 
234
- expect( getURLInput() ).toBeVisible();
212
+ expect(
213
+ screen.getByRole( 'combobox', { name: 'URL' } )
214
+ ).toBeVisible();
235
215
  } );
236
216
 
237
217
  it( 'false', async () => {
@@ -247,7 +227,9 @@ describe( 'Basic rendering', () => {
247
227
 
248
228
  await user.click( editButton );
249
229
 
250
- expect( getURLInput() ).toBeVisible();
230
+ expect(
231
+ screen.getByRole( 'combobox', { name: 'URL' } )
232
+ ).toBeVisible();
251
233
 
252
234
  // If passed `forceIsEditingLink` of `false` while editing, should
253
235
  // forcefully reset to the preview state.
@@ -258,7 +240,9 @@ describe( 'Basic rendering', () => {
258
240
  />
259
241
  );
260
242
 
261
- expect( getURLInput() ).not.toBeInTheDocument();
243
+ expect(
244
+ screen.queryByRole( 'combobox', { name: 'URL' } )
245
+ ).not.toBeInTheDocument();
262
246
  } );
263
247
 
264
248
  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 +266,7 @@ describe( 'Basic rendering', () => {
282
266
  />
283
267
  );
284
268
 
285
- const linkPreview = getCurrentLink();
269
+ const linkPreview = screen.getByLabelText( 'Currently selected' );
286
270
 
287
271
  const isPreviewError = linkPreview.classList.contains( 'is-error' );
288
272
  expect( isPreviewError ).toBe( true );
@@ -338,10 +322,10 @@ describe( 'Searching for a link', () => {
338
322
 
339
323
  mockFetchSearchSuggestions.mockImplementation( fauxRequest );
340
324
 
341
- const { container } = render( <LinkControl /> );
325
+ render( <LinkControl /> );
342
326
 
343
327
  // Search Input UI.
344
- const searchInput = getURLInput();
328
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
345
329
 
346
330
  // Simulate searching for a term.
347
331
  searchInput.focus();
@@ -350,11 +334,11 @@ describe( 'Searching for a link', () => {
350
334
  // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
351
335
  await eventLoopTick();
352
336
 
353
- const searchResultElements = getSearchResults( container );
337
+ const searchResultsField = screen.queryByRole( 'listbox' );
354
338
 
355
339
  let loadingUI = screen.queryByRole( 'presentation' );
356
340
 
357
- expect( searchResultElements ).toHaveLength( 0 );
341
+ expect( searchResultsField ).not.toBeInTheDocument();
358
342
 
359
343
  expect( loadingUI ).toBeVisible();
360
344
 
@@ -374,10 +358,10 @@ describe( 'Searching for a link', () => {
374
358
  const searchTerm = 'Hello world';
375
359
  const firstFauxSuggestion = fauxEntitySuggestions[ 0 ];
376
360
 
377
- const { container } = render( <LinkControl /> );
361
+ render( <LinkControl /> );
378
362
 
379
363
  // Search Input UI.
380
- const searchInput = getURLInput();
364
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
381
365
 
382
366
  // Simulate searching for a term.
383
367
  searchInput.focus();
@@ -386,7 +370,11 @@ describe( 'Searching for a link', () => {
386
370
  // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
387
371
  await eventLoopTick();
388
372
 
389
- const searchResultElements = getSearchResults( container );
373
+ const searchResultElements = within(
374
+ screen.getByRole( 'listbox', {
375
+ name: /Search results for.*/,
376
+ } )
377
+ ).getAllByRole( 'option' );
390
378
 
391
379
  expect( searchResultElements ).toHaveLength(
392
380
  fauxEntitySuggestions.length
@@ -415,7 +403,7 @@ describe( 'Searching for a link', () => {
415
403
  const { container } = render( <LinkControl /> );
416
404
 
417
405
  // Search Input UI.
418
- const searchInput = getURLInput();
406
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
419
407
 
420
408
  // Simulate searching for a term.
421
409
  searchInput.focus();
@@ -453,23 +441,21 @@ describe( 'Searching for a link', () => {
453
441
 
454
442
  it( 'should not call search handler when showSuggestions is false', async () => {
455
443
  const user = userEvent.setup();
456
- const { container } = render(
457
- <LinkControl showSuggestions={ false } />
458
- );
444
+ render( <LinkControl showSuggestions={ false } /> );
459
445
 
460
446
  // Search Input UI.
461
- const searchInput = getURLInput();
447
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
462
448
 
463
449
  // Simulate searching for a term.
464
450
  searchInput.focus();
465
451
  await user.keyboard( 'anything' );
466
452
 
467
- const searchResultElements = getSearchResults( container );
453
+ const searchResultsField = screen.queryByRole( 'listbox' );
468
454
 
469
455
  // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
470
456
  await eventLoopTick();
471
457
 
472
- expect( searchResultElements ).toHaveLength( 0 );
458
+ expect( searchResultsField ).not.toBeInTheDocument();
473
459
  expect( mockFetchSearchSuggestions ).not.toHaveBeenCalled();
474
460
  } );
475
461
 
@@ -480,10 +466,10 @@ describe( 'Searching for a link', () => {
480
466
  'should display a URL suggestion as a default fallback for the search term "%s" which could potentially be a valid url.',
481
467
  async ( searchTerm ) => {
482
468
  const user = userEvent.setup();
483
- const { container } = render( <LinkControl /> );
469
+ render( <LinkControl /> );
484
470
 
485
471
  // Search Input UI.
486
- const searchInput = getURLInput();
472
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
487
473
 
488
474
  // Simulate searching for a term.
489
475
  searchInput.focus();
@@ -492,7 +478,11 @@ describe( 'Searching for a link', () => {
492
478
  // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
493
479
  await eventLoopTick();
494
480
 
495
- const searchResultElements = getSearchResults( container );
481
+ const searchResultElements = within(
482
+ screen.getByRole( 'listbox', {
483
+ name: /Search results for.*/,
484
+ } )
485
+ ).getAllByRole( 'option' );
496
486
 
497
487
  const lastSearchResultItem =
498
488
  searchResultElements[ searchResultElements.length - 1 ];
@@ -514,10 +504,10 @@ describe( 'Searching for a link', () => {
514
504
 
515
505
  it( 'should not display a URL suggestion as a default fallback when noURLSuggestion is passed.', async () => {
516
506
  const user = userEvent.setup();
517
- const { container } = render( <LinkControl noURLSuggestion /> );
507
+ render( <LinkControl noURLSuggestion /> );
518
508
 
519
509
  // Search Input UI.
520
- const searchInput = getURLInput();
510
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
521
511
 
522
512
  // Simulate searching for a term.
523
513
  searchInput.focus();
@@ -526,7 +516,11 @@ describe( 'Searching for a link', () => {
526
516
  // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
527
517
  await eventLoopTick();
528
518
 
529
- const searchResultElements = getSearchResults( container );
519
+ const searchResultElements = within(
520
+ screen.getByRole( 'listbox', {
521
+ name: /Search results for.*/,
522
+ } )
523
+ ).getAllByRole( 'option' );
530
524
 
531
525
  // We should see a search result for each of the expect search suggestions and nothing else.
532
526
  expect( searchResultElements ).toHaveLength(
@@ -544,10 +538,10 @@ describe( 'Manual link entry', () => {
544
538
  'should display a single suggestion result when the current input value is URL-like (eg: %s)',
545
539
  async ( searchTerm ) => {
546
540
  const user = userEvent.setup();
547
- const { container } = render( <LinkControl /> );
541
+ render( <LinkControl /> );
548
542
 
549
543
  // Search Input UI.
550
- const searchInput = getURLInput();
544
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
551
545
 
552
546
  // Simulate searching for a term.
553
547
  searchInput.focus();
@@ -556,12 +550,16 @@ describe( 'Manual link entry', () => {
556
550
  // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
557
551
  await eventLoopTick();
558
552
 
559
- const searchResultElements = getSearchResults( container );
553
+ const searchResultElements = within(
554
+ screen.getByRole( 'listbox', {
555
+ name: /Search results for.*/,
556
+ } )
557
+ ).getByRole( 'option' );
560
558
 
561
- expect( searchResultElements ).toHaveLength( 1 );
562
- expect( searchResultElements[ 0 ] ).toHaveTextContent( searchTerm );
563
- expect( searchResultElements[ 0 ] ).toHaveTextContent( 'URL' );
564
- expect( searchResultElements[ 0 ] ).toHaveTextContent(
559
+ expect( searchResultElements ).toBeVisible();
560
+ expect( searchResultElements ).toHaveTextContent( searchTerm );
561
+ expect( searchResultElements ).toHaveTextContent( 'URL' );
562
+ expect( searchResultElements ).toHaveTextContent(
565
563
  'Press ENTER to add this link'
566
564
  );
567
565
  }
@@ -582,7 +580,9 @@ describe( 'Manual link entry', () => {
582
580
  render( <LinkControl /> );
583
581
 
584
582
  // Search Input UI.
585
- const searchInput = getURLInput();
583
+ const searchInput = screen.getByRole( 'combobox', {
584
+ name: 'URL',
585
+ } );
586
586
 
587
587
  let submitButton = screen.queryByRole( 'button', {
588
588
  name: 'Submit',
@@ -625,7 +625,9 @@ describe( 'Manual link entry', () => {
625
625
  render( <LinkControl /> );
626
626
 
627
627
  // Search Input UI.
628
- const searchInput = getURLInput();
628
+ const searchInput = screen.getByRole( 'combobox', {
629
+ name: 'URL',
630
+ } );
629
631
 
630
632
  let submitButton = screen.queryByRole( 'button', {
631
633
  name: 'Submit',
@@ -671,10 +673,12 @@ describe( 'Manual link entry', () => {
671
673
  'should recognise "%s" as a %s link and handle as manual entry by displaying a single suggestion',
672
674
  async ( searchTerm, searchType ) => {
673
675
  const user = userEvent.setup();
674
- const { container } = render( <LinkControl /> );
676
+ render( <LinkControl /> );
675
677
 
676
678
  // Search Input UI.
677
- const searchInput = getURLInput();
679
+ const searchInput = screen.getByRole( 'combobox', {
680
+ name: 'URL',
681
+ } );
678
682
 
679
683
  // Simulate searching for a term.
680
684
  searchInput.focus();
@@ -683,16 +687,16 @@ describe( 'Manual link entry', () => {
683
687
  // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
684
688
  await eventLoopTick();
685
689
 
686
- const searchResultElements = getSearchResults( container );
690
+ const searchResultElements = within(
691
+ screen.getByRole( 'listbox', {
692
+ name: /Search results for.*/,
693
+ } )
694
+ ).getByRole( 'option' );
687
695
 
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(
696
+ expect( searchResultElements ).toBeVisible();
697
+ expect( searchResultElements ).toHaveTextContent( searchTerm );
698
+ expect( searchResultElements ).toHaveTextContent( searchType );
699
+ expect( searchResultElements ).toHaveTextContent(
696
700
  'Press ENTER to add this link'
697
701
  );
698
702
  }
@@ -717,7 +721,9 @@ describe( 'Default search suggestions', () => {
717
721
  // Verify input has no value has default suggestions should only show
718
722
  // when this does not have a value.
719
723
  // Search Input UI.
720
- expect( getURLInput() ).toHaveValue( '' );
724
+ expect( screen.getByRole( 'combobox', { name: 'URL' } ) ).toHaveValue(
725
+ ''
726
+ );
721
727
 
722
728
  // Ensure only called once as a guard against potential infinite
723
729
  // re-render loop within `componentDidUpdate` calling `updateSuggestions`
@@ -735,7 +741,7 @@ describe( 'Default search suggestions', () => {
735
741
 
736
742
  // Render with an initial value an ensure that no initial suggestions
737
743
  // are shown.
738
- const { container } = render(
744
+ render(
739
745
  <LinkControl
740
746
  showInitialSuggestions
741
747
  value={ fauxEntitySuggestions[ 0 ] }
@@ -748,17 +754,23 @@ describe( 'Default search suggestions', () => {
748
754
 
749
755
  // Click the "Edit/Change" button and check initial suggestions are not
750
756
  // shown.
751
- const currentLinkUI = getCurrentLink();
752
- const currentLinkBtn = currentLinkUI.querySelector( 'button' );
757
+ const currentLinkUI = screen.getByLabelText( 'Currently selected' );
758
+ const currentLinkBtn = within( currentLinkUI ).getByRole( 'button', {
759
+ name: 'Edit',
760
+ } );
753
761
 
754
762
  await user.click( currentLinkBtn );
755
763
 
756
- const searchInput = getURLInput();
764
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
757
765
  searchInput.focus();
758
766
 
759
767
  await eventLoopTick();
760
768
 
761
- const searchResultElements = getSearchResults( container );
769
+ const searchResultElements = within(
770
+ screen.getByRole( 'listbox', {
771
+ name: /Search results for.*/,
772
+ } )
773
+ ).getAllByRole( 'option' );
762
774
 
763
775
  // Search input is set to the URL value.
764
776
  expect( searchInput ).toHaveValue( fauxEntitySuggestions[ 0 ].url );
@@ -775,13 +787,13 @@ describe( 'Default search suggestions', () => {
775
787
  const user = userEvent.setup();
776
788
  const searchTerm = 'Hello world';
777
789
 
778
- const { container } = render( <LinkControl showInitialSuggestions /> );
790
+ render( <LinkControl showInitialSuggestions /> );
779
791
 
780
792
  let searchResultElements;
781
793
  let searchInput;
782
794
 
783
795
  // Search Input UI.
784
- searchInput = getURLInput();
796
+ searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
785
797
 
786
798
  // Simulate searching for a term.
787
799
  searchInput.focus();
@@ -792,16 +804,24 @@ describe( 'Default search suggestions', () => {
792
804
 
793
805
  expect( searchInput ).toHaveValue( searchTerm );
794
806
 
795
- searchResultElements = getSearchResults( container );
807
+ searchResultElements = within(
808
+ screen.getByRole( 'listbox', {
809
+ name: /Search results for.*/,
810
+ } )
811
+ ).getAllByRole( 'option' );
796
812
 
797
813
  // Delete the text.
798
814
  await userEvent.clear( searchInput );
799
815
 
800
816
  await eventLoopTick();
801
817
 
802
- searchResultElements = getSearchResults( container );
818
+ searchResultElements = within(
819
+ screen.getByRole( 'listbox', {
820
+ name: 'Recently updated',
821
+ } )
822
+ ).getAllByRole( 'option' );
803
823
 
804
- searchInput = getURLInput();
824
+ searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
805
825
 
806
826
  // Check the input is empty now.
807
827
  expect( searchInput ).toHaveValue( '' );
@@ -822,21 +842,17 @@ describe( 'Default search suggestions', () => {
822
842
  Promise.resolve( noResults )
823
843
  );
824
844
 
825
- const { container } = render( <LinkControl showInitialSuggestions /> );
845
+ render( <LinkControl showInitialSuggestions /> );
826
846
 
827
847
  await eventLoopTick();
828
848
 
829
- const searchInput = getURLInput();
830
-
831
- const searchResultElements = getSearchResults( container );
832
-
833
- const searchResultLabel = container.querySelector(
834
- '.block-editor-link-control__search-results-label'
835
- );
849
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
836
850
 
837
- expect( searchResultLabel ).not.toBeInTheDocument();
851
+ const searchResultsField = screen.queryByRole( 'listbox', {
852
+ name: 'Recently updated',
853
+ } );
838
854
 
839
- expect( searchResultElements ).toHaveLength( 0 );
855
+ expect( searchResultsField ).not.toBeInTheDocument();
840
856
 
841
857
  expect( searchInput ).toHaveAttribute( 'aria-expanded', 'false' );
842
858
  } );
@@ -889,10 +905,10 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
889
905
  );
890
906
  };
891
907
 
892
- const { container } = render( <LinkControlConsumer /> );
908
+ render( <LinkControlConsumer /> );
893
909
 
894
910
  // Search Input UI.
895
- const searchInput = getURLInput();
911
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
896
912
 
897
913
  // Simulate searching for a term.
898
914
  searchInput.focus();
@@ -916,13 +932,15 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
916
932
  await eventLoopTick();
917
933
 
918
934
  // Check for loading indicator.
919
- const loadingIndicator = container.querySelector(
920
- '.block-editor-link-control__loading'
921
- );
922
- const currentLinkLabel = getCurrentLink();
935
+ const loadingIndicator = screen.getByText( 'Creating…' );
936
+ const currentLinkLabel =
937
+ screen.queryByLabelText( 'Currently selected' );
923
938
 
924
939
  expect( currentLinkLabel ).not.toBeInTheDocument();
925
- expect( loadingIndicator ).toHaveTextContent( 'Creating' );
940
+ expect( loadingIndicator ).toBeVisible();
941
+ expect( loadingIndicator ).toHaveClass(
942
+ 'block-editor-link-control__loading'
943
+ );
926
944
 
927
945
  // Resolve the `createSuggestion` promise.
928
946
  await act( async () => {
@@ -931,7 +949,7 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
931
949
 
932
950
  await eventLoopTick();
933
951
 
934
- const currentLink = getCurrentLink();
952
+ const currentLink = screen.getByLabelText( 'Currently selected' );
935
953
 
936
954
  expect( currentLink ).toHaveTextContent( entityNameText );
937
955
  expect( currentLink ).toHaveTextContent( '/?p=123' );
@@ -959,10 +977,10 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
959
977
  );
960
978
  };
961
979
 
962
- const { container } = render( <LinkControlConsumer /> );
980
+ render( <LinkControlConsumer /> );
963
981
 
964
982
  // Search Input UI.
965
- const searchInput = getURLInput();
983
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
966
984
 
967
985
  // Simulate searching for a term.
968
986
  searchInput.focus();
@@ -970,10 +988,11 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
970
988
 
971
989
  await eventLoopTick();
972
990
 
973
- // TODO: select these by aria relationship to autocomplete rather than arbitrary selector.
974
- const searchResultElements = container.querySelectorAll(
975
- '[role="listbox"] [role="option"]'
976
- );
991
+ const searchResultElements = within(
992
+ screen.getByRole( 'listbox', {
993
+ name: /Search results for.*/,
994
+ } )
995
+ ).getAllByRole( 'option' );
977
996
 
978
997
  const createButton = Array.from( searchResultElements ).filter(
979
998
  ( result ) => result.innerHTML.includes( 'Create:' )
@@ -983,7 +1002,7 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
983
1002
 
984
1003
  await eventLoopTick();
985
1004
 
986
- const currentLink = getCurrentLink();
1005
+ const currentLink = screen.getByLabelText( 'Currently selected' );
987
1006
 
988
1007
  expect( currentLink ).toHaveTextContent( 'Some new page to create' );
989
1008
  expect( currentLink ).toHaveTextContent( '/?p=123' );
@@ -1014,10 +1033,10 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1014
1033
  );
1015
1034
  };
1016
1035
 
1017
- const { container } = render( <LinkControlConsumer /> );
1036
+ render( <LinkControlConsumer /> );
1018
1037
 
1019
1038
  // Search Input UI.
1020
- const searchInput = getURLInput();
1039
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
1021
1040
 
1022
1041
  // Simulate searching for a term.
1023
1042
  searchInput.focus();
@@ -1025,10 +1044,11 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1025
1044
 
1026
1045
  await eventLoopTick();
1027
1046
 
1028
- // TODO: select these by aria relationship to autocomplete rather than arbitrary selector.
1029
- const searchResultElements = container.querySelectorAll(
1030
- '[role="listbox"] [role="option"]'
1031
- );
1047
+ const searchResultElements = within(
1048
+ screen.getByRole( 'listbox', {
1049
+ name: /Search results for.*/,
1050
+ } )
1051
+ ).getAllByRole( 'option' );
1032
1052
  const createButton = Array.from( searchResultElements ).filter(
1033
1053
  ( result ) => result.innerHTML.includes( 'Create:' )
1034
1054
  )[ 0 ];
@@ -1044,7 +1064,9 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1044
1064
 
1045
1065
  await eventLoopTick();
1046
1066
 
1047
- expect( getCurrentLink() ).toHaveTextContent( entityNameText );
1067
+ expect(
1068
+ screen.getByLabelText( 'Currently selected' )
1069
+ ).toHaveTextContent( entityNameText );
1048
1070
  } );
1049
1071
 
1050
1072
  it( 'should allow customisation of button text', async () => {
@@ -1060,10 +1082,10 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1060
1082
  );
1061
1083
  };
1062
1084
 
1063
- const { container } = render( <LinkControlConsumer /> );
1085
+ render( <LinkControlConsumer /> );
1064
1086
 
1065
1087
  // Search Input UI.
1066
- const searchInput = getURLInput();
1088
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
1067
1089
 
1068
1090
  // Simulate searching for a term.
1069
1091
  searchInput.focus();
@@ -1071,10 +1093,11 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1071
1093
 
1072
1094
  await eventLoopTick();
1073
1095
 
1074
- // TODO: select these by aria relationship to autocomplete rather than arbitrary selector.
1075
- const searchResultElements = container.querySelectorAll(
1076
- '[role="listbox"] [role="option"]'
1077
- );
1096
+ const searchResultElements = within(
1097
+ screen.getByRole( 'listbox', {
1098
+ name: /Search results for.*/,
1099
+ } )
1100
+ ).getAllByRole( 'option' );
1078
1101
 
1079
1102
  const createButton = Array.from( searchResultElements ).filter(
1080
1103
  ( result ) => result.innerHTML.includes( 'Custom suggestion text' )
@@ -1087,32 +1110,26 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1087
1110
  it.each( [ [ undefined ], [ null ], [ false ] ] )(
1088
1111
  'should not show not show an option to create an entity when "createSuggestion" handler is %s',
1089
1112
  async ( handler ) => {
1090
- const { container } = render(
1091
- <LinkControl createSuggestion={ handler } />
1092
- );
1113
+ render( <LinkControl createSuggestion={ handler } /> );
1093
1114
 
1094
1115
  // Await the initial suggestions to be fetched.
1095
1116
  await eventLoopTick();
1096
1117
 
1097
1118
  // Search Input UI.
1098
- const searchInput = getURLInput();
1119
+ const searchInput = screen.getByRole( 'combobox', {
1120
+ name: 'URL',
1121
+ } );
1099
1122
 
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 ];
1123
+ const searchResultsField = screen.queryByRole( 'listbox' );
1107
1124
 
1108
1125
  // Verify input has no value.
1109
1126
  expect( searchInput ).toHaveValue( '' );
1110
- expect( createButton ).toBeFalsy(); // Shouldn't exist!
1127
+ expect( searchResultsField ).not.toBeInTheDocument(); // Shouldn't exist!
1111
1128
  }
1112
1129
  );
1113
1130
 
1114
1131
  it( 'should not show not show an option to create an entity when input is empty', async () => {
1115
- const { container } = render(
1132
+ render(
1116
1133
  <LinkControl
1117
1134
  showInitialSuggestions={ true } // Should show even if we're not showing initial suggestions.
1118
1135
  createSuggestion={ jest.fn() }
@@ -1123,19 +1140,13 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1123
1140
  await eventLoopTick();
1124
1141
 
1125
1142
  // Search Input UI.
1126
- const searchInput = getURLInput();
1143
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
1127
1144
 
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 ];
1145
+ const searchResultsField = screen.queryByRole( 'listbox' );
1135
1146
 
1136
1147
  // Verify input has no value.
1137
1148
  expect( searchInput ).toHaveValue( '' );
1138
- expect( createButton ).toBeFalsy(); // Shouldn't exist!
1149
+ expect( searchResultsField ).not.toBeInTheDocument(); // Shouldn't exist!
1139
1150
  } );
1140
1151
 
1141
1152
  it.each( [
@@ -1148,12 +1159,12 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1148
1159
  'should not show option to "Create Page" when text is a form of direct entry (eg: %s)',
1149
1160
  async ( inputText ) => {
1150
1161
  const user = userEvent.setup();
1151
- const { container } = render(
1152
- <LinkControl createSuggestion={ jest.fn() } />
1153
- );
1162
+ render( <LinkControl createSuggestion={ jest.fn() } /> );
1154
1163
 
1155
1164
  // Search Input UI.
1156
- const searchInput = getURLInput();
1165
+ const searchInput = screen.getByRole( 'combobox', {
1166
+ name: 'URL',
1167
+ } );
1157
1168
 
1158
1169
  // Simulate searching for a term.
1159
1170
  searchInput.focus();
@@ -1161,10 +1172,11 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1161
1172
 
1162
1173
  await eventLoopTick();
1163
1174
 
1164
- // TODO: select these by aria relationship to autocomplete rather than arbitrary selector.
1165
- const searchResultElements = container.querySelectorAll(
1166
- '[role="listbox"] [role="option"]'
1167
- );
1175
+ const searchResultElements = within(
1176
+ screen.getByRole( 'listbox', {
1177
+ name: /Search results for.*/,
1178
+ } )
1179
+ ).getAllByRole( 'option' );
1168
1180
 
1169
1181
  const createButton = Array.from( searchResultElements ).filter(
1170
1182
  ( result ) => result.innerHTML.includes( 'New page' )
@@ -1187,12 +1199,10 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1187
1199
 
1188
1200
  const createSuggestion = () => Promise.reject( throwsError() );
1189
1201
 
1190
- const { container } = render(
1191
- <LinkControl createSuggestion={ createSuggestion } />
1192
- );
1202
+ render( <LinkControl createSuggestion={ createSuggestion } /> );
1193
1203
 
1194
1204
  // Search Input UI.
1195
- searchInput = getURLInput();
1205
+ searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
1196
1206
 
1197
1207
  // Simulate searching for a term.
1198
1208
  searchInput.focus();
@@ -1200,11 +1210,12 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1200
1210
 
1201
1211
  await eventLoopTick();
1202
1212
 
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(
1213
+ const searchResultElements = within(
1214
+ screen.getByRole( 'listbox', {
1215
+ name: /Search results for.*/,
1216
+ } )
1217
+ ).getAllByRole( 'option' );
1218
+ const createButton = Array.from( searchResultElements ).filter(
1208
1219
  ( result ) => result.innerHTML.includes( 'Create:' )
1209
1220
  )[ 0 ];
1210
1221
 
@@ -1212,36 +1223,24 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1212
1223
 
1213
1224
  await eventLoopTick();
1214
1225
 
1215
- searchInput = getURLInput();
1226
+ searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
1216
1227
 
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
- );
1228
+ const errorNotice = screen.getAllByText(
1229
+ 'API response returned invalid entity.'
1230
+ )[ 1 ];
1224
1231
 
1225
1232
  // Catch the error in the test to avoid test failures.
1226
1233
  expect( throwsError ).toThrow( Error );
1227
1234
 
1228
1235
  // Check human readable error notice is perceivable.
1229
1236
  expect( errorNotice ).toBeVisible();
1230
- expect( errorNotice ).toHaveTextContent(
1231
- 'API response returned invalid entity'
1237
+ expect( errorNotice.parentElement ).toHaveClass(
1238
+ 'block-editor-link-control__search-error'
1232
1239
  );
1233
1240
 
1234
1241
  // Verify input is repopulated with original search text.
1235
1242
  expect( searchInput ).toBeVisible();
1236
1243
  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
1244
  } );
1246
1245
  } );
1247
1246
  } );
@@ -1258,12 +1257,12 @@ describe( 'Selecting links', () => {
1258
1257
 
1259
1258
  render( <LinkControlConsumer /> );
1260
1259
 
1261
- const currentLink = getCurrentLink();
1262
- const currentLinkAnchor = currentLink.querySelector(
1263
- `[href="${ selectedLink.url }"]`
1264
- );
1260
+ const currentLink = screen.getByLabelText( 'Currently selected' );
1261
+ const currentLinkAnchor = screen.getByRole( 'link', {
1262
+ name: `${ selectedLink.title } (opens in a new tab)`,
1263
+ } );
1265
1264
 
1266
- expect( currentLink ).toHaveTextContent( selectedLink.title );
1265
+ expect( currentLink ).toBeVisible();
1267
1266
  expect(
1268
1267
  screen.queryByRole( 'button', { name: 'Edit' } )
1269
1268
  ).toBeVisible();
@@ -1288,14 +1287,16 @@ describe( 'Selecting links', () => {
1288
1287
  render( <LinkControlConsumer /> );
1289
1288
 
1290
1289
  // Required in order to select the button below.
1291
- let currentLinkUI = getCurrentLink();
1292
- const currentLinkBtn = currentLinkUI.querySelector( 'button' );
1290
+ let currentLinkUI = screen.getByLabelText( 'Currently selected' );
1291
+ const currentLinkBtn = within( currentLinkUI ).getByRole( 'button', {
1292
+ name: 'Edit',
1293
+ } );
1293
1294
 
1294
1295
  // Simulate searching for a term.
1295
1296
  await user.click( currentLinkBtn );
1296
1297
 
1297
- const searchInput = getURLInput();
1298
- currentLinkUI = getCurrentLink();
1298
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
1299
+ currentLinkUI = screen.queryByLabelText( 'Currently selected' );
1299
1300
 
1300
1301
  // We should be back to showing the search input.
1301
1302
  expect( searchInput ).toBeVisible();
@@ -1331,10 +1332,12 @@ describe( 'Selecting links', () => {
1331
1332
  );
1332
1333
  };
1333
1334
 
1334
- const { container } = render( <LinkControlConsumer /> );
1335
+ render( <LinkControlConsumer /> );
1335
1336
 
1336
1337
  // Search Input UI.
1337
- const searchInput = getURLInput();
1338
+ const searchInput = screen.getByRole( 'combobox', {
1339
+ name: 'URL',
1340
+ } );
1338
1341
 
1339
1342
  // Simulate searching for a term.
1340
1343
  searchInput.focus();
@@ -1343,20 +1346,22 @@ describe( 'Selecting links', () => {
1343
1346
  // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
1344
1347
  await eventLoopTick();
1345
1348
 
1346
- const searchResultElements = getSearchResults( container );
1349
+ const searchResultElements = within(
1350
+ screen.getByRole( 'listbox', {
1351
+ name: /Search results for.*/,
1352
+ } )
1353
+ ).getAllByRole( 'option' );
1347
1354
 
1348
1355
  const firstSearchSuggestion = searchResultElements[ 0 ];
1349
1356
 
1350
1357
  // Simulate selecting the first of the search suggestions.
1351
1358
  await user.click( firstSearchSuggestion );
1352
1359
 
1353
- const currentLink = getCurrentLink();
1354
- const currentLinkAnchor = currentLink.querySelector(
1355
- `[href="${ selectedLink.url }"]`
1356
- );
1360
+ const currentLinkAnchor = screen.getByRole( 'link', {
1361
+ name: `${ selectedLink.title } (opens in a new tab)`,
1362
+ } );
1357
1363
 
1358
1364
  // Check that this suggestion is now shown as selected.
1359
- expect( currentLink ).toHaveTextContent( selectedLink.title );
1360
1365
  expect(
1361
1366
  screen.getByRole( 'button', { name: 'Edit' } )
1362
1367
  ).toBeVisible();
@@ -1396,7 +1401,9 @@ describe( 'Selecting links', () => {
1396
1401
  const { container } = render( <LinkControlConsumer /> );
1397
1402
 
1398
1403
  // Search Input UI.
1399
- const searchInput = getURLInput();
1404
+ const searchInput = screen.getByRole( 'combobox', {
1405
+ name: 'URL',
1406
+ } );
1400
1407
 
1401
1408
  // Simulate searching for a term.
1402
1409
  searchInput.focus();
@@ -1408,12 +1415,18 @@ describe( 'Selecting links', () => {
1408
1415
  // Step down into the search results, highlighting the first result item.
1409
1416
  triggerArrowDown( searchInput );
1410
1417
 
1411
- const searchResultElements = getSearchResults( container );
1418
+ const searchResultElements = within(
1419
+ screen.getByRole( 'listbox', {
1420
+ name: /Search results for.*/,
1421
+ } )
1422
+ ).getAllByRole( 'option' );
1412
1423
 
1413
1424
  const firstSearchSuggestion = searchResultElements[ 0 ];
1414
1425
  const secondSearchSuggestion = searchResultElements[ 1 ];
1415
1426
 
1416
- let selectedSearchResultElement = getSelectedResultElement();
1427
+ let selectedSearchResultElement = screen.getByRole( 'option', {
1428
+ selected: true,
1429
+ } );
1417
1430
 
1418
1431
  // We should have highlighted the first item using the keyboard.
1419
1432
  expect( selectedSearchResultElement ).toEqual(
@@ -1425,7 +1438,9 @@ describe( 'Selecting links', () => {
1425
1438
  // Check we can go down again using the down arrow.
1426
1439
  triggerArrowDown( searchInput );
1427
1440
 
1428
- selectedSearchResultElement = getSelectedResultElement();
1441
+ selectedSearchResultElement = screen.getByRole( 'option', {
1442
+ selected: true,
1443
+ } );
1429
1444
 
1430
1445
  // We should have highlighted the first item using the keyboard
1431
1446
  // eslint-disable-next-line jest/no-conditional-expect
@@ -1436,7 +1451,9 @@ describe( 'Selecting links', () => {
1436
1451
  // Check we can go back up via up arrow.
1437
1452
  triggerArrowUp( searchInput );
1438
1453
 
1439
- selectedSearchResultElement = getSelectedResultElement();
1454
+ selectedSearchResultElement = screen.getByRole( 'option', {
1455
+ selected: true,
1456
+ } );
1440
1457
 
1441
1458
  // We should be back to highlighting the first search result again
1442
1459
  // eslint-disable-next-line jest/no-conditional-expect
@@ -1449,15 +1466,16 @@ describe( 'Selecting links', () => {
1449
1466
  triggerEnter( searchInput );
1450
1467
 
1451
1468
  // 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
- );
1469
+ const currentLink =
1470
+ screen.getByLabelText( 'Currently selected' );
1471
+ const currentLinkAnchor = screen.getByRole( 'link', {
1472
+ name: `${ selectedLink.title } (opens in a new tab)`,
1473
+ } );
1456
1474
 
1457
1475
  // Make sure focus is retained after submission.
1458
1476
  expect( container ).toContainElement( document.activeElement );
1459
1477
 
1460
- expect( currentLink ).toHaveTextContent( selectedLink.title );
1478
+ expect( currentLink ).toBeVisible();
1461
1479
  expect(
1462
1480
  screen.getByRole( 'button', { name: 'Edit' } )
1463
1481
  ).toBeVisible();
@@ -1466,9 +1484,7 @@ describe( 'Selecting links', () => {
1466
1484
  );
1467
1485
 
1468
1486
  it( 'should allow selection of initial search results via the keyboard', async () => {
1469
- const { container } = render(
1470
- <LinkControl showInitialSuggestions />
1471
- );
1487
+ render( <LinkControl showInitialSuggestions /> );
1472
1488
 
1473
1489
  await eventLoopTick();
1474
1490
 
@@ -1479,19 +1495,25 @@ describe( 'Selecting links', () => {
1479
1495
  ).toBeVisible();
1480
1496
 
1481
1497
  // Search Input UI.
1482
- const searchInput = getURLInput();
1498
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
1483
1499
 
1484
1500
  // Step down into the search results, highlighting the first result item.
1485
1501
  triggerArrowDown( searchInput );
1486
1502
 
1487
1503
  await eventLoopTick();
1488
1504
 
1489
- const searchResultElements = getSearchResults( container );
1505
+ const searchResultElements = within(
1506
+ screen.getByRole( 'listbox', {
1507
+ name: 'Recently updated',
1508
+ } )
1509
+ ).getAllByRole( 'option' );
1490
1510
 
1491
1511
  const firstSearchSuggestion = searchResultElements[ 0 ];
1492
1512
  const secondSearchSuggestion = searchResultElements[ 1 ];
1493
1513
 
1494
- let selectedSearchResultElement = getSelectedResultElement();
1514
+ let selectedSearchResultElement = screen.getByRole( 'option', {
1515
+ selected: true,
1516
+ } );
1495
1517
 
1496
1518
  // We should have highlighted the first item using the keyboard.
1497
1519
  expect( selectedSearchResultElement ).toEqual(
@@ -1501,7 +1523,9 @@ describe( 'Selecting links', () => {
1501
1523
  // Check we can go down again using the down arrow.
1502
1524
  triggerArrowDown( searchInput );
1503
1525
 
1504
- selectedSearchResultElement = getSelectedResultElement();
1526
+ selectedSearchResultElement = screen.getByRole( 'option', {
1527
+ selected: true,
1528
+ } );
1505
1529
 
1506
1530
  // We should have highlighted the first item using the keyboard.
1507
1531
  expect( selectedSearchResultElement ).toEqual(
@@ -1511,7 +1535,9 @@ describe( 'Selecting links', () => {
1511
1535
  // Check we can go back up via up arrow.
1512
1536
  triggerArrowUp( searchInput );
1513
1537
 
1514
- selectedSearchResultElement = getSelectedResultElement();
1538
+ selectedSearchResultElement = screen.getByRole( 'option', {
1539
+ selected: true,
1540
+ } );
1515
1541
 
1516
1542
  // We should be back to highlighting the first search result again.
1517
1543
  expect( selectedSearchResultElement ).toEqual(
@@ -1534,18 +1560,17 @@ describe( 'Addition Settings UI', () => {
1534
1560
  return <LinkControl value={ link } />;
1535
1561
  };
1536
1562
 
1537
- const { container } = render( <LinkControlConsumer /> );
1563
+ render( <LinkControlConsumer /> );
1538
1564
 
1539
1565
  const newTabSettingLabel = screen.getByText( expectedSettingText );
1540
1566
  expect( newTabSettingLabel ).toBeVisible();
1541
1567
 
1542
- const newTabSettingLabelForAttr =
1543
- newTabSettingLabel.getAttribute( 'for' );
1544
- const newTabSettingInput = container.querySelector(
1545
- `#${ newTabSettingLabelForAttr }`
1546
- );
1568
+ const newTabSettingInput = screen.getByRole( 'checkbox', {
1569
+ name: expectedSettingText,
1570
+ checked: false,
1571
+ } );
1572
+
1547
1573
  expect( newTabSettingInput ).toBeVisible();
1548
- expect( newTabSettingInput ).not.toBeChecked();
1549
1574
  } );
1550
1575
 
1551
1576
  it( 'should display a setting control with correct default state for each of the custom settings provided', async () => {
@@ -1595,10 +1620,10 @@ describe( 'Post types', () => {
1595
1620
  const user = userEvent.setup();
1596
1621
  const searchTerm = 'Hello world';
1597
1622
 
1598
- const { container } = render( <LinkControl /> );
1623
+ render( <LinkControl /> );
1599
1624
 
1600
1625
  // Search Input UI.
1601
- const searchInput = getURLInput();
1626
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
1602
1627
 
1603
1628
  // Simulate searching for a term.
1604
1629
  searchInput.focus();
@@ -1607,7 +1632,11 @@ describe( 'Post types', () => {
1607
1632
  // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
1608
1633
  await eventLoopTick();
1609
1634
 
1610
- const searchResultElements = getSearchResults( container );
1635
+ const searchResultElements = within(
1636
+ screen.getByRole( 'listbox', {
1637
+ name: /Search results for.*/,
1638
+ } )
1639
+ ).getAllByRole( 'option' );
1611
1640
 
1612
1641
  searchResultElements.forEach( ( resultItem, index ) => {
1613
1642
  expect( resultItem ).toHaveTextContent(
@@ -1622,12 +1651,10 @@ describe( 'Post types', () => {
1622
1651
  const user = userEvent.setup();
1623
1652
  const searchTerm = 'Hello world';
1624
1653
 
1625
- const { container } = render(
1626
- <LinkControl suggestionsQuery={ { type: postType } } />
1627
- );
1654
+ render( <LinkControl suggestionsQuery={ { type: postType } } /> );
1628
1655
 
1629
1656
  // Search Input UI.
1630
- const searchInput = getURLInput();
1657
+ const searchInput = screen.getByRole( 'combobox', { name: 'URL' } );
1631
1658
 
1632
1659
  // Simulate searching for a term.
1633
1660
  searchInput.focus();
@@ -1636,7 +1663,11 @@ describe( 'Post types', () => {
1636
1663
  // fetchFauxEntitySuggestions resolves on next "tick" of event loop.
1637
1664
  await eventLoopTick();
1638
1665
 
1639
- const searchResultElements = getSearchResults( container );
1666
+ const searchResultElements = within(
1667
+ screen.getByRole( 'listbox', {
1668
+ name: /Search results for.*/,
1669
+ } )
1670
+ ).getAllByRole( 'option' );
1640
1671
 
1641
1672
  searchResultElements.forEach( ( resultItem, index ) => {
1642
1673
  expect(
@@ -1686,7 +1717,7 @@ describe( 'Rich link previews', () => {
1686
1717
  await eventLoopTick();
1687
1718
  } );
1688
1719
 
1689
- const linkPreview = getCurrentLink();
1720
+ const linkPreview = screen.getByLabelText( 'Currently selected' );
1690
1721
 
1691
1722
  const isRichLinkPreview = linkPreview.classList.contains( 'is-rich' );
1692
1723
 
@@ -1712,7 +1743,7 @@ describe( 'Rich link previews', () => {
1712
1743
  await eventLoopTick();
1713
1744
  } );
1714
1745
 
1715
- const linkPreview = getCurrentLink();
1746
+ const linkPreview = screen.getByLabelText( 'Currently selected' );
1716
1747
 
1717
1748
  const isRichLinkPreview = linkPreview.classList.contains( 'is-rich' );
1718
1749
 
@@ -1736,7 +1767,7 @@ describe( 'Rich link previews', () => {
1736
1767
  await eventLoopTick();
1737
1768
  } );
1738
1769
 
1739
- const linkPreview = getCurrentLink();
1770
+ const linkPreview = screen.getByLabelText( 'Currently selected' );
1740
1771
 
1741
1772
  // Todo: refactor to use user-facing queries.
1742
1773
  const hasRichImagePreview = linkPreview.querySelector(
@@ -1769,16 +1800,16 @@ describe( 'Rich link previews', () => {
1769
1800
  await eventLoopTick();
1770
1801
  } );
1771
1802
 
1772
- const linkPreview = getCurrentLink();
1803
+ const linkPreview = screen.getByLabelText( 'Currently selected' );
1773
1804
 
1774
1805
  const isRichLinkPreview = linkPreview.classList.contains( 'is-rich' );
1775
1806
  expect( isRichLinkPreview ).toBe( true );
1776
1807
 
1777
- const titlePreview = linkPreview.querySelector(
1778
- '.block-editor-link-control__search-item-title'
1779
- );
1808
+ const titlePreview = screen.getByText( selectedLink.title );
1780
1809
 
1781
- expect( titlePreview ).toHaveTextContent( selectedLink.title );
1810
+ expect( titlePreview ).toHaveClass(
1811
+ 'block-editor-link-control__search-item-title'
1812
+ );
1782
1813
  } );
1783
1814
 
1784
1815
  it( 'should display a fallback when icon is missing from rich data', async () => {
@@ -1798,7 +1829,7 @@ describe( 'Rich link previews', () => {
1798
1829
  await eventLoopTick();
1799
1830
  } );
1800
1831
 
1801
- const linkPreview = getCurrentLink();
1832
+ const linkPreview = screen.getByLabelText( 'Currently selected' );
1802
1833
 
1803
1834
  const isRichLinkPreview = linkPreview.classList.contains( 'is-rich' );
1804
1835
  expect( isRichLinkPreview ).toBe( true );
@@ -1836,7 +1867,7 @@ describe( 'Rich link previews', () => {
1836
1867
  await eventLoopTick();
1837
1868
  } );
1838
1869
 
1839
- const linkPreview = getCurrentLink();
1870
+ const linkPreview = screen.getByLabelText( 'Currently selected' );
1840
1871
 
1841
1872
  const isRichLinkPreview =
1842
1873
  linkPreview.classList.contains( 'is-rich' );
@@ -1867,7 +1898,7 @@ describe( 'Rich link previews', () => {
1867
1898
  await eventLoopTick();
1868
1899
  } );
1869
1900
 
1870
- const linkPreview = getCurrentLink();
1901
+ const linkPreview = screen.getByLabelText( 'Currently selected' );
1871
1902
 
1872
1903
  const isRichLinkPreview =
1873
1904
  linkPreview.classList.contains( 'is-rich' );
@@ -1888,7 +1919,7 @@ describe( 'Rich link previews', () => {
1888
1919
  await eventLoopTick();
1889
1920
  } );
1890
1921
 
1891
- const linkPreview = getCurrentLink();
1922
+ const linkPreview = screen.getByLabelText( 'Currently selected' );
1892
1923
 
1893
1924
  const isFetchingRichPreview =
1894
1925
  linkPreview.classList.contains( 'is-fetching' );
@@ -1910,7 +1941,7 @@ describe( 'Rich link previews', () => {
1910
1941
  await eventLoopTick();
1911
1942
  } );
1912
1943
 
1913
- const linkPreview = getCurrentLink();
1944
+ const linkPreview = screen.getByLabelText( 'Currently selected' );
1914
1945
 
1915
1946
  const isFetchingRichPreview =
1916
1947
  linkPreview.classList.contains( 'is-fetching' );