@primitiv-ui/react 0.1.28 → 0.1.29

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 (307) hide show
  1. package/README.md +4 -0
  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 +25 -4
  7. package/src/Accordion/__tests__/Accordion.asChild.test.tsx +46 -0
  8. package/src/Accordion/__tests__/Accordion.basic-rendering.test.tsx +15 -0
  9. package/src/Accordion/__tests__/Accordion.disabled-items.test.tsx +42 -0
  10. package/src/Accordion/__tests__/Accordion.error-handling.test.tsx +33 -0
  11. package/src/Accordion/hooks/useAccordionRoot.ts +44 -7
  12. package/src/Accordion/hooks/useAccordionTrigger.ts +9 -1
  13. package/src/Accordion/types.ts +72 -21
  14. package/src/Alert/Alert.tsx +36 -7
  15. package/src/Alert/__tests__/Alert.test.tsx +5 -0
  16. package/src/Alert/types.ts +18 -2
  17. package/src/Avatar/Avatar.tsx +99 -15
  18. package/src/Avatar/__tests__/Avatar.basic-rendering.test.tsx +10 -0
  19. package/src/Avatar/__tests__/Avatar.fallback.test.tsx +30 -0
  20. package/src/Avatar/__tests__/Avatar.image-loading.test.tsx +35 -0
  21. package/src/Avatar/hooks/useAvatarImage.ts +16 -0
  22. package/src/Avatar/types.ts +15 -3
  23. package/src/Breadcrumb/Breadcrumb.tsx +136 -18
  24. package/src/Breadcrumb/README.md +47 -0
  25. package/src/Breadcrumb/__tests__/Breadcrumb.basic-rendering.test.tsx +11 -0
  26. package/src/Breadcrumb/__tests__/Breadcrumb.ellipsis.test.tsx +110 -0
  27. package/src/Breadcrumb/types.ts +73 -7
  28. package/src/Button/Button.tsx +2 -0
  29. package/src/Button/__tests__/Button.basic-rendering.test.tsx +6 -0
  30. package/src/Button/types.ts +21 -0
  31. package/src/Carousel/Carousel.tsx +263 -15
  32. package/src/Carousel/README.md +749 -72
  33. package/src/Carousel/__tests__/Carousel.autoplay-status.test.tsx +239 -0
  34. package/src/Carousel/__tests__/Carousel.drag-status.test.tsx +292 -0
  35. package/src/Carousel/__tests__/Carousel.dynamic-slides.test.tsx +38 -0
  36. package/src/Carousel/__tests__/Carousel.indicators.test.tsx +101 -0
  37. package/src/Carousel/__tests__/Carousel.infinite.test.tsx +1453 -0
  38. package/src/Carousel/__tests__/Carousel.instant-scroll.test.tsx +124 -0
  39. package/src/Carousel/__tests__/Carousel.intersection-observer.test.tsx +88 -0
  40. package/src/Carousel/__tests__/Carousel.loop.test.tsx +163 -0
  41. package/src/Carousel/__tests__/Carousel.loopEngine.test.ts +77 -0
  42. package/src/Carousel/__tests__/Carousel.mouse-drag.test.tsx +504 -0
  43. package/src/Carousel/__tests__/Carousel.multi-slide-bounds.test.tsx +91 -0
  44. package/src/Carousel/__tests__/Carousel.orientation.test.tsx +207 -0
  45. package/src/Carousel/__tests__/Carousel.overscroll.test.tsx +532 -0
  46. package/src/Carousel/__tests__/Carousel.page-snap-points.test.tsx +50 -0
  47. package/src/Carousel/__tests__/Carousel.progress-text.test.tsx +76 -0
  48. package/src/Carousel/__tests__/Carousel.reduced-motion.test.tsx +3 -6
  49. package/src/Carousel/__tests__/Carousel.refresh-progress.test.tsx +2 -5
  50. package/src/Carousel/__tests__/Carousel.scroll-margin.test.tsx +167 -0
  51. package/src/Carousel/__tests__/Carousel.scroll-progress.test.tsx +518 -0
  52. package/src/Carousel/__tests__/Carousel.scroll-snap-change.test.tsx +32 -11
  53. package/src/Carousel/__tests__/Carousel.scroll-sync.test.tsx +237 -24
  54. package/src/Carousel/__tests__/Carousel.scroll-to-index.test.tsx +115 -0
  55. package/src/Carousel/__tests__/Carousel.slide-snap.test.tsx +172 -0
  56. package/src/Carousel/__tests__/Carousel.slides-per-move.test.tsx +179 -1
  57. package/src/Carousel/__tests__/Carousel.slides-per-page.test.tsx +91 -7
  58. package/src/Carousel/__tests__/Carousel.snap-type.test.tsx +35 -0
  59. package/src/Carousel/__tests__/Carousel.transition-modes.test.tsx +30 -5
  60. package/src/Carousel/__tests__/Carousel.translations.test.tsx +23 -0
  61. package/src/Carousel/__tests__/Carousel.wheel.test.tsx +160 -0
  62. package/src/Carousel/hooks/index.ts +1 -0
  63. package/src/Carousel/hooks/useCarouselLoop.ts +869 -0
  64. package/src/Carousel/hooks/useCarouselRoot.ts +338 -42
  65. package/src/Carousel/hooks/useCarouselSlide.ts +44 -9
  66. package/src/Carousel/hooks/useCarouselViewport.ts +649 -46
  67. package/src/Carousel/loopEngine.ts +54 -0
  68. package/src/Carousel/types.ts +457 -35
  69. package/src/Checkbox/Checkbox.tsx +9 -1
  70. package/src/Checkbox/__tests__/Checkbox.basic-rendering.test.tsx +14 -0
  71. package/src/Checkbox/hooks/useCheckboxInput.ts +4 -0
  72. package/src/Checkbox/types.ts +19 -0
  73. package/src/CheckboxCard/CheckboxCard.tsx +13 -4
  74. package/src/CheckboxCard/__tests__/CheckboxCard.basic-rendering.test.tsx +7 -0
  75. package/src/CheckboxCard/types.ts +65 -17
  76. package/src/Collapsible/Collapsible.tsx +49 -3
  77. package/src/Collapsible/README.md +40 -0
  78. package/src/Collapsible/__tests__/Collapsible.asChild.test.tsx +89 -0
  79. package/src/Collapsible/__tests__/Collapsible.basic-rendering.test.tsx +27 -0
  80. package/src/Collapsible/__tests__/Collapsible.collapsedHeight.test.tsx +140 -0
  81. package/src/Collapsible/hooks/useCollapsibleTrigger.ts +3 -1
  82. package/src/Collapsible/types.ts +89 -22
  83. package/src/ContextMenu/ContextMenu.tsx +270 -4
  84. package/src/ContextMenu/types.ts +148 -6
  85. package/src/DirectionProvider/DirectionProvider.tsx +41 -9
  86. package/src/DirectionProvider/__tests__/DirectionProvider.test.tsx +5 -0
  87. package/src/DirectionProvider/types.ts +28 -3
  88. package/src/Divider/Divider.tsx +24 -10
  89. package/src/Divider/__tests__/Divider.test.tsx +5 -0
  90. package/src/Divider/types.ts +10 -3
  91. package/src/Drawer/Drawer.tsx +315 -0
  92. package/src/Drawer/README.md +124 -0
  93. package/src/Drawer/__tests__/Drawer.behavior.test.tsx +100 -0
  94. package/src/Drawer/__tests__/Drawer.focus-trap.test.tsx +55 -0
  95. package/src/Drawer/__tests__/Drawer.rendering.test.tsx +111 -0
  96. package/src/Drawer/__tests__/Drawer.side.test.tsx +69 -0
  97. package/src/Drawer/__tests__/dialog-polyfill.ts +39 -0
  98. package/src/Drawer/index.ts +2 -0
  99. package/src/Drawer/types.ts +66 -0
  100. package/src/Dropdown/Dropdown.tsx +117 -0
  101. package/src/Dropdown/types.ts +241 -11
  102. package/src/EmptyState/EmptyState.tsx +74 -4
  103. package/src/EmptyState/__tests__/EmptyState.Actions.test.tsx +7 -2
  104. package/src/EmptyState/__tests__/EmptyState.Description.test.tsx +3 -1
  105. package/src/EmptyState/__tests__/EmptyState.Root.test.tsx +10 -0
  106. package/src/EmptyState/__tests__/EmptyState.Title.test.tsx +3 -1
  107. package/src/EmptyState/types.ts +49 -6
  108. package/src/Field/Field.tsx +23 -0
  109. package/src/Field/__tests__/Field.basic-rendering.test.tsx +11 -0
  110. package/src/Field/__tests__/Field.error-handling.test.tsx +23 -0
  111. package/src/Field/__tests__/Field.state-cascade.test.tsx +21 -0
  112. package/src/Field/hooks/__tests__/useFieldProps.test.tsx +104 -9
  113. package/src/Field/types.ts +60 -12
  114. package/src/Fieldset/Fieldset.tsx +54 -18
  115. package/src/Fieldset/__tests__/Fieldset.basic-rendering.test.tsx +9 -0
  116. package/src/Fieldset/types.ts +20 -2
  117. package/src/Input/Input.tsx +14 -9
  118. package/src/Input/__tests__/Input.basic-rendering.test.tsx +5 -0
  119. package/src/Input/types.ts +19 -2
  120. package/src/InputGroup/InputGroup.tsx +11 -3
  121. package/src/InputGroup/__tests__/InputGroup.basic-rendering.test.tsx +14 -0
  122. package/src/InputGroup/types.ts +26 -5
  123. package/src/MillerColumns/MillerColumns.tsx +86 -19
  124. package/src/MillerColumns/types.ts +83 -27
  125. package/src/Modal/Modal.tsx +31 -4
  126. package/src/Modal/README.md +19 -10
  127. package/src/Modal/__tests__/Modal.focus-trap.test.tsx +162 -0
  128. package/src/Modal/hooks/useModalContent.ts +32 -0
  129. package/src/Modal/types.ts +110 -29
  130. package/src/NavigationMenu/NavigationMenu.tsx +890 -0
  131. package/src/NavigationMenu/NavigationMenuContext.ts +67 -0
  132. package/src/NavigationMenu/README.md +355 -0
  133. package/src/NavigationMenu/__tests__/NavigationMenu.anchor-positioning.test.tsx +132 -0
  134. package/src/NavigationMenu/__tests__/NavigationMenu.asChild.test.tsx +73 -0
  135. package/src/NavigationMenu/__tests__/NavigationMenu.basic-rendering.test.tsx +92 -0
  136. package/src/NavigationMenu/__tests__/NavigationMenu.contract.test.tsx +45 -0
  137. package/src/NavigationMenu/__tests__/NavigationMenu.controlled-state.test.tsx +134 -0
  138. package/src/NavigationMenu/__tests__/NavigationMenu.error-handling.test.tsx +62 -0
  139. package/src/NavigationMenu/__tests__/NavigationMenu.fixtures.tsx +79 -0
  140. package/src/NavigationMenu/__tests__/NavigationMenu.forceMount.test.tsx +52 -0
  141. package/src/NavigationMenu/__tests__/NavigationMenu.hover-interaction.test.tsx +254 -0
  142. package/src/NavigationMenu/__tests__/NavigationMenu.indicator.test.tsx +252 -0
  143. package/src/NavigationMenu/__tests__/NavigationMenu.keyboard-interaction.test.tsx +294 -0
  144. package/src/NavigationMenu/__tests__/NavigationMenu.keyboard-navigation.test.tsx +126 -0
  145. package/src/NavigationMenu/__tests__/NavigationMenu.link.test.tsx +139 -0
  146. package/src/NavigationMenu/__tests__/NavigationMenu.motion.test.tsx +568 -0
  147. package/src/NavigationMenu/__tests__/NavigationMenu.mouse-interaction.test.tsx +76 -0
  148. package/src/NavigationMenu/__tests__/NavigationMenu.uncontrolled-state.test.tsx +30 -0
  149. package/src/NavigationMenu/__tests__/NavigationMenu.viewport.test.tsx +113 -0
  150. package/src/NavigationMenu/constants.ts +5 -0
  151. package/src/NavigationMenu/hooks/index.ts +7 -0
  152. package/src/NavigationMenu/hooks/useNavigationMenuEntry.ts +33 -0
  153. package/src/NavigationMenu/hooks/useNavigationMenuIndicator.ts +70 -0
  154. package/src/NavigationMenu/hooks/useNavigationMenuLink.ts +72 -0
  155. package/src/NavigationMenu/hooks/useNavigationMenuRoot.ts +301 -0
  156. package/src/NavigationMenu/hooks/useNavigationMenuTopLevelEntry.ts +65 -0
  157. package/src/NavigationMenu/hooks/useNavigationMenuTrigger.ts +158 -0
  158. package/src/NavigationMenu/hooks/useNavigationMenuViewport.ts +101 -0
  159. package/src/NavigationMenu/index.ts +3 -0
  160. package/src/NavigationMenu/types.ts +259 -0
  161. package/src/NavigationMenu/utils.ts +89 -0
  162. package/src/Popover/Popover.tsx +427 -0
  163. package/src/Popover/PopoverContext.ts +13 -0
  164. package/src/Popover/README.md +106 -0
  165. package/src/Popover/__tests__/Popover.anchor.test.tsx +38 -0
  166. package/src/Popover/__tests__/Popover.asChild.test.tsx +160 -0
  167. package/src/Popover/__tests__/Popover.basic-rendering.test.tsx +106 -0
  168. package/src/Popover/__tests__/Popover.click-outside.test.tsx +149 -0
  169. package/src/Popover/__tests__/Popover.close.test.tsx +70 -0
  170. package/src/Popover/__tests__/Popover.content.test.tsx +58 -0
  171. package/src/Popover/__tests__/Popover.controlled-state.test.tsx +45 -0
  172. package/src/Popover/__tests__/Popover.error-handling.test.tsx +51 -0
  173. package/src/Popover/__tests__/Popover.focus.test.tsx +43 -0
  174. package/src/Popover/__tests__/Popover.keyboard-interaction.test.tsx +71 -0
  175. package/src/Popover/__tests__/Popover.mouse-interaction.test.tsx +69 -0
  176. package/src/Popover/__tests__/Popover.uncontrolled-state.test.tsx +44 -0
  177. package/src/Popover/hooks/index.ts +3 -0
  178. package/src/Popover/hooks/usePopoverContent.ts +98 -0
  179. package/src/Popover/hooks/usePopoverRoot.ts +84 -0
  180. package/src/Popover/hooks/usePopoverTrigger.ts +33 -0
  181. package/src/Popover/index.ts +2 -0
  182. package/src/Popover/types.ts +197 -0
  183. package/src/Portal/Portal.tsx +50 -10
  184. package/src/Portal/__tests__/Portal.basic-rendering.test.tsx +5 -0
  185. package/src/Portal/types.ts +5 -0
  186. package/src/Progress/Progress.tsx +33 -1
  187. package/src/Progress/__tests__/Progress.basic-rendering.test.tsx +10 -0
  188. package/src/Progress/__tests__/Progress.value.test.tsx +21 -0
  189. package/src/Progress/types.ts +20 -4
  190. package/src/Radio/Radio.tsx +9 -1
  191. package/src/Radio/__tests__/Radio.basic-rendering.test.tsx +14 -0
  192. package/src/Radio/hooks/useRadioRoot.ts +3 -0
  193. package/src/Radio/types.ts +20 -0
  194. package/src/RadioCard/RadioCard.tsx +11 -0
  195. package/src/RadioCard/__tests__/RadioCard.basic-rendering.test.tsx +8 -0
  196. package/src/RadioCard/__tests__/RadioCard.disabled-items.test.tsx +27 -0
  197. package/src/RadioCard/__tests__/RadioCard.keyboard-interaction.test.tsx +53 -0
  198. package/src/RadioCard/__tests__/RadioCard.ref-forwarding.test.tsx +54 -0
  199. package/src/RadioCard/__tests__/RadioCard.uncontrolled-state.test.tsx +21 -0
  200. package/src/RadioCard/hooks/useRadioCardRoot.ts +11 -0
  201. package/src/RadioCard/types.ts +95 -22
  202. package/src/RadioGroup/RadioGroup.tsx +11 -0
  203. package/src/RadioGroup/__tests__/RadioGroup.basic-rendering.test.tsx +8 -0
  204. package/src/RadioGroup/__tests__/RadioGroup.disabled.test.tsx +27 -0
  205. package/src/RadioGroup/__tests__/RadioGroup.ref-forwarding.test.tsx +29 -0
  206. package/src/RadioGroup/__tests__/RadioGroup.roving-tabindex.test.tsx +31 -0
  207. package/src/RadioGroup/hooks/useRadioGroupRoot.ts +11 -0
  208. package/src/RadioGroup/types.ts +73 -9
  209. package/src/SegmentedControl/README.md +154 -0
  210. package/src/SegmentedControl/SegmentedControl.tsx +309 -0
  211. package/src/SegmentedControl/SegmentedControlContext.ts +28 -0
  212. package/src/SegmentedControl/__tests__/SegmentedControl.asChild.test.tsx +50 -0
  213. package/src/SegmentedControl/__tests__/SegmentedControl.basic-rendering.test.tsx +100 -0
  214. package/src/SegmentedControl/__tests__/SegmentedControl.controlled-state.test.tsx +107 -0
  215. package/src/SegmentedControl/__tests__/SegmentedControl.disabled-keydown-guards.test.tsx +62 -0
  216. package/src/SegmentedControl/__tests__/SegmentedControl.disabled.test.tsx +151 -0
  217. package/src/SegmentedControl/__tests__/SegmentedControl.error-handling.test.tsx +17 -0
  218. package/src/SegmentedControl/__tests__/SegmentedControl.keyboard-interaction.test.tsx +117 -0
  219. package/src/SegmentedControl/__tests__/SegmentedControl.orientation.test.tsx +54 -0
  220. package/src/SegmentedControl/__tests__/SegmentedControl.reading-direction.test.tsx +56 -0
  221. package/src/SegmentedControl/__tests__/SegmentedControl.ref-forwarding.test.tsx +45 -0
  222. package/src/SegmentedControl/__tests__/SegmentedControl.roving-tabindex.test.tsx +104 -0
  223. package/src/SegmentedControl/__tests__/SegmentedControl.uncontrolled-state.test.tsx +94 -0
  224. package/src/SegmentedControl/hooks/index.ts +2 -0
  225. package/src/SegmentedControl/hooks/useSegmentedControlContext.ts +1 -0
  226. package/src/SegmentedControl/hooks/useSegmentedControlRoot.ts +86 -0
  227. package/src/SegmentedControl/index.ts +2 -0
  228. package/src/SegmentedControl/types.ts +138 -0
  229. package/src/Select/README.md +122 -157
  230. package/src/Select/Select.tsx +595 -68
  231. package/src/Select/SelectContext.ts +38 -0
  232. package/src/Select/SelectItemIndicatorContext.ts +29 -0
  233. package/src/Select/__tests__/Select.asChild.test.tsx +2 -2
  234. package/src/Select/__tests__/Select.basic-rendering.test.tsx +22 -3
  235. package/src/Select/__tests__/Select.controlled-state.test.tsx +6 -6
  236. package/src/Select/__tests__/Select.data-attributes.test.tsx +4 -4
  237. package/src/Select/__tests__/Select.field-integration.test.tsx +11 -11
  238. package/src/Select/__tests__/Select.form.test.tsx +38 -0
  239. package/src/Select/__tests__/Select.group.test.tsx +6 -6
  240. package/src/Select/__tests__/Select.keyboard-edge.test.tsx +95 -0
  241. package/src/Select/__tests__/Select.keyboard.test.tsx +124 -0
  242. package/src/Select/__tests__/Select.mutation-hardening.test.tsx +317 -0
  243. package/src/Select/__tests__/Select.native-item.test.tsx +30 -0
  244. package/src/Select/__tests__/Select.open-close.test.tsx +57 -0
  245. package/src/Select/__tests__/Select.placeholder.test.tsx +4 -4
  246. package/src/Select/__tests__/Select.rich-asChild.test.tsx +60 -0
  247. package/src/Select/__tests__/Select.rich-mode.test.tsx +16 -0
  248. package/src/Select/__tests__/Select.selection.test.tsx +104 -0
  249. package/src/Select/__tests__/Select.separator.test.tsx +71 -0
  250. package/src/Select/__tests__/Select.strict-context.test.tsx +36 -0
  251. package/src/Select/__tests__/Select.typeahead.test.tsx +156 -0
  252. package/src/Select/__tests__/Select.value-mirror.test.tsx +240 -0
  253. package/src/Select/constants.ts +8 -0
  254. package/src/Select/hooks/index.ts +2 -0
  255. package/src/Select/hooks/useSelectContent.ts +149 -0
  256. package/src/Select/hooks/useSelectRoot.ts +149 -0
  257. package/src/Select/types.ts +199 -31
  258. package/src/SkipNav/SkipNav.tsx +69 -29
  259. package/src/SkipNav/__tests__/SkipNav.basic-rendering.test.tsx +6 -0
  260. package/src/SkipNav/types.ts +17 -9
  261. package/src/Slider/Slider.tsx +33 -1
  262. package/src/Slider/SliderContext.ts +28 -1
  263. package/src/Slider/types.ts +157 -9
  264. package/src/Slot/Slot.tsx +94 -19
  265. package/src/Slot/__tests__/Slot.test.tsx +110 -0
  266. package/src/Slot/composeEventHandlers.ts +26 -2
  267. package/src/Slot/types.ts +16 -0
  268. package/src/Status/Status.tsx +36 -15
  269. package/src/Status/__tests__/Status.test.tsx +5 -0
  270. package/src/Status/types.ts +12 -1
  271. package/src/Switch/Switch.tsx +15 -2
  272. package/src/Switch/__tests__/Switch.basic-rendering.test.tsx +7 -0
  273. package/src/Switch/hooks/useSwitchRoot.ts +3 -0
  274. package/src/Switch/types.ts +49 -22
  275. package/src/Table/Table.tsx +44 -10
  276. package/src/Table/types.ts +66 -17
  277. package/src/Tabs/Tabs.tsx +8 -0
  278. package/src/Tabs/types.ts +29 -4
  279. package/src/Textarea/Textarea.tsx +9 -1
  280. package/src/Textarea/__tests__/Textarea.basic-rendering.test.tsx +5 -0
  281. package/src/Textarea/types.ts +20 -3
  282. package/src/Toggle/Toggle.tsx +51 -13
  283. package/src/Toggle/__tests__/Toggle.basic-rendering.test.tsx +5 -0
  284. package/src/Toggle/types.ts +54 -4
  285. package/src/ToggleGroup/ToggleGroup.tsx +14 -1
  286. package/src/ToggleGroup/__tests__/ToggleGroup.asChild.test.tsx +20 -0
  287. package/src/ToggleGroup/__tests__/ToggleGroup.basic-rendering.test.tsx +9 -0
  288. package/src/ToggleGroup/__tests__/ToggleGroup.dynamic-items.test.tsx +70 -0
  289. package/src/ToggleGroup/__tests__/ToggleGroup.error-handling.test.tsx +23 -0
  290. package/src/ToggleGroup/__tests__/ToggleGroup.keyboard-interaction.test.tsx +23 -1
  291. package/src/ToggleGroup/__tests__/ToggleGroup.ref-forwarding.test.tsx +58 -0
  292. package/src/ToggleGroup/hooks/useToggleGroupRoot.ts +21 -1
  293. package/src/ToggleGroup/types.ts +55 -2
  294. package/src/Tooltip/Tooltip.tsx +98 -20
  295. package/src/Tooltip/hooks/__tests__/useTooltipProvider.test.tsx +40 -0
  296. package/src/Tooltip/hooks/useTooltipProvider.ts +13 -1
  297. package/src/Tooltip/types.ts +89 -8
  298. package/src/Tree/Tree.tsx +187 -33
  299. package/src/Tree/types.ts +54 -12
  300. package/src/VisuallyHidden/VisuallyHidden.tsx +33 -8
  301. package/src/VisuallyHidden/__tests__/VisuallyHidden.test.tsx +15 -1
  302. package/src/VisuallyHidden/types.ts +11 -1
  303. package/src/index.ts +4 -0
  304. package/src/test/intersectionObserverPolyfill.ts +7 -0
  305. package/src/test/resizeObserverPolyfill.ts +80 -0
  306. package/src/utils/focusable.ts +15 -0
  307. package/src/utils/index.ts +1 -0
@@ -0,0 +1,111 @@
1
+ import "./dialog-polyfill";
2
+
3
+ import { render, screen } from "@testing-library/react";
4
+
5
+ import { Drawer } from "../Drawer";
6
+
7
+ describe("Drawer — rendering & structure", () => {
8
+ it("Trigger renders a <button> with dialog ARIA", () => {
9
+ // Arrange / Act
10
+ render(
11
+ <Drawer.Root>
12
+ <Drawer.Trigger>Open</Drawer.Trigger>
13
+ </Drawer.Root>,
14
+ );
15
+
16
+ // Assert
17
+ const trigger = screen.getByRole("button", { name: "Open" });
18
+ expect(trigger).toHaveAttribute("type", "button");
19
+ expect(trigger).toHaveAttribute("aria-haspopup", "dialog");
20
+ expect(trigger).toHaveAttribute("aria-expanded", "false");
21
+ });
22
+
23
+ it("Content renders a native <dialog> when open", () => {
24
+ // Arrange / Act
25
+ render(
26
+ <Drawer.Root defaultOpen>
27
+ <Drawer.Content data-testid="drawer">body</Drawer.Content>
28
+ </Drawer.Root>,
29
+ );
30
+
31
+ // Assert
32
+ const dialog = screen.getByTestId("drawer");
33
+ expect(dialog).toBeInTheDocument();
34
+ expect(dialog.tagName).toBe("DIALOG");
35
+ });
36
+
37
+ it("Title and Description render and auto-wire the dialog's ARIA", () => {
38
+ // Arrange / Act
39
+ render(
40
+ <Drawer.Root defaultOpen>
41
+ <Drawer.Content data-testid="drawer">
42
+ <Drawer.Title>Filters</Drawer.Title>
43
+ <Drawer.Description>Narrow the results.</Drawer.Description>
44
+ </Drawer.Content>
45
+ </Drawer.Root>,
46
+ );
47
+
48
+ // Assert
49
+ const title = screen.getByRole("heading", { name: "Filters", level: 2 });
50
+ const description = screen.getByText("Narrow the results.");
51
+ const dialog = screen.getByTestId("drawer");
52
+ expect(dialog).toHaveAttribute("aria-labelledby", title.id);
53
+ expect(dialog).toHaveAttribute("aria-describedby", description.id);
54
+ expect(description.tagName).toBe("P");
55
+ });
56
+
57
+ it("Portal escapes to document.body and Overlay is a decorative sibling", () => {
58
+ // Arrange / Act
59
+ render(
60
+ <div data-testid="host">
61
+ <Drawer.Root defaultOpen>
62
+ <Drawer.Portal>
63
+ <Drawer.Overlay data-testid="overlay" />
64
+ <Drawer.Content data-testid="drawer">body</Drawer.Content>
65
+ </Drawer.Portal>
66
+ </Drawer.Root>
67
+ </div>,
68
+ );
69
+
70
+ // Assert
71
+ const overlay = screen.getByTestId("overlay");
72
+ expect(overlay).toHaveAttribute("aria-hidden", "true");
73
+ expect(overlay).toHaveAttribute("data-state", "open");
74
+ // portalled out of the host wrapper, onto document.body
75
+ expect(screen.getByTestId("host")).not.toContainElement(
76
+ screen.getByTestId("drawer"),
77
+ );
78
+ });
79
+
80
+ it("Close renders a <button>", () => {
81
+ // Arrange / Act
82
+ render(
83
+ <Drawer.Root defaultOpen>
84
+ <Drawer.Content>
85
+ <Drawer.Close>Done</Drawer.Close>
86
+ </Drawer.Content>
87
+ </Drawer.Root>,
88
+ );
89
+
90
+ // Assert
91
+ expect(screen.getByRole("button", { name: "Done" })).toHaveAttribute(
92
+ "type",
93
+ "button",
94
+ );
95
+ });
96
+
97
+ it("forwards asChild on the Trigger to the consumer element", () => {
98
+ // Arrange / Act
99
+ render(
100
+ <Drawer.Root>
101
+ <Drawer.Trigger asChild>
102
+ <a href="/cart">Cart</a>
103
+ </Drawer.Trigger>
104
+ </Drawer.Root>,
105
+ );
106
+
107
+ // Assert
108
+ const link = screen.getByRole("link", { name: "Cart" });
109
+ expect(link).toHaveAttribute("aria-haspopup", "dialog");
110
+ });
111
+ });
@@ -0,0 +1,69 @@
1
+ import "./dialog-polyfill";
2
+
3
+ import { render, screen } from "@testing-library/react";
4
+
5
+ import { Drawer } from "../Drawer";
6
+ import type { DrawerSide } from "../types";
7
+
8
+ const SIDES: DrawerSide[] = ["top", "right", "bottom", "left"];
9
+
10
+ describe("Drawer.Content — side axis", () => {
11
+ it("defaults data-side to 'right' when side is omitted", () => {
12
+ // Arrange / Act
13
+ render(
14
+ <Drawer.Root defaultOpen>
15
+ <Drawer.Content data-testid="drawer">body</Drawer.Content>
16
+ </Drawer.Root>,
17
+ );
18
+
19
+ // Assert
20
+ expect(screen.getByTestId("drawer")).toHaveAttribute("data-side", "right");
21
+ });
22
+
23
+ it.each(SIDES)(
24
+ "emits data-side='%s' on the dialog",
25
+ (side) => {
26
+ // Arrange / Act
27
+ render(
28
+ <Drawer.Root defaultOpen>
29
+ <Drawer.Content data-testid="drawer" side={side}>
30
+ body
31
+ </Drawer.Content>
32
+ </Drawer.Root>,
33
+ );
34
+
35
+ // Assert
36
+ expect(screen.getByTestId("drawer")).toHaveAttribute("data-side", side);
37
+ },
38
+ );
39
+
40
+ it("does not leak the `side` prop as a raw attribute on the dialog", () => {
41
+ // Arrange / Act
42
+ render(
43
+ <Drawer.Root defaultOpen>
44
+ <Drawer.Content data-testid="drawer" side="left">
45
+ body
46
+ </Drawer.Content>
47
+ </Drawer.Root>,
48
+ );
49
+
50
+ // Assert
51
+ expect(screen.getByTestId("drawer")).not.toHaveAttribute("side");
52
+ });
53
+
54
+ it("keeps data-state alongside data-side", () => {
55
+ // Arrange / Act
56
+ render(
57
+ <Drawer.Root defaultOpen>
58
+ <Drawer.Content data-testid="drawer" side="bottom">
59
+ body
60
+ </Drawer.Content>
61
+ </Drawer.Root>,
62
+ );
63
+
64
+ // Assert
65
+ const dialog = screen.getByTestId("drawer");
66
+ expect(dialog).toHaveAttribute("data-side", "bottom");
67
+ expect(dialog).toHaveAttribute("data-state", "open");
68
+ });
69
+ });
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Minimal scoped polyfill for the native <dialog> element in jsdom 29.
3
+ *
4
+ * jsdom does not implement showModal() / close() on HTMLDialogElement, so
5
+ * integration tests that render Drawer.Content (a native <dialog>, via
6
+ * Modal.Content) need a shim that:
7
+ * - toggles the `open` attribute (which is what .open reflects)
8
+ * - dispatches the native `close` event on close()
9
+ *
10
+ * The Drawer suite drives open/close through the trigger, the Close button,
11
+ * and the imperative API — the Esc/`cancel` and click-outside paths are
12
+ * Modal's behaviour and are covered by Modal's own suite, so this shim stays
13
+ * minimal. Mirrors Modal's scoped polyfill — imported explicitly where needed
14
+ * rather than installed globally, so other components keep exercising the real
15
+ * DOM.
16
+ */
17
+
18
+ type DialogProto = HTMLDialogElement & { returnValue: string };
19
+
20
+ const proto = HTMLDialogElement.prototype as DialogProto;
21
+
22
+ if (typeof proto.showModal !== "function") {
23
+ proto.showModal = function showModal(this: HTMLDialogElement) {
24
+ this.setAttribute("open", "");
25
+ };
26
+ }
27
+
28
+ if (typeof proto.close !== "function") {
29
+ proto.close = function close(this: HTMLDialogElement, returnValue?: string) {
30
+ if (!this.hasAttribute("open")) return;
31
+ this.removeAttribute("open");
32
+ if (returnValue !== undefined) {
33
+ (this as DialogProto).returnValue = String(returnValue);
34
+ }
35
+ this.dispatchEvent(new Event("close"));
36
+ };
37
+ }
38
+
39
+ export {};
@@ -0,0 +1,2 @@
1
+ export * from "./Drawer";
2
+ export * from "./types";
@@ -0,0 +1,66 @@
1
+ import type { Ref } from "react";
2
+
3
+ import type {
4
+ ModalCloseProps,
5
+ ModalContentProps,
6
+ ModalDescriptionProps,
7
+ ModalImperativeApi,
8
+ ModalOverlayProps,
9
+ ModalPortalProps,
10
+ ModalRootProps,
11
+ ModalTitleProps,
12
+ ModalTriggerProps,
13
+ } from "../Modal/types";
14
+
15
+ /**
16
+ * Which edge the drawer slides in from.
17
+ *
18
+ * - `"top"` / `"bottom"` — a horizontal sheet spanning the viewport width.
19
+ * - `"left"` / `"right"` — a vertical panel spanning the viewport height.
20
+ */
21
+ export type DrawerSide = "top" | "right" | "bottom" | "left";
22
+
23
+ /**
24
+ * Imperative handle exposed on `Drawer.Root`'s `ref`, letting callers open
25
+ * and close the drawer programmatically. Behaviour is inherited from
26
+ * {@link ModalImperativeApi | Modal} — a drawer is a Modal that slides from an edge.
27
+ */
28
+ export type DrawerImperativeApi = ModalImperativeApi;
29
+
30
+ /** Props for `Drawer.Root`, in either the controlled or uncontrolled mode. */
31
+ export type DrawerRootProps = ModalRootProps;
32
+
33
+ /** Props for `Drawer.Trigger` — a `<button>` that opens the drawer. */
34
+ export type DrawerTriggerProps = ModalTriggerProps;
35
+
36
+ /** Props for `Drawer.Portal` — renders its children into `container`. */
37
+ export type DrawerPortalProps = ModalPortalProps;
38
+
39
+ /** Props for `Drawer.Overlay` — the click-outside backdrop. */
40
+ export type DrawerOverlayProps = ModalOverlayProps;
41
+
42
+ /** Props for `Drawer.Title` — supplies the drawer's accessible name. */
43
+ export type DrawerTitleProps = ModalTitleProps;
44
+
45
+ /** Props for `Drawer.Description` — supplies the drawer's accessible description. */
46
+ export type DrawerDescriptionProps = ModalDescriptionProps;
47
+
48
+ /** Props for `Drawer.Close` — a `<button>` that closes the drawer. */
49
+ export type DrawerCloseProps = ModalCloseProps;
50
+
51
+ /**
52
+ * Props for `Drawer.Content` — the native `<dialog>` props plus the
53
+ * drawer-specific {@link DrawerContentProps.side | `side`} axis.
54
+ */
55
+ export type DrawerContentProps = ModalContentProps & {
56
+ /** Ref to the underlying native `<dialog>` element. */
57
+ ref?: Ref<HTMLDialogElement>;
58
+ /**
59
+ * Which edge the drawer slides in from. Emitted verbatim as `data-side`
60
+ * on the `<dialog>`, which the styled layer keys off to position and
61
+ * animate the panel against that edge. Purely presentational — it does
62
+ * not change any behaviour, focus, or ARIA.
63
+ * @default "right"
64
+ */
65
+ side?: DrawerSide;
66
+ };
@@ -121,6 +121,8 @@ DropdownRoot.displayName = "DropdownRoot";
121
121
  * component). All ARIA attributes and event handlers are merged onto the
122
122
  * child following the {@link Slot} composition rules.
123
123
  *
124
+ * @extends HTMLButtonElement
125
+ *
124
126
  * @example With an anchor via `asChild`
125
127
  * ```tsx
126
128
  * <Dropdown.Trigger asChild>
@@ -180,6 +182,17 @@ DropdownTrigger.displayName = "DropdownTrigger";
180
182
  * so `ArrowDown` past a {@link DropdownSubTrigger | `Dropdown.SubTrigger`}
181
183
  * lands on the next sibling item in the parent rather than getting
182
184
  * stuck on a non-focusable item inside a closed sub-popover.
185
+ *
186
+ * @extends HTMLMenuElement
187
+ *
188
+ * @example
189
+ * ```tsx
190
+ * <Dropdown.Content>
191
+ * <Dropdown.Item onSelect={rename}>Rename</Dropdown.Item>
192
+ * <Dropdown.Separator />
193
+ * <Dropdown.Item onSelect={remove}>Delete</Dropdown.Item>
194
+ * </Dropdown.Content>
195
+ * ```
183
196
  */
184
197
  export function DropdownContent({
185
198
  children,
@@ -218,6 +231,18 @@ DropdownContent.displayName = "DropdownContent";
218
231
  *
219
232
  * Disabled items receive `aria-disabled="true"` and are skipped by arrow
220
233
  * navigation, typeahead, and activation handlers.
234
+ *
235
+ * @extends HTMLLIElement
236
+ *
237
+ * @example Keep the menu open after selecting
238
+ * ```tsx
239
+ * <Dropdown.Item onSelect={(event) => {
240
+ * copyToClipboard();
241
+ * event.preventDefault();
242
+ * }}>
243
+ * Copy link
244
+ * </Dropdown.Item>
245
+ * ```
221
246
  */
222
247
  export function DropdownItem({
223
248
  children,
@@ -248,6 +273,15 @@ DropdownItem.displayName = "DropdownItem";
248
273
  * A visual separator between groups of items. Renders a `<li role="separator">`
249
274
  * by default; pass `asChild` to render any element with separator semantics.
250
275
  * Non-interactive — skipped by focus, arrow navigation, and typeahead.
276
+ *
277
+ * @extends HTMLLIElement
278
+ *
279
+ * @example
280
+ * ```tsx
281
+ * <Dropdown.Item>Cut</Dropdown.Item>
282
+ * <Dropdown.Separator />
283
+ * <Dropdown.Item>Paste</Dropdown.Item>
284
+ * ```
251
285
  */
252
286
  export function DropdownSeparator({
253
287
  asChild = false,
@@ -275,6 +309,17 @@ DropdownSeparator.displayName = "DropdownSeparator";
275
309
  * wired automatically via `aria-labelledby`. Nest a `Dropdown.Label` as the
276
310
  * first child to provide the accessible name; screen readers will announce
277
311
  * the group when arrowing into it.
312
+ *
313
+ * @extends HTMLLIElement
314
+ *
315
+ * @example
316
+ * ```tsx
317
+ * <Dropdown.Group>
318
+ * <Dropdown.Label>Sort by</Dropdown.Label>
319
+ * <Dropdown.Item>Name</Dropdown.Item>
320
+ * <Dropdown.Item>Date</Dropdown.Item>
321
+ * </Dropdown.Group>
322
+ * ```
278
323
  */
279
324
  export function DropdownGroup({
280
325
  children,
@@ -307,6 +352,16 @@ DropdownGroup.displayName = "DropdownGroup";
307
352
  *
308
353
  * Renders a `<li>` by default; pass `asChild` to render any element. A
309
354
  * caller-supplied `id` takes precedence over the auto-generated one.
355
+ *
356
+ * @extends HTMLLIElement
357
+ *
358
+ * @example
359
+ * ```tsx
360
+ * <Dropdown.Group>
361
+ * <Dropdown.Label>Actions</Dropdown.Label>
362
+ * <Dropdown.Item>Duplicate</Dropdown.Item>
363
+ * </Dropdown.Group>
364
+ * ```
310
365
  */
311
366
  export function DropdownLabel({
312
367
  id,
@@ -350,6 +405,18 @@ DropdownLabel.displayName = "DropdownLabel";
350
405
  * context and exposes `data-state` for styling.
351
406
  *
352
407
  * Disabled items receive `aria-disabled="true"` and no-op on activation.
408
+ *
409
+ * @extends HTMLLIElement
410
+ *
411
+ * @example Controlled checkbox item
412
+ * ```tsx
413
+ * <Dropdown.CheckboxItem checked={showGrid} onCheckedChange={setShowGrid}>
414
+ * <Dropdown.ItemIndicator>
415
+ * <CheckIcon />
416
+ * </Dropdown.ItemIndicator>
417
+ * Show grid
418
+ * </Dropdown.CheckboxItem>
419
+ * ```
353
420
  */
354
421
  export function DropdownCheckboxItem({
355
422
  children,
@@ -425,6 +492,22 @@ DropdownCheckboxItem.displayName = "DropdownCheckboxItem";
425
492
  * - **Uncontrolled** — pass `defaultValue` (or omit for no initial selection).
426
493
  * Optional `onValueChange` observes selections.
427
494
  * - **Controlled** — pass `value` *and* `onValueChange` together.
495
+ *
496
+ * @extends HTMLLIElement
497
+ *
498
+ * @example
499
+ * ```tsx
500
+ * <Dropdown.RadioGroup value={sort} onValueChange={setSort}>
501
+ * <Dropdown.RadioItem value="name">
502
+ * <Dropdown.ItemIndicator>•</Dropdown.ItemIndicator>
503
+ * Name
504
+ * </Dropdown.RadioItem>
505
+ * <Dropdown.RadioItem value="date">
506
+ * <Dropdown.ItemIndicator>•</Dropdown.ItemIndicator>
507
+ * Date
508
+ * </Dropdown.RadioItem>
509
+ * </Dropdown.RadioGroup>
510
+ * ```
428
511
  */
429
512
  export function DropdownRadioGroup({
430
513
  defaultValue,
@@ -476,6 +559,16 @@ DropdownRadioGroup.displayName = "DropdownRadioGroup";
476
559
  * and exposes `data-state` for styling.
477
560
  *
478
561
  * Disabled items receive `aria-disabled="true"` and no-op on activation.
562
+ *
563
+ * @extends HTMLLIElement
564
+ *
565
+ * @example
566
+ * ```tsx
567
+ * <Dropdown.RadioItem value="compact">
568
+ * <Dropdown.ItemIndicator>•</Dropdown.ItemIndicator>
569
+ * Compact
570
+ * </Dropdown.RadioItem>
571
+ * ```
479
572
  */
480
573
  export function DropdownRadioItem({
481
574
  children,
@@ -555,6 +648,8 @@ DropdownRadioItem.displayName = "DropdownRadioItem";
555
648
  * the DOM node mounted in both states, which is handy when animating the
556
649
  * indicator in and out via CSS transitions or a React animation library.
557
650
  *
651
+ * @extends HTMLSpanElement
652
+ *
558
653
  * @example
559
654
  * ```tsx
560
655
  * <Dropdown.CheckboxItem checked={showBookmarks} onCheckedChange={setShowBookmarks}>
@@ -683,6 +778,18 @@ DropdownSub.displayName = "DropdownSub";
683
778
  *
684
779
  * Disabled triggers receive `aria-disabled="true"` and ignore both click
685
780
  * and the open arrow key.
781
+ *
782
+ * @extends HTMLLIElement
783
+ *
784
+ * @example
785
+ * ```tsx
786
+ * <Dropdown.Sub>
787
+ * <Dropdown.SubTrigger>Share</Dropdown.SubTrigger>
788
+ * <Dropdown.SubContent>
789
+ * <Dropdown.Item>Email link</Dropdown.Item>
790
+ * </Dropdown.SubContent>
791
+ * </Dropdown.Sub>
792
+ * ```
686
793
  */
687
794
  export function DropdownSubTrigger({
688
795
  children,
@@ -749,6 +856,16 @@ DropdownSubTrigger.displayName = "DropdownSubTrigger";
749
856
  * `ArrowLeft` in `"ltr"`, `ArrowRight` in `"rtl"`. All other keys bubble
750
857
  * to the parent {@link DropdownContent | `Dropdown.Content`} so arrow
751
858
  * navigation and typeahead apply to the submenu's items.
859
+ *
860
+ * @extends HTMLMenuElement
861
+ *
862
+ * @example
863
+ * ```tsx
864
+ * <Dropdown.SubContent>
865
+ * <Dropdown.Item>Project A</Dropdown.Item>
866
+ * <Dropdown.Item>Project B</Dropdown.Item>
867
+ * </Dropdown.SubContent>
868
+ * ```
752
869
  */
753
870
  export function DropdownSubContent({
754
871
  children,