@astryxdesign/core 0.1.2 → 0.1.3-canary.6d57c8d

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
@@ -9,13 +9,46 @@
9
9
  * SYNC: When CheckboxList.tsx or CheckboxListItem.tsx changes, update tests to match new behavior
10
10
  */
11
11
 
12
- import {describe, it, expect, vi} from 'vitest';
13
- import {render, screen, fireEvent, within} from '@testing-library/react';
12
+ import {describe, it, expect, vi, beforeEach} from 'vitest';
13
+ import {
14
+ render,
15
+ screen,
16
+ fireEvent,
17
+ within,
18
+ waitFor,
19
+ } from '@testing-library/react';
14
20
  import userEvent from '@testing-library/user-event';
15
21
  import {CheckboxList} from './CheckboxList';
16
22
  import {CheckboxListItem} from './CheckboxListItem';
17
23
  import {List} from '../List/List';
18
24
 
25
+ // Mock showPopover/hidePopover (not implemented in jsdom) so the tooltip layer
26
+ // reflects its open state via a `popover-open` attribute the tests can assert.
27
+ beforeEach(() => {
28
+ HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) {
29
+ this.setAttribute('popover-open', '');
30
+ const event = new Event('toggle', {bubbles: false});
31
+ Object.defineProperty(event, 'newState', {value: 'open'});
32
+ this.dispatchEvent(event);
33
+ });
34
+ HTMLElement.prototype.hidePopover = vi.fn(function (this: HTMLElement) {
35
+ this.removeAttribute('popover-open');
36
+ const event = new Event('toggle', {bubbles: false});
37
+ Object.defineProperty(event, 'newState', {value: 'closed'});
38
+ this.dispatchEvent(event);
39
+ });
40
+ const originalMatches = HTMLElement.prototype.matches;
41
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
42
+ (HTMLElement.prototype as any).matches = function (
43
+ selector: string,
44
+ ): boolean {
45
+ if (selector === ':popover-open') {
46
+ return this.hasAttribute('popover-open');
47
+ }
48
+ return originalMatches.call(this, selector);
49
+ };
50
+ });
51
+
19
52
  describe('CheckboxList', () => {
20
53
  it('renders with label', () => {
21
54
  render(
@@ -26,6 +59,25 @@ describe('CheckboxList', () => {
26
59
  expect(screen.getByText('Preferences')).toBeInTheDocument();
27
60
  });
28
61
 
62
+ it('wraps items in a group named by the label (forms audit: group role)', () => {
63
+ render(
64
+ <CheckboxList label="Preferences" value={[]} onChange={() => {}}>
65
+ <CheckboxListItem label="Option A" value="a" />
66
+ </CheckboxList>,
67
+ );
68
+ // The checkboxes are wrapped in a role="group" whose accessible name comes
69
+ // from the field label (via aria-labelledby). The label is rendered as a
70
+ // <span> (not a literal <label>, which can't name a group) with no
71
+ // orphaned htmlFor.
72
+ const group = screen.getByRole('group', {name: 'Preferences'});
73
+ expect(group).toBeInTheDocument();
74
+ const label = screen.getByText('Preferences');
75
+ expect(label.tagName).toBe('SPAN');
76
+ expect(label.closest('label')).toBeNull();
77
+ expect(label).not.toHaveAttribute('for');
78
+ expect(group.getAttribute('aria-labelledby')).toBe(label.id);
79
+ });
80
+
29
81
  it('renders checkbox items', () => {
30
82
  render(
31
83
  <CheckboxList label="Preferences" value={[]} onChange={() => {}}>
@@ -39,10 +91,7 @@ describe('CheckboxList', () => {
39
91
 
40
92
  it('checks the correct items based on value prop', () => {
41
93
  render(
42
- <CheckboxList
43
- label="Preferences"
44
- value={['a', 'c']}
45
- onChange={() => {}}>
94
+ <CheckboxList label="Preferences" value={['a', 'c']} onChange={() => {}}>
46
95
  <CheckboxListItem label="Option A" value="a" />
47
96
  <CheckboxListItem label="Option B" value="b" />
48
97
  <CheckboxListItem label="Option C" value="c" />
@@ -58,10 +107,7 @@ describe('CheckboxList', () => {
58
107
  const user = userEvent.setup();
59
108
  const handleChange = vi.fn();
60
109
  render(
61
- <CheckboxList
62
- label="Preferences"
63
- value={['a']}
64
- onChange={handleChange}>
110
+ <CheckboxList label="Preferences" value={['a']} onChange={handleChange}>
65
111
  <CheckboxListItem label="Option A" value="a" />
66
112
  <CheckboxListItem label="Option B" value="b" />
67
113
  </CheckboxList>,
@@ -272,11 +318,7 @@ describe('CheckboxList', () => {
272
318
  isChecked={false}
273
319
  onCheck={handleSelectAll}
274
320
  />
275
- <CheckboxListItem
276
- label="Name"
277
- isChecked={true}
278
- onCheck={handleCheck}
279
- />
321
+ <CheckboxListItem label="Name" isChecked={true} onCheck={handleCheck} />
280
322
  <CheckboxListItem
281
323
  label="Email"
282
324
  isChecked={false}
@@ -347,8 +389,15 @@ describe('CheckboxListItem standalone mode', () => {
347
389
  <CheckboxListItem label="Partial" isChecked="indeterminate" />
348
390
  </List>,
349
391
  );
392
+ // The inner native checkbox exposes mixed state via the indeterminate DOM
393
+ // property (not a redundant aria-checked, forms-16). The list row is a
394
+ // plain listitem and must not carry aria-checked (aria-allowed-attr).
350
395
  const checkbox = screen.getByRole('checkbox');
351
- expect(checkbox).toHaveAttribute('aria-checked', 'mixed');
396
+ expect(checkbox).toBeInstanceOf(HTMLInputElement);
397
+ if (checkbox instanceof HTMLInputElement) {
398
+ expect(checkbox.indeterminate).toBe(true);
399
+ }
400
+ expect(checkbox).not.toHaveAttribute('aria-checked');
352
401
  });
353
402
 
354
403
  it('calls onCheck with true when clicking indeterminate item', async () => {
@@ -370,21 +419,26 @@ describe('CheckboxListItem standalone mode', () => {
370
419
  });
371
420
 
372
421
  describe('CheckboxListItem ARIA props', () => {
373
- it('sets aria-checked on the list item in collection mode', () => {
422
+ it('conveys checked state via the inner checkbox, not aria-checked on the listitem', () => {
374
423
  render(
375
424
  <CheckboxList label="Prefs" value={['a']} onChange={() => {}}>
376
425
  <CheckboxListItem label="Option A" value="a" />
377
426
  <CheckboxListItem label="Option B" value="b" />
378
427
  </CheckboxList>,
379
428
  );
429
+ // The listitem row must not carry aria-checked (not a valid attribute on
430
+ // role="listitem" — axe: aria-allowed-attr). Checked state is exposed by
431
+ // the inner native checkbox instead.
380
432
  const items = screen.getAllByRole('listitem');
381
- // Item A is checked
382
- expect(items[0]).toHaveAttribute('aria-checked', 'true');
383
- // Item B is not checked
384
- expect(items[1]).toHaveAttribute('aria-checked', 'false');
433
+ expect(items[0]).not.toHaveAttribute('aria-checked');
434
+ expect(items[1]).not.toHaveAttribute('aria-checked');
435
+
436
+ const checkboxes = screen.getAllByRole('checkbox');
437
+ expect(checkboxes[0]).toBeChecked();
438
+ expect(checkboxes[1]).not.toBeChecked();
385
439
  });
386
440
 
387
- it('sets aria-checked on the list item in standalone mode', () => {
441
+ it('does not put aria-checked on the listitem in standalone mode', () => {
388
442
  render(
389
443
  <List>
390
444
  <CheckboxListItem label="Done" isChecked={true} />
@@ -392,18 +446,26 @@ describe('CheckboxListItem ARIA props', () => {
392
446
  </List>,
393
447
  );
394
448
  const items = screen.getAllByRole('listitem');
395
- expect(items[0]).toHaveAttribute('aria-checked', 'true');
396
- expect(items[1]).toHaveAttribute('aria-checked', 'false');
449
+ expect(items[0]).not.toHaveAttribute('aria-checked');
450
+ expect(items[1]).not.toHaveAttribute('aria-checked');
451
+
452
+ const checkboxes = screen.getAllByRole('checkbox');
453
+ expect(checkboxes[0]).toBeChecked();
454
+ expect(checkboxes[1]).not.toBeChecked();
397
455
  });
398
456
 
399
- it('sets aria-checked="mixed" for indeterminate items', () => {
457
+ it('exposes indeterminate state via the checkbox, not aria-checked on the listitem', () => {
400
458
  render(
401
459
  <List>
402
460
  <CheckboxListItem label="Partial" isChecked="indeterminate" />
403
461
  </List>,
404
462
  );
405
463
  const item = screen.getByRole('listitem');
406
- expect(item).toHaveAttribute('aria-checked', 'mixed');
464
+ expect(item).not.toHaveAttribute('aria-checked');
465
+ const checkbox = screen.getByRole('checkbox');
466
+ if (checkbox instanceof HTMLInputElement) {
467
+ expect(checkbox.indeterminate).toBe(true);
468
+ }
407
469
  });
408
470
 
409
471
  it('does not mark items aria-busy when idle', () => {
@@ -500,4 +562,108 @@ describe('CheckboxListItem ARIA props', () => {
500
562
  expect(item).toHaveAttribute('aria-describedby', 'help-text');
501
563
  expect(item).toHaveAttribute('aria-label', 'custom label');
502
564
  });
565
+
566
+ describe('disabledMessage', () => {
567
+ const h = {hidden: true} as const;
568
+
569
+ function renderGroup(props?: {onChange?: (v: string[]) => void}) {
570
+ return render(
571
+ <CheckboxList
572
+ label="Notifications"
573
+ value={['email']}
574
+ onChange={props?.onChange ?? (() => {})}
575
+ isDisabled
576
+ disabledMessage="Notifications are managed by your administrator">
577
+ <CheckboxListItem label="Email" value="email" />
578
+ <CheckboxListItem label="SMS" value="sms" />
579
+ </CheckboxList>,
580
+ );
581
+ }
582
+
583
+ it('shows the reason tooltip on hover when the group is disabled with a reason', async () => {
584
+ renderGroup();
585
+ const tooltip = screen.getByRole('tooltip', h);
586
+ expect(tooltip).toHaveTextContent(
587
+ 'Notifications are managed by your administrator',
588
+ );
589
+ const group = screen.getByRole('group');
590
+ fireEvent.mouseEnter(group);
591
+ await waitFor(() => expect(tooltip).toHaveAttribute('popover-open'));
592
+ fireEvent.mouseLeave(group);
593
+ await waitFor(() => expect(tooltip).not.toHaveAttribute('popover-open'));
594
+ });
595
+
596
+ it('shows the reason tooltip on keyboard focus', async () => {
597
+ const user = userEvent.setup();
598
+ renderGroup();
599
+ const tooltip = screen.getByRole('tooltip', h);
600
+ await user.tab();
601
+ await waitFor(() => expect(tooltip).toHaveAttribute('popover-open'));
602
+ });
603
+
604
+ it('does not render a tooltip when not disabled', () => {
605
+ render(
606
+ <CheckboxList
607
+ label="Notifications"
608
+ value={['email']}
609
+ onChange={() => {}}
610
+ disabledMessage="Notifications are managed by your administrator">
611
+ <CheckboxListItem label="Email" value="email" />
612
+ </CheckboxList>,
613
+ );
614
+ expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
615
+ });
616
+
617
+ it('does not render a tooltip when disabled without a reason', () => {
618
+ render(
619
+ <CheckboxList
620
+ label="Notifications"
621
+ value={['email']}
622
+ onChange={() => {}}
623
+ isDisabled>
624
+ <CheckboxListItem label="Email" value="email" />
625
+ </CheckboxList>,
626
+ );
627
+ expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
628
+ });
629
+
630
+ it('keeps checkboxes focusable via aria-disabled when a reason is provided', () => {
631
+ renderGroup();
632
+ for (const checkbox of screen.getAllByRole('checkbox', h)) {
633
+ expect(checkbox).not.toBeDisabled();
634
+ expect(checkbox).toHaveAttribute('aria-disabled', 'true');
635
+ }
636
+ });
637
+
638
+ it('links the reason tooltip from the group via aria-describedby', () => {
639
+ renderGroup();
640
+ const group = screen.getByRole('group');
641
+ const tooltip = screen.getByRole('tooltip', h);
642
+ expect(group.getAttribute('aria-describedby')).toContain(tooltip.id);
643
+ });
644
+
645
+ it('blocks toggling while focusable-disabled', () => {
646
+ const onChange = vi.fn();
647
+ renderGroup({onChange});
648
+ const sms = screen.getByRole('checkbox', {name: 'SMS', hidden: true});
649
+ fireEvent.click(sms);
650
+ expect(onChange).not.toHaveBeenCalled();
651
+ });
652
+
653
+ it('keeps checkboxes natively disabled when disabled without a reason', () => {
654
+ render(
655
+ <CheckboxList
656
+ label="Notifications"
657
+ value={['email']}
658
+ onChange={() => {}}
659
+ isDisabled>
660
+ <CheckboxListItem label="Email" value="email" />
661
+ <CheckboxListItem label="SMS" value="sms" />
662
+ </CheckboxList>,
663
+ );
664
+ for (const checkbox of screen.getAllByRole('checkbox', h)) {
665
+ expect(checkbox).toBeDisabled();
666
+ }
667
+ });
668
+ });
503
669
  });
@@ -30,6 +30,7 @@ import {Field} from '../Field/Field';
30
30
  import type {InputStatus} from '../Field/types';
31
31
  import {List} from '../List/List';
32
32
  import type {ListDensity} from '../List/ListContext';
33
+ import {useTooltip} from '../Tooltip';
33
34
  import {mergeProps} from '../utils';
34
35
  import {themeProps} from '../utils/themeProps';
35
36
  import {
@@ -93,6 +94,18 @@ export interface CheckboxListProps extends Omit<
93
94
  * @default false
94
95
  */
95
96
  isDisabled?: boolean;
97
+ /**
98
+ * Explains why the checkbox group is disabled. Applies to the whole-group
99
+ * disabled state (`isDisabled`), not individual items. When set together with
100
+ * `isDisabled`, the group shows a tooltip with this text on hover and keyboard
101
+ * focus, and its checkboxes stay focusable (via `aria-disabled`) so the reason
102
+ * is discoverable by keyboard and assistive technology. Toggling stays
103
+ * blocked.
104
+ *
105
+ * Use this instead of wrapping a disabled group in `Tooltip` — disabled
106
+ * controls don't emit the pointer events an external tooltip needs.
107
+ */
108
+ disabledMessage?: string;
96
109
  /**
97
110
  * Whether all checkbox items are read-only.
98
111
  * Displays the current state at full opacity but prevents interaction.
@@ -141,6 +154,7 @@ export function CheckboxList({
141
154
  density = 'balanced',
142
155
  hasDividers = false,
143
156
  isDisabled = false,
157
+ disabledMessage,
144
158
  isReadOnly = false,
145
159
  children,
146
160
  ref,
@@ -151,6 +165,7 @@ export function CheckboxList({
151
165
  'data-testid': dataTestId,
152
166
  }: CheckboxListProps) {
153
167
  const inputID = useId();
168
+ const labelID = useId();
154
169
  const descriptionID = useId();
155
170
  const statusMessageID = useId();
156
171
 
@@ -162,6 +177,19 @@ export function CheckboxList({
162
177
  // the transition settles, so the spinner clears without manual cleanup.
163
178
  const [loadingValue, setLoadingValue] = useOptimistic<string | null>(null);
164
179
 
180
+ // Disabled-reason tooltip. Applies to the whole-group disabled state. Disabled
181
+ // controls swallow pointer events, so the tooltip listeners attach to the
182
+ // group container and the checkboxes stay perceivable via aria-disabled
183
+ // instead of the disabled attribute. Toggling is blocked in the item.
184
+ const showsDisabledMessage = isDisabled && !!disabledMessage;
185
+ const disabledMessageTooltip = useTooltip({
186
+ placement: 'above',
187
+ // The group container is not naturally focusable; focusin bubbles up from
188
+ // the checkboxes, so always attach focus listeners.
189
+ focusTrigger: 'always',
190
+ isEnabled: showsDisabledMessage,
191
+ });
192
+
165
193
  const handleChange = useCallback(
166
194
  (newValues: string[], toggledValue?: string) => {
167
195
  onChange?.(newValues);
@@ -189,6 +217,7 @@ export function CheckboxList({
189
217
  value: isCollectionMode ? optimisticValue : undefined,
190
218
  onChange: isCollectionMode ? handleChange : undefined,
191
219
  isDisabled,
220
+ hasDisabledMessage: showsDisabledMessage,
192
221
  isReadOnly,
193
222
  loadingValue,
194
223
  }),
@@ -197,6 +226,7 @@ export function CheckboxList({
197
226
  optimisticValue,
198
227
  handleChange,
199
228
  isDisabled,
229
+ showsDisabledMessage,
200
230
  isReadOnly,
201
231
  loadingValue,
202
232
  ],
@@ -210,6 +240,8 @@ export function CheckboxList({
210
240
  isLabelHidden={isLabelHidden}
211
241
  description={description}
212
242
  inputID={inputID}
243
+ labelID={labelID}
244
+ isGroupLabel
213
245
  descriptionID={description ? descriptionID : undefined}
214
246
  isDisabled={isDisabled}
215
247
  status={
@@ -226,10 +258,31 @@ export function CheckboxList({
226
258
  xstyle={xstyle}
227
259
  {...mergeProps(themeProps('checkbox-list'), {className, style})}>
228
260
  <CheckboxListContext value={contextValue}>
229
- <List density={density} hasDividers={hasDividers}>
230
- {children}
231
- </List>
261
+ <div
262
+ ref={el => {
263
+ // Anchor + hover/focus listeners for the disabled-message tooltip.
264
+ // Handlers are gated internally by isEnabled, so attaching
265
+ // unconditionally is safe.
266
+ disabledMessageTooltip.ref(el);
267
+ }}
268
+ role="group"
269
+ aria-labelledby={labelID}
270
+ aria-describedby={
271
+ [
272
+ description ? descriptionID : null,
273
+ status?.message ? statusMessageID : null,
274
+ showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
275
+ ]
276
+ .filter(Boolean)
277
+ .join(' ') || undefined
278
+ }>
279
+ <List density={density} hasDividers={hasDividers}>
280
+ {children}
281
+ </List>
282
+ </div>
232
283
  </CheckboxListContext>
284
+ {showsDisabledMessage &&
285
+ disabledMessageTooltip.renderTooltip(disabledMessage)}
233
286
  </Field>
234
287
  );
235
288
  }
@@ -21,6 +21,13 @@ export interface CheckboxListContextValue {
21
21
  */
22
22
  onChange?: (values: string[], toggledValue?: string) => void;
23
23
  isDisabled: boolean;
24
+ /**
25
+ * True when the whole group is disabled *and* a `disabledMessage` is set. In
26
+ * that mode items stay focusable via `aria-disabled` (instead of the native
27
+ * `disabled` attribute) so the group's disabled-reason tooltip is keyboard-
28
+ * and AT-discoverable; toggling is still blocked.
29
+ */
30
+ hasDisabledMessage?: boolean;
24
31
  isReadOnly: boolean;
25
32
  /**
26
33
  * The value of the item with a pending `changeAction`, or null when idle.
@@ -201,9 +201,6 @@ export function CheckboxListItem({
201
201
  endContent={endContent}
202
202
  isDisabled={effectiveDisabled}
203
203
  onClick={isInteractive ? handleToggle : undefined}
204
- aria-checked={
205
- resolvedChecked === 'indeterminate' ? 'mixed' : resolvedChecked
206
- }
207
204
  aria-busy={isBusy || undefined}
208
205
  xstyle={
209
206
  [
@@ -56,6 +56,16 @@ export const docs = {
56
56
  ],
57
57
  },
58
58
 
59
+ // `source` is a custom object type the docsite preview cannot generate
60
+ // automatically; without these defaults the properties tab shows the
61
+ // missing-required-props placeholder instead of an interactive preview.
62
+ playground: {
63
+ defaults: {
64
+ source: {title: 'Astryx Design', url: 'https://example.com'},
65
+ number: 1,
66
+ },
67
+ },
68
+
59
69
  props: [
60
70
  {
61
71
  name: 'source',
@@ -0,0 +1,121 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Citation.test.tsx
5
+ * @input Uses React Testing Library, Citation, theme tokens
6
+ * @output Tests for Citation component
7
+ */
8
+
9
+ import {render, screen} from '@testing-library/react';
10
+ import {describe, it, expect} from 'vitest';
11
+ import * as stylex from '@stylexjs/stylex';
12
+ import {colorVars} from '../theme/tokens.stylex';
13
+ import {Citation} from './Citation';
14
+
15
+ // StyleX emits one deterministic atomic class per property/value pair, so an
16
+ // element carries a probe's class exactly when it has the same declaration.
17
+ // The dev-mode debug class (contains "__") varies by source location and is
18
+ // excluded from the comparison.
19
+ const probe = stylex.create({
20
+ secondaryText: {color: colorVars['--color-text-secondary']},
21
+ accentText: {color: colorVars['--color-text-accent']},
22
+ badgeBackground: {backgroundColor: colorVars['--color-accent-muted']},
23
+ });
24
+
25
+ function atomicClasses(style: (typeof probe)[keyof typeof probe]): string[] {
26
+ const {className = ''} = stylex.props(style);
27
+ return className.split(' ').filter(c => c !== '' && !c.includes('__'));
28
+ }
29
+
30
+ describe('Citation', () => {
31
+ const source = {title: 'Example Source', url: 'https://example.com'};
32
+
33
+ it('renders the source title as a link in the label variant', () => {
34
+ render(<Citation source={source} number={1} data-testid="citation" />);
35
+ const el = screen.getByTestId('citation');
36
+ expect(el.tagName).toBe('A');
37
+ expect(el).toHaveAttribute('href', 'https://example.com');
38
+ expect(el).toHaveTextContent('Example Source');
39
+ });
40
+
41
+ it('renders the index as a badge in the number variant', () => {
42
+ render(
43
+ <Citation
44
+ source={source}
45
+ number={3}
46
+ variant="number"
47
+ data-testid="citation"
48
+ />,
49
+ );
50
+ const el = screen.getByTestId('citation');
51
+ expect(el).toHaveTextContent('3');
52
+ expect(el).toHaveAttribute('role', 'doc-noteref');
53
+ expect(el).toHaveAttribute('aria-label', 'Citation 3: Example Source');
54
+ });
55
+
56
+ it('renders as a span when the source has no url', () => {
57
+ render(
58
+ <Citation
59
+ source={{title: 'No link'}}
60
+ number={1}
61
+ data-testid="citation"
62
+ />,
63
+ );
64
+ const el = screen.getByTestId('citation');
65
+ expect(el.tagName).toBe('SPAN');
66
+ // doc-noteref is a reference role that is not permitted on a plain
67
+ // (unlinked) span (axe: aria-allowed-role), so it must be omitted here.
68
+ expect(el).not.toHaveAttribute('role');
69
+ });
70
+
71
+ it('renders astryx-* class names for theme targeting', () => {
72
+ render(<Citation source={source} number={1} data-testid="citation" />);
73
+ expect(screen.getByTestId('citation').className).toContain(
74
+ 'astryx-citation',
75
+ );
76
+ });
77
+
78
+ it('uses the secondary text color in the label variant', () => {
79
+ render(<Citation source={source} number={1} data-testid="citation" />);
80
+ const el = screen.getByTestId('citation');
81
+ for (const cls of atomicClasses(probe.secondaryText)) {
82
+ expect(el.classList.contains(cls)).toBe(true);
83
+ }
84
+ });
85
+
86
+ it('uses the secondary text color, not accent, in the number variant', () => {
87
+ render(
88
+ <Citation
89
+ source={source}
90
+ number={1}
91
+ variant="number"
92
+ data-testid="citation"
93
+ />,
94
+ );
95
+ const el = screen.getByTestId('citation');
96
+ for (const cls of atomicClasses(probe.secondaryText)) {
97
+ expect(el.classList.contains(cls)).toBe(true);
98
+ }
99
+ for (const cls of atomicClasses(probe.accentText)) {
100
+ expect(el.classList.contains(cls)).toBe(false);
101
+ }
102
+ });
103
+
104
+ it('keeps the accent-muted badge background when the source has a url', () => {
105
+ // `numberHover` must not clobber the base background: a hover-only
106
+ // conditional without a default replaces the whole property on merge,
107
+ // leaving linked badges with a transparent pill.
108
+ render(
109
+ <Citation
110
+ source={source}
111
+ number={1}
112
+ variant="number"
113
+ data-testid="citation"
114
+ />,
115
+ );
116
+ const el = screen.getByTestId('citation');
117
+ for (const cls of atomicClasses(probe.badgeBackground)) {
118
+ expect(el.classList.contains(cls)).toBe(true);
119
+ }
120
+ });
121
+ });
@@ -83,6 +83,10 @@ const styles = stylex.create({
83
83
  },
84
84
  },
85
85
  color: {
86
+ // Explicit default: without it, this hover-only conditional replaces
87
+ // the base secondary color from `label` (last-wins property merge),
88
+ // leaving linked citations to inherit the surrounding text color.
89
+ default: colorVars['--color-text-secondary'],
86
90
  ':hover': {
87
91
  '@media (hover: hover)': colorVars['--color-text-primary'],
88
92
  },
@@ -96,7 +100,7 @@ const styles = stylex.create({
96
100
  fontSize: typeScaleVars['--text-supporting-size'],
97
101
  fontWeight: fontWeightVars['--font-weight-semibold'],
98
102
  lineHeight: typeScaleVars['--text-supporting-leading'],
99
- color: colorVars['--color-text-accent'],
103
+ color: colorVars['--color-text-secondary'],
100
104
  backgroundColor: colorVars['--color-accent-muted'],
101
105
  borderRadius: radiusVars['--radius-full'],
102
106
  minWidth: spacingVars['--spacing-5'],
@@ -110,6 +114,10 @@ const styles = stylex.create({
110
114
  },
111
115
  numberHover: {
112
116
  backgroundColor: {
117
+ // Explicit default for the same reason as labelHover's color: a
118
+ // hover-only conditional replaces the base accent-muted pill from
119
+ // `number` on merge, leaving linked badges transparent.
120
+ default: colorVars['--color-accent-muted'],
113
121
  ':hover': {
114
122
  '@media (hover: hover)': colorVars['--color-overlay-hover'],
115
123
  },
@@ -150,6 +158,10 @@ export function Citation({
150
158
  const href = source.url;
151
159
  const icon = source.icon;
152
160
  const Tag = href ? 'a' : 'span';
161
+ // `doc-noteref` is a reference role — only appropriate on the interactive
162
+ // link form. On a plain (unlinked) span it is not a permitted role
163
+ // (axe: aria-allowed-role), so omit it there; the aria-label still names it.
164
+ const noteRole = href ? ('doc-noteref' as const) : undefined;
153
165
  const linkProps = href
154
166
  ? {
155
167
  href,
@@ -168,7 +180,7 @@ export function Citation({
168
180
  <Tag
169
181
  {...rest}
170
182
  ref={elementRef}
171
- role="doc-noteref"
183
+ role={noteRole}
172
184
  aria-label={`Citation ${number}: ${title}`}
173
185
  data-testid={testId}
174
186
  {...linkProps}
@@ -191,7 +203,7 @@ export function Citation({
191
203
  <Tag
192
204
  {...rest}
193
205
  ref={elementRef}
194
- role="doc-noteref"
206
+ role={noteRole}
195
207
  aria-label={`Citation ${number}: ${title}`}
196
208
  data-testid={testId}
197
209
  {...linkProps}