@astryxdesign/core 0.1.2 → 0.1.3

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 (567) hide show
  1. package/CHANGELOG.md +165 -0
  2. package/README.md +12 -12
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +11 -5
  5. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +1 -1
  6. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  7. package/dist/Breadcrumbs/BreadcrumbItem.js +24 -5
  8. package/dist/Button/Button.d.ts.map +1 -1
  9. package/dist/Button/Button.js +2 -4
  10. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  11. package/dist/ButtonGroup/ButtonGroup.js +0 -1
  12. package/dist/Calendar/Calendar.d.ts +5 -4
  13. package/dist/Calendar/Calendar.d.ts.map +1 -1
  14. package/dist/Calendar/Calendar.js +82 -43
  15. package/dist/Calendar/hooks/index.d.ts +0 -2
  16. package/dist/Calendar/hooks/index.d.ts.map +1 -1
  17. package/dist/Calendar/hooks/index.js +1 -2
  18. package/dist/Calendar/index.d.ts +3 -3
  19. package/dist/Calendar/index.d.ts.map +1 -1
  20. package/dist/Calendar/index.js +1 -1
  21. package/dist/Calendar/styles.d.ts +3 -9
  22. package/dist/Calendar/styles.d.ts.map +1 -1
  23. package/dist/Calendar/styles.js +3 -11
  24. package/dist/Carousel/Carousel.d.ts.map +1 -1
  25. package/dist/Carousel/Carousel.js +17 -3
  26. package/dist/Center/Center.d.ts +11 -1
  27. package/dist/Center/Center.d.ts.map +1 -1
  28. package/dist/Center/Center.js +9 -3
  29. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  30. package/dist/Chat/ChatComposerInput.js +0 -1
  31. package/dist/Chat/ChatMessageList.d.ts +14 -1
  32. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  33. package/dist/Chat/ChatMessageList.js +2 -0
  34. package/dist/Chat/useTriggerMenu.d.ts +8 -1
  35. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  36. package/dist/Chat/useTriggerMenu.js +15 -3
  37. package/dist/CheckboxInput/CheckboxInput.d.ts +23 -2
  38. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  39. package/dist/CheckboxInput/CheckboxInput.js +51 -9
  40. package/dist/CheckboxList/CheckboxList.d.ts +13 -1
  41. package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
  42. package/dist/CheckboxList/CheckboxList.js +39 -9
  43. package/dist/CheckboxList/CheckboxListContext.d.ts +7 -0
  44. package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
  45. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  46. package/dist/CheckboxList/CheckboxListItem.js +0 -1
  47. package/dist/Citation/Citation.d.ts.map +1 -1
  48. package/dist/Citation/Citation.js +9 -5
  49. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  50. package/dist/CodeBlock/CodeBlock.js +51 -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 +167 -99
  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/Field/Field.d.ts +19 -2
  71. package/dist/Field/Field.d.ts.map +1 -1
  72. package/dist/Field/Field.js +4 -0
  73. package/dist/Field/FieldLabel.d.ts +21 -2
  74. package/dist/Field/FieldLabel.d.ts.map +1 -1
  75. package/dist/Field/FieldLabel.js +41 -26
  76. package/dist/FileInput/FileInput.d.ts +22 -1
  77. package/dist/FileInput/FileInput.d.ts.map +1 -1
  78. package/dist/FileInput/FileInput.js +56 -10
  79. package/dist/Grid/Grid.d.ts +11 -1
  80. package/dist/Grid/Grid.d.ts.map +1 -1
  81. package/dist/Grid/Grid.js +32 -6
  82. package/dist/InputGroup/InputGroup.d.ts +4 -3
  83. package/dist/InputGroup/InputGroup.d.ts.map +1 -1
  84. package/dist/InputGroup/InputGroup.js +16 -6
  85. package/dist/InputGroup/InputGroupContext.d.ts +4 -0
  86. package/dist/InputGroup/InputGroupContext.d.ts.map +1 -1
  87. package/dist/InputGroup/InputGroupContext.js +2 -2
  88. package/dist/InputGroup/groupStyles.d.ts +1 -1
  89. package/dist/InputGroup/groupStyles.d.ts.map +1 -1
  90. package/dist/InputGroup/groupStyles.js +3 -3
  91. package/dist/InputGroup/index.d.ts +1 -0
  92. package/dist/InputGroup/index.d.ts.map +1 -1
  93. package/dist/Kbd/Kbd.d.ts.map +1 -1
  94. package/dist/Kbd/Kbd.js +33 -1
  95. package/dist/Layer/layerAnimations.stylex.d.ts +4 -0
  96. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  97. package/dist/Layer/layerAnimations.stylex.js +8 -4
  98. package/dist/Layer/useLayer.d.ts +10 -0
  99. package/dist/Layer/useLayer.d.ts.map +1 -1
  100. package/dist/Layer/useLayer.js +64 -9
  101. package/dist/Layout/Layout.d.ts +4 -11
  102. package/dist/Layout/Layout.d.ts.map +1 -1
  103. package/dist/Layout/Layout.js +10 -11
  104. package/dist/Layout/LayoutFooter.d.ts +2 -2
  105. package/dist/Layout/LayoutFooter.d.ts.map +1 -1
  106. package/dist/Layout/LayoutHeader.d.ts +2 -2
  107. package/dist/Layout/LayoutHeader.d.ts.map +1 -1
  108. package/dist/Layout/LayoutPanel.d.ts +2 -2
  109. package/dist/Layout/LayoutPanel.d.ts.map +1 -1
  110. package/dist/Layout/padding.stylex.d.ts +51 -0
  111. package/dist/Layout/padding.stylex.d.ts.map +1 -1
  112. package/dist/Layout/padding.stylex.js +63 -0
  113. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  114. package/dist/Lightbox/Lightbox.js +4 -2
  115. package/dist/Link/Link.d.ts +16 -1
  116. package/dist/Link/Link.d.ts.map +1 -1
  117. package/dist/Link/Link.js +18 -7
  118. package/dist/Markdown/Markdown.d.ts.map +1 -1
  119. package/dist/Markdown/Markdown.js +7 -0
  120. package/dist/MoreMenu/MoreMenu.d.ts +1 -7
  121. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  122. package/dist/MoreMenu/MoreMenu.js +0 -2
  123. package/dist/MultiSelector/MultiSelector.d.ts +27 -2
  124. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  125. package/dist/MultiSelector/MultiSelector.js +141 -42
  126. package/dist/MultiSelector/hooks.d.ts +12 -1
  127. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  128. package/dist/MultiSelector/hooks.js +14 -1
  129. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  130. package/dist/NavMenu/NavHeadingMenu.js +34 -3
  131. package/dist/NumberInput/NumberInput.d.ts +23 -2
  132. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  133. package/dist/NumberInput/NumberInput.js +62 -12
  134. package/dist/Pagination/Pagination.d.ts +1 -1
  135. package/dist/Pagination/Pagination.d.ts.map +1 -1
  136. package/dist/Pagination/Pagination.js +15 -1
  137. package/dist/Popover/Popover.d.ts +18 -2
  138. package/dist/Popover/Popover.d.ts.map +1 -1
  139. package/dist/Popover/Popover.js +5 -2
  140. package/dist/Popover/usePopover.d.ts +38 -3
  141. package/dist/Popover/usePopover.d.ts.map +1 -1
  142. package/dist/Popover/usePopover.js +24 -9
  143. package/dist/PowerSearch/PowerSearch.d.ts +11 -1
  144. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  145. package/dist/PowerSearch/PowerSearch.js +6 -1
  146. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  147. package/dist/ProgressBar/ProgressBar.js +3 -5
  148. package/dist/RadioList/RadioList.d.ts +20 -2
  149. package/dist/RadioList/RadioList.d.ts.map +1 -1
  150. package/dist/RadioList/RadioList.js +105 -9
  151. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  152. package/dist/RadioList/RadioListItem.js +13 -2
  153. package/dist/SegmentedControl/SegmentedControl.d.ts +13 -2
  154. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  155. package/dist/SegmentedControl/SegmentedControl.js +78 -44
  156. package/dist/SegmentedControl/SegmentedControlContext.d.ts +7 -0
  157. package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
  158. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  159. package/dist/SegmentedControl/SegmentedControlItem.js +13 -2
  160. package/dist/Selector/Selector.d.ts +24 -1
  161. package/dist/Selector/Selector.d.ts.map +1 -1
  162. package/dist/Selector/Selector.js +122 -43
  163. package/dist/Selector/hooks.d.ts +8 -1
  164. package/dist/Selector/hooks.d.ts.map +1 -1
  165. package/dist/Selector/hooks.js +14 -1
  166. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  167. package/dist/SideNav/SideNavHeading.js +2 -0
  168. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  169. package/dist/SideNav/SideNavItem.js +1 -1
  170. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  171. package/dist/Skeleton/Skeleton.js +8 -2
  172. package/dist/Slider/Slider.d.ts +21 -0
  173. package/dist/Slider/Slider.d.ts.map +1 -1
  174. package/dist/Slider/Slider.js +36 -9
  175. package/dist/Spinner/Spinner.d.ts.map +1 -1
  176. package/dist/Spinner/Spinner.js +1 -1
  177. package/dist/Stack/Stack.d.ts +38 -23
  178. package/dist/Stack/Stack.d.ts.map +1 -1
  179. package/dist/Stack/Stack.js +25 -1
  180. package/dist/Stack/StackItem.d.ts +12 -23
  181. package/dist/Stack/StackItem.d.ts.map +1 -1
  182. package/dist/Stack/StackItem.js +8 -1
  183. package/dist/Switch/Switch.d.ts +23 -2
  184. package/dist/Switch/Switch.d.ts.map +1 -1
  185. package/dist/Switch/Switch.js +41 -11
  186. package/dist/TabList/Tab.d.ts.map +1 -1
  187. package/dist/TabList/Tab.js +6 -0
  188. package/dist/TabList/TabList.d.ts +16 -4
  189. package/dist/TabList/TabList.d.ts.map +1 -1
  190. package/dist/TabList/TabList.js +87 -10
  191. package/dist/TabList/TabListContext.d.ts +5 -0
  192. package/dist/TabList/TabListContext.d.ts.map +1 -1
  193. package/dist/TabList/TabListContext.js +5 -0
  194. package/dist/TabList/TabMenu.d.ts.map +1 -1
  195. package/dist/TabList/TabMenu.js +6 -1
  196. package/dist/Table/BaseTable.d.ts.map +1 -1
  197. package/dist/Table/BaseTable.js +11 -8
  198. package/dist/Table/Table.d.ts.map +1 -1
  199. package/dist/Table/Table.js +10 -1
  200. package/dist/Table/TableCell.d.ts +8 -1
  201. package/dist/Table/TableCell.d.ts.map +1 -1
  202. package/dist/Table/TableCell.js +10 -10
  203. package/dist/Table/TableHeaderCell.d.ts +8 -1
  204. package/dist/Table/TableHeaderCell.d.ts.map +1 -1
  205. package/dist/Table/TableHeaderCell.js +16 -16
  206. package/dist/Table/index.d.ts +2 -1
  207. package/dist/Table/index.d.ts.map +1 -1
  208. package/dist/Table/index.js +2 -2
  209. package/dist/Table/plugins/pagination/paginateData.d.ts +2 -1
  210. package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
  211. package/dist/Table/plugins/pagination/paginateData.js +8 -3
  212. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  213. package/dist/Table/plugins/pagination/useTablePagination.js +6 -1
  214. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  215. package/dist/Table/plugins/sortable/useTableSortable.js +59 -3
  216. package/dist/Table/tableContextMenu.d.ts +33 -0
  217. package/dist/Table/tableContextMenu.d.ts.map +1 -0
  218. package/dist/Table/tableContextMenu.js +133 -0
  219. package/dist/Table/types.d.ts +66 -0
  220. package/dist/Table/types.d.ts.map +1 -1
  221. package/dist/TextArea/TextArea.d.ts +22 -1
  222. package/dist/TextArea/TextArea.d.ts.map +1 -1
  223. package/dist/TextArea/TextArea.js +41 -9
  224. package/dist/TextInput/TextInput.d.ts +23 -2
  225. package/dist/TextInput/TextInput.d.ts.map +1 -1
  226. package/dist/TextInput/TextInput.js +55 -10
  227. package/dist/TimeInput/TimeInput.d.ts +23 -1
  228. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  229. package/dist/TimeInput/TimeInput.js +147 -76
  230. package/dist/Toast/Toast.d.ts.map +1 -1
  231. package/dist/Toast/Toast.js +14 -0
  232. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  233. package/dist/Toast/ToastViewport.js +131 -3
  234. package/dist/Tokenizer/Tokenizer.d.ts +12 -1
  235. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  236. package/dist/Tokenizer/Tokenizer.js +26 -4
  237. package/dist/Toolbar/Toolbar.d.ts +3 -3
  238. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  239. package/dist/Toolbar/Toolbar.js +51 -7
  240. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  241. package/dist/Tooltip/useTooltip.js +56 -9
  242. package/dist/TopNav/TopNavHeading.d.ts +7 -1
  243. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  244. package/dist/TopNav/TopNavHeading.js +8 -0
  245. package/dist/TreeList/TreeList.d.ts +1 -1
  246. package/dist/TreeList/TreeList.d.ts.map +1 -1
  247. package/dist/TreeList/TreeList.js +72 -2
  248. package/dist/TreeList/TreeListItem.d.ts +11 -1
  249. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  250. package/dist/TreeList/TreeListItem.js +51 -12
  251. package/dist/TreeList/treeListItem.markers.stylex.d.ts +16 -0
  252. package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +1 -0
  253. package/dist/TreeList/treeListItem.markers.stylex.js +22 -0
  254. package/dist/Typeahead/BaseTypeahead.d.ts +13 -0
  255. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  256. package/dist/Typeahead/BaseTypeahead.js +69 -6
  257. package/dist/Typeahead/Typeahead.d.ts +26 -3
  258. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  259. package/dist/Typeahead/Typeahead.js +65 -29
  260. package/dist/VisuallyHidden/VisuallyHidden.d.ts +61 -0
  261. package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
  262. package/dist/VisuallyHidden/VisuallyHidden.js +65 -0
  263. package/dist/VisuallyHidden/index.d.ts +11 -0
  264. package/dist/VisuallyHidden/index.d.ts.map +1 -0
  265. package/dist/VisuallyHidden/index.js +13 -0
  266. package/dist/astryx.css +18 -0
  267. package/dist/astryx.umd.js +55 -55
  268. package/dist/astryx.umd.js.map +4 -4
  269. package/dist/hooks/focusableSelector.d.ts +22 -0
  270. package/dist/hooks/focusableSelector.d.ts.map +1 -0
  271. package/dist/hooks/focusableSelector.js +24 -0
  272. package/dist/hooks/index.d.ts +12 -2
  273. package/dist/hooks/index.d.ts.map +1 -1
  274. package/dist/hooks/index.js +7 -2
  275. package/dist/hooks/useAnnounce.d.ts +40 -0
  276. package/dist/hooks/useAnnounce.d.ts.map +1 -0
  277. package/dist/hooks/useAnnounce.js +142 -0
  278. package/dist/hooks/useClickableContainer.d.ts +16 -1
  279. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  280. package/dist/hooks/useClickableContainer.js +16 -4
  281. package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
  282. package/dist/hooks/useEntryAnimation.js +4 -4
  283. package/dist/hooks/useFocusTrap.d.ts +18 -0
  284. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  285. package/dist/hooks/useFocusTrap.js +87 -3
  286. package/dist/hooks/useGridFocus.d.ts +88 -1
  287. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  288. package/dist/hooks/useGridFocus.js +261 -62
  289. package/dist/hooks/useKeyboardHint.d.ts +82 -0
  290. package/dist/hooks/useKeyboardHint.d.ts.map +1 -0
  291. package/dist/hooks/useKeyboardHint.js +220 -0
  292. package/dist/hooks/useListFocus.d.ts +78 -7
  293. package/dist/hooks/useListFocus.d.ts.map +1 -1
  294. package/dist/hooks/useListFocus.js +305 -52
  295. package/dist/hooks/useLongPress.d.ts +21 -0
  296. package/dist/hooks/useLongPress.d.ts.map +1 -0
  297. package/dist/hooks/useLongPress.js +89 -0
  298. package/dist/hooks/useTreeFocus.d.ts +157 -0
  299. package/dist/hooks/useTreeFocus.d.ts.map +1 -0
  300. package/dist/hooks/useTreeFocus.js +337 -0
  301. package/dist/hooks/useTypeahead.d.ts +63 -0
  302. package/dist/hooks/useTypeahead.d.ts.map +1 -0
  303. package/dist/hooks/useTypeahead.js +117 -0
  304. package/dist/index.d.ts +1 -0
  305. package/dist/index.d.ts.map +1 -1
  306. package/dist/index.js +1 -0
  307. package/dist/utils/dateTypes.d.ts +12 -0
  308. package/dist/utils/dateTypes.d.ts.map +1 -1
  309. package/dist/utils/dateTypes.js +41 -1
  310. package/dist/utils/index.d.ts +4 -0
  311. package/dist/utils/index.d.ts.map +1 -1
  312. package/dist/utils/index.js +3 -1
  313. package/dist/utils/inputAria.d.ts +29 -0
  314. package/dist/utils/inputAria.d.ts.map +1 -0
  315. package/dist/utils/inputAria.js +30 -0
  316. package/dist/utils/timeParser.d.ts.map +1 -1
  317. package/dist/utils/timeParser.js +5 -2
  318. package/docs.mjs +2 -2
  319. package/package.json +12 -8
  320. package/src/Avatar/Avatar.test.tsx +37 -0
  321. package/src/Avatar/Avatar.tsx +10 -5
  322. package/src/Breadcrumbs/BreadcrumbItem.tsx +25 -7
  323. package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -10
  324. package/src/Button/Button.tsx +3 -16
  325. package/src/ButtonGroup/ButtonGroup.test.tsx +9 -9
  326. package/src/ButtonGroup/ButtonGroup.tsx +0 -1
  327. package/src/Calendar/Calendar.doc.mjs +4 -4
  328. package/src/Calendar/Calendar.test.tsx +168 -30
  329. package/src/Calendar/Calendar.tsx +121 -66
  330. package/src/Calendar/hooks/index.ts +0 -6
  331. package/src/Calendar/index.ts +1 -3
  332. package/src/Calendar/styles.ts +4 -9
  333. package/src/Carousel/Carousel.doc.mjs +7 -1
  334. package/src/Carousel/Carousel.test.tsx +20 -0
  335. package/src/Carousel/Carousel.tsx +16 -1
  336. package/src/Center/Center.doc.mjs +12 -2
  337. package/src/Center/Center.tsx +28 -3
  338. package/src/Chat/ChatComposerInput.test.tsx +26 -7
  339. package/src/Chat/ChatComposerInput.tsx +0 -1
  340. package/src/Chat/ChatDictationButton.doc.mjs +1 -1
  341. package/src/Chat/ChatMessageList.doc.mjs +8 -0
  342. package/src/Chat/ChatMessageList.test.tsx +18 -0
  343. package/src/Chat/ChatMessageList.tsx +16 -0
  344. package/src/Chat/useTriggerMenu.tsx +22 -4
  345. package/src/CheckboxInput/CheckboxInput.doc.mjs +10 -0
  346. package/src/CheckboxInput/CheckboxInput.test.tsx +172 -27
  347. package/src/CheckboxInput/CheckboxInput.tsx +78 -6
  348. package/src/CheckboxList/CheckboxList.doc.mjs +9 -0
  349. package/src/CheckboxList/CheckboxList.test.tsx +192 -26
  350. package/src/CheckboxList/CheckboxList.tsx +56 -3
  351. package/src/CheckboxList/CheckboxListContext.tsx +7 -0
  352. package/src/CheckboxList/CheckboxListItem.tsx +0 -3
  353. package/src/Citation/Citation.doc.mjs +10 -0
  354. package/src/Citation/Citation.test.tsx +121 -0
  355. package/src/Citation/Citation.tsx +15 -3
  356. package/src/CodeBlock/CodeBlock.test.tsx +115 -0
  357. package/src/CodeBlock/CodeBlock.tsx +63 -33
  358. package/src/CommandPalette/CommandPalette.test.tsx +47 -1
  359. package/src/CommandPalette/CommandPalette.tsx +9 -21
  360. package/src/ContextMenu/ContextMenu.doc.mjs +4 -4
  361. package/src/ContextMenu/ContextMenu.test.tsx +155 -1
  362. package/src/ContextMenu/ContextMenu.tsx +113 -16
  363. package/src/DateInput/DateInput.doc.mjs +286 -56
  364. package/src/DateInput/DateInput.test.tsx +248 -2
  365. package/src/DateInput/DateInput.tsx +196 -100
  366. package/src/DateRangeInput/DateRangeInput.doc.mjs +251 -46
  367. package/src/DateRangeInput/DateRangeInput.test.tsx +182 -20
  368. package/src/DateRangeInput/DateRangeInput.tsx +63 -5
  369. package/src/DateTimeInput/DateTimeInput.doc.mjs +368 -67
  370. package/src/DateTimeInput/DateTimeInput.test.tsx +279 -11
  371. package/src/DateTimeInput/DateTimeInput.tsx +125 -17
  372. package/src/Dialog/Dialog.doc.mjs +2 -2
  373. package/src/Dialog/Dialog.tsx +12 -0
  374. package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -8
  375. package/src/DropdownMenu/DropdownMenu.test.tsx +56 -42
  376. package/src/DropdownMenu/DropdownMenu.tsx +52 -18
  377. package/src/Field/Field.doc.mjs +59 -18
  378. package/src/Field/Field.test.tsx +32 -0
  379. package/src/Field/Field.tsx +23 -5
  380. package/src/Field/FieldLabel.tsx +55 -17
  381. package/src/FileInput/FileInput.doc.mjs +17 -0
  382. package/src/FileInput/FileInput.test.tsx +283 -49
  383. package/src/FileInput/FileInput.tsx +76 -4
  384. package/src/Grid/Grid.doc.mjs +17 -4
  385. package/src/Grid/Grid.test.tsx +43 -30
  386. package/src/Grid/Grid.tsx +38 -3
  387. package/src/Icon/Icon.doc.mjs +3 -3
  388. package/src/InputGroup/InputGroup.doc.mjs +111 -23
  389. package/src/InputGroup/InputGroup.test.tsx +257 -60
  390. package/src/InputGroup/InputGroup.tsx +23 -5
  391. package/src/InputGroup/InputGroupContext.ts +9 -4
  392. package/src/InputGroup/groupStyles.ts +7 -1
  393. package/src/InputGroup/index.ts +1 -0
  394. package/src/Kbd/Kbd.test.tsx +22 -4
  395. package/src/Kbd/Kbd.tsx +35 -3
  396. package/src/Layer/layerAnimations.stylex.ts +20 -4
  397. package/src/Layer/useLayer.doc.mjs +5 -0
  398. package/src/Layer/useLayer.test.tsx +111 -0
  399. package/src/Layer/useLayer.tsx +90 -9
  400. package/src/Layout/Layout.tsx +8 -16
  401. package/src/Layout/LayoutFooter.doc.mjs +4 -4
  402. package/src/Layout/LayoutFooter.tsx +3 -3
  403. package/src/Layout/LayoutHeader.doc.mjs +4 -4
  404. package/src/Layout/LayoutHeader.tsx +3 -3
  405. package/src/Layout/LayoutPanel.tsx +3 -3
  406. package/src/Layout/padding.stylex.ts +52 -0
  407. package/src/Lightbox/Lightbox.doc.mjs +9 -0
  408. package/src/Lightbox/Lightbox.test.tsx +53 -5
  409. package/src/Lightbox/Lightbox.tsx +9 -4
  410. package/src/Link/Link.doc.mjs +14 -0
  411. package/src/Link/Link.test.tsx +50 -0
  412. package/src/Link/Link.tsx +39 -4
  413. package/src/Markdown/Markdown.test.tsx +11 -0
  414. package/src/Markdown/Markdown.tsx +7 -0
  415. package/src/MetadataList/MetadataListItem.doc.mjs +4 -0
  416. package/src/MoreMenu/MoreMenu.doc.mjs +2 -17
  417. package/src/MoreMenu/MoreMenu.tsx +0 -9
  418. package/src/MultiSelector/MultiSelector.doc.mjs +30 -0
  419. package/src/MultiSelector/MultiSelector.test.tsx +375 -8
  420. package/src/MultiSelector/MultiSelector.tsx +194 -48
  421. package/src/MultiSelector/hooks.ts +27 -0
  422. package/src/NavMenu/NavHeadingMenu.test.tsx +77 -1
  423. package/src/NavMenu/NavHeadingMenu.tsx +50 -3
  424. package/src/NumberInput/NumberInput.doc.mjs +17 -0
  425. package/src/NumberInput/NumberInput.test.tsx +242 -40
  426. package/src/NumberInput/NumberInput.tsx +89 -11
  427. package/src/Pagination/Pagination.doc.mjs +3 -3
  428. package/src/Pagination/Pagination.test.tsx +117 -5
  429. package/src/Pagination/Pagination.tsx +21 -1
  430. package/src/Popover/Popover.doc.mjs +26 -0
  431. package/src/Popover/Popover.test.tsx +121 -0
  432. package/src/Popover/Popover.tsx +23 -2
  433. package/src/Popover/usePopover.doc.mjs +9 -3
  434. package/src/Popover/usePopover.tsx +70 -12
  435. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  436. package/src/PowerSearch/PowerSearch.test.tsx +97 -1
  437. package/src/PowerSearch/PowerSearch.tsx +16 -3
  438. package/src/ProgressBar/ProgressBar.test.tsx +34 -26
  439. package/src/ProgressBar/ProgressBar.tsx +3 -4
  440. package/src/RadioList/RadioList.doc.mjs +9 -0
  441. package/src/RadioList/RadioList.test.tsx +252 -16
  442. package/src/RadioList/RadioList.tsx +166 -10
  443. package/src/RadioList/RadioListItem.tsx +14 -2
  444. package/src/SegmentedControl/SegmentedControl.doc.mjs +9 -0
  445. package/src/SegmentedControl/SegmentedControl.test.tsx +214 -42
  446. package/src/SegmentedControl/SegmentedControl.tsx +102 -54
  447. package/src/SegmentedControl/SegmentedControlContext.ts +7 -0
  448. package/src/SegmentedControl/SegmentedControlItem.tsx +14 -1
  449. package/src/SelectableCard/SelectableCard.doc.mjs +1 -1
  450. package/src/Selector/Selector.doc.mjs +26 -0
  451. package/src/Selector/Selector.test.tsx +387 -15
  452. package/src/Selector/Selector.tsx +175 -67
  453. package/src/Selector/hooks.ts +23 -0
  454. package/src/SideNav/SideNav.test.tsx +48 -0
  455. package/src/SideNav/SideNavHeading.tsx +8 -2
  456. package/src/SideNav/SideNavItem.tsx +2 -4
  457. package/src/Skeleton/Skeleton.test.tsx +25 -0
  458. package/src/Skeleton/Skeleton.tsx +11 -1
  459. package/src/Slider/Slider.doc.mjs +15 -0
  460. package/src/Slider/Slider.test.tsx +155 -2
  461. package/src/Slider/Slider.tsx +53 -5
  462. package/src/Spinner/Spinner.tsx +7 -1
  463. package/src/Stack/Stack.doc.mjs +77 -0
  464. package/src/Stack/Stack.test.tsx +55 -5
  465. package/src/Stack/Stack.tsx +73 -24
  466. package/src/Stack/StackItem.test.tsx +23 -0
  467. package/src/Stack/StackItem.tsx +20 -23
  468. package/src/Switch/Switch.doc.mjs +15 -0
  469. package/src/Switch/Switch.test.tsx +162 -27
  470. package/src/Switch/Switch.tsx +61 -21
  471. package/src/TabList/Tab.tsx +6 -0
  472. package/src/TabList/TabList.doc.mjs +6 -0
  473. package/src/TabList/TabList.test.tsx +251 -0
  474. package/src/TabList/TabList.tsx +103 -12
  475. package/src/TabList/TabListContext.ts +7 -3
  476. package/src/TabList/TabMenu.tsx +6 -3
  477. package/src/Table/BaseTable.tsx +21 -17
  478. package/src/Table/Table.doc.mjs +2 -0
  479. package/src/Table/Table.test.tsx +10 -0
  480. package/src/Table/Table.tsx +8 -0
  481. package/src/Table/TableCell.tsx +25 -23
  482. package/src/Table/TableHeaderCell.tsx +28 -29
  483. package/src/Table/index.ts +3 -2
  484. package/src/Table/plugins/pagination/paginateData.ts +10 -3
  485. package/src/Table/plugins/pagination/useTablePagination.test.tsx +16 -0
  486. package/src/Table/plugins/pagination/useTablePagination.tsx +8 -1
  487. package/src/Table/plugins/sortable/useTableSortable.test.tsx +91 -2
  488. package/src/Table/plugins/sortable/useTableSortable.tsx +53 -1
  489. package/src/Table/tableContextMenu.test.tsx +157 -0
  490. package/src/Table/tableContextMenu.tsx +142 -0
  491. package/src/Table/types.ts +74 -0
  492. package/src/Table/useTableColumnResize.doc.mjs +3 -3
  493. package/src/Table/useTableStickyColumns.doc.mjs +2 -2
  494. package/src/Text/Text.doc.mjs +18 -18
  495. package/src/TextArea/TextArea.doc.mjs +17 -0
  496. package/src/TextArea/TextArea.test.tsx +167 -35
  497. package/src/TextArea/TextArea.tsx +62 -15
  498. package/src/TextInput/TextInput.doc.mjs +17 -0
  499. package/src/TextInput/TextInput.test.tsx +168 -34
  500. package/src/TextInput/TextInput.tsx +77 -9
  501. package/src/Thumbnail/Thumbnail.doc.mjs +4 -4
  502. package/src/TimeInput/TimeInput.doc.mjs +340 -60
  503. package/src/TimeInput/TimeInput.test.tsx +255 -7
  504. package/src/TimeInput/TimeInput.tsx +176 -70
  505. package/src/Timestamp/Timestamp.doc.mjs +19 -19
  506. package/src/Toast/Toast.tsx +14 -0
  507. package/src/Toast/ToastViewport.test.tsx +214 -0
  508. package/src/Toast/ToastViewport.tsx +167 -21
  509. package/src/Tokenizer/Tokenizer.doc.mjs +29 -14
  510. package/src/Tokenizer/Tokenizer.test.tsx +105 -1
  511. package/src/Tokenizer/Tokenizer.tsx +37 -1
  512. package/src/Toolbar/Toolbar.test.tsx +109 -6
  513. package/src/Toolbar/Toolbar.tsx +59 -5
  514. package/src/Tooltip/Tooltip.test.tsx +68 -0
  515. package/src/Tooltip/useTooltip.tsx +55 -8
  516. package/src/TopNav/TopNav.test.tsx +50 -0
  517. package/src/TopNav/TopNavHeading.tsx +17 -1
  518. package/src/TreeList/TreeList.test.tsx +295 -6
  519. package/src/TreeList/TreeList.tsx +73 -1
  520. package/src/TreeList/TreeListItem.tsx +51 -5
  521. package/src/TreeList/treeListItem.markers.stylex.ts +20 -0
  522. package/src/Typeahead/BaseTypeahead.tsx +93 -9
  523. package/src/Typeahead/Typeahead.doc.mjs +141 -30
  524. package/src/Typeahead/Typeahead.test.tsx +331 -1
  525. package/src/Typeahead/Typeahead.tsx +105 -33
  526. package/src/VisuallyHidden/VisuallyHidden.doc.mjs +80 -0
  527. package/src/VisuallyHidden/VisuallyHidden.test.tsx +76 -0
  528. package/src/VisuallyHidden/VisuallyHidden.tsx +113 -0
  529. package/src/VisuallyHidden/index.ts +15 -0
  530. package/src/docs-script.test.mjs +50 -0
  531. package/src/hooks/focusableSelector.ts +25 -0
  532. package/src/hooks/index.ts +28 -2
  533. package/src/hooks/useAnnounce.test.tsx +101 -0
  534. package/src/hooks/useAnnounce.ts +168 -0
  535. package/src/hooks/useClickableContainer.ts +16 -4
  536. package/src/hooks/useEntryAnimation.ts +16 -4
  537. package/src/hooks/useFocusTrap.test.tsx +132 -0
  538. package/src/hooks/useFocusTrap.ts +98 -4
  539. package/src/hooks/useGridFocus.doc.mjs +40 -2
  540. package/src/hooks/useGridFocus.test.tsx +132 -0
  541. package/src/hooks/useGridFocus.ts +343 -67
  542. package/src/hooks/useKeyboardHint.doc.mjs +101 -0
  543. package/src/hooks/useKeyboardHint.test.tsx +70 -0
  544. package/src/hooks/useKeyboardHint.tsx +332 -0
  545. package/src/hooks/useListFocus.doc.mjs +50 -10
  546. package/src/hooks/useListFocus.test.tsx +375 -0
  547. package/src/hooks/useListFocus.ts +395 -57
  548. package/src/hooks/useLongPress.test.tsx +134 -0
  549. package/src/hooks/useLongPress.ts +119 -0
  550. package/src/hooks/useTreeFocus.doc.mjs +140 -0
  551. package/src/hooks/useTreeFocus.test.tsx +214 -0
  552. package/src/hooks/useTreeFocus.ts +559 -0
  553. package/src/hooks/useTypeahead.test.tsx +109 -0
  554. package/src/hooks/useTypeahead.ts +183 -0
  555. package/src/index.ts +1 -0
  556. package/src/theme/derivedVarRegistry.test.ts +10 -5
  557. package/src/utils/dateTypes.test.ts +40 -0
  558. package/src/utils/dateTypes.ts +40 -0
  559. package/src/utils/index.ts +9 -0
  560. package/src/utils/inputAria.test.ts +39 -0
  561. package/src/utils/inputAria.ts +58 -0
  562. package/src/utils/timeParser.test.ts +10 -0
  563. package/src/utils/timeParser.ts +5 -2
  564. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts +0 -57
  565. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts.map +0 -1
  566. package/dist/Calendar/hooks/useCalendarRovingTabindex.js +0 -96
  567. package/src/Calendar/hooks/useCalendarRovingTabindex.ts +0 -118
@@ -4,13 +4,15 @@
4
4
 
5
5
  /**
6
6
  * @file Selector.tsx
7
- * @input Uses React, StyleX, usePopover, Icon
7
+ * @input Uses React, StyleX, usePopover, useTooltip, Icon, InputGroupContext
8
8
  * @output Exports Selector component
9
9
  * @position Core implementation; consumed by index.ts
10
10
  *
11
11
  * SYNC: When modified, update:
12
12
  * - /packages/core/src/Selector/Selector.doc.mjs
13
+ * - /packages/core/src/Selector/Selector.test.tsx
13
14
  * - /packages/core/src/Selector/index.ts
15
+ * - /apps/storybook/stories/InputGroup.stories.tsx
14
16
  * - /packages/cli/templates/blocks/components/Selector/ (showcase blocks)
15
17
  */
16
18
 
@@ -27,6 +29,7 @@ import React, {
27
29
  } from 'react';
28
30
  import * as stylex from '@stylexjs/stylex';
29
31
  import {usePopover} from '../Popover/usePopover';
32
+ import {useTooltip} from '../Tooltip';
30
33
  import {Icon, renderIconSlot, type IconType} from '../Icon';
31
34
  import type {IconName} from '../Icon';
32
35
  import {
@@ -61,11 +64,14 @@ import {
61
64
  } from './utils';
62
65
  import {useCombobox, useSelectedItemOffset} from './hooks';
63
66
  import {SelectorOption} from './SelectorOption';
64
- import {mergeProps} from '../utils';
67
+ import {getInputARIA, mergeProps} from '../utils';
65
68
  import {useSize} from '../SizeContext/SizeContext';
66
69
  import type {BaseProps} from '../BaseProps';
67
70
  import type {SizeValue} from '../utils/types';
68
71
  import {themeProps} from '../utils/themeProps';
72
+ import {groupStyles} from '../InputGroup/groupStyles';
73
+ import {useInputGroup} from '../InputGroup/InputGroupContext';
74
+ import {VisuallyHidden} from '../VisuallyHidden';
69
75
 
70
76
  const styles = stylex.create({
71
77
  // Trigger container — the enhanced click target wrapping the combobox button and clear button as siblings
@@ -369,6 +375,28 @@ interface SelectorPropsBase<
369
375
  */
370
376
  isDisabled?: boolean;
371
377
 
378
+ /**
379
+ * Explains why the selector is disabled. When set together with
380
+ * `isDisabled`, the selector shows a tooltip with this text on hover and
381
+ * keyboard focus, and the trigger stays focusable (via `aria-disabled`)
382
+ * so the reason is discoverable by keyboard and assistive technology.
383
+ * Activation stays blocked.
384
+ *
385
+ * Use this instead of wrapping a disabled selector in `Tooltip` — disabled
386
+ * controls don't emit the pointer events an external tooltip needs.
387
+ *
388
+ * @example
389
+ * ```
390
+ * <Selector
391
+ * label="Owner"
392
+ * options={owners}
393
+ * isDisabled
394
+ * disabledMessage="You need the Editor role to change this"
395
+ * />
396
+ * ```
397
+ */
398
+ disabledMessage?: string;
399
+
372
400
  /**
373
401
  * The options to display in the selector.
374
402
  * Can be strings, objects, dividers, or sections.
@@ -474,34 +502,30 @@ type SelectorPropsNonClearable<
474
502
  changeAction?: (value: string) => void | Promise<void>;
475
503
  };
476
504
 
477
- type SelectorPropsClearable<
478
- T extends SelectorOptionType = SelectorOptionType,
479
- > = SelectorPropsBase<T> & {
480
- /**
481
- * Whether to show a clear button when a value is selected.
482
- * When clicked, resets the value to `null` and returns focus to the trigger.
483
- *
484
- * When enabled, `value` and `onChange` widen to include `null`.
485
- */
486
- hasClear: true;
487
- value: string | null;
488
- onChange?: (value: string | null) => void;
489
- changeAction?: (value: string | null) => void | Promise<void>;
490
- };
491
-
492
- export type SelectorProps<
493
- T extends SelectorOptionType = SelectorOptionType,
494
- > = SelectorPropsNonClearable<T> | SelectorPropsClearable<T>;
505
+ type SelectorPropsClearable<T extends SelectorOptionType = SelectorOptionType> =
506
+ SelectorPropsBase<T> & {
507
+ /**
508
+ * Whether to show a clear button when a value is selected.
509
+ * When clicked, resets the value to `null` and returns focus to the trigger.
510
+ *
511
+ * When enabled, `value` and `onChange` widen to include `null`.
512
+ */
513
+ hasClear: true;
514
+ value: string | null;
515
+ onChange?: (value: string | null) => void;
516
+ changeAction?: (value: string | null) => void | Promise<void>;
517
+ };
518
+
519
+ export type SelectorProps<T extends SelectorOptionType = SelectorOptionType> =
520
+ | SelectorPropsNonClearable<T>
521
+ | SelectorPropsClearable<T>;
495
522
 
496
523
  /**
497
524
  * Default option renderer
498
525
  */
499
526
  function DefaultOption({option}: {option: SelectorOptionData}) {
500
527
  return (
501
- <SelectorOption
502
- icon={option.icon}
503
- label={option.label ?? option.value}
504
- />
528
+ <SelectorOption icon={option.icon} label={option.label ?? option.value} />
505
529
  );
506
530
  }
507
531
 
@@ -529,6 +553,7 @@ export function Selector<T extends SelectorOptionType>(
529
553
  isOptional = false,
530
554
  isRequired = false,
531
555
  isDisabled = false,
556
+ disabledMessage,
532
557
  options,
533
558
  value,
534
559
  onChange,
@@ -561,9 +586,11 @@ export function Selector<T extends SelectorOptionType>(
561
586
  const listboxId = useId();
562
587
  const descriptionId = useId();
563
588
  const statusMessageId = useId();
589
+ const inputLabelId = useId();
564
590
  const searchId = useId();
565
591
  const triggerRef = useRef<HTMLButtonElement>(null);
566
592
  const searchRef = useRef<HTMLInputElement>(null);
593
+ const inputGroup = useInputGroup();
567
594
 
568
595
  const [searchQuery, setSearchQuery] = useState('');
569
596
 
@@ -571,14 +598,29 @@ export function Selector<T extends SelectorOptionType>(
571
598
  const [optimisticValue, setOptimisticValue] = useOptimistic(normalizedValue);
572
599
  const isBusy = isLoading || optimisticValue !== normalizedValue;
573
600
 
574
- // Build aria-describedby
575
- const ariaDescribedBy =
601
+ // Disabled-reason tooltip. Disabled controls swallow pointer events, so the
602
+ // tooltip listeners attach to the trigger container (which already exists)
603
+ // and the trigger button stays perceivable via aria-disabled instead of the
604
+ // disabled attribute. Activation is blocked by the isDisabled guards in
605
+ // useCombobox (onTriggerClick / onKeyDown).
606
+ const showsDisabledMessage = isDisabled && !!disabledMessage;
607
+ const disabledMessageTooltip = useTooltip({
608
+ placement: 'above',
609
+ // The container div is not naturally focusable; focusin bubbles up from
610
+ // the trigger button, so always attach focus listeners.
611
+ focusTrigger: 'always',
612
+ isEnabled: showsDisabledMessage,
613
+ });
614
+
615
+ const {ariaLabelledBy, ariaDescribedBy} = getInputARIA(
616
+ inputLabelId,
576
617
  [
577
618
  description ? descriptionId : null,
578
619
  status?.message ? statusMessageId : null,
579
- ]
580
- .filter(Boolean)
581
- .join(' ') || undefined;
620
+ showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
621
+ ],
622
+ inputGroup,
623
+ );
582
624
 
583
625
  // Flatten options for keyboard navigation
584
626
  const selectableItems = useMemo(
@@ -622,6 +664,9 @@ export function Selector<T extends SelectorOptionType>(
622
664
  hasLightDismiss: true,
623
665
  hasCloseButton: false,
624
666
  hasAutoFocus: false,
667
+ // The popup's own role="listbox" is the exposed semantics; the trigger
668
+ // keeps DOM focus, so wrapping it in a modal dialog would misrepresent it.
669
+ role: 'none',
625
670
  });
626
671
 
627
672
  // Open dropdown on mount when isDefaultOpen is true
@@ -653,6 +698,18 @@ export function Selector<T extends SelectorOptionType>(
653
698
  ? {marginBlockStart: `-${selectedItemOffset}px`}
654
699
  : undefined;
655
700
 
701
+ // Clear the current value. Shared by the clear button and the keyboard
702
+ // Delete/Backspace path so clearing is reachable without a mouse.
703
+ const clearValue = useCallback(() => {
704
+ onChange?.(null);
705
+ if (changeAction) {
706
+ startTransition(async () => {
707
+ setOptimisticValue(undefined);
708
+ await changeAction(null);
709
+ });
710
+ }
711
+ }, [onChange, changeAction, startTransition, setOptimisticValue]);
712
+
656
713
  // Selector behavior (keyboard nav, typeahead, selection)
657
714
  const {
658
715
  highlightedIndex,
@@ -689,22 +746,30 @@ export function Selector<T extends SelectorOptionType>(
689
746
  },
690
747
  [onChange, changeAction, startTransition, setOptimisticValue],
691
748
  ),
749
+ onClear: hasClear ? clearValue : undefined,
692
750
  listboxId,
693
751
  });
694
752
 
753
+ // Keep the highlighted option visible during keyboard navigation. The
754
+ // listbox is a fixed-height scroll container, so without this the virtual
755
+ // cursor walks off-screen once navigation passes the visible window. Mirrors
756
+ // CommandPaletteItem's scrollIntoView({block: 'nearest'}) behavior.
757
+ useEffect(() => {
758
+ if (!popover.isOpen || highlightedIndex < 0) {
759
+ return;
760
+ }
761
+ document
762
+ .getElementById(getItemId(highlightedIndex))
763
+ ?.scrollIntoView?.({block: 'nearest'});
764
+ }, [popover.isOpen, highlightedIndex, getItemId]);
765
+
695
766
  // Handle clear button click
696
767
  const handleClear = useCallback(
697
768
  (e: React.MouseEvent) => {
698
769
  e.stopPropagation(); // Don't open dropdown
699
- onChange?.(null);
700
- if (changeAction) {
701
- startTransition(async () => {
702
- setOptimisticValue(undefined);
703
- await changeAction(null);
704
- });
705
- }
770
+ clearValue();
706
771
  },
707
- [onChange, changeAction, startTransition, setOptimisticValue],
772
+ [clearValue],
708
773
  );
709
774
 
710
775
  // Render search input
@@ -717,16 +782,30 @@ export function Selector<T extends SelectorOptionType>(
717
782
  <input
718
783
  ref={searchRef}
719
784
  id={searchId}
720
- role="searchbox"
785
+ // When hasSearch is set, focus moves into this input on open, so it —
786
+ // not the trigger — must be the combobox that reports the highlighted
787
+ // option via aria-activedescendant (comboboxes-4). A bare searchbox
788
+ // left the highlight silent to screen readers.
789
+ role="combobox"
790
+ aria-expanded={popover.isOpen}
721
791
  aria-controls={listboxId}
792
+ aria-autocomplete="list"
793
+ aria-activedescendant={
794
+ popover.isOpen && highlightedIndex >= 0
795
+ ? getItemId(highlightedIndex)
796
+ : undefined
797
+ }
722
798
  aria-label="Search options"
723
799
  type="text"
724
800
  value={searchQuery}
725
801
  onChange={e => setSearchQuery(e.target.value)}
726
802
  onKeyDown={e => {
803
+ // Arrow keys navigate options; Enter selects; Escape/Tab close.
804
+ // Home/End are left to the input for caret movement.
727
805
  if (
728
806
  e.key === 'ArrowDown' ||
729
807
  e.key === 'ArrowUp' ||
808
+ e.key === 'Enter' ||
730
809
  e.key === 'Escape' ||
731
810
  e.key === 'Tab'
732
811
  ) {
@@ -745,6 +824,9 @@ export function Selector<T extends SelectorOptionType>(
745
824
  searchQuery,
746
825
  searchPlaceholder,
747
826
  onKeyDown,
827
+ popover.isOpen,
828
+ highlightedIndex,
829
+ getItemId,
748
830
  ]);
749
831
 
750
832
  // Render an individual item
@@ -812,9 +894,7 @@ export function Selector<T extends SelectorOptionType>(
812
894
  const option = options[i];
813
895
 
814
896
  if (isDivider(option)) {
815
- elements.push(
816
- <Divider key={`divider-${i}`} xstyle={styles.divider} />,
817
- );
897
+ elements.push(<Divider key={`divider-${i}`} xstyle={styles.divider} />);
818
898
  } else if (isSection(option)) {
819
899
  const sectionItems: ReactNode[] = [];
820
900
  for (const opt of option.options) {
@@ -844,30 +924,15 @@ export function Selector<T extends SelectorOptionType>(
844
924
  return elements;
845
925
  }, [options, renderItem, hasSearch, searchQuery, filteredItems]);
846
926
 
847
- return (
848
- <Field
849
- label={label}
850
- isLabelHidden={isLabelHidden}
851
- description={description}
852
- inputID={triggerId}
853
- descriptionID={description ? descriptionId : undefined}
854
- isOptional={isOptional}
855
- isRequired={isRequired}
856
- isDisabled={isDisabled}
857
- status={
858
- status
859
- ? {
860
- type: status.type,
861
- message: status.message,
862
- messageID: status.message ? statusMessageId : undefined,
863
- }
864
- : undefined
865
- }
866
- labelTooltip={labelTooltip}
867
- width={width}>
927
+ const selectorContent = (
928
+ <>
868
929
  <div
869
930
  ref={el => {
870
931
  popover.triggerRef(el);
932
+ // Anchor + hover/focus listeners for the disabled-message tooltip.
933
+ // Handlers are gated internally by isEnabled, and anchor names
934
+ // compose, so attaching unconditionally is safe.
935
+ disabledMessageTooltip.ref(el);
871
936
  }}
872
937
  onClick={onTriggerClick}
873
938
  data-testid={testId}
@@ -881,6 +946,7 @@ export function Selector<T extends SelectorOptionType>(
881
946
  !selectedItem && styles.triggerPlaceholder,
882
947
  status && inputStatusBorderStyles[status.type],
883
948
  status && inputStatusHoverShadowStyles[status.type],
949
+ inputGroup && groupStyles.inGroup,
884
950
  xstyle,
885
951
  ),
886
952
  className,
@@ -888,27 +954,38 @@ export function Selector<T extends SelectorOptionType>(
888
954
  )}>
889
955
  {startIcon &&
890
956
  renderIconSlot(startIcon, {size: 'sm', color: 'secondary'})}
957
+ {inputGroup && (
958
+ <VisuallyHidden id={inputLabelId}>{label}</VisuallyHidden>
959
+ )}
891
960
  <button
892
961
  ref={triggerRef}
893
962
  id={triggerId}
894
963
  type="button"
895
- role="combobox"
964
+ // In hasSearch mode the popup's search input is the combobox (it owns
965
+ // focus + aria-activedescendant, comboboxes-4), so the trigger is a
966
+ // plain button that opens the listbox — not a second combobox.
967
+ role={hasSearch ? undefined : 'combobox'}
896
968
  {...rest}
897
969
  aria-haspopup="listbox"
898
970
  aria-expanded={popover.isOpen}
899
971
  aria-controls={listboxId}
900
972
  aria-activedescendant={
901
- popover.isOpen && highlightedIndex >= 0
973
+ !hasSearch && popover.isOpen && highlightedIndex >= 0
902
974
  ? getItemId(highlightedIndex)
903
975
  : undefined
904
976
  }
905
977
  aria-describedby={ariaDescribedBy}
978
+ aria-labelledby={ariaLabelledBy}
906
979
  aria-required={isRequired ? 'true' : undefined}
907
980
  aria-invalid={status?.type === 'error' ? 'true' : undefined}
908
981
  aria-busy={isBusy || undefined}
909
- disabled={isDisabled}
982
+ // With a disabledMessage the trigger keeps focusability via
983
+ // aria-disabled so the reason is focus-discoverable; activation is
984
+ // still blocked by the isDisabled guards in useCombobox.
985
+ disabled={isDisabled && !showsDisabledMessage}
986
+ aria-disabled={showsDisabledMessage ? 'true' : undefined}
910
987
  onKeyDown={onKeyDown}
911
- tabIndex={-1}
988
+ tabIndex={isDisabled && !showsDisabledMessage ? -1 : 0}
912
989
  {...stylex.props(styles.trigger)}>
913
990
  <span {...stylex.props(styles.triggerLabel)}>
914
991
  {selectedItem?.label ?? placeholder}
@@ -918,7 +995,6 @@ export function Selector<T extends SelectorOptionType>(
918
995
  {hasClear && value != null && !isDisabled && (
919
996
  <button
920
997
  type="button"
921
- tabIndex={-1}
922
998
  onClick={handleClear}
923
999
  aria-label={`Clear ${label}`}
924
1000
  {...stylex.props(styles.clearButton)}>
@@ -976,6 +1052,38 @@ export function Selector<T extends SelectorOptionType>(
976
1052
  style: popoverOffsetStyle,
977
1053
  },
978
1054
  )}
1055
+
1056
+ {showsDisabledMessage &&
1057
+ disabledMessageTooltip.renderTooltip(disabledMessage)}
1058
+ </>
1059
+ );
1060
+
1061
+ if (inputGroup) {
1062
+ return selectorContent;
1063
+ }
1064
+
1065
+ return (
1066
+ <Field
1067
+ label={label}
1068
+ isLabelHidden={isLabelHidden}
1069
+ description={description}
1070
+ inputID={triggerId}
1071
+ descriptionID={description ? descriptionId : undefined}
1072
+ isOptional={isOptional}
1073
+ isRequired={isRequired}
1074
+ isDisabled={isDisabled}
1075
+ status={
1076
+ status
1077
+ ? {
1078
+ type: status.type,
1079
+ message: status.message,
1080
+ messageID: status.message ? statusMessageId : undefined,
1081
+ }
1082
+ : undefined
1083
+ }
1084
+ labelTooltip={labelTooltip}
1085
+ width={width}>
1086
+ {selectorContent}
979
1087
  </Field>
980
1088
  );
981
1089
  }
@@ -138,6 +138,13 @@ interface UseComboboxOptions {
138
138
  onOpen: () => void;
139
139
  onClose: () => void;
140
140
  onSelect?: (value: string) => void;
141
+ /**
142
+ * Clear the current value. When provided, pressing Delete or Backspace on the
143
+ * closed trigger clears the selection — a keyboard equivalent of the clear
144
+ * button, so clearing is not mouse-only. No-op when the popup is open (arrow
145
+ * navigation owns those keys there) or when there is no value.
146
+ */
147
+ onClear?: () => void;
141
148
  listboxId: string;
142
149
  }
143
150
 
@@ -163,6 +170,7 @@ export function useCombobox({
163
170
  onOpen,
164
171
  onClose,
165
172
  onSelect,
173
+ onClear,
166
174
  listboxId,
167
175
  }: UseComboboxOptions): UseComboboxResult {
168
176
  const [highlightedIndex, setHighlightedIndex] = useState<number>(-1);
@@ -310,6 +318,19 @@ export function useCombobox({
310
318
  }
311
319
  break;
312
320
 
321
+ case 'Delete':
322
+ case 'Backspace':
323
+ // Keyboard equivalent of the clear button (comboboxes-2): clear the
324
+ // value from the closed trigger so clearing is not mouse-only. When
325
+ // hasSearch is active these keys must edit the search text instead,
326
+ // and when the popup is open arrow navigation owns interaction, so
327
+ // only handle the closed non-search case with a clearable value.
328
+ if (!hasSearch && !isOpen && onClear != null && value != null) {
329
+ e.preventDefault();
330
+ onClear();
331
+ }
332
+ break;
333
+
313
334
  default:
314
335
  if (!hasSearch && e.key.length === 1 && !e.ctrlKey && !e.metaKey) {
315
336
  const newTypeahead = typeahead + e.key.toLowerCase();
@@ -349,6 +370,8 @@ export function useCombobox({
349
370
  getEnabledIndices,
350
371
  typeahead,
351
372
  hasSearch,
373
+ onClear,
374
+ value,
352
375
  ],
353
376
  );
354
377
 
@@ -234,6 +234,38 @@ describe('SideNavHeading', () => {
234
234
  expect(links[1]).toHaveAttribute('href', '/product');
235
235
  });
236
236
 
237
+ it('gives every link an accessible name with superheadingHref, headingHref, and menu', () => {
238
+ render(
239
+ <SideNavHeading
240
+ icon={<span>Icon</span>}
241
+ superheading="Suite Name"
242
+ superheadingHref="/suite"
243
+ heading="Product Name"
244
+ headingHref="/product"
245
+ menu={<div>Analytics</div>}
246
+ />,
247
+ );
248
+ // The icon link to /product previously rendered with no text and no
249
+ // aria-label, producing an empty accessible name (axe rule: link-name).
250
+ // Every link pointing at /product must now expose "Product Name".
251
+ const productLinks = screen
252
+ .getAllByRole('link', {name: 'Product Name'})
253
+ .filter(link => link.getAttribute('href') === '/product');
254
+ expect(productLinks.length).toBeGreaterThan(0);
255
+ for (const link of productLinks) {
256
+ expect(link).toHaveAccessibleName('Product Name');
257
+ }
258
+ // The independent superheading link is unaffected.
259
+ expect(screen.getByRole('link', {name: 'Suite Name'})).toHaveAttribute(
260
+ 'href',
261
+ '/suite',
262
+ );
263
+ // No link should be missing an accessible name.
264
+ for (const link of screen.getAllByRole('link')) {
265
+ expect(link).toHaveAccessibleName();
266
+ }
267
+ });
268
+
237
269
  it('shows chevron when menu is provided', () => {
238
270
  render(<SideNavHeading heading="My App" menu={<div>Menu content</div>} />);
239
271
  // The chevron SVG should be rendered
@@ -577,6 +609,22 @@ describe('SideNavItem', () => {
577
609
  expect(link).toHaveAttribute('aria-current', 'page');
578
610
  });
579
611
 
612
+ it('places aria-current on the link, not the wrapper, for split-action items', () => {
613
+ // A collapsible item (has children) WITH a primary href renders the
614
+ // split-action path: the link and the expand toggle are siblings inside a
615
+ // wrapper div. aria-current="page" must sit on the focusable link so it is
616
+ // announced as the current page (navigation-8).
617
+ render(
618
+ <SideNavItem label="Reports" href="/reports" isSelected>
619
+ <SideNavItem label="Weekly" href="/reports/weekly" />
620
+ </SideNavItem>,
621
+ );
622
+ const link = screen.getByRole('link', {name: /Reports/});
623
+ expect(link).toHaveAttribute('aria-current', 'page');
624
+ // The wrapper div must NOT carry aria-current.
625
+ expect(link.closest('[aria-current="page"]')).toBe(link);
626
+ });
627
+
580
628
  it('renders custom component when as and href are provided', () => {
581
629
  render(<SideNavItem label="Dashboard" href="/dashboard" as={CustomLink} />);
582
630
  const link = screen.getByRole('link');
@@ -652,7 +652,10 @@ export function SideNavHeading({
652
652
  )}>
653
653
  {icon &&
654
654
  (headingHref ? (
655
- <LinkComponent href={headingHref} {...stylex.props(styles.icon)}>
655
+ <LinkComponent
656
+ href={headingHref}
657
+ aria-label={heading}
658
+ {...stylex.props(styles.icon)}>
656
659
  {icon}
657
660
  </LinkComponent>
658
661
  ) : (
@@ -709,7 +712,10 @@ export function SideNavHeading({
709
712
  {...props}>
710
713
  {icon &&
711
714
  (headingHref ? (
712
- <LinkComponent href={headingHref} {...stylex.props(styles.icon)}>
715
+ <LinkComponent
716
+ href={headingHref}
717
+ aria-label={heading}
718
+ {...stylex.props(styles.icon)}>
713
719
  {icon}
714
720
  </LinkComponent>
715
721
  ) : (
@@ -612,16 +612,14 @@ export function SideNavItem({
612
612
 
613
613
  if (hasIndependentToggle) {
614
614
  itemElement = (
615
- <div
616
- aria-current={isSelected ? ('page' as const) : undefined}
617
- data-testid={testId}
618
- {...navItemStyleProps}>
615
+ <div data-testid={testId} {...navItemStyleProps}>
619
616
  <NavItemElement
620
617
  ref={ref}
621
618
  href={href}
622
619
  as={as}
623
620
  isDisabled={isDisabled}
624
621
  onClick={handleClick}
622
+ aria-current={isSelected ? ('page' as const) : undefined}
625
623
  {...stylex.props(styles.splitAction)}>
626
624
  {itemContent}
627
625
  </NavItemElement>
@@ -0,0 +1,25 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import {describe, it, expect} from 'vitest';
4
+ import {render, screen} from '@testing-library/react';
5
+ import {Skeleton} from './Skeleton';
6
+
7
+ describe('Skeleton', () => {
8
+ it('renders a placeholder element', () => {
9
+ render(<Skeleton width={200} height={20} data-testid="sk" />);
10
+ expect(screen.getByTestId('sk')).toBeInTheDocument();
11
+ });
12
+
13
+ it('is hidden from assistive tech by default (complex-20)', () => {
14
+ render(<Skeleton width={200} height={20} data-testid="sk" />);
15
+ expect(screen.getByTestId('sk')).toHaveAttribute('aria-hidden', 'true');
16
+ });
17
+
18
+ it('allows the aria-hidden default to be overridden', () => {
19
+ render(
20
+ <Skeleton width={200} height={20} data-testid="sk" aria-hidden={false} />,
21
+ );
22
+ // Consumer opt-out: not hidden when explicitly set false.
23
+ expect(screen.getByTestId('sk')).toHaveAttribute('aria-hidden', 'false');
24
+ });
25
+ });
@@ -62,7 +62,12 @@ const styles = stylex.create({
62
62
  animationDirection: 'alternate',
63
63
  animationDuration: durationVars['--duration-medium-max'],
64
64
  animationIterationCount: 'infinite',
65
- animationName: skeletonFade,
65
+ // Disable the pulse under reduced-motion; the static placeholder still
66
+ // reads as loading (complex-20).
67
+ animationName: {
68
+ default: skeletonFade,
69
+ '@media (prefers-reduced-motion: reduce)': 'none',
70
+ },
66
71
  animationTimingFunction: 'steps(10, end)',
67
72
  },
68
73
  });
@@ -180,6 +185,11 @@ export function Skeleton({
180
185
  return (
181
186
  <div
182
187
  ref={ref}
188
+ // Purely decorative loading placeholder — hide from assistive tech so it
189
+ // isn't announced as empty content. The surrounding region should convey
190
+ // the loading/busy state (e.g. aria-busy) (complex-20). Consumers can
191
+ // override via props if a specific skeleton must be exposed.
192
+ aria-hidden="true"
183
193
  data-testid={testId}
184
194
  {...mergeProps(
185
195
  themeProps('skeleton'),