@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,91 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import userEvent from "@testing-library/user-event";
3
+ import { createRef } from "react";
4
+
5
+ import { Tabs } from "..";
6
+
7
+ describe("Tabs.Trigger asChild", () => {
8
+ it("renders the child element instead of a <button> when asChild is true", () => {
9
+ render(
10
+ <Tabs.Root defaultValue="tab1">
11
+ <Tabs.List label="Test tabs">
12
+ <Tabs.Trigger asChild value="tab1">
13
+ <a href="/tab1">Tab 1</a>
14
+ </Tabs.Trigger>
15
+ <Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
16
+ </Tabs.List>
17
+ <Tabs.Content value="tab1">Content 1</Tabs.Content>
18
+ <Tabs.Content value="tab2">Content 2</Tabs.Content>
19
+ </Tabs.Root>,
20
+ );
21
+
22
+ const trigger = screen.getByRole("tab", { name: "Tab 1" });
23
+ expect(trigger.tagName).toBe("A");
24
+ expect(trigger).toHaveAttribute("href", "/tab1");
25
+ });
26
+
27
+ it("merges ARIA attributes onto the child element", () => {
28
+ render(
29
+ <Tabs.Root defaultValue="tab1">
30
+ <Tabs.List label="Test tabs">
31
+ <Tabs.Trigger asChild value="tab1">
32
+ <a href="/tab1">Tab 1</a>
33
+ </Tabs.Trigger>
34
+ <Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
35
+ </Tabs.List>
36
+ <Tabs.Content value="tab1">Content 1</Tabs.Content>
37
+ <Tabs.Content value="tab2">Content 2</Tabs.Content>
38
+ </Tabs.Root>,
39
+ );
40
+
41
+ const trigger = screen.getByRole("tab", { name: "Tab 1" });
42
+ expect(trigger).toHaveAttribute("role", "tab");
43
+ expect(trigger).toHaveAttribute("aria-selected", "true");
44
+ expect(trigger).toHaveAttribute("aria-controls");
45
+ expect(trigger).toHaveAttribute("tabindex", "0");
46
+ });
47
+
48
+ it("composes child onClick with tab activation", async () => {
49
+ const user = userEvent.setup();
50
+ const childClick = vi.fn();
51
+
52
+ render(
53
+ <Tabs.Root defaultValue="tab1">
54
+ <Tabs.List label="Test tabs">
55
+ <Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
56
+ <Tabs.Trigger asChild value="tab2">
57
+ <a href="/tab2" onClick={childClick}>
58
+ Tab 2
59
+ </a>
60
+ </Tabs.Trigger>
61
+ </Tabs.List>
62
+ <Tabs.Content value="tab1">Content 1</Tabs.Content>
63
+ <Tabs.Content value="tab2">Content 2</Tabs.Content>
64
+ </Tabs.Root>,
65
+ );
66
+
67
+ await user.click(screen.getByRole("tab", { name: "Tab 2" }));
68
+
69
+ expect(childClick).toHaveBeenCalledTimes(1);
70
+ expect(screen.getByRole("tab", { name: "Tab 2" })).toHaveAttribute(
71
+ "aria-selected",
72
+ "true",
73
+ );
74
+ });
75
+
76
+ it("forwards ref to the child DOM element", () => {
77
+ const ref = createRef<HTMLAnchorElement>();
78
+ render(
79
+ <Tabs.Root defaultValue="tab1">
80
+ <Tabs.List label="Test tabs">
81
+ <Tabs.Trigger asChild value="tab1" ref={ref}>
82
+ <a href="/tab1">Tab 1</a>
83
+ </Tabs.Trigger>
84
+ </Tabs.List>
85
+ <Tabs.Content value="tab1">Content 1</Tabs.Content>
86
+ </Tabs.Root>,
87
+ );
88
+ expect(ref.current).not.toBeNull();
89
+ expect(ref.current?.tagName).toBe("A");
90
+ });
91
+ });
@@ -0,0 +1,483 @@
1
+ import { render, screen } from "@testing-library/react";
2
+
3
+ import { Tabs, TabsOrientation } from "..";
4
+
5
+ describe("Tabs basic rendering tests", () => {
6
+ describe("Tabs.Root", () => {
7
+ it("should render the Tabs.Root component", () => {
8
+ // Arrange
9
+ render(<Tabs.Root data-testid="tabs-root" />);
10
+ const tabsRoot = screen.getByTestId("tabs-root");
11
+
12
+ // Assert
13
+ expect(tabsRoot).toBeVisible();
14
+ });
15
+
16
+ it("should accept a className prop", () => {
17
+ // Arrange
18
+ const testClass = "custom-tabs-root";
19
+ render(<Tabs.Root className={testClass} data-testid="tabs-root" />);
20
+ const tabsRoot = screen.getByTestId("tabs-root");
21
+
22
+ // Assert
23
+ expect(tabsRoot).toHaveAttribute("class", testClass);
24
+ });
25
+
26
+ it("should apply a className of empty string by default when not specified", () => {
27
+ // Arrange
28
+ render(<Tabs.Root data-testid="tabs-root" />);
29
+ const tabsRoot = screen.getByTestId("tabs-root");
30
+
31
+ // Assert
32
+ expect(tabsRoot).toHaveAttribute("class", "");
33
+ });
34
+
35
+ it('should apply a data-orientation attribute of "horizontal" by default when not specified', () => {
36
+ // Arrange
37
+ render(<Tabs.Root data-testid="tabs-root" />);
38
+ const tabsRoot = screen.getByTestId("tabs-root");
39
+
40
+ // Assert
41
+ expect(tabsRoot).toHaveAttribute("data-orientation", "horizontal");
42
+ });
43
+
44
+ it.each(["horizontal", "vertical"] as TabsOrientation[])(
45
+ "should accept an orientation prop",
46
+ (orientation) => {
47
+ // Arrange
48
+ render(<Tabs.Root orientation={orientation} data-testid="tabs-root" />);
49
+ const tabsRoot = screen.getByTestId("tabs-root");
50
+
51
+ // Assert
52
+ expect(tabsRoot).toHaveAttribute("data-orientation", orientation);
53
+ },
54
+ );
55
+ });
56
+
57
+ describe("Tabs.List", () => {
58
+ it("should render the Tabs.List component", () => {
59
+ // Arrange
60
+ render(
61
+ <Tabs.Root>
62
+ <Tabs.List label="test label" />
63
+ </Tabs.Root>,
64
+ );
65
+ const tabList = screen.getByRole("tablist");
66
+
67
+ expect(tabList).toBeVisible();
68
+ });
69
+
70
+ it("should require a label prop when aria-labelledby is not provided", () => {
71
+ // Arrange
72
+ const testLabel = "test label";
73
+ render(
74
+ <Tabs.Root>
75
+ <Tabs.List label={testLabel} />
76
+ </Tabs.Root>,
77
+ );
78
+ const tabList = screen.getByLabelText(testLabel);
79
+
80
+ expect(tabList).toBeVisible();
81
+ });
82
+
83
+ it("should require an aria-labelledby prop when label is not provided", () => {
84
+ // Arrange
85
+ const testLabel = "test label";
86
+ const testLabelId = "#test-label";
87
+ render(
88
+ <>
89
+ <h2 id={testLabelId}>{testLabel}</h2>
90
+ <Tabs.Root>
91
+ <Tabs.List ariaLabelledBy={testLabelId} />
92
+ </Tabs.Root>
93
+ ,
94
+ </>,
95
+ );
96
+ const tabList = screen.getByLabelText(testLabel);
97
+
98
+ expect(tabList).toHaveAttribute("aria-labelledby", "#test-label");
99
+ });
100
+
101
+ it("should accept a className prop", () => {
102
+ // Arrange
103
+ const testClass = "test-class";
104
+ render(
105
+ <Tabs.Root>
106
+ <Tabs.List label="test label" className={testClass} />
107
+ </Tabs.Root>,
108
+ );
109
+ const tabList = screen.getByRole("tablist");
110
+
111
+ expect(tabList).toHaveAttribute("class", testClass);
112
+ });
113
+
114
+ it("should apply a className of empty string by default when not specified", () => {
115
+ // Arrange
116
+ render(
117
+ <Tabs.Root>
118
+ <Tabs.List label="test label" />
119
+ </Tabs.Root>,
120
+ );
121
+ const tabList = screen.getByRole("tablist");
122
+
123
+ // Assert
124
+ expect(tabList).toHaveAttribute("class", "");
125
+ });
126
+
127
+ it('should have an aria-orientation attribute of "horizontal" by default when not specified', () => {
128
+ // Arrange
129
+ render(
130
+ <Tabs.Root>
131
+ <Tabs.List label="test label" />
132
+ </Tabs.Root>,
133
+ );
134
+ const tabList = screen.getByRole("tablist");
135
+
136
+ // Assert
137
+ expect(tabList).toHaveAttribute("aria-orientation", "horizontal");
138
+ });
139
+
140
+ it.each(["horizontal", "vertical"] as TabsOrientation[])(
141
+ "should render the correct aria-orientation attribute when supplied to the Tabs.Root component",
142
+ (orientation) => {
143
+ // Arrange
144
+ render(
145
+ <Tabs.Root orientation={orientation}>
146
+ <Tabs.List label="test label" />
147
+ </Tabs.Root>,
148
+ );
149
+ const tabList = screen.getByRole("tablist");
150
+
151
+ // Assert
152
+ expect(tabList).toHaveAttribute("aria-orientation", orientation);
153
+ },
154
+ );
155
+
156
+ it('should render a data-orientation attribute of "horizontal" by default when not specified', () => {
157
+ // Arrange
158
+ render(
159
+ <Tabs.Root>
160
+ <Tabs.List label="test label" />
161
+ </Tabs.Root>,
162
+ );
163
+ const tabList = screen.getByRole("tablist");
164
+
165
+ // Assert
166
+ expect(tabList).toHaveAttribute("data-orientation", "horizontal");
167
+ });
168
+
169
+ it.each(["horizontal", "vertical"] as TabsOrientation[])(
170
+ "should render the correct data-orientation attribute when supplied to the Tabs.Root component",
171
+ (orientation) => {
172
+ // Arrange
173
+ render(
174
+ <Tabs.Root defaultValue="tab1" orientation={orientation}>
175
+ <Tabs.List label="test label">
176
+ <Tabs.Trigger value="tab1" />
177
+ </Tabs.List>
178
+ <Tabs.Content value="tab1" />
179
+ </Tabs.Root>,
180
+ );
181
+ const tabList = screen.getByRole("tablist");
182
+
183
+ // Assert
184
+ expect(tabList).toHaveAttribute("data-orientation", orientation);
185
+ },
186
+ );
187
+ });
188
+
189
+ describe("Tabs.Trigger", () => {
190
+ it("should render the Tabs.Trigger component", () => {
191
+ // Arrange
192
+ render(
193
+ <Tabs.Root>
194
+ <Tabs.List label="test label">
195
+ <Tabs.Trigger value="tab1" />
196
+ </Tabs.List>
197
+ </Tabs.Root>,
198
+ );
199
+ const tabTrigger = screen.getByRole("tab");
200
+
201
+ expect(tabTrigger).toBeVisible();
202
+ });
203
+
204
+ it('should render button with the type attribute of "button"', () => {
205
+ // Arrange
206
+ render(
207
+ <Tabs.Root>
208
+ <Tabs.List label="test label">
209
+ <Tabs.Trigger value="tab1" />
210
+ </Tabs.List>
211
+ </Tabs.Root>,
212
+ );
213
+ const tabTrigger = screen.getByRole("tab");
214
+
215
+ expect(tabTrigger).toHaveAttribute("type", "button");
216
+ });
217
+
218
+ it("should accept a className prop", () => {
219
+ // Arrange
220
+ const testClass = "test-class";
221
+ render(
222
+ <Tabs.Root>
223
+ <Tabs.List label="test label">
224
+ <Tabs.Trigger value="tab1" className={testClass} />
225
+ </Tabs.List>
226
+ </Tabs.Root>,
227
+ );
228
+ const tabTrigger = screen.getByRole("tab");
229
+
230
+ expect(tabTrigger).toHaveAttribute("class", testClass);
231
+ });
232
+
233
+ it("should apply a className of empty string by default when not specified", () => {
234
+ // Arrange
235
+ render(
236
+ <Tabs.Root>
237
+ <Tabs.List label="test label">
238
+ <Tabs.Trigger value="tab1" />
239
+ </Tabs.List>
240
+ </Tabs.Root>,
241
+ );
242
+ const tabTrigger = screen.getByRole("tab");
243
+
244
+ expect(tabTrigger).toHaveAttribute("class", "");
245
+ });
246
+
247
+ it("should apply a generated id for the tab that associates with the relevant content panel", () => {
248
+ // Arrange
249
+ render(
250
+ <Tabs.Root defaultValue="tab1">
251
+ <Tabs.List label="test label">
252
+ <Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
253
+ </Tabs.List>
254
+ <Tabs.Content value="tab1">Tab Content 1</Tabs.Content>
255
+ </Tabs.Root>,
256
+ );
257
+ const tabTrigger = screen.getByRole("tab", { name: "Tab 1" });
258
+
259
+ expect(tabTrigger).toHaveAttribute(
260
+ "id",
261
+ expect.stringContaining("-trigger-tab1"),
262
+ );
263
+ });
264
+
265
+ it("should apply a generated aria-controls for the tab that associates with the relevant content panel", () => {
266
+ // Arrange
267
+ render(
268
+ <Tabs.Root defaultValue="tab1">
269
+ <Tabs.List label="test label">
270
+ <Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
271
+ </Tabs.List>
272
+ <Tabs.Content value="tab1">Tab Content 1</Tabs.Content>
273
+ </Tabs.Root>,
274
+ );
275
+ const tabTrigger = screen.getByRole("tab", { name: "Tab 1" });
276
+
277
+ expect(tabTrigger).toHaveAttribute(
278
+ "aria-controls",
279
+ expect.stringContaining("-panel-tab1"),
280
+ );
281
+ });
282
+
283
+ it("should accept a children prop for the tab label", () => {
284
+ // Arrange
285
+ const testTriggerLabel = "Test Trigger";
286
+ render(
287
+ <Tabs.Root>
288
+ <Tabs.List label="test label">
289
+ <Tabs.Trigger value="tab1">{testTriggerLabel}</Tabs.Trigger>
290
+ </Tabs.List>
291
+ </Tabs.Root>,
292
+ );
293
+ const tabTrigger = screen.getByRole("tab", { name: testTriggerLabel });
294
+
295
+ expect(tabTrigger).toHaveTextContent(testTriggerLabel);
296
+ });
297
+
298
+ it('should render a data-orientation attribute of "horizontal" by default when not specified', () => {
299
+ // Arrange
300
+ render(
301
+ <Tabs.Root>
302
+ <Tabs.List label="test label">
303
+ <Tabs.Trigger value="tab1" />
304
+ </Tabs.List>
305
+ </Tabs.Root>,
306
+ );
307
+ const tabTrigger = screen.getByRole("tab");
308
+
309
+ // Assert
310
+ expect(tabTrigger).toHaveAttribute("data-orientation", "horizontal");
311
+ });
312
+
313
+ it.each(["horizontal", "vertical"] as TabsOrientation[])(
314
+ "should render the correct data-orientation attribute when supplied to the Tabs.Root component",
315
+ (orientation) => {
316
+ // Arrange
317
+ render(
318
+ <Tabs.Root orientation={orientation}>
319
+ <Tabs.List label="test label">
320
+ <Tabs.Trigger value="tab1" />
321
+ </Tabs.List>
322
+ </Tabs.Root>,
323
+ );
324
+ const tabTrigger = screen.getByRole("tab");
325
+
326
+ // Assert
327
+ expect(tabTrigger).toHaveAttribute("data-orientation", orientation);
328
+ },
329
+ );
330
+ });
331
+
332
+ describe("Tabs.Content", () => {
333
+ it("should render the Tabs.Content component", () => {
334
+ // Arrange
335
+ render(
336
+ <Tabs.Root defaultValue="tab1">
337
+ <Tabs.List label="test label">
338
+ <Tabs.Trigger value="tab1" />
339
+ </Tabs.List>
340
+ <Tabs.Content value="tab1" />
341
+ </Tabs.Root>,
342
+ );
343
+ const tabContent = screen.getByRole("tabpanel");
344
+
345
+ expect(tabContent).toBeVisible();
346
+ });
347
+
348
+ it("should accept a className prop", () => {
349
+ // Arrange
350
+ const testClass = "test-class";
351
+ render(
352
+ <Tabs.Root defaultValue="tab1">
353
+ <Tabs.List label="test label">
354
+ <Tabs.Trigger value="tab1" />
355
+ </Tabs.List>
356
+ <Tabs.Content value="tab1" className={testClass} />
357
+ </Tabs.Root>,
358
+ );
359
+ const tabContent = screen.getByRole("tabpanel");
360
+
361
+ expect(tabContent).toHaveAttribute("class", testClass);
362
+ });
363
+
364
+ it("should apply a className of empty string by default when not specified", () => {
365
+ // Arrange
366
+ render(
367
+ <Tabs.Root defaultValue="tab1">
368
+ <Tabs.List label="test label">
369
+ <Tabs.Trigger value="tab1" />
370
+ </Tabs.List>
371
+ <Tabs.Content value="tab1" />
372
+ </Tabs.Root>,
373
+ );
374
+ const tabContent = screen.getByRole("tabpanel");
375
+
376
+ expect(tabContent).toHaveAttribute("class", "");
377
+ });
378
+
379
+ it("should render children correctly", () => {
380
+ // Arrange
381
+ render(
382
+ <Tabs.Root defaultValue="tab1">
383
+ <Tabs.List label="test label">
384
+ <Tabs.Trigger value="tab1" />
385
+ </Tabs.List>
386
+ <Tabs.Content value="tab1">
387
+ <p>Test Content</p>
388
+ </Tabs.Content>
389
+ </Tabs.Root>,
390
+ );
391
+ const testContent = screen.getByText("Test Content");
392
+
393
+ expect(testContent).toBeVisible();
394
+ });
395
+
396
+ it("should apply a generated id for the content panel that associates with the relevant tab", () => {
397
+ // Arrange
398
+ render(
399
+ <Tabs.Root defaultValue="tab1">
400
+ <Tabs.List label="test label">
401
+ <Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
402
+ </Tabs.List>
403
+ <Tabs.Content value="tab1">Tab Content 1</Tabs.Content>
404
+ </Tabs.Root>,
405
+ );
406
+ const tabTrigger = screen.getByRole("tabpanel", { name: "Tab 1" });
407
+
408
+ expect(tabTrigger).toHaveAttribute(
409
+ "id",
410
+ expect.stringContaining("-panel-tab1"),
411
+ );
412
+ });
413
+
414
+ it("should apply a generated aria-labelledby for the content panel that associates with the relevant tab", () => {
415
+ // Arrange
416
+ render(
417
+ <Tabs.Root defaultValue="tab1">
418
+ <Tabs.List label="test label">
419
+ <Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
420
+ </Tabs.List>
421
+ <Tabs.Content value="tab1">Tab Content 1</Tabs.Content>
422
+ </Tabs.Root>,
423
+ );
424
+ const tabTrigger = screen.getByRole("tabpanel", { name: "Tab 1" });
425
+
426
+ expect(tabTrigger).toHaveAttribute(
427
+ "aria-labelledby",
428
+ expect.stringContaining("-trigger-tab1"),
429
+ );
430
+ });
431
+
432
+ it('should render a data-orientation attribute of "horizontal" by default when not specified', () => {
433
+ // Arrange
434
+ render(
435
+ <Tabs.Root defaultValue="tab1">
436
+ <Tabs.List label="test label">
437
+ <Tabs.Trigger value="tab1" />
438
+ </Tabs.List>
439
+ <Tabs.Content value="tab1" />
440
+ </Tabs.Root>,
441
+ );
442
+ const tabContent = screen.getByRole("tabpanel");
443
+
444
+ // Assert
445
+ expect(tabContent).toHaveAttribute("data-orientation", "horizontal");
446
+ });
447
+
448
+ it.each(["horizontal", "vertical"] as TabsOrientation[])(
449
+ "should render the correct data-orientation attribute when supplied to the Tabs.Root component",
450
+ (orientation) => {
451
+ // Arrange
452
+ render(
453
+ <Tabs.Root defaultValue="tab1" orientation={orientation}>
454
+ <Tabs.List label="test label">
455
+ <Tabs.Trigger value="tab1" />
456
+ </Tabs.List>
457
+ <Tabs.Content value="tab1" />
458
+ </Tabs.Root>,
459
+ );
460
+ const tabContent = screen.getByRole("tabpanel");
461
+
462
+ // Assert
463
+ expect(tabContent).toHaveAttribute("data-orientation", orientation);
464
+ },
465
+ );
466
+ });
467
+
468
+ describe("context errors", () => {
469
+ it.each([
470
+ ["Tabs.List", () => <Tabs.List label="test label" />],
471
+ ["Tabs.Trigger", () => <Tabs.Trigger value="tab1" />],
472
+ ["Tabs.Content", () => <Tabs.Content value="tab1" />],
473
+ ] as const)(
474
+ "should throw an error when %s is used outside Tabs.Root",
475
+ (_, ComponentRenderer) => {
476
+ // Arrange & Act & Assert
477
+ expect(() => {
478
+ render(ComponentRenderer());
479
+ }).toThrow("Component must be rendered as a child of Tabs.Root");
480
+ },
481
+ );
482
+ });
483
+ });