@astryxdesign/core 0.1.8 → 0.1.9

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 (487) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/dist/AppShell/AppShell.d.ts.map +1 -1
  3. package/dist/AppShell/AppShell.js +6 -2
  4. package/dist/AppShell/AppShellMobileContext.d.ts +8 -0
  5. package/dist/AppShell/AppShellMobileContext.d.ts.map +1 -1
  6. package/dist/Avatar/Avatar.d.ts +18 -2
  7. package/dist/Avatar/Avatar.d.ts.map +1 -1
  8. package/dist/Avatar/Avatar.js +63 -7
  9. package/dist/Avatar/AvatarStatusDot.d.ts +33 -5
  10. package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
  11. package/dist/Avatar/AvatarStatusDot.js +144 -19
  12. package/dist/Banner/Banner.d.ts +9 -1
  13. package/dist/Banner/Banner.d.ts.map +1 -1
  14. package/dist/Banner/Banner.js +31 -2
  15. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +31 -2
  16. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  17. package/dist/Breadcrumbs/BreadcrumbItem.js +208 -6
  18. package/dist/Breadcrumbs/index.d.ts +5 -0
  19. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  20. package/dist/Breadcrumbs/index.js +9 -1
  21. package/dist/Button/Button.d.ts +8 -2
  22. package/dist/Button/Button.d.ts.map +1 -1
  23. package/dist/Button/Button.js +31 -2
  24. package/dist/ButtonGroup/ButtonGroup.d.ts +9 -1
  25. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  26. package/dist/ButtonGroup/ButtonGroup.js +30 -1
  27. package/dist/Calendar/Calendar.d.ts.map +1 -1
  28. package/dist/Calendar/Calendar.js +30 -2
  29. package/dist/Calendar/dayCellUtils.js +1 -1
  30. package/dist/Card/Card.d.ts +8 -2
  31. package/dist/Card/Card.d.ts.map +1 -1
  32. package/dist/Card/Card.js +28 -2
  33. package/dist/Carousel/Carousel.d.ts +4 -1
  34. package/dist/Carousel/Carousel.d.ts.map +1 -1
  35. package/dist/Carousel/Carousel.js +24 -4
  36. package/dist/Chat/ChatComposer.d.ts +8 -0
  37. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  38. package/dist/Chat/ChatComposer.js +46 -7
  39. package/dist/Chat/ChatComposerDrawer.d.ts +2 -0
  40. package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
  41. package/dist/Chat/ChatComposerDrawer.js +11 -1
  42. package/dist/Chat/ChatComposerInput.d.ts +20 -3
  43. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  44. package/dist/Chat/ChatComposerInput.js +35 -3
  45. package/dist/Chat/ChatContext.d.ts +33 -0
  46. package/dist/Chat/ChatContext.d.ts.map +1 -1
  47. package/dist/Chat/ChatContext.js +16 -2
  48. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  49. package/dist/Chat/ChatDictationButton.js +1 -1
  50. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  51. package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
  52. package/dist/Chat/ChatLayoutScrollButton.js +4 -4
  53. package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
  54. package/dist/Chat/ChatSendButton.d.ts.map +1 -1
  55. package/dist/Chat/ChatSendButton.js +7 -1
  56. package/dist/Chat/ChatToolCalls.d.ts +5 -1
  57. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  58. package/dist/Chat/ChatToolCalls.js +18 -9
  59. package/dist/Chat/index.d.ts +2 -1
  60. package/dist/Chat/index.d.ts.map +1 -1
  61. package/dist/Chat/index.js +1 -1
  62. package/dist/Chat/useChatStreamScroll.d.ts +2 -1
  63. package/dist/Chat/useChatStreamScroll.d.ts.map +1 -1
  64. package/dist/Chat/useChatStreamScroll.js +22 -8
  65. package/dist/Chat/useSpeechRecognition.d.ts +0 -1
  66. package/dist/Chat/useSpeechRecognition.d.ts.map +1 -1
  67. package/dist/Chat/useSpeechRecognition.js +1 -1
  68. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  69. package/dist/CheckboxInput/CheckboxInput.js +1 -1
  70. package/dist/CheckboxList/CheckboxListItem.d.ts +20 -1
  71. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  72. package/dist/CheckboxList/CheckboxListItem.js +11 -1
  73. package/dist/ClickableCard/ClickableCard.d.ts +9 -2
  74. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  75. package/dist/ClickableCard/ClickableCard.js +5 -2
  76. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  77. package/dist/CodeBlock/CodeBlock.js +8 -1
  78. package/dist/CommandPalette/CommandPalette.d.ts +1 -1
  79. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  80. package/dist/CommandPalette/CommandPalette.js +3 -1
  81. package/dist/CommandPalette/CommandPaletteGroup.d.ts.map +1 -1
  82. package/dist/CommandPalette/CommandPaletteGroup.js +9 -2
  83. package/dist/CommandPalette/CommandPaletteInput.d.ts +7 -1
  84. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  85. package/dist/CommandPalette/CommandPaletteInput.js +6 -1
  86. package/dist/DateInput/DateInput.d.ts +47 -1
  87. package/dist/DateInput/DateInput.d.ts.map +1 -1
  88. package/dist/DateInput/DateInput.js +26 -2
  89. package/dist/DateInput/index.d.ts +1 -1
  90. package/dist/DateInput/index.d.ts.map +1 -1
  91. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  92. package/dist/DateTimeInput/DateTimeInput.js +10 -1
  93. package/dist/Dialog/Dialog.d.ts +3 -0
  94. package/dist/Dialog/Dialog.d.ts.map +1 -1
  95. package/dist/Dialog/Dialog.js +57 -6
  96. package/dist/Dialog/DialogContext.d.ts +7 -0
  97. package/dist/Dialog/DialogContext.d.ts.map +1 -1
  98. package/dist/Dialog/DialogContext.js +2 -2
  99. package/dist/Dialog/DialogHeader.d.ts +5 -2
  100. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  101. package/dist/Dialog/DialogHeader.js +6 -1
  102. package/dist/Divider/Divider.js +1 -1
  103. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +1 -2
  104. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  105. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +1 -2
  106. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +1 -2
  107. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
  108. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +1 -2
  109. package/dist/Field/Field.d.ts.map +1 -1
  110. package/dist/Field/Field.js +2 -3
  111. package/dist/Field/FieldLabel.d.ts +1 -1
  112. package/dist/Field/FieldLabel.d.ts.map +1 -1
  113. package/dist/Field/FieldLabel.js +42 -15
  114. package/dist/FieldStatus/FieldStatus.d.ts +8 -0
  115. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  116. package/dist/FieldStatus/FieldStatus.js +22 -4
  117. package/dist/FileInput/FileInput.d.ts.map +1 -1
  118. package/dist/FileInput/FileInput.js +10 -2
  119. package/dist/HoverCard/HoverCard.d.ts +9 -1
  120. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  121. package/dist/HoverCard/HoverCard.js +2 -0
  122. package/dist/HoverCard/useHoverCard.d.ts +8 -0
  123. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  124. package/dist/HoverCard/useHoverCard.js +7 -2
  125. package/dist/Icon/Icon.d.ts +7 -2
  126. package/dist/Icon/Icon.d.ts.map +1 -1
  127. package/dist/Item/Item.d.ts +6 -1
  128. package/dist/Item/Item.d.ts.map +1 -1
  129. package/dist/Item/Item.js +22 -1
  130. package/dist/Layer/useLayer.d.ts +6 -0
  131. package/dist/Layer/useLayer.d.ts.map +1 -1
  132. package/dist/Layer/useLayer.js +2 -0
  133. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  134. package/dist/MobileNav/MobileNav.js +6 -1
  135. package/dist/MobileNav/MobileNavToggle.d.ts.map +1 -1
  136. package/dist/MobileNav/MobileNavToggle.js +4 -0
  137. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  138. package/dist/MultiSelector/MultiSelector.js +39 -12
  139. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  140. package/dist/MultiSelector/hooks.js +16 -0
  141. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  142. package/dist/Outline/Outline.d.ts +0 -1
  143. package/dist/Outline/Outline.d.ts.map +1 -1
  144. package/dist/Outline/Outline.js +0 -1
  145. package/dist/OverflowList/OverflowList.d.ts +20 -3
  146. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  147. package/dist/OverflowList/OverflowList.js +29 -4
  148. package/dist/Popover/Popover.d.ts.map +1 -1
  149. package/dist/Popover/Popover.js +3 -2
  150. package/dist/Popover/usePopover.d.ts.map +1 -1
  151. package/dist/Popover/usePopover.js +3 -9
  152. package/dist/PowerSearch/PowerSearchEditPopover.js +1 -1
  153. package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
  154. package/dist/SelectableCard/SelectableCard.d.ts +9 -2
  155. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  156. package/dist/SelectableCard/SelectableCard.js +16 -13
  157. package/dist/Selector/Selector.d.ts.map +1 -1
  158. package/dist/Selector/Selector.js +4 -2
  159. package/dist/Selector/hooks.d.ts.map +1 -1
  160. package/dist/Selector/hooks.js +16 -0
  161. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  162. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  163. package/dist/SideNav/SideNavHeading.js +21 -8
  164. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  165. package/dist/Slider/Slider.d.ts.map +1 -1
  166. package/dist/Slider/Slider.js +19 -2
  167. package/dist/Stack/stack.stylex.d.ts +9 -9
  168. package/dist/Table/BaseTable.d.ts.map +1 -1
  169. package/dist/Table/BaseTable.js +3 -2
  170. package/dist/Table/index.d.ts +2 -0
  171. package/dist/Table/index.d.ts.map +1 -1
  172. package/dist/Table/index.js +1 -0
  173. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  174. package/dist/Table/plugins/rowStatus/index.d.ts +3 -0
  175. package/dist/Table/plugins/rowStatus/index.d.ts.map +1 -0
  176. package/dist/Table/plugins/rowStatus/index.js +3 -0
  177. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts +60 -0
  178. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -0
  179. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +145 -0
  180. package/dist/Table/plugins/selection/useTableSelection.d.ts +13 -0
  181. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  182. package/dist/Table/plugins/selection/useTableSelection.js +4 -1
  183. package/dist/Table/plugins/tree/useTableTreeData.d.ts +16 -0
  184. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  185. package/dist/Table/plugins/tree/useTableTreeData.js +87 -0
  186. package/dist/Table/plugins/tree/useTableTreeState.d.ts +6 -0
  187. package/dist/Table/plugins/tree/useTableTreeState.d.ts.map +1 -1
  188. package/dist/Table/plugins/tree/useTableTreeState.js +22 -1
  189. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  190. package/dist/Table/useBaseTablePlugins.js +5 -3
  191. package/dist/Thumbnail/Thumbnail.d.ts +26 -5
  192. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  193. package/dist/Thumbnail/Thumbnail.js +97 -54
  194. package/dist/Thumbnail/thumbnail.markers.stylex.d.ts +9 -0
  195. package/dist/Thumbnail/thumbnail.markers.stylex.d.ts.map +1 -0
  196. package/dist/Thumbnail/thumbnail.markers.stylex.js +14 -0
  197. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  198. package/dist/TimeInput/TimeInput.js +15 -4
  199. package/dist/Timestamp/Timestamp.d.ts +3 -1
  200. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  201. package/dist/Timestamp/Timestamp.js +8 -6
  202. package/dist/Toast/useToast.d.ts.map +1 -1
  203. package/dist/Toast/useToast.js +2 -5
  204. package/dist/ToggleButton/ToggleButtonGroup.d.ts +0 -1
  205. package/dist/ToggleButton/ToggleButtonGroup.d.ts.map +1 -1
  206. package/dist/ToggleButton/ToggleButtonGroup.js +1 -1
  207. package/dist/Tokenizer/Tokenizer.d.ts +1 -1
  208. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  209. package/dist/Tokenizer/Tokenizer.js +16 -4
  210. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  211. package/dist/TopNav/TopNavHeading.js +18 -8
  212. package/dist/TopNav/TopNavMegaMenu.js +10 -4
  213. package/dist/TopNav/TopNavMegaMenuFeaturedCard.d.ts +9 -1
  214. package/dist/TopNav/TopNavMegaMenuFeaturedCard.d.ts.map +1 -1
  215. package/dist/TopNav/TopNavMegaMenuFeaturedCard.js +8 -1
  216. package/dist/TopNav/TopNavMenu.d.ts +2 -1
  217. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  218. package/dist/TopNav/TopNavMenu.js +68 -6
  219. package/dist/TreeList/TreeList.d.ts +12 -3
  220. package/dist/TreeList/TreeList.d.ts.map +1 -1
  221. package/dist/TreeList/TreeList.js +2 -0
  222. package/dist/TreeList/TreeListBranches.d.ts +5 -0
  223. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  224. package/dist/TreeList/TreeListBranches.js +10 -4
  225. package/dist/TreeList/TreeListItem.d.ts +8 -2
  226. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  227. package/dist/TreeList/TreeListItem.js +27 -7
  228. package/dist/TreeList/TreeListTypes.d.ts +25 -1
  229. package/dist/TreeList/TreeListTypes.d.ts.map +1 -1
  230. package/dist/TreeList/index.d.ts +2 -2
  231. package/dist/TreeList/index.d.ts.map +1 -1
  232. package/dist/astryx.css +28 -18
  233. package/dist/astryx.umd.js +51 -51
  234. package/dist/astryx.umd.js.map +4 -4
  235. package/dist/authoring/doc.d.ts.map +1 -1
  236. package/dist/docs-types.d.ts +8 -0
  237. package/dist/docs-types.d.ts.map +1 -1
  238. package/dist/hooks/computeOverflow.d.ts +47 -0
  239. package/dist/hooks/computeOverflow.d.ts.map +1 -0
  240. package/dist/hooks/computeOverflow.js +213 -0
  241. package/dist/hooks/index.d.ts +1 -0
  242. package/dist/hooks/index.d.ts.map +1 -1
  243. package/dist/hooks/index.js +2 -1
  244. package/dist/hooks/useDevWarning.d.ts +18 -0
  245. package/dist/hooks/useDevWarning.d.ts.map +1 -0
  246. package/dist/hooks/useDevWarning.js +49 -0
  247. package/dist/hooks/useOverflow.d.ts +19 -0
  248. package/dist/hooks/useOverflow.d.ts.map +1 -1
  249. package/dist/hooks/useOverflow.js +36 -25
  250. package/dist/i18n/resolve.d.ts.map +1 -1
  251. package/dist/i18n/resolve.js +7 -18
  252. package/dist/theme/Theme.d.ts.map +1 -1
  253. package/dist/theme/Theme.js +2 -7
  254. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  255. package/dist/theme/generateThemeRules.js +51 -0
  256. package/dist/theme/hct.d.ts +0 -1
  257. package/dist/theme/hct.d.ts.map +1 -1
  258. package/dist/theme/hct.js +1 -1
  259. package/dist/theme/syntax/defineSyntaxTheme.d.ts.map +1 -1
  260. package/dist/theme/syntax/defineSyntaxTheme.js +2 -1
  261. package/dist/utils/devWarning.d.ts +42 -0
  262. package/dist/utils/devWarning.d.ts.map +1 -0
  263. package/dist/utils/devWarning.js +88 -0
  264. package/dist/utils/index.d.ts +3 -1
  265. package/dist/utils/index.d.ts.map +1 -1
  266. package/dist/utils/index.js +3 -2
  267. package/dist/utils/plainDate.d.ts +30 -0
  268. package/dist/utils/plainDate.d.ts.map +1 -1
  269. package/dist/utils/plainDate.js +53 -0
  270. package/dist/utils/types.d.ts +13 -0
  271. package/dist/utils/types.d.ts.map +1 -1
  272. package/locales/en.json +24 -0
  273. package/locales/fr-FR.json +14 -0
  274. package/locales/pseudo.json +18 -0
  275. package/package.json +1 -1
  276. package/src/AppShell/AppShell.tsx +6 -0
  277. package/src/AppShell/AppShellMobileContext.tsx +8 -0
  278. package/src/AppShell/useAppShellMobile.doc.mjs +8 -0
  279. package/src/Avatar/Avatar.doc.mjs +15 -3
  280. package/src/Avatar/Avatar.test.tsx +156 -1
  281. package/src/Avatar/Avatar.tsx +96 -4
  282. package/src/Avatar/AvatarStatusDot.doc.mjs +23 -12
  283. package/src/Avatar/AvatarStatusDot.test.tsx +284 -0
  284. package/src/Avatar/AvatarStatusDot.tsx +178 -21
  285. package/src/Banner/Banner.doc.mjs +9 -0
  286. package/src/Banner/Banner.test.tsx +30 -0
  287. package/src/Banner/Banner.tsx +47 -3
  288. package/src/Breadcrumbs/BreadcrumbItem.doc.mjs +26 -0
  289. package/src/Breadcrumbs/BreadcrumbItem.tsx +333 -14
  290. package/src/Breadcrumbs/Breadcrumbs.doc.mjs +2 -0
  291. package/src/Breadcrumbs/Breadcrumbs.test.tsx +284 -2
  292. package/src/Breadcrumbs/index.ts +27 -0
  293. package/src/Button/Button.doc.mjs +9 -0
  294. package/src/Button/Button.test.tsx +49 -0
  295. package/src/Button/Button.tsx +25 -1
  296. package/src/ButtonGroup/ButtonGroup.doc.mjs +3 -0
  297. package/src/ButtonGroup/ButtonGroup.test.tsx +37 -0
  298. package/src/ButtonGroup/ButtonGroup.tsx +32 -0
  299. package/src/Calendar/Calendar.doc.mjs +12 -1
  300. package/src/Calendar/Calendar.test.tsx +193 -0
  301. package/src/Calendar/Calendar.tsx +38 -16
  302. package/src/Calendar/dayCellUtils.test.ts +5 -0
  303. package/src/Calendar/dayCellUtils.ts +1 -1
  304. package/src/Card/Card.doc.mjs +9 -0
  305. package/src/Card/Card.test.tsx +21 -3
  306. package/src/Card/Card.tsx +35 -2
  307. package/src/Carousel/Carousel.doc.mjs +1 -0
  308. package/src/Carousel/Carousel.test.tsx +53 -0
  309. package/src/Carousel/Carousel.tsx +34 -4
  310. package/src/Chat/ChatComposer.doc.mjs +12 -3
  311. package/src/Chat/ChatComposer.test.tsx +115 -0
  312. package/src/Chat/ChatComposer.tsx +73 -9
  313. package/src/Chat/ChatComposerDrawer.test.tsx +61 -0
  314. package/src/Chat/ChatComposerDrawer.tsx +11 -1
  315. package/src/Chat/ChatComposerInput.doc.mjs +7 -0
  316. package/src/Chat/ChatComposerInput.test.tsx +103 -0
  317. package/src/Chat/ChatComposerInput.tsx +49 -3
  318. package/src/Chat/ChatContext.tsx +37 -10
  319. package/src/Chat/ChatDictationButton.tsx +4 -1
  320. package/src/Chat/ChatLayout.tsx +3 -1
  321. package/src/Chat/ChatLayoutScrollButton.tsx +7 -2
  322. package/src/Chat/ChatMessageMetadata.tsx +1 -2
  323. package/src/Chat/ChatSendButton.tsx +11 -1
  324. package/src/Chat/ChatToolCalls.test.tsx +64 -0
  325. package/src/Chat/ChatToolCalls.tsx +25 -4
  326. package/src/Chat/index.ts +5 -1
  327. package/src/Chat/useChatStreamScroll.test.tsx +49 -0
  328. package/src/Chat/useChatStreamScroll.ts +27 -10
  329. package/src/Chat/useSpeechRecognition.ts +1 -1
  330. package/src/Chat/useTriggerMenu.tsx +3 -1
  331. package/src/CheckboxInput/CheckboxInput.test.tsx +32 -1
  332. package/src/CheckboxInput/CheckboxInput.tsx +1 -1
  333. package/src/CheckboxList/CheckboxList.test.tsx +81 -3
  334. package/src/CheckboxList/CheckboxListItem.doc.mjs +33 -5
  335. package/src/CheckboxList/CheckboxListItem.tsx +41 -5
  336. package/src/ClickableCard/ClickableCard.doc.mjs +2 -0
  337. package/src/ClickableCard/ClickableCard.test.tsx +34 -0
  338. package/src/ClickableCard/ClickableCard.tsx +13 -3
  339. package/src/CodeBlock/CodeBlock.test.tsx +56 -0
  340. package/src/CodeBlock/CodeBlock.tsx +9 -1
  341. package/src/CommandPalette/CommandPalette.doc.mjs +1 -0
  342. package/src/CommandPalette/CommandPalette.tsx +3 -1
  343. package/src/CommandPalette/CommandPaletteGroup.test.tsx +71 -0
  344. package/src/CommandPalette/CommandPaletteGroup.tsx +12 -1
  345. package/src/CommandPalette/CommandPaletteInput.doc.mjs +26 -9
  346. package/src/CommandPalette/CommandPaletteInput.test.tsx +34 -0
  347. package/src/CommandPalette/CommandPaletteInput.tsx +11 -0
  348. package/src/ContextMenu/ContextMenu.test.tsx +193 -0
  349. package/src/DateInput/DateInput.doc.mjs +16 -0
  350. package/src/DateInput/DateInput.test.tsx +108 -0
  351. package/src/DateInput/DateInput.tsx +68 -3
  352. package/src/DateInput/index.ts +1 -0
  353. package/src/DateTimeInput/DateTimeInput.test.tsx +35 -0
  354. package/src/DateTimeInput/DateTimeInput.tsx +14 -2
  355. package/src/Dialog/Dialog.doc.mjs +2 -2
  356. package/src/Dialog/Dialog.test.tsx +77 -4
  357. package/src/Dialog/Dialog.tsx +89 -5
  358. package/src/Dialog/DialogContext.ts +9 -2
  359. package/src/Dialog/DialogHeader.doc.mjs +2 -2
  360. package/src/Dialog/DialogHeader.tsx +9 -2
  361. package/src/Divider/Divider.tsx +2 -2
  362. package/src/DropdownMenu/DropdownMenu.test.tsx +169 -0
  363. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +1 -2
  364. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +1 -2
  365. package/src/Field/Field.test.tsx +57 -12
  366. package/src/Field/Field.tsx +6 -5
  367. package/src/Field/FieldLabel.tsx +8 -0
  368. package/src/FieldStatus/FieldStatus.doc.mjs +17 -5
  369. package/src/FieldStatus/FieldStatus.test.tsx +78 -40
  370. package/src/FieldStatus/FieldStatus.tsx +22 -4
  371. package/src/FileInput/FileInput.test.tsx +52 -3
  372. package/src/FileInput/FileInput.tsx +13 -2
  373. package/src/HoverCard/HoverCard.doc.mjs +13 -0
  374. package/src/HoverCard/HoverCard.test.tsx +21 -2
  375. package/src/HoverCard/HoverCard.tsx +11 -0
  376. package/src/HoverCard/useHoverCard.doc.mjs +2 -0
  377. package/src/HoverCard/useHoverCard.tsx +24 -2
  378. package/src/Icon/Icon.doc.mjs +3 -2
  379. package/src/Icon/Icon.tsx +7 -2
  380. package/src/IconButton/IconButton.doc.mjs +8 -0
  381. package/src/IconButton/IconButton.test.tsx +15 -18
  382. package/src/Item/Item.test.tsx +40 -18
  383. package/src/Item/Item.tsx +38 -2
  384. package/src/Layer/useLayer.tsx +8 -0
  385. package/src/List/List.test.tsx +9 -8
  386. package/src/MobileNav/MobileNav.tsx +12 -1
  387. package/src/MobileNav/MobileNavToggle.test.tsx +116 -0
  388. package/src/MobileNav/MobileNavToggle.tsx +9 -1
  389. package/src/MultiSelector/MultiSelector.doc.mjs +4 -2
  390. package/src/MultiSelector/MultiSelector.test.tsx +172 -3
  391. package/src/MultiSelector/MultiSelector.tsx +55 -12
  392. package/src/MultiSelector/hooks.ts +17 -0
  393. package/src/NumberInput/NumberInput.test.tsx +11 -7
  394. package/src/NumberInput/NumberInput.tsx +1 -2
  395. package/src/Outline/Outline.tsx +0 -1
  396. package/src/OverflowList/OverflowList.doc.mjs +39 -6
  397. package/src/OverflowList/OverflowList.test.tsx +111 -0
  398. package/src/OverflowList/OverflowList.tsx +48 -8
  399. package/src/Popover/Popover.tsx +7 -4
  400. package/src/Popover/usePopover.tsx +9 -13
  401. package/src/PowerSearch/PowerSearch.doc.mjs +14 -0
  402. package/src/PowerSearch/PowerSearch.test.tsx +20 -0
  403. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +67 -0
  404. package/src/PowerSearch/PowerSearchEditPopover.tsx +1 -1
  405. package/src/PowerSearch/usePowerSearchSource.ts +1 -2
  406. package/src/SelectableCard/SelectableCard.doc.mjs +2 -0
  407. package/src/SelectableCard/SelectableCard.test.tsx +46 -4
  408. package/src/SelectableCard/SelectableCard.tsx +29 -16
  409. package/src/Selector/Selector.test.tsx +69 -0
  410. package/src/Selector/Selector.tsx +5 -1
  411. package/src/Selector/hooks.ts +17 -0
  412. package/src/SideNav/SideNav.test.tsx +119 -3
  413. package/src/SideNav/SideNavCollapseButton.tsx +6 -1
  414. package/src/SideNav/SideNavHeading.tsx +24 -8
  415. package/src/SideNav/SideNavItem.tsx +5 -1
  416. package/src/Slider/Slider.test.tsx +75 -2
  417. package/src/Slider/Slider.tsx +21 -5
  418. package/src/Switch/Switch.test.tsx +32 -1
  419. package/src/Table/BaseTable.tsx +3 -5
  420. package/src/Table/index.ts +5 -0
  421. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +6 -2
  422. package/src/Table/plugins/rowStatus/index.ts +7 -0
  423. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +138 -0
  424. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +189 -0
  425. package/src/Table/plugins/selection/useTableSelection.test.tsx +16 -0
  426. package/src/Table/plugins/selection/useTableSelection.tsx +19 -1
  427. package/src/Table/plugins/tree/useTableTreeData.test.tsx +135 -3
  428. package/src/Table/plugins/tree/useTableTreeData.tsx +128 -1
  429. package/src/Table/plugins/tree/useTableTreeState.test.tsx +104 -0
  430. package/src/Table/plugins/tree/useTableTreeState.tsx +46 -2
  431. package/src/Table/useBaseTablePlugins.ts +10 -6
  432. package/src/Table/useTableRowStatus.doc.mjs +30 -0
  433. package/src/Table/useTableSelection.doc.mjs +65 -14
  434. package/src/Table/useTableTreeData.doc.mjs +29 -0
  435. package/src/Table/useTableTreeState.doc.mjs +2 -2
  436. package/src/TextArea/TextArea.test.tsx +9 -1
  437. package/src/Thumbnail/Thumbnail.doc.mjs +19 -10
  438. package/src/Thumbnail/Thumbnail.test.tsx +98 -7
  439. package/src/Thumbnail/Thumbnail.tsx +141 -41
  440. package/src/Thumbnail/thumbnail.markers.stylex.ts +12 -0
  441. package/src/TimeInput/TimeInput.test.tsx +76 -1
  442. package/src/TimeInput/TimeInput.tsx +19 -6
  443. package/src/Timestamp/Timestamp.doc.mjs +4 -4
  444. package/src/Timestamp/Timestamp.test.tsx +32 -0
  445. package/src/Timestamp/Timestamp.tsx +28 -8
  446. package/src/Toast/useToast.tsx +7 -8
  447. package/src/ToggleButton/ToggleButtonGroup.tsx +1 -1
  448. package/src/Tokenizer/Tokenizer.doc.mjs +15 -1
  449. package/src/Tokenizer/Tokenizer.test.tsx +183 -2
  450. package/src/Tokenizer/Tokenizer.tsx +16 -4
  451. package/src/TopNav/TopNav.test.tsx +125 -1
  452. package/src/TopNav/TopNavHeading.tsx +24 -8
  453. package/src/TopNav/TopNavMegaMenu.test.tsx +149 -1
  454. package/src/TopNav/TopNavMegaMenu.tsx +10 -4
  455. package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +3 -3
  456. package/src/TopNav/TopNavMegaMenuFeaturedCard.tsx +19 -2
  457. package/src/TopNav/TopNavMenu.test.tsx +118 -4
  458. package/src/TopNav/TopNavMenu.tsx +83 -5
  459. package/src/TreeList/TreeList.doc.mjs +22 -0
  460. package/src/TreeList/TreeList.test.tsx +232 -0
  461. package/src/TreeList/TreeList.tsx +18 -2
  462. package/src/TreeList/TreeListBranches.tsx +16 -2
  463. package/src/TreeList/TreeListItem.tsx +51 -11
  464. package/src/TreeList/TreeListTypes.ts +27 -1
  465. package/src/TreeList/index.ts +2 -2
  466. package/src/authoring/doc.ts +1 -3
  467. package/src/docs-types.ts +5 -0
  468. package/src/hooks/computeOverflow.test.ts +422 -0
  469. package/src/hooks/computeOverflow.ts +290 -0
  470. package/src/hooks/index.ts +2 -0
  471. package/src/hooks/useDevWarning.test.tsx +45 -0
  472. package/src/hooks/useDevWarning.ts +54 -0
  473. package/src/hooks/useOverflow.test.ts +96 -4
  474. package/src/hooks/useOverflow.ts +74 -33
  475. package/src/i18n/InternationalizationProvider.doc.mjs +99 -2
  476. package/src/i18n/resolve.ts +9 -21
  477. package/src/theme/Theme.tsx +11 -14
  478. package/src/theme/generateThemeRules.test.ts +65 -0
  479. package/src/theme/generateThemeRules.ts +56 -0
  480. package/src/theme/hct.ts +1 -1
  481. package/src/theme/syntax/defineSyntaxTheme.ts +5 -6
  482. package/src/utils/devWarning.test.ts +56 -0
  483. package/src/utils/devWarning.ts +102 -0
  484. package/src/utils/index.ts +6 -0
  485. package/src/utils/plainDate.test.ts +22 -0
  486. package/src/utils/plainDate.ts +61 -0
  487. package/src/utils/types.ts +14 -0
@@ -0,0 +1,189 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file useTableRowStatus.tsx
7
+ * @input React, StyleX, Icon, Table types
8
+ * @output Exports useTableRowStatus hook + config type
9
+ * @position Row-status 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 {useMemo} from 'react';
16
+ import * as stylex from '@stylexjs/stylex';
17
+ import {Icon, type IconColor, type IconName} from '../../../Icon';
18
+ import {Tooltip} from '../../../Tooltip';
19
+ import type {TableColumn, TablePlugin} from '../../types';
20
+
21
+ /**
22
+ * Semantic status colors, resolved to the design system's icon color tokens.
23
+ * Prefer these over raw CSS so status colors stay consistent with the theme.
24
+ */
25
+ export type TableRowStatusColor =
26
+ | 'accent'
27
+ | 'success'
28
+ | 'error'
29
+ | 'warning'
30
+ | 'red'
31
+ | 'orange'
32
+ | 'green'
33
+ | 'yellow'
34
+ | 'blue'
35
+ | 'gray';
36
+
37
+ const SEMANTIC_COLORS: Record<TableRowStatusColor, string> = {
38
+ accent: 'var(--color-icon-accent)',
39
+ success: 'var(--color-icon-green)',
40
+ error: 'var(--color-icon-red)',
41
+ warning: 'var(--color-icon-orange)',
42
+ red: 'var(--color-icon-red)',
43
+ orange: 'var(--color-icon-orange)',
44
+ green: 'var(--color-icon-green)',
45
+ yellow: 'var(--color-icon-yellow)',
46
+ blue: 'var(--color-icon-blue)',
47
+ gray: 'var(--color-icon-gray)',
48
+ };
49
+
50
+ /** Icon colors that map cleanly from a semantic status color. */
51
+ const ICON_COLOR_BY_STATUS: Record<TableRowStatusColor, IconColor> = {
52
+ accent: 'accent',
53
+ success: 'success',
54
+ error: 'error',
55
+ warning: 'warning',
56
+ red: 'red',
57
+ orange: 'warning',
58
+ green: 'green',
59
+ yellow: 'warning',
60
+ blue: 'blue',
61
+ gray: 'gray',
62
+ };
63
+
64
+ /**
65
+ * A row's status indicator. `color` accepts a semantic status color
66
+ * (mapped to a theme token) or any raw CSS color string as an escape hatch.
67
+ * By default the plugin renders a colored status dot. Provide `icon` to signal
68
+ * status by shape as well as color, which is more accessible when several
69
+ * statuses coexist. `label` is required so the status is never conveyed by
70
+ * color alone — it names the indicator for assistive technology and shows on
71
+ * hover. Return `null` for rows with no status.
72
+ */
73
+ export interface TableRowStatus {
74
+ /** Semantic status color (preferred) or a raw CSS color string. */
75
+ color: TableRowStatusColor | (string & {});
76
+ /** Optional icon rendered as the signifier instead of the dot (shape as an a11y differentiator). */
77
+ icon?: IconName;
78
+ /**
79
+ * Accessible name for the status, announced to assistive technology and
80
+ * shown in a tooltip on hover. Required: a status must never be conveyed by
81
+ * color alone.
82
+ */
83
+ label: string;
84
+ }
85
+
86
+ /** Configuration for {@link useTableRowStatus}. */
87
+ export interface UseTableRowStatusConfig<T extends Record<string, unknown>> {
88
+ /**
89
+ * Derive the status indicator for a row. Return `null` for no indicator.
90
+ * Memoize with `useCallback` for a stable plugin identity across renders.
91
+ *
92
+ * @example
93
+ * ```
94
+ * getStatus: row =>
95
+ * row.hasError ? {color: 'error', icon: 'error', label: 'Error'} : null
96
+ * ```
97
+ */
98
+ getStatus: (item: T) => TableRowStatus | null;
99
+ }
100
+
101
+ // The status column holds a small centered dot (or an icon when provided).
102
+ // A fixed narrow width keeps every row's indicator aligned in one gutter.
103
+ const STATUS_COLUMN_WIDTH = {type: 'pixel' as const, value: 28};
104
+
105
+ /** Resolve a semantic color name to a token, or pass a raw CSS color through. */
106
+ function resolveColor(color: string): string {
107
+ return (SEMANTIC_COLORS as Record<string, string>)[color] ?? color;
108
+ }
109
+
110
+ const styles = stylex.create({
111
+ // Centers the dot or icon within the narrow status column.
112
+ wrap: {
113
+ display: 'flex',
114
+ alignItems: 'center',
115
+ justifyContent: 'center',
116
+ },
117
+ dot: (color: string) => ({
118
+ width: '8px',
119
+ height: '8px',
120
+ borderRadius: '50%',
121
+ backgroundColor: color,
122
+ flexShrink: 0,
123
+ }),
124
+ });
125
+
126
+ /**
127
+ * Returns a {@link TablePlugin} that prepends a narrow column signaling per-row
128
+ * status: a colored status dot by default, or an icon when `icon` is provided
129
+ * (shape + color is more accessible than color alone).
130
+ *
131
+ * @example
132
+ * ```
133
+ * const rowStatus = useTableRowStatus<Row>({
134
+ * getStatus: row =>
135
+ * row.state === 'error'
136
+ * ? {color: 'error', icon: 'error', label: 'Error'}
137
+ * : null,
138
+ * });
139
+ * <Table data={data} columns={columns} idKey="id" plugins={{rowStatus}} />;
140
+ * ```
141
+ */
142
+ export function useTableRowStatus<T extends Record<string, unknown>>(
143
+ config: UseTableRowStatusConfig<T>,
144
+ ): TablePlugin<T> {
145
+ const {getStatus} = config;
146
+
147
+ return useMemo(
148
+ (): TablePlugin<T> => ({
149
+ transformColumns(columns) {
150
+ const statusColumn: TableColumn<T> = {
151
+ key: '__rowStatus',
152
+ header: '',
153
+ width: STATUS_COLUMN_WIDTH,
154
+ resizable: false,
155
+ renderCell: (item: T) => {
156
+ const status = getStatus(item);
157
+ if (!status) {
158
+ return null;
159
+ }
160
+ const signifier = status.icon ? (
161
+ <Icon
162
+ icon={status.icon}
163
+ size="xsm"
164
+ color={
165
+ ICON_COLOR_BY_STATUS[status.color as TableRowStatusColor] ??
166
+ 'primary'
167
+ }
168
+ />
169
+ ) : (
170
+ <span {...stylex.props(styles.dot(resolveColor(status.color)))} />
171
+ );
172
+ return (
173
+ <Tooltip content={status.label}>
174
+ <span
175
+ {...stylex.props(styles.wrap)}
176
+ role="img"
177
+ aria-label={status.label}>
178
+ {signifier}
179
+ </span>
180
+ </Tooltip>
181
+ );
182
+ },
183
+ };
184
+ return [statusColumn, ...columns];
185
+ },
186
+ }),
187
+ [getStatus],
188
+ );
189
+ }
@@ -40,9 +40,11 @@ const selectableColumns: TableColumn<SelectableUser>[] = [
40
40
  function SelectionTable({
41
41
  getIsItemSelectable,
42
42
  getIsItemEnabled,
43
+ getRowLabel,
43
44
  }: {
44
45
  getIsItemSelectable?: (item: SelectableUser) => boolean;
45
46
  getIsItemEnabled?: (item: SelectableUser) => boolean;
47
+ getRowLabel?: (item: SelectableUser) => string;
46
48
  }) {
47
49
  const [selectedKeys, setSelectedKeys] = useState<Set<string>>(new Set());
48
50
 
@@ -75,6 +77,7 @@ function SelectionTable({
75
77
  },
76
78
  getIsItemSelectable,
77
79
  getIsItemEnabled,
80
+ getRowLabel,
78
81
  });
79
82
 
80
83
  return (
@@ -109,6 +112,19 @@ describe('useTableSelection', () => {
109
112
  expect(rowCheckboxes).toHaveLength(3);
110
113
  });
111
114
 
115
+ it('derives per-row accessible names from getRowLabel', () => {
116
+ render(<SelectionTable getRowLabel={item => item.name} />);
117
+ expect(screen.getByLabelText('Select Alice')).toBeInTheDocument();
118
+ expect(screen.getByLabelText('Select Bob')).toBeInTheDocument();
119
+ expect(screen.getByLabelText('Select Charlie')).toBeInTheDocument();
120
+ expect(screen.queryByLabelText('Select row')).not.toBeInTheDocument();
121
+ });
122
+
123
+ it('keeps the "Select all rows" header label when getRowLabel is provided', () => {
124
+ render(<SelectionTable getRowLabel={item => item.name} />);
125
+ expect(screen.getByLabelText('Select all rows')).toBeInTheDocument();
126
+ });
127
+
112
128
  it('toggles individual row selection on click', async () => {
113
129
  const user = userEvent.setup();
114
130
  render(<SelectionTable />);
@@ -68,6 +68,19 @@ export interface UseTableSelectionConfig<T extends Record<string, unknown>> {
68
68
  getIsItemSelectable?: (item: T) => boolean;
69
69
  /** Is this row's checkbox interactive? Disabled rows show disabled checkbox. @default () => true */
70
70
  getIsItemEnabled?: (item: T) => boolean;
71
+ /**
72
+ * Derive a human-readable identity for a row, used in the row checkbox's
73
+ * hidden label as `Select ${getRowLabel(item)}`. Without it, every row
74
+ * checkbox announces an undifferentiated "Select row" to screen readers.
75
+ * With `getRowLabel: item => item.name`, checkbox accessible names become
76
+ * "Select Alice", "Select Bob", and so on.
77
+ *
78
+ * @example
79
+ * ```
80
+ * getRowLabel: item => item.name
81
+ * ```
82
+ */
83
+ getRowLabel?: (item: T) => string;
71
84
  }
72
85
 
73
86
  // =============================================================================
@@ -235,6 +248,7 @@ function SelectionCellContentInner<T extends Record<string, unknown>>({
235
248
  const isSelected = useIsItemSelected(store, item);
236
249
  const selectable = config.getIsItemSelectable?.(item) ?? true;
237
250
  const enabled = config.getIsItemEnabled?.(item) ?? true;
251
+ const rowLabel = config.getRowLabel?.(item);
238
252
 
239
253
  if (!selectable) {
240
254
  return null;
@@ -242,7 +256,11 @@ function SelectionCellContentInner<T extends Record<string, unknown>>({
242
256
 
243
257
  return (
244
258
  <CheckboxInput
245
- label={t('@astryx.table.selection.selectRow')}
259
+ label={
260
+ rowLabel != null
261
+ ? t('@astryx.table.selection.selectRowNamed', {label: rowLabel})
262
+ : t('@astryx.table.selection.selectRow')
263
+ }
246
264
  isLabelHidden
247
265
  value={isSelected}
248
266
  onChange={() =>
@@ -55,14 +55,18 @@ const columns: TableColumn<FileRow>[] = [
55
55
  ];
56
56
 
57
57
  function TreeTable(
58
- props: Partial<UseTableTreeStateConfig<FileRow>> & {data?: FileRow[]},
58
+ props: Partial<UseTableTreeStateConfig<FileRow>> & {
59
+ data?: FileRow[];
60
+ hasExpandAllControl?: boolean;
61
+ },
59
62
  ) {
63
+ const {hasExpandAllControl, ...stateProps} = props;
60
64
  const {visibleData, treeConfig} = useTableTreeState<FileRow>({
61
65
  data: props.data ?? fileTree,
62
66
  idKey: 'id',
63
- ...props,
67
+ ...stateProps,
64
68
  });
65
- const tree = useTableTreeData(treeConfig);
69
+ const tree = useTableTreeData({...treeConfig, hasExpandAllControl});
66
70
 
67
71
  return (
68
72
  <Table data={visibleData} columns={columns} idKey="id" plugins={{tree}} />
@@ -592,3 +596,131 @@ describe('useTableTreeData — composition with sorting', () => {
592
596
  expect(names).toEqual(['src', 'utils.ts', 'components', 'README.md']);
593
597
  });
594
598
  });
599
+
600
+ // =============================================================================
601
+ // header expand-all control
602
+ // =============================================================================
603
+
604
+ describe('useTableTreeData: expand-all header control', () => {
605
+ /** The header row is the first row; return its expand-all toggle if present. */
606
+ function queryExpandAllButton(): HTMLElement | null {
607
+ const headerRow = screen.getAllByRole('row')[0];
608
+ return (
609
+ within(headerRow).queryByRole('button', {name: /expand all/i}) ??
610
+ within(headerRow).queryByRole('button', {name: /collapse all/i})
611
+ );
612
+ }
613
+
614
+ it('renders no expand-all control by default', () => {
615
+ render(<TreeTable defaultExpandedIds={['src']} />);
616
+ expect(queryExpandAllButton()).toBeNull();
617
+ });
618
+
619
+ it('renders an "Expand all" toggle in the tree column header when enabled and collapsed', () => {
620
+ render(<TreeTable hasExpandAllControl />);
621
+ const headerRow = screen.getAllByRole('row')[0];
622
+ expect(
623
+ within(headerRow).getByRole('button', {name: /expand all/i}),
624
+ ).toBeInTheDocument();
625
+ });
626
+
627
+ it('expands every row when the collapsed toggle is clicked', async () => {
628
+ const user = userEvent.setup();
629
+ render(<TreeTable hasExpandAllControl />);
630
+
631
+ // Collapsed: only roots are visible.
632
+ expect(getBodyRows()).toHaveLength(2);
633
+
634
+ await user.click(
635
+ within(screen.getAllByRole('row')[0]).getByRole('button', {
636
+ name: /expand all/i,
637
+ }),
638
+ );
639
+
640
+ // Every level is now visible.
641
+ expect(
642
+ getBodyRows().map(r => within(r).getByText(/.+/, {selector: 'td *'})),
643
+ ).not.toHaveLength(2);
644
+ expect(screen.getByText('Button.tsx')).toBeInTheDocument();
645
+ });
646
+
647
+ it('relabels the toggle "Collapse all" once everything is expanded', () => {
648
+ render(
649
+ <TreeTable
650
+ hasExpandAllControl
651
+ defaultExpandedIds={['src', 'components']}
652
+ />,
653
+ );
654
+ const headerRow = screen.getAllByRole('row')[0];
655
+ expect(
656
+ within(headerRow).getByRole('button', {name: /collapse all/i}),
657
+ ).toBeInTheDocument();
658
+ });
659
+
660
+ it('collapses back to roots when the expanded toggle is clicked', async () => {
661
+ const user = userEvent.setup();
662
+ render(
663
+ <TreeTable
664
+ hasExpandAllControl
665
+ defaultExpandedIds={['src', 'components']}
666
+ />,
667
+ );
668
+ expect(screen.getByText('Button.tsx')).toBeInTheDocument();
669
+
670
+ await user.click(
671
+ within(screen.getAllByRole('row')[0]).getByRole('button', {
672
+ name: /collapse all/i,
673
+ }),
674
+ );
675
+
676
+ expect(getBodyRows()).toHaveLength(2);
677
+ expect(screen.queryByText('Button.tsx')).not.toBeInTheDocument();
678
+ });
679
+
680
+ it('marks the toggle aria-expanded=false in the indeterminate (partial) state', () => {
681
+ // 'src' expanded but 'components' not: partially expanded.
682
+ render(<TreeTable hasExpandAllControl defaultExpandedIds={['src']} />);
683
+ const headerRow = screen.getAllByRole('row')[0];
684
+ const toggle = within(headerRow).getByRole('button', {
685
+ name: /expand all/i,
686
+ });
687
+ expect(toggle).toHaveAttribute('aria-expanded', 'false');
688
+ });
689
+
690
+ it('does not render the control for flat data even when enabled', () => {
691
+ const flat: FileRow[] = [
692
+ {id: 'a', name: 'A', size: 1},
693
+ {id: 'b', name: 'B', size: 2},
694
+ ];
695
+ render(<TreeTable data={flat} hasExpandAllControl />);
696
+ expect(queryExpandAllButton()).toBeNull();
697
+ });
698
+
699
+ it('renders the toggle inline with the header label, not stacked above it', () => {
700
+ render(<TreeTable hasExpandAllControl />);
701
+ const headerRow = screen.getAllByRole('row')[0];
702
+ const toggle = within(headerRow).getByRole('button', {
703
+ name: /expand all/i,
704
+ });
705
+ // The tree column's <th> holds the label text ('Name'). The bug was that
706
+ // the toggle went into the `before` slot, which BaseTable renders as a
707
+ // block sibling of the label, stacking the chevron ABOVE the title. The
708
+ // fix wraps the label + toggle in one inline-flex container, so they must
709
+ // share the same immediate parent (the toggle is a sibling of the label,
710
+ // not in a separate stacked slot).
711
+ const th = toggle.closest('th');
712
+ expect(th).not.toBeNull();
713
+ const label = within(th as HTMLElement).getByText('Name');
714
+ const wrapper = label.parentElement as HTMLElement;
715
+ expect(wrapper).toContainElement(toggle);
716
+ // The toggle precedes the label within that shared wrapper (chevron sits
717
+ // to the LEFT of the title). StyleX compiles the inline-flex layout to a
718
+ // class on this wrapper; assert the class is present (real CSS is not
719
+ // applied in jsdom, so we check the class hook, not computed display).
720
+ expect(wrapper.className).not.toBe('');
721
+ const kids = Array.from(wrapper.childNodes);
722
+ expect(kids.indexOf(toggle)).toBeLessThan(
723
+ kids.findIndex(n => n.textContent === 'Name' || n.contains(label)),
724
+ );
725
+ });
726
+ });
@@ -43,7 +43,12 @@ import * as stylex from '@stylexjs/stylex';
43
43
  import {colorVars, radiusVars, spacingVars} from '../../../theme/tokens.stylex';
44
44
  import {Icon} from '../../../Icon';
45
45
  import {mergeRefs} from '../../../utils';
46
- import type {TablePlugin, TableColumn, BodyRowRenderProps} from '../../types';
46
+ import type {
47
+ TablePlugin,
48
+ TableColumn,
49
+ BodyRowRenderProps,
50
+ HeaderCellRenderProps,
51
+ } from '../../types';
47
52
  import {useTranslator} from '../../../i18n';
48
53
 
49
54
  // =============================================================================
@@ -78,6 +83,22 @@ export interface UseTableTreeDataConfig<T extends Record<string, unknown>> {
78
83
  * identically to a Table without the plugin.
79
84
  */
80
85
  hasExpandableRows: boolean;
86
+ /**
87
+ * Aggregate expansion state across every expandable row. When provided
88
+ * together with `onExpandAll`/`onCollapseAll`, the tree column header shows
89
+ * an expand-all toggle. `useTableTreeState` supplies all three.
90
+ */
91
+ isAllExpanded?: boolean | 'indeterminate';
92
+ /** Expand every expandable row. Wired to the header expand-all control. */
93
+ onExpandAll?: () => void;
94
+ /** Collapse every row. Wired to the header expand-all control. */
95
+ onCollapseAll?: () => void;
96
+ /**
97
+ * Show the expand-all/collapse-all toggle in the tree column header. Needs
98
+ * `isAllExpanded` and `onExpandAll`/`onCollapseAll` to be present.
99
+ * @default false
100
+ */
101
+ hasExpandAllControl?: boolean;
81
102
  /**
82
103
  * Indent step per level, as spacing tokens.
83
104
  * @default 'md'
@@ -243,6 +264,13 @@ const treeStyles = stylex.create({
243
264
  height: '24px',
244
265
  flexShrink: '0',
245
266
  },
267
+ /** Header expand-all toggle: same affordance as a row expander. */
268
+ headerCell: {
269
+ display: 'inline-flex',
270
+ alignItems: 'center',
271
+ gap: spacingVars['--spacing-1'],
272
+ minWidth: 0,
273
+ },
246
274
  });
247
275
 
248
276
  // =============================================================================
@@ -282,6 +310,53 @@ function TreeExpander({
282
310
  );
283
311
  }
284
312
 
313
+ /**
314
+ * Header expand-all/collapse-all toggle. Rendered in the tree column header
315
+ * when `hasExpandAllControl` is set and the state hook supplies the aggregate
316
+ * `isAllExpanded` plus `onExpandAll`/`onCollapseAll`. Shares the chevron
317
+ * affordance with the per-row expander; the chevron points down (expanded)
318
+ * only when every expandable row is expanded, matching the row expander.
319
+ */
320
+ function TreeExpandAllToggle({
321
+ isAllExpanded,
322
+ onExpandAll,
323
+ onCollapseAll,
324
+ }: {
325
+ isAllExpanded: boolean | 'indeterminate';
326
+ onExpandAll: () => void;
327
+ onCollapseAll: () => void;
328
+ }) {
329
+ const t = useTranslator();
330
+ const allExpanded = isAllExpanded === true;
331
+ return (
332
+ <button
333
+ type="button"
334
+ {...stylex.props(treeStyles.expanderButton)}
335
+ onClick={e => {
336
+ e.stopPropagation();
337
+ if (allExpanded) {
338
+ onCollapseAll();
339
+ } else {
340
+ onExpandAll();
341
+ }
342
+ }}
343
+ aria-label={
344
+ allExpanded
345
+ ? t('@astryx.tableTree.collapseAllRows')
346
+ : t('@astryx.tableTree.expandAllRows')
347
+ }
348
+ aria-expanded={allExpanded}>
349
+ <span
350
+ {...stylex.props(
351
+ treeStyles.chevron,
352
+ allExpanded && treeStyles.chevronExpanded,
353
+ )}>
354
+ <Icon icon="chevronRight" size="xsm" />
355
+ </span>
356
+ </button>
357
+ );
358
+ }
359
+
285
360
  function TreeCellContent<T extends Record<string, unknown>>({
286
361
  item,
287
362
  children,
@@ -380,6 +455,11 @@ export function useTableTreeData<T extends Record<string, unknown>>(
380
455
  output: TableColumn<T>[];
381
456
  } | null>(null);
382
457
 
458
+ // The resolved tree column key, written by transformColumns (pipeline step 1)
459
+ // and read by transformHeaderCell (step 4) to place the expand-all toggle on
460
+ // the same column that carries the row expanders.
461
+ const treeKeyRef = useRef<string | undefined>(undefined);
462
+
383
463
  // The plugin object is created once per store and never changes shape:
384
464
  // every transform reads the live config through the store, and
385
465
  // internally no-ops when hasExpandableRows is false. Swapping between
@@ -426,6 +506,7 @@ export function useTableTreeData<T extends Record<string, unknown>>(
426
506
  ? treeColumnKey
427
507
  : (columns.find(c => !c.key.startsWith('__'))?.key ??
428
508
  columns[0]?.key);
509
+ treeKeyRef.current = treeKey;
429
510
 
430
511
  // Migration guarantee: flat data renders identically to a Table
431
512
  // without the plugin.
@@ -459,6 +540,52 @@ export function useTableTreeData<T extends Record<string, unknown>>(
459
540
  return output;
460
541
  },
461
542
 
543
+ transformHeaderCell(
544
+ props: HeaderCellRenderProps,
545
+ column: TableColumn<T>,
546
+ ): HeaderCellRenderProps {
547
+ const {
548
+ hasExpandableRows,
549
+ hasExpandAllControl,
550
+ isAllExpanded,
551
+ onExpandAll,
552
+ onCollapseAll,
553
+ } = store.getConfig();
554
+
555
+ // Only the tree column carries the toggle, and only when the control
556
+ // is enabled, the data is actually hierarchical, and the state hook
557
+ // supplied the aggregate state plus both handlers. Otherwise this is a
558
+ // pass-through (flat data stays a no-op).
559
+ if (
560
+ !hasExpandAllControl ||
561
+ !hasExpandableRows ||
562
+ column.key !== treeKeyRef.current ||
563
+ isAllExpanded === undefined ||
564
+ !onExpandAll ||
565
+ !onCollapseAll
566
+ ) {
567
+ return props;
568
+ }
569
+
570
+ // Wrap the header label + the toggle in one inline-flex row so the
571
+ // chevron sits to the LEFT of the title on the same line. BaseTable
572
+ // only applies its own flex row for the `after` slot, so a bare
573
+ // `before` would stack above the label in the block-level <th>.
574
+ return {
575
+ ...props,
576
+ content: (
577
+ <span {...stylex.props(treeStyles.headerCell)}>
578
+ <TreeExpandAllToggle
579
+ isAllExpanded={isAllExpanded}
580
+ onExpandAll={onExpandAll}
581
+ onCollapseAll={onCollapseAll}
582
+ />
583
+ {props.content}
584
+ </span>
585
+ ),
586
+ };
587
+ },
588
+
462
589
  transformBodyRow(props: BodyRowRenderProps, item: T) {
463
590
  // Attach a ref that subscribes to the store for imperative row
464
591
  // ARIA. The ref returns a cleanup so React unsubscribes on