@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
@@ -10,7 +10,13 @@
10
10
  */
11
11
 
12
12
  import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
13
- import {render, screen, fireEvent, waitFor} from '@testing-library/react';
13
+ import {
14
+ render,
15
+ screen,
16
+ fireEvent,
17
+ waitFor,
18
+ within,
19
+ } from '@testing-library/react';
14
20
  import userEvent from '@testing-library/user-event';
15
21
  import {MultiSelector} from './MultiSelector';
16
22
  import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
@@ -360,6 +366,30 @@ describe('MultiSelector', () => {
360
366
  expect(activeId).toBeTruthy();
361
367
  });
362
368
 
369
+ it('End/Home jump the highlight to the last/first option (non-search)', async () => {
370
+ const user = userEvent.setup();
371
+ render(
372
+ <MultiSelector
373
+ label="Fruit"
374
+ options={defaultOptions}
375
+ value={[]}
376
+ onChange={() => {}}
377
+ />,
378
+ );
379
+
380
+ const trigger = screen.getByRole('combobox');
381
+ await user.click(trigger);
382
+ const options = screen.getAllByRole('option', h);
383
+
384
+ await user.keyboard('{End}');
385
+ expect(trigger).toHaveAttribute(
386
+ 'aria-activedescendant',
387
+ options[options.length - 1].id,
388
+ );
389
+ await user.keyboard('{Home}');
390
+ expect(trigger).toHaveAttribute('aria-activedescendant', options[0].id);
391
+ });
392
+
363
393
  it('toggles item with Enter key', async () => {
364
394
  const user = userEvent.setup();
365
395
  const onChange = vi.fn();
@@ -535,6 +565,65 @@ describe('MultiSelector', () => {
535
565
  expect(options).toHaveLength(1);
536
566
  });
537
567
 
568
+ it('PageDown/PageUp jump the highlight to the last/first filtered option', async () => {
569
+ const user = userEvent.setup();
570
+ render(
571
+ <MultiSelector
572
+ label="Fruit"
573
+ options={defaultOptions}
574
+ value={[]}
575
+ onChange={() => {}}
576
+ hasSearch
577
+ />,
578
+ );
579
+
580
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
581
+ const searchInput = screen.getByRole('combobox', h);
582
+ // Filter to Banana and Orange so "last" means last *visible* option.
583
+ await user.type(searchInput, 'an');
584
+ const options = screen.getAllByRole('option', h);
585
+ expect(options).toHaveLength(2);
586
+
587
+ await user.keyboard('{PageDown}');
588
+ expect(searchInput).toHaveAttribute(
589
+ 'aria-activedescendant',
590
+ options[options.length - 1].id,
591
+ );
592
+ await user.keyboard('{PageUp}');
593
+ expect(searchInput).toHaveAttribute('aria-activedescendant', options[0].id);
594
+ });
595
+
596
+ it('Home/End move the search caret, not the option highlight', async () => {
597
+ const user = userEvent.setup();
598
+ render(
599
+ <MultiSelector
600
+ label="Fruit"
601
+ options={defaultOptions}
602
+ value={[]}
603
+ onChange={() => {}}
604
+ hasSearch
605
+ />,
606
+ );
607
+
608
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
609
+ const searchInput = screen.getByRole<HTMLInputElement>('combobox', h);
610
+ await user.type(searchInput, 'an');
611
+ expect(searchInput.selectionStart).toBe(2);
612
+ const activeBefore = searchInput.getAttribute('aria-activedescendant');
613
+ // Home/End stay on the input for caret movement (APG editable combobox);
614
+ // the option highlight must not move.
615
+ await user.keyboard('{Home}');
616
+ expect(searchInput.selectionStart).toBe(0);
617
+ expect(searchInput.getAttribute('aria-activedescendant')).toBe(
618
+ activeBefore,
619
+ );
620
+ await user.keyboard('{End}');
621
+ expect(searchInput.selectionStart).toBe(2);
622
+ expect(searchInput.getAttribute('aria-activedescendant')).toBe(
623
+ activeBefore,
624
+ );
625
+ });
626
+
538
627
  it('shows empty state when search has no results', async () => {
539
628
  const user = userEvent.setup();
540
629
  render(
@@ -551,7 +640,85 @@ describe('MultiSelector', () => {
551
640
  const searchInput = screen.getByRole('combobox', h);
552
641
  await user.type(searchInput, 'xyz');
553
642
 
554
- expect(screen.getByText('No results found')).toBeInTheDocument();
643
+ expect(
644
+ within(screen.getByRole('listbox', h)).getByText('No results found'),
645
+ ).toBeInTheDocument();
646
+ });
647
+
648
+ describe('result announcements', () => {
649
+ it('announces the match count politely while searching', async () => {
650
+ const user = userEvent.setup();
651
+ render(
652
+ <MultiSelector
653
+ label="Fruit"
654
+ options={defaultOptions}
655
+ value={EMPTY_VALUE}
656
+ onChange={() => {}}
657
+ hasSearch
658
+ />,
659
+ );
660
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
661
+ // "an" matches Banana and Orange.
662
+ await user.type(screen.getByRole('combobox', h), 'an');
663
+ await waitFor(() => {
664
+ expect(politeRegion()).toHaveTextContent('2 results');
665
+ });
666
+ });
667
+
668
+ it('announces the singular form when one option matches', async () => {
669
+ const user = userEvent.setup();
670
+ render(
671
+ <MultiSelector
672
+ label="Fruit"
673
+ options={defaultOptions}
674
+ value={EMPTY_VALUE}
675
+ onChange={() => {}}
676
+ hasSearch
677
+ />,
678
+ );
679
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
680
+ // "app" matches only Apple. Anchored so it cannot pass on "1 results".
681
+ await user.type(screen.getByRole('combobox', h), 'app');
682
+ await waitFor(() => {
683
+ expect(politeRegion()).toHaveTextContent(/^1 result$/);
684
+ });
685
+ });
686
+
687
+ it('announces "No results found" when nothing matches', async () => {
688
+ const user = userEvent.setup();
689
+ render(
690
+ <MultiSelector
691
+ label="Fruit"
692
+ options={defaultOptions}
693
+ value={EMPTY_VALUE}
694
+ onChange={() => {}}
695
+ hasSearch
696
+ />,
697
+ );
698
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
699
+ await user.type(screen.getByRole('combobox', h), 'xyz');
700
+ await waitFor(() => {
701
+ expect(politeRegion()).toHaveTextContent('No results found');
702
+ });
703
+ });
704
+
705
+ it('does not announce results until the user searches', async () => {
706
+ const user = userEvent.setup();
707
+ render(
708
+ <MultiSelector
709
+ label="Fruit"
710
+ options={defaultOptions}
711
+ value={EMPTY_VALUE}
712
+ onChange={() => {}}
713
+ hasSearch
714
+ />,
715
+ );
716
+ // Popover closed: nothing announced.
717
+ expect(politeRegion()?.textContent ?? '').toBe('');
718
+ // Open with an empty query: still nothing announced.
719
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
720
+ expect(politeRegion()?.textContent ?? '').toBe('');
721
+ });
555
722
  });
556
723
 
557
724
  it('renders with description', () => {
@@ -1072,7 +1239,32 @@ describe('MultiSelector', () => {
1072
1239
  />
1073
1240
  </form>,
1074
1241
  );
1075
- expect([...new FormData(container.querySelector('form')!).keys()]).toEqual([]);
1242
+ expect([
1243
+ ...new FormData(container.querySelector('form')!).keys(),
1244
+ ]).toEqual([]);
1076
1245
  });
1077
1246
  });
1078
1247
  });
1248
+
1249
+
1250
+ describe('MultiSelector statusVariant forwarding', () => {
1251
+ it('defaults to attached (status renders with data-variant="attached")', () => {
1252
+ const {container} = render(
1253
+ <MultiSelector label="Fruit" options={['Apple', 'Banana']} value={[]} onChange={() => {}} status={{type: 'error', message: 'Required'}} />,
1254
+ );
1255
+ expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
1256
+ 'data-variant',
1257
+ 'attached',
1258
+ );
1259
+ });
1260
+
1261
+ it('forwards statusVariant="detached" to the underlying Field status', () => {
1262
+ const {container} = render(
1263
+ <MultiSelector label="Fruit" options={['Apple', 'Banana']} value={[]} onChange={() => {}} status={{type: 'error', message: 'Required'}} statusVariant="detached" />,
1264
+ );
1265
+ expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
1266
+ 'data-variant',
1267
+ 'detached',
1268
+ );
1269
+ });
1270
+ });
@@ -37,6 +37,7 @@ import {
37
37
  inputStatusBorderStyles,
38
38
  inputStatusHoverShadowStyles,
39
39
  inputWrapperStyles,
40
+ type FieldStatusVariant,
40
41
  } from '../Field';
41
42
  import {Divider} from '../Divider';
42
43
  import {Spinner} from '../Spinner';
@@ -479,6 +480,13 @@ export interface MultiSelectorProps<
479
480
  * Status indicator for the selector.
480
481
  */
481
482
  status?: MultiSelectorStatus;
483
+ /**
484
+ * How the status message is placed relative to the input.
485
+ * - 'attached': message overlaps directly below the input (bordered treatment)
486
+ * - 'detached': message floats below as a separate element with spacing
487
+ * @default 'attached'
488
+ */
489
+ statusVariant?: FieldStatusVariant;
482
490
 
483
491
  /**
484
492
  * Width of the field. Numbers are treated as pixels, strings are used as-is
@@ -562,6 +570,23 @@ export interface MultiSelectorProps<
562
570
  'data-testid'?: string;
563
571
  }
564
572
 
573
+ // Case-insensitive substring filter over the selectable options. Shared by the
574
+ // `filteredItems` memo (rendering) and the search-change handler, which needs
575
+ // the count for the *next* query synchronously to announce it exactly once per
576
+ // keystroke rather than reacting to state in an effect.
577
+ function filterOptionsByQuery(
578
+ items: MultiSelectorOptionData[],
579
+ query: string,
580
+ ): MultiSelectorOptionData[] {
581
+ if (!query) {
582
+ return items;
583
+ }
584
+ const q = query.toLowerCase();
585
+ return items.filter(item =>
586
+ (item.label ?? item.value).toLowerCase().includes(q),
587
+ );
588
+ }
589
+
565
590
  /**
566
591
  * A multi-select dropdown component with checkboxes for choosing
567
592
  * multiple items from a list of options.
@@ -593,6 +618,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
593
618
  placeholder: placeholderFromProps,
594
619
  size: sizeProp,
595
620
  status,
621
+ statusVariant = 'attached',
596
622
  labelTooltip,
597
623
  startIcon,
598
624
  hasClear = false,
@@ -692,15 +718,10 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
692
718
  );
693
719
 
694
720
  // Filter items by search query
695
- const filteredItems = useMemo(() => {
696
- if (!searchQuery) {
697
- return selectableItems;
698
- }
699
- const query = searchQuery.toLowerCase();
700
- return selectableItems.filter(item =>
701
- (item.label ?? item.value).toLowerCase().includes(query),
702
- );
703
- }, [selectableItems, searchQuery]);
721
+ const filteredItems = useMemo(
722
+ () => filterOptionsByQuery(selectableItems, searchQuery),
723
+ [selectableItems, searchQuery],
724
+ );
704
725
 
705
726
  // Single source of truth for item order. Both the hook (keyboard navigation)
706
727
  // and renderOptions (DOM rendering) consume this list — no independent sorting.
@@ -772,8 +793,11 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
772
793
  const handleLayerHide = useCallback(() => {
773
794
  setSearchQuery('');
774
795
  setSelectedAtOpen(null);
796
+ // Clear any lingering result count when the popover closes so stale status
797
+ // text does not linger in the a11y tree.
798
+ announce('');
775
799
  triggerRef.current?.focus();
776
- }, []);
800
+ }, [announce]);
777
801
 
778
802
  const popover = usePopover({
779
803
  hasLightDismiss: true,
@@ -793,6 +817,30 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
793
817
  // eslint-disable-next-line @eslint-react/exhaustive-deps -- mount-only: isDefaultOpen is not reactive
794
818
  }, []);
795
819
 
820
+ // Announce the filtered result count from the query-change handler (matching
821
+ // BaseTypeahead) rather than a reactive effect: computing the count for the
822
+ // next query here fires the announcement exactly once per keystroke and does
823
+ // not re-speak on unrelated re-renders. Reuses the announce instance shared
824
+ // with the selection-count announcements above.
825
+ const handleSearchChange = useCallback(
826
+ (event: React.ChangeEvent<HTMLInputElement>) => {
827
+ const nextQuery = event.target.value;
828
+ setSearchQuery(nextQuery);
829
+ if (nextQuery.length === 0) {
830
+ // Emptying the query clears the region rather than announcing a count.
831
+ announce('');
832
+ return;
833
+ }
834
+ const count = filterOptionsByQuery(selectableItems, nextQuery).length;
835
+ announce(
836
+ count === 0
837
+ ? 'No results found'
838
+ : `${count} result${count === 1 ? '' : 's'}`,
839
+ );
840
+ },
841
+ [announce, selectableItems],
842
+ );
843
+
796
844
  // Handle toggle
797
845
  // Clear all selected values. Shared by the clear button and the keyboard
798
846
  // Delete/Backspace path so clearing is reachable without a mouse.
@@ -1041,14 +1089,17 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1041
1089
  aria-label={t('@astryx.multiSelector.searchOptions')}
1042
1090
  type="text"
1043
1091
  value={searchQuery}
1044
- onChange={e => setSearchQuery(e.target.value)}
1092
+ onChange={handleSearchChange}
1045
1093
  onKeyDown={e => {
1046
1094
  // Arrow keys navigate options; Enter toggles; Escape/Tab close.
1047
1095
  // Space and Home/End are left to the input (type a space / move
1048
- // the caret), not forwarded to option navigation.
1096
+ // the caret) per the APG editable combobox; PageUp/PageDown are
1097
+ // the sanctioned substitute for jumping to the first/last option.
1049
1098
  if (
1050
1099
  e.key === 'ArrowDown' ||
1051
1100
  e.key === 'ArrowUp' ||
1101
+ e.key === 'PageUp' ||
1102
+ e.key === 'PageDown' ||
1052
1103
  e.key === 'Enter' ||
1053
1104
  e.key === 'Escape' ||
1054
1105
  e.key === 'Tab'
@@ -1067,6 +1118,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1067
1118
  listboxId,
1068
1119
  searchQuery,
1069
1120
  searchPlaceholder,
1121
+ handleSearchChange,
1070
1122
  onKeyDown,
1071
1123
  popover.isOpen,
1072
1124
  highlightedIndex,
@@ -1383,6 +1435,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1383
1435
  }
1384
1436
  : undefined
1385
1437
  }
1438
+ statusVariant={statusVariant}
1386
1439
  labelTooltip={labelTooltip}
1387
1440
  width={width}>
1388
1441
  {multiSelectorContent}
@@ -192,6 +192,23 @@ export function useMultiCombobox({
192
192
  }
193
193
  break;
194
194
 
195
+ // PageUp/PageDown mirror Home/End. In search mode Home/End stay on
196
+ // the input for caret movement (APG editable combobox), so these are
197
+ // the sanctioned substitute for jumping to the first/last option.
198
+ case 'PageUp':
199
+ e.preventDefault();
200
+ if (isOpen && enabledIndices.length > 0) {
201
+ setHighlightedIndex(enabledIndices[0]);
202
+ }
203
+ break;
204
+
205
+ case 'PageDown':
206
+ e.preventDefault();
207
+ if (isOpen && enabledIndices.length > 0) {
208
+ setHighlightedIndex(enabledIndices[enabledIndices.length - 1]);
209
+ }
210
+ break;
211
+
195
212
  case 'Delete':
196
213
  case 'Backspace':
197
214
  // Keyboard equivalent of the clear button (comboboxes-2): clear all
@@ -49,7 +49,7 @@ const styles = stylex.create({
49
49
  },
50
50
  border: 'none',
51
51
  cursor: 'pointer',
52
- textAlign: 'left',
52
+ textAlign: 'start',
53
53
  outline: 'none',
54
54
  textDecoration: 'none',
55
55
  },
@@ -94,6 +94,13 @@ export const docs = {
94
94
  type: "{type: 'error' | 'warning' | 'success', message?: string}",
95
95
  description: 'Validation status with optional message.',
96
96
  },
97
+ {
98
+ name: 'statusVariant',
99
+ type: "'attached' | 'detached'",
100
+ description:
101
+ '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.',
102
+ default: "'attached'",
103
+ },
97
104
  {
98
105
  name: 'min',
99
106
  type: 'number | null',
@@ -275,6 +282,13 @@ export const docsZh = {
275
282
  type: "{type: 'error' | 'warning' | 'success', message?: string}",
276
283
  description: '带可选消息的验证状态。',
277
284
  },
285
+ {
286
+ name: 'statusVariant',
287
+ type: "'attached' | 'detached'",
288
+ description:
289
+ '状态消息相对于输入框的放置方式。attached 直接叠加在输入框下方(带边框处理);detached 作为独立元素浮于下方并留有间距。',
290
+ default: "'attached'",
291
+ },
278
292
  {
279
293
  name: 'min',
280
294
  type: 'number | null',
@@ -402,6 +416,7 @@ export const docsDense = {
402
416
  startIcon: 'Icon at input start.',
403
417
  labelIcon: 'Icon before label text.',
404
418
  status: 'Validation status w/ optional message.',
419
+ statusVariant: 'How status message is placed: attached overlaps below input; detached floats below w/ spacing.',
405
420
  min: 'Minimum value allowed.',
406
421
  max: 'Maximum value allowed.',
407
422
  step: 'Step increment.',
@@ -9,11 +9,20 @@
9
9
  * SYNC: When NumberInput.tsx changes, update tests to match new behavior
10
10
  */
11
11
 
12
- import {describe, it, expect, vi, beforeEach} from 'vitest';
12
+ import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
13
13
  import {render, screen, fireEvent, waitFor} from '@testing-library/react';
14
14
  import userEvent from '@testing-library/user-event';
15
15
  import {TestIcon} from '../__tests__/TestIcon';
16
+ import {InputGroup} from '../InputGroup';
16
17
  import {NumberInput} from './NumberInput';
18
+ import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
19
+
20
+ // FieldStatus announces status messages through the persistent useAnnounce
21
+ // singletons; remove them between tests so role/aria-live queries in this
22
+ // file never match a leftover region.
23
+ afterEach(() => {
24
+ __resetLiveRegionsForTest();
25
+ });
17
26
 
18
27
  // Mock showPopover/hidePopover since jsdom does not implement them. Used by the
19
28
  // disabledMessage tooltip.
@@ -328,6 +337,33 @@ describe('NumberInput', () => {
328
337
  expect(screen.queryByText('%')).not.toBeInTheDocument();
329
338
  expect(screen.queryByText('GB')).not.toBeInTheDocument();
330
339
  });
340
+
341
+ it('includes the units text in the accessible description (WCAG 1.3.1)', () => {
342
+ render(
343
+ <NumberInput
344
+ label="Storage"
345
+ value={50}
346
+ onChange={() => {}}
347
+ units="GB"
348
+ />,
349
+ );
350
+ expect(screen.getByRole('spinbutton')).toHaveAccessibleDescription(/GB/);
351
+ });
352
+
353
+ it('combines units with the description in the accessible description', () => {
354
+ render(
355
+ <NumberInput
356
+ label="Discount"
357
+ value={10}
358
+ onChange={() => {}}
359
+ description="Applied at checkout"
360
+ units="%"
361
+ />,
362
+ );
363
+ const input = screen.getByRole('spinbutton');
364
+ expect(input).toHaveAccessibleDescription(/Applied at checkout/);
365
+ expect(input).toHaveAccessibleDescription(/%/);
366
+ });
331
367
  });
332
368
 
333
369
  describe('event callbacks', () => {
@@ -455,6 +491,26 @@ describe('NumberInput', () => {
455
491
  expect(screen.getByText('Value must be positive')).toBeInTheDocument();
456
492
  });
457
493
 
494
+ it('has no dangling aria-describedby ids inside InputGroup (WCAG 1.3.1)', () => {
495
+ // Inside an InputGroup no Field renders, so the status message element
496
+ // does not exist; aria-describedby must not reference its id.
497
+ render(
498
+ <InputGroup label="Price">
499
+ <NumberInput
500
+ label="Amount"
501
+ value={null}
502
+ onChange={() => {}}
503
+ status={{type: 'error', message: 'Value must be positive'}}
504
+ />
505
+ </InputGroup>,
506
+ );
507
+ const input = screen.getByRole('spinbutton');
508
+ const describedBy = input.getAttribute('aria-describedby') ?? '';
509
+ for (const idToken of describedBy.split(/\s+/).filter(Boolean)) {
510
+ expect(document.getElementById(idToken)).not.toBeNull();
511
+ }
512
+ });
513
+
458
514
  it('does not render status message when not provided', () => {
459
515
  render(
460
516
  <NumberInput
@@ -871,9 +927,7 @@ describe('NumberInput', () => {
871
927
  describe('keyboard clearing with hasClear (#3599)', () => {
872
928
  it('commits null when the input is emptied and blurred', () => {
873
929
  const onChange = vi.fn();
874
- render(
875
- <NumberInput label="Qty" hasClear value={42} onChange={onChange} />,
876
- );
930
+ render(<NumberInput label="Qty" hasClear value={42} onChange={onChange} />);
877
931
  const input = screen.getByLabelText('Qty');
878
932
  fireEvent.change(input, {target: {value: ''}});
879
933
  fireEvent.blur(input);
@@ -882,9 +936,7 @@ describe('keyboard clearing with hasClear (#3599)', () => {
882
936
 
883
937
  it('commits null when the input is emptied and Enter is pressed', () => {
884
938
  const onChange = vi.fn();
885
- render(
886
- <NumberInput label="Qty" hasClear value={42} onChange={onChange} />,
887
- );
939
+ render(<NumberInput label="Qty" hasClear value={42} onChange={onChange} />);
888
940
  const input = screen.getByLabelText('Qty');
889
941
  fireEvent.change(input, {target: {value: ''}});
890
942
  fireEvent.keyDown(input, {key: 'Enter'});
@@ -912,3 +964,26 @@ describe('keyboard clearing with hasClear (#3599)', () => {
912
964
  expect((input as HTMLInputElement).value).toBe('42');
913
965
  });
914
966
  });
967
+
968
+
969
+ describe('NumberInput statusVariant forwarding', () => {
970
+ it('defaults to attached (status renders with data-variant="attached")', () => {
971
+ const {container} = render(
972
+ <NumberInput label="Amount" value={null} onChange={() => {}} status={{type: 'error', message: 'Must be positive'}} />,
973
+ );
974
+ expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
975
+ 'data-variant',
976
+ 'attached',
977
+ );
978
+ });
979
+
980
+ it('forwards statusVariant="detached" to the underlying Field status', () => {
981
+ const {container} = render(
982
+ <NumberInput label="Amount" value={null} onChange={() => {}} status={{type: 'error', message: 'Must be positive'}} statusVariant="detached" />,
983
+ );
984
+ expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
985
+ 'data-variant',
986
+ 'detached',
987
+ );
988
+ });
989
+ });
@@ -44,6 +44,7 @@ import {
44
44
  inputStatusBorderStyles,
45
45
  inputStatusHoverShadowStyles,
46
46
  inputStatusFocusWithinStyles,
47
+ type FieldStatusVariant,
47
48
  } from '../Field';
48
49
  import {Icon, renderIconSlot, type IconType} from '../Icon';
49
50
  import {VisuallyHidden} from '../VisuallyHidden';
@@ -207,6 +208,13 @@ interface NumberInputPropsBase extends Omit<
207
208
  * If message is provided, displays a floating message box below the input.
208
209
  */
209
210
  status?: InputStatus;
211
+ /**
212
+ * How the status message is placed relative to the input.
213
+ * - 'attached': message overlaps directly below the input (bordered treatment)
214
+ * - 'detached': message floats below as a separate element with spacing
215
+ * @default 'attached'
216
+ */
217
+ statusVariant?: FieldStatusVariant;
210
218
  /**
211
219
  * The size of the input.
212
220
  * - 'sm': Compact size (28px height)
@@ -311,8 +319,7 @@ type NumberInputPropsClearable = NumberInputPropsBase & {
311
319
  };
312
320
 
313
321
  export type NumberInputProps =
314
- | NumberInputPropsNonClearable
315
- | NumberInputPropsClearable;
322
+ NumberInputPropsNonClearable | NumberInputPropsClearable;
316
323
 
317
324
  /**
318
325
  * Parse and validate a string input as a number.
@@ -375,6 +382,7 @@ export function NumberInput({
375
382
  startIcon,
376
383
  labelIcon,
377
384
  status,
385
+ statusVariant = 'attached',
378
386
  size: sizeProp,
379
387
  onChange,
380
388
  value,
@@ -406,6 +414,7 @@ export function NumberInput({
406
414
  const inputLabelID = useId();
407
415
  const descriptionID = useId();
408
416
  const statusMessageID = useId();
417
+ const unitsID = useId();
409
418
  const inputRef = useRef<HTMLInputElement | null>(null);
410
419
  const containerRef = useRef<HTMLDivElement | null>(null);
411
420
  const inputGroup = useInputGroup();
@@ -446,7 +455,10 @@ export function NumberInput({
446
455
  inputLabelID,
447
456
  [
448
457
  description ? descriptionID : null,
449
- status?.message ? statusMessageID : null,
458
+ // The status message element is rendered by Field, which is skipped
459
+ // inside an InputGroup — only reference it when it actually exists.
460
+ !inputGroup && status?.message ? statusMessageID : null,
461
+ units ? unitsID : null,
450
462
  showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
451
463
  ],
452
464
  inputGroup,
@@ -652,7 +664,11 @@ export function NumberInput({
652
664
  !isInputValid && styles.inputInvalid,
653
665
  )}
654
666
  />
655
- {units && <span {...stylex.props(styles.units)}>{units}</span>}
667
+ {units && (
668
+ <span id={unitsID} {...stylex.props(styles.units)}>
669
+ {units}
670
+ </span>
671
+ )}
656
672
  {/*
657
673
  Live region announcing invalid typed input to assistive technology.
658
674
  The value silently reverts on blur, so without this a screen-reader
@@ -710,6 +726,7 @@ export function NumberInput({
710
726
  }
711
727
  : undefined
712
728
  }
729
+ statusVariant={statusVariant}
713
730
  labelTooltip={labelTooltip}
714
731
  width={width}>
715
732
  {inputWrapper}
@@ -299,7 +299,6 @@ function getIndentStyle(level: number) {
299
299
  * @example
300
300
  * ```
301
301
  * const scrollContainerRef = useRef<HTMLDivElement>(null);
302
- *
303
302
  * <div ref={scrollContainerRef} style={{overflowY: 'auto'}}>...</div>
304
303
  * <Outline
305
304
  * items={items}