@astryxdesign/core 0.1.1 → 0.1.2-canary.04e71b2

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 (631) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/README.md +131 -13
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +11 -5
  5. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
  6. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  7. package/dist/AvatarGroup/AvatarGroupOverflow.js +4 -1
  8. package/dist/Banner/Banner.d.ts +7 -0
  9. package/dist/Banner/Banner.d.ts.map +1 -1
  10. package/dist/Banner/Banner.js +9 -2
  11. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +1 -1
  12. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  13. package/dist/Breadcrumbs/BreadcrumbItem.js +24 -5
  14. package/dist/Button/Button.d.ts +12 -2
  15. package/dist/Button/Button.d.ts.map +1 -1
  16. package/dist/Button/Button.js +53 -17
  17. package/dist/Calendar/Calendar.d.ts +5 -4
  18. package/dist/Calendar/Calendar.d.ts.map +1 -1
  19. package/dist/Calendar/Calendar.js +82 -43
  20. package/dist/Calendar/hooks/index.d.ts +0 -2
  21. package/dist/Calendar/hooks/index.d.ts.map +1 -1
  22. package/dist/Calendar/hooks/index.js +1 -2
  23. package/dist/Calendar/index.d.ts +3 -3
  24. package/dist/Calendar/index.d.ts.map +1 -1
  25. package/dist/Calendar/index.js +1 -1
  26. package/dist/Calendar/styles.d.ts +3 -9
  27. package/dist/Calendar/styles.d.ts.map +1 -1
  28. package/dist/Calendar/styles.js +3 -11
  29. package/dist/Carousel/Carousel.d.ts.map +1 -1
  30. package/dist/Carousel/Carousel.js +17 -3
  31. package/dist/Center/Center.d.ts +11 -1
  32. package/dist/Center/Center.d.ts.map +1 -1
  33. package/dist/Center/Center.js +9 -3
  34. package/dist/Chat/ChatMessageList.d.ts +14 -1
  35. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  36. package/dist/Chat/ChatMessageList.js +2 -0
  37. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  38. package/dist/Chat/useTriggerMenu.js +4 -1
  39. package/dist/CheckboxInput/CheckboxInput.d.ts +23 -2
  40. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  41. package/dist/CheckboxInput/CheckboxInput.js +51 -9
  42. package/dist/CheckboxList/CheckboxList.d.ts +13 -1
  43. package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
  44. package/dist/CheckboxList/CheckboxList.js +39 -9
  45. package/dist/CheckboxList/CheckboxListContext.d.ts +7 -0
  46. package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
  47. package/dist/Citation/Citation.d.ts.map +1 -1
  48. package/dist/Citation/Citation.js +3 -3
  49. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  50. package/dist/CodeBlock/CodeBlock.js +49 -37
  51. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  52. package/dist/CommandPalette/CommandPalette.js +5 -5
  53. package/dist/ContextMenu/ContextMenu.d.ts +4 -5
  54. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  55. package/dist/ContextMenu/ContextMenu.js +93 -11
  56. package/dist/DateInput/DateInput.d.ts +23 -1
  57. package/dist/DateInput/DateInput.d.ts.map +1 -1
  58. package/dist/DateInput/DateInput.js +59 -8
  59. package/dist/DateRangeInput/DateRangeInput.d.ts +23 -1
  60. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  61. package/dist/DateRangeInput/DateRangeInput.js +42 -9
  62. package/dist/DateTimeInput/DateTimeInput.d.ts +35 -2
  63. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  64. package/dist/DateTimeInput/DateTimeInput.js +78 -15
  65. package/dist/Dialog/Dialog.d.ts.map +1 -1
  66. package/dist/Dialog/Dialog.js +12 -0
  67. package/dist/DropdownMenu/DropdownMenu.d.ts +1 -8
  68. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  69. package/dist/DropdownMenu/DropdownMenu.js +42 -14
  70. package/dist/EmptyState/EmptyState.d.ts.map +1 -1
  71. package/dist/EmptyState/EmptyState.js +7 -1
  72. package/dist/Field/Field.d.ts +19 -2
  73. package/dist/Field/Field.d.ts.map +1 -1
  74. package/dist/Field/Field.js +4 -0
  75. package/dist/Field/FieldLabel.d.ts +21 -2
  76. package/dist/Field/FieldLabel.d.ts.map +1 -1
  77. package/dist/Field/FieldLabel.js +41 -26
  78. package/dist/FileInput/FileInput.d.ts +22 -1
  79. package/dist/FileInput/FileInput.d.ts.map +1 -1
  80. package/dist/FileInput/FileInput.js +56 -10
  81. package/dist/Grid/Grid.d.ts +11 -1
  82. package/dist/Grid/Grid.d.ts.map +1 -1
  83. package/dist/Grid/Grid.js +8 -0
  84. package/dist/HoverCard/HoverCard.d.ts +2 -2
  85. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  86. package/dist/HoverCard/HoverCard.js +18 -6
  87. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  88. package/dist/HoverCard/useHoverCard.js +7 -3
  89. package/dist/InputGroup/InputGroup.d.ts +2 -2
  90. package/dist/InputGroup/InputGroup.d.ts.map +1 -1
  91. package/dist/InputGroup/InputGroup.js +14 -5
  92. package/dist/InputGroup/InputGroupContext.d.ts +4 -0
  93. package/dist/InputGroup/InputGroupContext.d.ts.map +1 -1
  94. package/dist/InputGroup/InputGroupContext.js +2 -2
  95. package/dist/InputGroup/index.d.ts +1 -0
  96. package/dist/InputGroup/index.d.ts.map +1 -1
  97. package/dist/Kbd/Kbd.d.ts.map +1 -1
  98. package/dist/Kbd/Kbd.js +34 -2
  99. package/dist/Layer/anchorName.d.ts +20 -0
  100. package/dist/Layer/anchorName.d.ts.map +1 -0
  101. package/dist/Layer/anchorName.js +36 -0
  102. package/dist/Layer/layerAnimations.stylex.d.ts +4 -0
  103. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  104. package/dist/Layer/layerAnimations.stylex.js +8 -4
  105. package/dist/Layer/useLayer.d.ts +29 -0
  106. package/dist/Layer/useLayer.d.ts.map +1 -1
  107. package/dist/Layer/useLayer.js +55 -11
  108. package/dist/Layout/Layout.d.ts +4 -11
  109. package/dist/Layout/Layout.d.ts.map +1 -1
  110. package/dist/Layout/Layout.js +10 -11
  111. package/dist/Layout/LayoutFooter.d.ts +2 -2
  112. package/dist/Layout/LayoutFooter.d.ts.map +1 -1
  113. package/dist/Layout/LayoutHeader.d.ts +2 -2
  114. package/dist/Layout/LayoutHeader.d.ts.map +1 -1
  115. package/dist/Layout/LayoutPanel.d.ts +2 -2
  116. package/dist/Layout/LayoutPanel.d.ts.map +1 -1
  117. package/dist/Layout/padding.stylex.d.ts +51 -0
  118. package/dist/Layout/padding.stylex.d.ts.map +1 -1
  119. package/dist/Layout/padding.stylex.js +63 -0
  120. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  121. package/dist/Lightbox/Lightbox.js +4 -2
  122. package/dist/Link/Link.d.ts +17 -2
  123. package/dist/Link/Link.d.ts.map +1 -1
  124. package/dist/Link/Link.js +20 -9
  125. package/dist/Link/LinkProvider.d.ts +1 -0
  126. package/dist/Link/LinkProvider.d.ts.map +1 -1
  127. package/dist/Link/LinkProvider.js +1 -0
  128. package/dist/Markdown/Markdown.d.ts.map +1 -1
  129. package/dist/Markdown/Markdown.js +18 -3
  130. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  131. package/dist/MobileNav/MobileNav.js +13 -0
  132. package/dist/MoreMenu/MoreMenu.d.ts +1 -7
  133. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  134. package/dist/MoreMenu/MoreMenu.js +0 -2
  135. package/dist/MultiSelector/MultiSelector.d.ts +25 -2
  136. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  137. package/dist/MultiSelector/MultiSelector.js +103 -22
  138. package/dist/MultiSelector/hooks.d.ts +12 -1
  139. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  140. package/dist/MultiSelector/hooks.js +14 -1
  141. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  142. package/dist/NavMenu/NavHeadingMenu.js +34 -3
  143. package/dist/NumberInput/NumberInput.d.ts +23 -2
  144. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  145. package/dist/NumberInput/NumberInput.js +62 -12
  146. package/dist/Pagination/Pagination.d.ts +1 -1
  147. package/dist/Pagination/Pagination.d.ts.map +1 -1
  148. package/dist/Pagination/Pagination.js +46 -28
  149. package/dist/Popover/usePopover.d.ts +25 -2
  150. package/dist/Popover/usePopover.d.ts.map +1 -1
  151. package/dist/Popover/usePopover.js +18 -6
  152. package/dist/PowerSearch/PowerSearch.d.ts +11 -1
  153. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  154. package/dist/PowerSearch/PowerSearch.js +6 -1
  155. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  156. package/dist/ProgressBar/ProgressBar.js +3 -5
  157. package/dist/RadioList/RadioList.d.ts +20 -2
  158. package/dist/RadioList/RadioList.d.ts.map +1 -1
  159. package/dist/RadioList/RadioList.js +105 -9
  160. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  161. package/dist/RadioList/RadioListItem.js +13 -2
  162. package/dist/SegmentedControl/SegmentedControl.d.ts +13 -2
  163. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  164. package/dist/SegmentedControl/SegmentedControl.js +78 -44
  165. package/dist/SegmentedControl/SegmentedControlContext.d.ts +7 -0
  166. package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
  167. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  168. package/dist/SegmentedControl/SegmentedControlItem.js +13 -2
  169. package/dist/Selector/Selector.d.ts +22 -1
  170. package/dist/Selector/Selector.d.ts.map +1 -1
  171. package/dist/Selector/Selector.js +84 -23
  172. package/dist/Selector/hooks.d.ts +8 -1
  173. package/dist/Selector/hooks.d.ts.map +1 -1
  174. package/dist/Selector/hooks.js +14 -1
  175. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  176. package/dist/SideNav/SideNavItem.js +1 -1
  177. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  178. package/dist/Skeleton/Skeleton.js +8 -2
  179. package/dist/Slider/Slider.d.ts +21 -0
  180. package/dist/Slider/Slider.d.ts.map +1 -1
  181. package/dist/Slider/Slider.js +38 -11
  182. package/dist/Spinner/Spinner.d.ts.map +1 -1
  183. package/dist/Spinner/Spinner.js +1 -1
  184. package/dist/Stack/Stack.d.ts +38 -23
  185. package/dist/Stack/Stack.d.ts.map +1 -1
  186. package/dist/Stack/Stack.js +25 -1
  187. package/dist/Stack/StackItem.d.ts +12 -23
  188. package/dist/Stack/StackItem.d.ts.map +1 -1
  189. package/dist/Stack/StackItem.js +8 -1
  190. package/dist/Switch/Switch.d.ts +23 -2
  191. package/dist/Switch/Switch.d.ts.map +1 -1
  192. package/dist/Switch/Switch.js +41 -11
  193. package/dist/TabList/Tab.d.ts.map +1 -1
  194. package/dist/TabList/Tab.js +6 -0
  195. package/dist/TabList/TabList.d.ts +16 -4
  196. package/dist/TabList/TabList.d.ts.map +1 -1
  197. package/dist/TabList/TabList.js +86 -10
  198. package/dist/TabList/TabListContext.d.ts +5 -0
  199. package/dist/TabList/TabListContext.d.ts.map +1 -1
  200. package/dist/TabList/TabListContext.js +5 -0
  201. package/dist/TabList/TabMenu.d.ts.map +1 -1
  202. package/dist/TabList/TabMenu.js +6 -1
  203. package/dist/Table/BaseTable.d.ts.map +1 -1
  204. package/dist/Table/BaseTable.js +38 -16
  205. package/dist/Table/Table.d.ts.map +1 -1
  206. package/dist/Table/Table.js +36 -7
  207. package/dist/Table/TableCell.d.ts +8 -1
  208. package/dist/Table/TableCell.d.ts.map +1 -1
  209. package/dist/Table/TableCell.js +10 -10
  210. package/dist/Table/TableHeaderCell.d.ts +8 -1
  211. package/dist/Table/TableHeaderCell.d.ts.map +1 -1
  212. package/dist/Table/TableHeaderCell.js +16 -16
  213. package/dist/Table/TableRow.d.ts +6 -1
  214. package/dist/Table/TableRow.d.ts.map +1 -1
  215. package/dist/Table/TableRow.js +15 -7
  216. package/dist/Table/index.d.ts +4 -1
  217. package/dist/Table/index.d.ts.map +1 -1
  218. package/dist/Table/index.js +3 -2
  219. package/dist/Table/plugins/pagination/paginateData.d.ts +2 -1
  220. package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
  221. package/dist/Table/plugins/pagination/paginateData.js +8 -3
  222. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  223. package/dist/Table/plugins/pagination/useTablePagination.js +6 -1
  224. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  225. package/dist/Table/plugins/sortable/useTableSortable.js +59 -3
  226. package/dist/Table/plugins/stickyColumns/index.d.ts +3 -0
  227. package/dist/Table/plugins/stickyColumns/index.d.ts.map +1 -0
  228. package/dist/Table/plugins/stickyColumns/index.js +3 -0
  229. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts +25 -0
  230. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -0
  231. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +369 -0
  232. package/dist/Table/tableContextMenu.d.ts +33 -0
  233. package/dist/Table/tableContextMenu.d.ts.map +1 -0
  234. package/dist/Table/tableContextMenu.js +133 -0
  235. package/dist/Table/types.d.ts +161 -5
  236. package/dist/Table/types.d.ts.map +1 -1
  237. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  238. package/dist/Table/useBaseTablePlugins.js +1 -1
  239. package/dist/Text/text.stylex.d.ts +2 -2
  240. package/dist/Text/text.stylex.js +2 -2
  241. package/dist/TextArea/TextArea.d.ts +22 -1
  242. package/dist/TextArea/TextArea.d.ts.map +1 -1
  243. package/dist/TextArea/TextArea.js +41 -9
  244. package/dist/TextInput/TextInput.d.ts +23 -2
  245. package/dist/TextInput/TextInput.d.ts.map +1 -1
  246. package/dist/TextInput/TextInput.js +55 -10
  247. package/dist/TimeInput/TimeInput.d.ts +23 -1
  248. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  249. package/dist/TimeInput/TimeInput.js +53 -10
  250. package/dist/Timestamp/Timestamp.d.ts +1 -1
  251. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  252. package/dist/Timestamp/Timestamp.js +28 -3
  253. package/dist/Toast/Toast.d.ts.map +1 -1
  254. package/dist/Toast/Toast.js +14 -0
  255. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  256. package/dist/Toast/ToastViewport.js +132 -3
  257. package/dist/ToggleButton/ToggleButton.d.ts +7 -9
  258. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  259. package/dist/ToggleButton/ToggleButton.js +34 -57
  260. package/dist/Tokenizer/Tokenizer.d.ts +12 -1
  261. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  262. package/dist/Tokenizer/Tokenizer.js +27 -5
  263. package/dist/Toolbar/Toolbar.d.ts +3 -3
  264. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  265. package/dist/Toolbar/Toolbar.js +51 -7
  266. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  267. package/dist/Tooltip/useTooltip.js +57 -9
  268. package/dist/TreeList/TreeList.d.ts +1 -1
  269. package/dist/TreeList/TreeList.d.ts.map +1 -1
  270. package/dist/TreeList/TreeList.js +72 -2
  271. package/dist/TreeList/TreeListItem.d.ts +11 -1
  272. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  273. package/dist/TreeList/TreeListItem.js +51 -12
  274. package/dist/TreeList/treeListItem.markers.stylex.d.ts +16 -0
  275. package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +1 -0
  276. package/dist/TreeList/treeListItem.markers.stylex.js +22 -0
  277. package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
  278. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  279. package/dist/Typeahead/BaseTypeahead.js +67 -6
  280. package/dist/Typeahead/Typeahead.d.ts +24 -1
  281. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  282. package/dist/Typeahead/Typeahead.js +29 -5
  283. package/dist/VisuallyHidden/VisuallyHidden.d.ts +61 -0
  284. package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
  285. package/dist/VisuallyHidden/VisuallyHidden.js +65 -0
  286. package/dist/VisuallyHidden/index.d.ts +11 -0
  287. package/dist/VisuallyHidden/index.d.ts.map +1 -0
  288. package/dist/VisuallyHidden/index.js +13 -0
  289. package/dist/astryx.css +40 -1
  290. package/dist/astryx.umd.js +147 -0
  291. package/dist/astryx.umd.js.map +7 -0
  292. package/dist/hooks/focusableSelector.d.ts +22 -0
  293. package/dist/hooks/focusableSelector.d.ts.map +1 -0
  294. package/dist/hooks/focusableSelector.js +24 -0
  295. package/dist/hooks/index.d.ts +12 -2
  296. package/dist/hooks/index.d.ts.map +1 -1
  297. package/dist/hooks/index.js +7 -2
  298. package/dist/hooks/useAnnounce.d.ts +40 -0
  299. package/dist/hooks/useAnnounce.d.ts.map +1 -0
  300. package/dist/hooks/useAnnounce.js +142 -0
  301. package/dist/hooks/useClickableContainer.d.ts +16 -1
  302. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  303. package/dist/hooks/useClickableContainer.js +16 -4
  304. package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
  305. package/dist/hooks/useEntryAnimation.js +4 -4
  306. package/dist/hooks/useFocusTrap.d.ts +18 -0
  307. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  308. package/dist/hooks/useFocusTrap.js +87 -3
  309. package/dist/hooks/useGridFocus.d.ts +88 -1
  310. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  311. package/dist/hooks/useGridFocus.js +261 -62
  312. package/dist/hooks/useKeyboardHint.d.ts +82 -0
  313. package/dist/hooks/useKeyboardHint.d.ts.map +1 -0
  314. package/dist/hooks/useKeyboardHint.js +220 -0
  315. package/dist/hooks/useListFocus.d.ts +78 -7
  316. package/dist/hooks/useListFocus.d.ts.map +1 -1
  317. package/dist/hooks/useListFocus.js +305 -52
  318. package/dist/hooks/useLongPress.d.ts +21 -0
  319. package/dist/hooks/useLongPress.d.ts.map +1 -0
  320. package/dist/hooks/useLongPress.js +89 -0
  321. package/dist/hooks/useTreeFocus.d.ts +157 -0
  322. package/dist/hooks/useTreeFocus.d.ts.map +1 -0
  323. package/dist/hooks/useTreeFocus.js +337 -0
  324. package/dist/hooks/useTypeahead.d.ts +63 -0
  325. package/dist/hooks/useTypeahead.d.ts.map +1 -0
  326. package/dist/hooks/useTypeahead.js +117 -0
  327. package/dist/index.d.ts +1 -0
  328. package/dist/index.d.ts.map +1 -1
  329. package/dist/index.js +1 -0
  330. package/dist/theme/generateThemeRules.js +1 -1
  331. package/dist/theme/syntax/SyntaxTheme.d.ts +1 -1
  332. package/dist/theme/syntax/SyntaxTheme.js +1 -1
  333. package/dist/theme/syntax/defineSyntaxTheme.d.ts.map +1 -1
  334. package/dist/theme/syntax/defineSyntaxTheme.js +2 -2
  335. package/dist/theme/syntax/presets.d.ts +1 -1
  336. package/dist/theme/syntax/presets.d.ts.map +1 -1
  337. package/dist/theme/syntax/presets.js +1 -1
  338. package/dist/theme/syntax/tokens.d.ts +1 -1
  339. package/dist/theme/syntax/tokens.js +1 -1
  340. package/dist/theme/types.d.ts +1 -1
  341. package/dist/utils/dateTypes.d.ts +12 -0
  342. package/dist/utils/dateTypes.d.ts.map +1 -1
  343. package/dist/utils/dateTypes.js +41 -1
  344. package/dist/utils/index.d.ts +4 -0
  345. package/dist/utils/index.d.ts.map +1 -1
  346. package/dist/utils/index.js +3 -1
  347. package/dist/utils/inputAria.d.ts +29 -0
  348. package/dist/utils/inputAria.d.ts.map +1 -0
  349. package/dist/utils/inputAria.js +30 -0
  350. package/dist/utils/timeParser.d.ts.map +1 -1
  351. package/dist/utils/timeParser.js +5 -2
  352. package/package.json +15 -5
  353. package/src/Avatar/Avatar.test.tsx +37 -0
  354. package/src/Avatar/Avatar.tsx +10 -5
  355. package/src/AvatarGroup/AvatarGroupOverflow.tsx +3 -0
  356. package/src/Banner/Banner.doc.mjs +8 -0
  357. package/src/Banner/Banner.test.tsx +16 -7
  358. package/src/Banner/Banner.tsx +9 -2
  359. package/src/Breadcrumbs/BreadcrumbItem.tsx +25 -7
  360. package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -10
  361. package/src/Button/Button.doc.mjs +6 -0
  362. package/src/Button/Button.test.tsx +105 -11
  363. package/src/Button/Button.tsx +76 -21
  364. package/src/Calendar/Calendar.doc.mjs +4 -4
  365. package/src/Calendar/Calendar.test.tsx +168 -30
  366. package/src/Calendar/Calendar.tsx +121 -66
  367. package/src/Calendar/hooks/index.ts +0 -6
  368. package/src/Calendar/index.ts +1 -3
  369. package/src/Calendar/styles.ts +4 -9
  370. package/src/Carousel/Carousel.doc.mjs +7 -1
  371. package/src/Carousel/Carousel.test.tsx +20 -0
  372. package/src/Carousel/Carousel.tsx +16 -1
  373. package/src/Center/Center.doc.mjs +12 -2
  374. package/src/Center/Center.tsx +28 -3
  375. package/src/Chat/ChatComposer.doc.mjs +3 -0
  376. package/src/Chat/ChatComposerDrawer.doc.mjs +12 -0
  377. package/src/Chat/ChatMessageList.doc.mjs +8 -0
  378. package/src/Chat/ChatMessageList.test.tsx +18 -0
  379. package/src/Chat/ChatMessageList.tsx +16 -0
  380. package/src/Chat/useTriggerMenu.tsx +3 -0
  381. package/src/CheckboxInput/CheckboxInput.doc.mjs +18 -0
  382. package/src/CheckboxInput/CheckboxInput.test.tsx +172 -27
  383. package/src/CheckboxInput/CheckboxInput.tsx +78 -6
  384. package/src/CheckboxList/CheckboxList.doc.mjs +9 -0
  385. package/src/CheckboxList/CheckboxList.test.tsx +169 -16
  386. package/src/CheckboxList/CheckboxList.tsx +56 -3
  387. package/src/CheckboxList/CheckboxListContext.tsx +7 -0
  388. package/src/Citation/Citation.doc.mjs +10 -0
  389. package/src/Citation/Citation.test.tsx +117 -0
  390. package/src/Citation/Citation.tsx +9 -1
  391. package/src/ClickableCard/ClickableCard.doc.mjs +15 -0
  392. package/src/CodeBlock/CodeBlock.test.tsx +115 -0
  393. package/src/CodeBlock/CodeBlock.tsx +61 -33
  394. package/src/CommandPalette/CommandPalette.test.tsx +47 -1
  395. package/src/CommandPalette/CommandPalette.tsx +9 -21
  396. package/src/ContextMenu/ContextMenu.doc.mjs +4 -4
  397. package/src/ContextMenu/ContextMenu.test.tsx +155 -1
  398. package/src/ContextMenu/ContextMenu.tsx +113 -16
  399. package/src/DateInput/DateInput.doc.mjs +281 -56
  400. package/src/DateInput/DateInput.test.tsx +188 -2
  401. package/src/DateInput/DateInput.tsx +89 -10
  402. package/src/DateRangeInput/DateRangeInput.doc.mjs +251 -46
  403. package/src/DateRangeInput/DateRangeInput.test.tsx +182 -20
  404. package/src/DateRangeInput/DateRangeInput.tsx +63 -5
  405. package/src/DateTimeInput/DateTimeInput.doc.mjs +368 -67
  406. package/src/DateTimeInput/DateTimeInput.test.tsx +279 -11
  407. package/src/DateTimeInput/DateTimeInput.tsx +125 -17
  408. package/src/Dialog/Dialog.doc.mjs +2 -2
  409. package/src/Dialog/Dialog.tsx +12 -0
  410. package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -8
  411. package/src/DropdownMenu/DropdownMenu.test.tsx +56 -42
  412. package/src/DropdownMenu/DropdownMenu.tsx +52 -18
  413. package/src/EmptyState/EmptyState.test.tsx +4 -2
  414. package/src/EmptyState/EmptyState.tsx +6 -2
  415. package/src/Field/Field.test.tsx +32 -0
  416. package/src/Field/Field.tsx +23 -5
  417. package/src/Field/FieldLabel.tsx +55 -17
  418. package/src/FileInput/FileInput.doc.mjs +17 -0
  419. package/src/FileInput/FileInput.test.tsx +283 -49
  420. package/src/FileInput/FileInput.tsx +76 -4
  421. package/src/Grid/Grid.doc.mjs +14 -4
  422. package/src/Grid/Grid.tsx +20 -0
  423. package/src/HoverCard/HoverCard.doc.mjs +3 -0
  424. package/src/HoverCard/HoverCard.test.tsx +189 -2
  425. package/src/HoverCard/HoverCard.tsx +20 -16
  426. package/src/HoverCard/useHoverCard.tsx +13 -10
  427. package/src/Icon/Icon.doc.mjs +8 -0
  428. package/src/InputGroup/InputGroup.doc.mjs +2 -2
  429. package/src/InputGroup/InputGroup.test.tsx +98 -60
  430. package/src/InputGroup/InputGroup.tsx +21 -4
  431. package/src/InputGroup/InputGroupContext.ts +9 -4
  432. package/src/InputGroup/index.ts +1 -0
  433. package/src/Kbd/Kbd.test.tsx +22 -4
  434. package/src/Kbd/Kbd.tsx +37 -12
  435. package/src/Layer/anchorName.test.ts +146 -0
  436. package/src/Layer/anchorName.ts +46 -0
  437. package/src/Layer/layerAnimations.stylex.ts +20 -4
  438. package/src/Layer/useLayer.doc.mjs +12 -2
  439. package/src/Layer/useLayer.tsx +100 -15
  440. package/src/Layout/Layout.doc.mjs +1 -1
  441. package/src/Layout/Layout.tsx +8 -16
  442. package/src/Layout/LayoutFooter.doc.mjs +4 -4
  443. package/src/Layout/LayoutFooter.tsx +3 -3
  444. package/src/Layout/LayoutHeader.doc.mjs +4 -4
  445. package/src/Layout/LayoutHeader.tsx +3 -3
  446. package/src/Layout/LayoutPanel.tsx +3 -3
  447. package/src/Layout/padding.stylex.ts +52 -0
  448. package/src/Lightbox/Lightbox.doc.mjs +9 -2
  449. package/src/Lightbox/Lightbox.test.tsx +53 -5
  450. package/src/Lightbox/Lightbox.tsx +9 -4
  451. package/src/Link/Link.doc.mjs +14 -0
  452. package/src/Link/Link.test.tsx +52 -2
  453. package/src/Link/Link.tsx +42 -7
  454. package/src/Link/LinkProvider.tsx +1 -0
  455. package/src/List/List.doc.mjs +7 -0
  456. package/src/Markdown/Markdown.doc.mjs +2 -0
  457. package/src/Markdown/Markdown.test.tsx +28 -26
  458. package/src/Markdown/Markdown.tsx +21 -6
  459. package/src/MetadataList/MetadataListItem.doc.mjs +4 -0
  460. package/src/MobileNav/MobileNav.doc.mjs +8 -8
  461. package/src/MobileNav/MobileNav.tsx +13 -0
  462. package/src/MobileNav/MobileNavReopen.test.tsx +118 -0
  463. package/src/MoreMenu/MoreMenu.doc.mjs +2 -17
  464. package/src/MoreMenu/MoreMenu.tsx +0 -9
  465. package/src/MultiSelector/MultiSelector.doc.mjs +25 -0
  466. package/src/MultiSelector/MultiSelector.test.tsx +375 -8
  467. package/src/MultiSelector/MultiSelector.tsx +145 -21
  468. package/src/MultiSelector/hooks.ts +27 -0
  469. package/src/NavMenu/NavHeadingMenu.test.tsx +77 -1
  470. package/src/NavMenu/NavHeadingMenu.tsx +50 -3
  471. package/src/NumberInput/NumberInput.doc.mjs +17 -0
  472. package/src/NumberInput/NumberInput.test.tsx +242 -40
  473. package/src/NumberInput/NumberInput.tsx +89 -11
  474. package/src/OverflowList/OverflowList.doc.mjs +17 -0
  475. package/src/Pagination/Pagination.doc.mjs +3 -3
  476. package/src/Pagination/Pagination.test.tsx +250 -14
  477. package/src/Pagination/Pagination.tsx +54 -29
  478. package/src/Popover/usePopover.doc.mjs +7 -3
  479. package/src/Popover/usePopover.tsx +49 -6
  480. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  481. package/src/PowerSearch/PowerSearch.test.tsx +97 -1
  482. package/src/PowerSearch/PowerSearch.tsx +16 -3
  483. package/src/ProgressBar/ProgressBar.test.tsx +45 -27
  484. package/src/ProgressBar/ProgressBar.tsx +3 -4
  485. package/src/RadioList/RadioList.doc.mjs +9 -0
  486. package/src/RadioList/RadioList.test.tsx +252 -16
  487. package/src/RadioList/RadioList.tsx +166 -10
  488. package/src/RadioList/RadioListItem.tsx +14 -2
  489. package/src/Resizable/Resizable.doc.mjs +1 -1
  490. package/src/Section/Section.doc.mjs +11 -0
  491. package/src/SegmentedControl/SegmentedControl.doc.mjs +9 -0
  492. package/src/SegmentedControl/SegmentedControl.test.tsx +214 -42
  493. package/src/SegmentedControl/SegmentedControl.tsx +102 -54
  494. package/src/SegmentedControl/SegmentedControlContext.ts +7 -0
  495. package/src/SegmentedControl/SegmentedControlItem.tsx +14 -1
  496. package/src/SelectableCard/SelectableCard.doc.mjs +15 -0
  497. package/src/Selector/Selector.doc.mjs +25 -0
  498. package/src/Selector/Selector.test.tsx +322 -15
  499. package/src/Selector/Selector.tsx +126 -40
  500. package/src/Selector/hooks.ts +23 -0
  501. package/src/SideNav/SideNav.test.tsx +16 -0
  502. package/src/SideNav/SideNavItem.tsx +2 -4
  503. package/src/Skeleton/Skeleton.doc.mjs +11 -1
  504. package/src/Skeleton/Skeleton.test.tsx +25 -0
  505. package/src/Skeleton/Skeleton.tsx +11 -1
  506. package/src/Slider/Slider.doc.mjs +19 -0
  507. package/src/Slider/Slider.test.tsx +158 -18
  508. package/src/Slider/Slider.tsx +54 -6
  509. package/src/Spinner/Spinner.tsx +7 -1
  510. package/src/Stack/Stack.doc.mjs +77 -0
  511. package/src/Stack/Stack.test.tsx +55 -5
  512. package/src/Stack/Stack.tsx +73 -24
  513. package/src/Stack/StackItem.test.tsx +23 -0
  514. package/src/Stack/StackItem.tsx +20 -23
  515. package/src/Switch/Switch.doc.mjs +15 -0
  516. package/src/Switch/Switch.test.tsx +162 -27
  517. package/src/Switch/Switch.tsx +61 -21
  518. package/src/TabList/Tab.tsx +6 -0
  519. package/src/TabList/TabList.doc.mjs +12 -0
  520. package/src/TabList/TabList.test.tsx +208 -0
  521. package/src/TabList/TabList.tsx +102 -12
  522. package/src/TabList/TabListContext.ts +7 -3
  523. package/src/TabList/TabMenu.tsx +6 -3
  524. package/src/Table/BaseTable.tsx +72 -41
  525. package/src/Table/Table.doc.mjs +14 -0
  526. package/src/Table/Table.test.tsx +39 -30
  527. package/src/Table/Table.tsx +27 -1
  528. package/src/Table/TableCell.tsx +25 -23
  529. package/src/Table/TableHeaderCell.tsx +28 -29
  530. package/src/Table/TableRow.tsx +37 -7
  531. package/src/Table/index.ts +6 -2
  532. package/src/Table/plugins/pagination/paginateData.ts +10 -3
  533. package/src/Table/plugins/pagination/useTablePagination.test.tsx +16 -0
  534. package/src/Table/plugins/pagination/useTablePagination.tsx +8 -1
  535. package/src/Table/plugins/sortable/useTableSortable.test.tsx +91 -2
  536. package/src/Table/plugins/sortable/useTableSortable.tsx +53 -1
  537. package/src/Table/plugins/stickyColumns/index.ts +4 -0
  538. package/src/Table/plugins/stickyColumns/useTableStickyColumns.test.tsx +163 -0
  539. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +407 -0
  540. package/src/Table/tableContextMenu.test.tsx +157 -0
  541. package/src/Table/tableContextMenu.tsx +142 -0
  542. package/src/Table/types.ts +175 -4
  543. package/src/Table/useBaseTablePlugins.ts +1 -0
  544. package/src/Table/useTableColumnResize.doc.mjs +96 -0
  545. package/src/Table/useTableStickyColumns.doc.mjs +59 -0
  546. package/src/Text/Heading.doc.mjs +15 -2
  547. package/src/Text/Text.doc.mjs +7 -1
  548. package/src/Text/text.stylex.ts +2 -2
  549. package/src/TextArea/TextArea.doc.mjs +17 -0
  550. package/src/TextArea/TextArea.test.tsx +167 -35
  551. package/src/TextArea/TextArea.tsx +62 -15
  552. package/src/TextInput/TextInput.doc.mjs +17 -0
  553. package/src/TextInput/TextInput.test.tsx +168 -34
  554. package/src/TextInput/TextInput.tsx +77 -9
  555. package/src/TimeInput/TimeInput.doc.mjs +335 -60
  556. package/src/TimeInput/TimeInput.test.tsx +144 -7
  557. package/src/TimeInput/TimeInput.tsx +74 -5
  558. package/src/Timestamp/Timestamp.doc.mjs +9 -0
  559. package/src/Timestamp/Timestamp.test.tsx +44 -18
  560. package/src/Timestamp/Timestamp.tsx +30 -10
  561. package/src/Toast/Toast.tsx +14 -0
  562. package/src/Toast/ToastViewport.test.tsx +204 -0
  563. package/src/Toast/ToastViewport.tsx +167 -21
  564. package/src/ToggleButton/ToggleButton.doc.mjs +9 -3
  565. package/src/ToggleButton/ToggleButton.test.tsx +39 -18
  566. package/src/ToggleButton/ToggleButton.tsx +46 -83
  567. package/src/Tokenizer/Tokenizer.doc.mjs +15 -0
  568. package/src/Tokenizer/Tokenizer.test.tsx +105 -1
  569. package/src/Tokenizer/Tokenizer.tsx +41 -2
  570. package/src/Toolbar/Toolbar.doc.mjs +65 -5
  571. package/src/Toolbar/Toolbar.test.tsx +109 -6
  572. package/src/Toolbar/Toolbar.tsx +59 -5
  573. package/src/Tooltip/Tooltip.test.tsx +81 -0
  574. package/src/Tooltip/useTooltip.tsx +57 -11
  575. package/src/TreeList/TreeList.test.tsx +295 -6
  576. package/src/TreeList/TreeList.tsx +73 -1
  577. package/src/TreeList/TreeListItem.tsx +51 -5
  578. package/src/TreeList/treeListItem.markers.stylex.ts +20 -0
  579. package/src/Typeahead/BaseTypeahead.tsx +86 -9
  580. package/src/Typeahead/Typeahead.doc.mjs +136 -30
  581. package/src/Typeahead/Typeahead.test.tsx +331 -1
  582. package/src/Typeahead/Typeahead.tsx +52 -1
  583. package/src/VisuallyHidden/VisuallyHidden.doc.mjs +80 -0
  584. package/src/VisuallyHidden/VisuallyHidden.test.tsx +76 -0
  585. package/src/VisuallyHidden/VisuallyHidden.tsx +113 -0
  586. package/src/VisuallyHidden/index.ts +15 -0
  587. package/src/hooks/focusableSelector.ts +25 -0
  588. package/src/hooks/index.ts +28 -2
  589. package/src/hooks/useAnnounce.test.tsx +101 -0
  590. package/src/hooks/useAnnounce.ts +168 -0
  591. package/src/hooks/useClickableContainer.ts +16 -4
  592. package/src/hooks/useEntryAnimation.ts +16 -4
  593. package/src/hooks/useFocusTrap.test.tsx +132 -0
  594. package/src/hooks/useFocusTrap.ts +98 -4
  595. package/src/hooks/useGridFocus.doc.mjs +40 -2
  596. package/src/hooks/useGridFocus.test.tsx +132 -0
  597. package/src/hooks/useGridFocus.ts +343 -67
  598. package/src/hooks/useImageMode.test.ts +133 -0
  599. package/src/hooks/useKeyboardHint.doc.mjs +101 -0
  600. package/src/hooks/useKeyboardHint.test.tsx +70 -0
  601. package/src/hooks/useKeyboardHint.tsx +332 -0
  602. package/src/hooks/useListFocus.doc.mjs +50 -10
  603. package/src/hooks/useListFocus.test.tsx +375 -0
  604. package/src/hooks/useListFocus.ts +395 -57
  605. package/src/hooks/useLongPress.test.tsx +134 -0
  606. package/src/hooks/useLongPress.ts +119 -0
  607. package/src/hooks/useTreeFocus.doc.mjs +140 -0
  608. package/src/hooks/useTreeFocus.test.tsx +214 -0
  609. package/src/hooks/useTreeFocus.ts +559 -0
  610. package/src/hooks/useTypeahead.test.tsx +109 -0
  611. package/src/hooks/useTypeahead.ts +183 -0
  612. package/src/index.ts +1 -0
  613. package/src/theme/derivedVarRegistry.test.ts +4 -0
  614. package/src/theme/generateThemeRules.test.ts +7 -8
  615. package/src/theme/generateThemeRules.ts +1 -1
  616. package/src/theme/syntax/SyntaxTheme.tsx +1 -1
  617. package/src/theme/syntax/defineSyntaxTheme.ts +20 -11
  618. package/src/theme/syntax/presets.ts +5 -2
  619. package/src/theme/syntax/tokens.ts +1 -1
  620. package/src/theme/types.ts +1 -1
  621. package/src/utils/dateTypes.test.ts +40 -0
  622. package/src/utils/dateTypes.ts +40 -0
  623. package/src/utils/index.ts +9 -0
  624. package/src/utils/inputAria.test.ts +39 -0
  625. package/src/utils/inputAria.ts +58 -0
  626. package/src/utils/timeParser.test.ts +10 -0
  627. package/src/utils/timeParser.ts +5 -2
  628. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts +0 -57
  629. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts.map +0 -1
  630. package/dist/Calendar/hooks/useCalendarRovingTabindex.js +0 -96
  631. package/src/Calendar/hooks/useCalendarRovingTabindex.ts +0 -118
@@ -9,10 +9,26 @@
9
9
  * SYNC: When Pagination.tsx changes, update tests to match new behavior
10
10
  */
11
11
 
12
- import {describe, it, expect, vi} from 'vitest';
13
- import {render, screen, within} from '@testing-library/react';
12
+ import {describe, it, expect, vi, afterEach} from 'vitest';
13
+ import {
14
+ render,
15
+ screen,
16
+ within,
17
+ fireEvent,
18
+ act,
19
+ waitFor,
20
+ } from '@testing-library/react';
14
21
  import userEvent from '@testing-library/user-event';
15
22
  import {Pagination, generatePageRange} from './Pagination';
23
+ import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
24
+
25
+ afterEach(() => {
26
+ __resetLiveRegionsForTest();
27
+ });
28
+
29
+ function politeRegion(): HTMLElement | null {
30
+ return document.querySelector('[data-astryx-live-region="polite"]');
31
+ }
16
32
 
17
33
  // =============================================================================
18
34
  // generatePageRange helper
@@ -239,6 +255,65 @@ describe('Pagination', () => {
239
255
  });
240
256
  });
241
257
 
258
+ // ---------------------------------------------------------------------------
259
+ // pageSize guarding
260
+ // ---------------------------------------------------------------------------
261
+
262
+ describe('pageSize guarding', () => {
263
+ it('does not crash the dots variant when pageSize is 0', () => {
264
+ render(
265
+ <Pagination
266
+ page={1}
267
+ onChange={() => {}}
268
+ totalItems={5}
269
+ pageSize={0}
270
+ variant="dots"
271
+ />,
272
+ );
273
+ const group = screen.getByRole('group', {name: 'Page indicators'});
274
+ expect(within(group).getAllByRole('button')).toHaveLength(5);
275
+ });
276
+
277
+ it('treats NaN pageSize as the default', () => {
278
+ render(
279
+ <Pagination
280
+ page={1}
281
+ onChange={() => {}}
282
+ totalItems={50}
283
+ pageSize={NaN}
284
+ variant="compact"
285
+ />,
286
+ );
287
+ expect(screen.getByText('Page 1 of 5')).toBeInTheDocument();
288
+ });
289
+
290
+ it('clamps negative pageSize to 1', () => {
291
+ render(
292
+ <Pagination
293
+ page={1}
294
+ onChange={() => {}}
295
+ totalItems={5}
296
+ pageSize={-10}
297
+ variant="compact"
298
+ />,
299
+ );
300
+ expect(screen.getByText('Page 1 of 5')).toBeInTheDocument();
301
+ });
302
+
303
+ it('floors fractional pageSize', () => {
304
+ render(
305
+ <Pagination
306
+ page={1}
307
+ onChange={() => {}}
308
+ totalItems={50}
309
+ pageSize={2.5}
310
+ variant="compact"
311
+ />,
312
+ );
313
+ expect(screen.getByText('Page 1 of 25')).toBeInTheDocument();
314
+ });
315
+ });
316
+
242
317
  // ---------------------------------------------------------------------------
243
318
  // Variant: dots
244
319
  // ---------------------------------------------------------------------------
@@ -304,6 +379,38 @@ describe('Pagination', () => {
304
379
  // ---------------------------------------------------------------------------
305
380
 
306
381
  describe('page change callbacks', () => {
382
+ it('does not announce on initial mount', () => {
383
+ render(<Pagination page={1} onChange={() => {}} totalPages={10} />);
384
+ expect(politeRegion()).toBeNull();
385
+ });
386
+
387
+ it('announces the new page politely when navigating', async () => {
388
+ const user = userEvent.setup();
389
+ render(<Pagination page={2} onChange={() => {}} totalPages={10} />);
390
+ await user.click(screen.getByRole('button', {name: 'Go to page 3'}));
391
+ await waitFor(() => {
392
+ expect(politeRegion()).toHaveTextContent('Page 3 of 10');
393
+ });
394
+ });
395
+
396
+ it('announces the next page when clicking next', async () => {
397
+ const user = userEvent.setup();
398
+ render(<Pagination page={2} onChange={() => {}} totalPages={5} />);
399
+ await user.click(screen.getByRole('button', {name: 'Go to next page'}));
400
+ await waitFor(() => {
401
+ expect(politeRegion()).toHaveTextContent('Page 3 of 5');
402
+ });
403
+ });
404
+
405
+ it('announces without a total when only hasMore is known', async () => {
406
+ const user = userEvent.setup();
407
+ render(<Pagination page={1} onChange={() => {}} hasMore />);
408
+ await user.click(screen.getByRole('button', {name: 'Go to next page'}));
409
+ await waitFor(() => {
410
+ expect(politeRegion()).toHaveTextContent('Page 2');
411
+ });
412
+ });
413
+
307
414
  it('calls onChange when clicking a page button', async () => {
308
415
  const user = userEvent.setup();
309
416
  const onChange = vi.fn();
@@ -346,6 +453,145 @@ describe('Pagination', () => {
346
453
  });
347
454
  });
348
455
 
456
+ // ---------------------------------------------------------------------------
457
+ // changeAction (interruptible, optimistic)
458
+ // ---------------------------------------------------------------------------
459
+
460
+ describe('changeAction', () => {
461
+ it('fires onChange then changeAction with the new page', async () => {
462
+ const user = userEvent.setup();
463
+ const order: string[] = [];
464
+ const onChange = vi.fn(() => order.push('onChange'));
465
+ const changeAction = vi.fn(() => {
466
+ order.push('changeAction');
467
+ });
468
+ render(
469
+ <Pagination
470
+ page={1}
471
+ onChange={onChange}
472
+ changeAction={changeAction}
473
+ totalPages={5}
474
+ />,
475
+ );
476
+ await user.click(screen.getByRole('button', {name: 'Go to next page'}));
477
+ expect(onChange).toHaveBeenCalledWith(2);
478
+ expect(changeAction).toHaveBeenCalledWith(2);
479
+ expect(order).toEqual(['onChange', 'changeAction']);
480
+ });
481
+
482
+ it('shows the optimistic page while changeAction is pending', async () => {
483
+ const user = userEvent.setup();
484
+ let resolveAction: (() => void) | undefined;
485
+ const changeAction = vi.fn(
486
+ async () =>
487
+ new Promise<void>(resolve => {
488
+ resolveAction = resolve;
489
+ }),
490
+ );
491
+ render(
492
+ <Pagination
493
+ page={1}
494
+ onChange={() => {}}
495
+ changeAction={changeAction}
496
+ totalPages={5}
497
+ variant="compact"
498
+ />,
499
+ );
500
+
501
+ // The committed `page` prop stays at 1, but the indicator optimistically
502
+ // reflects the page being navigated to.
503
+ await user.click(screen.getByRole('button', {name: 'Go to next page'}));
504
+ expect(changeAction).toHaveBeenCalledWith(2);
505
+ expect(
506
+ within(screen.getByRole('navigation')).getByText('Page 2 of 5'),
507
+ ).toBeInTheDocument();
508
+
509
+ await act(async () => {
510
+ resolveAction?.();
511
+ await Promise.resolve();
512
+ });
513
+ });
514
+
515
+ it('interrupts an in-flight action on rapid next clicks', async () => {
516
+ // Each click derives its target from the optimistic page, so clicking
517
+ // next twice before the action settles advances 1 -> 2 -> 3 instead of
518
+ // being dropped by a re-entry guard.
519
+ const resolvers: (() => void)[] = [];
520
+ const changeAction = vi.fn(
521
+ async () =>
522
+ new Promise<void>(resolve => {
523
+ resolvers.push(resolve);
524
+ }),
525
+ );
526
+ render(
527
+ <Pagination
528
+ page={1}
529
+ onChange={() => {}}
530
+ changeAction={changeAction}
531
+ totalPages={5}
532
+ variant="compact"
533
+ />,
534
+ );
535
+
536
+ const next = screen.getByRole('button', {name: 'Go to next page'});
537
+ const nav = screen.getByRole('navigation');
538
+ await act(async () => {
539
+ fireEvent.click(next);
540
+ });
541
+ // Scope to the nav landmark: the live region on document.body also
542
+ // carries the announced page text.
543
+ expect(within(nav).getByText('Page 2 of 5')).toBeInTheDocument();
544
+ await act(async () => {
545
+ fireEvent.click(next);
546
+ });
547
+ expect(within(nav).getByText('Page 3 of 5')).toBeInTheDocument();
548
+
549
+ expect(changeAction).toHaveBeenCalledTimes(2);
550
+ expect(changeAction).toHaveBeenNthCalledWith(1, 2);
551
+ expect(changeAction).toHaveBeenNthCalledWith(2, 3);
552
+
553
+ await act(async () => {
554
+ resolvers.forEach(resolve => resolve());
555
+ await Promise.resolve();
556
+ });
557
+ });
558
+
559
+ it('supports a synchronous changeAction', async () => {
560
+ const user = userEvent.setup();
561
+ const changeAction = vi.fn((_page: number) => {});
562
+ const onChange = vi.fn();
563
+ render(
564
+ <Pagination
565
+ page={2}
566
+ onChange={onChange}
567
+ changeAction={changeAction}
568
+ totalPages={5}
569
+ />,
570
+ );
571
+ await user.click(
572
+ screen.getByRole('button', {name: 'Go to previous page'}),
573
+ );
574
+ expect(onChange).toHaveBeenCalledWith(1);
575
+ expect(changeAction).toHaveBeenCalledWith(1);
576
+ });
577
+
578
+ it('does not fire changeAction when disabled', async () => {
579
+ const user = userEvent.setup();
580
+ const changeAction = vi.fn();
581
+ render(
582
+ <Pagination
583
+ page={1}
584
+ onChange={() => {}}
585
+ changeAction={changeAction}
586
+ totalPages={5}
587
+ isDisabled
588
+ />,
589
+ );
590
+ await user.click(screen.getByRole('button', {name: 'Go to next page'}));
591
+ expect(changeAction).not.toHaveBeenCalled();
592
+ });
593
+ });
594
+
349
595
  // ---------------------------------------------------------------------------
350
596
  // Boundary states
351
597
  // ---------------------------------------------------------------------------
@@ -459,12 +705,7 @@ describe('Pagination', () => {
459
705
  describe('disabled state', () => {
460
706
  it('disables all page buttons when isDisabled', () => {
461
707
  render(
462
- <Pagination
463
- page={3}
464
- onChange={() => {}}
465
- totalPages={5}
466
- isDisabled
467
- />,
708
+ <Pagination page={3} onChange={() => {}} totalPages={5} isDisabled />,
468
709
  );
469
710
  expect(
470
711
  screen.getByRole('button', {name: 'Go to previous page'}),
@@ -480,12 +721,7 @@ describe('Pagination', () => {
480
721
  const user = userEvent.setup();
481
722
  const onChange = vi.fn();
482
723
  render(
483
- <Pagination
484
- page={3}
485
- onChange={onChange}
486
- totalPages={5}
487
- isDisabled
488
- />,
724
+ <Pagination page={3} onChange={onChange} totalPages={5} isDisabled />,
489
725
  );
490
726
  // Disabled buttons can't be clicked
491
727
  await user.click(screen.getByRole('button', {name: 'Go to page 1'}));
@@ -19,7 +19,7 @@
19
19
  * label, data-testid, xstyle
20
20
  */
21
21
 
22
- import {useTransition} from 'react';
22
+ import {useOptimistic, useTransition} from 'react';
23
23
  import * as stylex from '@stylexjs/stylex';
24
24
  import {
25
25
  colorVars,
@@ -34,6 +34,7 @@ import {Button} from '../Button';
34
34
  import {Icon} from '../Icon';
35
35
  import {Selector} from '../Selector';
36
36
  import {Text} from '../Text';
37
+ import {useAnnounce} from '../hooks/useAnnounce';
37
38
  import {mergeProps} from '../utils';
38
39
  import type {BaseProps} from '../BaseProps';
39
40
  import {themeProps} from '../utils/themeProps';
@@ -335,7 +336,7 @@ export function Pagination({
335
336
  totalItems,
336
337
  totalPages: totalPagesProp,
337
338
  hasMore,
338
- pageSize = 10,
339
+ pageSize: pageSizeProp = 10,
339
340
  pageSizeOptions,
340
341
  onPageSizeChange,
341
342
  variant = 'pages',
@@ -349,16 +350,36 @@ export function Pagination({
349
350
  style,
350
351
  ref,
351
352
  }: PaginationProps) {
352
- const [isPending, startTransition] = useTransition();
353
+ const [, startTransition] = useTransition();
354
+
355
+ // pageSize is typed as number, so 0, NaN, and negatives are valid at the
356
+ // type level but yield Infinity/NaN page counts, and
357
+ // Array.from({length: Infinity}) crashes the dots variant. Coerce to a
358
+ // positive integer; non-finite values fall back to the default.
359
+ const pageSize = Number.isFinite(pageSizeProp)
360
+ ? Math.max(1, Math.floor(pageSizeProp))
361
+ : 10;
362
+
363
+ // Announce page changes politely (navigation-10). The controls carry no
364
+ // live region, so page transitions were previously silent to screen readers.
365
+ // Only user-driven changes go through handlePageChange, so initial mount is
366
+ // never announced.
367
+ const announce = useAnnounce();
368
+
369
+ // Track the page optimistically so rapid prev/next clicks advance from the
370
+ // in-flight target instead of stalling on the last committed page.
371
+ const [optimisticPage, setOptimisticPage] = useOptimistic(page);
353
372
 
354
373
  // Compute pagination state
355
374
  const computedTotalPages =
356
375
  totalPagesProp ??
357
376
  (totalItems != null ? Math.ceil(totalItems / pageSize) : undefined);
358
377
 
359
- const hasPrevious = page > 1;
378
+ const hasPrevious = optimisticPage > 1;
360
379
  const hasNext =
361
- computedTotalPages != null ? page < computedTotalPages : (hasMore ?? false);
380
+ computedTotalPages != null
381
+ ? optimisticPage < computedTotalPages
382
+ : (hasMore ?? false);
362
383
 
363
384
  // Return null for empty state
364
385
  if (totalItems != null && totalItems <= 0) {
@@ -368,48 +389,52 @@ export function Pagination({
368
389
  return null;
369
390
  }
370
391
 
392
+ // Interruptible: re-clicking before the transition settles starts a fresh one
393
+ // with the next optimistic page rather than being dropped, so there is no
394
+ // re-entry guard.
371
395
  const handlePageChange = (newPage: number) => {
372
- if (isDisabled || isPending) {
396
+ if (isDisabled) {
373
397
  return;
374
398
  }
399
+ // Keep onChange urgent so controlled page state updates in the same commit
400
+ // as the click; only the optimistic indicator and changeAction defer.
375
401
  onChange(newPage);
376
- if (changeAction) {
377
- startTransition(async () => {
378
- await changeAction(newPage);
379
- });
380
- }
402
+ announce(
403
+ computedTotalPages != null
404
+ ? `Page ${newPage} of ${computedTotalPages}`
405
+ : `Page ${newPage}`,
406
+ );
407
+ startTransition(async () => {
408
+ setOptimisticPage(newPage);
409
+ await changeAction?.(newPage);
410
+ });
381
411
  };
382
412
 
383
413
  const handlePrevious = () => {
384
414
  if (hasPrevious) {
385
- handlePageChange(page - 1);
415
+ handlePageChange(optimisticPage - 1);
386
416
  }
387
417
  };
388
418
 
389
419
  const handleNext = () => {
390
420
  if (hasNext) {
391
- handlePageChange(page + 1);
421
+ handlePageChange(optimisticPage + 1);
392
422
  }
393
423
  };
394
424
 
395
425
  const handlePageSizeChange = (value: string) => {
396
426
  const newSize = Number(value);
397
427
  onPageSizeChange?.(newSize);
398
- // Reset to page 1 when page size changes
399
- onChange(1);
400
- if (changeAction) {
401
- startTransition(async () => {
402
- await changeAction(1);
403
- });
404
- }
428
+ // Reset to page 1 when page size changes.
429
+ handlePageChange(1);
405
430
  };
406
431
 
407
432
  // Item range for count display
408
- const rangeStart = (page - 1) * pageSize + 1;
433
+ const rangeStart = (optimisticPage - 1) * pageSize + 1;
409
434
  const rangeEnd =
410
435
  totalItems != null
411
- ? Math.min(page * pageSize, totalItems)
412
- : page * pageSize;
436
+ ? Math.min(optimisticPage * pageSize, totalItems)
437
+ : optimisticPage * pageSize;
413
438
 
414
439
  const buttonSize = size === 'sm' ? 'sm' : 'md';
415
440
  const isSm = size === 'sm';
@@ -421,7 +446,7 @@ export function Pagination({
421
446
  return null;
422
447
  }
423
448
  const pageRange = generatePageRange(
424
- page,
449
+ optimisticPage,
425
450
  computedTotalPages,
426
451
  siblingCount,
427
452
  );
@@ -443,7 +468,7 @@ export function Pagination({
443
468
  </span>
444
469
  );
445
470
  }
446
- const isActive = item === page;
471
+ const isActive = item === optimisticPage;
447
472
  return (
448
473
  <Button
449
474
  key={item}
@@ -483,7 +508,7 @@ export function Pagination({
483
508
  return (
484
509
  <span {...stylex.props(styles.infoText)}>
485
510
  <Text type="body" size="sm" color="secondary">
486
- {`Page ${page} of ${computedTotalPages}`}
511
+ {`Page ${optimisticPage} of ${computedTotalPages}`}
487
512
  </Text>
488
513
  </span>
489
514
  );
@@ -503,18 +528,18 @@ export function Pagination({
503
528
  key={i + 1}
504
529
  type="button"
505
530
  aria-label={`Go to page ${i + 1}`}
506
- aria-current={i + 1 === page ? 'page' : undefined}
531
+ aria-current={i + 1 === optimisticPage ? 'page' : undefined}
507
532
  onClick={() => handlePageChange(i + 1)}
508
533
  disabled={isDisabled}
509
534
  {...mergeProps(
510
535
  themeProps('pagination-dot', {
511
- active: i + 1 === page ? 'active' : null,
536
+ active: i + 1 === optimisticPage ? 'active' : null,
512
537
  size,
513
538
  }),
514
539
  stylex.props(
515
540
  styles.dot,
516
541
  isSm && styles.dotSm,
517
- i + 1 === page && styles.dotActive,
542
+ i + 1 === optimisticPage && styles.dotActive,
518
543
  isDisabled && styles.dotDisabled,
519
544
  ),
520
545
  )}
@@ -14,7 +14,9 @@ export const docs = {
14
14
  {name: 'hasAutoFocus', type: 'boolean', description: 'Whether to automatically focus the first focusable element when opened.', default: 'true'},
15
15
  {name: 'hasCloseButton', type: 'boolean', description: 'Whether to include a hidden close button that appears for keyboard users.', default: 'true'},
16
16
  {name: 'closeButtonLabel', type: 'string', description: 'Accessible label for the hidden close button.', default: "'Close popover'"},
17
- {name: 'dialogLabel', type: 'string', description: 'Accessible label for the popover dialog. Provide one when there is no visible title.'},
17
+ {name: 'dialogLabel', type: 'string', description: 'Accessible label for the popover dialog (only applies when role is "dialog"). Provide one when there is no visible title.'},
18
+ {name: 'role', type: "'dialog' | 'none'", description: 'ARIA role on the content wrapper. Use "dialog" for genuine dialog content; use "none" for listbox/menu popups whose own content role should be exposed and whose trigger keeps DOM focus.', default: "'dialog'"},
19
+ {name: 'isModal', type: 'boolean', description: 'Whether a dialog-role popover is modal (aria-modal). Only applies when role is "dialog".', default: 'true'},
18
20
  {name: 'hasSurface', type: 'boolean', description: 'Whether to apply the default popover surface background, radius, and shadow.', default: 'true'},
19
21
  ],
20
22
  returns: [
@@ -27,7 +29,7 @@ export const docs = {
27
29
  {name: 'isOpen', type: 'boolean', description: 'Whether the popover is currently open.'},
28
30
  {name: 'id', type: 'string', description: 'Unique ID for aria-describedby or aria-controls.'},
29
31
  {name: 'render', type: '(children: ReactNode, props?: ContextRenderProps) => ReactNode', description: 'Render function for anchor-positioned popover content. Pass placement and alignment here.'},
30
- {name: 'triggerProps', type: '{aria-haspopup: "dialog"; aria-expanded: boolean; aria-controls: string}', description: 'ARIA attributes to spread onto the trigger element.'},
32
+ {name: 'triggerProps', type: '{aria-haspopup: "dialog" | "true"; aria-expanded: boolean; aria-controls: string}', description: 'ARIA attributes to spread onto the trigger element. aria-haspopup reflects the popover role.'},
31
33
  ],
32
34
  usage: {
33
35
  description: 'Headless hook for click-triggered popovers with focus trapping. Combines useLayer with useFocusTrap, auto-focus, light dismiss, Escape handling, and an optional hidden close button for accessible dialog-like popover behavior. Use for custom interactive floating content that needs keyboard navigation.',
@@ -54,7 +56,9 @@ export const docsDense = {
54
56
  hasAutoFocus: 'whether first focusable element receives focus on open.',
55
57
  hasCloseButton: 'whether hidden keyboard close button is included.',
56
58
  closeButtonLabel: 'label for hidden close button.',
57
- dialogLabel: 'accessible label for popover dialog.',
59
+ dialogLabel: 'accessible label for popover dialog (role="dialog" only).',
60
+ role: 'content wrapper ARIA role; "none" for listbox/menu popups.',
61
+ isModal: 'whether a dialog-role popover is modal (aria-modal).',
58
62
  hasSurface: 'apply default surface background/radius/shadow.',
59
63
  },
60
64
  returnDescriptions: {
@@ -130,10 +130,35 @@ export interface UsePopoverOptions {
130
130
 
131
131
  /**
132
132
  * Accessible label for the dialog.
133
- * Required for screen readers to announce the dialog purpose.
133
+ * Required for screen readers to announce the dialog purpose
134
+ * (only applies when `role` is `'dialog'`).
134
135
  */
135
136
  dialogLabel?: string;
136
137
 
138
+ /**
139
+ * ARIA role stamped on the popover content wrapper.
140
+ *
141
+ * - `'dialog'` (default): the wrapper is a `role="dialog"` and, when
142
+ * `isModal` is true, carries `aria-modal`. Use for genuine dialog content.
143
+ * - `'none'`: the wrapper carries no role or `aria-modal`, so the popup's own
144
+ * content role (e.g. a child `role="listbox"` or `role="menu"`) is the
145
+ * exposed semantics. Use for comboboxes, listboxes, and menus — their
146
+ * trigger keeps DOM focus, so announcing an unnamed modal dialog around
147
+ * them is incorrect.
148
+ *
149
+ * @default 'dialog'
150
+ */
151
+ role?: 'dialog' | 'none';
152
+
153
+ /**
154
+ * Whether the dialog is modal (`aria-modal`). Only applies when `role` is
155
+ * `'dialog'`. Set to `false` for non-modal dialogs that do not inert the rest
156
+ * of the page.
157
+ *
158
+ * @default true
159
+ */
160
+ isModal?: boolean;
161
+
137
162
  /**
138
163
  * Whether to apply the default popover surface (background, border-radius,
139
164
  * box-shadow) to the content wrapper.
@@ -215,7 +240,7 @@ export interface UsePopoverReturn {
215
240
  * ARIA attributes to spread on the trigger element
216
241
  */
217
242
  triggerProps: {
218
- 'aria-haspopup': 'dialog';
243
+ 'aria-haspopup': 'dialog' | 'true';
219
244
  'aria-expanded': boolean;
220
245
  'aria-controls': string;
221
246
  };
@@ -274,6 +299,8 @@ export function usePopover(
274
299
  hasCloseButton = true,
275
300
  closeButtonLabel = 'Close popover',
276
301
  dialogLabel,
302
+ role = 'dialog',
303
+ isModal = true,
277
304
  } = options;
278
305
 
279
306
  // Track the trigger element for returning focus
@@ -339,20 +366,34 @@ export function usePopover(
339
366
 
340
367
  // ARIA attributes for the trigger
341
368
  const triggerProps = {
342
- 'aria-haspopup': 'dialog' as const,
369
+ 'aria-haspopup': role === 'dialog' ? ('dialog' as const) : ('true' as const),
343
370
  'aria-expanded': layer.isOpen,
344
371
  'aria-controls': layer.id,
345
372
  };
346
373
 
374
+ // Dev-time guardrail: a dialog popover should always be labeled. Warn once
375
+ // per hook instance (in an effect) rather than on every render.
376
+ const warnedUnnamedDialogRef = useRef(false);
377
+ useEffect(() => {
378
+ if (role === 'dialog' && !dialogLabel && !warnedUnnamedDialogRef.current) {
379
+ warnedUnnamedDialogRef.current = true;
380
+ console.warn(
381
+ 'usePopover: role="dialog" without a `dialogLabel` renders an unnamed ' +
382
+ 'dialog. Pass `dialogLabel`, or use `role: "none"` for listbox/menu ' +
383
+ 'popups whose content already carries its own role.',
384
+ );
385
+ }
386
+ }, [role, dialogLabel]);
387
+
347
388
  // Wrapped render function that includes surface styles and optional hidden close button
348
389
  const render = useCallback(
349
390
  (children: ReactNode, props?: ContextRenderProps): ReactNode => {
350
391
  return layer.render(
351
392
  <div
352
393
  ref={contentRef}
353
- role="dialog"
354
- aria-modal="true"
355
- aria-label={dialogLabel}
394
+ role={role === 'dialog' ? 'dialog' : undefined}
395
+ aria-modal={role === 'dialog' && isModal ? true : undefined}
396
+ aria-label={role === 'dialog' ? dialogLabel : undefined}
356
397
  {...stylex.props(
357
398
  styles.contentWrapper,
358
399
  hasSurface && styles.surface,
@@ -379,6 +420,8 @@ export function usePopover(
379
420
  closeButtonLabel,
380
421
  contentRef,
381
422
  dialogLabel,
423
+ role,
424
+ isModal,
382
425
  xstyle,
383
426
  ],
384
427
  );
@@ -71,6 +71,12 @@ export const docs = {
71
71
  description: 'Disables the entire component.',
72
72
  default: 'false',
73
73
  },
74
+ {
75
+ name: 'disabledMessage',
76
+ type: 'string',
77
+ description:
78
+ 'Explains why the search is disabled. With isDisabled, shows a tooltip on hover/keyboard focus and keeps the input focusable via aria-disabled (input stays blocked). Use this instead of wrapping a disabled PowerSearch in Tooltip — disabled controls swallow the hover events an external Tooltip needs.',
79
+ },
74
80
  {
75
81
  name: 'status',
76
82
  type: 'InputStatus',
@@ -136,6 +142,7 @@ export const docs = {
136
142
  { guidance: true, description: 'Define clear, descriptive field names and aliases so users can quickly find the filter they need.' },
137
143
  { guidance: true, description: 'Provide a result count to give users feedback on how their filters affect the data set.' },
138
144
  { guidance: false, description: 'Use PowerSearch for simple keyword searches; a standard text input is more appropriate for single-field lookups.' },
145
+ { guidance: false, description: 'Wrap a disabled PowerSearch in Tooltip to explain why it is disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead.' },
139
146
  ],
140
147
  },
141
148
  };
@@ -212,6 +219,12 @@ export const docsZh = {
212
219
  description: '禁用整个组件。',
213
220
  default: 'false',
214
221
  },
222
+ {
223
+ name: 'disabledMessage',
224
+ type: 'string',
225
+ description:
226
+ 'Explains why the search is disabled. With isDisabled, shows a tooltip on hover/keyboard focus and keeps the input focusable via aria-disabled (input stays blocked). Use this instead of wrapping a disabled PowerSearch in Tooltip — disabled controls swallow the hover events an external Tooltip needs.',
227
+ },
215
228
  {
216
229
  name: 'status',
217
230
  type: 'InputStatus',
@@ -269,6 +282,7 @@ export const docsZh = {
269
282
  { guidance: true, description: 'Define clear, descriptive field names and aliases so users can quickly find the filter they need.' },
270
283
  { guidance: true, description: 'Provide a result count to give users feedback on how their filters affect the data set.' },
271
284
  { guidance: false, description: 'Use PowerSearch for simple keyword searches; a standard text input is more appropriate for single-field lookups.' },
285
+ { guidance: false, description: 'Wrap a disabled PowerSearch in Tooltip to explain why it is disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead.' },
272
286
  ],
273
287
  },
274
288
  };
@@ -284,6 +298,7 @@ export const docsDense = {
284
298
  { guidance: true, description: 'Define clear, descriptive field names and aliases so users can quickly find the filter they need.' },
285
299
  { guidance: true, description: 'Provide a result count to give users feedback on how their filters affect the data set.' },
286
300
  { guidance: false, description: 'Use PowerSearch for simple keyword searches; a standard text input is more appropriate for single-field lookups.' },
301
+ { guidance: false, description: 'Wrap a disabled PowerSearch in Tooltip to explain why it is disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead.' },
287
302
  ],
288
303
  },
289
304
  propDescriptions: {