@astryxdesign/core 0.1.8 → 0.1.9-canary.1887ee2

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 (610) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/dist/AppShell/AppShell.d.ts.map +1 -1
  3. package/dist/AppShell/AppShell.js +6 -2
  4. package/dist/AppShell/AppShellMobileContext.d.ts +8 -0
  5. package/dist/AppShell/AppShellMobileContext.d.ts.map +1 -1
  6. package/dist/Avatar/Avatar.d.ts +53 -3
  7. package/dist/Avatar/Avatar.d.ts.map +1 -1
  8. package/dist/Avatar/Avatar.js +203 -49
  9. package/dist/Avatar/AvatarStatusDot.d.ts +33 -5
  10. package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
  11. package/dist/Avatar/AvatarStatusDot.js +144 -19
  12. package/dist/AvatarGroup/AvatarGroup.d.ts +7 -1
  13. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  14. package/dist/AvatarGroup/AvatarGroup.js +58 -6
  15. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  16. package/dist/AvatarGroup/AvatarGroupOverflow.js +1 -0
  17. package/dist/Banner/Banner.d.ts +9 -1
  18. package/dist/Banner/Banner.d.ts.map +1 -1
  19. package/dist/Banner/Banner.js +33 -4
  20. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +31 -2
  21. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  22. package/dist/Breadcrumbs/BreadcrumbItem.js +208 -6
  23. package/dist/Breadcrumbs/index.d.ts +5 -0
  24. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  25. package/dist/Breadcrumbs/index.js +9 -1
  26. package/dist/Button/Button.d.ts +8 -2
  27. package/dist/Button/Button.d.ts.map +1 -1
  28. package/dist/Button/Button.js +33 -4
  29. package/dist/ButtonGroup/ButtonGroup.d.ts +9 -1
  30. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  31. package/dist/ButtonGroup/ButtonGroup.js +30 -1
  32. package/dist/Calendar/Calendar.d.ts.map +1 -1
  33. package/dist/Calendar/Calendar.js +30 -2
  34. package/dist/Calendar/dayCellUtils.js +1 -1
  35. package/dist/Card/Card.d.ts +8 -2
  36. package/dist/Card/Card.d.ts.map +1 -1
  37. package/dist/Card/Card.js +28 -2
  38. package/dist/Carousel/Carousel.d.ts +4 -1
  39. package/dist/Carousel/Carousel.d.ts.map +1 -1
  40. package/dist/Carousel/Carousel.js +24 -4
  41. package/dist/Chat/ChatComposer.d.ts +8 -0
  42. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  43. package/dist/Chat/ChatComposer.js +46 -7
  44. package/dist/Chat/ChatComposerDrawer.d.ts +2 -0
  45. package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
  46. package/dist/Chat/ChatComposerDrawer.js +11 -1
  47. package/dist/Chat/ChatComposerInput.d.ts +20 -3
  48. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  49. package/dist/Chat/ChatComposerInput.js +36 -4
  50. package/dist/Chat/ChatContext.d.ts +33 -0
  51. package/dist/Chat/ChatContext.d.ts.map +1 -1
  52. package/dist/Chat/ChatContext.js +16 -2
  53. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  54. package/dist/Chat/ChatDictationButton.js +1 -1
  55. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  56. package/dist/Chat/ChatLayout.js +4 -4
  57. package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
  58. package/dist/Chat/ChatLayoutScrollButton.js +4 -4
  59. package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
  60. package/dist/Chat/ChatSendButton.d.ts.map +1 -1
  61. package/dist/Chat/ChatSendButton.js +7 -1
  62. package/dist/Chat/ChatToolCalls.d.ts +5 -1
  63. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  64. package/dist/Chat/ChatToolCalls.js +18 -9
  65. package/dist/Chat/index.d.ts +2 -1
  66. package/dist/Chat/index.d.ts.map +1 -1
  67. package/dist/Chat/index.js +1 -1
  68. package/dist/Chat/useChatStreamScroll.d.ts +2 -1
  69. package/dist/Chat/useChatStreamScroll.d.ts.map +1 -1
  70. package/dist/Chat/useChatStreamScroll.js +22 -8
  71. package/dist/Chat/useSpeechRecognition.d.ts +0 -1
  72. package/dist/Chat/useSpeechRecognition.d.ts.map +1 -1
  73. package/dist/Chat/useSpeechRecognition.js +1 -1
  74. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  75. package/dist/Chat/useTriggerMenu.js +2 -2
  76. package/dist/CheckboxInput/CheckboxInput.js +1 -1
  77. package/dist/CheckboxList/CheckboxListItem.d.ts +20 -1
  78. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  79. package/dist/CheckboxList/CheckboxListItem.js +11 -1
  80. package/dist/Citation/Citation.d.ts +22 -1
  81. package/dist/Citation/Citation.d.ts.map +1 -1
  82. package/dist/Citation/Citation.js +20 -6
  83. package/dist/ClickableCard/ClickableCard.d.ts +9 -2
  84. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  85. package/dist/ClickableCard/ClickableCard.js +5 -2
  86. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  87. package/dist/CodeBlock/CodeBlock.js +9 -2
  88. package/dist/CommandPalette/CommandPalette.d.ts +1 -1
  89. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  90. package/dist/CommandPalette/CommandPalette.js +3 -1
  91. package/dist/CommandPalette/CommandPaletteGroup.d.ts.map +1 -1
  92. package/dist/CommandPalette/CommandPaletteGroup.js +9 -2
  93. package/dist/CommandPalette/CommandPaletteInput.d.ts +7 -1
  94. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  95. package/dist/CommandPalette/CommandPaletteInput.js +6 -1
  96. package/dist/CommandPalette/CommandPaletteItem.js +1 -1
  97. package/dist/DateInput/DateInput.d.ts +55 -2
  98. package/dist/DateInput/DateInput.d.ts.map +1 -1
  99. package/dist/DateInput/DateInput.js +28 -2
  100. package/dist/DateInput/index.d.ts +1 -1
  101. package/dist/DateInput/index.d.ts.map +1 -1
  102. package/dist/DateRangeInput/DateRangeInput.d.ts +9 -2
  103. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  104. package/dist/DateRangeInput/DateRangeInput.js +3 -1
  105. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  106. package/dist/DateTimeInput/DateTimeInput.js +18 -3
  107. package/dist/Dialog/Dialog.d.ts +3 -0
  108. package/dist/Dialog/Dialog.d.ts.map +1 -1
  109. package/dist/Dialog/Dialog.js +57 -6
  110. package/dist/Dialog/DialogContext.d.ts +7 -0
  111. package/dist/Dialog/DialogContext.d.ts.map +1 -1
  112. package/dist/Dialog/DialogContext.js +2 -2
  113. package/dist/Dialog/DialogHeader.d.ts +5 -2
  114. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  115. package/dist/Dialog/DialogHeader.js +6 -1
  116. package/dist/Divider/Divider.js +1 -1
  117. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +1 -2
  118. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  119. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +1 -2
  120. package/dist/DropdownMenu/DropdownMenuItem.js +1 -1
  121. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +1 -2
  122. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
  123. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +1 -2
  124. package/dist/Field/Field.d.ts.map +1 -1
  125. package/dist/Field/Field.js +2 -3
  126. package/dist/Field/FieldLabel.d.ts +1 -1
  127. package/dist/Field/FieldLabel.d.ts.map +1 -1
  128. package/dist/Field/FieldLabel.js +43 -16
  129. package/dist/FieldStatus/FieldStatus.d.ts +15 -0
  130. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  131. package/dist/FieldStatus/FieldStatus.js +58 -6
  132. package/dist/FileInput/FileInput.d.ts +9 -2
  133. package/dist/FileInput/FileInput.d.ts.map +1 -1
  134. package/dist/FileInput/FileInput.js +42 -20
  135. package/dist/HoverCard/HoverCard.d.ts +9 -1
  136. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  137. package/dist/HoverCard/HoverCard.js +2 -0
  138. package/dist/HoverCard/useHoverCard.d.ts +8 -0
  139. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  140. package/dist/HoverCard/useHoverCard.js +7 -2
  141. package/dist/Icon/Icon.d.ts +7 -2
  142. package/dist/Icon/Icon.d.ts.map +1 -1
  143. package/dist/Item/Item.d.ts +6 -1
  144. package/dist/Item/Item.d.ts.map +1 -1
  145. package/dist/Item/Item.js +22 -1
  146. package/dist/Layer/useLayer.d.ts +6 -0
  147. package/dist/Layer/useLayer.d.ts.map +1 -1
  148. package/dist/Layer/useLayer.js +2 -0
  149. package/dist/Lightbox/Lightbox.js +4 -4
  150. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  151. package/dist/MobileNav/MobileNav.js +6 -1
  152. package/dist/MobileNav/MobileNavToggle.d.ts.map +1 -1
  153. package/dist/MobileNav/MobileNavToggle.js +4 -0
  154. package/dist/MultiSelector/MultiSelector.d.ts +9 -1
  155. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  156. package/dist/MultiSelector/MultiSelector.js +41 -12
  157. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  158. package/dist/MultiSelector/hooks.js +16 -0
  159. package/dist/NavMenu/NavHeadingMenuItem.js +1 -1
  160. package/dist/NumberInput/NumberInput.d.ts +9 -2
  161. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  162. package/dist/NumberInput/NumberInput.js +8 -1
  163. package/dist/Outline/Outline.d.ts +0 -1
  164. package/dist/Outline/Outline.d.ts.map +1 -1
  165. package/dist/Outline/Outline.js +0 -1
  166. package/dist/OverflowList/OverflowList.d.ts +20 -3
  167. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  168. package/dist/OverflowList/OverflowList.js +29 -4
  169. package/dist/Pagination/Pagination.d.ts.map +1 -1
  170. package/dist/Pagination/Pagination.js +11 -3
  171. package/dist/Popover/Popover.d.ts.map +1 -1
  172. package/dist/Popover/Popover.js +3 -2
  173. package/dist/Popover/usePopover.d.ts.map +1 -1
  174. package/dist/Popover/usePopover.js +3 -9
  175. package/dist/PowerSearch/PowerSearch.d.ts +9 -2
  176. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  177. package/dist/PowerSearch/PowerSearch.js +2 -0
  178. package/dist/PowerSearch/PowerSearchEditPopover.js +1 -1
  179. package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
  180. package/dist/SelectableCard/SelectableCard.d.ts +9 -2
  181. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  182. package/dist/SelectableCard/SelectableCard.js +16 -13
  183. package/dist/Selector/Selector.d.ts +8 -0
  184. package/dist/Selector/Selector.d.ts.map +1 -1
  185. package/dist/Selector/Selector.js +7 -3
  186. package/dist/Selector/hooks.d.ts.map +1 -1
  187. package/dist/Selector/hooks.js +16 -0
  188. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  189. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  190. package/dist/SideNav/SideNavHeading.js +21 -8
  191. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  192. package/dist/Slider/Slider.d.ts.map +1 -1
  193. package/dist/Slider/Slider.js +19 -2
  194. package/dist/Stack/stack.stylex.d.ts +9 -9
  195. package/dist/TabList/Tab.js +2 -2
  196. package/dist/TabList/TabMenu.js +1 -1
  197. package/dist/Table/BaseTable.d.ts.map +1 -1
  198. package/dist/Table/BaseTable.js +3 -2
  199. package/dist/Table/TableCell.js +3 -3
  200. package/dist/Table/TableHeaderCell.js +3 -3
  201. package/dist/Table/index.d.ts +2 -0
  202. package/dist/Table/index.d.ts.map +1 -1
  203. package/dist/Table/index.js +1 -0
  204. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  205. package/dist/Table/plugins/rowStatus/index.d.ts +3 -0
  206. package/dist/Table/plugins/rowStatus/index.d.ts.map +1 -0
  207. package/dist/Table/plugins/rowStatus/index.js +3 -0
  208. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts +60 -0
  209. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -0
  210. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +145 -0
  211. package/dist/Table/plugins/selection/useTableSelection.d.ts +13 -0
  212. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  213. package/dist/Table/plugins/selection/useTableSelection.js +4 -1
  214. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +1 -1
  215. package/dist/Table/plugins/tree/useTableTreeData.d.ts +16 -0
  216. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  217. package/dist/Table/plugins/tree/useTableTreeData.js +87 -0
  218. package/dist/Table/plugins/tree/useTableTreeState.d.ts +6 -0
  219. package/dist/Table/plugins/tree/useTableTreeState.d.ts.map +1 -1
  220. package/dist/Table/plugins/tree/useTableTreeState.js +22 -1
  221. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  222. package/dist/Table/useBaseTablePlugins.js +5 -3
  223. package/dist/TextArea/TextArea.d.ts +9 -1
  224. package/dist/TextArea/TextArea.d.ts.map +1 -1
  225. package/dist/TextArea/TextArea.js +2 -0
  226. package/dist/TextInput/TextInput.d.ts +9 -2
  227. package/dist/TextInput/TextInput.d.ts.map +1 -1
  228. package/dist/TextInput/TextInput.js +6 -1
  229. package/dist/Thumbnail/Thumbnail.d.ts +26 -5
  230. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  231. package/dist/Thumbnail/Thumbnail.js +97 -54
  232. package/dist/Thumbnail/thumbnail.markers.stylex.d.ts +9 -0
  233. package/dist/Thumbnail/thumbnail.markers.stylex.d.ts.map +1 -0
  234. package/dist/Thumbnail/thumbnail.markers.stylex.js +14 -0
  235. package/dist/TimeInput/TimeInput.d.ts +9 -2
  236. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  237. package/dist/TimeInput/TimeInput.js +17 -4
  238. package/dist/Timestamp/Timestamp.d.ts +3 -1
  239. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  240. package/dist/Timestamp/Timestamp.js +8 -6
  241. package/dist/Toast/useToast.d.ts.map +1 -1
  242. package/dist/Toast/useToast.js +2 -5
  243. package/dist/ToggleButton/ToggleButtonGroup.d.ts +0 -1
  244. package/dist/ToggleButton/ToggleButtonGroup.d.ts.map +1 -1
  245. package/dist/ToggleButton/ToggleButtonGroup.js +1 -1
  246. package/dist/Tokenizer/Tokenizer.d.ts +10 -3
  247. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  248. package/dist/Tokenizer/Tokenizer.js +18 -4
  249. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  250. package/dist/TopNav/TopNavHeading.js +18 -8
  251. package/dist/TopNav/TopNavMegaMenu.js +10 -4
  252. package/dist/TopNav/TopNavMegaMenuFeaturedCard.d.ts +9 -1
  253. package/dist/TopNav/TopNavMegaMenuFeaturedCard.d.ts.map +1 -1
  254. package/dist/TopNav/TopNavMegaMenuFeaturedCard.js +8 -1
  255. package/dist/TopNav/TopNavMenu.d.ts +2 -1
  256. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  257. package/dist/TopNav/TopNavMenu.js +68 -6
  258. package/dist/TreeList/TreeList.d.ts +12 -3
  259. package/dist/TreeList/TreeList.d.ts.map +1 -1
  260. package/dist/TreeList/TreeList.js +2 -0
  261. package/dist/TreeList/TreeListBranches.d.ts +5 -0
  262. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  263. package/dist/TreeList/TreeListBranches.js +10 -4
  264. package/dist/TreeList/TreeListItem.d.ts +8 -2
  265. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  266. package/dist/TreeList/TreeListItem.js +28 -8
  267. package/dist/TreeList/TreeListTypes.d.ts +25 -1
  268. package/dist/TreeList/TreeListTypes.d.ts.map +1 -1
  269. package/dist/TreeList/index.d.ts +2 -2
  270. package/dist/TreeList/index.d.ts.map +1 -1
  271. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  272. package/dist/Typeahead/BaseTypeahead.js +20 -11
  273. package/dist/Typeahead/Typeahead.d.ts +9 -2
  274. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  275. package/dist/Typeahead/Typeahead.js +2 -0
  276. package/dist/astryx.css +41 -31
  277. package/dist/astryx.umd.js +51 -51
  278. package/dist/astryx.umd.js.map +4 -4
  279. package/dist/authoring/doc.d.ts.map +1 -1
  280. package/dist/docs-types.d.ts +8 -0
  281. package/dist/docs-types.d.ts.map +1 -1
  282. package/dist/hooks/computeOverflow.d.ts +47 -0
  283. package/dist/hooks/computeOverflow.d.ts.map +1 -0
  284. package/dist/hooks/computeOverflow.js +213 -0
  285. package/dist/hooks/index.d.ts +1 -0
  286. package/dist/hooks/index.d.ts.map +1 -1
  287. package/dist/hooks/index.js +2 -1
  288. package/dist/hooks/isRtlElement.d.ts +18 -0
  289. package/dist/hooks/isRtlElement.d.ts.map +1 -0
  290. package/dist/hooks/isRtlElement.js +27 -0
  291. package/dist/hooks/useDevWarning.d.ts +18 -0
  292. package/dist/hooks/useDevWarning.d.ts.map +1 -0
  293. package/dist/hooks/useDevWarning.js +49 -0
  294. package/dist/hooks/useGridFocus.d.ts +6 -1
  295. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  296. package/dist/hooks/useGridFocus.js +12 -9
  297. package/dist/hooks/useListFocus.d.ts +6 -1
  298. package/dist/hooks/useListFocus.d.ts.map +1 -1
  299. package/dist/hooks/useListFocus.js +12 -4
  300. package/dist/hooks/useOverflow.d.ts +19 -0
  301. package/dist/hooks/useOverflow.d.ts.map +1 -1
  302. package/dist/hooks/useOverflow.js +36 -25
  303. package/dist/i18n/InternationalizationContext.d.ts +1 -0
  304. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  305. package/dist/i18n/InternationalizationContext.js +3 -0
  306. package/dist/i18n/InternationalizationProvider.d.ts +8 -1
  307. package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
  308. package/dist/i18n/InternationalizationProvider.js +5 -1
  309. package/dist/i18n/getLocaleDirection.d.ts +18 -0
  310. package/dist/i18n/getLocaleDirection.d.ts.map +1 -0
  311. package/dist/i18n/getLocaleDirection.js +29 -0
  312. package/dist/i18n/index.d.ts +6 -0
  313. package/dist/i18n/index.d.ts.map +1 -1
  314. package/dist/i18n/index.js +7 -1
  315. package/dist/i18n/resolve.d.ts.map +1 -1
  316. package/dist/i18n/resolve.js +7 -18
  317. package/dist/i18n/useDirection.d.ts +2 -0
  318. package/dist/i18n/useDirection.d.ts.map +1 -0
  319. package/dist/i18n/useDirection.js +27 -0
  320. package/dist/theme/Theme.d.ts.map +1 -1
  321. package/dist/theme/Theme.js +2 -7
  322. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  323. package/dist/theme/derivedVarRegistry.js +13 -0
  324. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  325. package/dist/theme/generateThemeRules.js +51 -0
  326. package/dist/theme/hct.d.ts +0 -1
  327. package/dist/theme/hct.d.ts.map +1 -1
  328. package/dist/theme/hct.js +1 -1
  329. package/dist/theme/syntax/defineSyntaxTheme.d.ts.map +1 -1
  330. package/dist/theme/syntax/defineSyntaxTheme.js +2 -1
  331. package/dist/utils/devWarning.d.ts +42 -0
  332. package/dist/utils/devWarning.d.ts.map +1 -0
  333. package/dist/utils/devWarning.js +88 -0
  334. package/dist/utils/index.d.ts +3 -1
  335. package/dist/utils/index.d.ts.map +1 -1
  336. package/dist/utils/index.js +3 -2
  337. package/dist/utils/plainDate.d.ts +30 -0
  338. package/dist/utils/plainDate.d.ts.map +1 -1
  339. package/dist/utils/plainDate.js +53 -0
  340. package/dist/utils/types.d.ts +13 -0
  341. package/dist/utils/types.d.ts.map +1 -1
  342. package/locales/en.json +32 -0
  343. package/locales/fr-FR.json +14 -0
  344. package/locales/pseudo.json +24 -0
  345. package/package.json +1 -1
  346. package/src/AppShell/AppShell.tsx +6 -0
  347. package/src/AppShell/AppShellMobileContext.tsx +8 -0
  348. package/src/AppShell/useAppShellMobile.doc.mjs +8 -0
  349. package/src/Avatar/Avatar.doc.mjs +60 -3
  350. package/src/Avatar/Avatar.test.tsx +281 -1
  351. package/src/Avatar/Avatar.tsx +324 -64
  352. package/src/Avatar/AvatarStatusDot.doc.mjs +23 -12
  353. package/src/Avatar/AvatarStatusDot.test.tsx +284 -0
  354. package/src/Avatar/AvatarStatusDot.tsx +178 -21
  355. package/src/AvatarGroup/AvatarGroup.doc.mjs +2 -0
  356. package/src/AvatarGroup/AvatarGroup.test.tsx +125 -0
  357. package/src/AvatarGroup/AvatarGroup.tsx +58 -3
  358. package/src/AvatarGroup/AvatarGroupOverflow.tsx +1 -0
  359. package/src/Banner/Banner.doc.mjs +9 -0
  360. package/src/Banner/Banner.test.tsx +30 -0
  361. package/src/Banner/Banner.tsx +53 -9
  362. package/src/Breadcrumbs/BreadcrumbItem.doc.mjs +26 -0
  363. package/src/Breadcrumbs/BreadcrumbItem.tsx +333 -14
  364. package/src/Breadcrumbs/Breadcrumbs.doc.mjs +2 -0
  365. package/src/Breadcrumbs/Breadcrumbs.test.tsx +284 -2
  366. package/src/Breadcrumbs/index.ts +27 -0
  367. package/src/Button/Button.doc.mjs +9 -0
  368. package/src/Button/Button.test.tsx +49 -0
  369. package/src/Button/Button.tsx +27 -3
  370. package/src/ButtonGroup/ButtonGroup.doc.mjs +3 -0
  371. package/src/ButtonGroup/ButtonGroup.test.tsx +37 -0
  372. package/src/ButtonGroup/ButtonGroup.tsx +32 -0
  373. package/src/Calendar/Calendar.doc.mjs +12 -1
  374. package/src/Calendar/Calendar.test.tsx +193 -0
  375. package/src/Calendar/Calendar.tsx +38 -16
  376. package/src/Calendar/dayCellUtils.test.ts +5 -0
  377. package/src/Calendar/dayCellUtils.ts +1 -1
  378. package/src/Card/Card.doc.mjs +9 -0
  379. package/src/Card/Card.test.tsx +21 -3
  380. package/src/Card/Card.tsx +35 -2
  381. package/src/Carousel/Carousel.doc.mjs +1 -0
  382. package/src/Carousel/Carousel.test.tsx +53 -0
  383. package/src/Carousel/Carousel.tsx +34 -4
  384. package/src/Chat/ChatComposer.doc.mjs +12 -3
  385. package/src/Chat/ChatComposer.test.tsx +115 -0
  386. package/src/Chat/ChatComposer.tsx +73 -9
  387. package/src/Chat/ChatComposerDrawer.test.tsx +61 -0
  388. package/src/Chat/ChatComposerDrawer.tsx +11 -1
  389. package/src/Chat/ChatComposerInput.doc.mjs +7 -0
  390. package/src/Chat/ChatComposerInput.test.tsx +103 -0
  391. package/src/Chat/ChatComposerInput.tsx +51 -5
  392. package/src/Chat/ChatContext.tsx +37 -10
  393. package/src/Chat/ChatDictationButton.tsx +4 -1
  394. package/src/Chat/ChatLayout.tsx +7 -5
  395. package/src/Chat/ChatLayoutScrollButton.tsx +7 -2
  396. package/src/Chat/ChatMessageMetadata.tsx +1 -2
  397. package/src/Chat/ChatSendButton.tsx +11 -1
  398. package/src/Chat/ChatToolCalls.test.tsx +64 -0
  399. package/src/Chat/ChatToolCalls.tsx +25 -4
  400. package/src/Chat/index.ts +5 -1
  401. package/src/Chat/useChatStreamScroll.test.tsx +49 -0
  402. package/src/Chat/useChatStreamScroll.ts +27 -10
  403. package/src/Chat/useSpeechRecognition.ts +1 -1
  404. package/src/Chat/useTriggerMenu.tsx +4 -2
  405. package/src/CheckboxInput/CheckboxInput.test.tsx +32 -1
  406. package/src/CheckboxInput/CheckboxInput.tsx +1 -1
  407. package/src/CheckboxList/CheckboxList.test.tsx +81 -3
  408. package/src/CheckboxList/CheckboxListItem.doc.mjs +33 -5
  409. package/src/CheckboxList/CheckboxListItem.tsx +41 -5
  410. package/src/Citation/Citation.doc.mjs +4 -3
  411. package/src/Citation/Citation.test.tsx +97 -0
  412. package/src/Citation/Citation.tsx +43 -11
  413. package/src/ClickableCard/ClickableCard.doc.mjs +2 -0
  414. package/src/ClickableCard/ClickableCard.test.tsx +34 -0
  415. package/src/ClickableCard/ClickableCard.tsx +13 -3
  416. package/src/CodeBlock/CodeBlock.test.tsx +56 -0
  417. package/src/CodeBlock/CodeBlock.tsx +10 -2
  418. package/src/CommandPalette/CommandPalette.doc.mjs +1 -0
  419. package/src/CommandPalette/CommandPalette.tsx +3 -1
  420. package/src/CommandPalette/CommandPaletteGroup.test.tsx +71 -0
  421. package/src/CommandPalette/CommandPaletteGroup.tsx +12 -1
  422. package/src/CommandPalette/CommandPaletteInput.doc.mjs +26 -9
  423. package/src/CommandPalette/CommandPaletteInput.test.tsx +34 -0
  424. package/src/CommandPalette/CommandPaletteInput.tsx +11 -0
  425. package/src/CommandPalette/CommandPaletteItem.tsx +1 -1
  426. package/src/ContextMenu/ContextMenu.test.tsx +193 -0
  427. package/src/DateInput/DateInput.doc.mjs +31 -0
  428. package/src/DateInput/DateInput.test.tsx +131 -0
  429. package/src/DateInput/DateInput.tsx +78 -3
  430. package/src/DateInput/index.ts +1 -0
  431. package/src/DateRangeInput/DateRangeInput.doc.mjs +8 -0
  432. package/src/DateRangeInput/DateRangeInput.test.tsx +23 -0
  433. package/src/DateRangeInput/DateRangeInput.tsx +13 -3
  434. package/src/DateTimeInput/DateTimeInput.doc.mjs +16 -1
  435. package/src/DateTimeInput/DateTimeInput.test.tsx +147 -0
  436. package/src/DateTimeInput/DateTimeInput.tsx +42 -18
  437. package/src/Dialog/Dialog.doc.mjs +2 -2
  438. package/src/Dialog/Dialog.test.tsx +77 -4
  439. package/src/Dialog/Dialog.tsx +89 -5
  440. package/src/Dialog/DialogContext.ts +9 -2
  441. package/src/Dialog/DialogHeader.doc.mjs +2 -2
  442. package/src/Dialog/DialogHeader.tsx +9 -2
  443. package/src/Divider/Divider.tsx +2 -2
  444. package/src/DropdownMenu/DropdownMenu.test.tsx +169 -0
  445. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +1 -2
  446. package/src/DropdownMenu/DropdownMenuItem.tsx +1 -1
  447. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +1 -2
  448. package/src/Field/Field.test.tsx +57 -12
  449. package/src/Field/Field.tsx +6 -5
  450. package/src/Field/FieldLabel.tsx +9 -1
  451. package/src/FieldStatus/FieldStatus.doc.mjs +17 -5
  452. package/src/FieldStatus/FieldStatus.test.tsx +150 -40
  453. package/src/FieldStatus/FieldStatus.tsx +63 -5
  454. package/src/FileInput/FileInput.doc.mjs +17 -2
  455. package/src/FileInput/FileInput.test.tsx +135 -12
  456. package/src/FileInput/FileInput.tsx +53 -28
  457. package/src/HoverCard/HoverCard.doc.mjs +13 -0
  458. package/src/HoverCard/HoverCard.test.tsx +21 -2
  459. package/src/HoverCard/HoverCard.tsx +11 -0
  460. package/src/HoverCard/useHoverCard.doc.mjs +2 -0
  461. package/src/HoverCard/useHoverCard.tsx +24 -2
  462. package/src/Icon/Icon.doc.mjs +3 -2
  463. package/src/Icon/Icon.tsx +7 -2
  464. package/src/IconButton/IconButton.doc.mjs +8 -0
  465. package/src/IconButton/IconButton.test.tsx +15 -18
  466. package/src/Item/Item.test.tsx +40 -18
  467. package/src/Item/Item.tsx +38 -2
  468. package/src/Layer/useLayer.tsx +8 -0
  469. package/src/Lightbox/Lightbox.tsx +4 -4
  470. package/src/List/List.test.tsx +9 -8
  471. package/src/MobileNav/MobileNav.tsx +12 -1
  472. package/src/MobileNav/MobileNavToggle.test.tsx +116 -0
  473. package/src/MobileNav/MobileNavToggle.tsx +9 -1
  474. package/src/MultiSelector/MultiSelector.doc.mjs +13 -2
  475. package/src/MultiSelector/MultiSelector.test.tsx +195 -3
  476. package/src/MultiSelector/MultiSelector.tsx +65 -12
  477. package/src/MultiSelector/hooks.ts +17 -0
  478. package/src/NavMenu/NavHeadingMenuItem.tsx +1 -1
  479. package/src/NumberInput/NumberInput.doc.mjs +15 -0
  480. package/src/NumberInput/NumberInput.test.tsx +82 -7
  481. package/src/NumberInput/NumberInput.tsx +21 -4
  482. package/src/Outline/Outline.tsx +0 -1
  483. package/src/OverflowList/OverflowList.doc.mjs +39 -6
  484. package/src/OverflowList/OverflowList.test.tsx +111 -0
  485. package/src/OverflowList/OverflowList.tsx +48 -8
  486. package/src/Pagination/Pagination.tsx +11 -3
  487. package/src/Popover/Popover.tsx +7 -4
  488. package/src/Popover/usePopover.tsx +9 -13
  489. package/src/PowerSearch/PowerSearch.doc.mjs +29 -0
  490. package/src/PowerSearch/PowerSearch.test.tsx +43 -0
  491. package/src/PowerSearch/PowerSearch.tsx +10 -1
  492. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +67 -0
  493. package/src/PowerSearch/PowerSearchEditPopover.tsx +1 -1
  494. package/src/PowerSearch/usePowerSearchSource.ts +1 -2
  495. package/src/SelectableCard/SelectableCard.doc.mjs +2 -0
  496. package/src/SelectableCard/SelectableCard.test.tsx +46 -4
  497. package/src/SelectableCard/SelectableCard.tsx +29 -16
  498. package/src/Selector/Selector.doc.mjs +7 -0
  499. package/src/Selector/Selector.test.tsx +92 -0
  500. package/src/Selector/Selector.tsx +16 -2
  501. package/src/Selector/hooks.ts +17 -0
  502. package/src/SideNav/SideNav.test.tsx +119 -3
  503. package/src/SideNav/SideNavCollapseButton.tsx +6 -1
  504. package/src/SideNav/SideNavHeading.tsx +24 -8
  505. package/src/SideNav/SideNavItem.tsx +5 -1
  506. package/src/Slider/Slider.test.tsx +75 -2
  507. package/src/Slider/Slider.tsx +21 -5
  508. package/src/Switch/Switch.test.tsx +32 -1
  509. package/src/TabList/Tab.tsx +2 -2
  510. package/src/TabList/TabMenu.tsx +2 -2
  511. package/src/Table/BaseTable.tsx +3 -5
  512. package/src/Table/TableCell.tsx +3 -3
  513. package/src/Table/TableHeaderCell.tsx +3 -3
  514. package/src/Table/index.ts +5 -0
  515. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +6 -2
  516. package/src/Table/plugins/rowStatus/index.ts +7 -0
  517. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +138 -0
  518. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +189 -0
  519. package/src/Table/plugins/selection/useTableSelection.test.tsx +16 -0
  520. package/src/Table/plugins/selection/useTableSelection.tsx +19 -1
  521. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +1 -1
  522. package/src/Table/plugins/tree/useTableTreeData.test.tsx +135 -3
  523. package/src/Table/plugins/tree/useTableTreeData.tsx +128 -1
  524. package/src/Table/plugins/tree/useTableTreeState.test.tsx +104 -0
  525. package/src/Table/plugins/tree/useTableTreeState.tsx +46 -2
  526. package/src/Table/useBaseTablePlugins.ts +10 -6
  527. package/src/Table/useTableRowStatus.doc.mjs +30 -0
  528. package/src/Table/useTableSelection.doc.mjs +65 -14
  529. package/src/Table/useTableTreeData.doc.mjs +29 -0
  530. package/src/Table/useTableTreeState.doc.mjs +2 -2
  531. package/src/TextArea/TextArea.doc.mjs +15 -0
  532. package/src/TextArea/TextArea.test.tsx +32 -1
  533. package/src/TextArea/TextArea.tsx +10 -0
  534. package/src/TextInput/TextInput.doc.mjs +15 -0
  535. package/src/TextInput/TextInput.test.tsx +44 -0
  536. package/src/TextInput/TextInput.tsx +13 -1
  537. package/src/Thumbnail/Thumbnail.doc.mjs +19 -10
  538. package/src/Thumbnail/Thumbnail.test.tsx +98 -7
  539. package/src/Thumbnail/Thumbnail.tsx +142 -42
  540. package/src/Thumbnail/thumbnail.markers.stylex.ts +12 -0
  541. package/src/TimeInput/TimeInput.doc.mjs +15 -0
  542. package/src/TimeInput/TimeInput.test.tsx +99 -1
  543. package/src/TimeInput/TimeInput.tsx +29 -6
  544. package/src/Timestamp/Timestamp.doc.mjs +4 -4
  545. package/src/Timestamp/Timestamp.test.tsx +32 -0
  546. package/src/Timestamp/Timestamp.tsx +28 -8
  547. package/src/Toast/useToast.tsx +7 -8
  548. package/src/ToggleButton/ToggleButtonGroup.tsx +1 -1
  549. package/src/Tokenizer/Tokenizer.doc.mjs +30 -1
  550. package/src/Tokenizer/Tokenizer.test.tsx +206 -2
  551. package/src/Tokenizer/Tokenizer.tsx +26 -4
  552. package/src/TopNav/TopNav.test.tsx +125 -1
  553. package/src/TopNav/TopNavHeading.tsx +24 -8
  554. package/src/TopNav/TopNavMegaMenu.test.tsx +149 -1
  555. package/src/TopNav/TopNavMegaMenu.tsx +10 -4
  556. package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +3 -3
  557. package/src/TopNav/TopNavMegaMenuFeaturedCard.tsx +19 -2
  558. package/src/TopNav/TopNavMenu.test.tsx +118 -4
  559. package/src/TopNav/TopNavMenu.tsx +83 -5
  560. package/src/TreeList/TreeList.doc.mjs +22 -0
  561. package/src/TreeList/TreeList.test.tsx +232 -0
  562. package/src/TreeList/TreeList.tsx +18 -2
  563. package/src/TreeList/TreeListBranches.tsx +16 -2
  564. package/src/TreeList/TreeListItem.tsx +52 -12
  565. package/src/TreeList/TreeListTypes.ts +27 -1
  566. package/src/TreeList/index.ts +2 -2
  567. package/src/Typeahead/BaseTypeahead.tsx +30 -15
  568. package/src/Typeahead/Typeahead.doc.mjs +7 -0
  569. package/src/Typeahead/Typeahead.test.tsx +50 -0
  570. package/src/Typeahead/Typeahead.tsx +10 -0
  571. package/src/authoring/doc.ts +1 -3
  572. package/src/docs-types.ts +5 -0
  573. package/src/hooks/computeOverflow.test.ts +422 -0
  574. package/src/hooks/computeOverflow.ts +290 -0
  575. package/src/hooks/index.ts +2 -0
  576. package/src/hooks/isRtlElement.ts +27 -0
  577. package/src/hooks/useDevWarning.test.tsx +45 -0
  578. package/src/hooks/useDevWarning.ts +54 -0
  579. package/src/hooks/useGridFocus.doc.mjs +3 -3
  580. package/src/hooks/useGridFocus.test.tsx +41 -0
  581. package/src/hooks/useGridFocus.ts +21 -10
  582. package/src/hooks/useListFocus.doc.mjs +3 -3
  583. package/src/hooks/useListFocus.test.tsx +67 -1
  584. package/src/hooks/useListFocus.ts +21 -5
  585. package/src/hooks/useOverflow.test.ts +96 -4
  586. package/src/hooks/useOverflow.ts +74 -33
  587. package/src/i18n/InternationalizationContext.ts +4 -0
  588. package/src/i18n/InternationalizationProvider.doc.mjs +99 -2
  589. package/src/i18n/InternationalizationProvider.tsx +12 -2
  590. package/src/i18n/__tests__/getLocaleDirection.test.ts +40 -0
  591. package/src/i18n/__tests__/useDirection.test.tsx +66 -0
  592. package/src/i18n/getLocaleDirection.ts +29 -0
  593. package/src/i18n/index.ts +6 -0
  594. package/src/i18n/resolve.ts +9 -21
  595. package/src/i18n/useDirection.ts +29 -0
  596. package/src/reset.css +0 -26
  597. package/src/reset.test.ts +60 -0
  598. package/src/theme/Theme.tsx +11 -14
  599. package/src/theme/derivedVarRegistry.test.ts +1 -0
  600. package/src/theme/derivedVarRegistry.ts +6 -0
  601. package/src/theme/generateThemeRules.test.ts +107 -0
  602. package/src/theme/generateThemeRules.ts +56 -0
  603. package/src/theme/hct.ts +1 -1
  604. package/src/theme/syntax/defineSyntaxTheme.ts +5 -6
  605. package/src/utils/devWarning.test.ts +56 -0
  606. package/src/utils/devWarning.ts +102 -0
  607. package/src/utils/index.ts +6 -0
  608. package/src/utils/plainDate.test.ts +22 -0
  609. package/src/utils/plainDate.ts +61 -0
  610. package/src/utils/types.ts +14 -0
@@ -9,10 +9,22 @@
9
9
  * SYNC: When Field.tsx changes, update tests to match new behavior
10
10
  */
11
11
 
12
- import {describe, it, expect, vi} from 'vitest';
13
- import {render, screen} from '@testing-library/react';
12
+ import {describe, it, expect, vi, afterEach} from 'vitest';
13
+ import {render, screen, waitFor} from '@testing-library/react';
14
14
  import {Field} from './Field';
15
15
  import {FormLayoutContext} from '../FormLayout/FormLayoutContext';
16
+ import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
17
+
18
+ function politeRegion(): HTMLElement | null {
19
+ return document.querySelector('[data-astryx-live-region="polite"]');
20
+ }
21
+ function assertiveRegion(): HTMLElement | null {
22
+ return document.querySelector('[data-astryx-live-region="assertive"]');
23
+ }
24
+
25
+ afterEach(() => {
26
+ __resetLiveRegionsForTest();
27
+ });
16
28
 
17
29
  describe('Field', () => {
18
30
  it('renders with label', () => {
@@ -228,7 +240,11 @@ describe('Field', () => {
228
240
  expect(document.querySelector('svg')).not.toBeInTheDocument();
229
241
  });
230
242
 
231
- it('status has role="alert" and aria-live="assertive" for error type', () => {
243
+ // The status message is announced through the persistent useAnnounce live
244
+ // regions rather than role/aria-live on the (conditionally mounted)
245
+ // FieldStatus element itself — regions born with their content are not
246
+ // reliably announced by assistive technology.
247
+ it('announces error status assertively via the persistent live region', async () => {
232
248
  render(
233
249
  <Field
234
250
  label="Email"
@@ -237,12 +253,13 @@ describe('Field', () => {
237
253
  <input id="email-input" />
238
254
  </Field>,
239
255
  );
240
- const status = screen.getByRole('alert');
241
- expect(status).toHaveTextContent('Invalid email');
242
- expect(status).toHaveAttribute('aria-live', 'assertive');
256
+ expect(screen.getByText('Invalid email')).not.toHaveAttribute('role');
257
+ await waitFor(() => {
258
+ expect(assertiveRegion()).toHaveTextContent('Invalid email');
259
+ });
243
260
  });
244
261
 
245
- it('status has role="status" and aria-live="polite" for warning type', () => {
262
+ it('announces warning status politely via the persistent live region', async () => {
246
263
  render(
247
264
  <Field
248
265
  label="Email"
@@ -251,9 +268,34 @@ describe('Field', () => {
251
268
  <input id="email-input" />
252
269
  </Field>,
253
270
  );
254
- const status = screen.getByRole('status');
255
- expect(status).toHaveTextContent('Check this');
256
- expect(status).toHaveAttribute('aria-live', 'polite');
271
+ expect(screen.getByText('Check this')).not.toHaveAttribute('aria-live');
272
+ await waitFor(() => {
273
+ expect(politeRegion()).toHaveTextContent('Check this');
274
+ });
275
+ });
276
+
277
+ // Regression: the status live region must NOT be born together with its
278
+ // content. A message that appears after mount (the common validation flow)
279
+ // has to land in the persistent announce region.
280
+ it('announces a status message that appears after mount', async () => {
281
+ const {rerender} = render(
282
+ <Field label="Email" inputID="email-input">
283
+ <input id="email-input" />
284
+ </Field>,
285
+ );
286
+ expect(assertiveRegion()).toBeNull();
287
+
288
+ rerender(
289
+ <Field
290
+ label="Email"
291
+ inputID="email-input"
292
+ status={{type: 'error', message: 'Email is required'}}>
293
+ <input id="email-input" />
294
+ </Field>,
295
+ );
296
+ await waitFor(() => {
297
+ expect(assertiveRegion()).toHaveTextContent('Email is required');
298
+ });
257
299
  });
258
300
 
259
301
  it('auto-generates description ID as {inputID}-desc when descriptionID is not provided', () => {
@@ -277,7 +319,10 @@ describe('Field', () => {
277
319
  <input id="my-input" />
278
320
  </Field>,
279
321
  );
280
- expect(screen.getByRole('alert')).toHaveAttribute('id', 'my-input-status');
322
+ expect(screen.getByText('Required')).toHaveAttribute(
323
+ 'id',
324
+ 'my-input-status',
325
+ );
281
326
  });
282
327
 
283
328
  it('warns when isOptional and isRequired are both set', () => {
@@ -421,7 +466,7 @@ describe('Field', () => {
421
466
  </Field>,
422
467
  {wrapper: horizontalLabelsWrapper},
423
468
  );
424
- const statusEl = screen.getByRole('alert');
469
+ const statusEl = screen.getByText('Required');
425
470
  const inputEl = screen.getByTestId('email');
426
471
  // Both status and input should be inside the same wrapper div (column 2)
427
472
  expect(statusEl.parentElement).toBe(inputEl.parentElement);
@@ -25,6 +25,7 @@ import {FieldStatus} from '../FieldStatus/FieldStatus';
25
25
  import {spacingVars, borderVars} from '../theme/tokens.stylex';
26
26
  import type {IconType} from '../Icon';
27
27
  import {mergeProps} from '../utils';
28
+ import {useDevWarning} from '../hooks/useDevWarning';
28
29
  import {FormLayoutContext} from '../FormLayout/FormLayoutContext';
29
30
  import {Text} from '../Text';
30
31
  import {themeProps} from '../utils/themeProps';
@@ -215,11 +216,11 @@ export function Field({
215
216
  const resolvedMessageID =
216
217
  status?.messageID ?? (status?.message ? `${inputID}-status` : undefined);
217
218
 
218
- if (isOptional && isRequired) {
219
- console.warn(
220
- 'Field: isOptional and isRequired are mutually exclusive. isOptional takes precedence.',
221
- );
222
- }
219
+ useDevWarning(
220
+ 'Field',
221
+ 'isOptional and isRequired are mutually exclusive. isOptional takes precedence.',
222
+ isOptional && isRequired,
223
+ );
223
224
 
224
225
  const labelNode = (
225
226
  <FieldLabel
@@ -48,7 +48,7 @@ const styles = stylex.create({
48
48
  borderStyle: 'none',
49
49
  clip: 'rect(0, 0, 0, 0)',
50
50
  height: 1,
51
- left: 0,
51
+ insetInlineStart: 0,
52
52
  margin: -1,
53
53
  overflow: 'hidden',
54
54
  padding: 0,
@@ -171,7 +171,11 @@ export function FieldLabel({
171
171
  labelTooltip,
172
172
  description,
173
173
  descriptionID,
174
+ className,
175
+ style,
176
+ xstyle,
174
177
  ref,
178
+ ...rest
175
179
  }: FieldLabelProps) {
176
180
  const statusText = isOptional ? 'Optional' : isRequired ? 'Required' : null;
177
181
 
@@ -207,13 +211,17 @@ export function FieldLabel({
207
211
  // `htmlFor` only applies to a real `<label>` associating with a single
208
212
  // control; a group label (span) has no `htmlFor`.
209
213
  htmlFor={isGroupLabel ? undefined : inputID}
214
+ {...rest}
210
215
  {...mergeProps(
211
216
  themeProps('field-label'),
212
217
  stylex.props(
213
218
  styles.label,
214
219
  isDisabled && styles.labelDisabled,
215
220
  isLabelHidden && styles.srOnly,
221
+ xstyle,
216
222
  ),
223
+ className,
224
+ style,
217
225
  )}>
218
226
  {labelContent}
219
227
  </LabelElement>
@@ -8,7 +8,7 @@ export const docs = {
8
8
  category: 'Data Input',
9
9
  isHiddenFromOverview: true,
10
10
  description:
11
- 'Status message component for form field validation feedback.',
11
+ 'Status message component for form field validation feedback. Messages are announced to screen readers through persistent live regions (assertive for errors, polite otherwise), so conditional mounting is safe.',
12
12
  theming: {
13
13
  targets: [
14
14
  {className: 'astryx-field-status', visualProps: ['type', 'variant']},
@@ -66,12 +66,24 @@ export const docs = {
66
66
  /** @type {import('../docs-types').TranslationDoc} */
67
67
  export const docsDense = {
68
68
  description:
69
- 'Validation feedback message for fields/custom controls. Supports error, warning, success and attached/detached variants.',
69
+ 'Validation feedback message for fields/custom controls. Supports error, warning, success and attached/detached variants. Announced via persistent live regions (assertive for errors, polite otherwise).',
70
70
  usage: {
71
71
  bestPractices: [
72
- {guidance: true, description: 'Use attached status below bordered inputs when message belongs to that input.'},
73
- {guidance: true, description: 'Use detached status for checkboxes, switches, and custom controls where overlap is visually awkward.'},
74
- {guidance: false, description: 'Use FieldStatus for general alerts or page-level notices; use Banner or Toast instead.'},
72
+ {
73
+ guidance: true,
74
+ description:
75
+ 'Use attached status below bordered inputs when message belongs to that input.',
76
+ },
77
+ {
78
+ guidance: true,
79
+ description:
80
+ 'Use detached status for checkboxes, switches, and custom controls where overlap is visually awkward.',
81
+ },
82
+ {
83
+ guidance: false,
84
+ description:
85
+ 'Use FieldStatus for general alerts or page-level notices; use Banner or Toast instead.',
86
+ },
75
87
  ],
76
88
  },
77
89
  propDescriptions: {
@@ -9,15 +9,27 @@
9
9
  * SYNC: When FieldStatus.tsx changes, update tests to match new behavior
10
10
  */
11
11
 
12
- import {describe, it, expect, vi} from 'vitest';
13
- import {render, screen} from '@testing-library/react';
12
+ import {describe, it, expect, vi, afterEach} from 'vitest';
13
+ import {render, screen, waitFor} from '@testing-library/react';
14
14
  import * as stylex from '@stylexjs/stylex';
15
15
  import {FieldStatus} from './FieldStatus';
16
+ import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
16
17
 
17
18
  const testStyles = stylex.create({
18
19
  custom: {color: 'rebeccapurple'},
19
20
  });
20
21
 
22
+ function politeRegion(): HTMLElement | null {
23
+ return document.querySelector('[data-astryx-live-region="polite"]');
24
+ }
25
+ function assertiveRegion(): HTMLElement | null {
26
+ return document.querySelector('[data-astryx-live-region="assertive"]');
27
+ }
28
+
29
+ afterEach(() => {
30
+ __resetLiveRegionsForTest();
31
+ });
32
+
21
33
  describe('FieldStatus', () => {
22
34
  it('renders the message text', () => {
23
35
  render(<FieldStatus type="error" message="This field is required" />);
@@ -29,47 +41,75 @@ describe('FieldStatus', () => {
29
41
  expect(screen.getByTestId('fs').tagName).toBe('DIV');
30
42
  });
31
43
 
32
- describe('role semantics', () => {
33
- // Errors are urgent — they get role="alert" so assistive tech interrupts.
34
- it('uses role="alert" for error status', () => {
35
- render(<FieldStatus type="error" message="msg" />);
36
- expect(screen.getByRole('alert')).toHaveTextContent('msg');
44
+ describe('screen-reader announcements', () => {
45
+ // The rendered element is NOT itself a live region: FieldStatus is
46
+ // conditionally mounted by every caller, and live regions born together
47
+ // with their content are not reliably announced. Announcements go through
48
+ // the persistent useAnnounce singletons instead.
49
+ it('does not carry role or aria-live on the rendered element', () => {
50
+ render(<FieldStatus type="error" message="msg" data-testid="fs" />);
51
+ const el = screen.getByTestId('fs');
52
+ expect(el).not.toHaveAttribute('role');
53
+ expect(el).not.toHaveAttribute('aria-live');
37
54
  });
38
55
 
39
- // Non-error statuses are advisory — role="status" is a polite live region.
40
- it('uses role="status" for warning status', () => {
41
- render(<FieldStatus type="warning" message="msg" data-testid="fs" />);
42
- expect(screen.getByTestId('fs')).toHaveAttribute('role', 'status');
43
- expect(screen.queryByRole('alert')).not.toBeInTheDocument();
56
+ // Errors are urgent — they interrupt via the assertive channel.
57
+ it('announces error messages assertively, including on first mount', async () => {
58
+ render(<FieldStatus type="error" message="This field is required" />);
59
+ await waitFor(() => {
60
+ expect(assertiveRegion()).toHaveTextContent('This field is required');
61
+ });
62
+ expect(politeRegion()).toHaveTextContent('');
44
63
  });
45
64
 
46
- it('uses role="status" for success status', () => {
47
- render(<FieldStatus type="success" message="msg" data-testid="fs" />);
48
- expect(screen.getByTestId('fs')).toHaveAttribute('role', 'status');
65
+ it('announces warning messages politely', async () => {
66
+ render(<FieldStatus type="warning" message="Check this value" />);
67
+ await waitFor(() => {
68
+ expect(politeRegion()).toHaveTextContent('Check this value');
69
+ });
70
+ expect(assertiveRegion()).toHaveTextContent('');
49
71
  });
50
- });
51
72
 
52
- describe('aria-live politeness', () => {
53
- // Error → assertive so screen readers announce immediately.
54
- it('sets aria-live="assertive" for error', () => {
55
- render(<FieldStatus type="error" message="msg" data-testid="fs" />);
56
- expect(screen.getByTestId('fs')).toHaveAttribute(
57
- 'aria-live',
58
- 'assertive',
59
- );
73
+ it('announces success messages politely', async () => {
74
+ render(<FieldStatus type="success" message="Looks good" />);
75
+ await waitFor(() => {
76
+ expect(politeRegion()).toHaveTextContent('Looks good');
77
+ });
60
78
  });
61
79
 
62
- it('sets aria-live="polite" for warning', () => {
63
- render(<FieldStatus type="warning" message="msg" data-testid="fs" />);
64
- expect(screen.getByTestId('fs')).toHaveAttribute('aria-live', 'polite');
80
+ it('announces message changes', async () => {
81
+ const {rerender} = render(<FieldStatus type="error" message="First" />);
82
+ await waitFor(() => {
83
+ expect(assertiveRegion()).toHaveTextContent('First');
84
+ });
85
+ rerender(<FieldStatus type="error" message="Second" />);
86
+ await waitFor(() => {
87
+ expect(assertiveRegion()).toHaveTextContent('Second');
88
+ });
89
+ });
90
+
91
+ // Severity changes re-route the announcement to the matching channel.
92
+ it('re-routes to the polite channel when type changes from error', async () => {
93
+ const {rerender} = render(<FieldStatus type="error" message="msg" />);
94
+ await waitFor(() => {
95
+ expect(assertiveRegion()).toHaveTextContent('msg');
96
+ });
97
+ rerender(<FieldStatus type="success" message="msg" />);
98
+ await waitFor(() => {
99
+ expect(politeRegion()).toHaveTextContent('msg');
100
+ });
65
101
  });
66
102
 
67
- it('sets aria-live="polite" for success', () => {
68
- render(<FieldStatus type="success" message="msg" data-testid="fs" />);
69
- expect(screen.getByTestId('fs')).toHaveAttribute('aria-live', 'polite');
103
+ it('does not announce an empty message', () => {
104
+ render(<FieldStatus type="error" message="" />);
105
+ // The live regions are created lazily on first announce; an empty
106
+ // message must not trigger one.
107
+ expect(assertiveRegion()).toBeNull();
108
+ expect(politeRegion()).toBeNull();
70
109
  });
71
110
 
72
- // The element is a live region, never hidden from assistive tech.
111
+ // The visible message stays perceivable by assistive tech (it is the
112
+ // aria-describedby target for the input).
73
113
  it('does not mark itself aria-hidden', () => {
74
114
  render(<FieldStatus type="error" message="msg" data-testid="fs" />);
75
115
  expect(screen.getByTestId('fs')).not.toHaveAttribute('aria-hidden');
@@ -245,20 +285,90 @@ describe('FieldStatus', () => {
245
285
  expect(screen.getByTestId('fs')).toHaveTextContent('Second');
246
286
  });
247
287
 
248
- // Switching from error to a non-error type must relax both the role and
249
- // the live-region politeness in lockstep.
250
- it('relaxes role and aria-live when type changes from error', () => {
288
+ // The element must never regain live-region semantics when the type
289
+ // changes — announcements always flow through the persistent regions.
290
+ it('keeps the element role-free when type changes from error', () => {
251
291
  const {rerender} = render(
252
292
  <FieldStatus type="error" message="msg" data-testid="fs" />,
253
293
  );
254
- let el = screen.getByTestId('fs');
255
- expect(el).toHaveAttribute('role', 'alert');
256
- expect(el).toHaveAttribute('aria-live', 'assertive');
294
+ expect(screen.getByTestId('fs')).not.toHaveAttribute('role');
257
295
 
258
296
  rerender(<FieldStatus type="success" message="msg" data-testid="fs" />);
259
- el = screen.getByTestId('fs');
260
- expect(el).toHaveAttribute('role', 'status');
261
- expect(el).toHaveAttribute('aria-live', 'polite');
297
+ const el = screen.getByTestId('fs');
298
+ expect(el).not.toHaveAttribute('role');
299
+ expect(el).not.toHaveAttribute('aria-live');
300
+ });
301
+ });
302
+
303
+ // The detached message must convey status by more than color/position:
304
+ // a leading status glyph precedes the message text (WCAG 1.4.1). The glyph
305
+ // is decorative for AT (aria-hidden) because the message text already names
306
+ // the status in words and it is announced via the live region.
307
+ describe('detached leading status icon (use-of-color a11y)', () => {
308
+ it('renders a leading status icon before the message for the detached variant', () => {
309
+ render(
310
+ <FieldStatus
311
+ type="error"
312
+ message="Something went wrong"
313
+ variant="detached"
314
+ data-testid="fs"
315
+ />,
316
+ );
317
+ const el = screen.getByTestId('fs');
318
+ const icon = el.querySelector('[aria-hidden="true"]');
319
+ const text = screen.getByText('Something went wrong');
320
+ expect(icon).toBeInTheDocument();
321
+ // Icon comes before the message text in document order.
322
+ expect(
323
+ icon!.compareDocumentPosition(text) & Node.DOCUMENT_POSITION_FOLLOWING,
324
+ ).toBeTruthy();
325
+ });
326
+
327
+ it('marks the status icon aria-hidden (visual redundancy, not a second announcement)', () => {
328
+ render(
329
+ <FieldStatus
330
+ type="warning"
331
+ message="Heads up"
332
+ variant="detached"
333
+ data-testid="fs"
334
+ />,
335
+ );
336
+ const icon = screen
337
+ .getByTestId('fs')
338
+ .querySelector('[aria-hidden="true"]');
339
+ expect(icon).toBeInTheDocument();
340
+ expect(icon).toHaveAttribute('aria-hidden', 'true');
341
+ });
342
+
343
+ it('renders a status icon for each status type in the detached variant', () => {
344
+ for (const type of ['error', 'warning', 'success'] as const) {
345
+ const {unmount} = render(
346
+ <FieldStatus
347
+ type={type}
348
+ message="msg"
349
+ variant="detached"
350
+ data-testid="fs"
351
+ />,
352
+ );
353
+ expect(
354
+ screen.getByTestId('fs').querySelector('[aria-hidden="true"]'),
355
+ ).toBeInTheDocument();
356
+ unmount();
357
+ }
358
+ });
359
+
360
+ it('does not render a leading status icon for the attached variant', () => {
361
+ render(
362
+ <FieldStatus
363
+ type="error"
364
+ message="msg"
365
+ variant="attached"
366
+ data-testid="fs"
367
+ />,
368
+ );
369
+ expect(
370
+ screen.getByTestId('fs').querySelector('[aria-hidden="true"]'),
371
+ ).toBeNull();
262
372
  });
263
373
  });
264
374
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  /**
4
4
  * @file FieldStatus.tsx
5
- * @input Uses React, stylex, theme tokens
5
+ * @input Uses React, stylex, theme tokens, useAnnounce, Icon
6
6
  * @output Exports FieldStatus component, FieldStatusProps
7
7
  * @position Core implementation; consumed by Field, Switch, CheckboxInput, and the FieldStatus entrypoint
8
8
  *
@@ -16,9 +16,10 @@
16
16
 
17
17
  'use client';
18
18
 
19
- import React from 'react';
19
+ import React, {useEffect} from 'react';
20
20
  import * as stylex from '@stylexjs/stylex';
21
21
  import type {BaseProps} from '../BaseProps';
22
+ import {useAnnounce} from '../hooks/useAnnounce';
22
23
  import {mergeProps} from '../utils';
23
24
  import {
24
25
  colorVars,
@@ -30,6 +31,19 @@ import {
30
31
  import type {InputStatusType} from '../Field/types';
31
32
  import {useEntryAnimation} from '../hooks/useEntryAnimation';
32
33
  import {themeProps} from '../utils/themeProps';
34
+ import {Icon} from '../Icon';
35
+ import type {IconName} from '../Icon';
36
+
37
+ /**
38
+ * Maps each status type to its status glyph. Mirrors the mapping the input
39
+ * controls already use for the on-field status affordance, so the detached
40
+ * message shows the same icon a consumer sees elsewhere for that status.
41
+ */
42
+ const statusIconMap: Record<InputStatusType, IconName> = {
43
+ warning: 'warning',
44
+ error: 'error',
45
+ success: 'success',
46
+ };
33
47
 
34
48
  const styles = stylex.create({
35
49
  base: {
@@ -51,6 +65,17 @@ const styles = stylex.create({
51
65
  paddingInline: spacingVars['--spacing-2'],
52
66
  borderRadius: radiusVars['--radius-element'],
53
67
  },
68
+ detachedContent: {
69
+ display: 'flex',
70
+ alignItems: 'flex-start',
71
+ gap: spacingVars['--spacing-1'],
72
+ },
73
+ detachedIcon: {
74
+ // Match the height of the first text line so the glyph top-aligns to it
75
+ // (rather than to the flex box) when the message wraps to multiple lines.
76
+ lineHeight: typeScaleVars['--text-supporting-leading'],
77
+ flexShrink: 0,
78
+ },
54
79
  });
55
80
 
56
81
  const colorStyles = stylex.create({
@@ -113,6 +138,21 @@ export interface FieldStatusProps extends BaseProps<HTMLDivElement> {
113
138
  /**
114
139
  * A status message component for form fields.
115
140
  *
141
+ * The `detached` variant renders a leading status icon before the message so
142
+ * status is not conveyed by color or position alone (WCAG 1.4.1). The icon is
143
+ * decorative for assistive tech (`aria-hidden`): the message text already names
144
+ * the status in words and is announced through the live region. The `attached`
145
+ * variant keeps its status affordance on the bordered input, so it renders no
146
+ * icon here to avoid a duplicate.
147
+ *
148
+ * Screen-reader announcements go through the persistent `useAnnounce` live
149
+ * regions (assertive for errors, polite otherwise) rather than `role`/
150
+ * `aria-live` on the rendered element. Live regions that mount together with
151
+ * their content are not reliably announced by assistive technology, and
152
+ * FieldStatus is almost always conditionally rendered by its callers. The
153
+ * message is announced whenever it appears — including on first mount — and
154
+ * whenever it changes.
155
+ *
116
156
  * @example
117
157
  * ```
118
158
  * <FieldStatus
@@ -138,13 +178,22 @@ export function FieldStatus({
138
178
  ...rest
139
179
  }: FieldStatusProps) {
140
180
  const entryStyle = useEntryAnimation('slideDown');
181
+ const announce = useAnnounce();
182
+
183
+ // Announce the message through the persistently-mounted live regions.
184
+ // Announce-on-mount is intentional: callers conditionally mount FieldStatus
185
+ // when a status appears (and whole forms can mount with a server-side
186
+ // validation error already present), and both cases must be heard.
187
+ useEffect(() => {
188
+ if (message) {
189
+ announce(message, type === 'error' ? 'assertive' : 'polite');
190
+ }
191
+ }, [announce, message, type]);
141
192
 
142
193
  return (
143
194
  <div
144
195
  ref={ref}
145
196
  id={id}
146
- role={type === 'error' ? 'alert' : 'status'}
147
- aria-live={type === 'error' ? 'assertive' : 'polite'}
148
197
  {...rest}
149
198
  {...mergeProps(
150
199
  themeProps('field-status', {type, variant}),
@@ -158,7 +207,16 @@ export function FieldStatus({
158
207
  className,
159
208
  style,
160
209
  )}>
161
- {message}
210
+ {variant === 'detached' ? (
211
+ <span {...stylex.props(styles.detachedContent)}>
212
+ <span {...stylex.props(styles.detachedIcon)}>
213
+ <Icon icon={statusIconMap[type]} size="sm" color="inherit" />
214
+ </span>
215
+ <span>{message}</span>
216
+ </span>
217
+ ) : (
218
+ message
219
+ )}
162
220
  </div>
163
221
  );
164
222
  }
@@ -78,7 +78,7 @@ export const docs = {
78
78
  name: 'isRequired',
79
79
  type: 'boolean',
80
80
  description:
81
- 'Displays a "Required" indicator next to the label and sets aria-required. Mutually exclusive with isOptional.',
81
+ 'Displays a "Required" indicator next to the label and adds a screen-reader-only "Required" description to the trigger (aria-required is not supported on role="button"). Mutually exclusive with isOptional.',
82
82
  default: 'false',
83
83
  },
84
84
  {
@@ -119,6 +119,13 @@ export const docs = {
119
119
  description:
120
120
  'Validation status: applies a colored border. If message is provided, displays a floating message below the input. Error type also sets aria-invalid.',
121
121
  },
122
+ {
123
+ name: 'statusVariant',
124
+ type: "'attached' | 'detached'",
125
+ description:
126
+ 'How the status message is placed relative to the input. attached overlaps directly below the input (bordered treatment); detached floats below as a separate element with spacing.',
127
+ default: "'attached'",
128
+ },
122
129
  {
123
130
  name: 'labelTooltip',
124
131
  type: 'string',
@@ -246,6 +253,13 @@ export const docsZh = {
246
253
  type: "{type: 'error' | 'warning' | 'success', message?: string}",
247
254
  description: '验证状态。',
248
255
  },
256
+ {
257
+ name: 'statusVariant',
258
+ type: "'attached' | 'detached'",
259
+ description:
260
+ '状态消息相对于输入框的放置方式。attached 直接叠加在输入框下方(带边框处理);detached 作为独立元素浮于下方并留有间距。',
261
+ default: "'attached'",
262
+ },
249
263
  ],
250
264
  theming: {
251
265
  targets: [
@@ -308,7 +322,7 @@ export const docsDense = {
308
322
  isLabelHidden: 'Visually hides label; keeps screen reader access.',
309
323
  description: 'Description text between label+input.',
310
324
  isOptional: 'Shows "Optional" indicator.',
311
- isRequired: 'Shows "Required" indicator+sets aria-required.',
325
+ isRequired: 'Shows "Required" indicator+SR-only trigger description.',
312
326
  isDisabled: 'Disables input, prevents interaction.',
313
327
  disabledMessage:
314
328
  'Explains why input is disabled. With isDisabled, shows tooltip on hover/focus + keeps trigger focusable via aria-disabled (opening picker stays blocked). Use instead of wrapping a disabled FileInput in Tooltip.',
@@ -316,6 +330,7 @@ export const docsDense = {
316
330
  placeholder: 'Placeholder when no files selected.',
317
331
  mode: "Visual mode: 'input' (compact) or 'dropzone' (drag-and-drop).",
318
332
  status: 'Validation status; colored border. Message floats below.',
333
+ statusVariant: 'How status message is placed: attached overlaps below input; detached floats below w/ spacing.',
319
334
  labelTooltip: 'Tooltip in info icon at label end.',
320
335
  },
321
336
  };