@astryxdesign/core 0.1.9 → 0.2.0-canary.136a00f

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 (560) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/dist/Avatar/Avatar.d.ts +42 -3
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +190 -59
  5. package/dist/Avatar/AvatarStatusDot.d.ts +6 -4
  6. package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
  7. package/dist/AvatarGroup/AvatarGroup.d.ts +7 -1
  8. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  9. package/dist/AvatarGroup/AvatarGroup.js +58 -6
  10. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
  11. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  12. package/dist/AvatarGroup/AvatarGroupOverflow.js +7 -2
  13. package/dist/Banner/Banner.js +2 -2
  14. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  15. package/dist/Breadcrumbs/BreadcrumbItem.js +11 -4
  16. package/dist/Breadcrumbs/index.d.ts +1 -0
  17. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  18. package/dist/Breadcrumbs/index.js +2 -1
  19. package/dist/Button/Button.d.ts +1 -1
  20. package/dist/Button/Button.d.ts.map +1 -1
  21. package/dist/Button/Button.js +6 -4
  22. package/dist/Calendar/Calendar.d.ts.map +1 -1
  23. package/dist/Calendar/Calendar.js +50 -9
  24. package/dist/Calendar/dayCellUtils.d.ts +4 -4
  25. package/dist/Calendar/dayCellUtils.js +2 -2
  26. package/dist/Calendar/styles.d.ts +30 -31
  27. package/dist/Calendar/styles.d.ts.map +1 -1
  28. package/dist/Calendar/styles.js +30 -31
  29. package/dist/Card/Card.js +1 -1
  30. package/dist/Carousel/Carousel.d.ts.map +1 -1
  31. package/dist/Carousel/Carousel.js +22 -12
  32. package/dist/Chat/ChatComposerInput.js +1 -1
  33. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  34. package/dist/Chat/ChatDictationButton.js +3 -1
  35. package/dist/Chat/ChatLayout.js +4 -4
  36. package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
  37. package/dist/Chat/ChatMessageBubble.js +8 -8
  38. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  39. package/dist/Chat/ChatToolCalls.js +10 -3
  40. package/dist/Chat/useTriggerMenu.js +2 -2
  41. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  42. package/dist/CheckboxInput/CheckboxInput.js +6 -6
  43. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  44. package/dist/CheckboxList/CheckboxListItem.js +19 -4
  45. package/dist/Citation/Citation.d.ts +22 -1
  46. package/dist/Citation/Citation.d.ts.map +1 -1
  47. package/dist/Citation/Citation.js +20 -6
  48. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  49. package/dist/CodeBlock/CodeBlock.js +16 -10
  50. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  51. package/dist/Collapsible/Collapsible.js +3 -1
  52. package/dist/CommandPalette/CommandPalette.d.ts +1 -1
  53. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  54. package/dist/CommandPalette/CommandPalette.js +42 -3
  55. package/dist/CommandPalette/CommandPaletteItem.js +1 -1
  56. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  57. package/dist/ContextMenu/ContextMenu.js +23 -5
  58. package/dist/ContextMenu/index.d.ts +1 -0
  59. package/dist/ContextMenu/index.d.ts.map +1 -1
  60. package/dist/ContextMenu/index.js +3 -1
  61. package/dist/DateInput/DateInput.d.ts +10 -2
  62. package/dist/DateInput/DateInput.d.ts.map +1 -1
  63. package/dist/DateInput/DateInput.js +33 -19
  64. package/dist/DateRangeInput/DateRangeInput.d.ts +10 -2
  65. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  66. package/dist/DateRangeInput/DateRangeInput.js +23 -20
  67. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  68. package/dist/DateTimeInput/DateTimeInput.js +23 -18
  69. package/dist/Divider/Divider.d.ts +2 -2
  70. package/dist/Divider/Divider.d.ts.map +1 -1
  71. package/dist/Divider/Divider.js +13 -0
  72. package/dist/DropdownMenu/DropdownMenu.d.ts +6 -0
  73. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  74. package/dist/DropdownMenu/DropdownMenu.js +22 -5
  75. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  76. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +4 -1
  77. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  78. package/dist/DropdownMenu/DropdownMenuItem.js +5 -2
  79. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +8 -11
  80. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
  81. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +3 -1
  82. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +1 -1
  83. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  84. package/dist/DropdownMenu/DropdownMenuRadioItem.js +5 -2
  85. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +104 -0
  86. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -0
  87. package/dist/DropdownMenu/DropdownMenuSubMenu.js +427 -0
  88. package/dist/DropdownMenu/index.d.ts +10 -0
  89. package/dist/DropdownMenu/index.d.ts.map +1 -1
  90. package/dist/DropdownMenu/index.js +13 -0
  91. package/dist/DropdownMenu/menuItemHover.d.ts +23 -0
  92. package/dist/DropdownMenu/menuItemHover.d.ts.map +1 -0
  93. package/dist/DropdownMenu/menuItemHover.js +33 -0
  94. package/dist/DropdownMenu/menuItemRoles.d.ts +8 -0
  95. package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -1
  96. package/dist/DropdownMenu/menuItemRoles.js +10 -1
  97. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  98. package/dist/DropdownMenu/renderDropdownItems.js +21 -6
  99. package/dist/Field/Field.d.ts +3 -1
  100. package/dist/Field/Field.d.ts.map +1 -1
  101. package/dist/Field/Field.js +4 -1
  102. package/dist/Field/FieldLabel.d.ts +2 -1
  103. package/dist/Field/FieldLabel.d.ts.map +1 -1
  104. package/dist/Field/FieldLabel.js +9 -5
  105. package/dist/Field/inputStyles.stylex.d.ts +1 -0
  106. package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
  107. package/dist/Field/inputStyles.stylex.js +1 -0
  108. package/dist/FieldStatus/FieldStatus.d.ts +10 -0
  109. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  110. package/dist/FieldStatus/FieldStatus.js +39 -3
  111. package/dist/FileInput/FileInput.d.ts +10 -2
  112. package/dist/FileInput/FileInput.d.ts.map +1 -1
  113. package/dist/FileInput/FileInput.js +93 -65
  114. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  115. package/dist/HoverCard/useHoverCard.js +10 -4
  116. package/dist/Icon/Icon.d.ts +15 -1
  117. package/dist/Icon/Icon.d.ts.map +1 -1
  118. package/dist/Icon/Icon.js +19 -3
  119. package/dist/Item/Item.d.ts +16 -3
  120. package/dist/Item/Item.d.ts.map +1 -1
  121. package/dist/Item/Item.js +31 -6
  122. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  123. package/dist/Layer/layerAnimations.stylex.js +10 -2
  124. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  125. package/dist/Lightbox/Lightbox.js +19 -13
  126. package/dist/List/List.d.ts.map +1 -1
  127. package/dist/List/List.js +6 -3
  128. package/dist/List/ListItem.d.ts +11 -1
  129. package/dist/List/ListItem.d.ts.map +1 -1
  130. package/dist/List/ListItem.js +2 -0
  131. package/dist/Markdown/Markdown.d.ts.map +1 -1
  132. package/dist/Markdown/Markdown.js +7 -0
  133. package/dist/MetadataList/MetadataList.d.ts +1 -1
  134. package/dist/MetadataList/MetadataList.d.ts.map +1 -1
  135. package/dist/MetadataList/MetadataList.js +4 -2
  136. package/dist/MultiSelector/MultiSelector.d.ts +9 -1
  137. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  138. package/dist/MultiSelector/MultiSelector.js +80 -42
  139. package/dist/NavMenu/NavHeadingMenuItem.js +1 -1
  140. package/dist/NumberInput/NumberInput.d.ts +10 -2
  141. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  142. package/dist/NumberInput/NumberInput.js +22 -17
  143. package/dist/Outline/Outline.js +2 -2
  144. package/dist/Pagination/Pagination.d.ts.map +1 -1
  145. package/dist/Pagination/Pagination.js +11 -3
  146. package/dist/PowerSearch/PowerSearch.d.ts +9 -2
  147. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  148. package/dist/PowerSearch/PowerSearch.js +2 -0
  149. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  150. package/dist/ProgressBar/ProgressBar.js +6 -1
  151. package/dist/RadioList/RadioList.d.ts +2 -2
  152. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  153. package/dist/RadioList/RadioListItem.js +5 -5
  154. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  155. package/dist/Resizable/ResizeHandle.js +49 -19
  156. package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
  157. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  158. package/dist/SegmentedControl/SegmentedControlItem.js +5 -3
  159. package/dist/Selector/Selector.d.ts +8 -0
  160. package/dist/Selector/Selector.d.ts.map +1 -1
  161. package/dist/Selector/Selector.js +66 -17
  162. package/dist/SideNav/SideNav.d.ts.map +1 -1
  163. package/dist/SideNav/SideNav.js +5 -5
  164. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  165. package/dist/SideNav/SideNavCollapseButton.js +13 -10
  166. package/dist/SideNav/SideNavItem.d.ts +1 -1
  167. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  168. package/dist/SideNav/SideNavItem.js +12 -10
  169. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  170. package/dist/Skeleton/Skeleton.js +2 -2
  171. package/dist/Slider/Slider.d.ts.map +1 -1
  172. package/dist/Slider/Slider.js +87 -34
  173. package/dist/Spinner/Spinner.d.ts +3 -2
  174. package/dist/Spinner/Spinner.d.ts.map +1 -1
  175. package/dist/Spinner/Spinner.js +11 -2
  176. package/dist/Switch/Switch.d.ts +8 -1
  177. package/dist/Switch/Switch.d.ts.map +1 -1
  178. package/dist/Switch/Switch.js +171 -91
  179. package/dist/TabList/Tab.js +2 -2
  180. package/dist/TabList/TabList.d.ts +2 -11
  181. package/dist/TabList/TabList.d.ts.map +1 -1
  182. package/dist/TabList/TabList.js +5 -9
  183. package/dist/TabList/TabListContext.d.ts +0 -5
  184. package/dist/TabList/TabListContext.d.ts.map +1 -1
  185. package/dist/TabList/TabListContext.js +0 -5
  186. package/dist/TabList/TabMenu.js +1 -1
  187. package/dist/Table/BaseTable.d.ts.map +1 -1
  188. package/dist/Table/BaseTable.js +30 -4
  189. package/dist/Table/TableCell.js +3 -3
  190. package/dist/Table/TableHeaderCell.js +3 -3
  191. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  192. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +15 -11
  193. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  194. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +37 -27
  195. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  196. package/dist/Table/plugins/selection/useTableSelection.js +3 -0
  197. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  198. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +15 -7
  199. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  200. package/dist/Table/plugins/tree/useTableTreeData.js +29 -23
  201. package/dist/Table/types.d.ts +32 -0
  202. package/dist/Table/types.d.ts.map +1 -1
  203. package/dist/TextArea/TextArea.d.ts +10 -1
  204. package/dist/TextArea/TextArea.d.ts.map +1 -1
  205. package/dist/TextArea/TextArea.js +18 -19
  206. package/dist/TextInput/TextInput.d.ts +10 -2
  207. package/dist/TextInput/TextInput.d.ts.map +1 -1
  208. package/dist/TextInput/TextInput.js +20 -17
  209. package/dist/Thumbnail/Thumbnail.js +2 -2
  210. package/dist/TimeInput/TimeInput.d.ts +10 -2
  211. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  212. package/dist/TimeInput/TimeInput.js +17 -17
  213. package/dist/Timestamp/Timestamp.d.ts +31 -3
  214. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  215. package/dist/Timestamp/Timestamp.js +47 -76
  216. package/dist/Timestamp/formatInstant.d.ts +41 -0
  217. package/dist/Timestamp/formatInstant.d.ts.map +1 -0
  218. package/dist/Timestamp/formatInstant.js +177 -0
  219. package/dist/Timestamp/index.d.ts +1 -0
  220. package/dist/Timestamp/index.d.ts.map +1 -1
  221. package/dist/Timestamp/tooltipEntries.d.ts +56 -0
  222. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -0
  223. package/dist/Timestamp/tooltipEntries.js +163 -0
  224. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  225. package/dist/Toast/ToastViewport.js +61 -9
  226. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  227. package/dist/ToggleButton/ToggleButton.js +3 -1
  228. package/dist/Token/Token.d.ts +34 -4
  229. package/dist/Token/Token.d.ts.map +1 -1
  230. package/dist/Token/Token.js +85 -45
  231. package/dist/Token/TokenLink.d.ts +62 -0
  232. package/dist/Token/TokenLink.d.ts.map +1 -0
  233. package/dist/Token/TokenLink.js +76 -0
  234. package/dist/Token/index.d.ts +1 -1
  235. package/dist/Token/index.d.ts.map +1 -1
  236. package/dist/Tokenizer/Tokenizer.d.ts +9 -2
  237. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  238. package/dist/Tokenizer/Tokenizer.js +4 -2
  239. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  240. package/dist/Tooltip/useTooltip.js +17 -1
  241. package/dist/TopNav/TopNavItem.d.ts +3 -1
  242. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  243. package/dist/TopNav/TopNavItem.js +30 -3
  244. package/dist/TreeList/TreeList.d.ts.map +1 -1
  245. package/dist/TreeList/TreeList.js +1 -0
  246. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  247. package/dist/TreeList/TreeListBranches.js +8 -6
  248. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  249. package/dist/TreeList/TreeListItem.js +32 -14
  250. package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
  251. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  252. package/dist/Typeahead/BaseTypeahead.js +22 -11
  253. package/dist/Typeahead/Typeahead.d.ts +9 -2
  254. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  255. package/dist/Typeahead/Typeahead.js +11 -2
  256. package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
  257. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  258. package/dist/Typeahead/TypeaheadItem.js +20 -10
  259. package/dist/astryx.css +70 -30
  260. package/dist/astryx.umd.js +51 -51
  261. package/dist/astryx.umd.js.map +4 -4
  262. package/dist/hooks/index.d.ts +2 -0
  263. package/dist/hooks/index.d.ts.map +1 -1
  264. package/dist/hooks/index.js +1 -0
  265. package/dist/hooks/isRtlElement.d.ts +18 -0
  266. package/dist/hooks/isRtlElement.d.ts.map +1 -0
  267. package/dist/hooks/isRtlElement.js +27 -0
  268. package/dist/hooks/useAnnounce.d.ts +3 -0
  269. package/dist/hooks/useAnnounce.d.ts.map +1 -1
  270. package/dist/hooks/useAnnounce.js +39 -1
  271. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  272. package/dist/hooks/useFocusTrap.js +52 -11
  273. package/dist/hooks/useGridFocus.d.ts +6 -1
  274. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  275. package/dist/hooks/useGridFocus.js +12 -9
  276. package/dist/hooks/useHotkeys.d.ts +9 -0
  277. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  278. package/dist/hooks/useHotkeys.js +9 -0
  279. package/dist/hooks/useInputStatusIcon.d.ts +38 -0
  280. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -0
  281. package/dist/hooks/useInputStatusIcon.js +158 -0
  282. package/dist/hooks/useKeyboardHint.d.ts +1 -1
  283. package/dist/hooks/useKeyboardHint.d.ts.map +1 -1
  284. package/dist/hooks/useKeyboardHint.js +4 -2
  285. package/dist/hooks/useListFocus.d.ts +39 -1
  286. package/dist/hooks/useListFocus.d.ts.map +1 -1
  287. package/dist/hooks/useListFocus.js +54 -9
  288. package/dist/i18n/InternationalizationContext.d.ts +1 -0
  289. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  290. package/dist/i18n/InternationalizationContext.js +3 -0
  291. package/dist/i18n/InternationalizationProvider.d.ts +8 -1
  292. package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
  293. package/dist/i18n/InternationalizationProvider.js +5 -1
  294. package/dist/i18n/getLocaleDirection.d.ts +18 -0
  295. package/dist/i18n/getLocaleDirection.d.ts.map +1 -0
  296. package/dist/i18n/getLocaleDirection.js +29 -0
  297. package/dist/i18n/index.d.ts +6 -0
  298. package/dist/i18n/index.d.ts.map +1 -1
  299. package/dist/i18n/index.js +7 -1
  300. package/dist/i18n/useDirection.d.ts +2 -0
  301. package/dist/i18n/useDirection.d.ts.map +1 -0
  302. package/dist/i18n/useDirection.js +27 -0
  303. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  304. package/dist/theme/derivedVarRegistry.js +13 -0
  305. package/dist/utils/index.d.ts +1 -0
  306. package/dist/utils/index.d.ts.map +1 -1
  307. package/dist/utils/index.js +2 -1
  308. package/dist/utils/plainDate.d.ts +17 -0
  309. package/dist/utils/plainDate.d.ts.map +1 -1
  310. package/dist/utils/plainDate.js +23 -4
  311. package/dist/utils/rtlStyles.d.ts +24 -0
  312. package/dist/utils/rtlStyles.d.ts.map +1 -0
  313. package/dist/utils/rtlStyles.js +27 -0
  314. package/locales/en.json +112 -0
  315. package/locales/pseudo.json +84 -0
  316. package/package.json +6 -1
  317. package/src/Avatar/Avatar.doc.mjs +50 -3
  318. package/src/Avatar/Avatar.test.tsx +218 -0
  319. package/src/Avatar/Avatar.tsx +285 -79
  320. package/src/Avatar/AvatarStatusDot.doc.mjs +4 -4
  321. package/src/Avatar/AvatarStatusDot.tsx +6 -4
  322. package/src/AvatarGroup/AvatarGroup.doc.mjs +2 -0
  323. package/src/AvatarGroup/AvatarGroup.test.tsx +162 -2
  324. package/src/AvatarGroup/AvatarGroup.tsx +58 -3
  325. package/src/AvatarGroup/AvatarGroupOverflow.tsx +5 -2
  326. package/src/Banner/Banner.tsx +6 -6
  327. package/src/Breadcrumbs/BreadcrumbItem.tsx +10 -10
  328. package/src/Breadcrumbs/Breadcrumbs.test.tsx +52 -1
  329. package/src/Breadcrumbs/index.ts +4 -0
  330. package/src/Button/Button.doc.mjs +0 -4
  331. package/src/Button/Button.test.tsx +16 -0
  332. package/src/Button/Button.tsx +6 -4
  333. package/src/Calendar/Calendar.test.tsx +170 -20
  334. package/src/Calendar/Calendar.tsx +73 -16
  335. package/src/Calendar/dayCellUtils.test.ts +18 -18
  336. package/src/Calendar/dayCellUtils.ts +2 -2
  337. package/src/Calendar/styles.ts +33 -39
  338. package/src/Card/Card.tsx +1 -1
  339. package/src/Carousel/Carousel.tsx +25 -6
  340. package/src/Chat/ChatComposer.doc.mjs +2 -2
  341. package/src/Chat/ChatComposerInput.doc.mjs +1 -1
  342. package/src/Chat/ChatComposerInput.tsx +2 -2
  343. package/src/Chat/ChatDictationButton.tsx +7 -1
  344. package/src/Chat/ChatLayout.tsx +4 -4
  345. package/src/Chat/ChatMessageBubble.tsx +15 -10
  346. package/src/Chat/ChatSystemMessage.test.tsx +5 -0
  347. package/src/Chat/ChatToolCalls.tsx +9 -2
  348. package/src/Chat/useTriggerMenu.tsx +1 -1
  349. package/src/CheckboxInput/CheckboxInput.doc.mjs +6 -0
  350. package/src/CheckboxInput/CheckboxInput.test.tsx +23 -0
  351. package/src/CheckboxInput/CheckboxInput.tsx +21 -6
  352. package/src/CheckboxList/CheckboxList.doc.mjs +6 -0
  353. package/src/CheckboxList/CheckboxList.test.tsx +94 -3
  354. package/src/CheckboxList/CheckboxListItem.tsx +17 -10
  355. package/src/Citation/Citation.doc.mjs +4 -3
  356. package/src/Citation/Citation.test.tsx +97 -0
  357. package/src/Citation/Citation.tsx +43 -11
  358. package/src/CodeBlock/CodeBlock.test.tsx +17 -0
  359. package/src/CodeBlock/CodeBlock.tsx +38 -11
  360. package/src/Collapsible/Collapsible.doc.mjs +1 -0
  361. package/src/Collapsible/Collapsible.test.tsx +7 -0
  362. package/src/Collapsible/Collapsible.tsx +16 -6
  363. package/src/CommandPalette/CommandPalette.test.tsx +95 -1
  364. package/src/CommandPalette/CommandPalette.tsx +41 -2
  365. package/src/CommandPalette/CommandPaletteItem.tsx +1 -1
  366. package/src/ContextMenu/ContextMenu.test.tsx +59 -2
  367. package/src/ContextMenu/ContextMenu.tsx +22 -11
  368. package/src/ContextMenu/index.ts +5 -0
  369. package/src/DateInput/DateInput.doc.mjs +25 -0
  370. package/src/DateInput/DateInput.test.tsx +230 -0
  371. package/src/DateInput/DateInput.tsx +51 -29
  372. package/src/DateRangeInput/DateRangeInput.doc.mjs +16 -0
  373. package/src/DateRangeInput/DateRangeInput.test.tsx +140 -0
  374. package/src/DateRangeInput/DateRangeInput.tsx +42 -30
  375. package/src/DateTimeInput/DateTimeInput.doc.mjs +22 -1
  376. package/src/DateTimeInput/DateTimeInput.test.tsx +112 -0
  377. package/src/DateTimeInput/DateTimeInput.tsx +44 -39
  378. package/src/Divider/Divider.test.tsx +32 -8
  379. package/src/Divider/Divider.tsx +14 -1
  380. package/src/DropdownMenu/DropdownMenu.test.tsx +60 -0
  381. package/src/DropdownMenu/DropdownMenu.tsx +31 -12
  382. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +8 -2
  383. package/src/DropdownMenu/DropdownMenuItem.tsx +9 -3
  384. package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +4 -12
  385. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +9 -10
  386. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +9 -3
  387. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +20 -2
  388. package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +88 -0
  389. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +635 -0
  390. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +553 -0
  391. package/src/DropdownMenu/index.ts +16 -0
  392. package/src/DropdownMenu/menuItemHover.ts +39 -0
  393. package/src/DropdownMenu/menuItemRoles.ts +9 -0
  394. package/src/DropdownMenu/renderDropdownItems.tsx +29 -15
  395. package/src/Field/Field.tsx +14 -9
  396. package/src/Field/FieldLabel.test.tsx +25 -0
  397. package/src/Field/FieldLabel.tsx +12 -3
  398. package/src/Field/inputStyles.stylex.ts +2 -0
  399. package/src/FieldStatus/FieldStatus.test.tsx +72 -0
  400. package/src/FieldStatus/FieldStatus.tsx +48 -2
  401. package/src/FileInput/FileInput.doc.mjs +23 -2
  402. package/src/FileInput/FileInput.test.tsx +104 -13
  403. package/src/FileInput/FileInput.tsx +106 -79
  404. package/src/HoverCard/useHoverCard.tsx +15 -3
  405. package/src/Icon/Icon.doc.mjs +13 -0
  406. package/src/Icon/Icon.test.tsx +89 -0
  407. package/src/Icon/Icon.tsx +51 -3
  408. package/src/Item/Item.doc.mjs +5 -0
  409. package/src/Item/Item.test.tsx +88 -0
  410. package/src/Item/Item.tsx +57 -11
  411. package/src/Layer/layerAnimations.stylex.ts +23 -0
  412. package/src/Lightbox/Lightbox.tsx +15 -7
  413. package/src/List/List.test.tsx +17 -29
  414. package/src/List/List.tsx +6 -1
  415. package/src/List/ListItem.doc.mjs +12 -0
  416. package/src/List/ListItem.tsx +13 -0
  417. package/src/Markdown/Markdown.test.tsx +21 -0
  418. package/src/Markdown/Markdown.tsx +4 -0
  419. package/src/MetadataList/MetadataList.test.tsx +24 -0
  420. package/src/MetadataList/MetadataList.tsx +6 -2
  421. package/src/MultiSelector/MultiSelector.doc.mjs +20 -0
  422. package/src/MultiSelector/MultiSelector.test.tsx +402 -0
  423. package/src/MultiSelector/MultiSelector.tsx +120 -59
  424. package/src/NavMenu/NavHeadingMenuItem.tsx +1 -1
  425. package/src/NumberInput/NumberInput.doc.mjs +21 -0
  426. package/src/NumberInput/NumberInput.test.tsx +71 -0
  427. package/src/NumberInput/NumberInput.tsx +35 -26
  428. package/src/Outline/Outline.test.tsx +12 -12
  429. package/src/Outline/Outline.tsx +2 -2
  430. package/src/Pagination/Pagination.tsx +11 -3
  431. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  432. package/src/PowerSearch/PowerSearch.test.tsx +23 -0
  433. package/src/PowerSearch/PowerSearch.tsx +10 -1
  434. package/src/ProgressBar/ProgressBar.test.tsx +34 -0
  435. package/src/ProgressBar/ProgressBar.tsx +16 -1
  436. package/src/RadioList/RadioList.doc.mjs +6 -0
  437. package/src/RadioList/RadioList.test.tsx +26 -4
  438. package/src/RadioList/RadioList.tsx +2 -2
  439. package/src/RadioList/RadioListItem.tsx +12 -5
  440. package/src/Resizable/ResizeHandle.test.tsx +94 -0
  441. package/src/Resizable/ResizeHandle.tsx +70 -20
  442. package/src/SegmentedControl/SegmentedControl.test.tsx +28 -0
  443. package/src/SegmentedControl/SegmentedControlItem.tsx +22 -2
  444. package/src/Selector/Selector.doc.mjs +15 -0
  445. package/src/Selector/Selector.test.tsx +370 -0
  446. package/src/Selector/Selector.tsx +88 -20
  447. package/src/SideNav/SideNav.tsx +1 -8
  448. package/src/SideNav/SideNavCollapseButton.tsx +9 -7
  449. package/src/SideNav/SideNavItem.tsx +5 -3
  450. package/src/Skeleton/Skeleton.test.tsx +16 -0
  451. package/src/Skeleton/Skeleton.tsx +13 -1
  452. package/src/Slider/Slider.doc.mjs +6 -0
  453. package/src/Slider/Slider.test.tsx +240 -12
  454. package/src/Slider/Slider.tsx +96 -23
  455. package/src/Spinner/Spinner.test.tsx +10 -8
  456. package/src/Spinner/Spinner.tsx +15 -5
  457. package/src/Switch/Switch.doc.mjs +142 -42
  458. package/src/Switch/Switch.test.tsx +111 -0
  459. package/src/Switch/Switch.tsx +149 -32
  460. package/src/TabList/Tab.tsx +2 -2
  461. package/src/TabList/TabList.doc.mjs +0 -6
  462. package/src/TabList/TabList.test.tsx +3 -15
  463. package/src/TabList/TabList.tsx +6 -17
  464. package/src/TabList/TabListContext.ts +0 -6
  465. package/src/TabList/TabMenu.tsx +2 -2
  466. package/src/Table/BaseTable.tsx +34 -2
  467. package/src/Table/Table.doc.mjs +11 -0
  468. package/src/Table/Table.test.tsx +85 -0
  469. package/src/Table/TableCell.tsx +3 -3
  470. package/src/Table/TableHeaderCell.tsx +3 -3
  471. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +9 -6
  472. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +19 -0
  473. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +27 -18
  474. package/src/Table/plugins/selection/useTableSelection.test.tsx +61 -3
  475. package/src/Table/plugins/selection/useTableSelection.tsx +3 -0
  476. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +38 -9
  477. package/src/Table/plugins/tree/useTableTreeData.tsx +17 -13
  478. package/src/Table/types.ts +33 -0
  479. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  480. package/src/TextArea/TextArea.doc.mjs +21 -0
  481. package/src/TextArea/TextArea.test.tsx +23 -0
  482. package/src/TextArea/TextArea.tsx +27 -26
  483. package/src/TextInput/TextInput.doc.mjs +21 -0
  484. package/src/TextInput/TextInput.test.tsx +150 -0
  485. package/src/TextInput/TextInput.tsx +28 -25
  486. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  487. package/src/Thumbnail/Thumbnail.tsx +1 -1
  488. package/src/TimeInput/TimeInput.doc.mjs +21 -0
  489. package/src/TimeInput/TimeInput.test.tsx +23 -0
  490. package/src/TimeInput/TimeInput.tsx +24 -25
  491. package/src/Timestamp/Timestamp.doc.mjs +32 -6
  492. package/src/Timestamp/Timestamp.test.tsx +371 -0
  493. package/src/Timestamp/Timestamp.tsx +96 -93
  494. package/src/Timestamp/formatInstant.ts +218 -0
  495. package/src/Timestamp/index.ts +4 -0
  496. package/src/Timestamp/tooltipEntries.test.ts +552 -0
  497. package/src/Timestamp/tooltipEntries.ts +210 -0
  498. package/src/Toast/ToastViewport.test.tsx +218 -8
  499. package/src/Toast/ToastViewport.tsx +71 -12
  500. package/src/ToggleButton/ToggleButton.test.tsx +24 -0
  501. package/src/ToggleButton/ToggleButton.tsx +19 -1
  502. package/src/Token/Token.test.tsx +166 -15
  503. package/src/Token/Token.tsx +104 -47
  504. package/src/Token/TokenLink.tsx +107 -0
  505. package/src/Token/index.ts +1 -1
  506. package/src/Tokenizer/Tokenizer.doc.mjs +23 -1
  507. package/src/Tokenizer/Tokenizer.test.tsx +23 -0
  508. package/src/Tokenizer/Tokenizer.tsx +14 -2
  509. package/src/Tooltip/Tooltip.test.tsx +46 -0
  510. package/src/Tooltip/useTooltip.tsx +20 -0
  511. package/src/TopNav/TopNav.test.tsx +93 -2
  512. package/src/TopNav/TopNavItem.doc.mjs +3 -3
  513. package/src/TopNav/TopNavItem.tsx +35 -6
  514. package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +1 -1
  515. package/src/TreeList/TreeList.doc.mjs +6 -0
  516. package/src/TreeList/TreeList.test.tsx +50 -10
  517. package/src/TreeList/TreeList.tsx +5 -0
  518. package/src/TreeList/TreeListBranches.tsx +8 -6
  519. package/src/TreeList/TreeListItem.tsx +33 -11
  520. package/src/Typeahead/BaseTypeahead.tsx +42 -15
  521. package/src/Typeahead/Typeahead.doc.mjs +13 -0
  522. package/src/Typeahead/Typeahead.test.tsx +112 -0
  523. package/src/Typeahead/Typeahead.tsx +17 -1
  524. package/src/Typeahead/TypeaheadItem.tsx +2 -1
  525. package/src/__tests__/forcedColors.ts +60 -0
  526. package/src/__tests__/inputWidthContract.test.tsx +62 -0
  527. package/src/doc-mjs.d.ts +19 -0
  528. package/src/hooks/index.ts +6 -0
  529. package/src/hooks/isRtlElement.ts +27 -0
  530. package/src/hooks/useAnnounce.test.tsx +75 -2
  531. package/src/hooks/useAnnounce.ts +47 -1
  532. package/src/hooks/useFocusTrap.test.tsx +104 -0
  533. package/src/hooks/useFocusTrap.ts +61 -12
  534. package/src/hooks/useGridFocus.doc.mjs +3 -3
  535. package/src/hooks/useGridFocus.test.tsx +41 -0
  536. package/src/hooks/useGridFocus.ts +21 -10
  537. package/src/hooks/useHotkeys.ts +9 -0
  538. package/src/hooks/useInputStatusIcon.test.tsx +162 -0
  539. package/src/hooks/useInputStatusIcon.tsx +224 -0
  540. package/src/hooks/useKeyboardHint.test.tsx +22 -0
  541. package/src/hooks/useKeyboardHint.tsx +6 -2
  542. package/src/hooks/useListFocus.doc.mjs +25 -3
  543. package/src/hooks/useListFocus.test.tsx +153 -1
  544. package/src/hooks/useListFocus.ts +102 -9
  545. package/src/i18n/InternationalizationContext.ts +4 -0
  546. package/src/i18n/InternationalizationProvider.tsx +12 -2
  547. package/src/i18n/__tests__/getLocaleDirection.test.ts +40 -0
  548. package/src/i18n/__tests__/useDirection.test.tsx +66 -0
  549. package/src/i18n/getLocaleDirection.ts +29 -0
  550. package/src/i18n/index.ts +6 -0
  551. package/src/i18n/useDirection.ts +29 -0
  552. package/src/reset.css +0 -26
  553. package/src/reset.test.ts +60 -0
  554. package/src/theme/derivedVarRegistry.test.ts +1 -0
  555. package/src/theme/derivedVarRegistry.ts +6 -0
  556. package/src/theme/generateThemeRules.test.ts +42 -0
  557. package/src/utils/index.ts +2 -0
  558. package/src/utils/plainDate.test.ts +24 -6
  559. package/src/utils/plainDate.ts +18 -4
  560. package/src/utils/rtlStyles.ts +27 -0
@@ -4,7 +4,8 @@ import {render, screen} from '@testing-library/react';
4
4
  import userEvent from '@testing-library/user-event';
5
5
  import {AvatarGroup} from './AvatarGroup';
6
6
  import {AvatarGroupOverflow} from './AvatarGroupOverflow';
7
- import {Avatar} from '../Avatar';
7
+ import {Avatar, AvatarStatusDot} from '../Avatar';
8
+ import {InternationalizationProvider} from '../i18n';
8
9
 
9
10
  describe('AvatarGroup', () => {
10
11
  it('renders all avatar children', () => {
@@ -21,6 +22,21 @@ describe('AvatarGroup', () => {
21
22
  expect(screen.getByLabelText('Charlie')).toBeInTheDocument();
22
23
  });
23
24
 
25
+ it('composes a labelled status into a grouped avatar accessible name (WCAG 4.1.2)', () => {
26
+ render(
27
+ <AvatarGroup>
28
+ <Avatar
29
+ name="Alice"
30
+ status={<AvatarStatusDot variant="success" label="Online" />}
31
+ />
32
+ <Avatar name="Bob" />
33
+ </AvatarGroup>,
34
+ );
35
+
36
+ expect(screen.getByLabelText('Alice, Online')).toBeInTheDocument();
37
+ expect(screen.getByLabelText('Bob')).toBeInTheDocument();
38
+ });
39
+
24
40
  it('renders with role="group" and default aria-label', () => {
25
41
  render(
26
42
  <AvatarGroup>
@@ -223,6 +239,150 @@ describe('AvatarGroupOverflow — hardening', () => {
223
239
  );
224
240
 
225
241
  expect(screen.getByText('+4912')).toBeInTheDocument();
226
- expect(screen.getByLabelText('4912 more')).toBeInTheDocument();
242
+ // The aria-label routes through the catalog's `{count, number}` ICU
243
+ // argument, so the en locale adds a grouping separator.
244
+ expect(screen.getByLabelText('4,912 more')).toBeInTheDocument();
245
+ });
246
+
247
+ it('localizes the overflow label through the i18n catalog', () => {
248
+ render(
249
+ <InternationalizationProvider
250
+ locale="fr"
251
+ overrides={{
252
+ fr: {'@astryx.avatarGroup.overflow': '{count, number} de plus'},
253
+ }}>
254
+ <AvatarGroup>
255
+ <Avatar name="Alice" />
256
+ <AvatarGroupOverflow count={3} />
257
+ </AvatarGroup>
258
+ </InternationalizationProvider>,
259
+ );
260
+
261
+ expect(screen.getByLabelText('3 de plus')).toBeInTheDocument();
262
+ });
263
+ });
264
+
265
+ describe('AvatarGroup — roving focus + keyboard hint', () => {
266
+ it('is a single tab stop over interactive avatars (one tabindex=0, rest -1)', () => {
267
+ render(
268
+ <AvatarGroup>
269
+ <Avatar name="Alice" href="/alice" />
270
+ <Avatar name="Bob" href="/bob" />
271
+ <Avatar name="Charlie" href="/charlie" />
272
+ </AvatarGroup>,
273
+ );
274
+
275
+ const alice = screen.getByRole('link', {name: 'Alice'});
276
+ const bob = screen.getByRole('link', {name: 'Bob'});
277
+ const charlie = screen.getByRole('link', {name: 'Charlie'});
278
+
279
+ expect(alice).toHaveAttribute('tabindex', '0');
280
+ expect(bob).toHaveAttribute('tabindex', '-1');
281
+ expect(charlie).toHaveAttribute('tabindex', '-1');
282
+ });
283
+
284
+ it('roves focus with ArrowRight/ArrowLeft across interactive avatars', async () => {
285
+ const user = userEvent.setup();
286
+ render(
287
+ <AvatarGroup>
288
+ <Avatar name="Alice" href="/alice" />
289
+ <Avatar name="Bob" onClick={() => {}} />
290
+ <Avatar name="Charlie" href="/charlie" />
291
+ </AvatarGroup>,
292
+ );
293
+
294
+ const alice = screen.getByRole('link', {name: 'Alice'});
295
+ const bob = screen.getByRole('button', {name: 'Bob'});
296
+ const charlie = screen.getByRole('link', {name: 'Charlie'});
297
+
298
+ alice.focus();
299
+ expect(alice).toHaveFocus();
300
+
301
+ await user.keyboard('{ArrowRight}');
302
+ expect(bob).toHaveFocus();
303
+ expect(bob).toHaveAttribute('tabindex', '0');
304
+ expect(alice).toHaveAttribute('tabindex', '-1');
305
+
306
+ await user.keyboard('{ArrowRight}');
307
+ expect(charlie).toHaveFocus();
308
+
309
+ await user.keyboard('{ArrowLeft}');
310
+ expect(bob).toHaveFocus();
311
+ });
312
+
313
+ it('includes the overflow button as the last roving item', async () => {
314
+ const user = userEvent.setup();
315
+ render(
316
+ <AvatarGroup>
317
+ <Avatar name="Alice" href="/alice" />
318
+ <AvatarGroupOverflow count={3} onClick={() => {}} />
319
+ </AvatarGroup>,
320
+ );
321
+
322
+ const alice = screen.getByRole('link', {name: 'Alice'});
323
+ const overflow = screen.getByRole('button', {name: '3 more'});
324
+ expect(overflow).toHaveAttribute('data-avatar-item');
325
+
326
+ alice.focus();
327
+ await user.keyboard('{ArrowRight}');
328
+ expect(overflow).toHaveFocus();
329
+ });
330
+
331
+ it('does NOT rove over a non-avatar button in a status slot', async () => {
332
+ const user = userEvent.setup();
333
+ render(
334
+ <AvatarGroup>
335
+ <Avatar
336
+ name="Alice"
337
+ href="/alice"
338
+ status={<button type="button">badge</button>}
339
+ />
340
+ <Avatar name="Bob" href="/bob" />
341
+ </AvatarGroup>,
342
+ );
343
+
344
+ const alice = screen.getByRole('link', {name: 'Alice'});
345
+ const bob = screen.getByRole('link', {name: 'Bob'});
346
+ const statusButton = screen.getByRole('button', {name: 'badge'});
347
+
348
+ // The status button carries no data-avatar-item marker.
349
+ expect(statusButton).not.toHaveAttribute('data-avatar-item');
350
+
351
+ alice.focus();
352
+ await user.keyboard('{ArrowRight}');
353
+ // Arrow moves to the next avatar, skipping the nested status button.
354
+ expect(bob).toHaveFocus();
355
+ expect(statusButton).not.toHaveFocus();
356
+ });
357
+
358
+ it('attaches an aria-describedby keyboard hint when interactive children exist', () => {
359
+ render(
360
+ <AvatarGroup>
361
+ <Avatar name="Alice" href="/alice" />
362
+ <Avatar name="Bob" href="/bob" />
363
+ </AvatarGroup>,
364
+ );
365
+
366
+ const group = screen.getByRole('group');
367
+ const describedBy = group.getAttribute('aria-describedby');
368
+ expect(describedBy).toBeTruthy();
369
+ const hint = document.getElementById(describedBy!);
370
+ expect(hint).not.toBeNull();
371
+ expect(hint).toHaveTextContent('Use arrow keys to move between avatars');
372
+ });
373
+
374
+ it('a purely static group has no tab stop and no keyboard hint', () => {
375
+ render(
376
+ <AvatarGroup>
377
+ <Avatar name="Alice" data-testid="alice" />
378
+ <Avatar name="Bob" data-testid="bob" />
379
+ </AvatarGroup>,
380
+ );
381
+
382
+ const group = screen.getByRole('group');
383
+ expect(group).not.toHaveAttribute('aria-describedby');
384
+ // Static avatars are not focusable — no roving tabindex stamped.
385
+ expect(screen.getByTestId('alice')).not.toHaveAttribute('tabindex');
386
+ expect(screen.getByTestId('bob')).not.toHaveAttribute('tabindex');
227
387
  });
228
388
  });
@@ -11,6 +11,12 @@
11
11
  * one AvatarGroupOverflow). The group provides overlap styling via
12
12
  * context — no child introspection needed.
13
13
  *
14
+ * When the group contains interactive avatars (rendered as links/buttons) or an
15
+ * interactive AvatarGroupOverflow, it becomes a single Tab stop with roving
16
+ * arrow-key focus (via useListFocus with hasRovingTabIndex) and exposes a
17
+ * screen-reader keyboard hint via aria-describedby. A purely static facepile
18
+ * gets neither — it stays a plain non-focusable group.
19
+ *
14
20
  * SYNC: When modified, update these files to stay in sync:
15
21
  * - /packages/core/src/AvatarGroup/AvatarGroup.doc.mjs (props table, features)
16
22
  * - /packages/core/src/AvatarGroup/index.ts (exports if types change)
@@ -18,14 +24,18 @@
18
24
  * - /packages/cli/templates/blocks/components/AvatarGroup/ (showcase blocks)
19
25
  */
20
26
 
21
- import {useMemo, type ReactNode} from 'react';
27
+ import {useId, useMemo, useState, type ReactNode} from 'react';
22
28
  import type {BaseProps} from '../BaseProps';
23
29
  import {resolveSize, type AvatarSize} from '../Avatar';
24
30
  import * as stylex from '@stylexjs/stylex';
25
- import {mergeProps} from '../utils';
31
+ import {mergeProps, mergeRefs} from '../utils';
32
+ import {composeEventHandlers} from '../utils/composeEventHandlers';
26
33
  import {AvatarGroupContext} from './AvatarGroupContext';
27
34
  import {themeProps} from '../utils/themeProps';
28
35
  import {useTranslator} from '../i18n';
36
+ import {useListFocus} from '../hooks/useListFocus';
37
+ import {useIsomorphicLayoutEffect} from '../hooks/useIsomorphicLayoutEffect';
38
+ import {VisuallyHidden} from '../VisuallyHidden';
29
39
 
30
40
  const OVERLAP_RATIO = 0.25;
31
41
 
@@ -78,6 +88,9 @@ export function AvatarGroup({
78
88
  size = 'md',
79
89
  'data-testid': testId,
80
90
  'aria-label': ariaLabelFromProps,
91
+ 'aria-describedby': ariaDescribedByFromProps,
92
+ onKeyDown,
93
+ onFocus,
81
94
  xstyle,
82
95
  className,
83
96
  style,
@@ -94,14 +107,51 @@ export function AvatarGroup({
94
107
  [size, overlap, numericSize],
95
108
  );
96
109
 
110
+ // The keyboard hint and roving tab stop only make sense once the group has
111
+ // interactive children. Detect their presence (and the writing direction for
112
+ // RTL-correct arrow navigation) from the rendered DOM after commit, matching
113
+ // how the codebase reads direction elsewhere (`getComputedStyle(el).direction`).
114
+ const [hasInteractiveItems, setHasInteractiveItems] = useState(false);
115
+ const [isRtl, setIsRtl] = useState(false);
116
+
117
+ // Single tab stop + roving arrow focus over the group's interactive items.
118
+ // `itemSelector` targets the shared `[data-avatar-item]` marker stamped on
119
+ // interactive avatars (their rendered <a>/<button>) and on the overflow
120
+ // button — NOT a tag/role selector — so roving never catches a nested button
121
+ // inside a custom status/badge slot (O5).
122
+ const {listRef, handleKeyDown, handleFocus} = useListFocus<HTMLDivElement>({
123
+ itemSelector: '[data-avatar-item]',
124
+ orientation: 'horizontal',
125
+ hasRovingTabIndex: true,
126
+ isRtl,
127
+ });
128
+
129
+ useIsomorphicLayoutEffect(() => {
130
+ const root = listRef.current;
131
+ if (!root) {
132
+ return;
133
+ }
134
+ setHasInteractiveItems(root.querySelector('[data-avatar-item]') != null);
135
+ setIsRtl(getComputedStyle(root).direction === 'rtl');
136
+ });
137
+
138
+ const hintId = useId();
139
+ const describedBy =
140
+ [ariaDescribedByFromProps, hasInteractiveItems ? hintId : null]
141
+ .filter(Boolean)
142
+ .join(' ') || undefined;
143
+
97
144
  return (
98
145
  <AvatarGroupContext value={contextValue}>
99
146
  <div
100
147
  {...props}
101
- ref={ref}
148
+ ref={mergeRefs(ref, listRef)}
102
149
  role="group"
103
150
  aria-label={ariaLabel}
151
+ aria-describedby={describedBy}
104
152
  data-testid={testId}
153
+ onKeyDown={composeEventHandlers(onKeyDown, handleKeyDown)}
154
+ onFocus={composeEventHandlers(onFocus, handleFocus)}
105
155
  {...mergeProps(
106
156
  themeProps('avatar-group', {size}),
107
157
  stylex.props(styles.root, xstyle),
@@ -109,6 +159,11 @@ export function AvatarGroup({
109
159
  style,
110
160
  )}>
111
161
  {children}
162
+ {hasInteractiveItems && (
163
+ <VisuallyHidden id={hintId}>
164
+ {t('@astryx.avatarGroup.keyboardHint')}
165
+ </VisuallyHidden>
166
+ )}
112
167
  </div>
113
168
  </AvatarGroupContext>
114
169
  );
@@ -3,7 +3,7 @@
3
3
 
4
4
  /**
5
5
  * @file AvatarGroupOverflow.tsx
6
- * @input Uses React, StyleX, AvatarGroupContext
6
+ * @input Uses React, StyleX, AvatarGroupContext, i18n (useTranslator)
7
7
  * @output Exports AvatarGroupOverflow for overflow indicator
8
8
  * @position Slot component used inside AvatarGroup
9
9
  *
@@ -26,6 +26,7 @@ import {mergeProps} from '../utils';
26
26
  import {useAvatarGroup} from './AvatarGroupContext';
27
27
  import type {BaseProps} from '../BaseProps';
28
28
  import {themeProps} from '../utils/themeProps';
29
+ import {useTranslator} from '../i18n';
29
30
 
30
31
  const BORDER_WIDTH = 2;
31
32
  const OVERFLOW_FONT_RATIO = 0.35;
@@ -150,11 +151,12 @@ export function AvatarGroupOverflow({
150
151
  style,
151
152
  ...rest
152
153
  }: AvatarGroupOverflowProps): ReactNode {
154
+ const t = useTranslator();
153
155
  const group = useAvatarGroup();
154
156
  const numericSize = group?.numericSize ?? 36;
155
157
  const overlap = group?.overlap ?? 0;
156
158
 
157
- const label = `${count} more`;
159
+ const label = t('@astryx.avatarGroup.overflow', {count});
158
160
  const content = children ?? `+${count}`;
159
161
 
160
162
  if (onClick) {
@@ -165,6 +167,7 @@ export function AvatarGroupOverflow({
165
167
  onClick={onClick}
166
168
  {...rest}
167
169
  aria-label={label}
170
+ data-avatar-item=""
168
171
  {...mergeProps(
169
172
  themeProps('avatar-group-overflow'),
170
173
  stylex.props(
@@ -294,14 +294,14 @@ const styles = stylex.create({
294
294
  backgroundColor: colorVars['--color-background-card'],
295
295
  paddingBlock: spacingVars['--spacing-3'],
296
296
  paddingInline: spacingVars['--spacing-4'],
297
- borderLeftWidth: borderVars['--border-width'],
298
- borderRightWidth: borderVars['--border-width'],
297
+ borderInlineStartWidth: borderVars['--border-width'],
298
+ borderInlineEndWidth: borderVars['--border-width'],
299
299
  borderBottomWidth: borderVars['--border-width'],
300
- borderLeftStyle: 'solid',
301
- borderRightStyle: 'solid',
300
+ borderInlineStartStyle: 'solid',
301
+ borderInlineEndStyle: 'solid',
302
302
  borderBottomStyle: 'solid',
303
- borderLeftColor: colorVars['--color-border'],
304
- borderRightColor: colorVars['--color-border'],
303
+ borderInlineStartColor: colorVars['--color-border'],
304
+ borderInlineEndColor: colorVars['--color-border'],
305
305
  borderBottomColor: colorVars['--color-border'],
306
306
  },
307
307
  contentAreaCard: {
@@ -59,6 +59,7 @@ import {
59
59
  import {
60
60
  MENU_ITEM_ROLES,
61
61
  MENU_ITEM_SELECTOR,
62
+ MENU_BOUNDARY_SELECTOR,
62
63
  } from '../DropdownMenu/menuItemRoles';
63
64
  import type {DropdownMenuOption} from '../DropdownMenu/DropdownMenu';
64
65
 
@@ -515,21 +516,15 @@ function BreadcrumbMenuTrigger({
515
516
  handleKeyDown: listNavKeyDown,
516
517
  focusFirst,
517
518
  focusItem,
519
+ ownsEvent,
520
+ getItems: getMenuItems,
518
521
  } = useListFocus<HTMLDivElement>({
519
522
  itemSelector: MENU_ITEM_SELECTOR,
523
+ boundarySelector: MENU_BOUNDARY_SELECTOR,
520
524
  wrap: false,
521
525
  onEscape: closeMenu,
522
526
  });
523
527
 
524
- const getMenuItems = useCallback(
525
- (): HTMLElement[] =>
526
- listRef.current
527
- ? Array.from(
528
- listRef.current.querySelectorAll<HTMLElement>(MENU_ITEM_SELECTOR),
529
- )
530
- : [],
531
- [listRef],
532
- );
533
528
  const typeahead = useTypeahead({
534
529
  getItemLabels: () => getMenuItems().map(el => el.textContent),
535
530
  onMatch: focusItem,
@@ -542,6 +537,11 @@ function BreadcrumbMenuTrigger({
542
537
 
543
538
  const listKeyDown = useCallback(
544
539
  (e: React.KeyboardEvent) => {
540
+ // A submenu flyout renders inline inside this menu; its key events bubble
541
+ // up here. Let that level own them — only handle events from this level.
542
+ if (!ownsEvent(e)) {
543
+ return;
544
+ }
545
545
  if (e.key === 'Enter' || e.key === ' ') {
546
546
  e.preventDefault();
547
547
  const focused = document.activeElement as HTMLElement | null;
@@ -564,7 +564,7 @@ function BreadcrumbMenuTrigger({
564
564
  }
565
565
  listNavKeyDown(e);
566
566
  },
567
- [listNavKeyDown, closeMenu, typeahead],
567
+ [listNavKeyDown, closeMenu, typeahead, ownsEvent],
568
568
  );
569
569
 
570
570
  const openAndFocus = useCallback(() => {
@@ -519,7 +519,7 @@ describe('BreadcrumbItem menu', () => {
519
519
  <BreadcrumbItem
520
520
  menu={
521
521
  <BreadcrumbMenuRadioGroup
522
- aria-label="Sort by"
522
+ label="Sort by"
523
523
  value="name"
524
524
  onChange={onChange}>
525
525
  <BreadcrumbMenuRadioItem value="name" label="Name" />
@@ -589,6 +589,57 @@ describe('BreadcrumbItem menu', () => {
589
589
  ).toHaveFocus();
590
590
  });
591
591
 
592
+ it('renders a submenu from a nested items array and keyboard-reaches an item after it', async () => {
593
+ // Breadcrumb menus reuse the DropdownMenu item pipeline, so a nested
594
+ // `items` array becomes a submenu. The inline flyout must not pollute the
595
+ // breadcrumb menu's roving order — an item after the submenu row stays
596
+ // keyboard-reachable.
597
+ const onDelete = vi.fn();
598
+ render(
599
+ <Breadcrumbs>
600
+ <BreadcrumbItem
601
+ menu={[
602
+ {label: 'Rename', onClick: vi.fn()},
603
+ {
604
+ label: 'Move to',
605
+ items: [
606
+ {label: 'Folder A', onClick: vi.fn()},
607
+ {label: 'Folder B', onClick: vi.fn()},
608
+ ],
609
+ },
610
+ {type: 'divider'},
611
+ {label: 'Delete', onClick: onDelete},
612
+ ]}>
613
+ Teams
614
+ </BreadcrumbItem>
615
+ <BreadcrumbItem isCurrent>Overview</BreadcrumbItem>
616
+ </Breadcrumbs>,
617
+ );
618
+ const trigger = screen.getByRole('button', {name: 'Teams'});
619
+ trigger.focus();
620
+ fireEvent.keyDown(trigger, {key: 'ArrowDown'});
621
+ // Two role="menu" exist (the breadcrumb menu + the inline submenu flyout);
622
+ // the breadcrumb menu is the first in DOM order.
623
+ const menu = screen.getAllByRole('menu', {hidden: true})[0];
624
+ const submenuTrigger = screen.getByRole('menuitem', {
625
+ name: /Move to/,
626
+ hidden: true,
627
+ });
628
+ expect(submenuTrigger).toHaveAttribute('aria-haspopup', 'menu');
629
+ await waitFor(() => {
630
+ expect(
631
+ screen.getByRole('menuitem', {name: 'Rename', hidden: true}),
632
+ ).toHaveFocus();
633
+ });
634
+ // Rename → Move to → Delete, one step per press.
635
+ fireEvent.keyDown(menu, {key: 'ArrowDown'});
636
+ expect(submenuTrigger).toHaveFocus();
637
+ fireEvent.keyDown(menu, {key: 'ArrowDown'});
638
+ expect(
639
+ screen.getByRole('menuitem', {name: 'Delete', hidden: true}),
640
+ ).toHaveFocus();
641
+ });
642
+
592
643
  it('allows menu together with isCurrent (both aria-current and aria-haspopup)', () => {
593
644
  render(
594
645
  <Breadcrumbs>
@@ -35,6 +35,10 @@ export {
35
35
  DropdownMenuRadioItem as BreadcrumbMenuRadioItem,
36
36
  type DropdownMenuRadioItemProps as BreadcrumbMenuRadioItemProps,
37
37
  } from '../DropdownMenu/DropdownMenuRadioItem';
38
+ export {
39
+ DropdownMenuSubMenu as BreadcrumbMenuSubMenu,
40
+ type DropdownMenuSubMenuProps as BreadcrumbMenuSubMenuProps,
41
+ } from '../DropdownMenu/DropdownMenuSubMenu';
38
42
  export type {
39
43
  DropdownMenuOption as BreadcrumbMenuOption,
40
44
  DropdownMenuItemData as BreadcrumbMenuItemData,
@@ -163,8 +163,6 @@ export const docs = {
163
163
  ],
164
164
  vars: [
165
165
  {name: '--_button-radius', description: 'Border radius', default: 'var(--radius-element)', private: true},
166
- {name: '--button-press-scale', description: 'Active press transform', default: 'scale(0.98)'},
167
- {name: '--button-disabled-opacity', description: 'Opacity when disabled', default: '0.5'},
168
166
  {name: '--button-focus-offset', description: 'Focus ring outline offset', default: '3px'},
169
167
  {name: '--button-icon-only-aspect', description: 'Aspect ratio for icon-only buttons', default: '1 / 1'},
170
168
  ],
@@ -247,8 +245,6 @@ export const docsZh = {
247
245
  ],
248
246
  vars: [
249
247
  {name: '--_button-radius', description: '圆角半径', default: 'var(--radius-element)', private: true},
250
- {name: '--button-press-scale', description: '按下时的变换', default: 'scale(0.98)'},
251
- {name: '--button-disabled-opacity', description: '禁用时的不透明度', default: '0.5'},
252
248
  {name: '--button-focus-offset', description: '焦点环轮廓偏移', default: '3px'},
253
249
  {name: '--button-icon-only-aspect', description: '纯图标按钮的宽高比', default: '1 / 1'},
254
250
  ],
@@ -14,6 +14,7 @@ import {render, screen, fireEvent, act} from '@testing-library/react';
14
14
  import userEvent from '@testing-library/user-event';
15
15
  import {Button} from './Button';
16
16
  import {Badge} from '../Badge/Badge';
17
+ import {InternationalizationProvider} from '../i18n';
17
18
 
18
19
  describe('Button', () => {
19
20
  it('renders label as visible text', () => {
@@ -446,6 +447,21 @@ describe('Button', () => {
446
447
  expect(liveRegion).toHaveTextContent('Loading');
447
448
  });
448
449
 
450
+ it('localizes the loading announcement through the i18n catalog', () => {
451
+ render(
452
+ <InternationalizationProvider
453
+ locale="fr"
454
+ overrides={{fr: {'@astryx.button.loading': 'Chargement'}}}>
455
+ <Button label="Submit" isLoading />
456
+ </InternationalizationProvider>,
457
+ );
458
+ const button = screen.getByRole('button');
459
+ // The Spinner also has role="status", so grab the live region explicitly.
460
+ const regions = button.querySelectorAll('[role="status"]');
461
+ const liveRegion = regions[regions.length - 1];
462
+ expect(liveRegion).toHaveTextContent('Chargement');
463
+ });
464
+
449
465
  describe('elevation', () => {
450
466
  it('renders a distinct class for each elevation level', () => {
451
467
  const classFor = (elevation: 'none' | 'low' | 'med' | 'high') => {
@@ -4,7 +4,7 @@
4
4
 
5
5
  /**
6
6
  * @file Button.tsx
7
- * @input Uses React, ButtonHTMLAttributes, ReactNode
7
+ * @input Uses React, ButtonHTMLAttributes, ReactNode, i18n (useTranslator)
8
8
  * @output Exports Button component, ButtonProps, ButtonVariant types
9
9
  * @position Core implementation; consumed by index.ts, tested by Button.test.tsx
10
10
  *
@@ -45,6 +45,7 @@ import {mergeProps, mergeRefs} from '../utils';
45
45
  import {useLinkComponent} from '../Link/useLinkComponent';
46
46
  import type {LinkComponentType} from '../Link/types';
47
47
  import {themeProps} from '../utils/themeProps';
48
+ import {useTranslator} from '../i18n';
48
49
 
49
50
  /**
50
51
  * Base button styles
@@ -449,8 +450,8 @@ const loadingStyles = stylex.create({
449
450
  spinnerOverlay: {
450
451
  position: 'absolute',
451
452
  top: 0,
452
- left: 0,
453
- right: 0,
453
+ insetInlineStart: 0,
454
+ insetInlineEnd: 0,
454
455
  bottom: 0,
455
456
  display: 'grid',
456
457
  placeItems: 'center',
@@ -611,6 +612,7 @@ export function Button({
611
612
  ref,
612
613
  ...props
613
614
  }: ButtonProps): ReactNode {
615
+ const t = useTranslator();
614
616
  const size = useSize(sizeProp, 'md');
615
617
  const buttonGroup = useButtonGroup();
616
618
 
@@ -760,7 +762,7 @@ export function Button({
760
762
  </span>
761
763
  {/* Live region for loading state announcements */}
762
764
  <VisuallyHidden role="status" aria-live="polite">
763
- {isLoadingState ? 'Loading' : ''}
765
+ {isLoadingState ? t('@astryx.button.loading') : ''}
764
766
  </VisuallyHidden>
765
767
  </>
766
768
  );