@primer/components 31.2.0-rc.b718ff50 → 31.2.0-rc.decfca99

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 (420) hide show
  1. package/.github/workflows/ci.yml +5 -2
  2. package/.github/workflows/release.yml +1 -0
  3. package/.github/workflows/release_canary.yml +1 -0
  4. package/CHANGELOG.md +13 -1
  5. package/dist/browser.esm.js +620 -618
  6. package/dist/browser.esm.js.map +1 -1
  7. package/dist/browser.umd.js +188 -186
  8. package/dist/browser.umd.js.map +1 -1
  9. package/docs/content/ActionList2.mdx +360 -0
  10. package/docs/content/StateLabel.md +5 -4
  11. package/docs/content/getting-started.md +1 -1
  12. package/docs/src/@primer/gatsby-theme-doctocat/live-code-scope.js +17 -0
  13. package/lib/ActionList/Divider.jsx +29 -0
  14. package/lib/ActionList/Group.jsx +23 -0
  15. package/lib/ActionList/Header.jsx +66 -0
  16. package/lib/ActionList/Item.jsx +301 -0
  17. package/lib/ActionList/List.jsx +138 -0
  18. package/lib/ActionList/index.js +12 -23
  19. package/lib/ActionList2/Description.d.ts +12 -0
  20. package/lib/ActionList2/Description.js +53 -0
  21. package/lib/ActionList2/Description.jsx +30 -0
  22. package/lib/ActionList2/Divider.d.ts +5 -0
  23. package/lib/ActionList2/Divider.js +35 -0
  24. package/lib/ActionList2/Divider.jsx +22 -0
  25. package/lib/ActionList2/Group.d.ts +11 -0
  26. package/lib/ActionList2/Group.js +57 -0
  27. package/lib/ActionList2/Group.jsx +25 -0
  28. package/lib/ActionList2/Header.d.ts +26 -0
  29. package/lib/ActionList2/Header.js +55 -0
  30. package/lib/ActionList2/Header.jsx +36 -0
  31. package/lib/ActionList2/Item.d.ts +63 -0
  32. package/lib/ActionList2/Item.js +244 -0
  33. package/lib/ActionList2/Item.jsx +174 -0
  34. package/lib/ActionList2/LinkItem.d.ts +17 -0
  35. package/lib/ActionList2/LinkItem.js +57 -0
  36. package/lib/ActionList2/LinkItem.jsx +28 -0
  37. package/lib/ActionList2/List.d.ts +26 -0
  38. package/lib/ActionList2/List.js +59 -0
  39. package/lib/ActionList2/List.jsx +41 -0
  40. package/lib/ActionList2/Selection.d.ts +5 -0
  41. package/lib/ActionList2/Selection.js +70 -0
  42. package/lib/ActionList2/Selection.jsx +36 -0
  43. package/lib/ActionList2/Visuals.d.ts +9 -0
  44. package/lib/ActionList2/Visuals.js +90 -0
  45. package/lib/ActionList2/Visuals.jsx +48 -0
  46. package/lib/ActionList2/index.d.ts +36 -0
  47. package/lib/ActionList2/index.js +29 -0
  48. package/lib/ActionMenu.jsx +73 -0
  49. package/lib/AnchoredOverlay/AnchoredOverlay.jsx +100 -0
  50. package/lib/AnchoredOverlay/index.js +4 -12
  51. package/lib/Autocomplete/Autocomplete.d.ts +28 -28
  52. package/lib/Autocomplete/Autocomplete.jsx +100 -0
  53. package/lib/Autocomplete/AutocompleteContext.jsx +5 -0
  54. package/lib/Autocomplete/AutocompleteInput.d.ts +28 -28
  55. package/lib/Autocomplete/AutocompleteInput.jsx +113 -0
  56. package/lib/Autocomplete/AutocompleteMenu.jsx +190 -0
  57. package/lib/Autocomplete/AutocompleteOverlay.jsx +55 -0
  58. package/lib/Autocomplete/index.js +7 -14
  59. package/lib/Avatar.jsx +34 -0
  60. package/lib/AvatarPair.jsx +29 -0
  61. package/lib/AvatarStack.jsx +151 -0
  62. package/lib/BaseStyles.jsx +65 -0
  63. package/lib/BorderBox.jsx +18 -0
  64. package/lib/Box.jsx +10 -0
  65. package/lib/BranchName.jsx +20 -0
  66. package/lib/Breadcrumbs.jsx +74 -0
  67. package/lib/Button/Button.d.ts +25 -25
  68. package/lib/Button/Button.jsx +60 -0
  69. package/lib/Button/ButtonBase.jsx +36 -0
  70. package/lib/Button/ButtonClose.d.ts +45 -45
  71. package/lib/Button/ButtonClose.jsx +55 -0
  72. package/lib/Button/ButtonDanger.d.ts +25 -25
  73. package/lib/Button/ButtonDanger.jsx +63 -0
  74. package/lib/Button/ButtonGroup.jsx +55 -0
  75. package/lib/Button/ButtonInvisible.d.ts +25 -25
  76. package/lib/Button/ButtonInvisible.jsx +52 -0
  77. package/lib/Button/ButtonOutline.d.ts +25 -25
  78. package/lib/Button/ButtonOutline.jsx +63 -0
  79. package/lib/Button/ButtonPrimary.d.ts +25 -25
  80. package/lib/Button/ButtonPrimary.jsx +62 -0
  81. package/lib/Button/ButtonStyles.jsx +37 -0
  82. package/lib/Button/ButtonTableList.jsx +49 -0
  83. package/lib/Button/index.js +21 -70
  84. package/lib/Caret.jsx +93 -0
  85. package/lib/CircleBadge.jsx +43 -0
  86. package/lib/CircleOcticon.d.ts +42 -42
  87. package/lib/CircleOcticon.jsx +21 -0
  88. package/lib/CounterLabel.jsx +44 -0
  89. package/lib/Details.jsx +21 -0
  90. package/lib/Dialog/ConfirmationDialog.jsx +146 -0
  91. package/lib/Dialog/Dialog.jsx +273 -0
  92. package/lib/Dialog.d.ts +45 -45
  93. package/lib/Dialog.jsx +131 -0
  94. package/lib/Dropdown.d.ts +176 -176
  95. package/lib/Dropdown.jsx +134 -0
  96. package/lib/DropdownMenu/DropdownButton.d.ts +46 -46
  97. package/lib/DropdownMenu/DropdownButton.jsx +14 -0
  98. package/lib/DropdownMenu/DropdownMenu.jsx +70 -0
  99. package/lib/DropdownMenu/index.js +6 -20
  100. package/lib/DropdownStyles.js +18 -26
  101. package/lib/FilterList.d.ts +42 -42
  102. package/lib/FilterList.jsx +63 -0
  103. package/lib/FilteredActionList/FilteredActionList.jsx +100 -0
  104. package/lib/FilteredActionList/index.js +4 -12
  105. package/lib/FilteredSearch.jsx +29 -0
  106. package/lib/Flash.jsx +70 -0
  107. package/lib/Flex.jsx +15 -0
  108. package/lib/FormGroup.jsx +25 -0
  109. package/lib/Grid.jsx +15 -0
  110. package/lib/Header.jsx +90 -0
  111. package/lib/Heading.jsx +21 -0
  112. package/lib/Label.jsx +84 -0
  113. package/lib/LabelGroup.jsx +19 -0
  114. package/lib/Link.jsx +38 -0
  115. package/lib/Overlay.jsx +156 -0
  116. package/lib/Pagehead.jsx +18 -0
  117. package/lib/Pagination/Pagination.jsx +163 -0
  118. package/lib/Pagination/index.js +6 -12
  119. package/lib/Pagination/model.jsx +174 -0
  120. package/lib/PointerBox.jsx +25 -0
  121. package/lib/Popover.jsx +210 -0
  122. package/lib/Portal/Portal.jsx +79 -0
  123. package/lib/Portal/index.js +5 -16
  124. package/lib/Position.d.ts +4 -4
  125. package/lib/Position.jsx +46 -0
  126. package/lib/ProgressBar.jsx +39 -0
  127. package/lib/SelectMenu/SelectMenu.d.ts +246 -246
  128. package/lib/SelectMenu/SelectMenu.jsx +114 -0
  129. package/lib/SelectMenu/SelectMenuContext.jsx +5 -0
  130. package/lib/SelectMenu/SelectMenuDivider.jsx +43 -0
  131. package/lib/SelectMenu/SelectMenuFilter.jsx +59 -0
  132. package/lib/SelectMenu/SelectMenuFooter.jsx +46 -0
  133. package/lib/SelectMenu/SelectMenuHeader.jsx +44 -0
  134. package/lib/SelectMenu/SelectMenuItem.d.ts +1 -1
  135. package/lib/SelectMenu/SelectMenuItem.jsx +143 -0
  136. package/lib/SelectMenu/SelectMenuList.jsx +60 -0
  137. package/lib/SelectMenu/SelectMenuLoadingAnimation.jsx +21 -0
  138. package/lib/SelectMenu/SelectMenuModal.d.ts +1 -1
  139. package/lib/SelectMenu/SelectMenuModal.jsx +119 -0
  140. package/lib/SelectMenu/SelectMenuTab.jsx +93 -0
  141. package/lib/SelectMenu/SelectMenuTabPanel.jsx +43 -0
  142. package/lib/SelectMenu/SelectMenuTabs.jsx +58 -0
  143. package/lib/SelectMenu/hooks/useKeyboardNav.js +80 -96
  144. package/lib/SelectMenu/index.js +7 -14
  145. package/lib/SelectPanel/SelectPanel.jsx +105 -0
  146. package/lib/SelectPanel/index.js +4 -12
  147. package/lib/SideNav.jsx +177 -0
  148. package/lib/Spinner.jsx +35 -0
  149. package/lib/StateLabel.d.ts +1 -1
  150. package/lib/StateLabel.js +6 -1
  151. package/lib/StateLabel.jsx +94 -0
  152. package/lib/StyledOcticon.jsx +20 -0
  153. package/lib/SubNav.jsx +104 -0
  154. package/lib/TabNav.jsx +60 -0
  155. package/lib/Text.jsx +14 -0
  156. package/lib/TextInput.jsx +23 -0
  157. package/lib/TextInputWithTokens.d.ts +28 -28
  158. package/lib/TextInputWithTokens.jsx +218 -0
  159. package/lib/ThemeProvider.jsx +130 -0
  160. package/lib/Timeline.d.ts +43 -43
  161. package/lib/Timeline.jsx +124 -0
  162. package/lib/Token/AvatarToken.d.ts +1 -1
  163. package/lib/Token/AvatarToken.jsx +54 -0
  164. package/lib/Token/IssueLabelToken.d.ts +1 -1
  165. package/lib/Token/IssueLabelToken.jsx +125 -0
  166. package/lib/Token/Token.d.ts +1 -1
  167. package/lib/Token/Token.jsx +103 -0
  168. package/lib/Token/TokenBase.jsx +88 -0
  169. package/lib/Token/_RemoveTokenButton.jsx +108 -0
  170. package/lib/Token/_TokenTextContainer.jsx +49 -0
  171. package/lib/Token/index.js +11 -30
  172. package/lib/Tooltip.jsx +246 -0
  173. package/lib/Truncate.jsx +27 -0
  174. package/lib/UnderlineNav.jsx +90 -0
  175. package/lib/_TextInputWrapper.js +2 -2
  176. package/lib/_TextInputWrapper.jsx +120 -0
  177. package/lib/_UnstyledTextInput.jsx +22 -0
  178. package/lib/__tests__/ActionList.test.jsx +49 -0
  179. package/lib/__tests__/ActionList.types.test.jsx +45 -0
  180. package/lib/__tests__/ActionList2.test.d.ts +1 -0
  181. package/lib/__tests__/ActionList2.test.js +53 -0
  182. package/lib/__tests__/ActionList2.test.jsx +46 -0
  183. package/lib/__tests__/ActionMenu.test.jsx +124 -0
  184. package/lib/__tests__/AnchoredOverlay.test.jsx +121 -0
  185. package/lib/__tests__/Autocomplete.test.jsx +299 -0
  186. package/lib/__tests__/Avatar.test.jsx +42 -0
  187. package/lib/__tests__/AvatarStack.test.jsx +43 -0
  188. package/lib/__tests__/BorderBox.test.jsx +36 -0
  189. package/lib/__tests__/Box.test.jsx +41 -0
  190. package/lib/__tests__/BranchName.test.jsx +27 -0
  191. package/lib/__tests__/Breadcrumbs.test.jsx +28 -0
  192. package/lib/__tests__/BreadcrumbsItem.test.jsx +31 -0
  193. package/lib/__tests__/Button.test.jsx +100 -0
  194. package/lib/__tests__/Caret.test.jsx +37 -0
  195. package/lib/__tests__/CircleBadge.test.jsx +55 -0
  196. package/lib/__tests__/CircleOcticon.test.jsx +45 -0
  197. package/lib/__tests__/ConfirmationDialog.test.jsx +119 -0
  198. package/lib/__tests__/CounterLabel.test.jsx +36 -0
  199. package/lib/__tests__/Details.test.jsx +85 -0
  200. package/lib/__tests__/Dialog.test.jsx +139 -0
  201. package/lib/__tests__/Dropdown.test.jsx +49 -0
  202. package/lib/__tests__/DropdownMenu.test.jsx +119 -0
  203. package/lib/__tests__/FilterList.test.jsx +27 -0
  204. package/lib/__tests__/FilterListItem.test.jsx +31 -0
  205. package/lib/__tests__/FilteredSearch.test.jsx +27 -0
  206. package/lib/__tests__/Flash.test.jsx +36 -0
  207. package/lib/__tests__/Flex.test.jsx +51 -0
  208. package/lib/__tests__/FormGroup.test.jsx +36 -0
  209. package/lib/__tests__/Grid.test.jsx +69 -0
  210. package/lib/__tests__/Header.test.jsx +45 -0
  211. package/lib/__tests__/Heading.test.jsx +71 -0
  212. package/lib/__tests__/KeyPaths.types.test.js +5 -8
  213. package/lib/__tests__/Label.test.jsx +33 -0
  214. package/lib/__tests__/LabelGroup.test.jsx +29 -0
  215. package/lib/__tests__/Link.test.jsx +43 -0
  216. package/lib/__tests__/Merge.types.test.js +13 -19
  217. package/lib/__tests__/Overlay.test.jsx +105 -0
  218. package/lib/__tests__/Pagehead.test.jsx +25 -0
  219. package/lib/__tests__/Pagination/Pagination.test.jsx +32 -0
  220. package/lib/__tests__/Pagination/PaginationModel.test.jsx +118 -0
  221. package/lib/__tests__/PointerBox.test.jsx +33 -0
  222. package/lib/__tests__/Popover.test.jsx +58 -0
  223. package/lib/__tests__/Portal.test.jsx +102 -0
  224. package/lib/__tests__/Position.test.jsx +96 -0
  225. package/lib/__tests__/ProgressBar.test.jsx +38 -0
  226. package/lib/__tests__/SelectMenu.test.jsx +120 -0
  227. package/lib/__tests__/SelectPanel.test.jsx +48 -0
  228. package/lib/__tests__/SideNav.test.jsx +55 -0
  229. package/lib/__tests__/Spinner.test.jsx +41 -0
  230. package/lib/__tests__/StateLabel.test.jsx +46 -0
  231. package/lib/__tests__/StyledOcticon.test.jsx +28 -0
  232. package/lib/__tests__/SubNav.test.jsx +47 -0
  233. package/lib/__tests__/SubNavLink.test.jsx +31 -0
  234. package/lib/__tests__/TabNav.test.jsx +32 -0
  235. package/lib/__tests__/Text.test.jsx +71 -0
  236. package/lib/__tests__/TextInput.test.jsx +45 -0
  237. package/lib/__tests__/TextInputWithTokens.test.jsx +302 -0
  238. package/lib/__tests__/ThemeProvider.test.jsx +314 -0
  239. package/lib/__tests__/Timeline.test.jsx +51 -0
  240. package/lib/__tests__/Token.test.jsx +93 -0
  241. package/lib/__tests__/Tooltip.test.jsx +46 -0
  242. package/lib/__tests__/Truncate.test.jsx +41 -0
  243. package/lib/__tests__/UnderlineNav.test.jsx +53 -0
  244. package/lib/__tests__/UnderlineNavLink.test.jsx +31 -0
  245. package/lib/__tests__/behaviors/anchoredPosition.test.js +229 -376
  246. package/lib/__tests__/behaviors/focusTrap.test.jsx +184 -0
  247. package/lib/__tests__/behaviors/focusZone.test.jsx +406 -0
  248. package/lib/__tests__/behaviors/iterateFocusableElements.test.jsx +58 -0
  249. package/lib/__tests__/behaviors/scrollIntoViewingArea.test.js +145 -216
  250. package/lib/__tests__/filterObject.test.js +48 -27
  251. package/lib/__tests__/hooks/useAnchoredPosition.test.jsx +29 -0
  252. package/lib/__tests__/hooks/useOnEscapePress.test.jsx +19 -0
  253. package/lib/__tests__/hooks/useOnOutsideClick.test.jsx +63 -0
  254. package/lib/__tests__/hooks/useOpenAndCloseFocus.test.jsx +61 -0
  255. package/lib/__tests__/hooks/useProvidedStateOrCreate.test.jsx +56 -0
  256. package/lib/__tests__/theme.test.js +33 -34
  257. package/lib/__tests__/themeGet.test.js +12 -23
  258. package/lib/__tests__/useSafeTimeout.test.jsx +36 -0
  259. package/lib/__tests__/utils/createSlots.test.d.ts +1 -0
  260. package/lib/__tests__/utils/createSlots.test.js +75 -0
  261. package/lib/__tests__/utils/createSlots.test.jsx +57 -0
  262. package/lib/behaviors/anchoredPosition.js +205 -234
  263. package/lib/behaviors/focusTrap.js +121 -157
  264. package/lib/behaviors/focusZone.js +434 -509
  265. package/lib/behaviors/scrollIntoViewingArea.js +18 -35
  266. package/lib/constants.js +39 -43
  267. package/lib/drafts.d.ts +7 -0
  268. package/lib/drafts.js +20 -0
  269. package/lib/hooks/index.js +16 -60
  270. package/lib/hooks/useAnchoredPosition.js +32 -40
  271. package/lib/hooks/useCombinedRefs.js +32 -36
  272. package/lib/hooks/useDetails.jsx +39 -0
  273. package/lib/hooks/useDialog.js +72 -96
  274. package/lib/hooks/useFocusTrap.js +43 -60
  275. package/lib/hooks/useFocusZone.js +54 -50
  276. package/lib/hooks/useOnEscapePress.js +25 -36
  277. package/lib/hooks/useOnOutsideClick.jsx +61 -0
  278. package/lib/hooks/useOpenAndCloseFocus.js +22 -34
  279. package/lib/hooks/useOverlay.jsx +15 -0
  280. package/lib/hooks/useProvidedRefOrCreate.js +10 -14
  281. package/lib/hooks/useProvidedStateOrCreate.js +13 -16
  282. package/lib/hooks/useRenderForcingRef.js +13 -17
  283. package/lib/hooks/useResizeObserver.js +15 -18
  284. package/lib/hooks/useSafeTimeout.js +22 -30
  285. package/lib/hooks/useScrollFlash.js +16 -23
  286. package/lib/index.js +161 -636
  287. package/lib/polyfills/eventListenerSignal.js +37 -45
  288. package/lib/stories/ActionList2.stories.js +908 -0
  289. package/lib/stories/TextInput.stories.js +144 -0
  290. package/lib/sx.d.ts +2 -0
  291. package/lib/sx.js +10 -14
  292. package/lib/theme-preval.js +65 -2945
  293. package/lib/theme.js +3 -12
  294. package/lib/utils/create-slots.d.ts +17 -0
  295. package/lib/utils/create-slots.js +105 -0
  296. package/lib/utils/create-slots.jsx +65 -0
  297. package/lib/utils/deprecate.jsx +59 -0
  298. package/lib/utils/isNumeric.jsx +7 -0
  299. package/lib/utils/iterateFocusableElements.js +63 -85
  300. package/lib/utils/ssr.jsx +6 -0
  301. package/lib/utils/test-deprecations.jsx +20 -0
  302. package/lib/utils/test-helpers.jsx +8 -0
  303. package/lib/utils/test-matchers.jsx +100 -0
  304. package/lib/utils/testing.d.ts +14 -1
  305. package/lib/utils/testing.jsx +206 -0
  306. package/lib/utils/theme.js +33 -47
  307. package/lib/utils/types/AriaRole.js +2 -1
  308. package/lib/utils/types/ComponentProps.js +2 -1
  309. package/lib/utils/types/Flatten.js +2 -1
  310. package/lib/utils/types/KeyPaths.js +2 -1
  311. package/lib/utils/types/MandateProps.js +16 -1
  312. package/lib/utils/types/Merge.js +2 -1
  313. package/lib/utils/types/index.js +16 -69
  314. package/lib/utils/uniqueId.js +5 -8
  315. package/lib/utils/use-force-update.d.ts +1 -0
  316. package/lib/utils/use-force-update.js +13 -0
  317. package/lib/utils/useIsomorphicLayoutEffect.js +8 -11
  318. package/lib/utils/userAgent.js +8 -12
  319. package/lib-esm/ActionList2/Description.d.ts +12 -0
  320. package/lib-esm/ActionList2/Description.js +37 -0
  321. package/lib-esm/ActionList2/Divider.d.ts +5 -0
  322. package/lib-esm/ActionList2/Divider.js +23 -0
  323. package/lib-esm/ActionList2/Group.d.ts +11 -0
  324. package/lib-esm/ActionList2/Group.js +40 -0
  325. package/lib-esm/ActionList2/Header.d.ts +26 -0
  326. package/lib-esm/ActionList2/Header.js +44 -0
  327. package/lib-esm/ActionList2/Item.d.ts +63 -0
  328. package/lib-esm/ActionList2/Item.js +210 -0
  329. package/lib-esm/ActionList2/LinkItem.d.ts +17 -0
  330. package/lib-esm/ActionList2/LinkItem.js +43 -0
  331. package/lib-esm/ActionList2/List.d.ts +26 -0
  332. package/lib-esm/ActionList2/List.js +37 -0
  333. package/lib-esm/ActionList2/Selection.d.ts +5 -0
  334. package/lib-esm/ActionList2/Selection.js +52 -0
  335. package/lib-esm/ActionList2/Visuals.d.ts +9 -0
  336. package/lib-esm/ActionList2/Visuals.js +68 -0
  337. package/lib-esm/ActionList2/index.d.ts +36 -0
  338. package/lib-esm/ActionList2/index.js +33 -0
  339. package/lib-esm/Autocomplete/Autocomplete.d.ts +28 -28
  340. package/lib-esm/Autocomplete/AutocompleteInput.d.ts +28 -28
  341. package/lib-esm/Button/Button.d.ts +25 -25
  342. package/lib-esm/Button/ButtonClose.d.ts +45 -45
  343. package/lib-esm/Button/ButtonDanger.d.ts +25 -25
  344. package/lib-esm/Button/ButtonInvisible.d.ts +25 -25
  345. package/lib-esm/Button/ButtonOutline.d.ts +25 -25
  346. package/lib-esm/Button/ButtonPrimary.d.ts +25 -25
  347. package/lib-esm/CircleOcticon.d.ts +42 -42
  348. package/lib-esm/Dialog.d.ts +45 -45
  349. package/lib-esm/Dropdown.d.ts +176 -176
  350. package/lib-esm/DropdownMenu/DropdownButton.d.ts +46 -46
  351. package/lib-esm/FilterList.d.ts +42 -42
  352. package/lib-esm/Position.d.ts +4 -4
  353. package/lib-esm/SelectMenu/SelectMenu.d.ts +246 -246
  354. package/lib-esm/SelectMenu/SelectMenuItem.d.ts +1 -1
  355. package/lib-esm/SelectMenu/SelectMenuModal.d.ts +1 -1
  356. package/lib-esm/StateLabel.d.ts +1 -1
  357. package/lib-esm/StateLabel.js +7 -2
  358. package/lib-esm/TextInputWithTokens.d.ts +28 -28
  359. package/lib-esm/Timeline.d.ts +43 -43
  360. package/lib-esm/Token/AvatarToken.d.ts +1 -1
  361. package/lib-esm/Token/IssueLabelToken.d.ts +1 -1
  362. package/lib-esm/Token/Token.d.ts +1 -1
  363. package/lib-esm/_TextInputWrapper.js +2 -2
  364. package/lib-esm/__tests__/ActionList2.test.d.ts +1 -0
  365. package/lib-esm/__tests__/ActionList2.test.js +41 -0
  366. package/lib-esm/__tests__/utils/createSlots.test.d.ts +1 -0
  367. package/lib-esm/__tests__/utils/createSlots.test.js +67 -0
  368. package/lib-esm/drafts.d.ts +7 -0
  369. package/lib-esm/drafts.js +8 -0
  370. package/lib-esm/stories/ActionList2.stories.js +796 -0
  371. package/lib-esm/stories/TextInput.stories.js +117 -0
  372. package/lib-esm/sx.d.ts +2 -0
  373. package/lib-esm/sx.js +3 -1
  374. package/lib-esm/theme-preval.js +81 -2
  375. package/lib-esm/utils/create-slots.d.ts +17 -0
  376. package/lib-esm/utils/create-slots.js +84 -0
  377. package/lib-esm/utils/testing.d.ts +14 -1
  378. package/lib-esm/utils/use-force-update.d.ts +1 -0
  379. package/lib-esm/utils/use-force-update.js +6 -0
  380. package/package-lock.json +153 -14
  381. package/package.json +7 -4
  382. package/script/build +1 -1
  383. package/src/ActionList2/Description.tsx +49 -0
  384. package/src/ActionList2/Divider.tsx +24 -0
  385. package/src/ActionList2/Group.tsx +34 -0
  386. package/src/ActionList2/Header.tsx +58 -0
  387. package/src/ActionList2/Item.tsx +245 -0
  388. package/src/ActionList2/LinkItem.tsx +49 -0
  389. package/src/ActionList2/List.tsx +55 -0
  390. package/src/ActionList2/Selection.tsx +40 -0
  391. package/src/ActionList2/Visuals.tsx +76 -0
  392. package/src/ActionList2/index.ts +39 -0
  393. package/src/StateLabel.tsx +14 -2
  394. package/src/_TextInputWrapper.tsx +7 -0
  395. package/src/__tests__/ActionList2.test.tsx +47 -0
  396. package/src/__tests__/__snapshots__/ActionList2.test.tsx.snap +14 -0
  397. package/src/__tests__/__snapshots__/Autocomplete.test.tsx.snap +1 -0
  398. package/src/__tests__/__snapshots__/CircleBadge.test.tsx.snap +1 -0
  399. package/src/__tests__/__snapshots__/CircleOcticon.test.tsx.snap +1 -0
  400. package/src/__tests__/__snapshots__/Dialog.test.tsx.snap +1 -0
  401. package/src/__tests__/__snapshots__/DropdownMenu.test.tsx.snap +1 -0
  402. package/src/__tests__/__snapshots__/SelectMenu.test.tsx.snap +4 -0
  403. package/src/__tests__/__snapshots__/SelectPanel.test.tsx.snap +1 -0
  404. package/src/__tests__/__snapshots__/StateLabel.test.tsx.snap +13 -6
  405. package/src/__tests__/__snapshots__/StyledOcticon.test.tsx.snap +1 -0
  406. package/src/__tests__/__snapshots__/TextInputWithTokens.test.tsx.snap +66 -0
  407. package/src/__tests__/__snapshots__/Token.test.tsx.snap +17 -0
  408. package/src/__tests__/utils/__snapshots__/createSlots.test.tsx.snap +55 -0
  409. package/src/__tests__/utils/createSlots.test.tsx +74 -0
  410. package/src/drafts.ts +9 -0
  411. package/src/stories/ActionList2.stories.tsx +1291 -0
  412. package/src/stories/TextInput.stories.tsx +113 -0
  413. package/src/sx.ts +3 -0
  414. package/src/theme-preval.js +1 -0
  415. package/src/utils/create-slots.tsx +96 -0
  416. package/src/utils/use-force-update.ts +7 -0
  417. package/stats.html +1 -1
  418. package/tsconfig.base.json +20 -0
  419. package/tsconfig.build.json +2 -2
  420. package/tsconfig.json +4 -17
@@ -0,0 +1,184 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const react_1 = __importDefault(require("react"));
7
+ const react_2 = require("@testing-library/react");
8
+ const focusTrap_1 = require("../../behaviors/focusTrap");
9
+ // Since we use strict `isTabbable` checks within focus trap, we need to mock these
10
+ // properties that Jest does not populate.
11
+ beforeAll(() => {
12
+ try {
13
+ Object.defineProperties(HTMLElement.prototype, {
14
+ offsetHeight: {
15
+ get: () => 42
16
+ },
17
+ offsetWidth: {
18
+ get: () => 42
19
+ },
20
+ getClientRects: {
21
+ get: () => () => [42]
22
+ }
23
+ });
24
+ }
25
+ catch {
26
+ // ignore
27
+ }
28
+ });
29
+ it('Should initially focus the container when activated', () => {
30
+ const { container } = react_2.render(<div>
31
+ <button tabIndex={0}>Bad Apple</button>
32
+ <div id="trapContainer">
33
+ <button tabIndex={0}>Apple</button>
34
+ <button tabIndex={0}>Banana</button>
35
+ <button tabIndex={0}>Cantaloupe</button>
36
+ </div>
37
+ </div>);
38
+ const trapContainer = container.querySelector('#trapContainer');
39
+ const controller = focusTrap_1.focusTrap(trapContainer);
40
+ expect(document.activeElement).toEqual(trapContainer);
41
+ controller.abort();
42
+ });
43
+ it('Should initially focus the initialFocus element when specified', () => {
44
+ const { container } = react_2.render(<div id="trapContainer">
45
+ <button tabIndex={0}>Apple</button>
46
+ <button tabIndex={0}>Banana</button>
47
+ <button tabIndex={0}>Cantaloupe</button>
48
+ </div>);
49
+ const trapContainer = container.querySelector('#trapContainer');
50
+ const secondButton = trapContainer.querySelectorAll('button')[1];
51
+ const controller = focusTrap_1.focusTrap(trapContainer, secondButton);
52
+ expect(document.activeElement).toEqual(secondButton);
53
+ controller.abort();
54
+ });
55
+ it('Should prevent focus from exiting the trap, returns focus to previously-focused element', async () => {
56
+ const { container } = react_2.render(<div>
57
+ <div id="trapContainer">
58
+ <button tabIndex={0}>Apple</button>
59
+ <button tabIndex={0}>Banana</button>
60
+ <button tabIndex={0}>Cantaloupe</button>
61
+ </div>
62
+ <button id="durian" tabIndex={0}>
63
+ Durian
64
+ </button>
65
+ </div>);
66
+ const trapContainer = container.querySelector('#trapContainer');
67
+ const secondButton = trapContainer.querySelectorAll('button')[1];
68
+ const durianButton = container.querySelector('#durian');
69
+ const controller = focusTrap_1.focusTrap(trapContainer);
70
+ focus(durianButton);
71
+ expect(document.activeElement).toEqual(trapContainer);
72
+ focus(secondButton);
73
+ expect(document.activeElement).toEqual(secondButton);
74
+ focus(durianButton);
75
+ expect(document.activeElement).toEqual(secondButton);
76
+ controller.abort();
77
+ });
78
+ it('Should prevent focus from exiting the trap if there are no focusable children', async () => {
79
+ const { container } = react_2.render(<div>
80
+ <div id="trapContainer"></div>
81
+ <button id="durian" tabIndex={0}>
82
+ Durian
83
+ </button>
84
+ </div>);
85
+ const trapContainer = container.querySelector('#trapContainer');
86
+ const durianButton = container.querySelector('#durian');
87
+ const controller = focusTrap_1.focusTrap(trapContainer);
88
+ focus(durianButton);
89
+ expect(document.activeElement === durianButton).toEqual(false);
90
+ controller.abort();
91
+ });
92
+ it('Should cycle focus from last element to first element and vice-versa', async () => {
93
+ const { container } = react_2.render(<div>
94
+ <div id="trapContainer">
95
+ <button tabIndex={0}>Apple</button>
96
+ <button tabIndex={0}>Banana</button>
97
+ <button tabIndex={0}>Cantaloupe</button>
98
+ </div>
99
+ <button id="durian" tabIndex={0}>
100
+ Durian
101
+ </button>
102
+ </div>);
103
+ const trapContainer = container.querySelector('#trapContainer');
104
+ const firstButton = trapContainer.querySelector('button');
105
+ const lastButton = trapContainer.querySelectorAll('button')[2];
106
+ const controller = focusTrap_1.focusTrap(trapContainer);
107
+ lastButton.focus();
108
+ react_2.fireEvent(lastButton, new KeyboardEvent('keydown', { bubbles: true, key: 'Tab' }));
109
+ expect(document.activeElement).toEqual(firstButton);
110
+ react_2.fireEvent(firstButton, new KeyboardEvent('keydown', { bubbles: true, key: 'Tab', shiftKey: true }));
111
+ expect(document.activeElement).toEqual(lastButton);
112
+ controller.abort();
113
+ });
114
+ it('Should should release the trap when the signal is aborted', async () => {
115
+ const { container } = react_2.render(<div>
116
+ <div id="trapContainer">
117
+ <button tabIndex={0}>Apple</button>
118
+ <button tabIndex={0}>Banana</button>
119
+ <button tabIndex={0}>Cantaloupe</button>
120
+ </div>
121
+ <button id="durian" tabIndex={0}>
122
+ Durian
123
+ </button>
124
+ </div>);
125
+ const trapContainer = container.querySelector('#trapContainer');
126
+ const durianButton = container.querySelector('#durian');
127
+ const controller = focusTrap_1.focusTrap(trapContainer);
128
+ focus(durianButton);
129
+ expect(document.activeElement).toEqual(trapContainer);
130
+ controller.abort();
131
+ focus(durianButton);
132
+ expect(document.activeElement).toEqual(durianButton);
133
+ });
134
+ it('Should should release the trap when the container is removed from the DOM', async () => {
135
+ const { container } = react_2.render(<div>
136
+ <div id="trapContainer">
137
+ <button tabIndex={0}>Apple</button>
138
+ </div>
139
+ <button id="durian" tabIndex={0}>
140
+ Durian
141
+ </button>
142
+ </div>);
143
+ const trapContainer = container.querySelector('#trapContainer');
144
+ const durianButton = container.querySelector('#durian');
145
+ const firstButton = trapContainer.querySelector('button');
146
+ focusTrap_1.focusTrap(trapContainer);
147
+ focus(durianButton);
148
+ expect(document.activeElement).toEqual(trapContainer);
149
+ // empty trap and remove it from the DOM
150
+ trapContainer.removeChild(firstButton);
151
+ trapContainer.parentElement?.removeChild(trapContainer);
152
+ focus(durianButton);
153
+ expect(document.activeElement).toEqual(durianButton);
154
+ });
155
+ it('Should handle dynamic content', async () => {
156
+ const { container } = react_2.render(<div>
157
+ <div id="trapContainer">
158
+ <button tabIndex={0}>Apple</button>
159
+ <button tabIndex={0}>Banana</button>
160
+ <button tabIndex={0}>Cantaloupe</button>
161
+ </div>
162
+ <button id="durian" tabIndex={0}>
163
+ Durian
164
+ </button>
165
+ </div>);
166
+ const trapContainer = container.querySelector('#trapContainer');
167
+ const [firstButton, secondButton, thirdButton] = trapContainer.querySelectorAll('button');
168
+ const controller = focusTrap_1.focusTrap(trapContainer);
169
+ secondButton.focus();
170
+ trapContainer.removeChild(thirdButton);
171
+ react_2.fireEvent(secondButton, new KeyboardEvent('keydown', { bubbles: true, key: 'Tab' }));
172
+ expect(document.activeElement).toEqual(firstButton);
173
+ react_2.fireEvent(firstButton, new KeyboardEvent('keydown', { bubbles: true, key: 'Tab', shiftKey: true }));
174
+ expect(document.activeElement).toEqual(secondButton);
175
+ controller.abort();
176
+ });
177
+ /**
178
+ * Helper to handle firing the focusin event, which jest/JSDOM does not do for us.
179
+ * @param element
180
+ */
181
+ function focus(element) {
182
+ element.focus();
183
+ react_2.fireEvent(element, new FocusEvent('focusin', { bubbles: true }));
184
+ }
@@ -0,0 +1,406 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const react_1 = __importDefault(require("react"));
7
+ const react_2 = require("@testing-library/react");
8
+ const user_event_1 = __importDefault(require("@testing-library/user-event"));
9
+ const focusZone_1 = require("../../behaviors/focusZone");
10
+ async function nextTick() {
11
+ return new Promise(resolve => setTimeout(resolve, 0));
12
+ }
13
+ const moveDown = () => user_event_1.default.type(document.activeElement, '{arrowdown}');
14
+ const moveUp = () => user_event_1.default.type(document.activeElement, '{arrowup}');
15
+ // Since we use strict `isTabbable` checks within focus trap, we need to mock these
16
+ // properties that Jest does not populate.
17
+ beforeAll(() => {
18
+ try {
19
+ Object.defineProperties(HTMLElement.prototype, {
20
+ offsetHeight: {
21
+ get: () => 42
22
+ },
23
+ offsetWidth: {
24
+ get: () => 42
25
+ },
26
+ getClientRects: {
27
+ get: () => () => [42]
28
+ }
29
+ });
30
+ }
31
+ catch {
32
+ // ignore
33
+ }
34
+ });
35
+ it('Should allow arrow keys to move focus', () => {
36
+ const { container } = react_2.render(<div>
37
+ <button tabIndex={0}>Bad Apple</button>
38
+ <div id="focusZone">
39
+ <button tabIndex={0}>Apple</button>
40
+ <button tabIndex={0}>Banana</button>
41
+ <button tabIndex={0}>Cantaloupe</button>
42
+ </div>
43
+ </div>);
44
+ const focusZoneContainer = container.querySelector('#focusZone');
45
+ const [firstButton, secondButton] = focusZoneContainer.querySelectorAll('button');
46
+ const controller = focusZone_1.focusZone(focusZoneContainer);
47
+ firstButton.focus();
48
+ expect(document.activeElement).toEqual(firstButton);
49
+ user_event_1.default.type(firstButton, '{arrowdown}');
50
+ expect(document.activeElement).toEqual(secondButton);
51
+ controller.abort();
52
+ });
53
+ it('Should have one tab-stop inside the focus zone when enabled', () => {
54
+ const { container } = react_2.render(<div>
55
+ <button tabIndex={0}>Bad Apple</button>
56
+ <div id="focusZone">
57
+ <button tabIndex={0}>Apple</button>
58
+ <button tabIndex={0}>Banana</button>
59
+ <button tabIndex={0}>Cantaloupe</button>
60
+ </div>
61
+ <button tabIndex={0}>Next Apple</button>
62
+ </div>);
63
+ const focusZoneContainer = container.querySelector('#focusZone');
64
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
65
+ const [one, two, three, four, five] = container.querySelectorAll('button');
66
+ const controller = focusZone_1.focusZone(focusZoneContainer);
67
+ one.focus();
68
+ user_event_1.default.tab();
69
+ user_event_1.default.tab();
70
+ expect(document.activeElement).toEqual(five);
71
+ controller.abort();
72
+ one.focus();
73
+ user_event_1.default.tab();
74
+ user_event_1.default.tab();
75
+ expect(document.activeElement).toEqual(three);
76
+ controller.abort();
77
+ });
78
+ it('Should prevent moving focus outside the zone', () => {
79
+ const { container } = react_2.render(<div>
80
+ <button tabIndex={0}>Bad Apple</button>
81
+ <div id="focusZone">
82
+ <button tabIndex={0}>Apple</button>
83
+ <button tabIndex={0}>Banana</button>
84
+ <button tabIndex={0}>Cantaloupe</button>
85
+ </div>
86
+ </div>);
87
+ const focusZoneContainer = container.querySelector('#focusZone');
88
+ const [firstButton, secondButton, thirdButton] = focusZoneContainer.querySelectorAll('button');
89
+ const controller = focusZone_1.focusZone(focusZoneContainer);
90
+ firstButton.focus();
91
+ expect(document.activeElement).toEqual(firstButton);
92
+ user_event_1.default.type(firstButton, '{arrowup}');
93
+ expect(document.activeElement).toEqual(firstButton);
94
+ user_event_1.default.type(firstButton, '{arrowdown}');
95
+ expect(document.activeElement).toEqual(secondButton);
96
+ user_event_1.default.type(secondButton, '{arrowdown}');
97
+ expect(document.activeElement).toEqual(thirdButton);
98
+ user_event_1.default.type(thirdButton, '{arrowdown}');
99
+ expect(document.activeElement).toEqual(thirdButton);
100
+ controller.abort();
101
+ });
102
+ it('Should do focus wrapping correctly', () => {
103
+ const { container } = react_2.render(<div>
104
+ <button tabIndex={0}>Bad Apple</button>
105
+ <div id="focusZone">
106
+ <button tabIndex={0}>Apple</button>
107
+ <button tabIndex={0}>Banana</button>
108
+ <button tabIndex={0}>Cantaloupe</button>
109
+ </div>
110
+ </div>);
111
+ const focusZoneContainer = container.querySelector('#focusZone');
112
+ const [firstButton, secondButton, thirdButton] = focusZoneContainer.querySelectorAll('button');
113
+ const controller = focusZone_1.focusZone(focusZoneContainer, { focusOutBehavior: 'wrap' });
114
+ firstButton.focus();
115
+ expect(document.activeElement).toEqual(firstButton);
116
+ user_event_1.default.type(firstButton, '{arrowup}');
117
+ expect(document.activeElement).toEqual(thirdButton);
118
+ user_event_1.default.type(thirdButton, '{arrowup}');
119
+ expect(document.activeElement).toEqual(secondButton);
120
+ user_event_1.default.type(secondButton, '{arrowdown}');
121
+ expect(document.activeElement).toEqual(thirdButton);
122
+ user_event_1.default.type(thirdButton, '{arrowdown}');
123
+ expect(document.activeElement).toEqual(firstButton);
124
+ controller.abort();
125
+ });
126
+ it('Should call custom getNextFocusable callback', () => {
127
+ const { container } = react_2.render(<div>
128
+ <button tabIndex={0}>Bad Apple</button>
129
+ <div id="focusZone">
130
+ <button tabIndex={0}>Apple</button>
131
+ <button tabIndex={0}>Banana</button>
132
+ <button tabIndex={0}>Cantaloupe</button>
133
+ </div>
134
+ </div>);
135
+ const focusZoneContainer = container.querySelector('#focusZone');
136
+ const [firstButton, secondButton] = focusZoneContainer.querySelectorAll('button');
137
+ const getNextFocusableCallback = jest.fn();
138
+ const controller = focusZone_1.focusZone(focusZoneContainer, { getNextFocusable: getNextFocusableCallback });
139
+ firstButton.focus();
140
+ expect(document.activeElement).toEqual(firstButton);
141
+ user_event_1.default.type(firstButton, '{arrowdown}');
142
+ expect(getNextFocusableCallback).toHaveBeenCalledWith('next', firstButton, expect.anything());
143
+ user_event_1.default.type(secondButton, '{home}');
144
+ expect(getNextFocusableCallback).toHaveBeenCalledWith('start', secondButton, expect.anything());
145
+ controller.abort();
146
+ });
147
+ it('Should focus-in to the most recently-focused element', () => {
148
+ const { container } = react_2.render(<div>
149
+ <button tabIndex={0} id="outside">
150
+ Bad Apple
151
+ </button>
152
+ <div id="focusZone">
153
+ <button tabIndex={0}>Apple</button>
154
+ <button tabIndex={0}>Banana</button>
155
+ <button tabIndex={0}>Cantaloupe</button>
156
+ </div>
157
+ </div>);
158
+ const focusZoneContainer = container.querySelector('#focusZone');
159
+ const outsideButton = container.querySelector('#outside');
160
+ const [firstButton, secondButton] = focusZoneContainer.querySelectorAll('button');
161
+ const controller = focusZone_1.focusZone(focusZoneContainer);
162
+ firstButton.focus();
163
+ expect(document.activeElement).toEqual(firstButton);
164
+ user_event_1.default.type(firstButton, '{arrowdown}');
165
+ expect(document.activeElement).toEqual(secondButton);
166
+ outsideButton.focus();
167
+ user_event_1.default.tab();
168
+ expect(document.activeElement).toEqual(secondButton);
169
+ controller.abort();
170
+ });
171
+ it('Should focus-in to the first element when focusInStrategy is "first"', () => {
172
+ const { container } = react_2.render(<div>
173
+ <button tabIndex={0} id="outside">
174
+ Bad Apple
175
+ </button>
176
+ <div id="focusZone">
177
+ <button tabIndex={0}>Apple</button>
178
+ <button tabIndex={0}>Banana</button>
179
+ <button tabIndex={0}>Cantaloupe</button>
180
+ </div>
181
+ </div>);
182
+ const focusZoneContainer = container.querySelector('#focusZone');
183
+ const outsideButton = container.querySelector('#outside');
184
+ const [firstButton, secondButton] = focusZoneContainer.querySelectorAll('button');
185
+ const controller = focusZone_1.focusZone(focusZoneContainer, { focusInStrategy: 'first' });
186
+ firstButton.focus();
187
+ expect(document.activeElement).toEqual(firstButton);
188
+ user_event_1.default.type(firstButton, '{arrowdown}');
189
+ expect(document.activeElement).toEqual(secondButton);
190
+ outsideButton.focus();
191
+ user_event_1.default.tab();
192
+ expect(document.activeElement).toEqual(firstButton);
193
+ controller.abort();
194
+ });
195
+ it('Should focus-in to the closest element when focusInStrategy is "closest"', () => {
196
+ const { container } = react_2.render(<div>
197
+ <button tabIndex={0} id="outsideBefore">
198
+ Bad Apple
199
+ </button>
200
+ <div id="focusZone">
201
+ <button id="apple" tabIndex={0}>
202
+ Apple
203
+ </button>
204
+ <button id="banana" tabIndex={0}>
205
+ Banana
206
+ </button>
207
+ <button id="cantaloupe" tabIndex={0}>
208
+ Cantaloupe
209
+ </button>
210
+ </div>
211
+ <button tabIndex={0} id="outsideAfter">
212
+ Good Apple
213
+ </button>
214
+ </div>);
215
+ const focusZoneContainer = container.querySelector('#focusZone');
216
+ const outsideBefore = container.querySelector('#outsideBefore');
217
+ const outsideAfter = container.querySelector('#outsideAfter');
218
+ const [firstButton, secondButton, thirdButton] = focusZoneContainer.querySelectorAll('button');
219
+ const controller = focusZone_1.focusZone(focusZoneContainer, { focusInStrategy: 'closest' });
220
+ firstButton.focus();
221
+ expect(document.activeElement).toEqual(firstButton);
222
+ user_event_1.default.type(firstButton, '{arrowdown}');
223
+ expect(document.activeElement).toEqual(secondButton);
224
+ outsideBefore.focus();
225
+ user_event_1.default.tab();
226
+ expect(document.activeElement).toEqual(firstButton);
227
+ outsideAfter.focus();
228
+ user_event_1.default.tab({ shift: true });
229
+ expect(document.activeElement).toEqual(thirdButton);
230
+ controller.abort();
231
+ });
232
+ it('Should call the custom focusInStrategy callback', () => {
233
+ const { container } = react_2.render(<div>
234
+ <button tabIndex={0} id="outside">
235
+ Bad Apple
236
+ </button>
237
+ <div id="focusZone">
238
+ <button tabIndex={0}>Apple</button>
239
+ <button tabIndex={0}>Banana</button>
240
+ <button tabIndex={0}>Cantaloupe</button>
241
+ </div>
242
+ </div>);
243
+ const focusZoneContainer = container.querySelector('#focusZone');
244
+ const outsideButton = container.querySelector('#outside');
245
+ const [, secondButton] = focusZoneContainer.querySelectorAll('button');
246
+ const focusInCallback = jest.fn().mockReturnValue(secondButton);
247
+ const controller = focusZone_1.focusZone(focusZoneContainer, { focusInStrategy: focusInCallback });
248
+ outsideButton.focus();
249
+ user_event_1.default.tab();
250
+ expect(focusInCallback).toHaveBeenCalledWith(outsideButton);
251
+ expect(document.activeElement).toEqual(secondButton);
252
+ controller.abort();
253
+ });
254
+ it('Should respect inputs by not moving focus if key would have some other effect', () => {
255
+ const { container } = react_2.render(<div>
256
+ <button tabIndex={0} id="outside">
257
+ Bad Apple
258
+ </button>
259
+ <div id="focusZone">
260
+ <button tabIndex={0}>Apple</button>
261
+ <input type="text" defaultValue="Banana" tabIndex={0}/>
262
+ <button tabIndex={0}>Cantaloupe</button>
263
+ </div>
264
+ </div>);
265
+ const focusZoneContainer = container.querySelector('#focusZone');
266
+ const [firstButton, secondButton] = focusZoneContainer.querySelectorAll('button');
267
+ const input = focusZoneContainer.querySelector('input');
268
+ const controller = focusZone_1.focusZone(focusZoneContainer, { bindKeys: focusZone_1.FocusKeys.ArrowHorizontal | focusZone_1.FocusKeys.HomeAndEnd });
269
+ firstButton.focus();
270
+ user_event_1.default.type(firstButton, '{arrowright}');
271
+ expect(document.activeElement).toEqual(input);
272
+ user_event_1.default.type(input, '{arrowleft}');
273
+ expect(document.activeElement).toEqual(input);
274
+ user_event_1.default.type(input, '{arrowright}');
275
+ expect(document.activeElement).toEqual(input);
276
+ user_event_1.default.type(input, '{arrowright}');
277
+ expect(document.activeElement).toEqual(secondButton);
278
+ controller.abort();
279
+ });
280
+ it('Should focus-in to the first element if the last-focused element is removed', async () => {
281
+ const { container } = react_2.render(<div>
282
+ <button tabIndex={0} id="outside">
283
+ Bad Apple
284
+ </button>
285
+ <div id="focusZone">
286
+ <button tabIndex={0}>Apple</button>
287
+ <button tabIndex={0}>Banana</button>
288
+ <button tabIndex={0}>Cantaloupe</button>
289
+ </div>
290
+ </div>);
291
+ const focusZoneContainer = container.querySelector('#focusZone');
292
+ const [firstButton, secondButton, thirdButton] = focusZoneContainer.querySelectorAll('button');
293
+ const outsideButton = container.querySelector('#outside');
294
+ const controller = focusZone_1.focusZone(focusZoneContainer);
295
+ firstButton.focus();
296
+ user_event_1.default.type(firstButton, '{arrowdown}');
297
+ expect(document.activeElement).toEqual(secondButton);
298
+ outsideButton.focus();
299
+ focusZoneContainer.removeChild(secondButton);
300
+ // The mutation observer fires asynchronously
301
+ await nextTick();
302
+ user_event_1.default.tab();
303
+ expect(document.activeElement).toEqual(firstButton);
304
+ user_event_1.default.type(firstButton, '{arrowdown}');
305
+ expect(document.activeElement).toEqual(thirdButton);
306
+ controller.abort();
307
+ });
308
+ it('Should call onActiveDescendantChanged properly', () => {
309
+ const { container } = react_2.render(<div>
310
+ <button tabIndex={0} id="outside">
311
+ Bad Apple
312
+ </button>
313
+ <input id="control" defaultValue="control input" tabIndex={0}/>
314
+ <div id="focusZone">
315
+ <button tabIndex={0}>Apple</button>
316
+ <button tabIndex={0}>Banana</button>
317
+ <button tabIndex={0}>Cantaloupe</button>
318
+ </div>
319
+ </div>);
320
+ const focusZoneContainer = container.querySelector('#focusZone');
321
+ const [firstButton, secondButton] = focusZoneContainer.querySelectorAll('button');
322
+ const control = container.querySelector('#control');
323
+ const activeDescendantChangedCallback = jest.fn();
324
+ const controller = focusZone_1.focusZone(focusZoneContainer, {
325
+ activeDescendantControl: control,
326
+ onActiveDescendantChanged: activeDescendantChangedCallback
327
+ });
328
+ control.focus();
329
+ expect(activeDescendantChangedCallback).toHaveBeenLastCalledWith(firstButton, undefined, false);
330
+ user_event_1.default.type(control, '{arrowdown}');
331
+ expect(activeDescendantChangedCallback).toHaveBeenLastCalledWith(secondButton, firstButton, true);
332
+ user_event_1.default.type(control, '{arrowup}');
333
+ expect(activeDescendantChangedCallback).toHaveBeenLastCalledWith(firstButton, secondButton, true);
334
+ react_2.fireEvent.mouseMove(secondButton);
335
+ expect(activeDescendantChangedCallback).toHaveBeenLastCalledWith(secondButton, firstButton, false);
336
+ user_event_1.default.type(control, '{arrowup}');
337
+ expect(activeDescendantChangedCallback).toHaveBeenLastCalledWith(firstButton, secondButton, true);
338
+ user_event_1.default.type(control, '{arrowUp}');
339
+ expect(activeDescendantChangedCallback).toHaveBeenLastCalledWith(firstButton, firstButton, true);
340
+ activeDescendantChangedCallback.mockReset();
341
+ react_2.fireEvent.mouseMove(firstButton);
342
+ expect(activeDescendantChangedCallback).not.toBeCalled();
343
+ controller.abort();
344
+ });
345
+ it('Should set aria-activedescendant correctly', () => {
346
+ const { container } = react_2.render(<div>
347
+ <button tabIndex={0} id="outside">
348
+ Bad Apple
349
+ </button>
350
+ <input id="control" defaultValue="control input" tabIndex={0}/>
351
+ <div id="focusZone">
352
+ <button tabIndex={0}>Apple</button>
353
+ <button tabIndex={0}>Banana</button>
354
+ <button tabIndex={0}>Cantaloupe</button>
355
+ </div>
356
+ </div>);
357
+ const focusZoneContainer = container.querySelector('#focusZone');
358
+ const [firstButton, secondButton] = focusZoneContainer.querySelectorAll('button');
359
+ const outsideButton = container.querySelector('#outside');
360
+ const control = container.querySelector('#control');
361
+ const controller = focusZone_1.focusZone(focusZoneContainer, { activeDescendantControl: control });
362
+ control.focus();
363
+ expect(control.getAttribute('aria-activedescendant')).toEqual(firstButton.id);
364
+ user_event_1.default.type(control, '{arrowdown}');
365
+ expect(control.getAttribute('aria-activedescendant')).toEqual(secondButton.id);
366
+ user_event_1.default.type(control, '{arrowup}');
367
+ expect(control.getAttribute('aria-activedescendant')).toEqual(firstButton.id);
368
+ expect(document.activeElement).toEqual(control);
369
+ user_event_1.default.type(control, '{arrowup}');
370
+ expect(control.getAttribute('aria-activedescendant')).toEqual(firstButton.id);
371
+ outsideButton.focus();
372
+ expect(control.hasAttribute('aria-activedescendant')).toBeFalsy();
373
+ controller.abort();
374
+ });
375
+ it('Should handle elements being reordered', async () => {
376
+ const { container } = react_2.render(<div>
377
+ <div id="focusZone">
378
+ <button tabIndex={0}>Apple</button>
379
+ <button tabIndex={0}>Banana</button>
380
+ <button tabIndex={0}>Cantaloupe</button>
381
+ <button tabIndex={0}>Durian</button>
382
+ </div>
383
+ </div>);
384
+ const focusZoneContainer = container.querySelector('#focusZone');
385
+ const [firstButton, secondButton, thirdButton, fourthButton] = focusZoneContainer.querySelectorAll('button');
386
+ const controller = focusZone_1.focusZone(focusZoneContainer);
387
+ firstButton.focus();
388
+ expect(document.activeElement).toEqual(firstButton);
389
+ moveDown();
390
+ expect(document.activeElement).toEqual(secondButton);
391
+ moveUp();
392
+ expect(document.activeElement).toEqual(firstButton);
393
+ // move secondButton and thirdButton to the end of the zone, in reverse order
394
+ focusZoneContainer.appendChild(thirdButton);
395
+ focusZoneContainer.appendChild(secondButton);
396
+ // The mutation observer fires asynchronously
397
+ await nextTick();
398
+ expect(document.activeElement).toEqual(firstButton);
399
+ moveDown();
400
+ expect(document.activeElement).toEqual(fourthButton);
401
+ moveDown();
402
+ expect(document.activeElement).toEqual(thirdButton);
403
+ moveDown();
404
+ expect(document.activeElement).toEqual(secondButton);
405
+ controller.abort();
406
+ });
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const react_1 = __importDefault(require("react"));
7
+ const iterateFocusableElements_1 = require("../../utils/iterateFocusableElements");
8
+ const react_2 = require("@testing-library/react");
9
+ it('Should iterate through focusable elements only', () => {
10
+ const { container } = react_2.render(<div>
11
+ <div>
12
+ <textarea></textarea>
13
+ </div>
14
+ <input />
15
+ <button>Hello</button>
16
+ <p>Not focusable</p>
17
+ {/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */}
18
+ <div tabIndex={0}>
19
+ <a tabIndex={-1} href="#boo">
20
+ Not focusable
21
+ </a>
22
+ <a href="#yah">Focusable</a>
23
+ </div>
24
+ </div>);
25
+ const focusable = Array.from(iterateFocusableElements_1.iterateFocusableElements(container, { onlyTabbable: true }));
26
+ expect(focusable.length).toEqual(5);
27
+ expect(focusable[0].tagName.toLowerCase()).toEqual('textarea');
28
+ expect(focusable[1].tagName.toLowerCase()).toEqual('input');
29
+ expect(focusable[2].tagName.toLowerCase()).toEqual('button');
30
+ expect(focusable[3].tagName.toLowerCase()).toEqual('div');
31
+ expect(focusable[4].tagName.toLowerCase()).toEqual('a');
32
+ expect(focusable[4].getAttribute('href')).toEqual('#yah');
33
+ });
34
+ it('Should iterate through focusable elements in reverse', () => {
35
+ const { container } = react_2.render(<div>
36
+ <div>
37
+ <textarea></textarea>
38
+ </div>
39
+ <input />
40
+ <button>Hello</button>
41
+ <p>Not focusable</p>
42
+ {/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */}
43
+ <div tabIndex={0}>
44
+ <a tabIndex={-1} href="#boo">
45
+ Not focusable
46
+ </a>
47
+ <a href="#yah">Focusable</a>
48
+ </div>
49
+ </div>);
50
+ const focusable = Array.from(iterateFocusableElements_1.iterateFocusableElements(container, { reverse: true, onlyTabbable: true }));
51
+ expect(focusable.length).toEqual(5);
52
+ expect(focusable[0].tagName.toLowerCase()).toEqual('a');
53
+ expect(focusable[0].getAttribute('href')).toEqual('#yah');
54
+ expect(focusable[1].tagName.toLowerCase()).toEqual('div');
55
+ expect(focusable[2].tagName.toLowerCase()).toEqual('button');
56
+ expect(focusable[3].tagName.toLowerCase()).toEqual('input');
57
+ expect(focusable[4].tagName.toLowerCase()).toEqual('textarea');
58
+ });