@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
@@ -351,6 +351,10 @@ export function SideNavHeading({
351
351
 
352
352
  const popover = usePopover({
353
353
  dialogLabel: t('@astryx.sideNav.heading.dialogLabel'),
354
+ // The popup exposes its own role="menu" semantics; a role="dialog"
355
+ // aria-modal wrapper would announce "dialog, Navigation menu" around a
356
+ // menu (the anti-pattern removed in a478a3dcf).
357
+ role: 'none',
354
358
  hasCloseButton: false,
355
359
  });
356
360
 
@@ -428,7 +432,6 @@ export function SideNavHeading({
428
432
  {popover.render(
429
433
  <div
430
434
  ref={menuRef}
431
- role="menu"
432
435
  {...stylex.props(styles.popoverContent)}
433
436
  {...contentProps}>
434
437
  <button
@@ -461,9 +464,14 @@ export function SideNavHeading({
461
464
  )}
462
465
  </span>
463
466
  </button>
464
- <NavHeadingCloseContext value={closeMenuCtx}>
465
- {menu}
466
- </NavHeadingCloseContext>
467
+ {/* The menu role is scoped to the actual menu items so the
468
+ heading button above stays a valid sibling, not an invalid
469
+ child of a role="menu" element. */}
470
+ <div role="menu" aria-label={heading}>
471
+ <NavHeadingCloseContext value={closeMenuCtx}>
472
+ {menu}
473
+ </NavHeadingCloseContext>
474
+ </div>
467
475
  </div>,
468
476
  {placement: 'below', alignment: 'start', xstyle: styles.popover},
469
477
  )}
@@ -620,11 +628,15 @@ export function SideNavHeading({
620
628
  {popover.render(
621
629
  <div
622
630
  ref={menuRef}
623
- role="menu"
624
631
  {...stylex.props(styles.popoverContent)}
625
632
  {...contentProps}>
626
633
  {popoverHeadingContent}
627
- {menu}
634
+ {/* The menu role is scoped to the actual menu items so the
635
+ heading button above stays a valid sibling, not an invalid
636
+ child of a role="menu" element. */}
637
+ <div role="menu" aria-label={heading}>
638
+ {menu}
639
+ </div>
628
640
  </div>,
629
641
  {
630
642
  placement: 'below',
@@ -683,11 +695,15 @@ export function SideNavHeading({
683
695
  {popover.render(
684
696
  <div
685
697
  ref={menuRef}
686
- role="menu"
687
698
  {...stylex.props(styles.popoverContent)}
688
699
  {...contentProps}>
689
700
  {popoverHeadingContent}
690
- {menu}
701
+ {/* The menu role is scoped to the actual menu items so the
702
+ heading button above stays a valid sibling, not an invalid
703
+ child of a role="menu" element. */}
704
+ <div role="menu" aria-label={heading}>
705
+ {menu}
706
+ </div>
691
707
  </div>,
692
708
  {
693
709
  placement: 'below',
@@ -628,7 +628,11 @@ export function SideNavItem({
628
628
  <button
629
629
  type="button"
630
630
  onClick={handleToggleClick}
631
- aria-label={isItemCollapsed ? t('@astryx.sideNavItem.expand', {label}) : t('@astryx.sideNavItem.collapse', {label})}
631
+ aria-label={
632
+ isItemCollapsed
633
+ ? t('@astryx.sideNavItem.expand', {label})
634
+ : t('@astryx.sideNavItem.collapse', {label})
635
+ }
632
636
  aria-expanded={!isItemCollapsed}
633
637
  aria-controls={`${id}-children`}
634
638
  {...stylex.props(styles.expandToggle)}>
@@ -85,6 +85,19 @@ describe('Slider', () => {
85
85
  expect(slider).toHaveAttribute('aria-valuenow', '72');
86
86
  });
87
87
 
88
+ it.each([
89
+ {value: 150, expectedValue: 100, expectedPosition: '100%'},
90
+ {value: -50, expectedValue: 0, expectedPosition: '0%'},
91
+ ])(
92
+ 'clamps a controlled value of $value to $expectedValue',
93
+ ({value, expectedValue, expectedPosition}) => {
94
+ render(<Slider label="Volume" value={value} min={0} max={100} />);
95
+ const slider = screen.getByRole('slider');
96
+ expect(slider).toHaveAttribute('aria-valuenow', String(expectedValue));
97
+ expect(slider).toHaveStyle({left: expectedPosition});
98
+ },
99
+ );
100
+
88
101
  it('range mode sets correct aria values on both thumbs', () => {
89
102
  render(
90
103
  <Slider
@@ -155,6 +168,60 @@ describe('Slider', () => {
155
168
  expect(ariaHidden.length).toBeGreaterThanOrEqual(2);
156
169
  });
157
170
 
171
+ // --- Required state ---
172
+
173
+ it('conveys required state through the accessible description', () => {
174
+ render(<Slider label="Volume" value={50} isRequired />);
175
+ const slider = screen.getByRole('slider');
176
+ expect(slider).toHaveAccessibleDescription(/Required/);
177
+ // aria-required is not a supported property of role="slider" in
178
+ // WAI-ARIA 1.2, so it must never appear on the thumb.
179
+ expect(slider).not.toHaveAttribute('aria-required');
180
+ });
181
+
182
+ it('conveys required state on both thumbs of a range slider', () => {
183
+ render(
184
+ <Slider
185
+ label="Price range"
186
+ value={[20, 80] as [number, number]}
187
+ isRequired
188
+ />,
189
+ );
190
+ const sliders = screen.getAllByRole('slider');
191
+ expect(sliders).toHaveLength(2);
192
+ for (const thumb of sliders) {
193
+ expect(thumb).toHaveAccessibleDescription(/Required/);
194
+ expect(thumb).not.toHaveAttribute('aria-required');
195
+ }
196
+ });
197
+
198
+ it('combines required with other describedby parts in the description', () => {
199
+ render(
200
+ <Slider
201
+ label="Volume"
202
+ value={50}
203
+ description="Adjust the volume level"
204
+ isRequired
205
+ />,
206
+ );
207
+ const slider = screen.getByRole('slider');
208
+ expect(slider).toHaveAccessibleDescription(/Adjust the volume level/);
209
+ expect(slider).toHaveAccessibleDescription(/Required/);
210
+ });
211
+
212
+ it('does not mention required without isRequired', () => {
213
+ render(
214
+ <Slider
215
+ label="Volume"
216
+ value={50}
217
+ description="Adjust the volume level"
218
+ />,
219
+ );
220
+ const slider = screen.getByRole('slider');
221
+ expect(slider).not.toHaveAccessibleDescription(/Required/);
222
+ expect(slider).not.toHaveAttribute('aria-required');
223
+ });
224
+
158
225
  // --- Disabled guards ---
159
226
 
160
227
  it('disables thumbs when isDisabled is true', () => {
@@ -549,7 +616,11 @@ describe('Slider', () => {
549
616
  it('submits both range values under the same name', () => {
550
617
  const {container} = render(
551
618
  <form>
552
- <Slider label="Price" htmlName="price" value={[20, 80] as [number, number]} />
619
+ <Slider
620
+ label="Price"
621
+ htmlName="price"
622
+ value={[20, 80] as [number, number]}
623
+ />
553
624
  </form>,
554
625
  );
555
626
  const data = new FormData(container.querySelector('form')!);
@@ -562,7 +633,9 @@ describe('Slider', () => {
562
633
  <Slider label="Volume" htmlName="volume" value={50} isDisabled />
563
634
  </form>,
564
635
  );
565
- expect([...new FormData(container.querySelector('form')!).keys()]).toEqual([]);
636
+ expect([
637
+ ...new FormData(container.querySelector('form')!).keys(),
638
+ ]).toEqual([]);
566
639
  });
567
640
  });
568
641
  });
@@ -4,7 +4,7 @@
4
4
 
5
5
  /**
6
6
  * @file Slider.tsx
7
- * @input Uses React, useId, useRef, useCallback, Field, Tooltip, useTooltip
7
+ * @input Uses React, useId, useRef, useCallback, Field, Tooltip, useTooltip, VisuallyHidden
8
8
  * @output Exports Slider component, SliderProps, SliderSingleProps, SliderRangeProps, SliderBaseProps
9
9
  * @position Core implementation; consumed by index.ts, tested by Slider.test.tsx
10
10
  *
@@ -38,6 +38,7 @@ import {
38
38
  import {Field} from '../Field/Field';
39
39
  import {Tooltip} from '../Tooltip/Tooltip';
40
40
  import {useTooltip} from '../Tooltip';
41
+ import {VisuallyHidden} from '../VisuallyHidden';
41
42
  import type {InputStatus} from '../Field/types';
42
43
  import {mergeProps, mergeRefs} from '../utils';
43
44
  import type {BaseProps} from '../BaseProps';
@@ -393,6 +394,7 @@ export function Slider({ref, ...props}: SliderProps) {
393
394
  const id = useId();
394
395
  const descriptionID = useId();
395
396
  const statusMessageID = useId();
397
+ const requiredID = useId();
396
398
 
397
399
  const trackRef = useRef<HTMLDivElement>(null);
398
400
  const draggingThumbRef = useRef<number | null>(null);
@@ -412,6 +414,12 @@ export function Slider({ref, ...props}: SliderProps) {
412
414
  isEnabled: showsDisabledMessage,
413
415
  });
414
416
 
417
+ // Required state. `aria-required` is not a supported property of
418
+ // role="slider" in WAI-ARIA 1.2, so the thumb instead points its
419
+ // aria-describedby at a visually hidden "Required" span — mirroring the
420
+ // Field label's visible indicator (where isOptional takes precedence).
421
+ const conveysRequired = isRequired && !isOptional;
422
+
415
423
  // Build aria-describedby
416
424
  const describedByParts: string[] = [];
417
425
  if (description) {
@@ -420,6 +428,9 @@ export function Slider({ref, ...props}: SliderProps) {
420
428
  if (status?.message) {
421
429
  describedByParts.push(statusMessageID);
422
430
  }
431
+ if (conveysRequired) {
432
+ describedByParts.push(requiredID);
433
+ }
423
434
  if (showsDisabledMessage) {
424
435
  describedByParts.push(disabledMessageTooltip.describedBy);
425
436
  }
@@ -428,10 +439,12 @@ export function Slider({ref, ...props}: SliderProps) {
428
439
 
429
440
  // Value helpers — guard against undefined value (e.g. playground previews
430
441
  // that render the component without providing a value prop).
431
- const values: number[] = useMemo(
432
- () => (isRange ? value : [value != null ? value : min]),
433
- [isRange, value, min],
434
- );
442
+ const values: number[] = useMemo(() => {
443
+ const currentValues = Array.isArray(value)
444
+ ? value
445
+ : [value != null ? value : min];
446
+ return currentValues.map(currentValue => clamp(currentValue, min, max));
447
+ }, [value, min, max]);
435
448
 
436
449
  const valuesRef = useRef(values);
437
450
  valuesRef.current = values;
@@ -921,6 +934,9 @@ export function Slider({ref, ...props}: SliderProps) {
921
934
 
922
935
  {textDisplay}
923
936
  </div>
937
+ {conveysRequired && (
938
+ <VisuallyHidden id={requiredID}>Required</VisuallyHidden>
939
+ )}
924
940
  {showsDisabledMessage &&
925
941
  disabledMessageTooltip.renderTooltip(disabledMessage)}
926
942
  </Field>
@@ -9,10 +9,15 @@
9
9
  * SYNC: When Switch.tsx changes, update tests to match new behavior
10
10
  */
11
11
 
12
- import {describe, it, expect, vi, beforeEach} from 'vitest';
12
+ import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
13
13
  import {render, screen, fireEvent, waitFor} from '@testing-library/react';
14
14
  import userEvent from '@testing-library/user-event';
15
15
  import {Switch} from './Switch';
16
+ import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
17
+
18
+ afterEach(() => {
19
+ __resetLiveRegionsForTest();
20
+ });
16
21
 
17
22
  // Mock showPopover/hidePopover (not implemented in jsdom) so the tooltip layer
18
23
  // reflects its open state via a `popover-open` attribute the tests can assert.
@@ -324,6 +329,32 @@ describe('Switch', () => {
324
329
  expect(describedBy).toBeTruthy();
325
330
  });
326
331
 
332
+ // Regression: the status is conditionally mounted, so it must be announced
333
+ // through the persistent useAnnounce live region — a live region born
334
+ // together with its content is not reliably announced.
335
+ it('announces a status message that appears after mount', async () => {
336
+ const {rerender} = render(
337
+ <Switch label="Enable notifications" value={false} onChange={() => {}} />,
338
+ );
339
+ expect(
340
+ document.querySelector('[data-astryx-live-region="assertive"]'),
341
+ ).toBeNull();
342
+
343
+ rerender(
344
+ <Switch
345
+ label="Enable notifications"
346
+ value={false}
347
+ onChange={() => {}}
348
+ status={{type: 'error', message: 'Failed to save setting'}}
349
+ />,
350
+ );
351
+ await waitFor(() => {
352
+ expect(
353
+ document.querySelector('[data-astryx-live-region="assertive"]'),
354
+ ).toHaveTextContent('Failed to save setting');
355
+ });
356
+ });
357
+
327
358
  it('calls onFocus and onBlur callbacks', async () => {
328
359
  const user = userEvent.setup();
329
360
  const handleFocus = vi.fn();
@@ -42,6 +42,7 @@ import {TableHeaderCell} from './TableHeaderCell';
42
42
  import {TableHeader} from './TableHeader';
43
43
  import {TableBody} from './TableBody';
44
44
  import {mergeProps} from '../utils';
45
+ import {devError} from '../utils/devWarning';
45
46
  import {EmptyState} from '../EmptyState';
46
47
  import {Text} from '../Text';
47
48
  import {themeProps} from '../utils/themeProps';
@@ -92,10 +93,7 @@ function applyPlugins<TPlugin, TProps, TArgs extends unknown[]>(
92
93
  try {
93
94
  return transform(acc, ...args);
94
95
  } catch (error) {
95
- console.error(
96
- `[Table] Plugin at index ${index} threw in transform:`,
97
- error,
98
- );
96
+ devError('Table', `Plugin at index ${index} threw in transform:`, error);
99
97
  return acc;
100
98
  }
101
99
  }, initial);
@@ -593,7 +591,7 @@ function BaseTableInner<T extends Record<string, unknown>>({
593
591
  try {
594
592
  tableElement = plugin.transformTableContext(tableElement);
595
593
  } catch (error) {
596
- console.error('[Table] Plugin threw in transformTableContext:', error);
594
+ devError('Table', 'Plugin threw in transformTableContext:', error);
597
595
  }
598
596
  }
599
597
  }
@@ -28,6 +28,7 @@ export {useTableColumnResize} from './plugins/columnResize';
28
28
  export {useTableStickyColumns} from './plugins/stickyColumns';
29
29
  export {useTableGroupedRows} from './plugins/groupedRows';
30
30
  export {useTableRowIndex} from './plugins/rowIndex';
31
+ export {useTableRowStatus} from './plugins/rowStatus';
31
32
  export {
32
33
  useTableRowExpansion,
33
34
  useTableRowExpansionState,
@@ -110,6 +111,10 @@ export type {
110
111
  UseTableGroupedRowsConfig,
111
112
  UseTableGroupedRowsResult,
112
113
  } from './plugins/groupedRows';
114
+ export type {
115
+ UseTableRowStatusConfig,
116
+ TableRowStatus,
117
+ } from './plugins/rowStatus';
113
118
  export type {
114
119
  TableTreeRowMeta,
115
120
  UseTableTreeDataConfig,
@@ -325,8 +325,12 @@ export function useTableGroupedRows<T extends Record<string, unknown>>(
325
325
  }}
326
326
  aria-label={
327
327
  collapsed
328
- ? t('@astryx.tableGroupedRows.expandGroup', {groupKey: header.groupKey})
329
- : t('@astryx.tableGroupedRows.collapseGroup', {groupKey: header.groupKey})
328
+ ? t('@astryx.tableGroupedRows.expandGroup', {
329
+ groupKey: header.groupKey,
330
+ })
331
+ : t('@astryx.tableGroupedRows.collapseGroup', {
332
+ groupKey: header.groupKey,
333
+ })
330
334
  }
331
335
  aria-expanded={!collapsed}>
332
336
  <span
@@ -0,0 +1,7 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ export {useTableRowStatus} from './useTableRowStatus';
4
+ export type {
5
+ UseTableRowStatusConfig,
6
+ TableRowStatus,
7
+ } from './useTableRowStatus';
@@ -0,0 +1,138 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import {describe, it, expect} from 'vitest';
4
+ import {render, screen, within} from '@testing-library/react';
5
+ import {Table} from '../../Table';
6
+ import type {TableColumn} from '../../types';
7
+ import {useTableRowStatus, type TableRowStatus} from './useTableRowStatus';
8
+
9
+ interface Row extends Record<string, unknown> {
10
+ id: string;
11
+ name: string;
12
+ state: 'error' | 'warning' | 'ok' | 'done';
13
+ }
14
+
15
+ const data: Row[] = [
16
+ {id: 'a', name: 'Alice', state: 'error'},
17
+ {id: 'b', name: 'Bob', state: 'ok'},
18
+ {id: 'c', name: 'Carol', state: 'warning'},
19
+ ];
20
+
21
+ const columns: TableColumn<Row>[] = [{key: 'name', header: 'Name'}];
22
+
23
+ function getStatus(item: Row): TableRowStatus | null {
24
+ if (item.state === 'error') {
25
+ return {color: 'red', label: 'Error'};
26
+ }
27
+ if (item.state === 'warning') {
28
+ return {color: 'orange', label: 'Warning'};
29
+ }
30
+ return null;
31
+ }
32
+
33
+ function Harness({
34
+ rows = data,
35
+ statusFn = getStatus,
36
+ }: {
37
+ rows?: Row[];
38
+ statusFn?: (item: Row) => TableRowStatus | null;
39
+ }) {
40
+ const rowStatus = useTableRowStatus<Row>({getStatus: statusFn});
41
+ return (
42
+ <Table data={rows} columns={columns} idKey="id" plugins={{rowStatus}} />
43
+ );
44
+ }
45
+
46
+ describe('useTableRowStatus', () => {
47
+ it('prepends a narrow status column with an empty header', () => {
48
+ render(<Harness />);
49
+ const headers = screen.getAllByRole('columnheader');
50
+ // Status column is first; its header is empty.
51
+ expect(headers[0]).toHaveAttribute('data-column-key', '__rowStatus');
52
+ expect(headers[0].textContent).toBe('');
53
+ });
54
+
55
+ it('renders a labeled dot for rows with a status', () => {
56
+ render(<Harness />);
57
+ expect(screen.getByRole('img', {name: 'Error'})).toBeInTheDocument();
58
+ expect(screen.getByRole('img', {name: 'Warning'})).toBeInTheDocument();
59
+ });
60
+
61
+ it('renders a dot (not an icon) by default', () => {
62
+ render(<Harness />);
63
+ // Default (no icon) renders a plain colored dot: no svg in the indicator.
64
+ const dot = screen.getByRole('img', {name: 'Error'});
65
+ expect(dot.querySelector('svg')).toBeNull();
66
+ });
67
+
68
+ it('renders no indicator for rows returning null', () => {
69
+ render(<Harness />);
70
+ const rows = screen.getAllByRole('row');
71
+ // rows[2] is Bob (state ok): no status indicator in his status cell.
72
+ const bob = rows[2];
73
+ expect(within(bob).getByText('Bob')).toBeInTheDocument();
74
+ expect(within(bob).queryByRole('img')).not.toBeInTheDocument();
75
+ });
76
+
77
+ it('maps a semantic color name to its icon color token', () => {
78
+ render(<Harness />);
79
+ const errorDot = screen.getByRole('img', {name: 'Error'});
80
+ // 'red' resolves to var(--color-icon-red); StyleX emits it on the inline
81
+ // style of the inner dot element.
82
+ const dot = errorDot.querySelector('span');
83
+ expect(dot?.getAttribute('style')).toContain('--color-icon-red');
84
+ });
85
+
86
+ it('passes through a raw CSS color as an escape hatch', () => {
87
+ render(
88
+ <Harness
89
+ statusFn={item =>
90
+ item.state === 'error' ? {color: 'rgb(1, 2, 3)', label: 'Raw'} : null
91
+ }
92
+ />,
93
+ );
94
+ const indicator = screen.getByRole('img', {name: 'Raw'});
95
+ const dot = indicator.querySelector('span');
96
+ expect(dot?.getAttribute('style')).toContain('rgb(1, 2, 3)');
97
+ });
98
+
99
+ it('renders an icon as the status signifier when icon is provided', () => {
100
+ render(
101
+ <Harness
102
+ statusFn={item =>
103
+ item.state === 'error'
104
+ ? {color: 'red', icon: 'error', label: 'Error'}
105
+ : null
106
+ }
107
+ />,
108
+ );
109
+ // Icon-mode still exposes the accessible label via role=img.
110
+ const indicator = screen.getByRole('img', {name: 'Error'});
111
+ expect(indicator).toBeInTheDocument();
112
+ // An SVG icon is rendered inside the indicator (dot mode has no svg).
113
+ expect(indicator.querySelector('svg')).not.toBeNull();
114
+ });
115
+
116
+ it('exposes the required label as the accessible name in dot mode', () => {
117
+ render(
118
+ <Harness
119
+ statusFn={item =>
120
+ item.state === 'error' ? {color: 'red', label: 'Error'} : null
121
+ }
122
+ />,
123
+ );
124
+ // Label is required, so every dot is announced via role=img with its name.
125
+ const indicator = screen.getByRole('img', {name: 'Error'});
126
+ expect(indicator).toBeInTheDocument();
127
+ // Dot mode renders no svg (that is icon mode).
128
+ expect(indicator.querySelector('svg')).toBeNull();
129
+ expect(screen.getByText('Alice')).toBeInTheDocument();
130
+ });
131
+
132
+ it('renders the status header with empty data and no indicators', () => {
133
+ render(<Harness rows={[]} />);
134
+ const headers = screen.getAllByRole('columnheader');
135
+ expect(headers[0]).toHaveAttribute('data-column-key', '__rowStatus');
136
+ expect(screen.queryByRole('img')).not.toBeInTheDocument();
137
+ });
138
+ });