@astryxdesign/core 0.1.4 → 0.1.5-canary.76e6697

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 (635) hide show
  1. package/CHANGELOG.md +141 -0
  2. package/dist/AlertDialog/AlertDialog.d.ts +1 -1
  3. package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
  4. package/dist/AlertDialog/AlertDialog.js +20 -18
  5. package/dist/AlertDialog/useImperativeAlertDialog.d.ts +0 -2
  6. package/dist/AlertDialog/useImperativeAlertDialog.d.ts.map +1 -1
  7. package/dist/AlertDialog/useImperativeAlertDialog.js +3 -5
  8. package/dist/AppShell/AppShell.d.ts +1 -1
  9. package/dist/AppShell/AppShell.d.ts.map +1 -1
  10. package/dist/AppShell/AppShell.js +55 -53
  11. package/dist/AspectRatio/AspectRatio.d.ts +48 -10
  12. package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
  13. package/dist/AspectRatio/AspectRatio.js +33 -12
  14. package/dist/AspectRatio/index.d.ts +1 -1
  15. package/dist/AspectRatio/index.d.ts.map +1 -1
  16. package/dist/Avatar/Avatar.d.ts.map +1 -1
  17. package/dist/Avatar/Avatar.js +28 -26
  18. package/dist/Avatar/AvatarStatusDot.js +6 -6
  19. package/dist/AvatarGroup/AvatarGroup.js +6 -6
  20. package/dist/AvatarGroup/AvatarGroupOverflow.js +7 -7
  21. package/dist/Badge/Badge.js +3 -3
  22. package/dist/Banner/Banner.d.ts.map +1 -1
  23. package/dist/Banner/Banner.js +32 -25
  24. package/dist/Blockquote/Blockquote.js +7 -7
  25. package/dist/Breadcrumbs/BreadcrumbItem.js +17 -17
  26. package/dist/Breadcrumbs/Breadcrumbs.js +7 -7
  27. package/dist/Button/Button.js +19 -19
  28. package/dist/ButtonGroup/ButtonGroup.js +7 -7
  29. package/dist/Calendar/Calendar.d.ts.map +1 -1
  30. package/dist/Calendar/Calendar.js +68 -48
  31. package/dist/Calendar/dayCellUtils.d.ts.map +1 -1
  32. package/dist/Calendar/styles.d.ts +2 -2
  33. package/dist/Calendar/styles.d.ts.map +1 -1
  34. package/dist/Calendar/styles.js +2 -2
  35. package/dist/Card/Card.d.ts +4 -2
  36. package/dist/Card/Card.d.ts.map +1 -1
  37. package/dist/Card/Card.js +18 -10
  38. package/dist/Carousel/Carousel.d.ts.map +1 -1
  39. package/dist/Carousel/Carousel.js +24 -21
  40. package/dist/Center/Center.js +3 -3
  41. package/dist/Chat/ChatComposer.js +23 -23
  42. package/dist/Chat/ChatComposerDrawer.js +14 -14
  43. package/dist/Chat/ChatComposerInput.d.ts +1 -1
  44. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  45. package/dist/Chat/ChatComposerInput.js +27 -15
  46. package/dist/Chat/ChatDictationButton.d.ts +1 -1
  47. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  48. package/dist/Chat/ChatDictationButton.js +13 -11
  49. package/dist/Chat/ChatLayout.js +17 -17
  50. package/dist/Chat/ChatLayoutScrollButton.d.ts +1 -1
  51. package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
  52. package/dist/Chat/ChatLayoutScrollButton.js +12 -10
  53. package/dist/Chat/ChatMessage.js +12 -12
  54. package/dist/Chat/ChatMessageBubble.js +7 -7
  55. package/dist/Chat/ChatMessageList.js +14 -14
  56. package/dist/Chat/ChatMessageMetadata.d.ts +1 -1
  57. package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
  58. package/dist/Chat/ChatMessageMetadata.js +14 -12
  59. package/dist/Chat/ChatPastedTextToken.js +13 -13
  60. package/dist/Chat/ChatSendButton.js +3 -3
  61. package/dist/Chat/ChatSystemMessage.d.ts +1 -1
  62. package/dist/Chat/ChatSystemMessage.d.ts.map +1 -1
  63. package/dist/Chat/ChatSystemMessage.js +17 -17
  64. package/dist/Chat/ChatTokenizedText.d.ts +1 -1
  65. package/dist/Chat/ChatTokenizedText.d.ts.map +1 -1
  66. package/dist/Chat/ChatTokenizedText.js +24 -15
  67. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  68. package/dist/Chat/ChatToolCalls.js +81 -74
  69. package/dist/Chat/index.d.ts +1 -1
  70. package/dist/Chat/index.d.ts.map +1 -1
  71. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  72. package/dist/Chat/useTriggerMenu.js +15 -15
  73. package/dist/CheckboxInput/CheckboxInput.d.ts +1 -1
  74. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  75. package/dist/CheckboxInput/CheckboxInput.js +25 -23
  76. package/dist/CheckboxList/CheckboxList.js +9 -9
  77. package/dist/CheckboxList/CheckboxListItem.d.ts +1 -1
  78. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  79. package/dist/CheckboxList/CheckboxListItem.js +8 -6
  80. package/dist/Citation/Citation.js +10 -10
  81. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  82. package/dist/ClickableCard/ClickableCard.js +32 -9
  83. package/dist/Code/Code.js +3 -3
  84. package/dist/CodeBlock/CodeBlock.d.ts +10 -1
  85. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  86. package/dist/CodeBlock/CodeBlock.js +88 -52
  87. package/dist/Collapsible/Collapsible.d.ts +9 -3
  88. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  89. package/dist/Collapsible/Collapsible.js +109 -22
  90. package/dist/Collapsible/CollapsibleGroup.d.ts +37 -7
  91. package/dist/Collapsible/CollapsibleGroup.d.ts.map +1 -1
  92. package/dist/Collapsible/CollapsibleGroup.js +64 -12
  93. package/dist/Collapsible/CollapsibleGroupContext.d.ts +24 -0
  94. package/dist/Collapsible/CollapsibleGroupContext.d.ts.map +1 -1
  95. package/dist/Collapsible/CollapsibleGroupContext.js +26 -3
  96. package/dist/Collapsible/index.d.ts +1 -0
  97. package/dist/Collapsible/index.d.ts.map +1 -1
  98. package/dist/Collapsible/useCollapsible.d.ts +4 -1
  99. package/dist/Collapsible/useCollapsible.d.ts.map +1 -1
  100. package/dist/Collapsible/useCollapsible.js +20 -6
  101. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  102. package/dist/CommandPalette/CommandPalette.js +36 -34
  103. package/dist/CommandPalette/CommandPaletteEmpty.js +3 -3
  104. package/dist/CommandPalette/CommandPaletteFooter.js +16 -16
  105. package/dist/CommandPalette/CommandPaletteGroup.js +5 -5
  106. package/dist/CommandPalette/CommandPaletteInput.js +13 -13
  107. package/dist/CommandPalette/CommandPaletteItem.js +3 -3
  108. package/dist/CommandPalette/CommandPaletteList.js +3 -3
  109. package/dist/ContextMenu/ContextMenu.d.ts +7 -2
  110. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  111. package/dist/ContextMenu/ContextMenu.js +76 -43
  112. package/dist/DateInput/DateInput.js +18 -18
  113. package/dist/DateRangeInput/DateRangeInput.js +21 -21
  114. package/dist/DateTimeInput/DateTimeInput.d.ts +5 -2
  115. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  116. package/dist/DateTimeInput/DateTimeInput.js +30 -26
  117. package/dist/DateTimeInput/index.d.ts +1 -1
  118. package/dist/DateTimeInput/index.d.ts.map +1 -1
  119. package/dist/Dialog/Dialog.js +9 -9
  120. package/dist/Dialog/DialogHeader.js +16 -16
  121. package/dist/Dialog/useImperativeDialog.js +3 -3
  122. package/dist/Divider/Divider.js +7 -7
  123. package/dist/DropdownMenu/DropdownMenu.js +10 -10
  124. package/dist/DropdownMenu/DropdownMenuItem.d.ts +2 -17
  125. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  126. package/dist/DropdownMenu/DropdownMenuItem.js +3 -3
  127. package/dist/DropdownMenu/renderDropdownItems.js +10 -10
  128. package/dist/EmptyState/EmptyState.js +9 -9
  129. package/dist/Field/Field.js +17 -17
  130. package/dist/Field/FieldLabel.js +15 -15
  131. package/dist/Field/InputClearButton.js +5 -5
  132. package/dist/FieldStatus/FieldStatus.js +3 -3
  133. package/dist/FileInput/FileInput.js +29 -29
  134. package/dist/FormLayout/FormLayout.js +5 -5
  135. package/dist/Grid/Grid.d.ts +4 -3
  136. package/dist/Grid/Grid.d.ts.map +1 -1
  137. package/dist/Grid/Grid.js +29 -12
  138. package/dist/Grid/GridSpan.js +3 -3
  139. package/dist/HStack/HStack.js +3 -3
  140. package/dist/Heading/Heading.js +10 -10
  141. package/dist/HoverCard/HoverCard.js +9 -9
  142. package/dist/HoverCard/useHoverCard.d.ts +5 -1
  143. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  144. package/dist/HoverCard/useHoverCard.js +3 -3
  145. package/dist/Icon/Icon.d.ts.map +1 -1
  146. package/dist/Icon/Icon.js +16 -12
  147. package/dist/Icon/defaultIcons.js +116 -116
  148. package/dist/IconButton/IconButton.js +3 -3
  149. package/dist/InputGroup/InputGroup.js +11 -11
  150. package/dist/InputGroup/InputGroupText.js +3 -3
  151. package/dist/Item/Item.js +18 -18
  152. package/dist/Kbd/Kbd.js +5 -5
  153. package/dist/Layer/LayerProvider.js +7 -7
  154. package/dist/Layer/useLayer.d.ts +29 -2
  155. package/dist/Layer/useLayer.d.ts.map +1 -1
  156. package/dist/Layer/useLayer.js +44 -31
  157. package/dist/Layout/Layout.js +21 -21
  158. package/dist/Layout/LayoutContent.js +3 -3
  159. package/dist/Layout/LayoutFooter.js +5 -5
  160. package/dist/Layout/LayoutHeader.js +5 -5
  161. package/dist/Layout/LayoutPanel.js +3 -3
  162. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  163. package/dist/Lightbox/Lightbox.js +55 -31
  164. package/dist/Lightbox/useLightbox.d.ts +0 -1
  165. package/dist/Lightbox/useLightbox.d.ts.map +1 -1
  166. package/dist/Lightbox/useLightbox.js +3 -4
  167. package/dist/Link/Link.d.ts +0 -2
  168. package/dist/Link/Link.d.ts.map +1 -1
  169. package/dist/Link/Link.js +15 -17
  170. package/dist/Link/LinkProvider.js +3 -3
  171. package/dist/Link/useLinkify.js +3 -3
  172. package/dist/List/List.js +11 -11
  173. package/dist/List/ListItem.js +11 -11
  174. package/dist/Markdown/Markdown.d.ts.map +1 -1
  175. package/dist/Markdown/Markdown.js +102 -102
  176. package/dist/MetadataList/MetadataList.js +10 -10
  177. package/dist/MetadataList/MetadataListItem.js +15 -15
  178. package/dist/MobileNav/MobileNav.d.ts +1 -1
  179. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  180. package/dist/MobileNav/MobileNav.js +26 -23
  181. package/dist/MobileNav/MobileNavToggle.js +5 -5
  182. package/dist/MoreMenu/MoreMenu.js +3 -3
  183. package/dist/MultiSelector/MultiSelector.js +56 -56
  184. package/dist/NavIcon/NavIcon.js +3 -3
  185. package/dist/NavMenu/NavHeadingMenu.js +5 -5
  186. package/dist/NavMenu/NavHeadingMenuItem.js +8 -8
  187. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  188. package/dist/NumberInput/NumberInput.js +48 -32
  189. package/dist/Outline/Outline.js +15 -15
  190. package/dist/OverflowList/OverflowList.js +8 -8
  191. package/dist/Overlay/Overlay.js +3 -3
  192. package/dist/Overlay/OverlayScrim.js +5 -5
  193. package/dist/Overlay/useOverlay.d.ts +0 -3
  194. package/dist/Overlay/useOverlay.d.ts.map +1 -1
  195. package/dist/Overlay/useOverlay.js +3 -6
  196. package/dist/Pagination/Pagination.d.ts +1 -1
  197. package/dist/Pagination/Pagination.d.ts.map +1 -1
  198. package/dist/Pagination/Pagination.js +125 -72
  199. package/dist/Popover/Popover.js +16 -16
  200. package/dist/Popover/usePopover.js +7 -7
  201. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  202. package/dist/PowerSearch/PowerSearch.js +110 -92
  203. package/dist/PowerSearch/PowerSearchEditPopover.js +84 -84
  204. package/dist/PowerSearch/PowerSearchFilterEditor.js +3 -3
  205. package/dist/PowerSearch/PowerSearchToken.js +5 -5
  206. package/dist/PowerSearch/PowerSearchValueEditor.js +54 -54
  207. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  208. package/dist/ProgressBar/ProgressBar.js +25 -18
  209. package/dist/RadioList/RadioList.js +7 -7
  210. package/dist/RadioList/RadioListItem.d.ts +1 -1
  211. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  212. package/dist/RadioList/RadioListItem.js +24 -21
  213. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  214. package/dist/Resizable/ResizeHandle.js +28 -10
  215. package/dist/Section/Section.js +5 -5
  216. package/dist/SegmentedControl/SegmentedControl.d.ts +1 -1
  217. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  218. package/dist/SegmentedControl/SegmentedControl.js +32 -10
  219. package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
  220. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  221. package/dist/SegmentedControl/SegmentedControlItem.js +18 -12
  222. package/dist/SelectableCard/SelectableCard.js +5 -5
  223. package/dist/Selector/Selector.js +46 -46
  224. package/dist/Selector/SelectorOption.js +3 -3
  225. package/dist/SideNav/SideNav.d.ts.map +1 -1
  226. package/dist/SideNav/SideNav.js +38 -34
  227. package/dist/SideNav/SideNavCollapseButton.d.ts +1 -1
  228. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  229. package/dist/SideNav/SideNavCollapseButton.js +8 -6
  230. package/dist/SideNav/SideNavHeading.js +87 -87
  231. package/dist/SideNav/SideNavItem.js +42 -42
  232. package/dist/SideNav/SideNavSection.d.ts +1 -1
  233. package/dist/SideNav/SideNavSection.d.ts.map +1 -1
  234. package/dist/SideNav/SideNavSection.js +18 -16
  235. package/dist/Skeleton/Skeleton.js +3 -3
  236. package/dist/Slider/Slider.js +23 -23
  237. package/dist/Spinner/Spinner.js +9 -9
  238. package/dist/StatusDot/StatusDot.js +5 -5
  239. package/dist/Switch/Switch.d.ts +8 -4
  240. package/dist/Switch/Switch.d.ts.map +1 -1
  241. package/dist/Switch/Switch.js +34 -29
  242. package/dist/TabList/Tab.js +18 -18
  243. package/dist/TabList/TabList.js +5 -5
  244. package/dist/TabList/TabMenu.d.ts.map +1 -1
  245. package/dist/TabList/TabMenu.js +57 -30
  246. package/dist/TabList/tab.markers.stylex.d.ts +1 -1
  247. package/dist/Table/BaseTable.d.ts.map +1 -1
  248. package/dist/Table/BaseTable.js +50 -39
  249. package/dist/Table/Table.js +7 -7
  250. package/dist/Table/TableBody.d.ts +1 -1
  251. package/dist/Table/TableBody.d.ts.map +1 -1
  252. package/dist/Table/TableBody.js +9 -5
  253. package/dist/Table/TableCell.js +3 -3
  254. package/dist/Table/TableFooter.d.ts +1 -1
  255. package/dist/Table/TableFooter.d.ts.map +1 -1
  256. package/dist/Table/TableFooter.js +9 -5
  257. package/dist/Table/TableHeader.d.ts +1 -1
  258. package/dist/Table/TableHeader.d.ts.map +1 -1
  259. package/dist/Table/TableHeader.js +9 -5
  260. package/dist/Table/TableHeaderCell.js +3 -3
  261. package/dist/Table/TableRow.js +5 -5
  262. package/dist/Table/index.d.ts +2 -0
  263. package/dist/Table/index.d.ts.map +1 -1
  264. package/dist/Table/index.js +1 -0
  265. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  266. package/dist/Table/plugins/columnResize/useTableColumnResize.js +14 -11
  267. package/dist/Table/plugins/filtering/useTableFiltering.js +71 -71
  268. package/dist/Table/plugins/groupedRows/index.d.ts +3 -0
  269. package/dist/Table/plugins/groupedRows/index.d.ts.map +1 -0
  270. package/dist/Table/plugins/groupedRows/index.js +3 -0
  271. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts +78 -0
  272. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -0
  273. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +250 -0
  274. package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
  275. package/dist/Table/plugins/pagination/paginateData.js +5 -1
  276. package/dist/Table/plugins/pagination/useTablePagination.js +7 -7
  277. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts +1 -1
  278. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +23 -23
  279. package/dist/Table/plugins/selection/useTableSelection.js +18 -18
  280. package/dist/Table/plugins/selection/useTableSelectionState.d.ts.map +1 -1
  281. package/dist/Table/plugins/selection/useTableSelectionState.js +11 -1
  282. package/dist/Table/plugins/sortable/useTableSortable.js +17 -17
  283. package/dist/Table/plugins/sortable/useTableSortableState.d.ts.map +1 -1
  284. package/dist/Table/plugins/sortable/useTableSortableState.js +8 -4
  285. package/dist/Table/tableContextMenu.js +9 -9
  286. package/dist/Text/Text.js +10 -10
  287. package/dist/TextArea/TextArea.js +14 -14
  288. package/dist/TextInput/TextInput.js +15 -15
  289. package/dist/Thumbnail/Thumbnail.d.ts +3 -2
  290. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  291. package/dist/Thumbnail/Thumbnail.js +30 -29
  292. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  293. package/dist/TimeInput/TimeInput.js +26 -21
  294. package/dist/Timestamp/Timestamp.d.ts +1 -1
  295. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  296. package/dist/Timestamp/Timestamp.js +37 -26
  297. package/dist/Toast/Toast.d.ts.map +1 -1
  298. package/dist/Toast/Toast.js +25 -17
  299. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  300. package/dist/Toast/ToastViewport.js +23 -11
  301. package/dist/Toast/useToast.d.ts.map +1 -1
  302. package/dist/Toast/useToast.js +44 -4
  303. package/dist/ToggleButton/ToggleButton.js +12 -12
  304. package/dist/ToggleButton/ToggleButtonGroup.js +5 -5
  305. package/dist/Token/Token.js +21 -21
  306. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  307. package/dist/Tokenizer/Tokenizer.js +36 -34
  308. package/dist/Toolbar/Toolbar.js +18 -18
  309. package/dist/Tooltip/Tooltip.js +11 -11
  310. package/dist/Tooltip/useTooltip.d.ts +5 -1
  311. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  312. package/dist/Tooltip/useTooltip.js +3 -3
  313. package/dist/TopNav/TopNav.js +32 -32
  314. package/dist/TopNav/TopNavHeading.js +67 -67
  315. package/dist/TopNav/TopNavItem.js +5 -5
  316. package/dist/TopNav/TopNavMegaMenu.js +30 -30
  317. package/dist/TopNav/TopNavMegaMenuFeaturedCard.d.ts +1 -1
  318. package/dist/TopNav/TopNavMegaMenuFeaturedCard.d.ts.map +1 -1
  319. package/dist/TopNav/TopNavMegaMenuFeaturedCard.js +25 -14
  320. package/dist/TopNav/TopNavMegaMenuItem.js +16 -16
  321. package/dist/TopNav/TopNavMenu.js +32 -32
  322. package/dist/TreeList/TreeList.js +8 -8
  323. package/dist/TreeList/TreeListBranches.js +11 -11
  324. package/dist/TreeList/TreeListItem.js +30 -30
  325. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  326. package/dist/Typeahead/BaseTypeahead.js +23 -20
  327. package/dist/Typeahead/Typeahead.js +12 -12
  328. package/dist/Typeahead/TypeaheadItem.js +10 -10
  329. package/dist/VStack/VStack.js +3 -3
  330. package/dist/VisuallyHidden/VisuallyHidden.d.ts +0 -3
  331. package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -1
  332. package/dist/VisuallyHidden/VisuallyHidden.js +0 -3
  333. package/dist/astryx.css +7 -0
  334. package/dist/astryx.umd.js +63 -63
  335. package/dist/astryx.umd.js.map +4 -4
  336. package/dist/docs-types.d.ts +8 -2
  337. package/dist/docs-types.d.ts.map +1 -1
  338. package/dist/hooks/focusableSelector.d.ts +1 -1
  339. package/dist/hooks/focusableSelector.d.ts.map +1 -1
  340. package/dist/hooks/focusableSelector.js +1 -1
  341. package/dist/hooks/index.d.ts +2 -0
  342. package/dist/hooks/index.d.ts.map +1 -1
  343. package/dist/hooks/index.js +1 -0
  344. package/dist/hooks/useFocusTrap.d.ts +3 -0
  345. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  346. package/dist/hooks/useFocusTrap.js +38 -1
  347. package/dist/hooks/useHotkeys.d.ts +51 -0
  348. package/dist/hooks/useHotkeys.d.ts.map +1 -0
  349. package/dist/hooks/useHotkeys.js +160 -0
  350. package/dist/hooks/useInteractiveRole.d.ts +6 -2
  351. package/dist/hooks/useInteractiveRole.d.ts.map +1 -1
  352. package/dist/hooks/useInteractiveRole.js +6 -2
  353. package/dist/hooks/useKeyboardHint.js +9 -9
  354. package/dist/hooks/useStreamingText.d.ts +3 -1
  355. package/dist/hooks/useStreamingText.d.ts.map +1 -1
  356. package/dist/hooks/useStreamingText.js +14 -5
  357. package/dist/theme/MediaTheme.js +3 -3
  358. package/dist/theme/Theme.js +7 -7
  359. package/dist/theme/defineTheme.d.ts +1 -1
  360. package/dist/theme/defineTheme.d.ts.map +1 -1
  361. package/dist/theme/expandColorScale.d.ts.map +1 -1
  362. package/dist/theme/expandColorScale.js +10 -3
  363. package/dist/theme/hct.d.ts +0 -15
  364. package/dist/theme/hct.d.ts.map +1 -1
  365. package/dist/theme/hct.js +9 -9
  366. package/dist/theme/syntax/SyntaxTheme.js +5 -5
  367. package/dist/theme/tokens.d.ts +10 -2
  368. package/dist/theme/tokens.d.ts.map +1 -1
  369. package/dist/theme/tokens.js +220 -4
  370. package/dist/theme/useTheme.d.ts +3 -2
  371. package/dist/theme/useTheme.d.ts.map +1 -1
  372. package/dist/theme/useTheme.js +73 -6
  373. package/dist/utils/color.d.ts +42 -0
  374. package/dist/utils/color.d.ts.map +1 -0
  375. package/dist/utils/color.js +150 -0
  376. package/dist/utils/composeEventHandlers.d.ts +30 -0
  377. package/dist/utils/composeEventHandlers.d.ts.map +1 -0
  378. package/dist/utils/composeEventHandlers.js +40 -0
  379. package/dist/utils/index.d.ts +3 -0
  380. package/dist/utils/index.d.ts.map +1 -1
  381. package/dist/utils/index.js +3 -1
  382. package/dist/utils/timeParser.d.ts.map +1 -1
  383. package/dist/utils/timeParser.js +8 -5
  384. package/package.json +4 -4
  385. package/src/AlertDialog/AlertDialog.doc.mjs +4 -0
  386. package/src/AlertDialog/AlertDialog.tsx +2 -0
  387. package/src/AlertDialog/useImperativeAlertDialog.tsx +0 -2
  388. package/src/AppShell/AppShell.tsx +2 -0
  389. package/src/AspectRatio/AspectRatio.doc.mjs +28 -1
  390. package/src/AspectRatio/AspectRatio.test.tsx +142 -1
  391. package/src/AspectRatio/AspectRatio.tsx +70 -10
  392. package/src/AspectRatio/index.ts +5 -1
  393. package/src/Avatar/Avatar.doc.mjs +11 -0
  394. package/src/Avatar/Avatar.test.tsx +32 -1
  395. package/src/Avatar/Avatar.tsx +14 -22
  396. package/src/Avatar/AvatarStatusDot.tsx +2 -2
  397. package/src/AvatarGroup/AvatarGroup.doc.mjs +1 -0
  398. package/src/AvatarGroup/AvatarGroup.tsx +2 -2
  399. package/src/AvatarGroup/AvatarGroupOverflow.tsx +2 -2
  400. package/src/Banner/Banner.doc.mjs +8 -0
  401. package/src/Banner/Banner.test.tsx +38 -0
  402. package/src/Banner/Banner.tsx +8 -1
  403. package/src/Calendar/Calendar.doc.mjs +1 -1
  404. package/src/Calendar/Calendar.test.tsx +122 -17
  405. package/src/Calendar/Calendar.tsx +26 -5
  406. package/src/Calendar/dayCellUtils.ts +3 -1
  407. package/src/Calendar/styles.ts +12 -2
  408. package/src/Card/Card.doc.mjs +1 -1
  409. package/src/Card/Card.tsx +16 -6
  410. package/src/Carousel/Carousel.doc.mjs +12 -12
  411. package/src/Carousel/Carousel.test.tsx +13 -0
  412. package/src/Carousel/Carousel.tsx +4 -2
  413. package/src/Chat/Chat.doc.mjs +1 -1
  414. package/src/Chat/ChatComposerInput.test.tsx +27 -1
  415. package/src/Chat/ChatComposerInput.tsx +20 -15
  416. package/src/Chat/ChatDictationButton.test.tsx +43 -0
  417. package/src/Chat/ChatDictationButton.tsx +4 -4
  418. package/src/Chat/ChatLayoutScrollButton.test.tsx +33 -0
  419. package/src/Chat/ChatLayoutScrollButton.tsx +3 -1
  420. package/src/Chat/ChatMessageMetadata.test.tsx +32 -0
  421. package/src/Chat/ChatMessageMetadata.tsx +4 -6
  422. package/src/Chat/ChatSystemMessage.test.tsx +26 -11
  423. package/src/Chat/ChatSystemMessage.tsx +7 -7
  424. package/src/Chat/ChatTokenizedText.test.tsx +29 -16
  425. package/src/Chat/ChatTokenizedText.tsx +15 -8
  426. package/src/Chat/ChatToolCalls.test.tsx +55 -0
  427. package/src/Chat/ChatToolCalls.tsx +10 -12
  428. package/src/Chat/index.ts +1 -4
  429. package/src/Chat/useTriggerMenu.tsx +1 -4
  430. package/src/CheckboxInput/CheckboxInput.doc.mjs +3 -3
  431. package/src/CheckboxInput/CheckboxInput.test.tsx +71 -4
  432. package/src/CheckboxInput/CheckboxInput.tsx +2 -0
  433. package/src/CheckboxList/CheckboxList.test.tsx +19 -0
  434. package/src/CheckboxList/CheckboxListItem.tsx +11 -2
  435. package/src/Citation/Citation.doc.mjs +5 -0
  436. package/src/ClickableCard/ClickableCard.tsx +45 -2
  437. package/src/CodeBlock/CodeBlock.doc.mjs +7 -2
  438. package/src/CodeBlock/CodeBlock.test.tsx +122 -3
  439. package/src/CodeBlock/CodeBlock.tsx +62 -4
  440. package/src/Collapsible/Collapsible.doc.mjs +8 -4
  441. package/src/Collapsible/Collapsible.test.tsx +403 -0
  442. package/src/Collapsible/Collapsible.tsx +85 -10
  443. package/src/Collapsible/CollapsibleGroup.doc.mjs +27 -3
  444. package/src/Collapsible/CollapsibleGroup.test.tsx +314 -0
  445. package/src/Collapsible/CollapsibleGroup.tsx +97 -9
  446. package/src/Collapsible/CollapsibleGroupContext.tsx +34 -2
  447. package/src/Collapsible/index.ts +1 -0
  448. package/src/Collapsible/useCollapsible.ts +20 -6
  449. package/src/CommandPalette/CommandPalette.doc.mjs +5 -0
  450. package/src/CommandPalette/CommandPalette.tsx +5 -3
  451. package/src/CommandPalette/CommandPaletteEmpty.doc.mjs +5 -0
  452. package/src/CommandPalette/CommandPaletteGroup.doc.mjs +10 -0
  453. package/src/CommandPalette/CommandPaletteInput.doc.mjs +6 -0
  454. package/src/CommandPalette/CommandPaletteItem.doc.mjs +6 -0
  455. package/src/CommandPalette/CommandPaletteList.doc.mjs +9 -0
  456. package/src/ContextMenu/ContextMenu.doc.mjs +15 -0
  457. package/src/ContextMenu/ContextMenu.test.tsx +87 -0
  458. package/src/ContextMenu/ContextMenu.tsx +87 -28
  459. package/src/ContextMenu/ContextMenuItem.doc.mjs +6 -0
  460. package/src/DateInput/DateInput.test.tsx +11 -20
  461. package/src/DateRangeInput/DateRangeInput.doc.mjs +9 -6
  462. package/src/DateRangeInput/DateRangeInput.test.tsx +31 -44
  463. package/src/DateTimeInput/DateTimeInput.doc.mjs +3 -2
  464. package/src/DateTimeInput/DateTimeInput.test.tsx +118 -16
  465. package/src/DateTimeInput/DateTimeInput.tsx +8 -2
  466. package/src/DateTimeInput/index.ts +1 -0
  467. package/src/Dialog/Dialog.doc.mjs +4 -0
  468. package/src/Dialog/DialogHeader.doc.mjs +45 -0
  469. package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -1
  470. package/src/DropdownMenu/DropdownMenu.test.tsx +22 -2
  471. package/src/DropdownMenu/DropdownMenuItem.tsx +5 -17
  472. package/src/EmptyState/EmptyState.doc.mjs +1 -1
  473. package/src/Field/Field.test.tsx +1 -4
  474. package/src/Field/FieldLabel.test.tsx +2 -2
  475. package/src/FieldStatus/FieldStatus.test.tsx +282 -0
  476. package/src/FormLayout/__snapshots__/FormLayout.test.tsx.snap +0 -3
  477. package/src/Grid/Grid.test.tsx +43 -18
  478. package/src/Grid/Grid.tsx +26 -9
  479. package/src/HoverCard/HoverCard.doc.mjs +6 -6
  480. package/src/HoverCard/useHoverCard.doc.mjs +5 -5
  481. package/src/HoverCard/useHoverCard.tsx +9 -2
  482. package/src/Icon/Icon.test.tsx +41 -29
  483. package/src/Icon/Icon.tsx +6 -2
  484. package/src/InputGroup/InputGroup.doc.mjs +1 -0
  485. package/src/InputGroup/InputGroup.tsx +2 -2
  486. package/src/InteractiveRoleContext/InteractiveRoleContext.test.tsx +185 -0
  487. package/src/Item/Item.tsx +1 -1
  488. package/src/Layer/useLayer.doc.mjs +2 -2
  489. package/src/Layer/useLayer.test.tsx +221 -2
  490. package/src/Layer/useLayer.tsx +69 -30
  491. package/src/Layout/Layout.test.tsx +285 -0
  492. package/src/Layout/LayoutContent.doc.mjs +12 -0
  493. package/src/Layout/LayoutPanel.doc.mjs +40 -0
  494. package/src/Layout/LayoutSlots.test.tsx +364 -0
  495. package/src/Lightbox/Lightbox.doc.mjs +5 -0
  496. package/src/Lightbox/Lightbox.test.tsx +123 -2
  497. package/src/Lightbox/Lightbox.tsx +29 -1
  498. package/src/Lightbox/useLightbox.tsx +0 -1
  499. package/src/Link/Link.tsx +0 -2
  500. package/src/Markdown/Markdown.tsx +9 -7
  501. package/src/Markdown/parser.perf.test.ts +34 -12
  502. package/src/MobileNav/MobileNav.test.tsx +56 -0
  503. package/src/MobileNav/MobileNav.tsx +13 -8
  504. package/src/NavMenu/NavMenu.doc.mjs +15 -0
  505. package/src/NumberInput/NumberInput.test.tsx +48 -3
  506. package/src/NumberInput/NumberInput.tsx +32 -15
  507. package/src/Outline/Outline.doc.mjs +2 -0
  508. package/src/Outline/parseOutlineFromMarkdown.test.ts +80 -0
  509. package/src/OverflowList/OverflowList.test.tsx +416 -0
  510. package/src/Overlay/Overlay.doc.mjs +6 -0
  511. package/src/Overlay/Overlay.test.tsx +356 -0
  512. package/src/Overlay/useOverlay.tsx +0 -3
  513. package/src/Pagination/Pagination.test.tsx +162 -0
  514. package/src/Pagination/Pagination.tsx +80 -26
  515. package/src/Popover/Popover.doc.mjs +6 -6
  516. package/src/Popover/Popover.test.tsx +65 -0
  517. package/src/Popover/usePopover.doc.mjs +1 -1
  518. package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
  519. package/src/PowerSearch/PowerSearch.test.tsx +109 -1
  520. package/src/PowerSearch/PowerSearch.tsx +37 -16
  521. package/src/ProgressBar/ProgressBar.doc.mjs +2 -0
  522. package/src/ProgressBar/ProgressBar.test.tsx +30 -0
  523. package/src/ProgressBar/ProgressBar.tsx +12 -5
  524. package/src/RadioList/RadioList.test.tsx +19 -0
  525. package/src/RadioList/RadioListItem.tsx +9 -4
  526. package/src/Resizable/ResizeHandle.test.tsx +218 -0
  527. package/src/Resizable/ResizeHandle.tsx +22 -4
  528. package/src/SegmentedControl/SegmentedControl.doc.mjs +1 -1
  529. package/src/SegmentedControl/SegmentedControl.test.tsx +192 -0
  530. package/src/SegmentedControl/SegmentedControl.tsx +27 -5
  531. package/src/SegmentedControl/SegmentedControlItem.tsx +9 -4
  532. package/src/SelectableCard/SelectableCard.doc.mjs +1 -1
  533. package/src/Selector/Selector.test.tsx +27 -1
  534. package/src/SideNav/SideNav.doc.mjs +7 -1
  535. package/src/SideNav/SideNav.test.tsx +66 -0
  536. package/src/SideNav/SideNav.tsx +6 -2
  537. package/src/SideNav/SideNavCollapseButton.tsx +4 -3
  538. package/src/SideNav/SideNavSection.tsx +6 -4
  539. package/src/SizeContext/SizeContext.test.tsx +226 -0
  540. package/src/Switch/Switch.doc.mjs +7 -7
  541. package/src/Switch/Switch.test.tsx +102 -3
  542. package/src/Switch/Switch.tsx +20 -7
  543. package/src/TabList/TabList.test.tsx +185 -1
  544. package/src/TabList/TabMenu.tsx +45 -9
  545. package/src/Table/BaseTable.tsx +13 -2
  546. package/src/Table/Table.doc.mjs +6 -1
  547. package/src/Table/Table.test.tsx +87 -0
  548. package/src/Table/TableBody.tsx +15 -2
  549. package/src/Table/TableFooter.tsx +15 -2
  550. package/src/Table/TableHeader.tsx +15 -2
  551. package/src/Table/index.ts +5 -0
  552. package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +13 -0
  553. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +3 -1
  554. package/src/Table/plugins/groupedRows/index.ts +7 -0
  555. package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +231 -0
  556. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +349 -0
  557. package/src/Table/plugins/pagination/paginateData.ts +5 -1
  558. package/src/Table/plugins/pagination/useTablePagination.test.tsx +13 -0
  559. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +1 -1
  560. package/src/Table/plugins/selection/useTableSelectionState.test.tsx +23 -0
  561. package/src/Table/plugins/selection/useTableSelectionState.tsx +11 -6
  562. package/src/Table/plugins/sortable/useTableSortableState.test.tsx +35 -1
  563. package/src/Table/plugins/sortable/useTableSortableState.tsx +8 -4
  564. package/src/Table/useTableGroupedRows.doc.mjs +71 -0
  565. package/src/TextArea/TextArea.test.tsx +3 -3
  566. package/src/TextInput/TextInput.test.tsx +3 -3
  567. package/src/Thumbnail/Thumbnail.test.tsx +36 -2
  568. package/src/Thumbnail/Thumbnail.tsx +4 -2
  569. package/src/TimeInput/TimeInput.test.tsx +37 -0
  570. package/src/TimeInput/TimeInput.tsx +11 -1
  571. package/src/Timestamp/Timestamp.doc.mjs +1 -1
  572. package/src/Timestamp/Timestamp.test.tsx +52 -0
  573. package/src/Timestamp/Timestamp.tsx +30 -16
  574. package/src/Toast/Toast.tsx +11 -3
  575. package/src/Toast/ToastViewport.test.tsx +64 -0
  576. package/src/Toast/ToastViewport.tsx +12 -0
  577. package/src/Toast/useToast.test.tsx +258 -0
  578. package/src/Toast/useToast.tsx +41 -0
  579. package/src/ToggleButton/ToggleButton.doc.mjs +1 -0
  580. package/src/Tokenizer/Tokenizer.doc.mjs +2 -2
  581. package/src/Tokenizer/Tokenizer.test.tsx +54 -0
  582. package/src/Tokenizer/Tokenizer.tsx +5 -4
  583. package/src/Tooltip/Tooltip.doc.mjs +6 -6
  584. package/src/Tooltip/useTooltip.doc.mjs +5 -5
  585. package/src/Tooltip/useTooltip.tsx +12 -2
  586. package/src/TopNav/TopNav.doc.mjs +1 -1
  587. package/src/TopNav/TopNav.test.tsx +23 -7
  588. package/src/TopNav/TopNavMegaMenuFeaturedCard.tsx +9 -2
  589. package/src/TreeList/TreeList.doc.mjs +2 -2
  590. package/src/Typeahead/BaseTypeahead.doc.mjs +19 -0
  591. package/src/Typeahead/BaseTypeahead.tsx +5 -2
  592. package/src/Typeahead/Typeahead.doc.mjs +1 -1
  593. package/src/Typeahead/Typeahead.test.tsx +45 -1
  594. package/src/VisuallyHidden/VisuallyHidden.tsx +0 -3
  595. package/src/__tests__/TestIcon.tsx +26 -0
  596. package/src/__tests__/fastRoleQueries.ts +66 -0
  597. package/src/docPropReferences.test.ts +178 -0
  598. package/src/docs-types.ts +9 -1
  599. package/src/hooks/focusableSelector.ts +1 -1
  600. package/src/hooks/index.ts +3 -0
  601. package/src/hooks/useFocusTrap.doc.mjs +4 -2
  602. package/src/hooks/useFocusTrap.test.tsx +149 -1
  603. package/src/hooks/useFocusTrap.ts +50 -1
  604. package/src/hooks/useHotkeys.doc.mjs +52 -0
  605. package/src/hooks/useHotkeys.test.ts +245 -0
  606. package/src/hooks/useHotkeys.ts +203 -0
  607. package/src/hooks/useInteractiveRole.ts +12 -4
  608. package/src/hooks/useStreamingText.test.ts +54 -19
  609. package/src/hooks/useStreamingText.ts +16 -5
  610. package/src/hooks/useTreeFocus.test.tsx +1 -3
  611. package/src/reset.css +29 -0
  612. package/src/theme/SyntaxTheme.doc.mjs +4 -4
  613. package/src/theme/defineTheme.ts +4 -13
  614. package/src/theme/derivedVarRegistry.test.ts +2 -2
  615. package/src/theme/expandColorScale.test.ts +29 -0
  616. package/src/theme/expandColorScale.ts +11 -6
  617. package/src/theme/hct.ts +9 -17
  618. package/src/theme/themingTargets.test.ts +353 -0
  619. package/src/theme/tokens.test.ts +182 -9
  620. package/src/theme/tokens.ts +239 -4
  621. package/src/theme/useTheme.test.tsx +126 -2
  622. package/src/theme/useTheme.ts +87 -6
  623. package/src/utils/color.test.ts +133 -0
  624. package/src/utils/color.ts +148 -0
  625. package/src/utils/composeEventHandlers.test.ts +71 -0
  626. package/src/utils/composeEventHandlers.ts +44 -0
  627. package/src/utils/index.ts +4 -0
  628. package/src/utils/mergeProps.test.ts +166 -0
  629. package/src/utils/sharedResizeObserver.test.ts +2 -2
  630. package/src/utils/timeParser.test.ts +13 -0
  631. package/src/utils/timeParser.ts +8 -5
  632. package/dist/Carousel/carousel.markers.stylex.d.ts +0 -7
  633. package/dist/Carousel/carousel.markers.stylex.d.ts.map +0 -1
  634. package/dist/Carousel/carousel.markers.stylex.js +0 -12
  635. package/src/Carousel/carousel.markers.stylex.ts +0 -10
@@ -0,0 +1,231 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import {describe, it, expect} from 'vitest';
4
+ import {render, screen, fireEvent, within} from '@testing-library/react';
5
+ import {useState, useCallback} from 'react';
6
+ import {Table} from '../../Table';
7
+ import type {TableColumn} from '../../types';
8
+ import {useTableGroupedRows} from './useTableGroupedRows';
9
+
10
+ interface Person extends Record<string, unknown> {
11
+ id: string;
12
+ name: string;
13
+ team: string;
14
+ }
15
+
16
+ const people: Person[] = [
17
+ {id: 'a', name: 'Alice', team: 'Core'},
18
+ {id: 'b', name: 'Bob', team: 'Core'},
19
+ {id: 'c', name: 'Carol', team: 'Infra'},
20
+ ];
21
+
22
+ const columns: TableColumn<Person>[] = [{key: 'name', header: 'Name'}];
23
+
24
+ const EMPTY = new Set<string>();
25
+
26
+ function Harness({
27
+ rows = people,
28
+ initialCollapsed = EMPTY,
29
+ renderGroupHeader,
30
+ }: {
31
+ rows?: Person[];
32
+ initialCollapsed?: Set<string>;
33
+ renderGroupHeader?: (
34
+ groupKey: string,
35
+ count: number,
36
+ collapsed: boolean,
37
+ ) => React.ReactNode;
38
+ }) {
39
+ const [collapsedGroups, setCollapsed] = useState(initialCollapsed);
40
+ const onToggleGroup = useCallback((key: string) => {
41
+ setCollapsed(prev => {
42
+ const next = new Set(prev);
43
+ if (next.has(key)) {
44
+ next.delete(key);
45
+ } else {
46
+ next.add(key);
47
+ }
48
+ return next;
49
+ });
50
+ }, []);
51
+ const grouped = useTableGroupedRows<Person>({
52
+ data: rows,
53
+ groupBy: p => p.team,
54
+ collapsedGroups,
55
+ onToggleGroup,
56
+ getRowKey: p => p.id,
57
+ renderGroupHeader,
58
+ });
59
+ return (
60
+ <Table
61
+ data={grouped.data}
62
+ columns={columns}
63
+ idKey={grouped.idKey}
64
+ plugins={{grouped: grouped.plugin}}
65
+ />
66
+ );
67
+ }
68
+
69
+ describe('useTableGroupedRows', () => {
70
+ it('renders a header row per group with label and count', () => {
71
+ render(<Harness />);
72
+ // Two groups: Core (2), Infra (1).
73
+ expect(screen.getByText('Core')).toBeInTheDocument();
74
+ expect(screen.getByText('(2)')).toBeInTheDocument();
75
+ expect(screen.getByText('Infra')).toBeInTheDocument();
76
+ expect(screen.getByText('(1)')).toBeInTheDocument();
77
+ });
78
+
79
+ it('shows group members when expanded', () => {
80
+ render(<Harness />);
81
+ expect(screen.getByText('Alice')).toBeInTheDocument();
82
+ expect(screen.getByText('Bob')).toBeInTheDocument();
83
+ expect(screen.getByText('Carol')).toBeInTheDocument();
84
+ });
85
+
86
+ it('hides members of a collapsed group but keeps the header', () => {
87
+ render(<Harness initialCollapsed={new Set(['Core'])} />);
88
+ expect(screen.getByText('Core')).toBeInTheDocument();
89
+ expect(screen.queryByText('Alice')).not.toBeInTheDocument();
90
+ expect(screen.queryByText('Bob')).not.toBeInTheDocument();
91
+ // Other group unaffected.
92
+ expect(screen.getByText('Carol')).toBeInTheDocument();
93
+ });
94
+
95
+ it('toggles a group via its chevron and back again', () => {
96
+ render(<Harness />);
97
+ expect(screen.getByText('Alice')).toBeInTheDocument();
98
+ // The standalone chevron button toggles the group; its accessible name is
99
+ // qualified with the group key.
100
+ fireEvent.click(screen.getByRole('button', {name: 'Collapse group Core'}));
101
+ expect(screen.queryByText('Alice')).not.toBeInTheDocument();
102
+ expect(screen.queryByText('Bob')).not.toBeInTheDocument();
103
+ expect(screen.getByText('Core')).toBeInTheDocument();
104
+ expect(screen.getByText('Carol')).toBeInTheDocument();
105
+ // Toggle back: the Core chevron now says "Expand group Core".
106
+ fireEvent.click(screen.getByRole('button', {name: 'Expand group Core'}));
107
+ expect(screen.getByText('Alice')).toBeInTheDocument();
108
+ expect(screen.getByText('Bob')).toBeInTheDocument();
109
+ });
110
+
111
+ it('exposes each group toggle as a named, keyboard-operable button', () => {
112
+ render(<Harness />);
113
+ // Native <button>: focusable and operable without a custom key handler.
114
+ const coreToggle = screen.getByRole('button', {
115
+ name: 'Collapse group Core',
116
+ });
117
+ expect(coreToggle.tagName).toBe('BUTTON');
118
+ expect(coreToggle).toHaveAttribute('aria-expanded', 'true');
119
+ });
120
+
121
+ it('sets aria-expanded on the header row reflecting collapse state', () => {
122
+ render(<Harness initialCollapsed={new Set(['Core'])} />);
123
+ const rows = screen.getAllByRole('row');
124
+ // rows[1] = Core header (collapsed), rows[2] = Infra header (expanded).
125
+ expect(rows[1]).toHaveAttribute('aria-expanded', 'false');
126
+ expect(rows[2]).toHaveAttribute('aria-expanded', 'true');
127
+ });
128
+
129
+ it('respects groupOrder in the flattened data', () => {
130
+ function OrderedHarness() {
131
+ const [collapsed, setCollapsed] = useState<Set<string>>(new Set());
132
+ const grouped = useTableGroupedRows<Person>({
133
+ data: people,
134
+ groupBy: p => p.team,
135
+ collapsedGroups: collapsed,
136
+ onToggleGroup: k => setCollapsed(new Set([k])),
137
+ getRowKey: p => p.id,
138
+ groupOrder: ['Infra', 'Core'],
139
+ });
140
+ expect(grouped.data.length).toBeGreaterThan(0);
141
+ return (
142
+ <Table
143
+ data={grouped.data}
144
+ columns={columns}
145
+ idKey={grouped.idKey}
146
+ plugins={{grouped: grouped.plugin}}
147
+ />
148
+ );
149
+ }
150
+ render(<OrderedHarness />);
151
+ const rows = screen.getAllByRole('row');
152
+ // rows[0] = column header; rows[1] = first group header (Infra).
153
+ expect(within(rows[1]).getByText('Infra')).toBeInTheDocument();
154
+ });
155
+
156
+ it('renders custom group header content via renderGroupHeader', () => {
157
+ render(
158
+ <Harness
159
+ renderGroupHeader={(key, count, collapsed) => (
160
+ <span>{`${key}::${count}::${collapsed ? 'closed' : 'open'}`}</span>
161
+ )}
162
+ />,
163
+ );
164
+ expect(screen.getByText('Core::2::open')).toBeInTheDocument();
165
+ expect(screen.getByText('Infra::1::open')).toBeInTheDocument();
166
+ });
167
+
168
+ it('renders nothing (no group headers) for empty data', () => {
169
+ render(<Harness rows={[]} />);
170
+ // Column header row still renders; no group header rows.
171
+ expect(screen.queryByText('Core')).not.toBeInTheDocument();
172
+ expect(screen.queryByText('Infra')).not.toBeInTheDocument();
173
+ expect(
174
+ screen.queryByRole('button', {name: /group/i}),
175
+ ).not.toBeInTheDocument();
176
+ });
177
+
178
+ it('keeps a group collapsed across a data change (state keyed by group)', () => {
179
+ function ChangingHarness() {
180
+ const [collapsed, setCollapsed] = useState<Set<string>>(
181
+ new Set(['Core']),
182
+ );
183
+ const [rows, setRows] = useState(people);
184
+ const onToggleGroup = useCallback((key: string) => {
185
+ setCollapsed(prev => {
186
+ const next = new Set(prev);
187
+ if (next.has(key)) {
188
+ next.delete(key);
189
+ } else {
190
+ next.add(key);
191
+ }
192
+ return next;
193
+ });
194
+ }, []);
195
+ const grouped = useTableGroupedRows<Person>({
196
+ data: rows,
197
+ groupBy: p => p.team,
198
+ collapsedGroups: collapsed,
199
+ onToggleGroup,
200
+ getRowKey: p => p.id,
201
+ });
202
+ return (
203
+ <>
204
+ <button
205
+ type="button"
206
+ onClick={() =>
207
+ setRows([...people, {id: 'd', name: 'Dave', team: 'Core'}])
208
+ }>
209
+ add
210
+ </button>
211
+ <Table
212
+ data={grouped.data}
213
+ columns={columns}
214
+ idKey={grouped.idKey}
215
+ plugins={{grouped: grouped.plugin}}
216
+ />
217
+ </>
218
+ );
219
+ }
220
+ render(<ChangingHarness />);
221
+ // Core collapsed initially: Alice hidden.
222
+ expect(screen.queryByText('Alice')).not.toBeInTheDocument();
223
+ // Add a new Core member; Core must stay collapsed (keyed by group value).
224
+ fireEvent.click(screen.getByText('add'));
225
+ expect(screen.queryByText('Alice')).not.toBeInTheDocument();
226
+ expect(screen.queryByText('Dave')).not.toBeInTheDocument();
227
+ // Count reflects the new member (3), header still present.
228
+ expect(screen.getByText('Core')).toBeInTheDocument();
229
+ expect(screen.getByText('(3)')).toBeInTheDocument();
230
+ });
231
+ });
@@ -0,0 +1,349 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file useTableGroupedRows.tsx
7
+ * @input React, StyleX, Icon, Table types + the flat data array
8
+ * @output Exports useTableGroupedRows hook + config/result types
9
+ * @position Grouped-rows plugin; consumed by Table via plugins prop
10
+ *
11
+ * SYNC: When modified, update these files to stay in sync:
12
+ * - /packages/core/src/Table/index.ts (exports)
13
+ */
14
+
15
+ import {useCallback, useMemo, type ReactNode} from 'react';
16
+ import * as stylex from '@stylexjs/stylex';
17
+ import {
18
+ spacingVars,
19
+ colorVars,
20
+ fontWeightVars,
21
+ } from '../../../theme/tokens.stylex';
22
+ import {Icon} from '../../../Icon';
23
+ import type {TablePlugin} from '../../types';
24
+
25
+ // A synthetic group-header row injected into the flattened data. Real rows
26
+ // never carry this marker.
27
+ const GROUP_HEADER = Symbol('tableGroupHeader');
28
+
29
+ interface GroupHeader {
30
+ [GROUP_HEADER]: true;
31
+ groupKey: string;
32
+ count: number;
33
+ }
34
+
35
+ function isGroupHeader(item: unknown): item is GroupHeader {
36
+ return (
37
+ typeof item === 'object' &&
38
+ item !== null &&
39
+ (item as Record<symbol, unknown>)[GROUP_HEADER] === true
40
+ );
41
+ }
42
+
43
+ // Proxy handler: any field access beyond the marker fields resolves to `''`
44
+ // so user cell renderers (`item.name.toUpperCase()`) never throw on a header.
45
+ const HEADER_PROXY_HANDLER: ProxyHandler<Record<string | symbol, unknown>> = {
46
+ // eslint-disable-next-line @typescript-eslint/promise-function-async -- Proxy get trap, not a promise-returning fn
47
+ get(t: Record<string | symbol, unknown>, prop: string | symbol): unknown {
48
+ if (prop === GROUP_HEADER || prop === 'groupKey' || prop === 'count') {
49
+ return t[prop];
50
+ }
51
+ return prop in t ? t[prop] : '';
52
+ },
53
+ };
54
+
55
+ /**
56
+ * Build a synthetic header row wrapped in a Proxy so arbitrary field access
57
+ * from user cell renderers (e.g. `item.name.toUpperCase()`) resolves to `''`
58
+ * instead of throwing — BaseTable evaluates `col.renderCell(item)` on every
59
+ * row (including synthetic headers) before `transformBodyRow` can replace the
60
+ * row's cells. `transformBodyRow` then discards those cells and renders a
61
+ * single full-width header cell.
62
+ */
63
+ function makeHeader<T extends Record<string, unknown>>(
64
+ groupKey: string,
65
+ count: number,
66
+ ): T {
67
+ const target: Record<string | symbol, unknown> = {
68
+ [GROUP_HEADER]: true,
69
+ groupKey,
70
+ count,
71
+ };
72
+ return new Proxy(target, HEADER_PROXY_HANDLER) as unknown as T;
73
+ }
74
+
75
+ /** Configuration for {@link useTableGroupedRows}. */
76
+ export interface UseTableGroupedRowsConfig<T extends Record<string, unknown>> {
77
+ /** The flat data to group. */
78
+ data: T[];
79
+ /** Derive the group key for a row. Rows with the same key share a section. */
80
+ groupBy: (item: T) => string;
81
+ /** Set of currently-collapsed group keys. */
82
+ collapsedGroups: Set<string>;
83
+ /** Called with a group key when its header is toggled. */
84
+ onToggleGroup: (groupKey: string) => void;
85
+ /**
86
+ * Custom renderer for a group header's content (right of the chevron).
87
+ * Defaults to `<groupKey> (<count>)`.
88
+ */
89
+ renderGroupHeader?: (
90
+ groupKey: string,
91
+ count: number,
92
+ collapsed: boolean,
93
+ ) => ReactNode;
94
+ /** Stable key for a real row. Falls back to a positional key when omitted. */
95
+ getRowKey?: (item: T) => string;
96
+ /** Explicit group ordering; groups not listed keep first-seen order after these. */
97
+ groupOrder?: string[];
98
+ }
99
+
100
+ export interface UseTableGroupedRowsResult<T extends Record<string, unknown>> {
101
+ /** Ready-to-use plugin for `<Table plugins>`. */
102
+ plugin: TablePlugin<T>;
103
+ /** Flattened rows: `[header, ...visibleRows, header, ...visibleRows]`. */
104
+ data: T[];
105
+ /**
106
+ * Row-key resolver (also keys synthetic headers as `__group_<key>`). Pass to
107
+ * `<Table idKey>` — named for parallelism with the Table prop:
108
+ * `<Table idKey={grouped.idKey} />`.
109
+ */
110
+ idKey: (item: T) => string;
111
+ }
112
+
113
+ const styles = stylex.create({
114
+ headerRow: {
115
+ cursor: 'pointer',
116
+ userSelect: 'none',
117
+ backgroundColor: colorVars['--color-background-muted'],
118
+ // Divider beneath each group header row (Ernest review #2).
119
+ borderBottomWidth: '1px',
120
+ borderBottomStyle: 'solid',
121
+ borderBottomColor: colorVars['--color-border'],
122
+ },
123
+ headerCell: {
124
+ paddingBlock: spacingVars['--spacing-2'],
125
+ // No inline start padding so the chevron aligns with the table's leading
126
+ // edge (Ernest review #1).
127
+ paddingInlineStart: spacingVars['--spacing-1'],
128
+ paddingInlineEnd: spacingVars['--spacing-3'],
129
+ },
130
+ headerInner: {
131
+ display: 'flex',
132
+ alignItems: 'center',
133
+ gap: spacingVars['--spacing-1'],
134
+ },
135
+ // Standalone chevron button with no heavy chrome (transparent, borderless,
136
+ // zero padding) so the icon sits flush with the start of the table
137
+ // (Ernest review #1) while staying keyboard-operable.
138
+ chevron: {
139
+ display: 'inline-flex',
140
+ alignItems: 'center',
141
+ justifyContent: 'center',
142
+ flexShrink: '0',
143
+ padding: 0,
144
+ margin: 0,
145
+ background: 'transparent',
146
+ border: 'none',
147
+ cursor: 'pointer',
148
+ color: {
149
+ default: colorVars['--color-icon-secondary'],
150
+ ':hover': colorVars['--color-icon-primary'],
151
+ },
152
+ },
153
+ chevronIcon: {
154
+ display: 'inline-flex',
155
+ transitionProperty: 'transform',
156
+ transitionDuration: '150ms',
157
+ },
158
+ chevronExpanded: {
159
+ transform: 'rotate(90deg)',
160
+ },
161
+ // Emphasized body text — same size as body, heavier weight (Ernest #3).
162
+ label: {
163
+ fontWeight: fontWeightVars['--font-weight-semibold'],
164
+ color: colorVars['--color-text-primary'],
165
+ },
166
+ count: {
167
+ fontWeight: fontWeightVars['--font-weight-normal'],
168
+ color: colorVars['--color-text-secondary'],
169
+ },
170
+ });
171
+
172
+ /**
173
+ * Groups a flat data array into collapsible section rows. Each distinct
174
+ * `groupBy` value becomes a full-width section-header row with a chevron
175
+ * toggle, the group label, and a member count; collapsing hides that group's
176
+ * data rows while keeping the header visible.
177
+ *
178
+ * Mirrors {@link useTableRowExpansionState}: the consumer owns the
179
+ * `collapsedGroups` set and this hook returns `{data, plugin, idKey}` —
180
+ * pass all three to `<Table>`.
181
+ *
182
+ * @example
183
+ * ```tsx
184
+ * const [collapsed, setCollapsed] = useState<Set<string>>(new Set());
185
+ * const grouped = useTableGroupedRows({
186
+ * data: rows,
187
+ * groupBy: r => r.team,
188
+ * collapsedGroups: collapsed,
189
+ * onToggleGroup: key =>
190
+ * setCollapsed(prev => {
191
+ * const next = new Set(prev);
192
+ * next.has(key) ? next.delete(key) : next.add(key);
193
+ * return next;
194
+ * }),
195
+ * getRowKey: r => r.id,
196
+ * });
197
+ * <Table
198
+ * data={grouped.data}
199
+ * columns={columns}
200
+ * idKey={grouped.idKey}
201
+ * plugins={{grouped: grouped.plugin}}
202
+ * />;
203
+ * ```
204
+ */
205
+ export function useTableGroupedRows<T extends Record<string, unknown>>(
206
+ config: UseTableGroupedRowsConfig<T>,
207
+ ): UseTableGroupedRowsResult<T> {
208
+ const {
209
+ data,
210
+ groupBy,
211
+ collapsedGroups,
212
+ onToggleGroup,
213
+ renderGroupHeader,
214
+ getRowKey: getRowKeyProp,
215
+ groupOrder,
216
+ } = config;
217
+
218
+ const flattened = useMemo((): T[] => {
219
+ if (data.length === 0) {
220
+ return [];
221
+ }
222
+
223
+ // Group preserving first-seen order.
224
+ const groups = new Map<string, T[]>();
225
+ for (const item of data) {
226
+ const key = groupBy(item);
227
+ const bucket = groups.get(key);
228
+ if (bucket) {
229
+ bucket.push(item);
230
+ } else {
231
+ groups.set(key, [item]);
232
+ }
233
+ }
234
+
235
+ // Determine iteration order.
236
+ let keys = [...groups.keys()];
237
+ if (groupOrder && groupOrder.length > 0) {
238
+ const ordered = groupOrder.filter(k => groups.has(k));
239
+ const rest = keys.filter(k => !groupOrder.includes(k));
240
+ keys = [...ordered, ...rest];
241
+ }
242
+
243
+ const out: T[] = [];
244
+ for (const key of keys) {
245
+ const rows = groups.get(key) ?? [];
246
+ out.push(makeHeader<T>(key, rows.length));
247
+ if (!collapsedGroups.has(key)) {
248
+ out.push(...rows);
249
+ }
250
+ }
251
+ return out;
252
+ }, [data, groupBy, collapsedGroups, groupOrder]);
253
+
254
+ // Positional fallback index, built once per flattened array so key lookup
255
+ // stays O(1) instead of O(n) per row (which would make table keying O(n²)).
256
+ const positionByItem = useMemo(() => {
257
+ if (getRowKeyProp) {
258
+ return null;
259
+ }
260
+ const map = new Map<T, number>();
261
+ for (let i = 0; i < flattened.length; i++) {
262
+ map.set(flattened[i], i);
263
+ }
264
+ return map;
265
+ }, [flattened, getRowKeyProp]);
266
+
267
+ const idKey = useCallback(
268
+ (item: T): string => {
269
+ if (isGroupHeader(item)) {
270
+ return `__group_${item.groupKey}`;
271
+ }
272
+ if (getRowKeyProp) {
273
+ return getRowKeyProp(item);
274
+ }
275
+ return String(positionByItem?.get(item) ?? -1);
276
+ },
277
+ [getRowKeyProp, positionByItem],
278
+ );
279
+
280
+ const plugin = useMemo(
281
+ (): TablePlugin<T> => ({
282
+ // Replace a header row's pre-rendered cells with one full-width cell.
283
+ transformBodyRow(props, item) {
284
+ if (!isGroupHeader(item)) {
285
+ return props;
286
+ }
287
+ const header = item as unknown as GroupHeader;
288
+ const collapsed = collapsedGroups.has(header.groupKey);
289
+ const toggle = () => onToggleGroup(header.groupKey);
290
+ const content: ReactNode = renderGroupHeader ? (
291
+ renderGroupHeader(header.groupKey, header.count, collapsed)
292
+ ) : (
293
+ <span {...stylex.props(styles.label)}>
294
+ {header.groupKey}{' '}
295
+ <span {...stylex.props(styles.count)}>({header.count})</span>
296
+ </span>
297
+ );
298
+ return {
299
+ ...props,
300
+ htmlProps: {
301
+ ...props.htmlProps,
302
+ // Convenience: clicking anywhere on the row toggles it. The chevron
303
+ // button below is the accessible, keyboard-operable control, so the
304
+ // row keeps its implicit `row` role (no role override here).
305
+ onClick: toggle,
306
+ 'aria-expanded': !collapsed,
307
+ },
308
+ styles: [...props.styles, styles.headerRow],
309
+ children: (
310
+ // colSpan larger than the column count is clamped by the browser
311
+ // to the actual number of columns, so the header always spans the
312
+ // full width without the plugin knowing the column count.
313
+ <td colSpan={999} {...stylex.props(styles.headerCell)}>
314
+ <span {...stylex.props(styles.headerInner)}>
315
+ {/* Standalone chevron button, flush with the table's start
316
+ edge (no heavy button chrome) — the keyboard control. */}
317
+ <button
318
+ type="button"
319
+ {...stylex.props(styles.chevron)}
320
+ onClick={e => {
321
+ e.stopPropagation();
322
+ toggle();
323
+ }}
324
+ aria-label={
325
+ collapsed
326
+ ? `Expand group ${header.groupKey}`
327
+ : `Collapse group ${header.groupKey}`
328
+ }
329
+ aria-expanded={!collapsed}>
330
+ <span
331
+ {...stylex.props(
332
+ styles.chevronIcon,
333
+ !collapsed && styles.chevronExpanded,
334
+ )}>
335
+ <Icon icon="chevronRight" size="xsm" />
336
+ </span>
337
+ </button>
338
+ {content}
339
+ </span>
340
+ </td>
341
+ ),
342
+ };
343
+ },
344
+ }),
345
+ [collapsedGroups, onToggleGroup, renderGroupHeader],
346
+ );
347
+
348
+ return {plugin, data: flattened, idKey};
349
+ }
@@ -38,6 +38,10 @@ export function paginateData<T>(
38
38
  const size = Number.isFinite(pageSize)
39
39
  ? Math.max(1, Math.floor(pageSize))
40
40
  : 10;
41
- const start = (page - 1) * size;
41
+ // page needs the same coercion: a negative start index makes Array.slice
42
+ // count from the END of the data (page -1 returns the tail dressed up as a
43
+ // page), and a fractional page returns a slice straddling two pages.
44
+ const p = Number.isFinite(page) ? Math.max(1, Math.floor(page)) : 1;
45
+ const start = (p - 1) * size;
42
46
  return data.slice(start, start + size);
43
47
  }
@@ -132,6 +132,19 @@ describe('paginateData', () => {
132
132
  expect(paginateData([], 1, 10)).toEqual([]);
133
133
  });
134
134
 
135
+ it('clamps invalid page numbers to the first page (#3593)', () => {
136
+ const data = generateItems(30);
137
+ // Negative pages previously fed a negative index to Array.slice, which
138
+ // counts from the END of the data — the tail dressed up as a page.
139
+ expect(paginateData(data, -1, 10)[0].id).toBe('1');
140
+ expect(paginateData(data, 0, 10)[0].id).toBe('1');
141
+ expect(paginateData(data, NaN, 10)[0].id).toBe('1');
142
+ // Fractional pages floor to the containing page instead of straddling two.
143
+ expect(paginateData(data, 1.5, 10).map(i => i.id)).toEqual(
144
+ paginateData(data, 1, 10).map(i => i.id),
145
+ );
146
+ });
147
+
135
148
  it('returns empty array when page exceeds data', () => {
136
149
  const data = generateItems(10);
137
150
  expect(paginateData(data, 5, 10)).toEqual([]);
@@ -109,7 +109,7 @@ export interface UseTableRowExpansionStateResult<
109
109
  * computing the expand-all state.
110
110
  *
111
111
  * @example
112
- * ```tsx
112
+ * ```
113
113
  * const [expandedKeys, setExpandedKeys] = useState<Set<string>>(new Set());
114
114
  * const {data, expansionConfig} = useTableRowExpansionState({
115
115
  * baseData: tree,
@@ -302,6 +302,29 @@ describe('useTableSelectionState with filtered data', () => {
302
302
  expect(screen.getByTestId('selected-keys')).toHaveTextContent('1,3');
303
303
  });
304
304
 
305
+ it('select-all is unchecked when the filter matches nothing (#3591)', async () => {
306
+ const user = userEvent.setup();
307
+ render(<FilteredSelectionTable />);
308
+
309
+ // Select Bob, then filter to an empty result set.
310
+ const checkboxes = screen.getAllByLabelText('Select row');
311
+ await user.click(checkboxes[1]);
312
+ expect(screen.getByTestId('selected-keys')).toHaveTextContent('2');
313
+
314
+ const input = screen.getByTestId('filter-input');
315
+ await user.clear(input);
316
+ await user.type(input, 'zzz');
317
+
318
+ // Zero visible rows must not read as "all selected" — previously the
319
+ // header checkbox rendered checked here and deselect-all was a no-op.
320
+ const selectAll = screen.getByLabelText('Select all rows');
321
+ expect(selectAll).not.toBeChecked();
322
+
323
+ // The hidden selection itself is preserved (frozen), as designed.
324
+ await user.clear(input);
325
+ expect(screen.getByTestId('selected-keys')).toHaveTextContent('2');
326
+ });
327
+
305
328
  it('selections persist when filter changes', async () => {
306
329
  const user = userEvent.setup();
307
330
  render(<FilteredSelectionTable />);
@@ -125,12 +125,17 @@ export function useTableSelectionState<T extends Record<string, unknown>>(
125
125
  [selectedKeys, selectableIDs],
126
126
  );
127
127
 
128
- const getAllSelected = useCallback(
129
- () =>
130
- allSelectableIDs.size === selectedKeys.size &&
131
- allSelectableIDs.size !== 0,
132
- [allSelectableIDs, selectedKeys],
133
- );
128
+ const getAllSelected = useCallback(() => {
129
+ // Require at least one actionable row: with zero visible selectable
130
+ // rows (e.g. a filter with no matches while a selection exists), the
131
+ // union-based size comparison would read any non-empty selection as
132
+ // "all selected" — a checked header checkbox over an empty table that
133
+ // deselect-all can't clear, since the invisible keys count as frozen.
134
+ if (selectableIDs.size === 0) {
135
+ return false;
136
+ }
137
+ return allSelectableIDs.size === selectedKeys.size;
138
+ }, [allSelectableIDs, selectableIDs, selectedKeys]);
134
139
 
135
140
  // Use refs to keep onSelectAll stable
136
141
  const frozenSelectedIDsRef = useRef(frozenSelectedIDs);