@astryxdesign/core 0.1.7 → 0.1.8-canary.0c4d8d7

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 (428) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +30 -19
  3. package/dist/AppShell/AppShell.d.ts.map +1 -1
  4. package/dist/AppShell/AppShell.js +6 -2
  5. package/dist/AppShell/AppShellMobileContext.d.ts +8 -0
  6. package/dist/AppShell/AppShellMobileContext.d.ts.map +1 -1
  7. package/dist/Avatar/Avatar.d.ts +10 -5
  8. package/dist/Avatar/Avatar.d.ts.map +1 -1
  9. package/dist/Avatar/Avatar.js +12 -8
  10. package/dist/Avatar/AvatarSizeContext.d.ts +1 -1
  11. package/dist/Avatar/AvatarSizeContext.js +1 -1
  12. package/dist/Avatar/AvatarStatusDot.d.ts +2 -2
  13. package/dist/Avatar/AvatarStatusDot.js +2 -2
  14. package/dist/AvatarGroup/AvatarGroup.d.ts +2 -2
  15. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  16. package/dist/AvatarGroup/AvatarGroup.js +2 -2
  17. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
  18. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  19. package/dist/AvatarGroup/AvatarGroupOverflow.js +16 -14
  20. package/dist/Banner/Banner.d.ts +9 -1
  21. package/dist/Banner/Banner.d.ts.map +1 -1
  22. package/dist/Banner/Banner.js +31 -2
  23. package/dist/Button/Button.d.ts +8 -2
  24. package/dist/Button/Button.d.ts.map +1 -1
  25. package/dist/Button/Button.js +31 -2
  26. package/dist/ButtonGroup/ButtonGroup.d.ts +9 -1
  27. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  28. package/dist/ButtonGroup/ButtonGroup.js +30 -1
  29. package/dist/Card/Card.d.ts +8 -2
  30. package/dist/Card/Card.d.ts.map +1 -1
  31. package/dist/Card/Card.js +28 -2
  32. package/dist/Carousel/Carousel.d.ts +6 -2
  33. package/dist/Carousel/Carousel.d.ts.map +1 -1
  34. package/dist/Carousel/Carousel.js +55 -5
  35. package/dist/Chat/ChatComposer.d.ts +8 -0
  36. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  37. package/dist/Chat/ChatComposer.js +31 -6
  38. package/dist/Chat/ChatComposerDrawer.d.ts +2 -0
  39. package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
  40. package/dist/Chat/ChatComposerDrawer.js +11 -1
  41. package/dist/Chat/ChatComposerInput.d.ts +20 -3
  42. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  43. package/dist/Chat/ChatComposerInput.js +19 -3
  44. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  45. package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
  46. package/dist/Chat/ChatMessage.d.ts +1 -1
  47. package/dist/Chat/ChatMessage.d.ts.map +1 -1
  48. package/dist/Chat/ChatMessage.js +1 -1
  49. package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
  50. package/dist/Chat/ChatSendButton.d.ts.map +1 -1
  51. package/dist/Chat/ChatToolCalls.d.ts +5 -1
  52. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  53. package/dist/Chat/ChatToolCalls.js +12 -3
  54. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  55. package/dist/Citation/Citation.d.ts.map +1 -1
  56. package/dist/Citation/Citation.js +10 -4
  57. package/dist/ClickableCard/ClickableCard.d.ts +9 -2
  58. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  59. package/dist/ClickableCard/ClickableCard.js +3 -0
  60. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  61. package/dist/CodeBlock/CodeBlock.js +8 -1
  62. package/dist/Collapsible/Collapsible.d.ts +11 -1
  63. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  64. package/dist/Collapsible/Collapsible.js +36 -4
  65. package/dist/CommandPalette/CommandPaletteInput.d.ts +7 -1
  66. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  67. package/dist/CommandPalette/CommandPaletteInput.js +6 -1
  68. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  69. package/dist/ContextMenu/ContextMenu.js +4 -3
  70. package/dist/ContextMenu/index.d.ts +3 -0
  71. package/dist/ContextMenu/index.d.ts.map +1 -1
  72. package/dist/ContextMenu/index.js +7 -1
  73. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  74. package/dist/DateTimeInput/DateTimeInput.js +10 -1
  75. package/dist/Dialog/Dialog.d.ts.map +1 -1
  76. package/dist/Dialog/Dialog.js +2 -1
  77. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  78. package/dist/DropdownMenu/DropdownMenu.js +8 -4
  79. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +88 -0
  80. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -0
  81. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +169 -0
  82. package/dist/DropdownMenu/DropdownMenuContext.d.ts +17 -1
  83. package/dist/DropdownMenu/DropdownMenuContext.d.ts.map +1 -1
  84. package/dist/DropdownMenu/DropdownMenuContext.js +18 -0
  85. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +68 -0
  86. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -0
  87. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +69 -0
  88. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +69 -0
  89. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -0
  90. package/dist/DropdownMenu/DropdownMenuRadioItem.js +178 -0
  91. package/dist/DropdownMenu/index.d.ts +5 -1
  92. package/dist/DropdownMenu/index.d.ts.map +1 -1
  93. package/dist/DropdownMenu/index.js +10 -1
  94. package/dist/DropdownMenu/menuItemRoles.d.ts +13 -0
  95. package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -0
  96. package/dist/DropdownMenu/menuItemRoles.js +15 -0
  97. package/dist/FieldStatus/FieldStatus.d.ts +8 -0
  98. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  99. package/dist/FieldStatus/FieldStatus.js +22 -4
  100. package/dist/FileInput/FileInput.d.ts.map +1 -1
  101. package/dist/FileInput/FileInput.js +10 -2
  102. package/dist/HoverCard/HoverCard.d.ts +9 -1
  103. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  104. package/dist/HoverCard/HoverCard.js +2 -0
  105. package/dist/HoverCard/useHoverCard.d.ts +8 -0
  106. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  107. package/dist/HoverCard/useHoverCard.js +7 -2
  108. package/dist/Icon/Icon.d.ts +43 -13
  109. package/dist/Icon/Icon.d.ts.map +1 -1
  110. package/dist/Icon/Icon.js +62 -27
  111. package/dist/Layer/useLayer.d.ts +6 -0
  112. package/dist/Layer/useLayer.d.ts.map +1 -1
  113. package/dist/Layer/useLayer.js +2 -0
  114. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  115. package/dist/MobileNav/MobileNav.js +6 -1
  116. package/dist/MobileNav/MobileNavToggle.d.ts.map +1 -1
  117. package/dist/MobileNav/MobileNavToggle.js +4 -0
  118. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  119. package/dist/MultiSelector/MultiSelector.js +39 -12
  120. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  121. package/dist/MultiSelector/hooks.js +16 -0
  122. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  123. package/dist/Outline/Outline.d.ts +68 -3
  124. package/dist/Outline/Outline.d.ts.map +1 -1
  125. package/dist/Outline/Outline.js +128 -26
  126. package/dist/Outline/useScrollSpy.d.ts +27 -7
  127. package/dist/Outline/useScrollSpy.d.ts.map +1 -1
  128. package/dist/Outline/useScrollSpy.js +202 -70
  129. package/dist/OverflowList/OverflowList.d.ts +20 -3
  130. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  131. package/dist/OverflowList/OverflowList.js +29 -4
  132. package/dist/PowerSearch/PowerSearchEditPopover.js +1 -1
  133. package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
  134. package/dist/SelectableCard/SelectableCard.d.ts +9 -2
  135. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  136. package/dist/SelectableCard/SelectableCard.js +14 -11
  137. package/dist/Selector/Selector.d.ts.map +1 -1
  138. package/dist/Selector/Selector.js +4 -2
  139. package/dist/Selector/hooks.d.ts.map +1 -1
  140. package/dist/Selector/hooks.js +16 -0
  141. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  142. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  143. package/dist/SideNav/SideNavHeading.js +21 -8
  144. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  145. package/dist/Slider/Slider.d.ts.map +1 -1
  146. package/dist/Slider/Slider.js +15 -1
  147. package/dist/TabList/Tab.d.ts.map +1 -1
  148. package/dist/TabList/Tab.js +2 -2
  149. package/dist/TabList/TabList.d.ts.map +1 -1
  150. package/dist/TabList/TabList.js +2 -0
  151. package/dist/TabList/TabMenu.d.ts.map +1 -1
  152. package/dist/TabList/TabMenu.js +1 -1
  153. package/dist/Table/Table.d.ts +1 -1
  154. package/dist/Table/Table.js +1 -1
  155. package/dist/Table/index.d.ts +4 -0
  156. package/dist/Table/index.d.ts.map +1 -1
  157. package/dist/Table/index.js +2 -0
  158. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  159. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  160. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +24 -2
  161. package/dist/Table/plugins/rowStatus/index.d.ts +3 -0
  162. package/dist/Table/plugins/rowStatus/index.d.ts.map +1 -0
  163. package/dist/Table/plugins/rowStatus/index.js +3 -0
  164. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts +60 -0
  165. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -0
  166. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +145 -0
  167. package/dist/Table/plugins/selection/useTableSelection.d.ts +12 -0
  168. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  169. package/dist/Table/plugins/selection/useTableSelection.js +4 -1
  170. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  171. package/dist/Table/plugins/sortable/useTableSortable.js +18 -7
  172. package/dist/Table/plugins/tree/index.d.ts +5 -0
  173. package/dist/Table/plugins/tree/index.d.ts.map +1 -0
  174. package/dist/Table/plugins/tree/index.js +4 -0
  175. package/dist/Table/plugins/tree/useTableTreeData.d.ts +38 -0
  176. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -0
  177. package/dist/Table/plugins/tree/useTableTreeData.js +358 -0
  178. package/dist/Table/plugins/tree/useTableTreeState.d.ts +51 -0
  179. package/dist/Table/plugins/tree/useTableTreeState.d.ts.map +1 -0
  180. package/dist/Table/plugins/tree/useTableTreeState.js +175 -0
  181. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  182. package/dist/Table/useBaseTablePlugins.js +6 -3
  183. package/dist/Thumbnail/Thumbnail.d.ts +8 -1
  184. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  185. package/dist/Thumbnail/Thumbnail.js +46 -8
  186. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  187. package/dist/TimeInput/TimeInput.js +15 -4
  188. package/dist/Tokenizer/Tokenizer.d.ts +1 -1
  189. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  190. package/dist/Tokenizer/Tokenizer.js +16 -4
  191. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  192. package/dist/Toolbar/Toolbar.js +8 -1
  193. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  194. package/dist/TopNav/TopNavHeading.js +18 -8
  195. package/dist/TopNav/TopNavMegaMenu.js +10 -4
  196. package/dist/TopNav/TopNavMenu.d.ts +2 -1
  197. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  198. package/dist/TopNav/TopNavMenu.js +68 -6
  199. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  200. package/dist/TreeList/TreeListItem.js +5 -14
  201. package/dist/astryx.css +51 -16
  202. package/dist/astryx.umd.js +56 -56
  203. package/dist/astryx.umd.js.map +4 -4
  204. package/dist/authoring/doc.d.ts.map +1 -1
  205. package/dist/hooks/computeOverflow.d.ts +47 -0
  206. package/dist/hooks/computeOverflow.d.ts.map +1 -0
  207. package/dist/hooks/computeOverflow.js +213 -0
  208. package/dist/hooks/useOverflow.d.ts +19 -0
  209. package/dist/hooks/useOverflow.d.ts.map +1 -1
  210. package/dist/hooks/useOverflow.js +37 -25
  211. package/dist/theme/Theme.js +1 -1
  212. package/dist/theme/defineTheme.d.ts +1 -1
  213. package/dist/theme/defineTheme.js +1 -1
  214. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  215. package/dist/theme/generateThemeRules.js +51 -0
  216. package/dist/theme/hct.d.ts +0 -1
  217. package/dist/theme/hct.d.ts.map +1 -1
  218. package/dist/theme/hct.js +0 -3
  219. package/dist/utils/types.d.ts +13 -0
  220. package/dist/utils/types.d.ts.map +1 -1
  221. package/docs.mjs +32 -461
  222. package/locales/en.json +212 -172
  223. package/locales/fr-FR.json +10 -0
  224. package/locales/pseudo.json +30 -0
  225. package/package.json +3 -1
  226. package/scripts/postinstall.mjs +93 -0
  227. package/src/AppShell/AppShell.tsx +6 -0
  228. package/src/AppShell/AppShellMobileContext.tsx +8 -0
  229. package/src/AppShell/useAppShellMobile.doc.mjs +8 -0
  230. package/src/Avatar/Avatar.doc.mjs +6 -6
  231. package/src/Avatar/Avatar.tsx +16 -11
  232. package/src/Avatar/AvatarSizeContext.ts +1 -1
  233. package/src/Avatar/AvatarStatusDot.tsx +2 -2
  234. package/src/AvatarGroup/AvatarGroup.doc.mjs +1 -1
  235. package/src/AvatarGroup/AvatarGroup.test.tsx +17 -3
  236. package/src/AvatarGroup/AvatarGroup.tsx +3 -3
  237. package/src/AvatarGroup/AvatarGroupOverflow.doc.mjs +4 -4
  238. package/src/AvatarGroup/AvatarGroupOverflow.tsx +21 -5
  239. package/src/Banner/Banner.doc.mjs +9 -0
  240. package/src/Banner/Banner.test.tsx +30 -0
  241. package/src/Banner/Banner.tsx +47 -3
  242. package/src/Button/Button.doc.mjs +9 -0
  243. package/src/Button/Button.test.tsx +49 -0
  244. package/src/Button/Button.tsx +25 -1
  245. package/src/ButtonGroup/ButtonGroup.doc.mjs +3 -0
  246. package/src/ButtonGroup/ButtonGroup.test.tsx +37 -0
  247. package/src/ButtonGroup/ButtonGroup.tsx +32 -0
  248. package/src/Card/Card.doc.mjs +9 -0
  249. package/src/Card/Card.test.tsx +21 -3
  250. package/src/Card/Card.tsx +35 -2
  251. package/src/Carousel/Carousel.doc.mjs +1 -0
  252. package/src/Carousel/Carousel.test.tsx +149 -1
  253. package/src/Carousel/Carousel.tsx +62 -5
  254. package/src/Chat/Chat.doc.mjs +1 -1
  255. package/src/Chat/ChatComposer.doc.mjs +9 -0
  256. package/src/Chat/ChatComposer.test.tsx +35 -0
  257. package/src/Chat/ChatComposer.tsx +58 -9
  258. package/src/Chat/ChatComposerDrawer.test.tsx +61 -0
  259. package/src/Chat/ChatComposerDrawer.tsx +11 -1
  260. package/src/Chat/ChatComposerInput.doc.mjs +7 -0
  261. package/src/Chat/ChatComposerInput.test.tsx +87 -0
  262. package/src/Chat/ChatComposerInput.tsx +35 -3
  263. package/src/Chat/ChatLayout.tsx +3 -1
  264. package/src/Chat/ChatLayoutScrollButton.tsx +3 -1
  265. package/src/Chat/ChatMessage.tsx +4 -2
  266. package/src/Chat/ChatMessageMetadata.tsx +1 -2
  267. package/src/Chat/ChatSendButton.tsx +5 -1
  268. package/src/Chat/ChatToolCalls.test.tsx +64 -0
  269. package/src/Chat/ChatToolCalls.tsx +13 -1
  270. package/src/Chat/useTriggerMenu.tsx +3 -1
  271. package/src/CheckboxInput/CheckboxInput.doc.mjs +1 -1
  272. package/src/CheckboxInput/CheckboxInput.test.tsx +32 -1
  273. package/src/CheckboxList/CheckboxList.test.tsx +9 -1
  274. package/src/Citation/Citation.test.tsx +51 -0
  275. package/src/Citation/Citation.tsx +8 -2
  276. package/src/ClickableCard/ClickableCard.doc.mjs +2 -0
  277. package/src/ClickableCard/ClickableCard.test.tsx +34 -0
  278. package/src/ClickableCard/ClickableCard.tsx +11 -1
  279. package/src/CodeBlock/CodeBlock.test.tsx +56 -0
  280. package/src/CodeBlock/CodeBlock.tsx +9 -1
  281. package/src/Collapsible/Collapsible.doc.mjs +7 -0
  282. package/src/Collapsible/Collapsible.test.tsx +80 -0
  283. package/src/Collapsible/Collapsible.tsx +58 -5
  284. package/src/CommandPalette/CommandPaletteInput.doc.mjs +26 -9
  285. package/src/CommandPalette/CommandPaletteInput.test.tsx +34 -0
  286. package/src/CommandPalette/CommandPaletteInput.tsx +11 -0
  287. package/src/ContextMenu/ContextMenu.test.tsx +75 -1
  288. package/src/ContextMenu/ContextMenu.tsx +10 -5
  289. package/src/ContextMenu/index.ts +15 -0
  290. package/src/DateTimeInput/DateTimeInput.test.tsx +35 -0
  291. package/src/DateTimeInput/DateTimeInput.tsx +14 -2
  292. package/src/Dialog/Dialog.tsx +12 -2
  293. package/src/DropdownMenu/DropdownMenu.doc.mjs +23 -0
  294. package/src/DropdownMenu/DropdownMenu.tsx +11 -6
  295. package/src/DropdownMenu/DropdownMenuCheckboxItem.doc.mjs +95 -0
  296. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +236 -0
  297. package/src/DropdownMenu/DropdownMenuContext.tsx +29 -1
  298. package/src/DropdownMenu/DropdownMenuItem.doc.mjs +7 -1
  299. package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +80 -0
  300. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +118 -0
  301. package/src/DropdownMenu/DropdownMenuRadioItem.doc.mjs +80 -0
  302. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +232 -0
  303. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +163 -0
  304. package/src/DropdownMenu/index.ts +23 -3
  305. package/src/DropdownMenu/menuItemRoles.ts +22 -0
  306. package/src/Field/Field.doc.mjs +1 -1
  307. package/src/Field/Field.test.tsx +57 -12
  308. package/src/Field/FieldLabel.doc.mjs +1 -1
  309. package/src/FieldStatus/FieldStatus.doc.mjs +17 -5
  310. package/src/FieldStatus/FieldStatus.test.tsx +78 -40
  311. package/src/FieldStatus/FieldStatus.tsx +22 -4
  312. package/src/FileInput/FileInput.test.tsx +52 -3
  313. package/src/FileInput/FileInput.tsx +13 -2
  314. package/src/HoverCard/HoverCard.doc.mjs +13 -0
  315. package/src/HoverCard/HoverCard.test.tsx +21 -2
  316. package/src/HoverCard/HoverCard.tsx +11 -0
  317. package/src/HoverCard/useHoverCard.doc.mjs +2 -0
  318. package/src/HoverCard/useHoverCard.tsx +24 -2
  319. package/src/Icon/Icon.doc.mjs +12 -0
  320. package/src/Icon/Icon.test.tsx +142 -0
  321. package/src/Icon/Icon.tsx +98 -30
  322. package/src/IconButton/IconButton.doc.mjs +8 -0
  323. package/src/IconButton/IconButton.test.tsx +15 -18
  324. package/src/Item/Item.doc.mjs +1 -1
  325. package/src/Layer/useLayer.tsx +8 -0
  326. package/src/MobileNav/MobileNav.tsx +12 -1
  327. package/src/MobileNav/MobileNavToggle.test.tsx +116 -0
  328. package/src/MobileNav/MobileNavToggle.tsx +9 -1
  329. package/src/MultiSelector/MultiSelector.doc.mjs +4 -2
  330. package/src/MultiSelector/MultiSelector.test.tsx +162 -1
  331. package/src/MultiSelector/MultiSelector.tsx +55 -12
  332. package/src/MultiSelector/hooks.ts +17 -0
  333. package/src/NumberInput/NumberInput.doc.mjs +2 -2
  334. package/src/NumberInput/NumberInput.test.tsx +11 -7
  335. package/src/NumberInput/NumberInput.tsx +1 -2
  336. package/src/Outline/Outline.doc.mjs +123 -3
  337. package/src/Outline/Outline.test.tsx +735 -0
  338. package/src/Outline/Outline.tsx +183 -33
  339. package/src/Outline/useScrollSpy.ts +248 -72
  340. package/src/OverflowList/OverflowList.doc.mjs +39 -6
  341. package/src/OverflowList/OverflowList.test.tsx +111 -0
  342. package/src/OverflowList/OverflowList.tsx +48 -8
  343. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +67 -0
  344. package/src/PowerSearch/PowerSearchEditPopover.tsx +1 -1
  345. package/src/PowerSearch/usePowerSearchSource.ts +1 -2
  346. package/src/SelectableCard/SelectableCard.doc.mjs +2 -0
  347. package/src/SelectableCard/SelectableCard.test.tsx +46 -4
  348. package/src/SelectableCard/SelectableCard.tsx +27 -14
  349. package/src/Selector/Selector.test.tsx +69 -0
  350. package/src/Selector/Selector.tsx +5 -1
  351. package/src/Selector/SelectorOption.doc.mjs +1 -1
  352. package/src/Selector/hooks.ts +17 -0
  353. package/src/SideNav/SideNav.test.tsx +119 -3
  354. package/src/SideNav/SideNavCollapseButton.tsx +6 -1
  355. package/src/SideNav/SideNavHeading.tsx +24 -8
  356. package/src/SideNav/SideNavItem.doc.mjs +2 -2
  357. package/src/SideNav/SideNavItem.tsx +5 -1
  358. package/src/Slider/Slider.test.tsx +62 -2
  359. package/src/Slider/Slider.tsx +15 -1
  360. package/src/Switch/Switch.doc.mjs +1 -1
  361. package/src/Switch/Switch.test.tsx +32 -1
  362. package/src/TabList/Tab.tsx +5 -1
  363. package/src/TabList/TabList.doc.mjs +3 -3
  364. package/src/TabList/TabList.test.tsx +66 -0
  365. package/src/TabList/TabList.tsx +8 -0
  366. package/src/TabList/TabMenu.tsx +4 -1
  367. package/src/Table/Table.doc.mjs +2 -0
  368. package/src/Table/Table.tsx +1 -1
  369. package/src/Table/index.ts +12 -0
  370. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +6 -2
  371. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +90 -1
  372. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +37 -5
  373. package/src/Table/plugins/rowStatus/index.ts +7 -0
  374. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +138 -0
  375. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +189 -0
  376. package/src/Table/plugins/selection/useTableSelection.test.tsx +16 -0
  377. package/src/Table/plugins/selection/useTableSelection.tsx +18 -1
  378. package/src/Table/plugins/sortable/useTableSortable.test.tsx +95 -9
  379. package/src/Table/plugins/sortable/useTableSortable.tsx +23 -7
  380. package/src/Table/plugins/tree/index.ts +12 -0
  381. package/src/Table/plugins/tree/useTableTreeData-perf.test.tsx +163 -0
  382. package/src/Table/plugins/tree/useTableTreeData.test.tsx +594 -0
  383. package/src/Table/plugins/tree/useTableTreeData.tsx +495 -0
  384. package/src/Table/plugins/tree/useTableTreeState.test.tsx +636 -0
  385. package/src/Table/plugins/tree/useTableTreeState.tsx +253 -0
  386. package/src/Table/useBaseTablePlugins.ts +6 -2
  387. package/src/Table/useTableRowStatus.doc.mjs +30 -0
  388. package/src/Table/useTableSelection.doc.mjs +65 -14
  389. package/src/Table/useTableTreeData.doc.mjs +75 -0
  390. package/src/Table/useTableTreeState.doc.mjs +98 -0
  391. package/src/TextArea/TextArea.doc.mjs +1 -1
  392. package/src/TextArea/TextArea.test.tsx +9 -1
  393. package/src/TextInput/TextInput.doc.mjs +1 -1
  394. package/src/Thumbnail/Thumbnail.doc.mjs +9 -0
  395. package/src/Thumbnail/Thumbnail.test.tsx +38 -8
  396. package/src/Thumbnail/Thumbnail.tsx +28 -1
  397. package/src/TimeInput/TimeInput.test.tsx +76 -1
  398. package/src/TimeInput/TimeInput.tsx +19 -6
  399. package/src/Tokenizer/Tokenizer.doc.mjs +1 -1
  400. package/src/Tokenizer/Tokenizer.test.tsx +137 -2
  401. package/src/Tokenizer/Tokenizer.tsx +16 -4
  402. package/src/Toolbar/Toolbar.tsx +10 -3
  403. package/src/TopNav/TopNav.test.tsx +80 -1
  404. package/src/TopNav/TopNavHeading.tsx +24 -8
  405. package/src/TopNav/TopNavMegaMenu.test.tsx +149 -1
  406. package/src/TopNav/TopNavMegaMenu.tsx +10 -4
  407. package/src/TopNav/TopNavMenu.test.tsx +118 -4
  408. package/src/TopNav/TopNavMenu.tsx +83 -5
  409. package/src/TreeList/TreeList.test.tsx +32 -1
  410. package/src/TreeList/TreeListItem.tsx +19 -10
  411. package/src/authoring/doc.ts +1 -3
  412. package/src/docs-script.test.mjs +18 -13
  413. package/src/hooks/computeOverflow.test.ts +422 -0
  414. package/src/hooks/computeOverflow.ts +290 -0
  415. package/src/hooks/useOverflow.test.ts +96 -4
  416. package/src/hooks/useOverflow.ts +77 -33
  417. package/src/postinstall.test.mjs +71 -0
  418. package/src/theme/Theme.doc.mjs +4 -4
  419. package/src/theme/Theme.tsx +1 -1
  420. package/src/theme/defineTheme.ts +1 -1
  421. package/src/theme/generateThemeRules.test.ts +65 -0
  422. package/src/theme/generateThemeRules.ts +56 -0
  423. package/src/theme/hct.ts +0 -4
  424. package/src/utils/types.ts +14 -0
  425. package/dist/TreeList/treeListItem.markers.stylex.d.ts +0 -16
  426. package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +0 -1
  427. package/dist/TreeList/treeListItem.markers.stylex.js +0 -22
  428. package/src/TreeList/treeListItem.markers.stylex.ts +0 -20
@@ -0,0 +1,116 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file MobileNavToggle.test.tsx
5
+ * @input Uses vitest, @testing-library/react, AppShell + SideNav
6
+ * @output Unit tests for MobileNavToggle ARIA wiring
7
+ * @position Testing; validates MobileNavToggle.tsx exposes aria-expanded and
8
+ * aria-controls so screen-reader users know the drawer state and target.
9
+ *
10
+ * SYNC: When MobileNavToggle.tsx changes, update tests to match new behavior
11
+ */
12
+
13
+ import {
14
+ describe,
15
+ it,
16
+ expect,
17
+ vi,
18
+ beforeAll,
19
+ beforeEach,
20
+ afterEach,
21
+ } from 'vitest';
22
+ import {render, screen, fireEvent} from '@testing-library/react';
23
+ import {AppShell} from '../AppShell/AppShell';
24
+ import {SideNav, SideNavItem, SideNavSection} from '../SideNav';
25
+
26
+ // jsdom doesn't implement showModal/close on <dialog>, so we mock them
27
+ beforeAll(() => {
28
+ HTMLDialogElement.prototype.showModal =
29
+ HTMLDialogElement.prototype.showModal ||
30
+ function (this: HTMLDialogElement) {
31
+ this.setAttribute('open', '');
32
+ };
33
+ HTMLDialogElement.prototype.close =
34
+ HTMLDialogElement.prototype.close ||
35
+ function (this: HTMLDialogElement) {
36
+ this.removeAttribute('open');
37
+ };
38
+ });
39
+
40
+ class MockResizeObserver {
41
+ observe() {}
42
+ unobserve() {}
43
+ disconnect() {}
44
+ }
45
+ vi.stubGlobal('ResizeObserver', MockResizeObserver);
46
+
47
+ function createMockMatchMedia(matches: boolean) {
48
+ return {
49
+ matches,
50
+ media: '',
51
+ onchange: null,
52
+ addEventListener: vi.fn(),
53
+ removeEventListener: vi.fn(),
54
+ addListener: vi.fn(),
55
+ removeListener: vi.fn(),
56
+ dispatchEvent: vi.fn(),
57
+ };
58
+ }
59
+
60
+ beforeEach(() => {
61
+ // Below the breakpoint so the mobile nav (and its toggle) render.
62
+ vi.stubGlobal(
63
+ 'matchMedia',
64
+ vi.fn().mockReturnValue(createMockMatchMedia(true)),
65
+ );
66
+ });
67
+
68
+ afterEach(() => {
69
+ vi.restoreAllMocks();
70
+ });
71
+
72
+ function TestShell() {
73
+ return (
74
+ <AppShell
75
+ sideNav={
76
+ <SideNav>
77
+ <SideNavSection title="Test" isHeaderHidden>
78
+ <SideNavItem label="Home" />
79
+ </SideNavSection>
80
+ </SideNav>
81
+ }
82
+ mobileNav={{breakpoint: 'md'}}>
83
+ <div>Content</div>
84
+ </AppShell>
85
+ );
86
+ }
87
+
88
+ describe('MobileNavToggle ARIA wiring', () => {
89
+ it('exposes aria-expanded="false" when the drawer is closed', () => {
90
+ render(<TestShell />);
91
+
92
+ const toggle = screen.getByRole('button', {name: /open navigation/i});
93
+ expect(toggle).toHaveAttribute('aria-expanded', 'false');
94
+ });
95
+
96
+ it('exposes aria-expanded="true" after opening the drawer', () => {
97
+ render(<TestShell />);
98
+
99
+ const toggle = screen.getByRole('button', {name: /open navigation/i});
100
+ fireEvent.click(toggle);
101
+ expect(toggle).toHaveAttribute('aria-expanded', 'true');
102
+ });
103
+
104
+ it('references the nav drawer via aria-controls that resolves to the dialog', () => {
105
+ render(<TestShell />);
106
+
107
+ const toggle = screen.getByRole('button', {name: /open navigation/i});
108
+ const controls = toggle.getAttribute('aria-controls');
109
+ expect(controls).toBeTruthy();
110
+
111
+ const target = document.getElementById(controls!);
112
+ expect(target).not.toBeNull();
113
+ expect(target).toBe(screen.getAllByRole('dialog', {hidden: true})[0]);
114
+ expect(target?.tagName).toBe('DIALOG');
115
+ });
116
+ });
@@ -69,7 +69,13 @@ export function MobileNavToggle({
69
69
  }: MobileNavToggleProps) {
70
70
  const t = useTranslator();
71
71
  const label = labelFromProps ?? t('@astryx.mobileNav.toggle.open');
72
- const {isMobile, isMobileNavEnabled, toggleMobileNav} = useAppShellMobile();
72
+ const {
73
+ isMobile,
74
+ isMobileNavEnabled,
75
+ isMobileNavOpen,
76
+ mobileNavId,
77
+ toggleMobileNav,
78
+ } = useAppShellMobile();
73
79
 
74
80
  // Don't render above the breakpoint or when mobile nav is disabled
75
81
  if (!isMobile || !isMobileNavEnabled) {
@@ -83,6 +89,8 @@ export function MobileNavToggle({
83
89
  label={label}
84
90
  icon={children ?? <Icon icon="menu" color="inherit" />}
85
91
  onClick={toggleMobileNav}
92
+ aria-expanded={isMobileNavOpen}
93
+ aria-controls={mobileNavId || undefined}
86
94
  data-testid={testId ?? 'mobile-nav-toggle'}
87
95
  xstyle={xstyle}
88
96
  className={className}
@@ -99,7 +99,8 @@ export const docs = {
99
99
  {
100
100
  name: 'hasSearch',
101
101
  type: 'boolean',
102
- description: 'Whether to show a search input for filtering options.',
102
+ description:
103
+ 'Whether to show a search input for filtering options. As the user types, the match count (or "No results found") is announced to screen readers via a polite live region.',
103
104
  },
104
105
  {
105
106
  name: 'searchPlaceholder',
@@ -239,7 +240,8 @@ export const docsZh = {
239
240
  hasSearch: '是否显示用于过滤选项的搜索输入。',
240
241
  searchPlaceholder: '搜索输入的占位文本。',
241
242
  isDisabled: '禁用选择器。',
242
- htmlName: '用于表单提交的 HTML name 属性。为每个已选值渲染一个隐藏输入,类似原生多选。',
243
+ htmlName:
244
+ '用于表单提交的 HTML name 属性。为每个已选值渲染一个隐藏输入,类似原生多选。',
243
245
  disabledMessage:
244
246
  '解释选择器被禁用的原因。与 isDisabled 一起使用时,悬停/键盘聚焦时显示工具提示,并通过 aria-disabled 保持触发器可聚焦(仍无法激活)。请使用此属性,而不是用 Tooltip 包裹被禁用的选择器。',
245
247
  isLabelHidden: '视觉上隐藏标签同时保持其可访问性。',
@@ -360,6 +360,30 @@ describe('MultiSelector', () => {
360
360
  expect(activeId).toBeTruthy();
361
361
  });
362
362
 
363
+ it('End/Home jump the highlight to the last/first option (non-search)', async () => {
364
+ const user = userEvent.setup();
365
+ render(
366
+ <MultiSelector
367
+ label="Fruit"
368
+ options={defaultOptions}
369
+ value={[]}
370
+ onChange={() => {}}
371
+ />,
372
+ );
373
+
374
+ const trigger = screen.getByRole('combobox');
375
+ await user.click(trigger);
376
+ const options = screen.getAllByRole('option', h);
377
+
378
+ await user.keyboard('{End}');
379
+ expect(trigger).toHaveAttribute(
380
+ 'aria-activedescendant',
381
+ options[options.length - 1].id,
382
+ );
383
+ await user.keyboard('{Home}');
384
+ expect(trigger).toHaveAttribute('aria-activedescendant', options[0].id);
385
+ });
386
+
363
387
  it('toggles item with Enter key', async () => {
364
388
  const user = userEvent.setup();
365
389
  const onChange = vi.fn();
@@ -535,6 +559,65 @@ describe('MultiSelector', () => {
535
559
  expect(options).toHaveLength(1);
536
560
  });
537
561
 
562
+ it('PageDown/PageUp jump the highlight to the last/first filtered option', async () => {
563
+ const user = userEvent.setup();
564
+ render(
565
+ <MultiSelector
566
+ label="Fruit"
567
+ options={defaultOptions}
568
+ value={[]}
569
+ onChange={() => {}}
570
+ hasSearch
571
+ />,
572
+ );
573
+
574
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
575
+ const searchInput = screen.getByRole('combobox', h);
576
+ // Filter to Banana and Orange so "last" means last *visible* option.
577
+ await user.type(searchInput, 'an');
578
+ const options = screen.getAllByRole('option', h);
579
+ expect(options).toHaveLength(2);
580
+
581
+ await user.keyboard('{PageDown}');
582
+ expect(searchInput).toHaveAttribute(
583
+ 'aria-activedescendant',
584
+ options[options.length - 1].id,
585
+ );
586
+ await user.keyboard('{PageUp}');
587
+ expect(searchInput).toHaveAttribute('aria-activedescendant', options[0].id);
588
+ });
589
+
590
+ it('Home/End move the search caret, not the option highlight', async () => {
591
+ const user = userEvent.setup();
592
+ render(
593
+ <MultiSelector
594
+ label="Fruit"
595
+ options={defaultOptions}
596
+ value={[]}
597
+ onChange={() => {}}
598
+ hasSearch
599
+ />,
600
+ );
601
+
602
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
603
+ const searchInput = screen.getByRole<HTMLInputElement>('combobox', h);
604
+ await user.type(searchInput, 'an');
605
+ expect(searchInput.selectionStart).toBe(2);
606
+ const activeBefore = searchInput.getAttribute('aria-activedescendant');
607
+ // Home/End stay on the input for caret movement (APG editable combobox);
608
+ // the option highlight must not move.
609
+ await user.keyboard('{Home}');
610
+ expect(searchInput.selectionStart).toBe(0);
611
+ expect(searchInput.getAttribute('aria-activedescendant')).toBe(
612
+ activeBefore,
613
+ );
614
+ await user.keyboard('{End}');
615
+ expect(searchInput.selectionStart).toBe(2);
616
+ expect(searchInput.getAttribute('aria-activedescendant')).toBe(
617
+ activeBefore,
618
+ );
619
+ });
620
+
538
621
  it('shows empty state when search has no results', async () => {
539
622
  const user = userEvent.setup();
540
623
  render(
@@ -554,6 +637,82 @@ describe('MultiSelector', () => {
554
637
  expect(screen.getByText('No results found')).toBeInTheDocument();
555
638
  });
556
639
 
640
+ describe('result announcements', () => {
641
+ it('announces the match count politely while searching', async () => {
642
+ const user = userEvent.setup();
643
+ render(
644
+ <MultiSelector
645
+ label="Fruit"
646
+ options={defaultOptions}
647
+ value={EMPTY_VALUE}
648
+ onChange={() => {}}
649
+ hasSearch
650
+ />,
651
+ );
652
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
653
+ // "an" matches Banana and Orange.
654
+ await user.type(screen.getByRole('combobox', h), 'an');
655
+ await waitFor(() => {
656
+ expect(politeRegion()).toHaveTextContent('2 results');
657
+ });
658
+ });
659
+
660
+ it('announces the singular form when one option matches', async () => {
661
+ const user = userEvent.setup();
662
+ render(
663
+ <MultiSelector
664
+ label="Fruit"
665
+ options={defaultOptions}
666
+ value={EMPTY_VALUE}
667
+ onChange={() => {}}
668
+ hasSearch
669
+ />,
670
+ );
671
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
672
+ // "app" matches only Apple. Anchored so it cannot pass on "1 results".
673
+ await user.type(screen.getByRole('combobox', h), 'app');
674
+ await waitFor(() => {
675
+ expect(politeRegion()).toHaveTextContent(/^1 result$/);
676
+ });
677
+ });
678
+
679
+ it('announces "No results found" when nothing matches', async () => {
680
+ const user = userEvent.setup();
681
+ render(
682
+ <MultiSelector
683
+ label="Fruit"
684
+ options={defaultOptions}
685
+ value={EMPTY_VALUE}
686
+ onChange={() => {}}
687
+ hasSearch
688
+ />,
689
+ );
690
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
691
+ await user.type(screen.getByRole('combobox', h), 'xyz');
692
+ await waitFor(() => {
693
+ expect(politeRegion()).toHaveTextContent('No results found');
694
+ });
695
+ });
696
+
697
+ it('does not announce results until the user searches', async () => {
698
+ const user = userEvent.setup();
699
+ render(
700
+ <MultiSelector
701
+ label="Fruit"
702
+ options={defaultOptions}
703
+ value={EMPTY_VALUE}
704
+ onChange={() => {}}
705
+ hasSearch
706
+ />,
707
+ );
708
+ // Popover closed: nothing announced.
709
+ expect(politeRegion()?.textContent ?? '').toBe('');
710
+ // Open with an empty query: still nothing announced.
711
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
712
+ expect(politeRegion()?.textContent ?? '').toBe('');
713
+ });
714
+ });
715
+
557
716
  it('renders with description', () => {
558
717
  render(
559
718
  <MultiSelector
@@ -1072,7 +1231,9 @@ describe('MultiSelector', () => {
1072
1231
  />
1073
1232
  </form>,
1074
1233
  );
1075
- expect([...new FormData(container.querySelector('form')!).keys()]).toEqual([]);
1234
+ expect([
1235
+ ...new FormData(container.querySelector('form')!).keys(),
1236
+ ]).toEqual([]);
1076
1237
  });
1077
1238
  });
1078
1239
  });
@@ -562,6 +562,23 @@ export interface MultiSelectorProps<
562
562
  'data-testid'?: string;
563
563
  }
564
564
 
565
+ // Case-insensitive substring filter over the selectable options. Shared by the
566
+ // `filteredItems` memo (rendering) and the search-change handler, which needs
567
+ // the count for the *next* query synchronously to announce it exactly once per
568
+ // keystroke rather than reacting to state in an effect.
569
+ function filterOptionsByQuery(
570
+ items: MultiSelectorOptionData[],
571
+ query: string,
572
+ ): MultiSelectorOptionData[] {
573
+ if (!query) {
574
+ return items;
575
+ }
576
+ const q = query.toLowerCase();
577
+ return items.filter(item =>
578
+ (item.label ?? item.value).toLowerCase().includes(q),
579
+ );
580
+ }
581
+
565
582
  /**
566
583
  * A multi-select dropdown component with checkboxes for choosing
567
584
  * multiple items from a list of options.
@@ -692,15 +709,10 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
692
709
  );
693
710
 
694
711
  // 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]);
712
+ const filteredItems = useMemo(
713
+ () => filterOptionsByQuery(selectableItems, searchQuery),
714
+ [selectableItems, searchQuery],
715
+ );
704
716
 
705
717
  // Single source of truth for item order. Both the hook (keyboard navigation)
706
718
  // and renderOptions (DOM rendering) consume this list — no independent sorting.
@@ -772,8 +784,11 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
772
784
  const handleLayerHide = useCallback(() => {
773
785
  setSearchQuery('');
774
786
  setSelectedAtOpen(null);
787
+ // Clear any lingering result count when the popover closes so stale status
788
+ // text does not linger in the a11y tree.
789
+ announce('');
775
790
  triggerRef.current?.focus();
776
- }, []);
791
+ }, [announce]);
777
792
 
778
793
  const popover = usePopover({
779
794
  hasLightDismiss: true,
@@ -793,6 +808,30 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
793
808
  // eslint-disable-next-line @eslint-react/exhaustive-deps -- mount-only: isDefaultOpen is not reactive
794
809
  }, []);
795
810
 
811
+ // Announce the filtered result count from the query-change handler (matching
812
+ // BaseTypeahead) rather than a reactive effect: computing the count for the
813
+ // next query here fires the announcement exactly once per keystroke and does
814
+ // not re-speak on unrelated re-renders. Reuses the announce instance shared
815
+ // with the selection-count announcements above.
816
+ const handleSearchChange = useCallback(
817
+ (event: React.ChangeEvent<HTMLInputElement>) => {
818
+ const nextQuery = event.target.value;
819
+ setSearchQuery(nextQuery);
820
+ if (nextQuery.length === 0) {
821
+ // Emptying the query clears the region rather than announcing a count.
822
+ announce('');
823
+ return;
824
+ }
825
+ const count = filterOptionsByQuery(selectableItems, nextQuery).length;
826
+ announce(
827
+ count === 0
828
+ ? 'No results found'
829
+ : `${count} result${count === 1 ? '' : 's'}`,
830
+ );
831
+ },
832
+ [announce, selectableItems],
833
+ );
834
+
796
835
  // Handle toggle
797
836
  // Clear all selected values. Shared by the clear button and the keyboard
798
837
  // Delete/Backspace path so clearing is reachable without a mouse.
@@ -1041,14 +1080,17 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1041
1080
  aria-label={t('@astryx.multiSelector.searchOptions')}
1042
1081
  type="text"
1043
1082
  value={searchQuery}
1044
- onChange={e => setSearchQuery(e.target.value)}
1083
+ onChange={handleSearchChange}
1045
1084
  onKeyDown={e => {
1046
1085
  // Arrow keys navigate options; Enter toggles; Escape/Tab close.
1047
1086
  // Space and Home/End are left to the input (type a space / move
1048
- // the caret), not forwarded to option navigation.
1087
+ // the caret) per the APG editable combobox; PageUp/PageDown are
1088
+ // the sanctioned substitute for jumping to the first/last option.
1049
1089
  if (
1050
1090
  e.key === 'ArrowDown' ||
1051
1091
  e.key === 'ArrowUp' ||
1092
+ e.key === 'PageUp' ||
1093
+ e.key === 'PageDown' ||
1052
1094
  e.key === 'Enter' ||
1053
1095
  e.key === 'Escape' ||
1054
1096
  e.key === 'Tab'
@@ -1067,6 +1109,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1067
1109
  listboxId,
1068
1110
  searchQuery,
1069
1111
  searchPlaceholder,
1112
+ handleSearchChange,
1070
1113
  onKeyDown,
1071
1114
  popover.isOpen,
1072
1115
  highlightedIndex,
@@ -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
@@ -82,12 +82,12 @@ export const docs = {
82
82
  {
83
83
  name: 'startIcon',
84
84
  type: 'IconType',
85
- description: 'Icon to display at the start of the input. See `npx astryx docs icons` for valid semantic names.',
85
+ description: 'Icon to display at the start of the input. See `astryx docs icons` for valid semantic names.',
86
86
  },
87
87
  {
88
88
  name: 'labelIcon',
89
89
  type: 'IconType',
90
- description: 'Icon to display before the label text. See `npx astryx docs icons` for valid semantic names.',
90
+ description: 'Icon to display before the label text. See `astryx docs icons` for valid semantic names.',
91
91
  },
92
92
  {
93
93
  name: 'status',
@@ -9,11 +9,19 @@
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
16
  import {NumberInput} from './NumberInput';
17
+ import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
18
+
19
+ // FieldStatus announces status messages through the persistent useAnnounce
20
+ // singletons; remove them between tests so role/aria-live queries in this
21
+ // file never match a leftover region.
22
+ afterEach(() => {
23
+ __resetLiveRegionsForTest();
24
+ });
17
25
 
18
26
  // Mock showPopover/hidePopover since jsdom does not implement them. Used by the
19
27
  // disabledMessage tooltip.
@@ -871,9 +879,7 @@ describe('NumberInput', () => {
871
879
  describe('keyboard clearing with hasClear (#3599)', () => {
872
880
  it('commits null when the input is emptied and blurred', () => {
873
881
  const onChange = vi.fn();
874
- render(
875
- <NumberInput label="Qty" hasClear value={42} onChange={onChange} />,
876
- );
882
+ render(<NumberInput label="Qty" hasClear value={42} onChange={onChange} />);
877
883
  const input = screen.getByLabelText('Qty');
878
884
  fireEvent.change(input, {target: {value: ''}});
879
885
  fireEvent.blur(input);
@@ -882,9 +888,7 @@ describe('keyboard clearing with hasClear (#3599)', () => {
882
888
 
883
889
  it('commits null when the input is emptied and Enter is pressed', () => {
884
890
  const onChange = vi.fn();
885
- render(
886
- <NumberInput label="Qty" hasClear value={42} onChange={onChange} />,
887
- );
891
+ render(<NumberInput label="Qty" hasClear value={42} onChange={onChange} />);
888
892
  const input = screen.getByLabelText('Qty');
889
893
  fireEvent.change(input, {target: {value: ''}});
890
894
  fireEvent.keyDown(input, {key: 'Enter'});
@@ -311,8 +311,7 @@ type NumberInputPropsClearable = NumberInputPropsBase & {
311
311
  };
312
312
 
313
313
  export type NumberInputProps =
314
- | NumberInputPropsNonClearable
315
- | NumberInputPropsClearable;
314
+ NumberInputPropsNonClearable | NumberInputPropsClearable;
316
315
 
317
316
  /**
318
317
  * Parse and validate a string input as a number.