@primitiv-ui/react 0.1.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 (585) hide show
  1. package/README.md +79 -0
  2. package/package.json +59 -0
  3. package/src/AccessibleIcon/AccessibleIcon.tsx +40 -0
  4. package/src/AccessibleIcon/README.md +42 -0
  5. package/src/AccessibleIcon/__tests__/AccessibleIcon.test.tsx +47 -0
  6. package/src/AccessibleIcon/index.ts +2 -0
  7. package/src/AccessibleIcon/types.ts +8 -0
  8. package/src/Accordion/Accordion.tsx +412 -0
  9. package/src/Accordion/AccordionContext.ts +12 -0
  10. package/src/Accordion/README.md +202 -0
  11. package/src/Accordion/__tests__/Accordion.asChild.test.tsx +237 -0
  12. package/src/Accordion/__tests__/Accordion.basic-rendering.test.tsx +333 -0
  13. package/src/Accordion/__tests__/Accordion.controlled-state.test.tsx +175 -0
  14. package/src/Accordion/__tests__/Accordion.data-attributes.test.tsx +272 -0
  15. package/src/Accordion/__tests__/Accordion.disabled-items.test.tsx +311 -0
  16. package/src/Accordion/__tests__/Accordion.error-handling.test.tsx +119 -0
  17. package/src/Accordion/__tests__/Accordion.forceMount.test.tsx +119 -0
  18. package/src/Accordion/__tests__/Accordion.keyboard-interaction.test.tsx +736 -0
  19. package/src/Accordion/__tests__/Accordion.mouse-interaction.test.tsx +212 -0
  20. package/src/Accordion/__tests__/Accordion.multiple-mode.test.tsx +90 -0
  21. package/src/Accordion/__tests__/Accordion.reading-direction.test.tsx +139 -0
  22. package/src/Accordion/__tests__/Accordion.uncontrolled-state.test.tsx +154 -0
  23. package/src/Accordion/hooks/index.ts +6 -0
  24. package/src/Accordion/hooks/useAccordionContext.ts +1 -0
  25. package/src/Accordion/hooks/useAccordionHeaderContext.ts +10 -0
  26. package/src/Accordion/hooks/useAccordionItem.ts +22 -0
  27. package/src/Accordion/hooks/useAccordionItemContext.ts +1 -0
  28. package/src/Accordion/hooks/useAccordionRoot.ts +151 -0
  29. package/src/Accordion/hooks/useAccordionTrigger.ts +90 -0
  30. package/src/Accordion/index.ts +1 -0
  31. package/src/Accordion/types.ts +81 -0
  32. package/src/Alert/Alert.tsx +43 -0
  33. package/src/Alert/README.md +54 -0
  34. package/src/Alert/__tests__/Alert.test.tsx +28 -0
  35. package/src/Alert/index.ts +2 -0
  36. package/src/Alert/types.ts +5 -0
  37. package/src/Avatar/Avatar.tsx +149 -0
  38. package/src/Avatar/AvatarContext.ts +20 -0
  39. package/src/Avatar/README.md +116 -0
  40. package/src/Avatar/__tests__/Avatar.asChild.test.tsx +53 -0
  41. package/src/Avatar/__tests__/Avatar.basic-rendering.test.tsx +14 -0
  42. package/src/Avatar/__tests__/Avatar.error-handling.test.tsx +30 -0
  43. package/src/Avatar/__tests__/Avatar.fallback.test.tsx +75 -0
  44. package/src/Avatar/__tests__/Avatar.image-loading.test.tsx +81 -0
  45. package/src/Avatar/hooks/index.ts +2 -0
  46. package/src/Avatar/hooks/useAvatarContext.ts +1 -0
  47. package/src/Avatar/hooks/useAvatarImage.ts +40 -0
  48. package/src/Avatar/index.ts +3 -0
  49. package/src/Avatar/types.ts +44 -0
  50. package/src/Breadcrumb/Breadcrumb.tsx +234 -0
  51. package/src/Breadcrumb/README.md +111 -0
  52. package/src/Breadcrumb/__tests__/Breadcrumb.asChild.test.tsx +33 -0
  53. package/src/Breadcrumb/__tests__/Breadcrumb.basic-rendering.test.tsx +132 -0
  54. package/src/Breadcrumb/index.ts +2 -0
  55. package/src/Breadcrumb/types.ts +22 -0
  56. package/src/Button/Button.tsx +95 -0
  57. package/src/Button/README.md +112 -0
  58. package/src/Button/__tests__/Button.asChild.test.tsx +91 -0
  59. package/src/Button/__tests__/Button.basic-rendering.test.tsx +126 -0
  60. package/src/Button/__tests__/Button.contract.test.tsx +72 -0
  61. package/src/Button/__tests__/Button.disabled.test.tsx +52 -0
  62. package/src/Button/__tests__/Button.icon-usage.test.tsx +57 -0
  63. package/src/Button/__tests__/Button.keyboard-interaction.test.tsx +70 -0
  64. package/src/Button/index.ts +2 -0
  65. package/src/Button/types.ts +8 -0
  66. package/src/Carousel/Carousel.tsx +708 -0
  67. package/src/Carousel/CarouselContext.ts +11 -0
  68. package/src/Carousel/README.md +848 -0
  69. package/src/Carousel/__tests__/Carousel.asChild.test.tsx +178 -0
  70. package/src/Carousel/__tests__/Carousel.auto-play.test.tsx +617 -0
  71. package/src/Carousel/__tests__/Carousel.basic-rendering.test.tsx +569 -0
  72. package/src/Carousel/__tests__/Carousel.controlled-state.test.tsx +137 -0
  73. package/src/Carousel/__tests__/Carousel.error-handling.test.tsx +81 -0
  74. package/src/Carousel/__tests__/Carousel.ids.test.tsx +111 -0
  75. package/src/Carousel/__tests__/Carousel.imperative-api.test.tsx +213 -0
  76. package/src/Carousel/__tests__/Carousel.indicators.test.tsx +560 -0
  77. package/src/Carousel/__tests__/Carousel.intersection-observer.test.tsx +276 -0
  78. package/src/Carousel/__tests__/Carousel.keyboard-navigation.test.tsx +158 -0
  79. package/src/Carousel/__tests__/Carousel.play-pause.test.tsx +232 -0
  80. package/src/Carousel/__tests__/Carousel.prev-next.test.tsx +68 -0
  81. package/src/Carousel/__tests__/Carousel.reduced-motion.test.tsx +49 -0
  82. package/src/Carousel/__tests__/Carousel.refresh-progress.test.tsx +87 -0
  83. package/src/Carousel/__tests__/Carousel.scroll-snap-change.test.tsx +179 -0
  84. package/src/Carousel/__tests__/Carousel.scroll-sync.test.tsx +109 -0
  85. package/src/Carousel/__tests__/Carousel.slides-per-move.test.tsx +151 -0
  86. package/src/Carousel/__tests__/Carousel.slides-per-page.test.tsx +183 -0
  87. package/src/Carousel/__tests__/Carousel.touch-interaction.test.tsx +96 -0
  88. package/src/Carousel/__tests__/Carousel.transition-modes.test.tsx +70 -0
  89. package/src/Carousel/__tests__/Carousel.translations.test.tsx +157 -0
  90. package/src/Carousel/__tests__/Carousel.uncontrolled-state.test.tsx +146 -0
  91. package/src/Carousel/hooks/index.ts +4 -0
  92. package/src/Carousel/hooks/useCarouselContext.ts +13 -0
  93. package/src/Carousel/hooks/useCarouselRoot.ts +450 -0
  94. package/src/Carousel/hooks/useCarouselSlide.ts +45 -0
  95. package/src/Carousel/hooks/useCarouselViewport.ts +290 -0
  96. package/src/Carousel/index.ts +3 -0
  97. package/src/Carousel/types.ts +400 -0
  98. package/src/Checkbox/Checkbox.tsx +228 -0
  99. package/src/Checkbox/CheckboxContext.ts +12 -0
  100. package/src/Checkbox/README.md +156 -0
  101. package/src/Checkbox/__tests__/Checkbox.asChild.test.tsx +69 -0
  102. package/src/Checkbox/__tests__/Checkbox.basic-rendering.test.tsx +41 -0
  103. package/src/Checkbox/__tests__/Checkbox.controlled-state.test.tsx +82 -0
  104. package/src/Checkbox/__tests__/Checkbox.disabled.test.tsx +15 -0
  105. package/src/Checkbox/__tests__/Checkbox.indeterminate.test.tsx +82 -0
  106. package/src/Checkbox/__tests__/Checkbox.indicator.test.tsx +117 -0
  107. package/src/Checkbox/__tests__/Checkbox.uncontrolled-state.test.tsx +89 -0
  108. package/src/Checkbox/hooks/index.ts +2 -0
  109. package/src/Checkbox/hooks/useCheckboxContext.ts +1 -0
  110. package/src/Checkbox/hooks/useCheckboxRoot.ts +32 -0
  111. package/src/Checkbox/index.ts +1 -0
  112. package/src/Checkbox/types.ts +33 -0
  113. package/src/CheckboxCard/CheckboxCard.tsx +208 -0
  114. package/src/CheckboxCard/CheckboxCardContext.ts +12 -0
  115. package/src/CheckboxCard/README.md +114 -0
  116. package/src/CheckboxCard/__tests__/CheckboxCard.asChild.test.tsx +54 -0
  117. package/src/CheckboxCard/__tests__/CheckboxCard.basic-rendering.test.tsx +58 -0
  118. package/src/CheckboxCard/__tests__/CheckboxCard.controlled-state.test.tsx +77 -0
  119. package/src/CheckboxCard/__tests__/CheckboxCard.disabled.test.tsx +55 -0
  120. package/src/CheckboxCard/__tests__/CheckboxCard.error-handling.test.tsx +20 -0
  121. package/src/CheckboxCard/__tests__/CheckboxCard.indeterminate.test.tsx +60 -0
  122. package/src/CheckboxCard/__tests__/CheckboxCard.indicator.test.tsx +136 -0
  123. package/src/CheckboxCard/__tests__/CheckboxCard.uncontrolled-state.test.tsx +73 -0
  124. package/src/CheckboxCard/hooks/index.ts +2 -0
  125. package/src/CheckboxCard/hooks/useCheckboxCardContext.ts +1 -0
  126. package/src/CheckboxCard/hooks/useCheckboxCardRoot.ts +30 -0
  127. package/src/CheckboxCard/index.ts +3 -0
  128. package/src/CheckboxCard/types.ts +33 -0
  129. package/src/Collapsible/Collapsible.tsx +316 -0
  130. package/src/Collapsible/CollapsibleContext.ts +7 -0
  131. package/src/Collapsible/README.md +174 -0
  132. package/src/Collapsible/__tests__/Collapsible.asChild.test.tsx +240 -0
  133. package/src/Collapsible/__tests__/Collapsible.basic-rendering.test.tsx +118 -0
  134. package/src/Collapsible/__tests__/Collapsible.controlled-state.test.tsx +134 -0
  135. package/src/Collapsible/__tests__/Collapsible.disabled.test.tsx +132 -0
  136. package/src/Collapsible/__tests__/Collapsible.error-handling.test.tsx +40 -0
  137. package/src/Collapsible/__tests__/Collapsible.forceMount.test.tsx +111 -0
  138. package/src/Collapsible/__tests__/Collapsible.triggerIcon.test.tsx +93 -0
  139. package/src/Collapsible/__tests__/Collapsible.uncontrolled-state.test.tsx +125 -0
  140. package/src/Collapsible/hooks/index.ts +2 -0
  141. package/src/Collapsible/hooks/useCollapsibleRoot.ts +34 -0
  142. package/src/Collapsible/hooks/useCollapsibleTrigger.ts +49 -0
  143. package/src/Collapsible/index.ts +1 -0
  144. package/src/Collapsible/types.ts +48 -0
  145. package/src/ContextMenu/ContextMenu.tsx +1004 -0
  146. package/src/ContextMenu/ContextMenuContentContext.ts +15 -0
  147. package/src/ContextMenu/ContextMenuContext.ts +21 -0
  148. package/src/ContextMenu/ContextMenuGroupContext.ts +8 -0
  149. package/src/ContextMenu/ContextMenuItemIndicatorContext.ts +8 -0
  150. package/src/ContextMenu/ContextMenuRadioGroupContext.ts +9 -0
  151. package/src/ContextMenu/ContextMenuSubContext.ts +15 -0
  152. package/src/ContextMenu/README.md +275 -0
  153. package/src/ContextMenu/__tests__/ContextMenu.asChild.test.tsx +186 -0
  154. package/src/ContextMenu/__tests__/ContextMenu.basic-rendering.test.tsx +39 -0
  155. package/src/ContextMenu/__tests__/ContextMenu.checkbox-item.test.tsx +145 -0
  156. package/src/ContextMenu/__tests__/ContextMenu.error-handling.test.tsx +113 -0
  157. package/src/ContextMenu/__tests__/ContextMenu.group-label.test.tsx +48 -0
  158. package/src/ContextMenu/__tests__/ContextMenu.item-indicator.test.tsx +88 -0
  159. package/src/ContextMenu/__tests__/ContextMenu.item.test.tsx +106 -0
  160. package/src/ContextMenu/__tests__/ContextMenu.keyboard-interaction.test.tsx +172 -0
  161. package/src/ContextMenu/__tests__/ContextMenu.mouse-interaction.test.tsx +227 -0
  162. package/src/ContextMenu/__tests__/ContextMenu.radio-item.test.tsx +127 -0
  163. package/src/ContextMenu/__tests__/ContextMenu.reading-direction.test.tsx +152 -0
  164. package/src/ContextMenu/__tests__/ContextMenu.separator.test.tsx +47 -0
  165. package/src/ContextMenu/__tests__/ContextMenu.state-modes.test.tsx +119 -0
  166. package/src/ContextMenu/__tests__/ContextMenu.sub.test.tsx +262 -0
  167. package/src/ContextMenu/__tests__/ContextMenu.typeahead.test.tsx +89 -0
  168. package/src/ContextMenu/constants.ts +4 -0
  169. package/src/ContextMenu/index.ts +1 -0
  170. package/src/ContextMenu/types.ts +199 -0
  171. package/src/DirectionProvider/DirectionContext.ts +21 -0
  172. package/src/DirectionProvider/DirectionProvider.tsx +31 -0
  173. package/src/DirectionProvider/README.md +62 -0
  174. package/src/DirectionProvider/__tests__/DirectionProvider.test.tsx +29 -0
  175. package/src/DirectionProvider/index.ts +3 -0
  176. package/src/DirectionProvider/types.ts +10 -0
  177. package/src/Divider/Divider.tsx +57 -0
  178. package/src/Divider/README.md +57 -0
  179. package/src/Divider/__tests__/Divider.test.tsx +41 -0
  180. package/src/Divider/index.ts +1 -0
  181. package/src/Divider/types.ts +5 -0
  182. package/src/Dropdown/Dropdown.tsx +842 -0
  183. package/src/Dropdown/DropdownContentContext.ts +15 -0
  184. package/src/Dropdown/DropdownContext.ts +17 -0
  185. package/src/Dropdown/DropdownGroupContext.ts +8 -0
  186. package/src/Dropdown/DropdownItemIndicatorContext.ts +13 -0
  187. package/src/Dropdown/DropdownRadioGroupContext.ts +9 -0
  188. package/src/Dropdown/DropdownSubContext.ts +15 -0
  189. package/src/Dropdown/README.md +284 -0
  190. package/src/Dropdown/__tests__/Dropdown.asChild.test.tsx +286 -0
  191. package/src/Dropdown/__tests__/Dropdown.basic-rendering.test.tsx +43 -0
  192. package/src/Dropdown/__tests__/Dropdown.checkbox-item.test.tsx +121 -0
  193. package/src/Dropdown/__tests__/Dropdown.disabled.test.tsx +143 -0
  194. package/src/Dropdown/__tests__/Dropdown.error-handling.test.tsx +85 -0
  195. package/src/Dropdown/__tests__/Dropdown.group-label.test.tsx +68 -0
  196. package/src/Dropdown/__tests__/Dropdown.item-indicator.test.tsx +260 -0
  197. package/src/Dropdown/__tests__/Dropdown.item.test.tsx +72 -0
  198. package/src/Dropdown/__tests__/Dropdown.keyboard-edge-cases.test.tsx +77 -0
  199. package/src/Dropdown/__tests__/Dropdown.keyboard-interaction.test.tsx +310 -0
  200. package/src/Dropdown/__tests__/Dropdown.mouse-interaction.test.tsx +347 -0
  201. package/src/Dropdown/__tests__/Dropdown.radio-item.test.tsx +134 -0
  202. package/src/Dropdown/__tests__/Dropdown.reading-direction.test.tsx +153 -0
  203. package/src/Dropdown/__tests__/Dropdown.separator.test.tsx +46 -0
  204. package/src/Dropdown/__tests__/Dropdown.state-modes.test.tsx +100 -0
  205. package/src/Dropdown/__tests__/Dropdown.sub.test.tsx +185 -0
  206. package/src/Dropdown/__tests__/Dropdown.trigger.test.tsx +110 -0
  207. package/src/Dropdown/__tests__/Dropdown.typeahead.test.tsx +133 -0
  208. package/src/Dropdown/constants.ts +4 -0
  209. package/src/Dropdown/hooks/index.ts +9 -0
  210. package/src/Dropdown/hooks/useCloseSiblingSub.ts +13 -0
  211. package/src/Dropdown/hooks/useDropdownContent.ts +162 -0
  212. package/src/Dropdown/hooks/useDropdownContext.ts +1 -0
  213. package/src/Dropdown/hooks/useDropdownGroup.ts +18 -0
  214. package/src/Dropdown/hooks/useDropdownItem.ts +49 -0
  215. package/src/Dropdown/hooks/useDropdownLabel.ts +15 -0
  216. package/src/Dropdown/hooks/useDropdownRoot.ts +57 -0
  217. package/src/Dropdown/hooks/useDropdownSubContext.ts +1 -0
  218. package/src/Dropdown/hooks/useDropdownTrigger.ts +31 -0
  219. package/src/Dropdown/index.ts +1 -0
  220. package/src/Dropdown/types.ts +200 -0
  221. package/src/EmptyState/EmptyState.tsx +245 -0
  222. package/src/EmptyState/README.md +129 -0
  223. package/src/EmptyState/__tests__/EmptyState.Actions.test.tsx +32 -0
  224. package/src/EmptyState/__tests__/EmptyState.Description.test.tsx +30 -0
  225. package/src/EmptyState/__tests__/EmptyState.Media.test.tsx +34 -0
  226. package/src/EmptyState/__tests__/EmptyState.Root.test.tsx +28 -0
  227. package/src/EmptyState/__tests__/EmptyState.Title.test.tsx +26 -0
  228. package/src/EmptyState/index.ts +2 -0
  229. package/src/EmptyState/types.ts +21 -0
  230. package/src/Field/Field.tsx +239 -0
  231. package/src/Field/FieldContext.ts +22 -0
  232. package/src/Field/README.md +167 -0
  233. package/src/Field/__tests__/Field.asChild.test.tsx +83 -0
  234. package/src/Field/__tests__/Field.basic-rendering.test.tsx +104 -0
  235. package/src/Field/__tests__/Field.state-cascade.test.tsx +75 -0
  236. package/src/Field/hooks/index.ts +2 -0
  237. package/src/Field/hooks/useFieldContext.ts +1 -0
  238. package/src/Field/hooks/useFieldProps.ts +57 -0
  239. package/src/Field/index.ts +2 -0
  240. package/src/Field/types.ts +33 -0
  241. package/src/Fieldset/Fieldset.tsx +104 -0
  242. package/src/Fieldset/README.md +74 -0
  243. package/src/Fieldset/__tests__/Fieldset.basic-rendering.test.tsx +81 -0
  244. package/src/Fieldset/__tests__/Fieldset.disabled.test.tsx +41 -0
  245. package/src/Fieldset/index.ts +2 -0
  246. package/src/Fieldset/types.ts +5 -0
  247. package/src/Input/Input.tsx +120 -0
  248. package/src/Input/README.md +180 -0
  249. package/src/Input/__tests__/Input.asChild.test.tsx +85 -0
  250. package/src/Input/__tests__/Input.basic-rendering.test.tsx +118 -0
  251. package/src/Input/__tests__/Input.disabled.test.tsx +49 -0
  252. package/src/Input/__tests__/Input.field-integration.test.tsx +148 -0
  253. package/src/Input/index.ts +2 -0
  254. package/src/Input/types.ts +7 -0
  255. package/src/InputGroup/InputGroup.tsx +228 -0
  256. package/src/InputGroup/README.md +178 -0
  257. package/src/InputGroup/__tests__/InputGroup.asChild.test.tsx +109 -0
  258. package/src/InputGroup/__tests__/InputGroup.basic-rendering.test.tsx +106 -0
  259. package/src/InputGroup/index.ts +2 -0
  260. package/src/InputGroup/types.ts +13 -0
  261. package/src/MillerColumns/MillerColumns.tsx +329 -0
  262. package/src/MillerColumns/MillerColumnsContext.ts +25 -0
  263. package/src/MillerColumns/README.md +278 -0
  264. package/src/MillerColumns/__tests__/MillerColumns.aria.test.tsx +82 -0
  265. package/src/MillerColumns/__tests__/MillerColumns.asChild.test.tsx +106 -0
  266. package/src/MillerColumns/__tests__/MillerColumns.auto-scroll.test.tsx +68 -0
  267. package/src/MillerColumns/__tests__/MillerColumns.basic-rendering.test.tsx +52 -0
  268. package/src/MillerColumns/__tests__/MillerColumns.column-projection.test.tsx +161 -0
  269. package/src/MillerColumns/__tests__/MillerColumns.controlled-state.test.tsx +90 -0
  270. package/src/MillerColumns/__tests__/MillerColumns.data-attributes.test.tsx +77 -0
  271. package/src/MillerColumns/__tests__/MillerColumns.disabled-items.test.tsx +65 -0
  272. package/src/MillerColumns/__tests__/MillerColumns.error-handling.test.tsx +57 -0
  273. package/src/MillerColumns/__tests__/MillerColumns.fixtures.ts +15 -0
  274. package/src/MillerColumns/__tests__/MillerColumns.item-indicator.test.tsx +57 -0
  275. package/src/MillerColumns/__tests__/MillerColumns.keyboard-interaction.test.tsx +181 -0
  276. package/src/MillerColumns/__tests__/MillerColumns.preview-panel.test.tsx +47 -0
  277. package/src/MillerColumns/__tests__/MillerColumns.resize.test.tsx +137 -0
  278. package/src/MillerColumns/__tests__/MillerColumns.roving-tabindex.test.tsx +91 -0
  279. package/src/MillerColumns/__tests__/MillerColumns.selection.test.tsx +54 -0
  280. package/src/MillerColumns/__tests__/MillerColumns.uncontrolled-state.test.tsx +70 -0
  281. package/src/MillerColumns/hooks/index.ts +7 -0
  282. package/src/MillerColumns/hooks/useMillerColumnsColumn.ts +23 -0
  283. package/src/MillerColumns/hooks/useMillerColumnsColumnContext.ts +1 -0
  284. package/src/MillerColumns/hooks/useMillerColumnsContext.ts +1 -0
  285. package/src/MillerColumns/hooks/useMillerColumnsItem.ts +157 -0
  286. package/src/MillerColumns/hooks/useMillerColumnsItemContext.ts +1 -0
  287. package/src/MillerColumns/hooks/useMillerColumnsResizeHandle.ts +76 -0
  288. package/src/MillerColumns/hooks/useMillerColumnsRoot.ts +0 -0
  289. package/src/MillerColumns/index.ts +3 -0
  290. package/src/MillerColumns/types.ts +93 -0
  291. package/src/MillerColumns/useMillerColumnsSelection.ts +31 -0
  292. package/src/MillerColumns/utils.ts +75 -0
  293. package/src/Modal/Modal.tsx +474 -0
  294. package/src/Modal/ModalContext.ts +13 -0
  295. package/src/Modal/README.md +207 -0
  296. package/src/Modal/__tests__/Modal.accessibility.test.tsx +167 -0
  297. package/src/Modal/__tests__/Modal.asChild.test.tsx +162 -0
  298. package/src/Modal/__tests__/Modal.click-outside.test.tsx +115 -0
  299. package/src/Modal/__tests__/Modal.content.test.tsx +193 -0
  300. package/src/Modal/__tests__/Modal.controlled-state.test.tsx +120 -0
  301. package/src/Modal/__tests__/Modal.error-handling.test.tsx +30 -0
  302. package/src/Modal/__tests__/Modal.escape-hatches.test.tsx +99 -0
  303. package/src/Modal/__tests__/Modal.imperative-api.test.tsx +119 -0
  304. package/src/Modal/__tests__/Modal.nested.test.tsx +106 -0
  305. package/src/Modal/__tests__/Modal.overlay.test.tsx +99 -0
  306. package/src/Modal/__tests__/Modal.portal.test.tsx +90 -0
  307. package/src/Modal/__tests__/Modal.presence.test.tsx +111 -0
  308. package/src/Modal/__tests__/Modal.trigger.test.tsx +70 -0
  309. package/src/Modal/__tests__/Modal.uncontrolled-state.test.tsx +72 -0
  310. package/src/Modal/__tests__/dialog-polyfill.ts +40 -0
  311. package/src/Modal/hooks/index.ts +4 -0
  312. package/src/Modal/hooks/useModalContent.ts +62 -0
  313. package/src/Modal/hooks/useModalContext.ts +1 -0
  314. package/src/Modal/hooks/useModalRoot.ts +81 -0
  315. package/src/Modal/hooks/useModalTrigger.ts +25 -0
  316. package/src/Modal/index.ts +3 -0
  317. package/src/Modal/types.ts +76 -0
  318. package/src/Portal/Portal.tsx +28 -0
  319. package/src/Portal/README.md +70 -0
  320. package/src/Portal/__tests__/Portal.basic-rendering.test.tsx +17 -0
  321. package/src/Portal/index.ts +2 -0
  322. package/src/Portal/types.ts +6 -0
  323. package/src/Progress/Progress.tsx +178 -0
  324. package/src/Progress/ProgressContext.ts +15 -0
  325. package/src/Progress/README.md +112 -0
  326. package/src/Progress/__tests__/Progress.asChild.test.tsx +37 -0
  327. package/src/Progress/__tests__/Progress.basic-rendering.test.tsx +65 -0
  328. package/src/Progress/__tests__/Progress.error-handling.test.tsx +40 -0
  329. package/src/Progress/__tests__/Progress.fixtures.ts +7 -0
  330. package/src/Progress/__tests__/Progress.value.test.tsx +83 -0
  331. package/src/Progress/hooks/index.ts +2 -0
  332. package/src/Progress/hooks/useProgressContext.ts +1 -0
  333. package/src/Progress/hooks/useProgressRoot.ts +45 -0
  334. package/src/Progress/index.ts +3 -0
  335. package/src/Progress/types.ts +43 -0
  336. package/src/RadioCard/README.md +133 -0
  337. package/src/RadioCard/RadioCard.tsx +334 -0
  338. package/src/RadioCard/RadioCardContext.ts +23 -0
  339. package/src/RadioCard/RadioCardItemContext.ts +10 -0
  340. package/src/RadioCard/__tests__/RadioCard.asChild.test.tsx +76 -0
  341. package/src/RadioCard/__tests__/RadioCard.basic-rendering.test.tsx +87 -0
  342. package/src/RadioCard/__tests__/RadioCard.controlled-state.test.tsx +107 -0
  343. package/src/RadioCard/__tests__/RadioCard.disabled-items.test.tsx +61 -0
  344. package/src/RadioCard/__tests__/RadioCard.error-handling.test.tsx +35 -0
  345. package/src/RadioCard/__tests__/RadioCard.indicator.test.tsx +119 -0
  346. package/src/RadioCard/__tests__/RadioCard.keyboard-interaction.test.tsx +158 -0
  347. package/src/RadioCard/__tests__/RadioCard.orientation.test.tsx +90 -0
  348. package/src/RadioCard/__tests__/RadioCard.reading-direction.test.tsx +65 -0
  349. package/src/RadioCard/__tests__/RadioCard.uncontrolled-state.test.tsx +108 -0
  350. package/src/RadioCard/hooks/index.ts +3 -0
  351. package/src/RadioCard/hooks/useRadioCardContext.ts +1 -0
  352. package/src/RadioCard/hooks/useRadioCardItemContext.ts +1 -0
  353. package/src/RadioCard/hooks/useRadioCardRoot.ts +77 -0
  354. package/src/RadioCard/index.ts +4 -0
  355. package/src/RadioCard/types.ts +51 -0
  356. package/src/RadioGroup/README.md +185 -0
  357. package/src/RadioGroup/RadioGroup.tsx +353 -0
  358. package/src/RadioGroup/RadioGroupContext.ts +23 -0
  359. package/src/RadioGroup/RadioGroupItemContext.ts +10 -0
  360. package/src/RadioGroup/__tests__/RadioGroup.asChild.test.tsx +105 -0
  361. package/src/RadioGroup/__tests__/RadioGroup.basic-rendering.test.tsx +72 -0
  362. package/src/RadioGroup/__tests__/RadioGroup.controlled-state.test.tsx +109 -0
  363. package/src/RadioGroup/__tests__/RadioGroup.disabled-keydown-guards.test.tsx +68 -0
  364. package/src/RadioGroup/__tests__/RadioGroup.disabled.test.tsx +79 -0
  365. package/src/RadioGroup/__tests__/RadioGroup.error-handling.test.tsx +33 -0
  366. package/src/RadioGroup/__tests__/RadioGroup.indicator.test.tsx +85 -0
  367. package/src/RadioGroup/__tests__/RadioGroup.keyboard-interaction.test.tsx +135 -0
  368. package/src/RadioGroup/__tests__/RadioGroup.orientation.test.tsx +90 -0
  369. package/src/RadioGroup/__tests__/RadioGroup.reading-direction.test.tsx +65 -0
  370. package/src/RadioGroup/__tests__/RadioGroup.ref-forwarding.test.tsx +45 -0
  371. package/src/RadioGroup/__tests__/RadioGroup.roving-tabindex.test.tsx +105 -0
  372. package/src/RadioGroup/__tests__/RadioGroup.uncontrolled-state.test.tsx +96 -0
  373. package/src/RadioGroup/hooks/index.ts +3 -0
  374. package/src/RadioGroup/hooks/useRadioGroupContext.ts +1 -0
  375. package/src/RadioGroup/hooks/useRadioGroupItemContext.ts +1 -0
  376. package/src/RadioGroup/hooks/useRadioGroupRoot.ts +87 -0
  377. package/src/RadioGroup/index.ts +1 -0
  378. package/src/RadioGroup/types.ts +51 -0
  379. package/src/Select/README.md +203 -0
  380. package/src/Select/Select.tsx +204 -0
  381. package/src/Select/__tests__/Select.asChild.test.tsx +36 -0
  382. package/src/Select/__tests__/Select.basic-rendering.test.tsx +17 -0
  383. package/src/Select/__tests__/Select.controlled-state.test.tsx +69 -0
  384. package/src/Select/__tests__/Select.data-attributes.test.tsx +29 -0
  385. package/src/Select/__tests__/Select.field-integration.test.tsx +150 -0
  386. package/src/Select/__tests__/Select.group.test.tsx +42 -0
  387. package/src/Select/__tests__/Select.placeholder.test.tsx +32 -0
  388. package/src/Select/index.ts +2 -0
  389. package/src/Select/types.ts +89 -0
  390. package/src/SkipNav/README.md +87 -0
  391. package/src/SkipNav/SkipNav.tsx +116 -0
  392. package/src/SkipNav/__tests__/SkipNav.basic-rendering.test.tsx +23 -0
  393. package/src/SkipNav/__tests__/SkipNav.ids.test.tsx +19 -0
  394. package/src/SkipNav/index.ts +1 -0
  395. package/src/SkipNav/types.ts +26 -0
  396. package/src/Slider/README.md +215 -0
  397. package/src/Slider/Slider.tsx +308 -0
  398. package/src/Slider/SliderContext.ts +24 -0
  399. package/src/Slider/__tests__/Slider.asChild.test.tsx +119 -0
  400. package/src/Slider/__tests__/Slider.basic-rendering.test.tsx +157 -0
  401. package/src/Slider/__tests__/Slider.controlled-state.test.tsx +78 -0
  402. package/src/Slider/__tests__/Slider.disabled.test.tsx +82 -0
  403. package/src/Slider/__tests__/Slider.error-handling.test.tsx +45 -0
  404. package/src/Slider/__tests__/Slider.fixtures.ts +53 -0
  405. package/src/Slider/__tests__/Slider.form.test.tsx +67 -0
  406. package/src/Slider/__tests__/Slider.inverted.test.tsx +112 -0
  407. package/src/Slider/__tests__/Slider.keyboard-interaction.test.tsx +118 -0
  408. package/src/Slider/__tests__/Slider.multiple-thumbs.test.tsx +84 -0
  409. package/src/Slider/__tests__/Slider.orientation.test.tsx +101 -0
  410. package/src/Slider/__tests__/Slider.pointer-interaction.test.tsx +205 -0
  411. package/src/Slider/__tests__/Slider.reading-direction.test.tsx +99 -0
  412. package/src/Slider/__tests__/Slider.uncontrolled-state.test.tsx +69 -0
  413. package/src/Slider/__tests__/Slider.value-commit.test.tsx +103 -0
  414. package/src/Slider/hooks/index.ts +3 -0
  415. package/src/Slider/hooks/useSliderContext.ts +1 -0
  416. package/src/Slider/hooks/useSliderRoot.ts +197 -0
  417. package/src/Slider/hooks/useSliderThumb.ts +77 -0
  418. package/src/Slider/index.ts +3 -0
  419. package/src/Slider/types.ts +48 -0
  420. package/src/Slider/utils.ts +155 -0
  421. package/src/Slot/Slot.tsx +158 -0
  422. package/src/Slot/__tests__/Slot.test.tsx +163 -0
  423. package/src/Slot/__tests__/composeEventHandlers.test.ts +74 -0
  424. package/src/Slot/composeEventHandlers.ts +38 -0
  425. package/src/Slot/index.ts +3 -0
  426. package/src/Slot/types.ts +5 -0
  427. package/src/Status/README.md +50 -0
  428. package/src/Status/Status.tsx +44 -0
  429. package/src/Status/__tests__/Status.test.tsx +28 -0
  430. package/src/Status/index.ts +2 -0
  431. package/src/Status/types.ts +5 -0
  432. package/src/Switch/README.md +121 -0
  433. package/src/Switch/Switch.tsx +167 -0
  434. package/src/Switch/SwitchContext.ts +10 -0
  435. package/src/Switch/__tests__/Switch.asChild.test.tsx +56 -0
  436. package/src/Switch/__tests__/Switch.basic-rendering.test.tsx +76 -0
  437. package/src/Switch/__tests__/Switch.contract.test.tsx +109 -0
  438. package/src/Switch/__tests__/Switch.controlled-state.test.tsx +79 -0
  439. package/src/Switch/__tests__/Switch.disabled.test.tsx +60 -0
  440. package/src/Switch/__tests__/Switch.error-handling.test.tsx +20 -0
  441. package/src/Switch/__tests__/Switch.keyboard-interaction.test.tsx +56 -0
  442. package/src/Switch/__tests__/Switch.thumb.test.tsx +84 -0
  443. package/src/Switch/__tests__/Switch.uncontrolled-state.test.tsx +83 -0
  444. package/src/Switch/hooks/index.ts +2 -0
  445. package/src/Switch/hooks/useSwitchContext.ts +1 -0
  446. package/src/Switch/hooks/useSwitchRoot.ts +28 -0
  447. package/src/Switch/index.ts +3 -0
  448. package/src/Switch/types.ts +37 -0
  449. package/src/Table/README.md +205 -0
  450. package/src/Table/Table.tsx +380 -0
  451. package/src/Table/__tests__/Table.Body.test.tsx +61 -0
  452. package/src/Table/__tests__/Table.Caption.test.tsx +70 -0
  453. package/src/Table/__tests__/Table.Cell.test.tsx +73 -0
  454. package/src/Table/__tests__/Table.Footer.test.tsx +61 -0
  455. package/src/Table/__tests__/Table.Head.test.tsx +61 -0
  456. package/src/Table/__tests__/Table.Header.test.tsx +73 -0
  457. package/src/Table/__tests__/Table.Root.test.tsx +49 -0
  458. package/src/Table/__tests__/Table.Row.test.tsx +67 -0
  459. package/src/Table/__tests__/Table.ScrollArea.test.tsx +83 -0
  460. package/src/Table/index.ts +1 -0
  461. package/src/Table/types.ts +63 -0
  462. package/src/Tabs/README.md +110 -0
  463. package/src/Tabs/Tabs.tsx +434 -0
  464. package/src/Tabs/TabsContext.ts +13 -0
  465. package/src/Tabs/__tests__/Tabs.activation-mode.test.tsx +114 -0
  466. package/src/Tabs/__tests__/Tabs.asChild.test.tsx +91 -0
  467. package/src/Tabs/__tests__/Tabs.basic-rendering.test.tsx +483 -0
  468. package/src/Tabs/__tests__/Tabs.change-event-callbacks.test.tsx +133 -0
  469. package/src/Tabs/__tests__/Tabs.controlled-state.test.tsx +152 -0
  470. package/src/Tabs/__tests__/Tabs.disabled-tabs.test.tsx +203 -0
  471. package/src/Tabs/__tests__/Tabs.error-handling.test.tsx +82 -0
  472. package/src/Tabs/__tests__/Tabs.fixtures.ts +171 -0
  473. package/src/Tabs/__tests__/Tabs.imperative-api.test.tsx +118 -0
  474. package/src/Tabs/__tests__/Tabs.keyboard-interaction.test.tsx +192 -0
  475. package/src/Tabs/__tests__/Tabs.lazy-mount.test.tsx +61 -0
  476. package/src/Tabs/__tests__/Tabs.mouse-interaction.test.tsx +216 -0
  477. package/src/Tabs/__tests__/Tabs.reading-direction.test.tsx +58 -0
  478. package/src/Tabs/__tests__/Tabs.uncontrolled-state.test.tsx +197 -0
  479. package/src/Tabs/hooks/index.ts +4 -0
  480. package/src/Tabs/hooks/useTabsContent.ts +27 -0
  481. package/src/Tabs/hooks/useTabsContext.ts +1 -0
  482. package/src/Tabs/hooks/useTabsRoot.ts +148 -0
  483. package/src/Tabs/hooks/useTabsTrigger.ts +111 -0
  484. package/src/Tabs/index.ts +3 -0
  485. package/src/Tabs/types.ts +99 -0
  486. package/src/Tabs/utils.ts +8 -0
  487. package/src/Textarea/README.md +98 -0
  488. package/src/Textarea/Textarea.tsx +93 -0
  489. package/src/Textarea/__tests__/Textarea.asChild.test.tsx +85 -0
  490. package/src/Textarea/__tests__/Textarea.basic-rendering.test.tsx +107 -0
  491. package/src/Textarea/__tests__/Textarea.disabled.test.tsx +49 -0
  492. package/src/Textarea/__tests__/Textarea.field-integration.test.tsx +134 -0
  493. package/src/Textarea/index.ts +2 -0
  494. package/src/Textarea/types.ts +7 -0
  495. package/src/Toggle/README.md +97 -0
  496. package/src/Toggle/Toggle.tsx +81 -0
  497. package/src/Toggle/__tests__/Toggle.asChild.test.tsx +42 -0
  498. package/src/Toggle/__tests__/Toggle.basic-rendering.test.tsx +28 -0
  499. package/src/Toggle/__tests__/Toggle.controlled-state.test.tsx +60 -0
  500. package/src/Toggle/__tests__/Toggle.disabled.test.tsx +34 -0
  501. package/src/Toggle/__tests__/Toggle.keyboard-interaction.test.tsx +42 -0
  502. package/src/Toggle/__tests__/Toggle.uncontrolled-state.test.tsx +40 -0
  503. package/src/Toggle/index.ts +2 -0
  504. package/src/Toggle/types.ts +23 -0
  505. package/src/ToggleGroup/README.md +137 -0
  506. package/src/ToggleGroup/ToggleGroup.tsx +298 -0
  507. package/src/ToggleGroup/ToggleGroupContext.ts +9 -0
  508. package/src/ToggleGroup/__tests__/ToggleGroup.asChild.test.tsx +65 -0
  509. package/src/ToggleGroup/__tests__/ToggleGroup.basic-rendering.test.tsx +50 -0
  510. package/src/ToggleGroup/__tests__/ToggleGroup.disabled.test.tsx +54 -0
  511. package/src/ToggleGroup/__tests__/ToggleGroup.keyboard-interaction.test.tsx +151 -0
  512. package/src/ToggleGroup/__tests__/ToggleGroup.multiple-mode.test.tsx +144 -0
  513. package/src/ToggleGroup/__tests__/ToggleGroup.reading-direction.test.tsx +28 -0
  514. package/src/ToggleGroup/__tests__/ToggleGroup.single-mode.test.tsx +139 -0
  515. package/src/ToggleGroup/hooks/index.ts +2 -0
  516. package/src/ToggleGroup/hooks/useToggleGroupContext.ts +1 -0
  517. package/src/ToggleGroup/hooks/useToggleGroupRoot.ts +110 -0
  518. package/src/ToggleGroup/index.ts +2 -0
  519. package/src/ToggleGroup/types.ts +72 -0
  520. package/src/Tooltip/README.md +214 -0
  521. package/src/Tooltip/Tooltip.tsx +260 -0
  522. package/src/Tooltip/TooltipContext.ts +20 -0
  523. package/src/Tooltip/__tests__/Tooltip.asChild.test.tsx +77 -0
  524. package/src/Tooltip/__tests__/Tooltip.basic-rendering.test.tsx +180 -0
  525. package/src/Tooltip/__tests__/Tooltip.controlled-state.test.tsx +128 -0
  526. package/src/Tooltip/__tests__/Tooltip.escape-hatches.test.tsx +73 -0
  527. package/src/Tooltip/__tests__/Tooltip.focus-interaction.test.tsx +88 -0
  528. package/src/Tooltip/__tests__/Tooltip.hover-interaction.test.tsx +179 -0
  529. package/src/Tooltip/__tests__/Tooltip.keyboard-interaction.test.tsx +85 -0
  530. package/src/Tooltip/__tests__/Tooltip.uncontrolled-state.test.tsx +67 -0
  531. package/src/Tooltip/hooks/index.ts +4 -0
  532. package/src/Tooltip/hooks/useTooltipContent.ts +53 -0
  533. package/src/Tooltip/hooks/useTooltipProvider.ts +41 -0
  534. package/src/Tooltip/hooks/useTooltipRoot.ts +106 -0
  535. package/src/Tooltip/hooks/useTooltipTrigger.ts +44 -0
  536. package/src/Tooltip/index.ts +1 -0
  537. package/src/Tooltip/types.ts +64 -0
  538. package/src/Tree/README.md +339 -0
  539. package/src/Tree/Tree.tsx +571 -0
  540. package/src/Tree/TreeContext.ts +24 -0
  541. package/src/Tree/__tests__/Tree.aria.test.tsx +53 -0
  542. package/src/Tree/__tests__/Tree.asChild.test.tsx +134 -0
  543. package/src/Tree/__tests__/Tree.basic-rendering.test.tsx +111 -0
  544. package/src/Tree/__tests__/Tree.branch-behaviour.test.tsx +87 -0
  545. package/src/Tree/__tests__/Tree.controlled-expansion.test.tsx +92 -0
  546. package/src/Tree/__tests__/Tree.data-attributes.test.tsx +88 -0
  547. package/src/Tree/__tests__/Tree.disabled-items.test.tsx +196 -0
  548. package/src/Tree/__tests__/Tree.error-handling.test.tsx +71 -0
  549. package/src/Tree/__tests__/Tree.forceMount.test.tsx +72 -0
  550. package/src/Tree/__tests__/Tree.keyboard-interaction.test.tsx +150 -0
  551. package/src/Tree/__tests__/Tree.multiple-selection.test.tsx +151 -0
  552. package/src/Tree/__tests__/Tree.range-selection.test.tsx +200 -0
  553. package/src/Tree/__tests__/Tree.recursion-depth.test.tsx +73 -0
  554. package/src/Tree/__tests__/Tree.roving-tabindex.test.tsx +117 -0
  555. package/src/Tree/__tests__/Tree.selection-path.test.tsx +404 -0
  556. package/src/Tree/__tests__/Tree.single-selection.test.tsx +108 -0
  557. package/src/Tree/__tests__/Tree.uncontrolled-expansion.test.tsx +69 -0
  558. package/src/Tree/hooks/index.ts +3 -0
  559. package/src/Tree/hooks/useTreeItemKeyboard.ts +86 -0
  560. package/src/Tree/hooks/useTreePath.ts +68 -0
  561. package/src/Tree/hooks/useTreeRoot.ts +279 -0
  562. package/src/Tree/index.ts +3 -0
  563. package/src/Tree/types.ts +224 -0
  564. package/src/Tree/utils.ts +59 -0
  565. package/src/VisuallyHidden/README.md +58 -0
  566. package/src/VisuallyHidden/VisuallyHidden.tsx +67 -0
  567. package/src/VisuallyHidden/__tests__/VisuallyHidden.test.tsx +59 -0
  568. package/src/VisuallyHidden/index.ts +2 -0
  569. package/src/VisuallyHidden/types.ts +5 -0
  570. package/src/hooks/index.ts +3 -0
  571. package/src/hooks/useCollection.ts +74 -0
  572. package/src/hooks/useControllableState.ts +81 -0
  573. package/src/hooks/useRovingTabindex.ts +178 -0
  574. package/src/index.ts +38 -0
  575. package/src/test/intersectionObserverPolyfill.ts +83 -0
  576. package/src/test/popoverPolyfill.ts +86 -0
  577. package/src/test/scrollPolyfill.ts +23 -0
  578. package/src/types.ts +13 -0
  579. package/src/utils/__tests__/createStrictContext.test.tsx +69 -0
  580. package/src/utils/__tests__/deriveId.test.ts +28 -0
  581. package/src/utils/__tests__/getKeyToActionMap.test.ts +106 -0
  582. package/src/utils/createStrictContext.ts +49 -0
  583. package/src/utils/deriveId.ts +31 -0
  584. package/src/utils/getKeyToActionMap.ts +95 -0
  585. package/src/utils/index.ts +3 -0
@@ -0,0 +1,736 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import userEvent from "@testing-library/user-event";
3
+
4
+ import { Accordion } from "../Accordion";
5
+
6
+ describe("Accordion keyboard interaction tests", () => {
7
+ it('should expand a closed accordion item panel when focus is on the accordion trigger and the "Enter" key is pressed', async () => {
8
+ // Arrange
9
+ const user = userEvent.setup();
10
+ const title1 = "Accordion Trigger 1";
11
+ const content1 = "Accordion Content 1";
12
+ const title2 = "Accordion Trigger 2";
13
+ const content2 = "Accordion Content 2";
14
+ render(
15
+ <Accordion.Root>
16
+ <Accordion.Item>
17
+ <Accordion.Header>
18
+ <Accordion.Trigger>{title1}</Accordion.Trigger>
19
+ </Accordion.Header>
20
+ <Accordion.Content>{content1}</Accordion.Content>
21
+ </Accordion.Item>
22
+ <Accordion.Item>
23
+ <Accordion.Header>
24
+ <Accordion.Trigger>{title2}</Accordion.Trigger>
25
+ </Accordion.Header>
26
+ <Accordion.Content>{content2}</Accordion.Content>
27
+ </Accordion.Item>
28
+ </Accordion.Root>,
29
+ );
30
+ const accordionItemPanel1 = screen.getByText(content1);
31
+
32
+ // Act
33
+ await user.tab();
34
+ await user.keyboard("[Enter]");
35
+
36
+ // Assert
37
+ expect(accordionItemPanel1).toBeVisible();
38
+ });
39
+
40
+ it('should expand a closed accordion item panel when focus is on the accordion trigger and the "Space" key is pressed', async () => {
41
+ // Arrange
42
+ const user = userEvent.setup();
43
+ const title1 = "Accordion Trigger 1";
44
+ const content1 = "Accordion Content 1";
45
+ const title2 = "Accordion Trigger 2";
46
+ const content2 = "Accordion Content 2";
47
+ render(
48
+ <Accordion.Root>
49
+ <Accordion.Item>
50
+ <Accordion.Header>
51
+ <Accordion.Trigger>{title1}</Accordion.Trigger>
52
+ </Accordion.Header>
53
+ <Accordion.Content>{content1}</Accordion.Content>
54
+ </Accordion.Item>
55
+ <Accordion.Item>
56
+ <Accordion.Header>
57
+ <Accordion.Trigger>{title2}</Accordion.Trigger>
58
+ </Accordion.Header>
59
+ <Accordion.Content>{content2}</Accordion.Content>
60
+ </Accordion.Item>
61
+ </Accordion.Root>,
62
+ );
63
+ const accordionItemPanel1 = screen.getByText(content1);
64
+
65
+ // Act
66
+ await user.tab();
67
+ await user.keyboard("[Space]");
68
+
69
+ // Assert
70
+ expect(accordionItemPanel1).toBeVisible();
71
+ });
72
+
73
+ it('should collapse an existing expanded accordion item panel when focus is on the accordion trigger and the "Enter" key is pressed', async () => {
74
+ // Arrange
75
+ const user = userEvent.setup();
76
+ const title1 = "Accordion Trigger 1";
77
+ const content1 = "Accordion Content 1";
78
+ const title2 = "Accordion Trigger 2";
79
+ const content2 = "Accordion Content 2";
80
+ const title3 = "Accordion Trigger 3";
81
+ const content3 = "Accordion Content 3";
82
+ render(
83
+ <Accordion.Root>
84
+ <Accordion.Item>
85
+ <Accordion.Header>
86
+ <Accordion.Trigger>{title1}</Accordion.Trigger>
87
+ </Accordion.Header>
88
+ <Accordion.Content>{content1}</Accordion.Content>
89
+ </Accordion.Item>
90
+ <Accordion.Item>
91
+ <Accordion.Header>
92
+ <Accordion.Trigger>{title2}</Accordion.Trigger>
93
+ </Accordion.Header>
94
+ <Accordion.Content>{content2}</Accordion.Content>
95
+ </Accordion.Item>
96
+ <Accordion.Item>
97
+ <Accordion.Header>
98
+ <Accordion.Trigger>{title3}</Accordion.Trigger>
99
+ </Accordion.Header>
100
+ <Accordion.Content>{content3}</Accordion.Content>
101
+ </Accordion.Item>
102
+ </Accordion.Root>,
103
+ );
104
+ const accordionItemPanel3 = screen.getByText(content3);
105
+
106
+ // Act - Tab to item 3 and expand it:
107
+ await user.tab();
108
+ await user.tab();
109
+ await user.tab();
110
+ await user.keyboard("[Enter]");
111
+
112
+ // Tab back to item 1 and expand it (should close item 3 in single mode):
113
+ await user.tab({ shift: true });
114
+ await user.tab({ shift: true });
115
+ await user.keyboard("[Enter]");
116
+
117
+ // Assert - Panel 3 should be closed because we're in single mode
118
+ expect(accordionItemPanel3).not.toBeVisible();
119
+ });
120
+
121
+ it('should collapse an existing expanded accordion item panel when focus is on the accordion trigger and the "Space" key is pressed', async () => {
122
+ // Arrange
123
+ const user = userEvent.setup();
124
+ const title1 = "Accordion Trigger 1";
125
+ const content1 = "Accordion Content 1";
126
+ const title2 = "Accordion Trigger 2";
127
+ const content2 = "Accordion Content 2";
128
+ const title3 = "Accordion Trigger 3";
129
+ const content3 = "Accordion Content 3";
130
+ render(
131
+ <Accordion.Root>
132
+ <Accordion.Item>
133
+ <Accordion.Header>
134
+ <Accordion.Trigger>{title1}</Accordion.Trigger>
135
+ </Accordion.Header>
136
+ <Accordion.Content>{content1}</Accordion.Content>
137
+ </Accordion.Item>
138
+ <Accordion.Item>
139
+ <Accordion.Header>
140
+ <Accordion.Trigger>{title2}</Accordion.Trigger>
141
+ </Accordion.Header>
142
+ <Accordion.Content>{content2}</Accordion.Content>
143
+ </Accordion.Item>
144
+ <Accordion.Item>
145
+ <Accordion.Header>
146
+ <Accordion.Trigger>{title3}</Accordion.Trigger>
147
+ </Accordion.Header>
148
+ <Accordion.Content>{content3}</Accordion.Content>
149
+ </Accordion.Item>
150
+ </Accordion.Root>,
151
+ );
152
+ const accordionItemPanel3 = screen.getByText(content3);
153
+
154
+ // Act - Tab to item 3 and expand it:
155
+ await user.tab();
156
+ await user.tab();
157
+ await user.tab();
158
+ await user.keyboard("[Space]");
159
+
160
+ // Tab back to item 1 and expand it (should close item 3 in single mode):
161
+ await user.tab({ shift: true });
162
+ await user.tab({ shift: true });
163
+ await user.keyboard("[Space]");
164
+
165
+ // Assert - Panel 3 should be closed because we're in single mode
166
+ expect(accordionItemPanel3).not.toBeVisible();
167
+ });
168
+
169
+ it('should move focus to the next accordion trigger when focus is on an accordion trigger and the "ArrowDown" key is pressed', async () => {
170
+ // Arrange
171
+ const user = userEvent.setup();
172
+ const title1 = "Accordion Trigger 1";
173
+ const title2 = "Accordion Trigger 2";
174
+ const title3 = "Accordion Trigger 3";
175
+ render(
176
+ <Accordion.Root>
177
+ <Accordion.Item>
178
+ <Accordion.Header>
179
+ <Accordion.Trigger>{title1}</Accordion.Trigger>
180
+ </Accordion.Header>
181
+ <Accordion.Content>Content</Accordion.Content>
182
+ </Accordion.Item>
183
+ <Accordion.Item>
184
+ <Accordion.Header>
185
+ <Accordion.Trigger>{title2}</Accordion.Trigger>
186
+ </Accordion.Header>
187
+ <Accordion.Content>Content</Accordion.Content>
188
+ </Accordion.Item>
189
+ <Accordion.Item>
190
+ <Accordion.Header>
191
+ <Accordion.Trigger>{title3}</Accordion.Trigger>
192
+ </Accordion.Header>
193
+ <Accordion.Content>Content</Accordion.Content>
194
+ </Accordion.Item>
195
+ </Accordion.Root>,
196
+ );
197
+ const accordionItemTrigger2 = screen.getByRole("button", {
198
+ name: title2,
199
+ });
200
+
201
+ // Act
202
+ await user.tab();
203
+ await user.keyboard("[ArrowDown]");
204
+
205
+ // Assert
206
+ expect(accordionItemTrigger2).toHaveFocus();
207
+ });
208
+
209
+ it('should move focus from the second to the third trigger when "ArrowDown" is pressed', async () => {
210
+ // Arrange
211
+ const user = userEvent.setup();
212
+ const title1 = "Accordion Trigger 1";
213
+ const title2 = "Accordion Trigger 2";
214
+ const title3 = "Accordion Trigger 3";
215
+ render(
216
+ <Accordion.Root>
217
+ <Accordion.Item>
218
+ <Accordion.Header>
219
+ <Accordion.Trigger>{title1}</Accordion.Trigger>
220
+ </Accordion.Header>
221
+ <Accordion.Content>Content</Accordion.Content>
222
+ </Accordion.Item>
223
+ <Accordion.Item>
224
+ <Accordion.Header>
225
+ <Accordion.Trigger>{title2}</Accordion.Trigger>
226
+ </Accordion.Header>
227
+ <Accordion.Content>Content</Accordion.Content>
228
+ </Accordion.Item>
229
+ <Accordion.Item>
230
+ <Accordion.Header>
231
+ <Accordion.Trigger>{title3}</Accordion.Trigger>
232
+ </Accordion.Header>
233
+ <Accordion.Content>Content</Accordion.Content>
234
+ </Accordion.Item>
235
+ </Accordion.Root>,
236
+ );
237
+ const accordionItemTrigger3 = screen.getByRole("button", {
238
+ name: title3,
239
+ });
240
+
241
+ // Act - Tab to second trigger, then press ArrowDown
242
+ await user.tab();
243
+ await user.tab();
244
+ await user.keyboard("[ArrowDown]");
245
+
246
+ // Assert
247
+ expect(accordionItemTrigger3).toHaveFocus();
248
+ });
249
+
250
+ it("should wrap to first item when pressing ArrowDown from last item", async () => {
251
+ // Arrange
252
+ const user = userEvent.setup();
253
+ const title1 = "Accordion Trigger 1";
254
+ const title2 = "Accordion Trigger 2";
255
+ const title3 = "Accordion Trigger 3";
256
+ render(
257
+ <Accordion.Root>
258
+ <Accordion.Item>
259
+ <Accordion.Header>
260
+ <Accordion.Trigger>{title1}</Accordion.Trigger>
261
+ </Accordion.Header>
262
+ <Accordion.Content>Content</Accordion.Content>
263
+ </Accordion.Item>
264
+ <Accordion.Item>
265
+ <Accordion.Header>
266
+ <Accordion.Trigger>{title2}</Accordion.Trigger>
267
+ </Accordion.Header>
268
+ <Accordion.Content>Content</Accordion.Content>
269
+ </Accordion.Item>
270
+ <Accordion.Item>
271
+ <Accordion.Header>
272
+ <Accordion.Trigger>{title3}</Accordion.Trigger>
273
+ </Accordion.Header>
274
+ <Accordion.Content>Content</Accordion.Content>
275
+ </Accordion.Item>
276
+ </Accordion.Root>,
277
+ );
278
+ const accordionItemTrigger1 = screen.getByRole("button", {
279
+ name: title1,
280
+ });
281
+ const accordionItemTrigger3 = screen.getByRole("button", {
282
+ name: title3,
283
+ });
284
+
285
+ // Act - Tab to last trigger, then press ArrowDown to wrap to first
286
+ await user.tab();
287
+ await user.tab();
288
+ await user.tab();
289
+ expect(accordionItemTrigger3).toHaveFocus();
290
+
291
+ await user.keyboard("[ArrowDown]");
292
+
293
+ // Assert - Should wrap around to the first item
294
+ expect(accordionItemTrigger1).toHaveFocus();
295
+ });
296
+
297
+ it('should move focus to the previous accordion trigger when focus is on an accordion trigger and the "ArrowUp" key is pressed', async () => {
298
+ // Arrange
299
+ const user = userEvent.setup();
300
+ const title1 = "Accordion Trigger 1";
301
+ const title2 = "Accordion Trigger 2";
302
+ const title3 = "Accordion Trigger 3";
303
+ render(
304
+ <Accordion.Root>
305
+ <Accordion.Item>
306
+ <Accordion.Header>
307
+ <Accordion.Trigger>{title1}</Accordion.Trigger>
308
+ </Accordion.Header>
309
+ <Accordion.Content>Content</Accordion.Content>
310
+ </Accordion.Item>
311
+ <Accordion.Item>
312
+ <Accordion.Header>
313
+ <Accordion.Trigger>{title2}</Accordion.Trigger>
314
+ </Accordion.Header>
315
+ <Accordion.Content>Content</Accordion.Content>
316
+ </Accordion.Item>
317
+ <Accordion.Item>
318
+ <Accordion.Header>
319
+ <Accordion.Trigger>{title3}</Accordion.Trigger>
320
+ </Accordion.Header>
321
+ <Accordion.Content>Content</Accordion.Content>
322
+ </Accordion.Item>
323
+ </Accordion.Root>,
324
+ );
325
+ const accordionItemTrigger1 = screen.getByRole("button", {
326
+ name: title1,
327
+ });
328
+
329
+ // Act - Tab to second trigger, then press ArrowUp
330
+ await user.tab();
331
+ await user.tab();
332
+ await user.keyboard("[ArrowUp]");
333
+
334
+ // Assert
335
+ expect(accordionItemTrigger1).toHaveFocus();
336
+ });
337
+
338
+ it('should move focus from the third to the second trigger when "ArrowUp" is pressed', async () => {
339
+ // Arrange
340
+ const user = userEvent.setup();
341
+ const title1 = "Accordion Trigger 1";
342
+ const title2 = "Accordion Trigger 2";
343
+ const title3 = "Accordion Trigger 3";
344
+ render(
345
+ <Accordion.Root>
346
+ <Accordion.Item>
347
+ <Accordion.Header>
348
+ <Accordion.Trigger>{title1}</Accordion.Trigger>
349
+ </Accordion.Header>
350
+ <Accordion.Content>Content</Accordion.Content>
351
+ </Accordion.Item>
352
+ <Accordion.Item>
353
+ <Accordion.Header>
354
+ <Accordion.Trigger>{title2}</Accordion.Trigger>
355
+ </Accordion.Header>
356
+ <Accordion.Content>Content</Accordion.Content>
357
+ </Accordion.Item>
358
+ <Accordion.Item>
359
+ <Accordion.Header>
360
+ <Accordion.Trigger>{title3}</Accordion.Trigger>
361
+ </Accordion.Header>
362
+ <Accordion.Content>Content</Accordion.Content>
363
+ </Accordion.Item>
364
+ </Accordion.Root>,
365
+ );
366
+ const accordionItemTrigger2 = screen.getByRole("button", {
367
+ name: title2,
368
+ });
369
+
370
+ // Act - Tab to third trigger, then press ArrowUp
371
+ await user.tab();
372
+ await user.tab();
373
+ await user.tab();
374
+ await user.keyboard("[ArrowUp]");
375
+
376
+ // Assert
377
+ expect(accordionItemTrigger2).toHaveFocus();
378
+ });
379
+
380
+ it("should wrap to last item when pressing ArrowUp from first item", async () => {
381
+ // Arrange
382
+ const user = userEvent.setup();
383
+ const title1 = "Accordion Trigger 1";
384
+ const title2 = "Accordion Trigger 2";
385
+ const title3 = "Accordion Trigger 3";
386
+ render(
387
+ <Accordion.Root>
388
+ <Accordion.Item>
389
+ <Accordion.Header>
390
+ <Accordion.Trigger>{title1}</Accordion.Trigger>
391
+ </Accordion.Header>
392
+ <Accordion.Content>Content</Accordion.Content>
393
+ </Accordion.Item>
394
+ <Accordion.Item>
395
+ <Accordion.Header>
396
+ <Accordion.Trigger>{title2}</Accordion.Trigger>
397
+ </Accordion.Header>
398
+ <Accordion.Content>Content</Accordion.Content>
399
+ </Accordion.Item>
400
+ <Accordion.Item>
401
+ <Accordion.Header>
402
+ <Accordion.Trigger>{title3}</Accordion.Trigger>
403
+ </Accordion.Header>
404
+ <Accordion.Content>Content</Accordion.Content>
405
+ </Accordion.Item>
406
+ </Accordion.Root>,
407
+ );
408
+ const accordionItemTrigger1 = screen.getByRole("button", {
409
+ name: title1,
410
+ });
411
+ const accordionItemTrigger3 = screen.getByRole("button", {
412
+ name: title3,
413
+ });
414
+
415
+ // Act - Tab to first trigger, then press ArrowUp to wrap to last
416
+ await user.tab();
417
+ expect(accordionItemTrigger1).toHaveFocus();
418
+
419
+ await user.keyboard("[ArrowUp]");
420
+
421
+ // Assert - Should wrap around to the last item
422
+ expect(accordionItemTrigger3).toHaveFocus();
423
+ });
424
+
425
+ it('should move focus to the first accordion trigger when focus is on an accordion trigger and the "Home" key is pressed', async () => {
426
+ // Arrange
427
+ const user = userEvent.setup();
428
+ const title1 = "Accordion Trigger 1";
429
+ const title2 = "Accordion Trigger 2";
430
+ const title3 = "Accordion Trigger 3";
431
+ render(
432
+ <Accordion.Root>
433
+ <Accordion.Item>
434
+ <Accordion.Header>
435
+ <Accordion.Trigger>{title1}</Accordion.Trigger>
436
+ </Accordion.Header>
437
+ <Accordion.Content>Content</Accordion.Content>
438
+ </Accordion.Item>
439
+ <Accordion.Item>
440
+ <Accordion.Header>
441
+ <Accordion.Trigger>{title2}</Accordion.Trigger>
442
+ </Accordion.Header>
443
+ <Accordion.Content>Content</Accordion.Content>
444
+ </Accordion.Item>
445
+ <Accordion.Item>
446
+ <Accordion.Header>
447
+ <Accordion.Trigger>{title3}</Accordion.Trigger>
448
+ </Accordion.Header>
449
+ <Accordion.Content>Content</Accordion.Content>
450
+ </Accordion.Item>
451
+ </Accordion.Root>,
452
+ );
453
+ const accordionItemTrigger1 = screen.getByRole("button", {
454
+ name: title1,
455
+ });
456
+
457
+ // Act - Tab to third trigger, then press Home
458
+ await user.tab();
459
+ await user.tab();
460
+ await user.tab();
461
+ await user.keyboard("[Home]");
462
+
463
+ // Assert
464
+ expect(accordionItemTrigger1).toHaveFocus();
465
+ });
466
+
467
+ it('should move focus to the last accordion trigger when focus is on an accordion trigger and the "End" key is pressed', async () => {
468
+ // Arrange
469
+ const user = userEvent.setup();
470
+ const title1 = "Accordion Trigger 1";
471
+ const title2 = "Accordion Trigger 2";
472
+ const title3 = "Accordion Trigger 3";
473
+ render(
474
+ <Accordion.Root>
475
+ <Accordion.Item>
476
+ <Accordion.Header>
477
+ <Accordion.Trigger>{title1}</Accordion.Trigger>
478
+ </Accordion.Header>
479
+ <Accordion.Content>Content</Accordion.Content>
480
+ </Accordion.Item>
481
+ <Accordion.Item>
482
+ <Accordion.Header>
483
+ <Accordion.Trigger>{title2}</Accordion.Trigger>
484
+ </Accordion.Header>
485
+ <Accordion.Content>Content</Accordion.Content>
486
+ </Accordion.Item>
487
+ <Accordion.Item>
488
+ <Accordion.Header>
489
+ <Accordion.Trigger>{title3}</Accordion.Trigger>
490
+ </Accordion.Header>
491
+ <Accordion.Content>Content</Accordion.Content>
492
+ </Accordion.Item>
493
+ </Accordion.Root>,
494
+ );
495
+ const accordionItemTrigger3 = screen.getByRole("button", {
496
+ name: title3,
497
+ });
498
+
499
+ // Act - Tab to first trigger, then press End
500
+ await user.tab();
501
+ await user.keyboard("[End]");
502
+
503
+ // Assert
504
+ expect(accordionItemTrigger3).toHaveFocus();
505
+ });
506
+
507
+ it('should move focus to the next trigger using "ArrowRight" when orientation="horizontal"', async () => {
508
+ // Arrange
509
+ const user = userEvent.setup();
510
+ const title1 = "Accordion Trigger 1";
511
+ const title2 = "Accordion Trigger 2";
512
+ const title3 = "Accordion Trigger 3";
513
+ render(
514
+ <Accordion.Root orientation="horizontal">
515
+ <Accordion.Item>
516
+ <Accordion.Header>
517
+ <Accordion.Trigger>{title1}</Accordion.Trigger>
518
+ </Accordion.Header>
519
+ <Accordion.Content>Content</Accordion.Content>
520
+ </Accordion.Item>
521
+ <Accordion.Item>
522
+ <Accordion.Header>
523
+ <Accordion.Trigger>{title2}</Accordion.Trigger>
524
+ </Accordion.Header>
525
+ <Accordion.Content>Content</Accordion.Content>
526
+ </Accordion.Item>
527
+ <Accordion.Item>
528
+ <Accordion.Header>
529
+ <Accordion.Trigger>{title3}</Accordion.Trigger>
530
+ </Accordion.Header>
531
+ <Accordion.Content>Content</Accordion.Content>
532
+ </Accordion.Item>
533
+ </Accordion.Root>,
534
+ );
535
+ const accordionItemTrigger2 = screen.getByRole("button", {
536
+ name: title2,
537
+ });
538
+
539
+ // Act
540
+ await user.tab();
541
+ await user.keyboard("[ArrowRight]");
542
+
543
+ // Assert
544
+ expect(accordionItemTrigger2).toHaveFocus();
545
+ });
546
+
547
+ it('should move focus to the previous trigger using "ArrowLeft" when orientation="horizontal"', async () => {
548
+ // Arrange
549
+ const user = userEvent.setup();
550
+ const title1 = "Accordion Trigger 1";
551
+ const title2 = "Accordion Trigger 2";
552
+ const title3 = "Accordion Trigger 3";
553
+ render(
554
+ <Accordion.Root orientation="horizontal">
555
+ <Accordion.Item>
556
+ <Accordion.Header>
557
+ <Accordion.Trigger>{title1}</Accordion.Trigger>
558
+ </Accordion.Header>
559
+ <Accordion.Content>Content</Accordion.Content>
560
+ </Accordion.Item>
561
+ <Accordion.Item>
562
+ <Accordion.Header>
563
+ <Accordion.Trigger>{title2}</Accordion.Trigger>
564
+ </Accordion.Header>
565
+ <Accordion.Content>Content</Accordion.Content>
566
+ </Accordion.Item>
567
+ <Accordion.Item>
568
+ <Accordion.Header>
569
+ <Accordion.Trigger>{title3}</Accordion.Trigger>
570
+ </Accordion.Header>
571
+ <Accordion.Content>Content</Accordion.Content>
572
+ </Accordion.Item>
573
+ </Accordion.Root>,
574
+ );
575
+ const accordionItemTrigger1 = screen.getByRole("button", {
576
+ name: title1,
577
+ });
578
+
579
+ // Act - Tab to second trigger, then press ArrowLeft
580
+ await user.tab();
581
+ await user.tab();
582
+ await user.keyboard("[ArrowLeft]");
583
+
584
+ // Assert
585
+ expect(accordionItemTrigger1).toHaveFocus();
586
+ });
587
+
588
+ it('should wrap to the first trigger using "ArrowRight" from the last trigger when orientation="horizontal"', async () => {
589
+ // Arrange
590
+ const user = userEvent.setup();
591
+ const title1 = "Accordion Trigger 1";
592
+ const title2 = "Accordion Trigger 2";
593
+ const title3 = "Accordion Trigger 3";
594
+ render(
595
+ <Accordion.Root orientation="horizontal">
596
+ <Accordion.Item>
597
+ <Accordion.Header>
598
+ <Accordion.Trigger>{title1}</Accordion.Trigger>
599
+ </Accordion.Header>
600
+ <Accordion.Content>Content</Accordion.Content>
601
+ </Accordion.Item>
602
+ <Accordion.Item>
603
+ <Accordion.Header>
604
+ <Accordion.Trigger>{title2}</Accordion.Trigger>
605
+ </Accordion.Header>
606
+ <Accordion.Content>Content</Accordion.Content>
607
+ </Accordion.Item>
608
+ <Accordion.Item>
609
+ <Accordion.Header>
610
+ <Accordion.Trigger>{title3}</Accordion.Trigger>
611
+ </Accordion.Header>
612
+ <Accordion.Content>Content</Accordion.Content>
613
+ </Accordion.Item>
614
+ </Accordion.Root>,
615
+ );
616
+ const accordionItemTrigger1 = screen.getByRole("button", {
617
+ name: title1,
618
+ });
619
+
620
+ // Act - Tab to last trigger, then press ArrowRight to wrap
621
+ await user.tab();
622
+ await user.tab();
623
+ await user.tab();
624
+ await user.keyboard("[ArrowRight]");
625
+
626
+ // Assert
627
+ expect(accordionItemTrigger1).toHaveFocus();
628
+ });
629
+
630
+ it('should wrap to the last trigger using "ArrowLeft" from the first trigger when orientation="horizontal"', async () => {
631
+ // Arrange
632
+ const user = userEvent.setup();
633
+ const title1 = "Accordion Trigger 1";
634
+ const title2 = "Accordion Trigger 2";
635
+ const title3 = "Accordion Trigger 3";
636
+ render(
637
+ <Accordion.Root orientation="horizontal">
638
+ <Accordion.Item>
639
+ <Accordion.Header>
640
+ <Accordion.Trigger>{title1}</Accordion.Trigger>
641
+ </Accordion.Header>
642
+ <Accordion.Content>Content</Accordion.Content>
643
+ </Accordion.Item>
644
+ <Accordion.Item>
645
+ <Accordion.Header>
646
+ <Accordion.Trigger>{title2}</Accordion.Trigger>
647
+ </Accordion.Header>
648
+ <Accordion.Content>Content</Accordion.Content>
649
+ </Accordion.Item>
650
+ <Accordion.Item>
651
+ <Accordion.Header>
652
+ <Accordion.Trigger>{title3}</Accordion.Trigger>
653
+ </Accordion.Header>
654
+ <Accordion.Content>Content</Accordion.Content>
655
+ </Accordion.Item>
656
+ </Accordion.Root>,
657
+ );
658
+ const accordionItemTrigger3 = screen.getByRole("button", {
659
+ name: title3,
660
+ });
661
+
662
+ // Act - Tab to first trigger, then press ArrowLeft to wrap
663
+ await user.tab();
664
+ await user.keyboard("[ArrowLeft]");
665
+
666
+ // Assert
667
+ expect(accordionItemTrigger3).toHaveFocus();
668
+ });
669
+
670
+ it('should NOT move focus when pressing "ArrowDown" while orientation="horizontal"', async () => {
671
+ // Arrange
672
+ const user = userEvent.setup();
673
+ const title1 = "Accordion Trigger 1";
674
+ const title2 = "Accordion Trigger 2";
675
+ render(
676
+ <Accordion.Root orientation="horizontal">
677
+ <Accordion.Item>
678
+ <Accordion.Header>
679
+ <Accordion.Trigger>{title1}</Accordion.Trigger>
680
+ </Accordion.Header>
681
+ <Accordion.Content>Content</Accordion.Content>
682
+ </Accordion.Item>
683
+ <Accordion.Item>
684
+ <Accordion.Header>
685
+ <Accordion.Trigger>{title2}</Accordion.Trigger>
686
+ </Accordion.Header>
687
+ <Accordion.Content>Content</Accordion.Content>
688
+ </Accordion.Item>
689
+ </Accordion.Root>,
690
+ );
691
+ const accordionItemTrigger1 = screen.getByRole("button", {
692
+ name: title1,
693
+ });
694
+
695
+ // Act
696
+ await user.tab();
697
+ await user.keyboard("[ArrowDown]");
698
+
699
+ // Assert - focus should remain on trigger 1
700
+ expect(accordionItemTrigger1).toHaveFocus();
701
+ });
702
+
703
+ it('should NOT move focus when pressing "ArrowUp" while orientation="horizontal"', async () => {
704
+ // Arrange
705
+ const user = userEvent.setup();
706
+ const title1 = "Accordion Trigger 1";
707
+ const title2 = "Accordion Trigger 2";
708
+ render(
709
+ <Accordion.Root orientation="horizontal">
710
+ <Accordion.Item>
711
+ <Accordion.Header>
712
+ <Accordion.Trigger>{title1}</Accordion.Trigger>
713
+ </Accordion.Header>
714
+ <Accordion.Content>Content</Accordion.Content>
715
+ </Accordion.Item>
716
+ <Accordion.Item>
717
+ <Accordion.Header>
718
+ <Accordion.Trigger>{title2}</Accordion.Trigger>
719
+ </Accordion.Header>
720
+ <Accordion.Content>Content</Accordion.Content>
721
+ </Accordion.Item>
722
+ </Accordion.Root>,
723
+ );
724
+ const accordionItemTrigger2 = screen.getByRole("button", {
725
+ name: title2,
726
+ });
727
+
728
+ // Act - Tab to second trigger, then press ArrowUp (should be ignored in horizontal)
729
+ await user.tab();
730
+ await user.tab();
731
+ await user.keyboard("[ArrowUp]");
732
+
733
+ // Assert - focus should remain on trigger 2
734
+ expect(accordionItemTrigger2).toHaveFocus();
735
+ });
736
+ });