@wordpress/components 25.12.0 → 25.14.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 (342) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/build/angle-picker-control/index.js +0 -1
  3. package/build/angle-picker-control/index.js.map +1 -1
  4. package/build/border-control/border-control-dropdown/component.js +4 -2
  5. package/build/border-control/border-control-dropdown/component.js.map +1 -1
  6. package/build/border-control/border-control-dropdown/hook.js +3 -2
  7. package/build/border-control/border-control-dropdown/hook.js.map +1 -1
  8. package/build/border-control/styles.js +17 -17
  9. package/build/border-control/styles.js.map +1 -1
  10. package/build/checkbox-control/index.js +1 -1
  11. package/build/checkbox-control/index.js.map +1 -1
  12. package/build/checkbox-control/types.js.map +1 -1
  13. package/build/custom-select-control-v2/index.js +87 -0
  14. package/build/custom-select-control-v2/index.js.map +1 -0
  15. package/build/custom-select-control-v2/styles.js +85 -0
  16. package/build/custom-select-control-v2/styles.js.map +1 -0
  17. package/build/custom-select-control-v2/types.js +6 -0
  18. package/build/custom-select-control-v2/types.js.map +1 -0
  19. package/build/date-time/time/timezone.js +11 -2
  20. package/build/date-time/time/timezone.js.map +1 -1
  21. package/build/dimension-control/index.js +2 -0
  22. package/build/dimension-control/index.js.map +1 -1
  23. package/build/dimension-control/types.js.map +1 -1
  24. package/build/dropdown-menu-v2-ariakit/index.js +49 -20
  25. package/build/dropdown-menu-v2-ariakit/index.js.map +1 -1
  26. package/build/dropdown-menu-v2-ariakit/styles.js +82 -59
  27. package/build/dropdown-menu-v2-ariakit/styles.js.map +1 -1
  28. package/build/dropdown-menu-v2-ariakit/types.js.map +1 -1
  29. package/build/focal-point-picker/controls.js +5 -1
  30. package/build/focal-point-picker/controls.js.map +1 -1
  31. package/build/focal-point-picker/index.js +2 -0
  32. package/build/focal-point-picker/index.js.map +1 -1
  33. package/build/focal-point-picker/styles/focal-point-picker-style.js +15 -15
  34. package/build/focal-point-picker/styles/focal-point-picker-style.js.map +1 -1
  35. package/build/focal-point-picker/types.js.map +1 -1
  36. package/build/font-size-picker/font-size-picker-select.js +2 -0
  37. package/build/font-size-picker/font-size-picker-select.js.map +1 -1
  38. package/build/font-size-picker/font-size-picker-toggle-group.js +2 -0
  39. package/build/font-size-picker/font-size-picker-toggle-group.js.map +1 -1
  40. package/build/font-size-picker/index.js +6 -1
  41. package/build/font-size-picker/index.js.map +1 -1
  42. package/build/font-size-picker/types.js.map +1 -1
  43. package/build/form-token-field/index.js +6 -2
  44. package/build/form-token-field/index.js.map +1 -1
  45. package/build/form-token-field/token-input.js.map +1 -1
  46. package/build/form-token-field/types.js.map +1 -1
  47. package/build/heading/hook.js +6 -3
  48. package/build/heading/hook.js.map +1 -1
  49. package/build/heading/types.js.map +1 -1
  50. package/build/index.native.js +0 -16
  51. package/build/index.native.js.map +1 -1
  52. package/build/mobile/global-styles-context/utils.native.js +13 -0
  53. package/build/mobile/global-styles-context/utils.native.js.map +1 -1
  54. package/build/mobile/utils/alignments.native.js +1 -1
  55. package/build/mobile/utils/alignments.native.js.map +1 -1
  56. package/build/palette-edit/index.js +21 -1
  57. package/build/palette-edit/index.js.map +1 -1
  58. package/build/private-apis.js +3 -2
  59. package/build/private-apis.js.map +1 -1
  60. package/build/query-controls/author-select.js +3 -1
  61. package/build/query-controls/author-select.js.map +1 -1
  62. package/build/query-controls/category-select.js +3 -1
  63. package/build/query-controls/category-select.js.map +1 -1
  64. package/build/query-controls/index.js +6 -1
  65. package/build/query-controls/index.js.map +1 -1
  66. package/build/query-controls/types.js.map +1 -1
  67. package/build/slot-fill/types.js.map +1 -1
  68. package/build/tabs/index.js +24 -5
  69. package/build/tabs/index.js.map +1 -1
  70. package/build/tabs/tab.js +4 -4
  71. package/build/tabs/tab.js.map +1 -1
  72. package/build/tabs/tabpanel.js +4 -3
  73. package/build/tabs/tabpanel.js.map +1 -1
  74. package/build/tabs/types.js.map +1 -1
  75. package/build/text/types.js.map +1 -1
  76. package/build/toggle-group-control/toggle-group-control/utils.js +17 -17
  77. package/build/toggle-group-control/toggle-group-control/utils.js.map +1 -1
  78. package/build/tools-panel/tools-panel-item/hook.js +16 -12
  79. package/build/tools-panel/tools-panel-item/hook.js.map +1 -1
  80. package/build-module/angle-picker-control/index.js +0 -1
  81. package/build-module/angle-picker-control/index.js.map +1 -1
  82. package/build-module/border-control/border-control-dropdown/component.js +4 -2
  83. package/build-module/border-control/border-control-dropdown/component.js.map +1 -1
  84. package/build-module/border-control/border-control-dropdown/hook.js +3 -2
  85. package/build-module/border-control/border-control-dropdown/hook.js.map +1 -1
  86. package/build-module/border-control/styles.js +17 -17
  87. package/build-module/border-control/styles.js.map +1 -1
  88. package/build-module/checkbox-control/index.js +1 -1
  89. package/build-module/checkbox-control/index.js.map +1 -1
  90. package/build-module/checkbox-control/types.js.map +1 -1
  91. package/build-module/custom-select-control-v2/index.js +74 -0
  92. package/build-module/custom-select-control-v2/index.js.map +1 -0
  93. package/build-module/custom-select-control-v2/styles.js +71 -0
  94. package/build-module/custom-select-control-v2/styles.js.map +1 -0
  95. package/build-module/custom-select-control-v2/types.js +2 -0
  96. package/build-module/custom-select-control-v2/types.js.map +1 -0
  97. package/build-module/date-time/time/timezone.js +11 -2
  98. package/build-module/date-time/time/timezone.js.map +1 -1
  99. package/build-module/dimension-control/index.js +2 -0
  100. package/build-module/dimension-control/index.js.map +1 -1
  101. package/build-module/dimension-control/types.js.map +1 -1
  102. package/build-module/dropdown-menu-v2-ariakit/index.js +46 -18
  103. package/build-module/dropdown-menu-v2-ariakit/index.js.map +1 -1
  104. package/build-module/dropdown-menu-v2-ariakit/styles.js +69 -40
  105. package/build-module/dropdown-menu-v2-ariakit/styles.js.map +1 -1
  106. package/build-module/dropdown-menu-v2-ariakit/types.js.map +1 -1
  107. package/build-module/focal-point-picker/controls.js +5 -1
  108. package/build-module/focal-point-picker/controls.js.map +1 -1
  109. package/build-module/focal-point-picker/index.js +2 -0
  110. package/build-module/focal-point-picker/index.js.map +1 -1
  111. package/build-module/focal-point-picker/styles/focal-point-picker-style.js +15 -15
  112. package/build-module/focal-point-picker/styles/focal-point-picker-style.js.map +1 -1
  113. package/build-module/focal-point-picker/types.js.map +1 -1
  114. package/build-module/font-size-picker/font-size-picker-select.js +2 -0
  115. package/build-module/font-size-picker/font-size-picker-select.js.map +1 -1
  116. package/build-module/font-size-picker/font-size-picker-toggle-group.js +2 -0
  117. package/build-module/font-size-picker/font-size-picker-toggle-group.js.map +1 -1
  118. package/build-module/font-size-picker/index.js +6 -1
  119. package/build-module/font-size-picker/index.js.map +1 -1
  120. package/build-module/font-size-picker/types.js.map +1 -1
  121. package/build-module/form-token-field/index.js +6 -2
  122. package/build-module/form-token-field/index.js.map +1 -1
  123. package/build-module/form-token-field/token-input.js.map +1 -1
  124. package/build-module/form-token-field/types.js.map +1 -1
  125. package/build-module/heading/hook.js +6 -3
  126. package/build-module/heading/hook.js.map +1 -1
  127. package/build-module/heading/types.js.map +1 -1
  128. package/build-module/index.native.js +0 -2
  129. package/build-module/index.native.js.map +1 -1
  130. package/build-module/mobile/global-styles-context/utils.native.js +13 -0
  131. package/build-module/mobile/global-styles-context/utils.native.js.map +1 -1
  132. package/build-module/mobile/utils/alignments.native.js +1 -1
  133. package/build-module/mobile/utils/alignments.native.js.map +1 -1
  134. package/build-module/palette-edit/index.js +20 -3
  135. package/build-module/palette-edit/index.js.map +1 -1
  136. package/build-module/private-apis.js +4 -3
  137. package/build-module/private-apis.js.map +1 -1
  138. package/build-module/query-controls/author-select.js +3 -1
  139. package/build-module/query-controls/author-select.js.map +1 -1
  140. package/build-module/query-controls/category-select.js +3 -1
  141. package/build-module/query-controls/category-select.js.map +1 -1
  142. package/build-module/query-controls/index.js +6 -1
  143. package/build-module/query-controls/index.js.map +1 -1
  144. package/build-module/query-controls/types.js.map +1 -1
  145. package/build-module/slot-fill/types.js.map +1 -1
  146. package/build-module/tabs/index.js +25 -6
  147. package/build-module/tabs/index.js.map +1 -1
  148. package/build-module/tabs/tab.js +6 -6
  149. package/build-module/tabs/tab.js.map +1 -1
  150. package/build-module/tabs/tabpanel.js +6 -5
  151. package/build-module/tabs/tabpanel.js.map +1 -1
  152. package/build-module/tabs/types.js.map +1 -1
  153. package/build-module/text/types.js.map +1 -1
  154. package/build-module/toggle-group-control/toggle-group-control/utils.js +17 -17
  155. package/build-module/toggle-group-control/toggle-group-control/utils.js.map +1 -1
  156. package/build-module/tools-panel/tools-panel-item/hook.js +17 -13
  157. package/build-module/tools-panel/tools-panel-item/hook.js.map +1 -1
  158. package/build-style/style-rtl.css +32 -6
  159. package/build-style/style.css +32 -6
  160. package/build-types/angle-picker-control/index.d.ts.map +1 -1
  161. package/build-types/border-control/border-control-dropdown/component.d.ts.map +1 -1
  162. package/build-types/border-control/border-control-dropdown/hook.d.ts +1 -0
  163. package/build-types/border-control/border-control-dropdown/hook.d.ts.map +1 -1
  164. package/build-types/border-control/styles.d.ts +1 -1
  165. package/build-types/border-control/styles.d.ts.map +1 -1
  166. package/build-types/box-control/stories/index.story.d.ts +1944 -0
  167. package/build-types/box-control/stories/index.story.d.ts.map +1 -0
  168. package/build-types/checkbox-control/index.d.ts.map +1 -1
  169. package/build-types/checkbox-control/types.d.ts +3 -2
  170. package/build-types/checkbox-control/types.d.ts.map +1 -1
  171. package/build-types/color-palette/styles.d.ts +4 -1
  172. package/build-types/color-palette/styles.d.ts.map +1 -1
  173. package/build-types/custom-select-control-v2/index.d.ts +6 -0
  174. package/build-types/custom-select-control-v2/index.d.ts.map +1 -0
  175. package/build-types/custom-select-control-v2/stories/index.story.d.ts +19 -0
  176. package/build-types/custom-select-control-v2/stories/index.story.d.ts.map +1 -0
  177. package/build-types/custom-select-control-v2/styles.d.ts +47 -0
  178. package/build-types/custom-select-control-v2/styles.d.ts.map +1 -0
  179. package/build-types/custom-select-control-v2/types.d.ts +57 -0
  180. package/build-types/custom-select-control-v2/types.d.ts.map +1 -0
  181. package/build-types/date-time/date/styles.d.ts +4 -1
  182. package/build-types/date-time/date/styles.d.ts.map +1 -1
  183. package/build-types/date-time/time/timezone.d.ts.map +1 -1
  184. package/build-types/dimension-control/index.d.ts.map +1 -1
  185. package/build-types/dimension-control/types.d.ts +6 -0
  186. package/build-types/dimension-control/types.d.ts.map +1 -1
  187. package/build-types/dropdown-menu-v2-ariakit/index.d.ts +11 -2
  188. package/build-types/dropdown-menu-v2-ariakit/index.d.ts.map +1 -1
  189. package/build-types/dropdown-menu-v2-ariakit/stories/index.story.d.ts.map +1 -1
  190. package/build-types/dropdown-menu-v2-ariakit/styles.d.ts +26 -18
  191. package/build-types/dropdown-menu-v2-ariakit/styles.d.ts.map +1 -1
  192. package/build-types/dropdown-menu-v2-ariakit/types.d.ts +0 -6
  193. package/build-types/dropdown-menu-v2-ariakit/types.d.ts.map +1 -1
  194. package/build-types/focal-point-picker/controls.d.ts +1 -1
  195. package/build-types/focal-point-picker/controls.d.ts.map +1 -1
  196. package/build-types/focal-point-picker/index.d.ts +1 -1
  197. package/build-types/focal-point-picker/index.d.ts.map +1 -1
  198. package/build-types/focal-point-picker/stories/index.story.d.ts +8 -4
  199. package/build-types/focal-point-picker/stories/index.story.d.ts.map +1 -1
  200. package/build-types/focal-point-picker/types.d.ts +7 -0
  201. package/build-types/focal-point-picker/types.d.ts.map +1 -1
  202. package/build-types/font-size-picker/font-size-picker-select.d.ts.map +1 -1
  203. package/build-types/font-size-picker/font-size-picker-toggle-group.d.ts.map +1 -1
  204. package/build-types/font-size-picker/index.d.ts.map +1 -1
  205. package/build-types/font-size-picker/types.d.ts +8 -1
  206. package/build-types/font-size-picker/types.d.ts.map +1 -1
  207. package/build-types/form-token-field/index.d.ts.map +1 -1
  208. package/build-types/form-token-field/token-input.d.ts.map +1 -1
  209. package/build-types/form-token-field/types.d.ts +1 -1
  210. package/build-types/form-token-field/types.d.ts.map +1 -1
  211. package/build-types/heading/component.d.ts +4 -1
  212. package/build-types/heading/component.d.ts.map +1 -1
  213. package/build-types/heading/hook.d.ts.map +1 -1
  214. package/build-types/heading/types.d.ts +20 -1
  215. package/build-types/heading/types.d.ts.map +1 -1
  216. package/build-types/navigation/styles/navigation-styles.d.ts +4 -1
  217. package/build-types/navigation/styles/navigation-styles.d.ts.map +1 -1
  218. package/build-types/palette-edit/index.d.ts +6 -1
  219. package/build-types/palette-edit/index.d.ts.map +1 -1
  220. package/build-types/palette-edit/styles.d.ts +4 -1
  221. package/build-types/palette-edit/styles.d.ts.map +1 -1
  222. package/build-types/private-apis.d.ts.map +1 -1
  223. package/build-types/query-controls/author-select.d.ts +1 -1
  224. package/build-types/query-controls/author-select.d.ts.map +1 -1
  225. package/build-types/query-controls/category-select.d.ts +1 -1
  226. package/build-types/query-controls/category-select.d.ts.map +1 -1
  227. package/build-types/query-controls/index.d.ts +1 -1
  228. package/build-types/query-controls/index.d.ts.map +1 -1
  229. package/build-types/query-controls/types.d.ts +9 -0
  230. package/build-types/query-controls/types.d.ts.map +1 -1
  231. package/build-types/slot-fill/bubbles-virtually/slot.d.ts +1 -1
  232. package/build-types/slot-fill/types.d.ts +16 -6
  233. package/build-types/slot-fill/types.d.ts.map +1 -1
  234. package/build-types/tabs/index.d.ts +3 -2
  235. package/build-types/tabs/index.d.ts.map +1 -1
  236. package/build-types/tabs/stories/index.story.d.ts.map +1 -1
  237. package/build-types/tabs/tab.d.ts +2 -1
  238. package/build-types/tabs/tab.d.ts.map +1 -1
  239. package/build-types/tabs/tabpanel.d.ts +2 -1
  240. package/build-types/tabs/tabpanel.d.ts.map +1 -1
  241. package/build-types/tabs/types.d.ts +8 -3
  242. package/build-types/tabs/types.d.ts.map +1 -1
  243. package/build-types/text/types.d.ts +15 -2
  244. package/build-types/text/types.d.ts.map +1 -1
  245. package/build-types/toggle-group-control/toggle-group-control/utils.d.ts.map +1 -1
  246. package/build-types/tools-panel/tools-panel-item/hook.d.ts.map +1 -1
  247. package/package.json +19 -19
  248. package/src/angle-picker-control/index.tsx +0 -1
  249. package/src/border-control/border-control-dropdown/component.tsx +3 -1
  250. package/src/border-control/border-control-dropdown/hook.ts +3 -2
  251. package/src/border-control/styles.ts +2 -9
  252. package/src/box-control/stories/index.story.tsx +82 -0
  253. package/src/button/style.scss +10 -2
  254. package/src/checkbox-control/README.md +2 -1
  255. package/src/checkbox-control/index.tsx +8 -6
  256. package/src/checkbox-control/test/__snapshots__/index.tsx.snap +3 -8
  257. package/src/checkbox-control/test/index.tsx +7 -0
  258. package/src/checkbox-control/types.ts +3 -2
  259. package/src/combobox-control/README.md +1 -3
  260. package/src/custom-select-control/test/index.js +367 -35
  261. package/src/custom-select-control-v2/README.md +73 -0
  262. package/src/custom-select-control-v2/index.tsx +99 -0
  263. package/src/custom-select-control-v2/stories/index.story.tsx +149 -0
  264. package/src/custom-select-control-v2/styles.ts +76 -0
  265. package/src/custom-select-control-v2/types.ts +63 -0
  266. package/src/date-time/time/timezone.tsx +15 -3
  267. package/src/dimension-control/index.tsx +2 -0
  268. package/src/dimension-control/test/__snapshots__/index.test.js.snap +2 -2
  269. package/src/dimension-control/types.ts +6 -0
  270. package/src/dropdown-menu-v2-ariakit/README.md +19 -5
  271. package/src/dropdown-menu-v2-ariakit/index.tsx +85 -36
  272. package/src/dropdown-menu-v2-ariakit/stories/index.story.tsx +204 -90
  273. package/src/dropdown-menu-v2-ariakit/styles.ts +165 -117
  274. package/src/dropdown-menu-v2-ariakit/test/index.tsx +5 -10
  275. package/src/dropdown-menu-v2-ariakit/types.ts +0 -7
  276. package/src/focal-point-picker/controls.tsx +4 -0
  277. package/src/focal-point-picker/index.tsx +2 -0
  278. package/src/focal-point-picker/styles/focal-point-picker-style.ts +1 -1
  279. package/src/focal-point-picker/types.ts +7 -0
  280. package/src/font-size-picker/font-size-picker-select.tsx +2 -0
  281. package/src/font-size-picker/font-size-picker-toggle-group.tsx +9 -1
  282. package/src/font-size-picker/index.tsx +11 -3
  283. package/src/font-size-picker/types.ts +8 -1
  284. package/src/form-toggle/style.scss +40 -8
  285. package/src/form-token-field/index.tsx +11 -3
  286. package/src/form-token-field/token-input.tsx +1 -3
  287. package/src/form-token-field/types.ts +1 -0
  288. package/src/heading/README.md +6 -1
  289. package/src/heading/hook.ts +6 -3
  290. package/src/heading/types.ts +23 -1
  291. package/src/index.native.js +0 -2
  292. package/src/mobile/global-styles-context/test/utils.native.js +22 -0
  293. package/src/mobile/global-styles-context/utils.native.js +14 -0
  294. package/src/mobile/link-settings/style.native.scss +0 -17
  295. package/src/mobile/utils/alignments.native.js +1 -0
  296. package/src/navigable-container/README.md +1 -1
  297. package/src/palette-edit/index.tsx +22 -8
  298. package/src/palette-edit/style.scss +2 -2
  299. package/src/palette-edit/test/index.tsx +75 -1
  300. package/src/private-apis.ts +4 -2
  301. package/src/query-controls/author-select.tsx +2 -0
  302. package/src/query-controls/category-select.tsx +2 -0
  303. package/src/query-controls/index.tsx +6 -1
  304. package/src/query-controls/types.ts +9 -0
  305. package/src/search-control/README.md +2 -0
  306. package/src/slot-fill/README.md +1 -1
  307. package/src/slot-fill/types.ts +18 -6
  308. package/src/spinner/README.md +2 -0
  309. package/src/tabs/README.md +4 -4
  310. package/src/tabs/index.tsx +34 -3
  311. package/src/tabs/stories/index.story.tsx +56 -48
  312. package/src/tabs/tab.tsx +7 -7
  313. package/src/tabs/tabpanel.tsx +10 -6
  314. package/src/tabs/test/index.tsx +180 -87
  315. package/src/tabs/types.ts +8 -3
  316. package/src/text/README.md +5 -1
  317. package/src/text/types.ts +15 -2
  318. package/src/toggle-control/README.md +2 -2
  319. package/src/toggle-group-control/test/index.tsx +54 -1
  320. package/src/toggle-group-control/toggle-group-control/utils.ts +15 -20
  321. package/src/tools-panel/tools-panel-item/hook.ts +21 -23
  322. package/tsconfig.tsbuildinfo +1 -1
  323. package/build/mobile/inserter-button/index.native.js +0 -98
  324. package/build/mobile/inserter-button/index.native.js.map +0 -1
  325. package/build/mobile/inserter-button/sparkles.js +0 -23
  326. package/build/mobile/inserter-button/sparkles.js.map +0 -1
  327. package/build/mobile/link-settings/image-link-destinations-screen.native.js +0 -119
  328. package/build/mobile/link-settings/image-link-destinations-screen.native.js.map +0 -1
  329. package/build-module/mobile/inserter-button/index.native.js +0 -89
  330. package/build-module/mobile/inserter-button/index.native.js.map +0 -1
  331. package/build-module/mobile/inserter-button/sparkles.js +0 -15
  332. package/build-module/mobile/inserter-button/sparkles.js.map +0 -1
  333. package/build-module/mobile/link-settings/image-link-destinations-screen.native.js +0 -110
  334. package/build-module/mobile/link-settings/image-link-destinations-screen.native.js.map +0 -1
  335. package/build-types/mobile/inserter-button/sparkles.d.ts +0 -3
  336. package/build-types/mobile/inserter-button/sparkles.d.ts.map +0 -1
  337. package/src/box-control/stories/index.story.js +0 -75
  338. package/src/mobile/inserter-button/README.md +0 -62
  339. package/src/mobile/inserter-button/index.native.js +0 -116
  340. package/src/mobile/inserter-button/sparkles.js +0 -15
  341. package/src/mobile/inserter-button/style.native.scss +0 -72
  342. package/src/mobile/link-settings/image-link-destinations-screen.native.js +0 -152
@@ -2,12 +2,12 @@
2
2
  * External dependencies
3
3
  */
4
4
  import { render, screen, waitFor } from '@testing-library/react';
5
- import userEvent from '@testing-library/user-event';
5
+ import { press, click } from '@ariakit/test';
6
6
 
7
7
  /**
8
8
  * WordPress dependencies
9
9
  */
10
- import { useState } from '@wordpress/element';
10
+ import { useEffect, useState } from '@wordpress/element';
11
11
 
12
12
  /**
13
13
  * Internal dependencies
@@ -16,7 +16,7 @@ import Tabs from '..';
16
16
  import type { TabsProps } from '../types';
17
17
 
18
18
  type Tab = {
19
- id: string;
19
+ tabId: string;
20
20
  title: string;
21
21
  content: React.ReactNode;
22
22
  tab: {
@@ -30,19 +30,19 @@ type Tab = {
30
30
 
31
31
  const TABS: Tab[] = [
32
32
  {
33
- id: 'alpha',
33
+ tabId: 'alpha',
34
34
  title: 'Alpha',
35
35
  content: 'Selected tab: Alpha',
36
36
  tab: { className: 'alpha-class' },
37
37
  },
38
38
  {
39
- id: 'beta',
39
+ tabId: 'beta',
40
40
  title: 'Beta',
41
41
  content: 'Selected tab: Beta',
42
42
  tab: { className: 'beta-class' },
43
43
  },
44
44
  {
45
- id: 'gamma',
45
+ tabId: 'gamma',
46
46
  title: 'Gamma',
47
47
  content: 'Selected tab: Gamma',
48
48
  tab: { className: 'gamma-class' },
@@ -52,7 +52,7 @@ const TABS: Tab[] = [
52
52
  const TABS_WITH_DELTA: Tab[] = [
53
53
  ...TABS,
54
54
  {
55
- id: 'delta',
55
+ tabId: 'delta',
56
56
  title: 'Delta',
57
57
  content: 'Selected tab: Delta',
58
58
  tab: { className: 'delta-class' },
@@ -70,8 +70,8 @@ const UncontrolledTabs = ( {
70
70
  <Tabs.TabList>
71
71
  { tabs.map( ( tabObj ) => (
72
72
  <Tabs.Tab
73
- key={ tabObj.id }
74
- id={ tabObj.id }
73
+ key={ tabObj.tabId }
74
+ tabId={ tabObj.tabId }
75
75
  className={ tabObj.tab.className }
76
76
  disabled={ tabObj.tab.disabled }
77
77
  >
@@ -81,8 +81,8 @@ const UncontrolledTabs = ( {
81
81
  </Tabs.TabList>
82
82
  { tabs.map( ( tabObj ) => (
83
83
  <Tabs.TabPanel
84
- key={ tabObj.id }
85
- id={ tabObj.id }
84
+ key={ tabObj.tabId }
85
+ tabId={ tabObj.tabId }
86
86
  focusable={ tabObj.tabpanel?.focusable }
87
87
  >
88
88
  { tabObj.content }
@@ -102,6 +102,10 @@ const ControlledTabs = ( {
102
102
  string | undefined | null
103
103
  >( props.selectedTabId );
104
104
 
105
+ useEffect( () => {
106
+ setSelectedTabId( props.selectedTabId );
107
+ }, [ props.selectedTabId ] );
108
+
105
109
  return (
106
110
  <Tabs
107
111
  { ...props }
@@ -114,8 +118,8 @@ const ControlledTabs = ( {
114
118
  <Tabs.TabList>
115
119
  { tabs.map( ( tabObj ) => (
116
120
  <Tabs.Tab
117
- key={ tabObj.id }
118
- id={ tabObj.id }
121
+ key={ tabObj.tabId }
122
+ tabId={ tabObj.tabId }
119
123
  className={ tabObj.tab.className }
120
124
  disabled={ tabObj.tab.disabled }
121
125
  >
@@ -124,7 +128,7 @@ const ControlledTabs = ( {
124
128
  ) ) }
125
129
  </Tabs.TabList>
126
130
  { tabs.map( ( tabObj ) => (
127
- <Tabs.TabPanel key={ tabObj.id } id={ tabObj.id }>
131
+ <Tabs.TabPanel key={ tabObj.tabId } tabId={ tabObj.tabId }>
128
132
  { tabObj.content }
129
133
  </Tabs.TabPanel>
130
134
  ) ) }
@@ -184,28 +188,24 @@ describe( 'Tabs', () => {
184
188
  } );
185
189
  describe( 'Focus Behavior', () => {
186
190
  it( 'should focus on the related TabPanel when pressing the Tab key', async () => {
187
- const user = userEvent.setup();
188
-
189
191
  render( <UncontrolledTabs tabs={ TABS } /> );
190
192
 
191
193
  const selectedTabPanel = await screen.findByRole( 'tabpanel' );
192
194
 
193
195
  // Tab should initially focus the first tab in the tablist, which
194
196
  // is Alpha.
195
- await user.keyboard( '[Tab]' );
197
+ await press.Tab();
196
198
  expect(
197
199
  await screen.findByRole( 'tab', { name: 'Alpha' } )
198
200
  ).toHaveFocus();
199
201
 
200
202
  // By default the tabpanel should receive focus
201
- await user.keyboard( '[Tab]' );
203
+ await press.Tab();
202
204
  expect( selectedTabPanel ).toHaveFocus();
203
205
  } );
204
206
  it( 'should not focus on the related TabPanel when pressing the Tab key if `focusable: false` is set', async () => {
205
- const user = userEvent.setup();
206
-
207
207
  const TABS_WITH_ALPHA_FOCUSABLE_FALSE = TABS.map( ( tabObj ) =>
208
- tabObj.id === 'alpha'
208
+ tabObj.tabId === 'alpha'
209
209
  ? {
210
210
  ...tabObj,
211
211
  content: (
@@ -229,13 +229,13 @@ describe( 'Tabs', () => {
229
229
 
230
230
  // Tab should initially focus the first tab in the tablist, which
231
231
  // is Alpha.
232
- await user.keyboard( '[Tab]' );
232
+ await press.Tab();
233
233
  expect(
234
234
  await screen.findByRole( 'tab', { name: 'Alpha' } )
235
235
  ).toHaveFocus();
236
236
  // Because the alpha tabpanel is set to `focusable: false`, pressing
237
237
  // the Tab key should focus the button, not the tabpanel
238
- await user.keyboard( '[Tab]' );
238
+ await press.Tab();
239
239
  expect( alphaButton ).toHaveFocus();
240
240
  } );
241
241
  } );
@@ -258,7 +258,6 @@ describe( 'Tabs', () => {
258
258
 
259
259
  describe( 'Tab Activation', () => {
260
260
  it( 'defaults to automatic tab activation (pointer clicks)', async () => {
261
- const user = userEvent.setup();
262
261
  const mockOnSelect = jest.fn();
263
262
 
264
263
  render(
@@ -273,7 +272,7 @@ describe( 'Tabs', () => {
273
272
  expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' );
274
273
 
275
274
  // Click on Beta, make sure beta is the selected tab
276
- await user.click( screen.getByRole( 'tab', { name: 'Beta' } ) );
275
+ await click( screen.getByRole( 'tab', { name: 'Beta' } ) );
277
276
 
278
277
  expect( await getSelectedTab() ).toHaveTextContent( 'Beta' );
279
278
  expect(
@@ -282,7 +281,7 @@ describe( 'Tabs', () => {
282
281
  expect( mockOnSelect ).toHaveBeenLastCalledWith( 'beta' );
283
282
 
284
283
  // Click on Alpha, make sure beta is the selected tab
285
- await user.click( screen.getByRole( 'tab', { name: 'Alpha' } ) );
284
+ await click( screen.getByRole( 'tab', { name: 'Alpha' } ) );
286
285
 
287
286
  expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
288
287
  expect(
@@ -292,7 +291,6 @@ describe( 'Tabs', () => {
292
291
  } );
293
292
 
294
293
  it( 'defaults to automatic tab activation (arrow keys)', async () => {
295
- const user = userEvent.setup();
296
294
  const mockOnSelect = jest.fn();
297
295
 
298
296
  render(
@@ -307,12 +305,12 @@ describe( 'Tabs', () => {
307
305
  // Tab to focus the tablist. Make sure alpha is focused.
308
306
  expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
309
307
  expect( await getSelectedTab() ).not.toHaveFocus();
310
- await user.keyboard( '[Tab]' );
308
+ await press.Tab();
311
309
  expect( await getSelectedTab() ).toHaveFocus();
312
310
 
313
311
  // Navigate forward with arrow keys and make sure the Beta tab is
314
312
  // selected automatically.
315
- await user.keyboard( '[ArrowRight]' );
313
+ await press.ArrowRight();
316
314
  expect( await getSelectedTab() ).toHaveTextContent( 'Beta' );
317
315
  expect( await getSelectedTab() ).toHaveFocus();
318
316
  expect( mockOnSelect ).toHaveBeenCalledTimes( 2 );
@@ -320,7 +318,7 @@ describe( 'Tabs', () => {
320
318
 
321
319
  // Navigate backwards with arrow keys. Make sure alpha is
322
320
  // selected automatically.
323
- await user.keyboard( '[ArrowLeft]' );
321
+ await press.ArrowLeft();
324
322
  expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
325
323
  expect( await getSelectedTab() ).toHaveFocus();
326
324
  expect( mockOnSelect ).toHaveBeenCalledTimes( 3 );
@@ -328,7 +326,6 @@ describe( 'Tabs', () => {
328
326
  } );
329
327
 
330
328
  it( 'wraps around the last/first tab when using arrow keys', async () => {
331
- const user = userEvent.setup();
332
329
  const mockOnSelect = jest.fn();
333
330
 
334
331
  render(
@@ -341,12 +338,12 @@ describe( 'Tabs', () => {
341
338
  // Tab to focus the tablist. Make sure Alpha is focused.
342
339
  expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
343
340
  expect( await getSelectedTab() ).not.toHaveFocus();
344
- await user.keyboard( '[Tab]' );
341
+ await press.Tab();
345
342
  expect( await getSelectedTab() ).toHaveFocus();
346
343
 
347
344
  // Navigate backwards with arrow keys and make sure that the Gamma tab
348
345
  // (the last tab) is selected automatically.
349
- await user.keyboard( '[ArrowLeft]' );
346
+ await press.ArrowLeft();
350
347
  expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' );
351
348
  expect( await getSelectedTab() ).toHaveFocus();
352
349
  expect( mockOnSelect ).toHaveBeenCalledTimes( 2 );
@@ -354,7 +351,7 @@ describe( 'Tabs', () => {
354
351
 
355
352
  // Navigate forward with arrow keys. Make sure alpha (the first tab) is
356
353
  // selected automatically.
357
- await user.keyboard( '[ArrowRight]' );
354
+ await press.ArrowRight();
358
355
  expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
359
356
  expect( await getSelectedTab() ).toHaveFocus();
360
357
  expect( mockOnSelect ).toHaveBeenCalledTimes( 3 );
@@ -362,7 +359,6 @@ describe( 'Tabs', () => {
362
359
  } );
363
360
 
364
361
  it( 'should not move tab selection when pressing the up/down arrow keys, unless the orientation is changed to `vertical`', async () => {
365
- const user = userEvent.setup();
366
362
  const mockOnSelect = jest.fn();
367
363
 
368
364
  const { rerender } = render(
@@ -377,18 +373,18 @@ describe( 'Tabs', () => {
377
373
  // Tab to focus the tablist. Make sure alpha is focused.
378
374
  expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
379
375
  expect( await getSelectedTab() ).not.toHaveFocus();
380
- await user.keyboard( '[Tab]' );
376
+ await press.Tab();
381
377
  expect( await getSelectedTab() ).toHaveFocus();
382
378
 
383
379
  // Press the arrow up key, nothing happens.
384
- await user.keyboard( '[ArrowUp]' );
380
+ await press.ArrowUp();
385
381
  expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
386
382
  expect( await getSelectedTab() ).toHaveFocus();
387
383
  expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
388
384
  expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' );
389
385
 
390
386
  // Press the arrow down key, nothing happens
391
- await user.keyboard( '[ArrowDown]' );
387
+ await press.ArrowDown();
392
388
  expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
393
389
  expect( await getSelectedTab() ).toHaveFocus();
394
390
  expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
@@ -415,7 +411,7 @@ describe( 'Tabs', () => {
415
411
 
416
412
  // Navigate forward with arrow keys and make sure the Beta tab is
417
413
  // selected automatically.
418
- await user.keyboard( '[ArrowDown]' );
414
+ await press.ArrowDown();
419
415
  expect( await getSelectedTab() ).toHaveTextContent( 'Beta' );
420
416
  expect( await getSelectedTab() ).toHaveFocus();
421
417
  expect( mockOnSelect ).toHaveBeenCalledTimes( 2 );
@@ -423,7 +419,7 @@ describe( 'Tabs', () => {
423
419
 
424
420
  // Navigate backwards with arrow keys. Make sure alpha is
425
421
  // selected automatically.
426
- await user.keyboard( '[ArrowUp]' );
422
+ await press.ArrowUp();
427
423
  expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
428
424
  expect( await getSelectedTab() ).toHaveFocus();
429
425
  expect( mockOnSelect ).toHaveBeenCalledTimes( 3 );
@@ -431,7 +427,7 @@ describe( 'Tabs', () => {
431
427
 
432
428
  // Navigate backwards with arrow keys. Make sure alpha is
433
429
  // selected automatically.
434
- await user.keyboard( '[ArrowUp]' );
430
+ await press.ArrowUp();
435
431
  expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' );
436
432
  expect( await getSelectedTab() ).toHaveFocus();
437
433
  expect( mockOnSelect ).toHaveBeenCalledTimes( 4 );
@@ -439,7 +435,7 @@ describe( 'Tabs', () => {
439
435
 
440
436
  // Navigate backwards with arrow keys. Make sure alpha is
441
437
  // selected automatically.
442
- await user.keyboard( '[ArrowDown]' );
438
+ await press.ArrowDown();
443
439
  expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
444
440
  expect( await getSelectedTab() ).toHaveFocus();
445
441
  expect( mockOnSelect ).toHaveBeenCalledTimes( 5 );
@@ -447,11 +443,10 @@ describe( 'Tabs', () => {
447
443
  } );
448
444
 
449
445
  it( 'should move focus on a tab even if disabled with arrow key, but not with pointer clicks', async () => {
450
- const user = userEvent.setup();
451
446
  const mockOnSelect = jest.fn();
452
447
 
453
448
  const TABS_WITH_DELTA_DISABLED = TABS_WITH_DELTA.map( ( tabObj ) =>
454
- tabObj.id === 'delta'
449
+ tabObj.tabId === 'delta'
455
450
  ? {
456
451
  ...tabObj,
457
452
  tab: {
@@ -477,7 +472,7 @@ describe( 'Tabs', () => {
477
472
  // Tab to focus the tablist. Make sure Alpha is focused.
478
473
  expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
479
474
  expect( await getSelectedTab() ).not.toHaveFocus();
480
- await user.keyboard( '[Tab]' );
475
+ await press.Tab();
481
476
  expect( await getSelectedTab() ).toHaveFocus();
482
477
  // Confirm onSelect has not been re-called
483
478
  expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
@@ -487,7 +482,9 @@ describe( 'Tabs', () => {
487
482
  // it was the tab that was last selected before delta. Therefore, the
488
483
  // `mockOnSelect` function gets called only twice (and not three times)
489
484
  // - it will receive focus, when using arrow keys
490
- await user.keyboard( '[ArrowRight][ArrowRight][ArrowRight]' );
485
+ await press.ArrowRight();
486
+ await press.ArrowRight();
487
+ await press.ArrowRight();
491
488
  expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' );
492
489
  expect(
493
490
  screen.getByRole( 'tab', { name: 'Delta' } )
@@ -498,7 +495,7 @@ describe( 'Tabs', () => {
498
495
  // Navigate backwards with arrow keys. The gamma tab receives focus.
499
496
  // The `mockOnSelect` callback doesn't fire, since the gamma tab was
500
497
  // already selected.
501
- await user.keyboard( '[ArrowLeft]' );
498
+ await press.ArrowLeft();
502
499
  expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' );
503
500
  expect( await getSelectedTab() ).toHaveFocus();
504
501
  expect( mockOnSelect ).toHaveBeenCalledTimes( 3 );
@@ -506,20 +503,18 @@ describe( 'Tabs', () => {
506
503
  // Click on the disabled tab. Compared to using arrow keys to move the
507
504
  // focus, disabled tabs ignore pointer clicks — and therefore, they don't
508
505
  // receive focus, nor they cause the `mockOnSelect` function to fire.
509
- await user.click( screen.getByRole( 'tab', { name: 'Delta' } ) );
506
+ await click( screen.getByRole( 'tab', { name: 'Delta' } ) );
510
507
  expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' );
511
508
  expect( await getSelectedTab() ).toHaveFocus();
512
509
  expect( mockOnSelect ).toHaveBeenCalledTimes( 3 );
513
510
  } );
514
511
 
515
512
  it( 'should not focus the next tab when the Tab key is pressed', async () => {
516
- const user = userEvent.setup();
517
-
518
513
  render( <UncontrolledTabs tabs={ TABS } /> );
519
514
 
520
515
  // Tab should initially focus the first tab in the tablist, which
521
516
  // is Alpha.
522
- await user.keyboard( '[Tab]' );
517
+ await press.Tab();
523
518
  expect(
524
519
  await screen.findByRole( 'tab', { name: 'Alpha' } )
525
520
  ).toHaveFocus();
@@ -527,7 +522,7 @@ describe( 'Tabs', () => {
527
522
  // Because all other tabs should have `tabindex=-1`, pressing Tab
528
523
  // should NOT move the focus to the next tab, which is Beta.
529
524
  // Instead, focus should go to the currently selected tabpanel (alpha).
530
- await user.keyboard( '[Tab]' );
525
+ await press.Tab();
531
526
  expect(
532
527
  await screen.findByRole( 'tabpanel', {
533
528
  name: 'Alpha',
@@ -536,7 +531,6 @@ describe( 'Tabs', () => {
536
531
  } );
537
532
 
538
533
  it( 'switches to manual tab activation when the `selectOnMove` prop is set to `false`', async () => {
539
- const user = userEvent.setup();
540
534
  const mockOnSelect = jest.fn();
541
535
 
542
536
  render(
@@ -554,7 +548,7 @@ describe( 'Tabs', () => {
554
548
 
555
549
  // Click on Alpha and make sure it is selected.
556
550
  // onSelect shouldn't fire since the selected tab didn't change.
557
- await user.click( screen.getByRole( 'tab', { name: 'Alpha' } ) );
551
+ await click( screen.getByRole( 'tab', { name: 'Alpha' } ) );
558
552
  expect(
559
553
  await screen.findByRole( 'tab', { name: 'Alpha' } )
560
554
  ).toHaveFocus();
@@ -565,13 +559,13 @@ describe( 'Tabs', () => {
565
559
  // that the tab selection happens only when pressing the spacebar
566
560
  // or enter key. onSelect shouldn't fire since the selected tab
567
561
  // didn't change.
568
- await user.keyboard( '[ArrowRight]' );
562
+ await press.ArrowRight();
569
563
  expect(
570
564
  await screen.findByRole( 'tab', { name: 'Beta' } )
571
565
  ).toHaveFocus();
572
566
  expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
573
567
 
574
- await user.keyboard( '[Enter]' );
568
+ await press.Enter();
575
569
  expect( mockOnSelect ).toHaveBeenCalledTimes( 2 );
576
570
  expect( mockOnSelect ).toHaveBeenLastCalledWith( 'beta' );
577
571
 
@@ -579,7 +573,7 @@ describe( 'Tabs', () => {
579
573
  // focused, but that tab selection happens only when pressing the
580
574
  // spacebar or enter key. onSelect shouldn't fire since the selected
581
575
  // tab didn't change.
582
- await user.keyboard( '[ArrowRight]' );
576
+ await press.ArrowRight();
583
577
  expect(
584
578
  await screen.findByRole( 'tab', { name: 'Gamma' } )
585
579
  ).toHaveFocus();
@@ -588,7 +582,7 @@ describe( 'Tabs', () => {
588
582
  screen.getByRole( 'tab', { name: 'Gamma' } )
589
583
  ).toHaveFocus();
590
584
 
591
- await user.keyboard( '[Space]' );
585
+ await press.Space();
592
586
  expect( mockOnSelect ).toHaveBeenCalledTimes( 3 );
593
587
  expect( mockOnSelect ).toHaveBeenLastCalledWith( 'gamma' );
594
588
  } );
@@ -614,7 +608,7 @@ describe( 'Tabs', () => {
614
608
  } );
615
609
  it( 'should not load any tab if the active tab is removed and there are no enabled tabs', async () => {
616
610
  const TABS_WITH_BETA_GAMMA_DISABLED = TABS.map( ( tabObj ) =>
617
- tabObj.id !== 'alpha'
611
+ tabObj.tabId !== 'alpha'
618
612
  ? {
619
613
  ...tabObj,
620
614
  tab: {
@@ -691,7 +685,6 @@ describe( 'Tabs', () => {
691
685
  } );
692
686
 
693
687
  it( 'should fall back to the tab associated to `initialTabId` if the currently active tab is removed', async () => {
694
- const user = userEvent.setup();
695
688
  const mockOnSelect = jest.fn();
696
689
 
697
690
  const { rerender } = render(
@@ -704,9 +697,7 @@ describe( 'Tabs', () => {
704
697
 
705
698
  expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' );
706
699
 
707
- await user.click(
708
- screen.getByRole( 'tab', { name: 'Alpha' } )
709
- );
700
+ await click( screen.getByRole( 'tab', { name: 'Alpha' } ) );
710
701
  expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
711
702
  expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' );
712
703
 
@@ -722,7 +713,6 @@ describe( 'Tabs', () => {
722
713
  } );
723
714
 
724
715
  it( 'should fall back to the tab associated to `initialTabId` if the currently active tab becomes disabled', async () => {
725
- const user = userEvent.setup();
726
716
  const mockOnSelect = jest.fn();
727
717
 
728
718
  const { rerender } = render(
@@ -735,14 +725,12 @@ describe( 'Tabs', () => {
735
725
 
736
726
  expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' );
737
727
 
738
- await user.click(
739
- screen.getByRole( 'tab', { name: 'Alpha' } )
740
- );
728
+ await click( screen.getByRole( 'tab', { name: 'Alpha' } ) );
741
729
  expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
742
730
  expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' );
743
731
 
744
732
  const TABS_WITH_ALPHA_DISABLED = TABS.map( ( tabObj ) =>
745
- tabObj.id === 'alpha'
733
+ tabObj.tabId === 'alpha'
746
734
  ? {
747
735
  ...tabObj,
748
736
  tab: {
@@ -813,12 +801,11 @@ describe( 'Tabs', () => {
813
801
 
814
802
  describe( 'Disabled tab', () => {
815
803
  it( 'should disable the tab when `disabled` is `true`', async () => {
816
- const user = userEvent.setup();
817
804
  const mockOnSelect = jest.fn();
818
805
 
819
806
  const TABS_WITH_DELTA_DISABLED = TABS_WITH_DELTA.map(
820
807
  ( tabObj ) =>
821
- tabObj.id === 'delta'
808
+ tabObj.tabId === 'delta'
822
809
  ? {
823
810
  ...tabObj,
824
811
  tab: {
@@ -844,10 +831,15 @@ describe( 'Tabs', () => {
844
831
  expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
845
832
  expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' );
846
833
 
834
+ // Move focus to the tablist, make sure alpha is focused.
835
+ await press.Tab();
836
+ expect(
837
+ screen.getByRole( 'tab', { name: 'Alpha' } )
838
+ ).toHaveFocus();
839
+
847
840
  // onSelect should not be called since the disabled tab is
848
841
  // highlighted, but not selected.
849
- await user.keyboard( '[Tab]' );
850
- await user.keyboard( '[ArrowLeft]' );
842
+ await press.ArrowLeft();
851
843
  expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
852
844
 
853
845
  // Delta (which is disabled) has focus
@@ -861,7 +853,7 @@ describe( 'Tabs', () => {
861
853
 
862
854
  it( 'should select first enabled tab when the initial tab is disabled', async () => {
863
855
  const TABS_WITH_ALPHA_DISABLED = TABS.map( ( tabObj ) =>
864
- tabObj.id === 'alpha'
856
+ tabObj.tabId === 'alpha'
865
857
  ? {
866
858
  ...tabObj,
867
859
  tab: {
@@ -890,7 +882,7 @@ describe( 'Tabs', () => {
890
882
 
891
883
  it( 'should select first enabled tab when the tab associated to `initialTabId` is disabled', async () => {
892
884
  const TABS_ONLY_GAMMA_ENABLED = TABS.map( ( tabObj ) =>
893
- tabObj.id !== 'gamma'
885
+ tabObj.tabId !== 'gamma'
894
886
  ? {
895
887
  ...tabObj,
896
888
  tab: {
@@ -932,7 +924,7 @@ describe( 'Tabs', () => {
932
924
  expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' );
933
925
 
934
926
  const TABS_WITH_ALPHA_DISABLED = TABS.map( ( tabObj ) =>
935
- tabObj.id === 'alpha'
927
+ tabObj.tabId === 'alpha'
936
928
  ? {
937
929
  ...tabObj,
938
930
  tab: {
@@ -979,7 +971,7 @@ describe( 'Tabs', () => {
979
971
  expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' );
980
972
 
981
973
  const TABS_WITH_GAMMA_DISABLED = TABS.map( ( tabObj ) =>
982
- tabObj.id === 'gamma'
974
+ tabObj.tabId === 'gamma'
983
975
  ? {
984
976
  ...tabObj,
985
977
  tab: {
@@ -1048,14 +1040,10 @@ describe( 'Tabs', () => {
1048
1040
  />
1049
1041
  );
1050
1042
 
1051
- // No tab should be selected i.e. it doesn't fall back to first tab.
1052
- // `waitFor` is needed here to prevent testing library from
1053
- // throwing a 'not wrapped in `act()`' error.
1054
- await waitFor( () =>
1055
- expect(
1056
- screen.queryByRole( 'tab', { selected: true } )
1057
- ).not.toBeInTheDocument()
1058
- );
1043
+ expect(
1044
+ screen.queryByRole( 'tab', { selected: true } )
1045
+ ).not.toBeInTheDocument();
1046
+
1059
1047
  // No tabpanel should be rendered either
1060
1048
  expect( screen.queryByRole( 'tabpanel' ) ).not.toBeInTheDocument();
1061
1049
  } );
@@ -1067,7 +1055,7 @@ describe( 'Tabs', () => {
1067
1055
  // Remove beta
1068
1056
  rerender(
1069
1057
  <ControlledTabs
1070
- tabs={ TABS.filter( ( tab ) => tab.id !== 'beta' ) }
1058
+ tabs={ TABS.filter( ( tab ) => tab.tabId !== 'beta' ) }
1071
1059
  selectedTabId="beta"
1072
1060
  />
1073
1061
  );
@@ -1101,7 +1089,7 @@ describe( 'Tabs', () => {
1101
1089
  it( 'should not render any tab if `selectedTabId` refers to a disabled tab', async () => {
1102
1090
  const TABS_WITH_DELTA_WITH_BETA_DISABLED = TABS_WITH_DELTA.map(
1103
1091
  ( tabObj ) =>
1104
- tabObj.id === 'beta'
1092
+ tabObj.tabId === 'beta'
1105
1093
  ? {
1106
1094
  ...tabObj,
1107
1095
  tab: {
@@ -1138,7 +1126,7 @@ describe( 'Tabs', () => {
1138
1126
  expect( await getSelectedTab() ).toHaveTextContent( 'Beta' );
1139
1127
 
1140
1128
  const TABS_WITH_BETA_DISABLED = TABS.map( ( tabObj ) =>
1141
- tabObj.id === 'beta'
1129
+ tabObj.tabId === 'beta'
1142
1130
  ? {
1143
1131
  ...tabObj,
1144
1132
  tab: {
@@ -1184,5 +1172,110 @@ describe( 'Tabs', () => {
1184
1172
  ).not.toBeInTheDocument();
1185
1173
  } );
1186
1174
  } );
1175
+
1176
+ describe( 'When `selectOnMove` is `true`', () => {
1177
+ it( 'should automatically select a newly focused tab', async () => {
1178
+ render( <ControlledTabs tabs={ TABS } selectedTabId="beta" /> );
1179
+
1180
+ await press.Tab();
1181
+
1182
+ // Tab key should focus the currently selected tab, which is Beta.
1183
+ expect( await getSelectedTab() ).toHaveTextContent( 'Beta' );
1184
+ expect( await getSelectedTab() ).toHaveFocus();
1185
+
1186
+ // Arrow keys should select and move focus to the next tab.
1187
+ await press.ArrowRight();
1188
+ expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' );
1189
+ expect( await getSelectedTab() ).toHaveFocus();
1190
+ } );
1191
+ it( 'should automatically update focus when the selected tab is changed by the controlling component', async () => {
1192
+ const { rerender } = render(
1193
+ <ControlledTabs tabs={ TABS } selectedTabId="beta" />
1194
+ );
1195
+
1196
+ // Tab key should focus the currently selected tab, which is Beta.
1197
+ await press.Tab();
1198
+ expect( await getSelectedTab() ).toHaveTextContent( 'Beta' );
1199
+ expect( await getSelectedTab() ).toHaveFocus();
1200
+
1201
+ rerender(
1202
+ <ControlledTabs tabs={ TABS } selectedTabId="gamma" />
1203
+ );
1204
+
1205
+ // When the selected tab is changed, it should automatically receive focus.
1206
+ expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' );
1207
+ expect( await getSelectedTab() ).toHaveFocus();
1208
+ } );
1209
+ } );
1210
+ describe( 'When `selectOnMove` is `false`', () => {
1211
+ it( 'should apply focus without automatically changing the selected tab', async () => {
1212
+ render(
1213
+ <ControlledTabs
1214
+ tabs={ TABS }
1215
+ selectedTabId="beta"
1216
+ selectOnMove={ false }
1217
+ />
1218
+ );
1219
+
1220
+ expect( await getSelectedTab() ).toHaveTextContent( 'Beta' );
1221
+
1222
+ // Tab key should focus the currently selected tab, which is Beta.
1223
+ await press.Tab();
1224
+ expect(
1225
+ await screen.findByRole( 'tab', { name: 'Beta' } )
1226
+ ).toHaveFocus();
1227
+
1228
+ // Arrow key should move focus but not automatically change the selected tab.
1229
+ await press.ArrowRight();
1230
+ expect(
1231
+ screen.getByRole( 'tab', { name: 'Gamma' } )
1232
+ ).toHaveFocus();
1233
+ expect( await getSelectedTab() ).toHaveTextContent( 'Beta' );
1234
+
1235
+ // Pressing the spacebar should select the focused tab.
1236
+ await press.Space();
1237
+ expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' );
1238
+
1239
+ // Arrow key should move focus but not automatically change the selected tab.
1240
+ await press.ArrowRight();
1241
+ expect(
1242
+ screen.getByRole( 'tab', { name: 'Alpha' } )
1243
+ ).toHaveFocus();
1244
+ expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' );
1245
+
1246
+ // Pressing the enter/return should select the focused tab.
1247
+ await press.Enter();
1248
+ expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
1249
+ } );
1250
+ it( 'should not automatically update focus when the selected tab is changed by the controlling component', async () => {
1251
+ const { rerender } = render(
1252
+ <ControlledTabs
1253
+ tabs={ TABS }
1254
+ selectedTabId="beta"
1255
+ selectOnMove={ false }
1256
+ />
1257
+ );
1258
+
1259
+ expect( await getSelectedTab() ).toHaveTextContent( 'Beta' );
1260
+
1261
+ // Tab key should focus the currently selected tab, which is Beta.
1262
+ await press.Tab();
1263
+ expect( await getSelectedTab() ).toHaveFocus();
1264
+
1265
+ rerender(
1266
+ <ControlledTabs
1267
+ tabs={ TABS }
1268
+ selectedTabId="gamma"
1269
+ selectOnMove={ false }
1270
+ />
1271
+ );
1272
+
1273
+ // When the selected tab is changed, it should not automatically receive focus.
1274
+ expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' );
1275
+ expect(
1276
+ screen.getByRole( 'tab', { name: 'Beta' } )
1277
+ ).toHaveFocus();
1278
+ } );
1279
+ } );
1187
1280
  } );
1188
1281
  } );