@astryxdesign/core 0.1.9-canary.d2d84f8 → 0.1.9-canary.e00a241

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 (414) hide show
  1. package/dist/Avatar/Avatar.d.ts +6 -1
  2. package/dist/Avatar/Avatar.d.ts.map +1 -1
  3. package/dist/Avatar/Avatar.js +46 -12
  4. package/dist/Avatar/AvatarStatusDot.d.ts +6 -4
  5. package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
  6. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  7. package/dist/Breadcrumbs/BreadcrumbItem.js +11 -4
  8. package/dist/Breadcrumbs/index.d.ts +1 -0
  9. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  10. package/dist/Breadcrumbs/index.js +2 -1
  11. package/dist/Calendar/Calendar.d.ts.map +1 -1
  12. package/dist/Calendar/Calendar.js +50 -9
  13. package/dist/Calendar/dayCellUtils.d.ts +4 -4
  14. package/dist/Calendar/dayCellUtils.js +2 -2
  15. package/dist/Calendar/styles.d.ts +30 -31
  16. package/dist/Calendar/styles.d.ts.map +1 -1
  17. package/dist/Calendar/styles.js +30 -31
  18. package/dist/Card/Card.js +1 -1
  19. package/dist/Carousel/Carousel.d.ts.map +1 -1
  20. package/dist/Carousel/Carousel.js +22 -12
  21. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  22. package/dist/Chat/ChatDictationButton.js +3 -1
  23. package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
  24. package/dist/Chat/ChatMessageBubble.js +8 -8
  25. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  26. package/dist/Chat/ChatToolCalls.js +10 -3
  27. package/dist/CheckboxInput/CheckboxInput.js +4 -4
  28. package/dist/Citation/Citation.d.ts +22 -1
  29. package/dist/Citation/Citation.d.ts.map +1 -1
  30. package/dist/Citation/Citation.js +20 -6
  31. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  32. package/dist/CodeBlock/CodeBlock.js +13 -7
  33. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  34. package/dist/Collapsible/Collapsible.js +3 -1
  35. package/dist/CommandPalette/CommandPalette.d.ts +1 -1
  36. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  37. package/dist/CommandPalette/CommandPalette.js +42 -3
  38. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  39. package/dist/ContextMenu/ContextMenu.js +14 -5
  40. package/dist/ContextMenu/index.d.ts +1 -0
  41. package/dist/ContextMenu/index.d.ts.map +1 -1
  42. package/dist/ContextMenu/index.js +3 -1
  43. package/dist/DateInput/DateInput.d.ts +1 -0
  44. package/dist/DateInput/DateInput.d.ts.map +1 -1
  45. package/dist/DateInput/DateInput.js +31 -19
  46. package/dist/DateRangeInput/DateRangeInput.d.ts +1 -0
  47. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  48. package/dist/DateRangeInput/DateRangeInput.js +20 -19
  49. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  50. package/dist/DateTimeInput/DateTimeInput.js +23 -18
  51. package/dist/Divider/Divider.d.ts +2 -2
  52. package/dist/Divider/Divider.d.ts.map +1 -1
  53. package/dist/Divider/Divider.js +13 -0
  54. package/dist/DropdownMenu/DropdownMenu.d.ts +6 -0
  55. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  56. package/dist/DropdownMenu/DropdownMenu.js +22 -5
  57. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  58. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +4 -1
  59. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  60. package/dist/DropdownMenu/DropdownMenuItem.js +4 -1
  61. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  62. package/dist/DropdownMenu/DropdownMenuRadioItem.js +4 -1
  63. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +104 -0
  64. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -0
  65. package/dist/DropdownMenu/DropdownMenuSubMenu.js +427 -0
  66. package/dist/DropdownMenu/index.d.ts +10 -0
  67. package/dist/DropdownMenu/index.d.ts.map +1 -1
  68. package/dist/DropdownMenu/index.js +13 -0
  69. package/dist/DropdownMenu/menuItemHover.d.ts +23 -0
  70. package/dist/DropdownMenu/menuItemHover.d.ts.map +1 -0
  71. package/dist/DropdownMenu/menuItemHover.js +33 -0
  72. package/dist/DropdownMenu/menuItemRoles.d.ts +8 -0
  73. package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -1
  74. package/dist/DropdownMenu/menuItemRoles.js +10 -1
  75. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  76. package/dist/DropdownMenu/renderDropdownItems.js +21 -6
  77. package/dist/Field/Field.d.ts +3 -1
  78. package/dist/Field/Field.d.ts.map +1 -1
  79. package/dist/Field/Field.js +4 -1
  80. package/dist/Field/inputStyles.stylex.d.ts +1 -0
  81. package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
  82. package/dist/Field/inputStyles.stylex.js +1 -0
  83. package/dist/FieldStatus/FieldStatus.d.ts +4 -1
  84. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  85. package/dist/FieldStatus/FieldStatus.js +4 -2
  86. package/dist/FileInput/FileInput.d.ts +1 -0
  87. package/dist/FileInput/FileInput.d.ts.map +1 -1
  88. package/dist/FileInput/FileInput.js +91 -65
  89. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  90. package/dist/HoverCard/useHoverCard.js +10 -4
  91. package/dist/Icon/Icon.d.ts +15 -1
  92. package/dist/Icon/Icon.d.ts.map +1 -1
  93. package/dist/Icon/Icon.js +19 -3
  94. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  95. package/dist/Lightbox/Lightbox.js +15 -9
  96. package/dist/List/List.d.ts.map +1 -1
  97. package/dist/List/List.js +6 -3
  98. package/dist/Markdown/Markdown.d.ts.map +1 -1
  99. package/dist/Markdown/Markdown.js +7 -0
  100. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  101. package/dist/MultiSelector/MultiSelector.js +65 -40
  102. package/dist/NumberInput/NumberInput.d.ts +1 -0
  103. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  104. package/dist/NumberInput/NumberInput.js +20 -17
  105. package/dist/Outline/Outline.js +2 -2
  106. package/dist/RadioList/RadioList.d.ts +2 -2
  107. package/dist/RadioList/RadioListItem.js +4 -4
  108. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  109. package/dist/Resizable/ResizeHandle.js +49 -19
  110. package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
  111. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  112. package/dist/SegmentedControl/SegmentedControlItem.js +2 -1
  113. package/dist/Selector/Selector.d.ts.map +1 -1
  114. package/dist/Selector/Selector.js +58 -16
  115. package/dist/SideNav/SideNav.d.ts.map +1 -1
  116. package/dist/SideNav/SideNav.js +5 -5
  117. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  118. package/dist/SideNav/SideNavCollapseButton.js +13 -10
  119. package/dist/SideNav/SideNavItem.d.ts +1 -1
  120. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  121. package/dist/SideNav/SideNavItem.js +12 -10
  122. package/dist/Slider/Slider.d.ts.map +1 -1
  123. package/dist/Slider/Slider.js +87 -34
  124. package/dist/Spinner/Spinner.d.ts +3 -2
  125. package/dist/Spinner/Spinner.d.ts.map +1 -1
  126. package/dist/Spinner/Spinner.js +11 -2
  127. package/dist/Switch/Switch.d.ts +8 -1
  128. package/dist/Switch/Switch.d.ts.map +1 -1
  129. package/dist/Switch/Switch.js +160 -91
  130. package/dist/TabList/TabList.d.ts +2 -11
  131. package/dist/TabList/TabList.d.ts.map +1 -1
  132. package/dist/TabList/TabList.js +5 -9
  133. package/dist/TabList/TabListContext.d.ts +0 -5
  134. package/dist/TabList/TabListContext.d.ts.map +1 -1
  135. package/dist/TabList/TabListContext.js +0 -5
  136. package/dist/Table/BaseTable.d.ts.map +1 -1
  137. package/dist/Table/BaseTable.js +30 -4
  138. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  139. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +15 -11
  140. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  141. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +36 -26
  142. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  143. package/dist/Table/plugins/selection/useTableSelection.js +3 -0
  144. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  145. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +14 -6
  146. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  147. package/dist/Table/plugins/tree/useTableTreeData.js +29 -23
  148. package/dist/Table/types.d.ts +32 -0
  149. package/dist/Table/types.d.ts.map +1 -1
  150. package/dist/TextArea/TextArea.d.ts +1 -0
  151. package/dist/TextArea/TextArea.d.ts.map +1 -1
  152. package/dist/TextArea/TextArea.js +16 -19
  153. package/dist/TextInput/TextInput.d.ts +1 -0
  154. package/dist/TextInput/TextInput.d.ts.map +1 -1
  155. package/dist/TextInput/TextInput.js +18 -17
  156. package/dist/TimeInput/TimeInput.d.ts +1 -0
  157. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  158. package/dist/TimeInput/TimeInput.js +15 -17
  159. package/dist/Timestamp/Timestamp.d.ts +31 -3
  160. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  161. package/dist/Timestamp/Timestamp.js +47 -76
  162. package/dist/Timestamp/formatInstant.d.ts +41 -0
  163. package/dist/Timestamp/formatInstant.d.ts.map +1 -0
  164. package/dist/Timestamp/formatInstant.js +177 -0
  165. package/dist/Timestamp/index.d.ts +1 -0
  166. package/dist/Timestamp/index.d.ts.map +1 -1
  167. package/dist/Timestamp/tooltipEntries.d.ts +56 -0
  168. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -0
  169. package/dist/Timestamp/tooltipEntries.js +163 -0
  170. package/dist/Token/Token.d.ts +30 -3
  171. package/dist/Token/Token.d.ts.map +1 -1
  172. package/dist/Token/Token.js +17 -2
  173. package/dist/Token/index.d.ts +1 -1
  174. package/dist/Token/index.d.ts.map +1 -1
  175. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  176. package/dist/Tokenizer/Tokenizer.js +2 -2
  177. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  178. package/dist/Tooltip/useTooltip.js +17 -1
  179. package/dist/TopNav/TopNavItem.d.ts +3 -1
  180. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  181. package/dist/TopNav/TopNavItem.js +30 -3
  182. package/dist/TreeList/TreeList.d.ts.map +1 -1
  183. package/dist/TreeList/TreeList.js +1 -0
  184. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  185. package/dist/TreeList/TreeListBranches.js +8 -6
  186. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  187. package/dist/TreeList/TreeListItem.js +32 -14
  188. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  189. package/dist/Typeahead/BaseTypeahead.js +19 -10
  190. package/dist/Typeahead/Typeahead.js +1 -1
  191. package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
  192. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  193. package/dist/Typeahead/TypeaheadItem.js +20 -10
  194. package/dist/astryx.css +38 -14
  195. package/dist/astryx.umd.js +52 -52
  196. package/dist/astryx.umd.js.map +4 -4
  197. package/dist/hooks/index.d.ts +2 -0
  198. package/dist/hooks/index.d.ts.map +1 -1
  199. package/dist/hooks/index.js +1 -0
  200. package/dist/hooks/isRtlElement.d.ts +18 -0
  201. package/dist/hooks/isRtlElement.d.ts.map +1 -0
  202. package/dist/hooks/isRtlElement.js +27 -0
  203. package/dist/hooks/useAnnounce.d.ts +3 -0
  204. package/dist/hooks/useAnnounce.d.ts.map +1 -1
  205. package/dist/hooks/useAnnounce.js +39 -1
  206. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  207. package/dist/hooks/useFocusTrap.js +52 -11
  208. package/dist/hooks/useGridFocus.d.ts +6 -1
  209. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  210. package/dist/hooks/useGridFocus.js +12 -9
  211. package/dist/hooks/useHotkeys.d.ts +9 -0
  212. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  213. package/dist/hooks/useHotkeys.js +9 -0
  214. package/dist/hooks/useInputStatusIcon.d.ts +38 -0
  215. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -0
  216. package/dist/hooks/useInputStatusIcon.js +158 -0
  217. package/dist/hooks/useListFocus.d.ts +39 -1
  218. package/dist/hooks/useListFocus.d.ts.map +1 -1
  219. package/dist/hooks/useListFocus.js +54 -9
  220. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  221. package/dist/theme/derivedVarRegistry.js +13 -0
  222. package/dist/utils/index.d.ts +1 -0
  223. package/dist/utils/index.d.ts.map +1 -1
  224. package/dist/utils/index.js +2 -1
  225. package/dist/utils/plainDate.d.ts +17 -0
  226. package/dist/utils/plainDate.d.ts.map +1 -1
  227. package/dist/utils/plainDate.js +23 -4
  228. package/dist/utils/rtlStyles.d.ts +24 -0
  229. package/dist/utils/rtlStyles.d.ts.map +1 -0
  230. package/dist/utils/rtlStyles.js +27 -0
  231. package/locales/en.json +76 -0
  232. package/locales/pseudo.json +57 -0
  233. package/package.json +6 -1
  234. package/src/Avatar/Avatar.doc.mjs +18 -4
  235. package/src/Avatar/Avatar.test.tsx +106 -0
  236. package/src/Avatar/Avatar.tsx +62 -10
  237. package/src/Avatar/AvatarStatusDot.doc.mjs +4 -4
  238. package/src/Avatar/AvatarStatusDot.tsx +6 -4
  239. package/src/AvatarGroup/AvatarGroup.test.tsx +16 -1
  240. package/src/Breadcrumbs/BreadcrumbItem.tsx +10 -10
  241. package/src/Breadcrumbs/Breadcrumbs.test.tsx +51 -0
  242. package/src/Breadcrumbs/index.ts +4 -0
  243. package/src/Button/Button.doc.mjs +0 -4
  244. package/src/Calendar/Calendar.test.tsx +170 -20
  245. package/src/Calendar/Calendar.tsx +73 -16
  246. package/src/Calendar/dayCellUtils.test.ts +18 -18
  247. package/src/Calendar/dayCellUtils.ts +2 -2
  248. package/src/Calendar/styles.ts +33 -39
  249. package/src/Card/Card.tsx +1 -1
  250. package/src/Carousel/Carousel.tsx +25 -6
  251. package/src/Chat/ChatComposer.doc.mjs +2 -2
  252. package/src/Chat/ChatComposerInput.doc.mjs +1 -1
  253. package/src/Chat/ChatDictationButton.tsx +7 -1
  254. package/src/Chat/ChatMessageBubble.tsx +15 -10
  255. package/src/Chat/ChatSystemMessage.test.tsx +5 -0
  256. package/src/Chat/ChatToolCalls.tsx +9 -2
  257. package/src/CheckboxInput/CheckboxInput.doc.mjs +6 -0
  258. package/src/CheckboxInput/CheckboxInput.tsx +4 -4
  259. package/src/CheckboxList/CheckboxList.doc.mjs +6 -0
  260. package/src/Citation/Citation.doc.mjs +4 -3
  261. package/src/Citation/Citation.test.tsx +97 -0
  262. package/src/Citation/Citation.tsx +43 -11
  263. package/src/CodeBlock/CodeBlock.tsx +35 -8
  264. package/src/Collapsible/Collapsible.doc.mjs +1 -0
  265. package/src/Collapsible/Collapsible.test.tsx +7 -0
  266. package/src/Collapsible/Collapsible.tsx +16 -6
  267. package/src/CommandPalette/CommandPalette.test.tsx +95 -1
  268. package/src/CommandPalette/CommandPalette.tsx +41 -2
  269. package/src/ContextMenu/ContextMenu.test.tsx +42 -0
  270. package/src/ContextMenu/ContextMenu.tsx +13 -11
  271. package/src/ContextMenu/index.ts +5 -0
  272. package/src/DateInput/DateInput.doc.mjs +15 -5
  273. package/src/DateInput/DateInput.test.tsx +210 -3
  274. package/src/DateInput/DateInput.tsx +41 -29
  275. package/src/DateRangeInput/DateRangeInput.doc.mjs +11 -3
  276. package/src/DateRangeInput/DateRangeInput.test.tsx +120 -3
  277. package/src/DateRangeInput/DateRangeInput.tsx +29 -27
  278. package/src/DateTimeInput/DateTimeInput.doc.mjs +22 -1
  279. package/src/DateTimeInput/DateTimeInput.test.tsx +112 -0
  280. package/src/DateTimeInput/DateTimeInput.tsx +44 -39
  281. package/src/Divider/Divider.test.tsx +32 -8
  282. package/src/Divider/Divider.tsx +14 -1
  283. package/src/DropdownMenu/DropdownMenu.test.tsx +60 -0
  284. package/src/DropdownMenu/DropdownMenu.tsx +31 -12
  285. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +8 -2
  286. package/src/DropdownMenu/DropdownMenuItem.tsx +8 -2
  287. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +8 -2
  288. package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +88 -0
  289. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +635 -0
  290. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +553 -0
  291. package/src/DropdownMenu/index.ts +16 -0
  292. package/src/DropdownMenu/menuItemHover.ts +39 -0
  293. package/src/DropdownMenu/menuItemRoles.ts +9 -0
  294. package/src/DropdownMenu/renderDropdownItems.tsx +29 -15
  295. package/src/Field/Field.tsx +14 -9
  296. package/src/Field/inputStyles.stylex.ts +2 -0
  297. package/src/FieldStatus/FieldStatus.tsx +10 -4
  298. package/src/FileInput/FileInput.doc.mjs +13 -7
  299. package/src/FileInput/FileInput.test.tsx +84 -16
  300. package/src/FileInput/FileInput.tsx +96 -79
  301. package/src/HoverCard/useHoverCard.tsx +15 -3
  302. package/src/Icon/Icon.doc.mjs +13 -0
  303. package/src/Icon/Icon.test.tsx +89 -0
  304. package/src/Icon/Icon.tsx +51 -3
  305. package/src/Lightbox/Lightbox.tsx +11 -3
  306. package/src/List/List.test.tsx +17 -29
  307. package/src/List/List.tsx +6 -1
  308. package/src/Markdown/Markdown.test.tsx +21 -0
  309. package/src/Markdown/Markdown.tsx +4 -0
  310. package/src/MultiSelector/MultiSelector.doc.mjs +11 -0
  311. package/src/MultiSelector/MultiSelector.test.tsx +306 -3
  312. package/src/MultiSelector/MultiSelector.tsx +88 -57
  313. package/src/NumberInput/NumberInput.doc.mjs +11 -5
  314. package/src/NumberInput/NumberInput.test.tsx +48 -0
  315. package/src/NumberInput/NumberInput.tsx +25 -26
  316. package/src/Outline/Outline.test.tsx +12 -12
  317. package/src/Outline/Outline.tsx +2 -2
  318. package/src/RadioList/RadioList.doc.mjs +6 -0
  319. package/src/RadioList/RadioList.tsx +2 -2
  320. package/src/RadioList/RadioListItem.tsx +4 -4
  321. package/src/Resizable/ResizeHandle.test.tsx +94 -0
  322. package/src/Resizable/ResizeHandle.tsx +70 -20
  323. package/src/SegmentedControl/SegmentedControlItem.tsx +2 -0
  324. package/src/Selector/Selector.doc.mjs +8 -0
  325. package/src/Selector/Selector.test.tsx +328 -3
  326. package/src/Selector/Selector.tsx +70 -19
  327. package/src/SideNav/SideNav.tsx +1 -8
  328. package/src/SideNav/SideNavCollapseButton.tsx +9 -7
  329. package/src/SideNav/SideNavItem.tsx +5 -3
  330. package/src/Slider/Slider.doc.mjs +6 -0
  331. package/src/Slider/Slider.test.tsx +240 -12
  332. package/src/Slider/Slider.tsx +96 -23
  333. package/src/Spinner/Spinner.test.tsx +10 -8
  334. package/src/Spinner/Spinner.tsx +15 -5
  335. package/src/Switch/Switch.doc.mjs +142 -42
  336. package/src/Switch/Switch.test.tsx +22 -0
  337. package/src/Switch/Switch.tsx +90 -33
  338. package/src/TabList/TabList.doc.mjs +0 -6
  339. package/src/TabList/TabList.test.tsx +3 -15
  340. package/src/TabList/TabList.tsx +6 -17
  341. package/src/TabList/TabListContext.ts +0 -6
  342. package/src/Table/BaseTable.tsx +34 -2
  343. package/src/Table/Table.doc.mjs +11 -0
  344. package/src/Table/Table.test.tsx +85 -0
  345. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +9 -6
  346. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +24 -17
  347. package/src/Table/plugins/selection/useTableSelection.test.tsx +61 -3
  348. package/src/Table/plugins/selection/useTableSelection.tsx +3 -0
  349. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +37 -8
  350. package/src/Table/plugins/tree/useTableTreeData.tsx +17 -13
  351. package/src/Table/types.ts +33 -0
  352. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  353. package/src/TextArea/TextArea.doc.mjs +11 -5
  354. package/src/TextArea/TextArea.tsx +17 -26
  355. package/src/TextInput/TextInput.doc.mjs +11 -5
  356. package/src/TextInput/TextInput.test.tsx +130 -3
  357. package/src/TextInput/TextInput.tsx +18 -25
  358. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  359. package/src/TimeInput/TimeInput.doc.mjs +11 -5
  360. package/src/TimeInput/TimeInput.tsx +14 -25
  361. package/src/Timestamp/Timestamp.doc.mjs +32 -6
  362. package/src/Timestamp/Timestamp.test.tsx +371 -0
  363. package/src/Timestamp/Timestamp.tsx +96 -93
  364. package/src/Timestamp/formatInstant.ts +218 -0
  365. package/src/Timestamp/index.ts +4 -0
  366. package/src/Timestamp/tooltipEntries.test.ts +552 -0
  367. package/src/Timestamp/tooltipEntries.ts +210 -0
  368. package/src/Token/Token.test.tsx +21 -15
  369. package/src/Token/Token.tsx +31 -14
  370. package/src/Token/index.ts +1 -1
  371. package/src/Tokenizer/Tokenizer.doc.mjs +8 -1
  372. package/src/Tokenizer/Tokenizer.tsx +4 -2
  373. package/src/Tooltip/Tooltip.test.tsx +46 -0
  374. package/src/Tooltip/useTooltip.tsx +20 -0
  375. package/src/TopNav/TopNav.test.tsx +93 -2
  376. package/src/TopNav/TopNavItem.doc.mjs +3 -3
  377. package/src/TopNav/TopNavItem.tsx +35 -6
  378. package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +1 -1
  379. package/src/TreeList/TreeList.doc.mjs +6 -0
  380. package/src/TreeList/TreeList.test.tsx +50 -10
  381. package/src/TreeList/TreeList.tsx +5 -0
  382. package/src/TreeList/TreeListBranches.tsx +8 -6
  383. package/src/TreeList/TreeListItem.tsx +33 -11
  384. package/src/Typeahead/BaseTypeahead.tsx +29 -14
  385. package/src/Typeahead/Typeahead.doc.mjs +6 -0
  386. package/src/Typeahead/Typeahead.test.tsx +27 -0
  387. package/src/Typeahead/Typeahead.tsx +1 -1
  388. package/src/Typeahead/TypeaheadItem.tsx +2 -1
  389. package/src/__tests__/inputWidthContract.test.tsx +62 -0
  390. package/src/doc-mjs.d.ts +19 -0
  391. package/src/hooks/index.ts +6 -0
  392. package/src/hooks/isRtlElement.ts +27 -0
  393. package/src/hooks/useAnnounce.test.tsx +75 -2
  394. package/src/hooks/useAnnounce.ts +47 -1
  395. package/src/hooks/useFocusTrap.test.tsx +104 -0
  396. package/src/hooks/useFocusTrap.ts +61 -12
  397. package/src/hooks/useGridFocus.doc.mjs +3 -3
  398. package/src/hooks/useGridFocus.test.tsx +41 -0
  399. package/src/hooks/useGridFocus.ts +21 -10
  400. package/src/hooks/useHotkeys.ts +9 -0
  401. package/src/hooks/useInputStatusIcon.test.tsx +162 -0
  402. package/src/hooks/useInputStatusIcon.tsx +224 -0
  403. package/src/hooks/useListFocus.doc.mjs +25 -3
  404. package/src/hooks/useListFocus.test.tsx +153 -1
  405. package/src/hooks/useListFocus.ts +102 -9
  406. package/src/reset.css +0 -26
  407. package/src/reset.test.ts +60 -0
  408. package/src/theme/derivedVarRegistry.test.ts +1 -0
  409. package/src/theme/derivedVarRegistry.ts +6 -0
  410. package/src/theme/generateThemeRules.test.ts +42 -0
  411. package/src/utils/index.ts +2 -0
  412. package/src/utils/plainDate.test.ts +24 -6
  413. package/src/utils/plainDate.ts +18 -4
  414. package/src/utils/rtlStyles.ts +27 -0
@@ -68,6 +68,28 @@ describe('Switch', () => {
68
68
  expect(screen.getByRole('switch')).toBeChecked();
69
69
  });
70
70
 
71
+ it('renders with custom size prop (sm / md)', () => {
72
+ const {rerender} = render(
73
+ <Switch
74
+ label="Small switch"
75
+ value={false}
76
+ size="sm"
77
+ onChange={() => {}}
78
+ />,
79
+ );
80
+ expect(screen.getByRole('switch')).toBeInTheDocument();
81
+
82
+ rerender(
83
+ <Switch
84
+ label="Medium switch"
85
+ value={false}
86
+ size="md"
87
+ onChange={() => {}}
88
+ />,
89
+ );
90
+ expect(screen.getByRole('switch')).toBeInTheDocument();
91
+ });
92
+
71
93
  it('calls onChange with new checked state when clicked', async () => {
72
94
  const user = userEvent.setup();
73
95
  const handleChange = vi.fn();
@@ -47,17 +47,75 @@ import type {SizeValue} from '../utils/types';
47
47
  import {themeProps} from '../utils/themeProps';
48
48
  import {VisuallyHidden} from '../VisuallyHidden';
49
49
 
50
- // Fixed dimensions: 40px width, 24px height, 16px thumb (off), 20px thumb (on)
51
- const SWITCH_WIDTH = 40;
52
- const SWITCH_HEIGHT = 24;
53
- const THUMB_SIZE_OFF = 16;
54
- const THUMB_SIZE_ON = 20;
55
- const TRACK_PADDING = 4;
56
- // Padding between thumb right edge and track inner edge when on
57
- const ON_RIGHT_PADDING = 2;
58
- // Travel distance for on state: positions thumb with ON_RIGHT_PADDING from right edge
59
- const THUMB_TRAVEL_ON =
60
- SWITCH_WIDTH - TRACK_PADDING - THUMB_SIZE_ON - ON_RIGHT_PADDING;
50
+ const wrapperSizeStyles = stylex.create({
51
+ sm: {
52
+ width: 32,
53
+ height: 20,
54
+ },
55
+ md: {
56
+ width: 40,
57
+ height: 24,
58
+ },
59
+ });
60
+
61
+ const inputSizeStyles = stylex.create({
62
+ sm: {
63
+ width: 32,
64
+ height: 20,
65
+ },
66
+ md: {
67
+ width: 40,
68
+ height: 24,
69
+ },
70
+ });
71
+
72
+ const trackSizeStyles = stylex.create({
73
+ sm: {
74
+ width: 32,
75
+ height: 20,
76
+ padding: 2,
77
+ },
78
+ md: {
79
+ width: 40,
80
+ height: 24,
81
+ padding: 4,
82
+ },
83
+ });
84
+
85
+ const thumbOffSizeStyles = stylex.create({
86
+ sm: {
87
+ width: 14,
88
+ height: 14,
89
+ transform: 'translateX(0)',
90
+ },
91
+ md: {
92
+ width: 16,
93
+ height: 16,
94
+ transform: 'translateX(0)',
95
+ },
96
+ });
97
+
98
+ const thumbOnSizeStyles = stylex.create({
99
+ sm: {
100
+ width: 16,
101
+ height: 16,
102
+ transform: 'translateX(12px)',
103
+ },
104
+ md: {
105
+ width: 20,
106
+ height: 20,
107
+ transform: 'translateX(14px)',
108
+ },
109
+ });
110
+
111
+ const labelWrapperSizeStyles = stylex.create({
112
+ sm: {
113
+ minHeight: 20,
114
+ },
115
+ md: {
116
+ minHeight: 24,
117
+ },
118
+ });
61
119
 
62
120
  const styles = stylex.create({
63
121
  container: {
@@ -77,8 +135,6 @@ const styles = stylex.create({
77
135
  display: 'flex',
78
136
  alignItems: 'center',
79
137
  flexShrink: 0,
80
- width: SWITCH_WIDTH,
81
- height: SWITCH_HEIGHT,
82
138
  isolation: 'isolate',
83
139
  },
84
140
  input: {
@@ -88,8 +144,6 @@ const styles = stylex.create({
88
144
  opacity: 0,
89
145
  cursor: 'pointer',
90
146
  zIndex: 1,
91
- width: SWITCH_WIDTH,
92
- height: SWITCH_HEIGHT,
93
147
  },
94
148
  inputDisabled: {
95
149
  cursor: 'not-allowed',
@@ -100,9 +154,6 @@ const styles = stylex.create({
100
154
  track: {
101
155
  display: 'flex',
102
156
  alignItems: 'center',
103
- width: SWITCH_WIDTH,
104
- height: SWITCH_HEIGHT,
105
- padding: TRACK_PADDING,
106
157
  borderRadius: radiusVars['--radius-full'],
107
158
  transitionProperty: 'background-color',
108
159
  transitionDuration: {
@@ -159,22 +210,11 @@ const styles = stylex.create({
159
210
  },
160
211
  transitionTimingFunction: easeVars['--ease-standard'],
161
212
  },
162
- thumbOff: {
163
- width: THUMB_SIZE_OFF,
164
- height: THUMB_SIZE_OFF,
165
- transform: 'translateX(0)',
166
- },
167
- thumbOn: {
168
- width: THUMB_SIZE_ON,
169
- height: THUMB_SIZE_ON,
170
- transform: `translateX(${THUMB_TRAVEL_ON}px)`,
171
- },
172
213
  labelWrapper: {
173
214
  display: 'flex',
174
215
  flexDirection: 'column',
175
216
  gap: spacingVars['--spacing-0-5'],
176
217
  justifyContent: 'center',
177
- minHeight: SWITCH_HEIGHT,
178
218
  },
179
219
  description: {
180
220
  fontFamily: typographyVars['--font-family-body'],
@@ -313,6 +353,13 @@ export interface SwitchProps extends Omit<BaseProps, 'onChange'> {
313
353
  * When set with a message, displays a colored message box below the switch.
314
354
  */
315
355
  status?: InputStatus;
356
+ /**
357
+ * Size variant controlling track and thumb dimensions.
358
+ * - 'sm': 34x20px (matches sm checkbox/radio vertical rhythm)
359
+ * - 'md': 40x24px (default, matches md checkbox/radio vertical rhythm)
360
+ * @default 'md'
361
+ */
362
+ size?: 'sm' | 'md';
316
363
  }
317
364
 
318
365
  // Dynamic field width (number -> px, string used as-is).
@@ -358,6 +405,7 @@ export function Switch({
358
405
  labelPosition = 'end',
359
406
  labelSpacing = 'hug',
360
407
  status,
408
+ size = 'md',
361
409
  width,
362
410
  xstyle,
363
411
  className,
@@ -410,7 +458,7 @@ export function Switch({
410
458
  describedByParts.length > 0 ? describedByParts.join(' ') : undefined;
411
459
 
412
460
  const switchElement = (
413
- <div {...stylex.props(styles.switchWrapper)}>
461
+ <div {...stylex.props(styles.switchWrapper, wrapperSizeStyles[size])}>
414
462
  <input
415
463
  ref={mergeRefs(ref, disabledMessageTooltip.positionRef)}
416
464
  id={id}
@@ -447,6 +495,7 @@ export function Switch({
447
495
  aria-busy={isBusy || undefined}
448
496
  {...stylex.props(
449
497
  styles.input,
498
+ inputSizeStyles[size],
450
499
  isDisabled && styles.inputDisabled,
451
500
  isBusy && styles.inputBusy,
452
501
  )}
@@ -457,9 +506,11 @@ export function Switch({
457
506
  themeProps('switch', {
458
507
  checked: isOn ? 'checked' : null,
459
508
  disabled: isDisabled ? 'disabled' : null,
509
+ size,
460
510
  }),
461
511
  stylex.props(
462
512
  styles.track,
513
+ trackSizeStyles[size],
463
514
  isOn ? styles.trackOn : styles.trackOff,
464
515
  !isDisabled && styles.trackFocus,
465
516
  isDisabled && styles.trackDisabled,
@@ -468,8 +519,14 @@ export function Switch({
468
519
  )}>
469
520
  <div
470
521
  {...mergeProps(
471
- themeProps('switch-thumb', {checked: isOn ? 'checked' : null}),
472
- stylex.props(styles.thumb, isOn ? styles.thumbOn : styles.thumbOff),
522
+ themeProps('switch-thumb', {
523
+ checked: isOn ? 'checked' : null,
524
+ size,
525
+ }),
526
+ stylex.props(
527
+ styles.thumb,
528
+ isOn ? thumbOnSizeStyles[size] : thumbOffSizeStyles[size],
529
+ ),
473
530
  )}>
474
531
  {isBusy && <Spinner size="sm" />}
475
532
  </div>
@@ -479,7 +536,7 @@ export function Switch({
479
536
  );
480
537
 
481
538
  const labelElement = (
482
- <div {...stylex.props(styles.labelWrapper)}>
539
+ <div {...stylex.props(styles.labelWrapper, labelWrapperSizeStyles[size])}>
483
540
  <FieldLabel
484
541
  label={label}
485
542
  inputID={id}
@@ -55,12 +55,6 @@ export const docs = {
55
55
  description: 'Whether to show a bottom border divider under the tab list.',
56
56
  default: 'false',
57
57
  },
58
- {
59
- name: 'orientation',
60
- type: "'horizontal' | 'vertical'",
61
- description: "Orientation of the tab strip, controlling which arrow keys move focus between tabs and the reported aria-orientation. 'horizontal': ArrowLeft/ArrowRight. 'vertical': ArrowUp/ArrowDown. Both axes' arrows are accepted regardless.",
62
- default: "'horizontal'",
63
- },
64
58
  {
65
59
  name: 'children',
66
60
  type: 'ReactNode',
@@ -77,10 +77,9 @@ describe('TabList', () => {
77
77
 
78
78
  it('does not set aria-orientation on the nav (invalid for role navigation)', () => {
79
79
  // Regression: aria-orientation is not an allowed attribute on the
80
- // navigation role and produces an axe aria-allowed-attr violation. The
81
- // `orientation` prop must drive keyboard/hint behavior without emitting
82
- // this attribute, regardless of the value passed.
83
- const {rerender} = render(
80
+ // navigation role and produces an axe aria-allowed-attr violation.
81
+ // TabList deliberately never sets this attribute.
82
+ render(
84
83
  <TabList value="home" onChange={() => {}}>
85
84
  <Tab value="home" label="Home" />
86
85
  <Tab value="settings" label="Settings" />
@@ -90,17 +89,6 @@ describe('TabList', () => {
90
89
  expect(screen.getByRole('navigation')).not.toHaveAttribute(
91
90
  'aria-orientation',
92
91
  );
93
-
94
- rerender(
95
- <TabList value="home" onChange={() => {}} orientation="vertical">
96
- <Tab value="home" label="Home" />
97
- <Tab value="settings" label="Settings" />
98
- </TabList>,
99
- );
100
-
101
- expect(screen.getByRole('navigation')).not.toHaveAttribute(
102
- 'aria-orientation',
103
- );
104
92
  });
105
93
 
106
94
  it('ignores a consumer-supplied aria-orientation on the nav', () => {
@@ -22,7 +22,7 @@ import * as stylex from '@stylexjs/stylex';
22
22
  import {borderVars, colorVars, spacingVars} from '../theme/tokens.stylex';
23
23
  import type {BaseProps} from '../BaseProps';
24
24
  import {TabListContext} from './TabListContext';
25
- import type {TabListOrientation, TabListSize} from './TabListContext';
25
+ import type {TabListSize} from './TabListContext';
26
26
  import {useSize} from '../SizeContext/SizeContext';
27
27
  import {mergeProps, mergeRefs} from '../utils';
28
28
  import {useListFocus} from '../hooks/useListFocus';
@@ -66,15 +66,6 @@ export interface TabListProps extends Omit<BaseProps<HTMLElement>, 'onChange'> {
66
66
  * @default false
67
67
  */
68
68
  hasDivider?: boolean;
69
- /**
70
- * Orientation of the tab strip, controlling which arrow keys move
71
- * focus between tabs.
72
- * - `'horizontal'` (default): ArrowLeft / ArrowRight (ArrowUp / ArrowDown
73
- * also work per the WAI-ARIA APG).
74
- * - `'vertical'`: ArrowUp / ArrowDown (ArrowLeft / ArrowRight also work).
75
- * @default 'horizontal'
76
- */
77
- orientation?: TabListOrientation;
78
69
  /**
79
70
  * Tab and TabMenu children.
80
71
  */
@@ -130,7 +121,6 @@ export function TabList({
130
121
  size: sizeProp,
131
122
  layout = 'hug',
132
123
  hasDivider = false,
133
- orientation = 'horizontal',
134
124
  xstyle,
135
125
  className,
136
126
  style,
@@ -150,14 +140,13 @@ export function TabList({
150
140
  // Roving-tabindex keyboard navigation across the tab strip via the shared
151
141
  // hook. `orientation: 'both'` accepts both arrow axes per the WAI-ARIA APG
152
142
  // allowance for tab strips (ArrowRight/ArrowDown advance, ArrowLeft/ArrowUp
153
- // retreat) regardless of the component's `orientation` prop, which only
154
- // drives the keyboard hint badge (see useKeyboardHint below). We do not set
155
- // `aria-orientation` on the `<nav>`: that attribute is invalid on the
156
- // navigation role and triggers an axe `aria-allowed-attr` violation.
143
+ // retreat). We do not set `aria-orientation` on the `<nav>`: that attribute
144
+ // is invalid on the navigation role and triggers an axe `aria-allowed-attr`
145
+ // violation.
157
146
  //
158
147
  // `hasRovingTabIndex` makes the hook own the single tab stop: it stamps
159
148
  // tabindex 0/-1, repairs the stop on mount and as stops mount/unmount or
160
- // toggle disabled, and — via `handleFocus` on the nav — keeps the stop in
149
+ // toggle disabled, and -- via `handleFocus` on the nav -- keeps the stop in
161
150
  // sync after clicks or programmatic focus. Individual Tabs still render
162
151
  // `tabIndex={isSelected ? 0 : -1}` (see Tab.tsx) as the initial source of
163
152
  // truth; the hook's repair preserves an existing tab stop and only promotes
@@ -173,7 +162,7 @@ export function TabList({
173
162
  onKeyDown: onHintKeyDown,
174
163
  onFocus: onHintFocus,
175
164
  onBlur: onHintBlur,
176
- } = useKeyboardHint({orientation});
165
+ } = useKeyboardHint();
177
166
 
178
167
  const contextValue = useMemo(
179
168
  () => ({value, onChange, size, layout}),
@@ -26,12 +26,6 @@ export type TabListSize = 'sm' | 'md' | 'lg';
26
26
  */
27
27
  export type TabListLayout = 'hug' | 'fill';
28
28
 
29
- /**
30
- * Orientation of the tab strip. Controls which arrow keys move focus
31
- * between tabs and the reported `aria-orientation`.
32
- */
33
- export type TabListOrientation = 'horizontal' | 'vertical';
34
-
35
29
  /**
36
30
  * Context for communicating value/onChange/size/layout from TabList to children.
37
31
  */
@@ -134,6 +134,12 @@ interface TableRowProps<T extends Record<string, unknown>> {
134
134
  textOverflow: 'wrap' | 'truncate';
135
135
  RowComponent: React.ComponentType<TableRowComponentProps>;
136
136
  CellComponent: React.ComponentType<TableCellComponentProps>;
137
+ /**
138
+ * 1-based ARIA row index for this row's position in the full dataset.
139
+ * `undefined` when the table hasn't opted into ARIA row indexing, in which
140
+ * case no `aria-rowindex` is emitted (native table semantics).
141
+ */
142
+ ariaRowIndex?: number;
137
143
  }
138
144
 
139
145
  /**
@@ -150,6 +156,7 @@ function TableRowInner<T extends Record<string, unknown>>({
150
156
  textOverflow,
151
157
  RowComponent,
152
158
  CellComponent,
159
+ ariaRowIndex,
153
160
  }: TableRowProps<T>): ReactElement {
154
161
  // Build cells first
155
162
  const cells = columns.map((col, columnIndex) => {
@@ -211,12 +218,14 @@ function TableRowInner<T extends Record<string, unknown>>({
211
218
  );
212
219
  });
213
220
 
214
- // Apply plugin transforms for row (with pre-rendered children)
221
+ // Apply plugin transforms for row (with pre-rendered children).
222
+ // Seed `aria-rowindex` (when the table opts into ARIA row indexing) as a
223
+ // base htmlProp so plugins compose over it and can still override.
215
224
  const rowRenderProps = applyPlugins(
216
225
  plugins,
217
226
  p => p.transformBodyRow,
218
227
  {
219
- htmlProps: {},
228
+ htmlProps: ariaRowIndex == null ? {} : {'aria-rowindex': ariaRowIndex},
220
229
  xstyle: [],
221
230
  children: <>{cells}</>,
222
231
  } satisfies BodyRowRenderProps,
@@ -256,6 +265,9 @@ function areRowPropsEqual<T extends Record<string, unknown>>(
256
265
  if (prevProps.rowIndex !== nextProps.rowIndex) {
257
266
  return false;
258
267
  }
268
+ if (prevProps.ariaRowIndex !== nextProps.ariaRowIndex) {
269
+ return false;
270
+ }
259
271
 
260
272
  if (prevProps.columns !== nextProps.columns) {
261
273
  return false;
@@ -323,6 +335,8 @@ function BaseTableInner<T extends Record<string, unknown>>({
323
335
  textOverflow = 'wrap',
324
336
  scrollWrapper: ScrollWrapper,
325
337
  emptyState,
338
+ rowIndexStart,
339
+ rowCount,
326
340
  xstyle,
327
341
  className,
328
342
  style,
@@ -333,6 +347,18 @@ function BaseTableInner<T extends Record<string, unknown>>({
333
347
  // Use stable empty array when no plugins provided
334
348
  const plugins = pluginsProp ?? (EMPTY_PLUGINS as TablePlugin<T>[]);
335
349
 
350
+ // ARIA row indexing. The row ordinal is an accessibility concern that is
351
+ // independent of any visible index column: when the consumer opts in (by
352
+ // passing rowIndexStart or rowCount), body rows carry `aria-rowindex`
353
+ // reflecting their position in the full dataset, and the <table> carries
354
+ // `aria-rowcount`. `aria-rowindex` is 1-based and counts data rows from
355
+ // `rowIndexStart` (default 1); a windowed/paginated view passes the offset
356
+ // of its first visible row. `aria-rowcount` is `rowCount` when known, or
357
+ // `-1` (ARIA's "unknown count") for a windowed view with an unknown total.
358
+ const ariaRowIndexingEnabled = rowIndexStart != null || rowCount != null;
359
+ const firstRowAriaIndex = rowIndexStart ?? 1;
360
+ const ariaRowCount = ariaRowIndexingEnabled ? (rowCount ?? -1) : undefined;
361
+
336
362
  const RowComponent = TableRow as React.ComponentType<TableRowComponentProps>;
337
363
  const CellComponent =
338
364
  TableCell as React.ComponentType<TableCellComponentProps>;
@@ -486,6 +512,7 @@ function BaseTableInner<T extends Record<string, unknown>>({
486
512
  let tableElement: ReactNode = (
487
513
  <table
488
514
  ref={ref}
515
+ {...(ariaRowCount != null ? {'aria-rowcount': ariaRowCount} : null)}
489
516
  {...tableRenderProps.htmlProps}
490
517
  {...mergeProps(
491
518
  themeProps('base-table'),
@@ -530,6 +557,11 @@ function BaseTableInner<T extends Record<string, unknown>>({
530
557
  textOverflow={textOverflow}
531
558
  RowComponent={RowComponent}
532
559
  CellComponent={CellComponent}
560
+ ariaRowIndex={
561
+ ariaRowIndexingEnabled
562
+ ? firstRowAriaIndex + rowIndex
563
+ : undefined
564
+ }
533
565
  />
534
566
  );
535
567
  })
@@ -93,6 +93,17 @@ export const docs = {
93
93
  type: 'Record<string, TablePlugin<T>>',
94
94
  description: 'Named plugins that extend table behavior via the transform pipeline. Converted to an ordered array internally.',
95
95
  },
96
+ {
97
+ name: 'rowIndexStart',
98
+ type: 'number',
99
+ description: 'ARIA row index (1-based) for the first rendered body row. The row ordinal is an accessibility concern independent of any visible index column, so setting this (or rowCount) makes the table emit aria-rowindex on body rows and aria-rowcount on the table. For a paginated/windowed view, pass the offset of the first visible row (e.g. (page - 1) * pageSize + 1) so aria-rowindex reflects position in the full dataset. Data-driven mode only.',
100
+ default: '1',
101
+ },
102
+ {
103
+ name: 'rowCount',
104
+ type: 'number',
105
+ description: 'Total number of body rows across all pages/windows, used for aria-rowcount so assistive tech can announce "row X of Y" against the full dataset. When omitted but rowIndexStart is set (windowed view with an unknown total), aria-rowcount is set to -1 per the ARIA unknown-count convention. Data-driven mode only.',
106
+ },
96
107
  {
97
108
  name: 'children',
98
109
  type: 'ReactNode',
@@ -1451,3 +1451,88 @@ describe('emptyState', () => {
1451
1451
  });
1452
1452
  });
1453
1453
  });
1454
+
1455
+ describe('ARIA row indexing (#3939)', () => {
1456
+ const bodyRows = (container: HTMLElement): HTMLTableRowElement[] =>
1457
+ Array.from(container.querySelectorAll('tbody tr'));
1458
+
1459
+ it('emits no aria-rowindex/aria-rowcount by default', () => {
1460
+ const {container} = render(<Table data={users} columns={columns} />);
1461
+ expect(screen.getByRole('table')).not.toHaveAttribute('aria-rowcount');
1462
+ for (const row of bodyRows(container)) {
1463
+ expect(row).not.toHaveAttribute('aria-rowindex');
1464
+ }
1465
+ });
1466
+
1467
+ it('numbers rows from 1 when rowCount is provided', () => {
1468
+ const {container} = render(
1469
+ <Table data={users} columns={columns} rowCount={users.length} />,
1470
+ );
1471
+ expect(screen.getByRole('table')).toHaveAttribute(
1472
+ 'aria-rowcount',
1473
+ String(users.length),
1474
+ );
1475
+ const indices = bodyRows(container).map(r =>
1476
+ r.getAttribute('aria-rowindex'),
1477
+ );
1478
+ expect(indices).toEqual(['1', '2', '3']);
1479
+ });
1480
+
1481
+ it('offsets aria-rowindex by rowIndexStart for a paginated view', () => {
1482
+ // Page 3 of a 10-per-page dataset: first visible row is dataset row 21.
1483
+ const {container} = render(
1484
+ <Table
1485
+ data={users}
1486
+ columns={columns}
1487
+ rowIndexStart={21}
1488
+ rowCount={100}
1489
+ />,
1490
+ );
1491
+ expect(screen.getByRole('table')).toHaveAttribute('aria-rowcount', '100');
1492
+ const indices = bodyRows(container).map(r =>
1493
+ r.getAttribute('aria-rowindex'),
1494
+ );
1495
+ expect(indices).toEqual(['21', '22', '23']);
1496
+ });
1497
+
1498
+ it('sets aria-rowcount to -1 (unknown) when only rowIndexStart is given', () => {
1499
+ // Windowed/cursor pagination: offset known, total unknown.
1500
+ const {container} = render(
1501
+ <Table data={users} columns={columns} rowIndexStart={5} />,
1502
+ );
1503
+ expect(screen.getByRole('table')).toHaveAttribute('aria-rowcount', '-1');
1504
+ const indices = bodyRows(container).map(r =>
1505
+ r.getAttribute('aria-rowindex'),
1506
+ );
1507
+ expect(indices).toEqual(['5', '6', '7']);
1508
+ });
1509
+
1510
+ it('does not assign an ARIA row index to the header row', () => {
1511
+ render(<Table data={users} columns={columns} rowCount={users.length} />);
1512
+ const header = screen.getAllByRole('row')[0];
1513
+ expect(header).not.toHaveAttribute('aria-rowindex');
1514
+ });
1515
+
1516
+ it('lets a plugin override the seeded aria-rowindex', () => {
1517
+ const plugin: TablePlugin<User> = {
1518
+ transformBodyRow(props, _item, index) {
1519
+ return {
1520
+ ...props,
1521
+ htmlProps: {...props.htmlProps, 'aria-rowindex': 100 + index},
1522
+ };
1523
+ },
1524
+ };
1525
+ const {container} = render(
1526
+ <Table
1527
+ data={users}
1528
+ columns={columns}
1529
+ rowCount={users.length}
1530
+ plugins={{custom: plugin}}
1531
+ />,
1532
+ );
1533
+ const indices = bodyRows(container).map(r =>
1534
+ r.getAttribute('aria-rowindex'),
1535
+ );
1536
+ expect(indices).toEqual(['100', '101', '102']);
1537
+ });
1538
+ });
@@ -20,6 +20,7 @@ import {
20
20
  fontWeightVars,
21
21
  } from '../../../theme/tokens.stylex';
22
22
  import {Icon} from '../../../Icon';
23
+ import {rtlStyles} from '../../../utils';
23
24
  import type {TablePlugin} from '../../types';
24
25
  import {useTranslator} from '../../../i18n';
25
26
 
@@ -333,12 +334,14 @@ export function useTableGroupedRows<T extends Record<string, unknown>>(
333
334
  })
334
335
  }
335
336
  aria-expanded={!collapsed}>
336
- <span
337
- {...stylex.props(
338
- styles.chevronIcon,
339
- !collapsed && styles.chevronExpanded,
340
- )}>
341
- <Icon icon="chevronRight" size="xsm" />
337
+ <span {...stylex.props(rtlStyles.mirror)}>
338
+ <span
339
+ {...stylex.props(
340
+ styles.chevronIcon,
341
+ !collapsed && styles.chevronExpanded,
342
+ )}>
343
+ <Icon icon="chevronRight" size="xsm" />
344
+ </span>
342
345
  </span>
343
346
  </button>
344
347
  {content}
@@ -16,6 +16,7 @@ import {useCallback, useMemo, useRef, type ReactNode} from 'react';
16
16
  import * as stylex from '@stylexjs/stylex';
17
17
  import {spacingVars, colorVars, radiusVars} from '../../../theme/tokens.stylex';
18
18
  import {Icon} from '../../../Icon';
19
+ import {rtlStyles} from '../../../utils';
19
20
  import {resolveContextActions} from '../../tableContextMenu';
20
21
  import {useTranslator} from '../../../i18n';
21
22
  import type {
@@ -361,12 +362,14 @@ function ExpansionChevron({
361
362
  }}
362
363
  aria-label={ariaLabel}
363
364
  aria-expanded={isExpanded}>
364
- <span
365
- {...stylex.props(
366
- expansionStyles.chevronIcon,
367
- isExpanded && expansionStyles.chevronExpanded,
368
- )}>
369
- <Icon icon="chevronRight" size="xsm" />
365
+ <span {...stylex.props(rtlStyles.mirror)}>
366
+ <span
367
+ {...stylex.props(
368
+ expansionStyles.chevronIcon,
369
+ isExpanded && expansionStyles.chevronExpanded,
370
+ )}>
371
+ <Icon icon="chevronRight" size="xsm" />
372
+ </span>
370
373
  </span>
371
374
  </button>
372
375
  );
@@ -533,12 +536,14 @@ export function useTableRowExpansion<T extends Record<string, unknown>>(
533
536
  ? t('@astryx.tableRowExpansion.collapseAllRows')
534
537
  : t('@astryx.tableRowExpansion.expandAllRows')
535
538
  }>
536
- <span
537
- {...stylex.props(
538
- expansionStyles.chevronIcon,
539
- allExpanded && expansionStyles.chevronExpanded,
540
- )}>
541
- <Icon icon="chevronRight" size="xsm" />
539
+ <span {...stylex.props(rtlStyles.mirror)}>
540
+ <span
541
+ {...stylex.props(
542
+ expansionStyles.chevronIcon,
543
+ allExpanded && expansionStyles.chevronExpanded,
544
+ )}>
545
+ <Icon icon="chevronRight" size="xsm" />
546
+ </span>
542
547
  </span>
543
548
  </button>
544
549
  ),
@@ -573,11 +578,13 @@ export function useTableRowExpansion<T extends Record<string, unknown>>(
573
578
  group: 'row-expansion',
574
579
  label: isExpanded ? 'Collapse row' : 'Expand row',
575
580
  icon: (
576
- <Icon
577
- icon={isExpanded ? 'chevronDown' : 'chevronRight'}
578
- size="xsm"
579
- aria-hidden
580
- />
581
+ <span {...stylex.props(rtlStyles.mirror)}>
582
+ <Icon
583
+ icon={isExpanded ? 'chevronDown' : 'chevronRight'}
584
+ size="xsm"
585
+ aria-hidden
586
+ />
587
+ </span>
581
588
  ),
582
589
  onSelect: () => onToggle(key),
583
590
  },