@primitiv-ui/react 0.1.28 → 0.1.30

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 (456) hide show
  1. package/README.md +19 -2
  2. package/package.json +6 -1
  3. package/src/AccessibleIcon/AccessibleIcon.tsx +32 -9
  4. package/src/AccessibleIcon/__tests__/AccessibleIcon.test.tsx +5 -0
  5. package/src/AccessibleIcon/types.ts +15 -2
  6. package/src/Accordion/Accordion.tsx +32 -11
  7. package/src/Accordion/README.md +2 -2
  8. package/src/Accordion/__tests__/Accordion.asChild.test.tsx +46 -0
  9. package/src/Accordion/__tests__/Accordion.basic-rendering.test.tsx +15 -0
  10. package/src/Accordion/__tests__/Accordion.disabled-items.test.tsx +42 -0
  11. package/src/Accordion/__tests__/Accordion.error-handling.test.tsx +33 -0
  12. package/src/Accordion/hooks/useAccordionRoot.ts +44 -7
  13. package/src/Accordion/hooks/useAccordionTrigger.ts +9 -1
  14. package/src/Accordion/types.ts +72 -21
  15. package/src/Alert/Alert.tsx +36 -7
  16. package/src/Alert/__tests__/Alert.test.tsx +5 -0
  17. package/src/Alert/types.ts +18 -2
  18. package/src/Avatar/Avatar.tsx +99 -15
  19. package/src/Avatar/__tests__/Avatar.basic-rendering.test.tsx +10 -0
  20. package/src/Avatar/__tests__/Avatar.fallback.test.tsx +30 -0
  21. package/src/Avatar/__tests__/Avatar.image-loading.test.tsx +35 -0
  22. package/src/Avatar/hooks/useAvatarImage.ts +16 -0
  23. package/src/Avatar/types.ts +15 -3
  24. package/src/Breadcrumb/Breadcrumb.tsx +139 -21
  25. package/src/Breadcrumb/README.md +47 -0
  26. package/src/Breadcrumb/__tests__/Breadcrumb.basic-rendering.test.tsx +11 -0
  27. package/src/Breadcrumb/__tests__/Breadcrumb.ellipsis.test.tsx +110 -0
  28. package/src/Breadcrumb/types.ts +73 -7
  29. package/src/Button/Button.tsx +2 -0
  30. package/src/Button/__tests__/Button.basic-rendering.test.tsx +6 -0
  31. package/src/Button/__tests__/Button.contract.test.tsx +1 -1
  32. package/src/Button/types.ts +21 -0
  33. package/src/Carousel/Carousel.tsx +268 -20
  34. package/src/Carousel/README.md +764 -87
  35. package/src/Carousel/__tests__/Carousel.autoplay-status.test.tsx +239 -0
  36. package/src/Carousel/__tests__/Carousel.drag-status.test.tsx +292 -0
  37. package/src/Carousel/__tests__/Carousel.dynamic-slides.test.tsx +38 -0
  38. package/src/Carousel/__tests__/Carousel.indicators.test.tsx +101 -0
  39. package/src/Carousel/__tests__/Carousel.infinite.test.tsx +1453 -0
  40. package/src/Carousel/__tests__/Carousel.instant-scroll.test.tsx +124 -0
  41. package/src/Carousel/__tests__/Carousel.intersection-observer.test.tsx +90 -2
  42. package/src/Carousel/__tests__/Carousel.loop.test.tsx +163 -0
  43. package/src/Carousel/__tests__/Carousel.loopEngine.test.ts +77 -0
  44. package/src/Carousel/__tests__/Carousel.mouse-drag.test.tsx +504 -0
  45. package/src/Carousel/__tests__/Carousel.multi-slide-bounds.test.tsx +91 -0
  46. package/src/Carousel/__tests__/Carousel.orientation.test.tsx +207 -0
  47. package/src/Carousel/__tests__/Carousel.overscroll.test.tsx +532 -0
  48. package/src/Carousel/__tests__/Carousel.page-snap-points.test.tsx +50 -0
  49. package/src/Carousel/__tests__/Carousel.progress-text.test.tsx +76 -0
  50. package/src/Carousel/__tests__/Carousel.reduced-motion.test.tsx +3 -6
  51. package/src/Carousel/__tests__/Carousel.refresh-progress.test.tsx +2 -5
  52. package/src/Carousel/__tests__/Carousel.scroll-margin.test.tsx +167 -0
  53. package/src/Carousel/__tests__/Carousel.scroll-progress.test.tsx +518 -0
  54. package/src/Carousel/__tests__/Carousel.scroll-snap-change.test.tsx +32 -11
  55. package/src/Carousel/__tests__/Carousel.scroll-sync.test.tsx +237 -24
  56. package/src/Carousel/__tests__/Carousel.scroll-to-index.test.tsx +115 -0
  57. package/src/Carousel/__tests__/Carousel.slide-snap.test.tsx +172 -0
  58. package/src/Carousel/__tests__/Carousel.slides-per-move.test.tsx +179 -1
  59. package/src/Carousel/__tests__/Carousel.slides-per-page.test.tsx +91 -7
  60. package/src/Carousel/__tests__/Carousel.snap-type.test.tsx +35 -0
  61. package/src/Carousel/__tests__/Carousel.transition-modes.test.tsx +30 -5
  62. package/src/Carousel/__tests__/Carousel.translations.test.tsx +23 -0
  63. package/src/Carousel/__tests__/Carousel.wheel.test.tsx +160 -0
  64. package/src/Carousel/hooks/index.ts +1 -0
  65. package/src/Carousel/hooks/useCarouselLoop.ts +869 -0
  66. package/src/Carousel/hooks/useCarouselRoot.ts +338 -42
  67. package/src/Carousel/hooks/useCarouselSlide.ts +44 -9
  68. package/src/Carousel/hooks/useCarouselViewport.ts +649 -46
  69. package/src/Carousel/loopEngine.ts +54 -0
  70. package/src/Carousel/types.ts +458 -36
  71. package/src/Checkbox/Checkbox.tsx +11 -3
  72. package/src/Checkbox/__tests__/Checkbox.basic-rendering.test.tsx +14 -0
  73. package/src/Checkbox/hooks/useCheckboxInput.ts +4 -0
  74. package/src/Checkbox/types.ts +19 -0
  75. package/src/CheckboxCard/CheckboxCard.tsx +14 -5
  76. package/src/CheckboxCard/README.md +1 -1
  77. package/src/CheckboxCard/__tests__/CheckboxCard.basic-rendering.test.tsx +7 -0
  78. package/src/CheckboxCard/types.ts +65 -17
  79. package/src/Collapsible/Collapsible.tsx +49 -3
  80. package/src/Collapsible/README.md +41 -1
  81. package/src/Collapsible/__tests__/Collapsible.asChild.test.tsx +89 -0
  82. package/src/Collapsible/__tests__/Collapsible.basic-rendering.test.tsx +27 -0
  83. package/src/Collapsible/__tests__/Collapsible.collapsedHeight.test.tsx +140 -0
  84. package/src/Collapsible/hooks/useCollapsibleTrigger.ts +3 -1
  85. package/src/Collapsible/types.ts +89 -22
  86. package/src/Combobox/Combobox.tsx +508 -0
  87. package/src/Combobox/ComboboxContext.ts +23 -0
  88. package/src/Combobox/README.md +170 -0
  89. package/src/Combobox/__tests__/Combobox.asChild.test.tsx +62 -0
  90. package/src/Combobox/__tests__/Combobox.basic-rendering.test.tsx +16 -0
  91. package/src/Combobox/__tests__/Combobox.content.test.tsx +36 -0
  92. package/src/Combobox/__tests__/Combobox.controlled-state.test.tsx +88 -0
  93. package/src/Combobox/__tests__/Combobox.empty.test.tsx +42 -0
  94. package/src/Combobox/__tests__/Combobox.ids.test.tsx +66 -0
  95. package/src/Combobox/__tests__/Combobox.item.test.tsx +76 -0
  96. package/src/Combobox/__tests__/Combobox.keyboard-interaction.test.tsx +196 -0
  97. package/src/Combobox/__tests__/Combobox.light-dismiss.test.tsx +101 -0
  98. package/src/Combobox/__tests__/Combobox.query.test.tsx +105 -0
  99. package/src/Combobox/hooks/index.ts +1 -0
  100. package/src/Combobox/hooks/useComboboxRoot.ts +2 -0
  101. package/src/Combobox/index.ts +3 -0
  102. package/src/Combobox/types.ts +133 -0
  103. package/src/ContextMenu/ContextMenu.tsx +270 -4
  104. package/src/ContextMenu/README.md +1 -1
  105. package/src/ContextMenu/types.ts +148 -6
  106. package/src/DirectionProvider/DirectionProvider.tsx +41 -9
  107. package/src/DirectionProvider/README.md +4 -4
  108. package/src/DirectionProvider/__tests__/DirectionProvider.test.tsx +5 -0
  109. package/src/DirectionProvider/types.ts +28 -3
  110. package/src/Divider/Divider.tsx +24 -10
  111. package/src/Divider/__tests__/Divider.contract.test.tsx +1 -1
  112. package/src/Divider/__tests__/Divider.test.tsx +5 -0
  113. package/src/Divider/types.ts +10 -3
  114. package/src/Drawer/Drawer.tsx +315 -0
  115. package/src/Drawer/README.md +124 -0
  116. package/src/Drawer/__tests__/Drawer.behavior.test.tsx +100 -0
  117. package/src/Drawer/__tests__/Drawer.focus-trap.test.tsx +55 -0
  118. package/src/Drawer/__tests__/Drawer.rendering.test.tsx +111 -0
  119. package/src/Drawer/__tests__/Drawer.side.test.tsx +69 -0
  120. package/src/Drawer/__tests__/dialog-polyfill.ts +39 -0
  121. package/src/Drawer/index.ts +2 -0
  122. package/src/Drawer/types.ts +66 -0
  123. package/src/Dropdown/Dropdown.tsx +119 -2
  124. package/src/Dropdown/README.md +7 -5
  125. package/src/Dropdown/types.ts +241 -11
  126. package/src/EmptyState/EmptyState.tsx +74 -4
  127. package/src/EmptyState/README.md +2 -2
  128. package/src/EmptyState/__tests__/EmptyState.Actions.test.tsx +7 -2
  129. package/src/EmptyState/__tests__/EmptyState.Description.test.tsx +3 -1
  130. package/src/EmptyState/__tests__/EmptyState.Root.test.tsx +10 -0
  131. package/src/EmptyState/__tests__/EmptyState.Title.test.tsx +3 -1
  132. package/src/EmptyState/types.ts +49 -6
  133. package/src/Field/Field.tsx +23 -0
  134. package/src/Field/README.md +1 -1
  135. package/src/Field/__tests__/Field.basic-rendering.test.tsx +11 -0
  136. package/src/Field/__tests__/Field.error-handling.test.tsx +23 -0
  137. package/src/Field/__tests__/Field.state-cascade.test.tsx +21 -0
  138. package/src/Field/hooks/__tests__/useFieldProps.test.tsx +104 -9
  139. package/src/Field/hooks/useFieldProps.ts +1 -1
  140. package/src/Field/types.ts +60 -12
  141. package/src/Fieldset/Fieldset.tsx +55 -19
  142. package/src/Fieldset/README.md +2 -2
  143. package/src/Fieldset/__tests__/Fieldset.basic-rendering.test.tsx +9 -0
  144. package/src/Fieldset/types.ts +20 -2
  145. package/src/Input/Input.tsx +15 -10
  146. package/src/Input/README.md +2 -2
  147. package/src/Input/__tests__/Input.basic-rendering.test.tsx +5 -0
  148. package/src/Input/types.ts +19 -2
  149. package/src/InputGroup/InputGroup.tsx +11 -3
  150. package/src/InputGroup/__tests__/InputGroup.basic-rendering.test.tsx +14 -0
  151. package/src/InputGroup/types.ts +26 -5
  152. package/src/Listbox/Listbox.tsx +475 -0
  153. package/src/Listbox/ListboxContext.ts +48 -0
  154. package/src/Listbox/README.md +248 -0
  155. package/src/Listbox/__tests__/Listbox.active-descendant.test.tsx +128 -0
  156. package/src/Listbox/__tests__/Listbox.asChild.test.tsx +91 -0
  157. package/src/Listbox/__tests__/Listbox.basic-rendering.test.tsx +16 -0
  158. package/src/Listbox/__tests__/Listbox.controlled-state.test.tsx +114 -0
  159. package/src/Listbox/__tests__/Listbox.disabled.test.tsx +90 -0
  160. package/src/Listbox/__tests__/Listbox.fixtures.ts +27 -0
  161. package/src/Listbox/__tests__/Listbox.group-label.test.tsx +97 -0
  162. package/src/Listbox/__tests__/Listbox.group.test.tsx +45 -0
  163. package/src/Listbox/__tests__/Listbox.keyboard-edge-cases.test.tsx +144 -0
  164. package/src/Listbox/__tests__/Listbox.keyboard-interaction.test.tsx +112 -0
  165. package/src/Listbox/__tests__/Listbox.modifier-keys.test.tsx +86 -0
  166. package/src/Listbox/__tests__/Listbox.multi-select-keyboard.test.tsx +368 -0
  167. package/src/Listbox/__tests__/Listbox.multiple-mode.test.tsx +91 -0
  168. package/src/Listbox/__tests__/Listbox.mutation-hardening.test.tsx +480 -0
  169. package/src/Listbox/__tests__/Listbox.orientation.test.tsx +59 -0
  170. package/src/Listbox/__tests__/Listbox.reading-direction.test.tsx +65 -0
  171. package/src/Listbox/__tests__/Listbox.reorder.test.tsx +78 -0
  172. package/src/Listbox/__tests__/Listbox.selection-follows-focus.test.tsx +64 -0
  173. package/src/Listbox/__tests__/Listbox.strict-context.test.tsx +21 -0
  174. package/src/Listbox/__tests__/Listbox.typeahead.test.tsx +123 -0
  175. package/src/Listbox/__tests__/Listbox.uncontrolled-state.test.tsx +48 -0
  176. package/src/Listbox/constants.ts +2 -0
  177. package/src/Listbox/hooks/index.ts +1 -0
  178. package/src/Listbox/hooks/useListboxRoot.ts +407 -0
  179. package/src/Listbox/index.ts +3 -0
  180. package/src/Listbox/types.ts +238 -0
  181. package/src/MillerColumns/MillerColumns.tsx +186 -45
  182. package/src/MillerColumns/README.md +90 -20
  183. package/src/MillerColumns/__tests__/MillerColumns.aria.test.tsx +45 -0
  184. package/src/MillerColumns/__tests__/MillerColumns.auto-scroll.test.tsx +4 -2
  185. package/src/MillerColumns/__tests__/MillerColumns.basic-rendering.test.tsx +18 -3
  186. package/src/MillerColumns/__tests__/MillerColumns.data-attributes.test.tsx +48 -0
  187. package/src/MillerColumns/__tests__/MillerColumns.keyboard-interaction.test.tsx +35 -2
  188. package/src/MillerColumns/__tests__/MillerColumns.keyboard-selection-state.test.tsx +96 -0
  189. package/src/MillerColumns/__tests__/MillerColumns.preview-panel.test.tsx +29 -4
  190. package/src/MillerColumns/__tests__/MillerColumns.preview-visibility.test.tsx +79 -0
  191. package/src/MillerColumns/__tests__/MillerColumns.resize.test.tsx +189 -0
  192. package/src/MillerColumns/__tests__/MillerColumns.styled-parts.test.tsx +143 -0
  193. package/src/MillerColumns/__tests__/MillerColumns.typeahead.test.tsx +104 -0
  194. package/src/MillerColumns/constants.ts +2 -0
  195. package/src/MillerColumns/hooks/useMillerColumnsColumn.ts +5 -2
  196. package/src/MillerColumns/hooks/useMillerColumnsItem.ts +29 -7
  197. package/src/MillerColumns/hooks/useMillerColumnsResizeHandle.ts +80 -10
  198. package/src/MillerColumns/hooks/useMillerColumnsRoot.ts +0 -0
  199. package/src/MillerColumns/index.ts +4 -0
  200. package/src/MillerColumns/types.ts +136 -30
  201. package/src/MillerColumns/utils.ts +69 -3
  202. package/src/Modal/Modal.tsx +35 -8
  203. package/src/Modal/README.md +24 -15
  204. package/src/Modal/__tests__/Modal.focus-trap.test.tsx +162 -0
  205. package/src/Modal/hooks/useModalContent.ts +32 -0
  206. package/src/Modal/types.ts +110 -29
  207. package/src/NavigationMenu/NavigationMenu.tsx +890 -0
  208. package/src/NavigationMenu/NavigationMenuContext.ts +67 -0
  209. package/src/NavigationMenu/README.md +355 -0
  210. package/src/NavigationMenu/__tests__/NavigationMenu.anchor-positioning.test.tsx +132 -0
  211. package/src/NavigationMenu/__tests__/NavigationMenu.asChild.test.tsx +73 -0
  212. package/src/NavigationMenu/__tests__/NavigationMenu.basic-rendering.test.tsx +92 -0
  213. package/src/NavigationMenu/__tests__/NavigationMenu.contract.test.tsx +45 -0
  214. package/src/NavigationMenu/__tests__/NavigationMenu.controlled-state.test.tsx +134 -0
  215. package/src/NavigationMenu/__tests__/NavigationMenu.error-handling.test.tsx +62 -0
  216. package/src/NavigationMenu/__tests__/NavigationMenu.fixtures.tsx +79 -0
  217. package/src/NavigationMenu/__tests__/NavigationMenu.forceMount.test.tsx +52 -0
  218. package/src/NavigationMenu/__tests__/NavigationMenu.hover-interaction.test.tsx +254 -0
  219. package/src/NavigationMenu/__tests__/NavigationMenu.indicator.test.tsx +252 -0
  220. package/src/NavigationMenu/__tests__/NavigationMenu.keyboard-interaction.test.tsx +294 -0
  221. package/src/NavigationMenu/__tests__/NavigationMenu.keyboard-navigation.test.tsx +126 -0
  222. package/src/NavigationMenu/__tests__/NavigationMenu.link.test.tsx +139 -0
  223. package/src/NavigationMenu/__tests__/NavigationMenu.motion.test.tsx +568 -0
  224. package/src/NavigationMenu/__tests__/NavigationMenu.mouse-interaction.test.tsx +76 -0
  225. package/src/NavigationMenu/__tests__/NavigationMenu.uncontrolled-state.test.tsx +30 -0
  226. package/src/NavigationMenu/__tests__/NavigationMenu.viewport.test.tsx +113 -0
  227. package/src/NavigationMenu/constants.ts +5 -0
  228. package/src/NavigationMenu/hooks/index.ts +7 -0
  229. package/src/NavigationMenu/hooks/useNavigationMenuEntry.ts +33 -0
  230. package/src/NavigationMenu/hooks/useNavigationMenuIndicator.ts +70 -0
  231. package/src/NavigationMenu/hooks/useNavigationMenuLink.ts +72 -0
  232. package/src/NavigationMenu/hooks/useNavigationMenuRoot.ts +301 -0
  233. package/src/NavigationMenu/hooks/useNavigationMenuTopLevelEntry.ts +65 -0
  234. package/src/NavigationMenu/hooks/useNavigationMenuTrigger.ts +158 -0
  235. package/src/NavigationMenu/hooks/useNavigationMenuViewport.ts +101 -0
  236. package/src/NavigationMenu/index.ts +3 -0
  237. package/src/NavigationMenu/types.ts +259 -0
  238. package/src/NavigationMenu/utils.ts +89 -0
  239. package/src/Popover/Popover.tsx +427 -0
  240. package/src/Popover/PopoverContext.ts +13 -0
  241. package/src/Popover/README.md +106 -0
  242. package/src/Popover/__tests__/Popover.anchor.test.tsx +38 -0
  243. package/src/Popover/__tests__/Popover.asChild.test.tsx +160 -0
  244. package/src/Popover/__tests__/Popover.basic-rendering.test.tsx +106 -0
  245. package/src/Popover/__tests__/Popover.click-outside.test.tsx +149 -0
  246. package/src/Popover/__tests__/Popover.close.test.tsx +70 -0
  247. package/src/Popover/__tests__/Popover.content.test.tsx +58 -0
  248. package/src/Popover/__tests__/Popover.controlled-state.test.tsx +45 -0
  249. package/src/Popover/__tests__/Popover.error-handling.test.tsx +51 -0
  250. package/src/Popover/__tests__/Popover.focus.test.tsx +43 -0
  251. package/src/Popover/__tests__/Popover.keyboard-interaction.test.tsx +71 -0
  252. package/src/Popover/__tests__/Popover.mouse-interaction.test.tsx +69 -0
  253. package/src/Popover/__tests__/Popover.uncontrolled-state.test.tsx +44 -0
  254. package/src/Popover/hooks/index.ts +3 -0
  255. package/src/Popover/hooks/usePopoverContent.ts +98 -0
  256. package/src/Popover/hooks/usePopoverRoot.ts +84 -0
  257. package/src/Popover/hooks/usePopoverTrigger.ts +33 -0
  258. package/src/Popover/index.ts +2 -0
  259. package/src/Popover/types.ts +197 -0
  260. package/src/Portal/Portal.tsx +50 -10
  261. package/src/Portal/README.md +2 -2
  262. package/src/Portal/__tests__/Portal.basic-rendering.test.tsx +5 -0
  263. package/src/Portal/types.ts +5 -0
  264. package/src/Progress/Progress.tsx +33 -1
  265. package/src/Progress/README.md +1 -1
  266. package/src/Progress/__tests__/Progress.basic-rendering.test.tsx +10 -0
  267. package/src/Progress/__tests__/Progress.value.test.tsx +21 -0
  268. package/src/Progress/types.ts +20 -4
  269. package/src/Radio/Radio.tsx +10 -2
  270. package/src/Radio/__tests__/Radio.basic-rendering.test.tsx +14 -0
  271. package/src/Radio/hooks/useRadioRoot.ts +3 -0
  272. package/src/Radio/types.ts +20 -0
  273. package/src/RadioCard/RadioCard.tsx +11 -0
  274. package/src/RadioCard/__tests__/RadioCard.basic-rendering.test.tsx +8 -0
  275. package/src/RadioCard/__tests__/RadioCard.disabled-items.test.tsx +27 -0
  276. package/src/RadioCard/__tests__/RadioCard.keyboard-interaction.test.tsx +53 -0
  277. package/src/RadioCard/__tests__/RadioCard.ref-forwarding.test.tsx +54 -0
  278. package/src/RadioCard/__tests__/RadioCard.uncontrolled-state.test.tsx +21 -0
  279. package/src/RadioCard/hooks/useRadioCardRoot.ts +11 -0
  280. package/src/RadioCard/types.ts +95 -22
  281. package/src/RadioGroup/README.md +2 -2
  282. package/src/RadioGroup/RadioGroup.tsx +12 -1
  283. package/src/RadioGroup/__tests__/RadioGroup.basic-rendering.test.tsx +8 -0
  284. package/src/RadioGroup/__tests__/RadioGroup.disabled.test.tsx +27 -0
  285. package/src/RadioGroup/__tests__/RadioGroup.ref-forwarding.test.tsx +29 -0
  286. package/src/RadioGroup/__tests__/RadioGroup.roving-tabindex.test.tsx +31 -0
  287. package/src/RadioGroup/hooks/useRadioGroupRoot.ts +11 -0
  288. package/src/RadioGroup/types.ts +73 -9
  289. package/src/SegmentedControl/README.md +154 -0
  290. package/src/SegmentedControl/SegmentedControl.tsx +309 -0
  291. package/src/SegmentedControl/SegmentedControlContext.ts +28 -0
  292. package/src/SegmentedControl/__tests__/SegmentedControl.asChild.test.tsx +50 -0
  293. package/src/SegmentedControl/__tests__/SegmentedControl.basic-rendering.test.tsx +100 -0
  294. package/src/SegmentedControl/__tests__/SegmentedControl.controlled-state.test.tsx +107 -0
  295. package/src/SegmentedControl/__tests__/SegmentedControl.disabled-keydown-guards.test.tsx +62 -0
  296. package/src/SegmentedControl/__tests__/SegmentedControl.disabled.test.tsx +151 -0
  297. package/src/SegmentedControl/__tests__/SegmentedControl.error-handling.test.tsx +17 -0
  298. package/src/SegmentedControl/__tests__/SegmentedControl.keyboard-interaction.test.tsx +117 -0
  299. package/src/SegmentedControl/__tests__/SegmentedControl.orientation.test.tsx +54 -0
  300. package/src/SegmentedControl/__tests__/SegmentedControl.reading-direction.test.tsx +56 -0
  301. package/src/SegmentedControl/__tests__/SegmentedControl.ref-forwarding.test.tsx +45 -0
  302. package/src/SegmentedControl/__tests__/SegmentedControl.roving-tabindex.test.tsx +104 -0
  303. package/src/SegmentedControl/__tests__/SegmentedControl.uncontrolled-state.test.tsx +94 -0
  304. package/src/SegmentedControl/hooks/index.ts +2 -0
  305. package/src/SegmentedControl/hooks/useSegmentedControlContext.ts +1 -0
  306. package/src/SegmentedControl/hooks/useSegmentedControlRoot.ts +87 -0
  307. package/src/SegmentedControl/index.ts +2 -0
  308. package/src/SegmentedControl/types.ts +138 -0
  309. package/src/Select/README.md +122 -157
  310. package/src/Select/Select.tsx +596 -69
  311. package/src/Select/SelectContext.ts +38 -0
  312. package/src/Select/SelectItemIndicatorContext.ts +29 -0
  313. package/src/Select/__tests__/Select.asChild.test.tsx +2 -2
  314. package/src/Select/__tests__/Select.basic-rendering.test.tsx +22 -3
  315. package/src/Select/__tests__/Select.controlled-state.test.tsx +6 -6
  316. package/src/Select/__tests__/Select.data-attributes.test.tsx +4 -4
  317. package/src/Select/__tests__/Select.field-integration.test.tsx +11 -11
  318. package/src/Select/__tests__/Select.form.test.tsx +38 -0
  319. package/src/Select/__tests__/Select.group.test.tsx +6 -6
  320. package/src/Select/__tests__/Select.keyboard-edge.test.tsx +95 -0
  321. package/src/Select/__tests__/Select.keyboard.test.tsx +124 -0
  322. package/src/Select/__tests__/Select.mutation-hardening.test.tsx +317 -0
  323. package/src/Select/__tests__/Select.native-item.test.tsx +30 -0
  324. package/src/Select/__tests__/Select.open-close.test.tsx +57 -0
  325. package/src/Select/__tests__/Select.placeholder.test.tsx +7 -7
  326. package/src/Select/__tests__/Select.rich-asChild.test.tsx +60 -0
  327. package/src/Select/__tests__/Select.rich-mode.test.tsx +16 -0
  328. package/src/Select/__tests__/Select.selection.test.tsx +104 -0
  329. package/src/Select/__tests__/Select.separator.test.tsx +71 -0
  330. package/src/Select/__tests__/Select.strict-context.test.tsx +36 -0
  331. package/src/Select/__tests__/Select.typeahead.test.tsx +156 -0
  332. package/src/Select/__tests__/Select.value-mirror.test.tsx +240 -0
  333. package/src/Select/constants.ts +8 -0
  334. package/src/Select/hooks/index.ts +2 -0
  335. package/src/Select/hooks/useSelectContent.ts +149 -0
  336. package/src/Select/hooks/useSelectRoot.ts +149 -0
  337. package/src/Select/types.ts +199 -31
  338. package/src/SkipNav/README.md +3 -3
  339. package/src/SkipNav/SkipNav.tsx +72 -32
  340. package/src/SkipNav/__tests__/SkipNav.basic-rendering.test.tsx +6 -0
  341. package/src/SkipNav/types.ts +17 -9
  342. package/src/Slider/README.md +37 -7
  343. package/src/Slider/Slider.tsx +42 -4
  344. package/src/Slider/SliderContext.ts +28 -1
  345. package/src/Slider/types.ts +157 -9
  346. package/src/Slot/Slot.tsx +94 -19
  347. package/src/Slot/__tests__/Slot.test.tsx +112 -2
  348. package/src/Slot/composeEventHandlers.ts +26 -2
  349. package/src/Slot/types.ts +16 -0
  350. package/src/SplitButton/README.md +225 -0
  351. package/src/SplitButton/SplitButton.tsx +457 -0
  352. package/src/SplitButton/SplitButtonContext.ts +26 -0
  353. package/src/SplitButton/__tests__/SplitButton.accessibility.test.tsx +55 -0
  354. package/src/SplitButton/__tests__/SplitButton.asChild.test.tsx +51 -0
  355. package/src/SplitButton/__tests__/SplitButton.basic-rendering.test.tsx +57 -0
  356. package/src/SplitButton/__tests__/SplitButton.data-attributes.test.tsx +28 -0
  357. package/src/SplitButton/__tests__/SplitButton.disabled.test.tsx +79 -0
  358. package/src/SplitButton/__tests__/SplitButton.error-handling.test.tsx +29 -0
  359. package/src/SplitButton/__tests__/SplitButton.keyboard-interaction.test.tsx +56 -0
  360. package/src/SplitButton/__tests__/SplitButton.state-modes.test.tsx +68 -0
  361. package/src/SplitButton/index.ts +2 -0
  362. package/src/SplitButton/types.ts +186 -0
  363. package/src/Status/Status.tsx +36 -15
  364. package/src/Status/__tests__/Status.test.tsx +5 -0
  365. package/src/Status/types.ts +12 -1
  366. package/src/Switch/README.md +1 -1
  367. package/src/Switch/Switch.tsx +17 -4
  368. package/src/Switch/__tests__/Switch.basic-rendering.test.tsx +7 -0
  369. package/src/Switch/hooks/useSwitchRoot.ts +3 -0
  370. package/src/Switch/types.ts +49 -22
  371. package/src/Table/README.md +110 -8
  372. package/src/Table/Table.tsx +246 -16
  373. package/src/Table/TableExpandableContext.ts +7 -0
  374. package/src/Table/__tests__/Table.DetailRow.test.tsx +83 -0
  375. package/src/Table/__tests__/Table.ExpandTrigger.test.tsx +102 -0
  376. package/src/Table/__tests__/Table.Expandable.test.tsx +121 -0
  377. package/src/Table/types.ts +191 -17
  378. package/src/Tabs/README.md +4 -4
  379. package/src/Tabs/Tabs.tsx +13 -4
  380. package/src/Tabs/__tests__/Tabs.activation-mode.test.tsx +58 -0
  381. package/src/Tabs/__tests__/Tabs.basic-rendering.test.tsx +15 -0
  382. package/src/Tabs/__tests__/Tabs.change-event-callbacks.test.tsx +24 -0
  383. package/src/Tabs/__tests__/Tabs.disabled-tabs.test.tsx +36 -0
  384. package/src/Tabs/__tests__/Tabs.lazy-mount.test.tsx +17 -0
  385. package/src/Tabs/hooks/useTabsContent.ts +5 -0
  386. package/src/Tabs/hooks/useTabsRoot.ts +29 -5
  387. package/src/Tabs/hooks/useTabsTrigger.ts +7 -2
  388. package/src/Tabs/types.ts +29 -4
  389. package/src/Textarea/Textarea.tsx +9 -1
  390. package/src/Textarea/__tests__/Textarea.basic-rendering.test.tsx +5 -0
  391. package/src/Textarea/types.ts +20 -3
  392. package/src/Toggle/Toggle.tsx +51 -13
  393. package/src/Toggle/__tests__/Toggle.basic-rendering.test.tsx +5 -0
  394. package/src/Toggle/types.ts +54 -4
  395. package/src/ToggleGroup/README.md +4 -4
  396. package/src/ToggleGroup/ToggleGroup.tsx +14 -1
  397. package/src/ToggleGroup/__tests__/ToggleGroup.asChild.test.tsx +20 -0
  398. package/src/ToggleGroup/__tests__/ToggleGroup.basic-rendering.test.tsx +9 -0
  399. package/src/ToggleGroup/__tests__/ToggleGroup.dynamic-items.test.tsx +70 -0
  400. package/src/ToggleGroup/__tests__/ToggleGroup.error-handling.test.tsx +23 -0
  401. package/src/ToggleGroup/__tests__/ToggleGroup.keyboard-interaction.test.tsx +23 -1
  402. package/src/ToggleGroup/__tests__/ToggleGroup.ref-forwarding.test.tsx +58 -0
  403. package/src/ToggleGroup/hooks/useToggleGroupRoot.ts +21 -1
  404. package/src/ToggleGroup/types.ts +55 -2
  405. package/src/Tooltip/README.md +7 -7
  406. package/src/Tooltip/Tooltip.tsx +100 -22
  407. package/src/Tooltip/__tests__/Tooltip.basic-rendering.test.tsx +50 -0
  408. package/src/Tooltip/__tests__/Tooltip.error-handling.test.tsx +37 -0
  409. package/src/Tooltip/__tests__/Tooltip.escape-hatches.test.tsx +73 -0
  410. package/src/Tooltip/__tests__/Tooltip.keyboard-interaction.test.tsx +134 -0
  411. package/src/Tooltip/__tests__/Tooltip.skip-delay.test.tsx +216 -0
  412. package/src/Tooltip/__tests__/Tooltip.timer-coordination.test.tsx +223 -0
  413. package/src/Tooltip/hooks/__tests__/useTooltipProvider.test.tsx +40 -0
  414. package/src/Tooltip/hooks/useTooltipContent.ts +3 -0
  415. package/src/Tooltip/hooks/useTooltipProvider.ts +46 -8
  416. package/src/Tooltip/hooks/useTooltipRoot.ts +30 -6
  417. package/src/Tooltip/types.ts +89 -8
  418. package/src/Tree/README.md +69 -5
  419. package/src/Tree/Tree.tsx +188 -34
  420. package/src/Tree/__tests__/Tree.styled-wrapper.test.tsx +159 -0
  421. package/src/Tree/hooks/useTreePath.ts +33 -1
  422. package/src/Tree/index.ts +6 -1
  423. package/src/Tree/types.ts +54 -12
  424. package/src/Tree/utils.ts +46 -4
  425. package/src/VisuallyHidden/VisuallyHidden.tsx +33 -8
  426. package/src/VisuallyHidden/__tests__/VisuallyHidden.test.tsx +15 -1
  427. package/src/VisuallyHidden/types.ts +11 -1
  428. package/src/hooks/index.ts +1 -0
  429. package/src/hooks/useTypeahead.ts +95 -0
  430. package/src/index.ts +10 -0
  431. package/src/test/intersectionObserverPolyfill.ts +7 -0
  432. package/src/test/resizeObserverPolyfill.ts +80 -0
  433. package/src/useLocalStorage/__tests__/useLocalStorage.reading.test.tsx +31 -0
  434. package/src/useLocalStorage/__tests__/useLocalStorage.resilience.test.tsx +97 -0
  435. package/src/useLocalStorage/__tests__/useLocalStorage.ssr.test.tsx +20 -0
  436. package/src/useLocalStorage/__tests__/useLocalStorage.writing.test.tsx +72 -0
  437. package/src/useLocalStorage/index.ts +1 -0
  438. package/src/useLocalStorage/useLocalStorage.ts +140 -0
  439. package/src/useMediaQuery/README.md +66 -0
  440. package/src/useMediaQuery/__tests__/useMediaQuery.matching.test.tsx +42 -0
  441. package/src/useMediaQuery/__tests__/useMediaQuery.ssr.test.tsx +19 -0
  442. package/src/useMediaQuery/__tests__/useMediaQuery.subscription.test.tsx +89 -0
  443. package/src/useMediaQuery/index.ts +1 -0
  444. package/src/useMediaQuery/useMediaQuery.ts +49 -0
  445. package/src/usePagination/README.md +170 -0
  446. package/src/usePagination/__tests__/usePagination.navigation.test.tsx +109 -0
  447. package/src/usePagination/__tests__/usePagination.page-count.test.tsx +56 -0
  448. package/src/usePagination/__tests__/usePagination.page-state.test.tsx +87 -0
  449. package/src/usePagination/__tests__/usePagination.range.test.tsx +82 -0
  450. package/src/usePagination/__tests__/usePagination.slice.test.tsx +59 -0
  451. package/src/usePagination/__tests__/usePagination.windowing.test.tsx +68 -0
  452. package/src/usePagination/index.ts +1 -0
  453. package/src/usePagination/usePagination.ts +292 -0
  454. package/src/utils/focusable.ts +15 -0
  455. package/src/utils/getKeyToActionMap.ts +1 -1
  456. package/src/utils/index.ts +1 -0
@@ -0,0 +1,83 @@
1
+ import { Table } from "../index.ts";
2
+ import { render, screen } from "@testing-library/react";
3
+
4
+ describe("Table.DetailRow rendering", () => {
5
+ it("should render a row whose single cell spans every column", () => {
6
+ // Arrange
7
+ render(
8
+ <table>
9
+ <tbody>
10
+ <Table.Expandable expanded>
11
+ <Table.DetailRow colSpan={4}>Detail panel</Table.DetailRow>
12
+ </Table.Expandable>
13
+ </tbody>
14
+ </table>,
15
+ );
16
+ const cell = screen.getByRole("cell");
17
+
18
+ // Assert
19
+ expect(cell).toHaveAttribute("colspan", "4");
20
+ expect(cell).toHaveTextContent("Detail panel");
21
+ });
22
+
23
+ it("should offset the panel behind an empty gutter cell, still spanning every column between them", () => {
24
+ // Arrange
25
+ render(
26
+ <table>
27
+ <tbody>
28
+ <Table.Expandable expanded>
29
+ <Table.DetailRow colSpan={5} gutter={2}>
30
+ Detail panel
31
+ </Table.DetailRow>
32
+ </Table.Expandable>
33
+ </tbody>
34
+ </table>,
35
+ );
36
+ const [gutter, panel] = screen.getAllByRole("cell");
37
+
38
+ // Assert — the gutter is what aligns the panel with the first DATA column
39
+ // at every size and density: it spans the control columns, so the browser's
40
+ // own table layout does the arithmetic and nothing has to be measured.
41
+ expect(gutter).toBeEmptyDOMElement();
42
+ expect(gutter).toHaveAttribute("colspan", "2");
43
+ expect(panel).toHaveAttribute("colspan", "3");
44
+ expect(panel).toHaveTextContent("Detail panel");
45
+ });
46
+
47
+ it("should hide the row while collapsed so it leaves the row count truthful", () => {
48
+ // Arrange
49
+ render(
50
+ <table>
51
+ <tbody>
52
+ <Table.Expandable>
53
+ <Table.DetailRow colSpan={2}>Detail panel</Table.DetailRow>
54
+ </Table.Expandable>
55
+ </tbody>
56
+ </table>,
57
+ );
58
+
59
+ // Assert
60
+ expect(screen.queryByRole("row")).not.toBeInTheDocument();
61
+ });
62
+
63
+ it("should stay mounted but aria-hidden while collapsed when forceMount is set", () => {
64
+ // Arrange
65
+ render(
66
+ <table>
67
+ <tbody>
68
+ <Table.Expandable>
69
+ <Table.DetailRow colSpan={2} forceMount>
70
+ Detail panel
71
+ </Table.DetailRow>
72
+ </Table.Expandable>
73
+ </tbody>
74
+ </table>,
75
+ );
76
+ const row = screen.getByRole("row", { hidden: true });
77
+
78
+ // Assert — no `hidden`, so CSS can transition it; `aria-hidden` instead, so
79
+ // a collapsed panel still does not inflate the table's announced row count.
80
+ expect(row).not.toHaveAttribute("hidden");
81
+ expect(row).toHaveAttribute("aria-hidden", "true");
82
+ });
83
+ });
@@ -0,0 +1,102 @@
1
+ import { Table } from "../index.ts";
2
+ import { render, screen } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+
5
+ function renderPair(props?: { expanded?: boolean }) {
6
+ return render(
7
+ <table>
8
+ <tbody>
9
+ <Table.Expandable expanded={props?.expanded}>
10
+ <Table.Row>
11
+ <Table.Cell>
12
+ <Table.ExpandTrigger>Show details for Ada</Table.ExpandTrigger>
13
+ </Table.Cell>
14
+ </Table.Row>
15
+ <Table.DetailRow colSpan={2}>Detail panel</Table.DetailRow>
16
+ </Table.Expandable>
17
+ </tbody>
18
+ </table>,
19
+ );
20
+ }
21
+
22
+ describe("Table.ExpandTrigger accessibility", () => {
23
+ it("should own the expanded state on the button and point aria-controls at the detail row", () => {
24
+ // Arrange
25
+ renderPair({ expanded: true });
26
+ const trigger = screen.getByRole("button", {
27
+ name: /show details for ada/i,
28
+ });
29
+ const detailRow = screen.getAllByRole("row")[1];
30
+
31
+ // Assert — the button carries the state, because a button's expanded state
32
+ // is announced universally while a row's is only reliable in a treegrid.
33
+ expect(trigger).toHaveAttribute("aria-expanded", "true");
34
+ expect(detailRow).toHaveAttribute("id");
35
+ expect(trigger).toHaveAttribute(
36
+ "aria-controls",
37
+ detailRow.getAttribute("id"),
38
+ );
39
+ });
40
+ });
41
+
42
+ describe("Table.ExpandTrigger composition", () => {
43
+ it("should hand its aria wiring to a consumer's own control when asChild is set", () => {
44
+ // Arrange — the styled layer composes a real ghost Button here rather than
45
+ // hand-rolling a bare <button>, which is what the Figma set models.
46
+ render(
47
+ <table>
48
+ <tbody>
49
+ <Table.Expandable>
50
+ <Table.Row>
51
+ <Table.Cell>
52
+ <Table.ExpandTrigger asChild>
53
+ <button type="button" className="my-button">
54
+ Details
55
+ </button>
56
+ </Table.ExpandTrigger>
57
+ </Table.Cell>
58
+ </Table.Row>
59
+ <Table.DetailRow colSpan={1}>Panel</Table.DetailRow>
60
+ </Table.Expandable>
61
+ </tbody>
62
+ </table>,
63
+ );
64
+ const trigger = screen.getByRole("button", { name: "Details" });
65
+
66
+ // Assert — one button, the consumer's, carrying the wiring
67
+ expect(trigger).toHaveClass("my-button");
68
+ expect(trigger).toHaveAttribute("aria-expanded", "false");
69
+ expect(trigger).toHaveAttribute("aria-controls");
70
+ expect(trigger).toHaveAttribute("data-state", "closed");
71
+ expect(screen.getAllByRole("button")).toHaveLength(1);
72
+ });
73
+ });
74
+
75
+ describe("Table.ExpandTrigger activation", () => {
76
+ it("should report the next expanded state and reveal the detail row when uncontrolled", async () => {
77
+ // Arrange
78
+ const onExpandedChange = vi.fn();
79
+ const user = userEvent.setup();
80
+ render(
81
+ <table>
82
+ <tbody>
83
+ <Table.Expandable onExpandedChange={onExpandedChange}>
84
+ <Table.Row>
85
+ <Table.Cell>
86
+ <Table.ExpandTrigger>Show details for Ada</Table.ExpandTrigger>
87
+ </Table.Cell>
88
+ </Table.Row>
89
+ <Table.DetailRow colSpan={2}>Detail panel</Table.DetailRow>
90
+ </Table.Expandable>
91
+ </tbody>
92
+ </table>,
93
+ );
94
+
95
+ // Act
96
+ await user.click(screen.getByRole("button"));
97
+
98
+ // Assert
99
+ expect(onExpandedChange).toHaveBeenCalledWith(true);
100
+ expect(screen.getByText("Detail panel")).toBeVisible();
101
+ });
102
+ });
@@ -0,0 +1,121 @@
1
+ import { Table } from "../index.ts";
2
+ import { render, screen } from "@testing-library/react";
3
+
4
+ describe("Table displayNames", () => {
5
+ it("sets a displayName on the compound and each sub-component", () => {
6
+ // Assert — empty displayNames would render each as anonymous in DevTools.
7
+ // `Table`, `Table.Root` and the underlying function are one object
8
+ // (Object.assign compound), so the compound's "Table" alias is the
9
+ // observable Root displayName; the sub-components are distinct objects.
10
+ expect(Table.displayName).toBe("Table");
11
+ expect(Table.Head.displayName).toBe("TableHead");
12
+ expect(Table.Body.displayName).toBe("TableBody");
13
+ expect(Table.Footer.displayName).toBe("TableFooter");
14
+ expect(Table.Row.displayName).toBe("TableRow");
15
+ expect(Table.Header.displayName).toBe("TableHeader");
16
+ expect(Table.Cell.displayName).toBe("TableCell");
17
+ expect(Table.ScrollArea.displayName).toBe("TableScrollArea");
18
+ expect(Table.Caption.displayName).toBe("TableCaption");
19
+ expect(Table.Expandable.displayName).toBe("TableExpandable");
20
+ expect(Table.ExpandTrigger.displayName).toBe("TableExpandTrigger");
21
+ expect(Table.DetailRow.displayName).toBe("TableDetailRow");
22
+ });
23
+ });
24
+
25
+ describe("Table.Expandable contract", () => {
26
+ it("should require its parts to be rendered inside a provider", () => {
27
+ // Assert — the strict context is what stops a trigger being wired to
28
+ // nothing. React logs the throw, so silence the expected console noise.
29
+ const consoleError = vi
30
+ .spyOn(console, "error")
31
+ .mockImplementation(() => undefined);
32
+
33
+ expect(() =>
34
+ render(
35
+ <table>
36
+ <tbody>
37
+ <Table.DetailRow colSpan={2}>Orphan</Table.DetailRow>
38
+ </tbody>
39
+ </table>,
40
+ ),
41
+ ).toThrow(
42
+ "Table.ExpandTrigger and Table.DetailRow must be rendered inside a <Table.Expandable>.",
43
+ );
44
+
45
+ consoleError.mockRestore();
46
+ });
47
+
48
+ it("should apply the hidden attribute — not merely leave the row unannounced — while collapsed", () => {
49
+ // Arrange
50
+ render(
51
+ <table>
52
+ <tbody>
53
+ <Table.Expandable>
54
+ <Table.DetailRow colSpan={2}>Detail panel</Table.DetailRow>
55
+ </Table.Expandable>
56
+ </tbody>
57
+ </table>,
58
+ );
59
+
60
+ // Assert — pins the `forceMount` default. Without `hidden` the row is only
61
+ // missing from the accessibility tree, which an aria-hidden row would be too.
62
+ expect(screen.getByText("Detail panel").closest("tr")).toHaveAttribute(
63
+ "hidden",
64
+ );
65
+ });
66
+
67
+ it.each([
68
+ { expanded: true, state: "open" },
69
+ { expanded: false, state: "closed" },
70
+ ])(
71
+ "should publish data-state=$state on both parts for the styling layer",
72
+ ({ expanded, state }) => {
73
+ // Arrange
74
+ render(
75
+ <table>
76
+ <tbody>
77
+ <Table.Expandable expanded={expanded}>
78
+ <Table.Row>
79
+ <Table.Cell>
80
+ <Table.ExpandTrigger>Toggle</Table.ExpandTrigger>
81
+ </Table.Cell>
82
+ </Table.Row>
83
+ <Table.DetailRow colSpan={2} forceMount>
84
+ Detail panel
85
+ </Table.DetailRow>
86
+ </Table.Expandable>
87
+ </tbody>
88
+ </table>,
89
+ );
90
+
91
+ // Assert — the registry stylesheet animates the panel off this attribute,
92
+ // because a CSS transition needs a selector, not an unmount.
93
+ expect(screen.getByRole("button", { name: "Toggle" })).toHaveAttribute(
94
+ "data-state",
95
+ state,
96
+ );
97
+ expect(
98
+ screen.getByText("Detail panel").closest("tr"),
99
+ ).toHaveAttribute("data-state", state);
100
+ },
101
+ );
102
+
103
+ it("should not aria-hide a force-mounted row once it is expanded", () => {
104
+ // Arrange
105
+ render(
106
+ <table>
107
+ <tbody>
108
+ <Table.Expandable expanded>
109
+ <Table.DetailRow colSpan={2} forceMount>
110
+ Detail panel
111
+ </Table.DetailRow>
112
+ </Table.Expandable>
113
+ </tbody>
114
+ </table>,
115
+ );
116
+
117
+ // Assert — aria-hidden belongs to "force-mounted AND collapsed" only; an
118
+ // expanded panel that is not announced would be invisible to a screen reader.
119
+ expect(screen.getByRole("row")).not.toHaveAttribute("aria-hidden");
120
+ });
121
+ });
@@ -1,4 +1,5 @@
1
1
  import type {
2
+ ButtonHTMLAttributes,
2
3
  HTMLAttributes,
3
4
  ReactNode,
4
5
  TableHTMLAttributes,
@@ -6,58 +7,231 @@ import type {
6
7
  TdHTMLAttributes,
7
8
  } from "react";
8
9
 
9
- /** Props for {@link Table.Root} — all `TableHTMLAttributes` on `<table>`. */
10
+ /**
11
+ * Props for {@link TableRoot | `Table.Root`} — all `TableHTMLAttributes` on
12
+ * the native `<table>`. No custom props are added.
13
+ *
14
+ * @extends HTMLTableElement
15
+ */
10
16
  export type TableRootProps = TableHTMLAttributes<HTMLTableElement>;
11
17
 
12
- /** Props for {@link Table.Head} — all `HTMLAttributes` on `<thead>`. */
18
+ /**
19
+ * Props for {@link TableHead | `Table.Head`} — all `HTMLAttributes` on the
20
+ * native `<thead>`. No custom props are added.
21
+ *
22
+ * @extends HTMLTableSectionElement
23
+ */
13
24
  export type TableHeadProps = HTMLAttributes<HTMLTableSectionElement>;
14
25
 
15
- /** Props for {@link Table.Body} — all `HTMLAttributes` on `<tbody>`. */
26
+ /**
27
+ * Props for {@link TableBody | `Table.Body`} — all `HTMLAttributes` on the
28
+ * native `<tbody>`. No custom props are added.
29
+ *
30
+ * @extends HTMLTableSectionElement
31
+ */
16
32
  export type TableBodyProps = HTMLAttributes<HTMLTableSectionElement>;
17
33
 
18
- /** Props for {@link Table.Footer} — all `HTMLAttributes` on `<tfoot>`. */
34
+ /**
35
+ * Props for {@link TableFooter | `Table.Footer`} — all `HTMLAttributes` on
36
+ * the native `<tfoot>`. No custom props are added.
37
+ *
38
+ * @extends HTMLTableSectionElement
39
+ */
19
40
  export type TableFooterProps = HTMLAttributes<HTMLTableSectionElement>;
20
41
 
21
- /** Props for {@link Table.Row} — all `HTMLAttributes` on `<tr>`. */
42
+ /**
43
+ * Props for {@link TableRow | `Table.Row`} — all `HTMLAttributes` on the
44
+ * native `<tr>`. No custom props are added.
45
+ *
46
+ * @extends HTMLTableRowElement
47
+ */
22
48
  export type TableRowProps = HTMLAttributes<HTMLTableRowElement>;
23
49
 
24
50
  /**
25
- * Props for {@link Table.Header} — all `ThHTMLAttributes` on `<th>`.
51
+ * Props for {@link TableHeader | `Table.Header`} — all `ThHTMLAttributes` on
52
+ * the native `<th>`, including `colSpan`, `rowSpan`, and `abbr`.
26
53
  *
27
54
  * The most important prop for accessibility is `scope`: set it to `"col"` for
28
55
  * column headers and `"row"` for row headers so assistive technology can
29
- * associate data cells with the correct header.
56
+ * associate data cells with the correct header. See the scope table on
57
+ * {@link TableHeader}.
58
+ *
59
+ * @extends HTMLTableCellElement
30
60
  */
31
61
  export type TableHeaderProps = ThHTMLAttributes<HTMLTableCellElement>;
32
62
 
33
63
  /**
34
- * Props for {@link Table.Cell} — all `TdHTMLAttributes` on `<td>`.
64
+ * Props for {@link TableCell | `Table.Cell`} — all `TdHTMLAttributes` on the
65
+ * native `<td>`.
35
66
  *
36
67
  * Includes `colSpan` and `rowSpan` for spanning multiple columns or rows.
68
+ *
69
+ * @extends HTMLTableCellElement
37
70
  */
38
71
  export type TableCellProps = TdHTMLAttributes<HTMLTableCellElement>;
39
72
 
40
73
  /**
41
- * Props for {@link Table.ScrollArea} — `children` plus all `HTMLAttributes`
42
- * on the wrapping `<div>`.
74
+ * Props for {@link TableScrollArea | `Table.ScrollArea`} — a required
75
+ * `children` plus all `HTMLAttributes` on the wrapping `<div>`.
43
76
  *
44
- * Any `style` properties you pass are merged with (and take priority over) the
45
- * base scroll styles (`display: block`, `overflow-x: auto`, `max-width: 100%`).
77
+ * @extends HTMLDivElement
46
78
  */
47
79
  export type TableScrollAreaProps = {
80
+ /** The table (typically a single {@link TableRoot | `Table.Root`}) to
81
+ * wrap in the horizontally-scrolling container. */
82
+ children: ReactNode;
83
+ /**
84
+ * Merged with (and taking priority over) the base scroll styles
85
+ * (`display: block`, `overflow-x: auto`, `max-width: 100%`), so
86
+ * additional styles can be layered on without repeating the scroll
87
+ * declarations. See {@link TableScrollArea}'s custom-styles example.
88
+ */
89
+ style?: HTMLAttributes<HTMLDivElement>["style"];
90
+ } & Omit<HTMLAttributes<HTMLDivElement>, "style">;
91
+
92
+ /**
93
+ * The value shared by {@link TableExpandable | `Table.Expandable`} with its
94
+ * {@link TableExpandTrigger | `Table.ExpandTrigger`} and
95
+ * {@link TableDetailRow | `Table.DetailRow`} children.
96
+ *
97
+ * @internal
98
+ */
99
+ export type TableExpandableContextValue = {
100
+ /** Whether the detail row is currently revealed. */
101
+ expanded: boolean;
102
+ /** The id wired to the detail row's `<tr>` and the trigger's `aria-controls`. */
103
+ detailId: string;
104
+ /** Flips `expanded` and notifies `onExpandedChange`. */
105
+ toggle: () => void;
106
+ };
107
+
108
+ /**
109
+ * Props for {@link TableExpandable | `Table.Expandable`} — the pairing
110
+ * provider for one expandable row and its detail row.
111
+ */
112
+ export type TableExpandableProps = {
113
+ /** The parent {@link TableRow | `Table.Row`} and its
114
+ * {@link TableDetailRow | `Table.DetailRow`}, as siblings. */
48
115
  children: ReactNode;
49
- } & HTMLAttributes<HTMLDivElement>;
116
+ /**
117
+ * Whether the detail row is revealed. Pass this (with
118
+ * `onExpandedChange`) to drive expansion from outside — which is what an
119
+ * external table engine such as TanStack Table does.
120
+ */
121
+ expanded?: boolean;
122
+ /**
123
+ * Initial expanded state when running uncontrolled. Ignored when
124
+ * `expanded` is supplied.
125
+ * @default false
126
+ */
127
+ defaultExpanded?: boolean;
128
+ /** Called with the next expanded state whenever the trigger is activated. */
129
+ onExpandedChange?: (expanded: boolean) => void;
130
+ };
131
+
132
+ /**
133
+ * Props for {@link TableExpandTrigger | `Table.ExpandTrigger`} — all
134
+ * `ButtonHTMLAttributes` on the native `<button>`.
135
+ *
136
+ * `aria-expanded`, `aria-controls` and `type` are owned by the component and
137
+ * therefore `Omit`-ted: they are derived from the surrounding
138
+ * {@link TableExpandable | `Table.Expandable`} and must not be overridden, or
139
+ * the relationship the pair exists to express would break.
140
+ *
141
+ * @extends HTMLButtonElement
142
+ */
143
+ export type TableExpandTriggerProps = {
144
+ /**
145
+ * Render a consumer-supplied control instead of the native `<button>`, with
146
+ * this part's `aria-expanded`, `aria-controls`, `data-state` and click
147
+ * handling merged onto it via the {@link Slot} pattern.
148
+ *
149
+ * This is how a styled layer composes a real Button rather than re-creating
150
+ * one: the registry `data-table` renders a ghost Button through it, matching
151
+ * the Figma set, whose Expand Trigger is an Icon Button instance rather than a
152
+ * drawn glyph. The child must be a single element that accepts a `ref`, and it
153
+ * should be a `<button>` — the wiring assumes a control.
154
+ * @default false
155
+ */
156
+ asChild?: boolean;
157
+ } & Omit<
158
+ ButtonHTMLAttributes<HTMLButtonElement>,
159
+ "aria-expanded" | "aria-controls" | "type"
160
+ >;
50
161
 
51
162
  /**
52
- * Props for {@link Table.Caption} — `children`, an optional
53
- * `captionSide`, plus all `HTMLAttributes` on `<caption>`.
163
+ * Props for {@link TableDetailRow | `Table.DetailRow`} — a required `colSpan`
164
+ * plus all `HTMLAttributes` on the native `<tr>`.
165
+ *
166
+ * `id` is component-owned (it is the target of the trigger's `aria-controls`)
167
+ * and so is `Omit`-ted.
168
+ *
169
+ * @extends HTMLTableRowElement
170
+ */
171
+ export type TableDetailRowProps = {
172
+ /** The detail panel's content. */
173
+ children?: ReactNode;
174
+ /**
175
+ * How many columns the panel spans — **every** column the table renders,
176
+ * control columns included.
177
+ *
178
+ * Deliberately a required prop rather than a value counted from context:
179
+ * counting would mean the table registering its own columns, and an
180
+ * external table engine already knows the number (TanStack Table:
181
+ * `table.getVisibleLeafColumns().length`).
182
+ */
183
+ colSpan: number;
184
+ /**
185
+ * How many leading columns to leave empty before the panel starts — the
186
+ * control columns (select, expand) a row renders in front of its data.
187
+ *
188
+ * The panel then spans the columns that remain, so `colSpan` keeps its
189
+ * meaning: the table's full column count, gutter included.
190
+ *
191
+ * This is how a panel aligns with the first **data** column instead of the
192
+ * table's edge (a panel starting under the checkbox reads as a new section of
193
+ * the table rather than an opened row). Alignment by empty cell rather than by
194
+ * a CSS indent is deliberate: the browser's own table layout resolves the
195
+ * width, so it stays exact across every size, density and control-column set
196
+ * with nothing measured and no arithmetic to drift.
197
+ * @default 0
198
+ */
199
+ gutter?: number;
200
+ /**
201
+ * Keep the row mounted while collapsed so a CSS transition can run.
202
+ *
203
+ * Off by default, and that default matters more here than it does on a
204
+ * `<div>`-based disclosure: a mounted row is still a **row**, so assistive
205
+ * technology counts it, and a table of 24 rows with 24 force-mounted
206
+ * detail rows announces 48. Opt in only when you are animating, and note
207
+ * that the collapsed row remains in the accessibility tree while you do.
208
+ * @default false
209
+ */
210
+ forceMount?: boolean;
211
+ /** Passed through to the `<td>` that spans the row, for styling the cell
212
+ * itself rather than the row. */
213
+ cellProps?: TdHTMLAttributes<HTMLTableCellElement>;
214
+ } & Omit<HTMLAttributes<HTMLTableRowElement>, "id" | "children">;
215
+
216
+ /**
217
+ * Props for {@link TableCaption | `Table.Caption`} — a required `children`,
218
+ * an optional `captionSide`, plus all `HTMLAttributes` on the native
219
+ * `<caption>`.
220
+ *
221
+ * @extends HTMLTableCaptionElement
54
222
  */
55
223
  export type TableCaptionProps = {
224
+ /** The visible, accessible label for the table. Preferred over
225
+ * `aria-label`/`aria-labelledby` on the table itself — the browser
226
+ * programmatically associates a `<caption>` with its `<table>`. */
56
227
  children: ReactNode;
57
228
  /**
58
- * Controls the CSS `caption-side` property.
59
- * - `"bottom"` (default) — caption appears below the table.
229
+ * Controls the CSS `caption-side` property, positioning the caption
230
+ * above or below the table. See {@link TableCaption}'s placement
231
+ * examples.
232
+ * - `"bottom"` — caption appears below the table.
60
233
  * - `"top"` — caption appears above the table.
234
+ * @default "bottom"
61
235
  */
62
236
  captionSide?: "bottom" | "top";
63
237
  } & HTMLAttributes<HTMLTableCaptionElement>;
@@ -11,8 +11,8 @@ import { Tabs } from "@primitiv-ui/react";
11
11
  <Tabs.Trigger value="overview">Overview</Tabs.Trigger>
12
12
  <Tabs.Trigger value="settings">Settings</Tabs.Trigger>
13
13
  </Tabs.List>
14
- <Tabs.Content value="overview">Dashboard…</Tabs.Content>
15
- <Tabs.Content value="settings">Preferences…</Tabs.Content>
14
+ <Tabs.Content value="overview">Dashboard...</Tabs.Content>
15
+ <Tabs.Content value="settings">Preferences...</Tabs.Content>
16
16
  </Tabs.Root>;
17
17
  ```
18
18
 
@@ -63,7 +63,7 @@ trigger and panel is always present.
63
63
 
64
64
  ```tsx
65
65
  <Tabs.Root defaultValue="overview" lazyMount>
66
- …
66
+ ...
67
67
  </Tabs.Root>
68
68
  ```
69
69
 
@@ -76,7 +76,7 @@ position must be computed while the panel has real dimensions.
76
76
  ```tsx
77
77
  const ref = useRef<TabsImperativeApi>(null);
78
78
  <Tabs.Root ref={ref} defaultValue="a">
79
- …
79
+ ...
80
80
  </Tabs.Root>;
81
81
  ref.current?.setActiveTab("b");
82
82
  ```
package/src/Tabs/Tabs.tsx CHANGED
@@ -66,6 +66,8 @@ import type {
66
66
  * inherited from the nearest {@link DirectionProvider}, falling back to
67
67
  * `"ltr"` when there is no provider.
68
68
  *
69
+ * @extends HTMLDivElement
70
+ *
69
71
  * @example Uncontrolled
70
72
  * ```tsx
71
73
  * <Tabs.Root defaultValue="overview">
@@ -73,8 +75,8 @@ import type {
73
75
  * <Tabs.Trigger value="overview">Overview</Tabs.Trigger>
74
76
  * <Tabs.Trigger value="settings">Settings</Tabs.Trigger>
75
77
  * </Tabs.List>
76
- * <Tabs.Content value="overview">Dashboard…</Tabs.Content>
77
- * <Tabs.Content value="settings">Preferences…</Tabs.Content>
78
+ * <Tabs.Content value="overview">Dashboard...</Tabs.Content>
79
+ * <Tabs.Content value="settings">Preferences...</Tabs.Content>
78
80
  * </Tabs.Root>
79
81
  * ```
80
82
  *
@@ -87,7 +89,7 @@ import type {
87
89
  * onValueChange={setTab}
88
90
  * onChange={({ name, index }) => track("tab_view", { name, index })}
89
91
  * >
90
- * …
92
+ * ...
91
93
  * </Tabs.Root>
92
94
  * ```
93
95
  *
@@ -96,7 +98,7 @@ import type {
96
98
  * const ref = useRef<TabsImperativeApi>(null);
97
99
  *
98
100
  * <Tabs.Root ref={ref} orientation="vertical" defaultValue="one">
99
- * …
101
+ * ...
100
102
  * </Tabs.Root>
101
103
  *
102
104
  * <button onClick={() => ref.current?.setActiveTab("two")}>Go to two</button>
@@ -146,6 +148,7 @@ export const TabsRoot: ForwardRefExoticComponent<
146
148
  );
147
149
  });
148
150
 
151
+ // Stryker disable next-line StringLiteral: overwritten by the compound alias — an equivalent mutant.
149
152
  TabsRoot.displayName = "TabsRoot";
150
153
 
151
154
  /**
@@ -160,6 +163,8 @@ TabsRoot.displayName = "TabsRoot";
160
163
  *
161
164
  * **Styling hooks.** `data-orientation="horizontal" | "vertical"`.
162
165
  *
166
+ * @extends HTMLDivElement
167
+ *
163
168
  * @example
164
169
  * ```tsx
165
170
  * <Tabs.List label="Document sections">
@@ -241,6 +246,8 @@ TabsList.displayName = "TabsList";
241
246
  * - `data-disabled=""` when disabled (omitted otherwise), so CSS can target
242
247
  * `[data-disabled]` without the `:disabled` pseudo-class.
243
248
  *
249
+ * @extends HTMLButtonElement
250
+ *
244
251
  * @example Basic usage
245
252
  * ```tsx
246
253
  * <Tabs.Trigger value="account">Account</Tabs.Trigger>
@@ -345,6 +352,8 @@ TabsTrigger.displayName = "TabsTrigger";
345
352
  * - `data-state="active" | "inactive"`.
346
353
  * - `data-orientation="horizontal" | "vertical"`.
347
354
  *
355
+ * @extends HTMLDivElement
356
+ *
348
357
  * @example
349
358
  * ```tsx
350
359
  * <Tabs.Content value="account">