@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
@@ -0,0 +1,119 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file useLongPress.ts
7
+ * @input Long-press options: onLongPress callback, disabled, delayMs, moveCancelPx
8
+ * @output Touch handlers to spread onto an element
9
+ * @position Core hook for touch long-press invocation; used by ContextMenu
10
+ *
11
+ * SYNC: When modified, update the hooks barrel /packages/core/src/hooks/index.ts
12
+ *
13
+ * Detects a single-finger long-press: fires `onLongPress` with the touch
14
+ * start point after `delayMs`. Cancels if the finger moves past
15
+ * `moveCancelPx` (treated as a scroll/drag), lifts, or the touch is
16
+ * cancelled. The pending timer is also cleared on unmount.
17
+ *
18
+ * Motivation: iOS Safari never synthesizes a `contextmenu` event on
19
+ * long-press, so long-press is the only touch affordance for opening
20
+ * cursor-positioned surfaces.
21
+ */
22
+
23
+ import {useCallback, useEffect, useRef} from 'react';
24
+
25
+ // Default long-press tuning.
26
+ const DEFAULT_DELAY_MS = 500;
27
+ const DEFAULT_MOVE_CANCEL_PX = 10;
28
+
29
+ export interface UseLongPressOptions {
30
+ /** Fired with the touch start point once the press is held for `delayMs`. */
31
+ onLongPress: (point: {x: number; y: number}) => void;
32
+ /** When true, touch handlers are inert. */
33
+ disabled?: boolean;
34
+ /** Hold duration before the press fires, in ms. Defaults to 500. */
35
+ delayMs?: number;
36
+ /** Movement past this distance (px, either axis) cancels the press. Defaults to 10. */
37
+ moveCancelPx?: number;
38
+ }
39
+
40
+ export interface UseLongPressHandlers {
41
+ onTouchStart: (e: React.TouchEvent) => void;
42
+ onTouchMove: (e: React.TouchEvent) => void;
43
+ onTouchEnd: () => void;
44
+ onTouchCancel: () => void;
45
+ }
46
+
47
+ export function useLongPress(
48
+ options: UseLongPressOptions,
49
+ ): UseLongPressHandlers {
50
+ const {
51
+ onLongPress,
52
+ disabled = false,
53
+ delayMs = DEFAULT_DELAY_MS,
54
+ moveCancelPx = DEFAULT_MOVE_CANCEL_PX,
55
+ } = options;
56
+
57
+ const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
58
+ const startRef = useRef<{x: number; y: number} | null>(null);
59
+ // Keep the latest callback without re-creating handlers on every render.
60
+ const onLongPressRef = useRef(onLongPress);
61
+ onLongPressRef.current = onLongPress;
62
+
63
+ const clear = useCallback(() => {
64
+ if (timerRef.current != null) {
65
+ clearTimeout(timerRef.current);
66
+ timerRef.current = null;
67
+ }
68
+ startRef.current = null;
69
+ }, []);
70
+
71
+ const onTouchStart = useCallback(
72
+ (e: React.TouchEvent) => {
73
+ if (disabled || e.touches.length !== 1) {
74
+ return;
75
+ }
76
+ const touch = e.touches[0];
77
+ // Clear any stale timer first, THEN record the start point — clearing
78
+ // also nulls startRef, so order matters.
79
+ clear();
80
+ startRef.current = {x: touch.clientX, y: touch.clientY};
81
+ timerRef.current = setTimeout(() => {
82
+ const start = startRef.current;
83
+ if (start == null) {
84
+ return;
85
+ }
86
+ onLongPressRef.current({x: start.x, y: start.y});
87
+ }, delayMs);
88
+ },
89
+ [disabled, delayMs, clear],
90
+ );
91
+
92
+ const onTouchMove = useCallback(
93
+ (e: React.TouchEvent) => {
94
+ const start = startRef.current;
95
+ if (start == null || e.touches.length !== 1) {
96
+ return;
97
+ }
98
+ const touch = e.touches[0];
99
+ if (
100
+ Math.abs(touch.clientX - start.x) > moveCancelPx ||
101
+ Math.abs(touch.clientY - start.y) > moveCancelPx
102
+ ) {
103
+ // Treat as a scroll/drag, not a long-press.
104
+ clear();
105
+ }
106
+ },
107
+ [moveCancelPx, clear],
108
+ );
109
+
110
+ // Cancel any pending long-press timer on unmount.
111
+ useEffect(() => clear, [clear]);
112
+
113
+ return {
114
+ onTouchStart,
115
+ onTouchMove,
116
+ onTouchEnd: clear,
117
+ onTouchCancel: clear,
118
+ };
119
+ }
@@ -0,0 +1,140 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../docs-types').HookDoc} */
4
+ export const docs = {
5
+ name: 'useTreeFocus',
6
+ displayName: 'useTreeFocus',
7
+ keywords: ['tree', 'treeview', 'focus', 'keyboard', 'navigation', 'arrow', 'expand', 'collapse', 'roving', 'tabindex', 'a11y', 'wai-aria', 'apg'],
8
+ params: [
9
+ {
10
+ name: 'options',
11
+ type: 'UseTreeFocusOptions',
12
+ description: 'Configuration object for tree focus behavior.',
13
+ required: false,
14
+ },
15
+ {
16
+ name: 'options.itemSelector',
17
+ type: 'string',
18
+ description: 'Selector for visible treeitems within the tree, in DOM order.',
19
+ default: "'[role=\"treeitem\"]'",
20
+ required: false,
21
+ },
22
+ {
23
+ name: 'options.isItemDisabled',
24
+ type: '(item: HTMLElement) => boolean',
25
+ description: 'Predicate for whether a treeitem is disabled and must be skipped during navigation. Defaults to reading `data-tree-disabled` / `aria-disabled`.',
26
+ required: false,
27
+ },
28
+ {
29
+ name: 'options.getLevel',
30
+ type: '(item: HTMLElement) => number',
31
+ description: 'Reads the 1-based nesting level of a treeitem. Defaults to the `aria-level` attribute.',
32
+ required: false,
33
+ },
34
+ {
35
+ name: 'options.onToggleExpand',
36
+ type: '(id: string) => void',
37
+ description: 'Called to expand/collapse the treeitem with the given id (ArrowRight on a collapsed parent, ArrowLeft on an expanded parent, Enter/Space on a parent without its own action).',
38
+ required: false,
39
+ },
40
+ {
41
+ name: 'options.onActivate',
42
+ type: '(item: HTMLElement, id: string | undefined) => boolean | void',
43
+ description: 'Called when Enter/Space activates a treeitem. Return true when handled; return false/undefined to let the hook fall back to toggling expansion.',
44
+ required: false,
45
+ },
46
+ {
47
+ name: 'options.onActiveChange',
48
+ type: '(id: string | undefined) => void',
49
+ description: 'Notified when the hook moves focus to a treeitem. Consumers use this to move a single roving tab stop.',
50
+ required: false,
51
+ },
52
+ {
53
+ name: 'options.hasRovingTabIndex',
54
+ type: 'boolean',
55
+ description: 'When true, the hook owns a single roving tab stop across the visible treeitems (stamps tabindex 0/-1, repairs on mount, moves with navigation). Preserves an existing tabindex="0" seed on mount. Attach the returned `handleFocus` to keep the stop in sync after clicks.',
56
+ default: 'false',
57
+ required: false,
58
+ },
59
+ {
60
+ name: 'options.typeahead',
61
+ type: 'boolean',
62
+ description: 'Whether typeahead (jump to next item whose text starts with the typed characters) is enabled.',
63
+ default: 'true',
64
+ required: false,
65
+ },
66
+ ],
67
+ returns: [
68
+ {
69
+ name: 'treeRef',
70
+ type: 'React.RefObject<HTMLElement | null>',
71
+ description: 'Ref to attach to the tree container element (role="tree").',
72
+ },
73
+ {
74
+ name: 'handleKeyDown',
75
+ type: '(e: React.KeyboardEvent) => void',
76
+ description: 'Key down handler to attach to the tree container.',
77
+ },
78
+ {
79
+ name: 'handleFocus',
80
+ type: '(e: React.FocusEvent) => void',
81
+ description: 'Focus handler to attach to the container\'s onFocus. Keeps the roving tab stop in sync when hasRovingTabIndex is enabled; a no-op otherwise, so always safe to attach.',
82
+ },
83
+ {
84
+ name: 'focusFirst',
85
+ type: '() => void',
86
+ description: 'Focus the first enabled visible treeitem.',
87
+ },
88
+ {
89
+ name: 'focusLast',
90
+ type: '() => void',
91
+ description: 'Focus the last enabled visible treeitem.',
92
+ },
93
+ ],
94
+ usage: {
95
+ description:
96
+ 'Manages roving-tabindex focus and the WAI-ARIA tree keyboard model. ArrowUp/ArrowDown/Home/End roam linearly over the visible treeitems (skipping disabled ones), while ArrowRight/ArrowLeft carry tree semantics (expand/collapse, move to first-child/parent). Enter/Space activate, and printable characters trigger typeahead.',
97
+ bestPractices: [
98
+ { guidance: true, description: 'Use for hierarchical tree widgets: wire onToggleExpand to your expansion state and onActiveChange to a single roving tab stop.' },
99
+ { guidance: true, description: 'Attach both treeRef and handleKeyDown to the role="tree" container element.' },
100
+ { guidance: false, description: 'Use for linear lists (prefer useListFocus) or 2D grids (prefer useGridFocus); those traversals differ from a tree.' },
101
+ ],
102
+ },
103
+ relatedComponents: ['TreeList'],
104
+ relatedHooks: ['useListFocus', 'useGridFocus', 'useFocusTrap'],
105
+ importPath: '@astryxdesign/core/hooks',
106
+ category: 'focus',
107
+ };
108
+
109
+ /** @type {import('../docs-types').HookTranslationDoc} */
110
+ export const docsDense = {
111
+ description:
112
+ 'Manages roving-tabindex focus + WAI-ARIA tree keyboard model. ArrowUp/Down/Home/End roam linearly over visible treeitems (skip disabled); ArrowRight/Left carry tree semantics (expand/collapse, move to first-child/parent). Enter/Space activate; printable chars trigger typeahead.',
113
+ paramDescriptions: {
114
+ options: 'config for tree focus behavior.',
115
+ 'options.itemSelector': 'selector for visible treeitems in DOM order.',
116
+ 'options.isItemDisabled': 'predicate: is treeitem disabled (skipped in nav). Defaults to data-tree-disabled / aria-disabled.',
117
+ 'options.getLevel': 'reads 1-based nesting level. Defaults to aria-level attr.',
118
+ 'options.onToggleExpand': 'expand/collapse treeitem by id (ArrowRight collapsed parent, ArrowLeft expanded parent, Enter/Space parent w/o own action).',
119
+ 'options.onActivate': 'called on Enter/Space activation. Return true when handled; else hook falls back to toggling expansion.',
120
+ 'options.onActiveChange': 'notified when focus moves to a treeitem. Use to move a single roving tab stop.',
121
+ 'options.hasRovingTabIndex': 'hook owns a single roving tab stop (stamps tabindex 0/-1, repairs on mount, moves w/ nav). Preserves an existing tabindex="0" seed. Attach handleFocus to sync after clicks.',
122
+ 'options.typeahead': 'enable typeahead (jump to next item whose text starts with typed chars).',
123
+ },
124
+ returnDescriptions: {
125
+ treeRef: 'ref to attach to tree container (role="tree").',
126
+ handleKeyDown: 'key down handler for tree container.',
127
+ handleFocus: 'onFocus handler; keeps roving tab stop in sync when hasRovingTabIndex on (no-op otherwise).',
128
+ focusFirst: 'focus first enabled visible treeitem.',
129
+ focusLast: 'focus last enabled visible treeitem.',
130
+ },
131
+ usage: {
132
+ description:
133
+ 'Manages roving-tabindex focus + WAI-ARIA tree keyboard model. ArrowUp/Down/Home/End roam linearly over visible treeitems (skip disabled); ArrowRight/Left carry tree semantics (expand/collapse, move to first-child/parent). Enter/Space activate; printable chars trigger typeahead.',
134
+ bestPractices: [
135
+ { guidance: true, description: 'Use for hierarchical tree widgets: wire onToggleExpand to expansion state + onActiveChange to a single roving tab stop.' },
136
+ { guidance: true, description: 'Attach both treeRef + handleKeyDown to role="tree" container.' },
137
+ { guidance: false, description: 'Use for linear lists (prefer useListFocus) or 2D grids (prefer useGridFocus).' },
138
+ ],
139
+ },
140
+ };
@@ -0,0 +1,214 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file useTreeFocus.test.tsx
5
+ * @input Uses vitest, @testing-library/react, useTreeFocus hook
6
+ * @output Unit tests for useTreeFocus APG tree keyboard navigation
7
+ * @position Testing; validates useTreeFocus.ts keyboard model
8
+ *
9
+ * SYNC: When useTreeFocus.ts changes, update tests to match new behavior
10
+ */
11
+
12
+ import {describe, it, expect, vi} from 'vitest';
13
+ import {useState} from 'react';
14
+ import {render, screen, fireEvent} from '@testing-library/react';
15
+ import {useTreeFocus} from './useTreeFocus';
16
+
17
+ interface Node {
18
+ id: string;
19
+ label: string;
20
+ level: number;
21
+ disabled?: boolean;
22
+ /** 'expanded' | 'collapsed' | undefined (leaf) */
23
+ expanded?: boolean;
24
+ }
25
+
26
+ const NO_NODES: Node[] = [];
27
+
28
+ /**
29
+ * Minimal generic tree harness driven by a flat list of visible nodes. Mirrors
30
+ * the DOM contract TreeList exposes: role="treeitem", aria-level, aria-expanded,
31
+ * data-tree-id, data-tree-disabled. Expansion is faked by swapping the visible
32
+ * node list on toggle so ArrowRight/ArrowLeft can be exercised.
33
+ */
34
+ function Tree({
35
+ collapsed = NO_NODES,
36
+ expanded = NO_NODES,
37
+ onActivate,
38
+ }: {
39
+ collapsed?: Node[];
40
+ expanded?: Node[];
41
+ onActivate?: (id: string | undefined) => boolean | undefined;
42
+ }) {
43
+ const [isOpen, setIsOpen] = useState(false);
44
+ const nodes = isOpen && expanded.length > 0 ? expanded : collapsed;
45
+ const {treeRef, handleKeyDown} = useTreeFocus<HTMLUListElement>({
46
+ onToggleExpand: () => setIsOpen(o => !o),
47
+ onActivate: onActivate
48
+ ? (_item, id) => onActivate(id)
49
+ : undefined,
50
+ });
51
+ return (
52
+ <ul ref={treeRef} role="tree" onKeyDown={handleKeyDown}>
53
+ {nodes.map(n => (
54
+ <li
55
+ key={n.id}
56
+ role="treeitem"
57
+ aria-level={n.level}
58
+ aria-expanded={n.expanded}
59
+ data-tree-id={n.id}
60
+ data-tree-disabled={n.disabled || undefined}
61
+ tabIndex={-1}
62
+ data-testid={n.id}>
63
+ {n.label}
64
+ </li>
65
+ ))}
66
+ </ul>
67
+ );
68
+ }
69
+
70
+ const FLAT: Node[] = [
71
+ {id: 'a', label: 'Apple', level: 1},
72
+ {id: 'b', label: 'Banana', level: 1},
73
+ {id: 'c', label: 'Cherry', level: 1},
74
+ ];
75
+
76
+ describe('useTreeFocus linear navigation', () => {
77
+ it('ArrowDown / ArrowUp move between visible treeitems', () => {
78
+ render(<Tree collapsed={FLAT} />);
79
+ const tree = screen.getByRole('tree');
80
+ screen.getByTestId('a').focus();
81
+
82
+ fireEvent.keyDown(tree, {key: 'ArrowDown'});
83
+ expect(screen.getByTestId('b')).toHaveFocus();
84
+ fireEvent.keyDown(tree, {key: 'ArrowDown'});
85
+ expect(screen.getByTestId('c')).toHaveFocus();
86
+ fireEvent.keyDown(tree, {key: 'ArrowUp'});
87
+ expect(screen.getByTestId('b')).toHaveFocus();
88
+ });
89
+
90
+ it('ArrowDown / ArrowUp skip disabled treeitems', () => {
91
+ const nodes: Node[] = [
92
+ {id: 'a', label: 'Apple', level: 1},
93
+ {id: 'b', label: 'Banana', level: 1, disabled: true},
94
+ {id: 'c', label: 'Cherry', level: 1},
95
+ ];
96
+ render(<Tree collapsed={nodes} />);
97
+ const tree = screen.getByRole('tree');
98
+ screen.getByTestId('a').focus();
99
+
100
+ fireEvent.keyDown(tree, {key: 'ArrowDown'});
101
+ expect(screen.getByTestId('c')).toHaveFocus();
102
+ fireEvent.keyDown(tree, {key: 'ArrowUp'});
103
+ expect(screen.getByTestId('a')).toHaveFocus();
104
+ });
105
+
106
+ it('Home / End move to the first and last visible treeitems', () => {
107
+ render(<Tree collapsed={FLAT} />);
108
+ const tree = screen.getByRole('tree');
109
+ screen.getByTestId('b').focus();
110
+
111
+ fireEvent.keyDown(tree, {key: 'End'});
112
+ expect(screen.getByTestId('c')).toHaveFocus();
113
+ fireEvent.keyDown(tree, {key: 'Home'});
114
+ expect(screen.getByTestId('a')).toHaveFocus();
115
+ });
116
+
117
+ it('ArrowDown does not wrap past the last item', () => {
118
+ render(<Tree collapsed={FLAT} />);
119
+ const tree = screen.getByRole('tree');
120
+ screen.getByTestId('c').focus();
121
+ fireEvent.keyDown(tree, {key: 'ArrowDown'});
122
+ expect(screen.getByTestId('c')).toHaveFocus();
123
+ });
124
+ });
125
+
126
+ describe('useTreeFocus tree semantics (Arrow Left/Right)', () => {
127
+ const COLLAPSED: Node[] = [
128
+ {id: 'p', label: 'Parent', level: 1, expanded: false},
129
+ ];
130
+ const EXPANDED: Node[] = [
131
+ {id: 'p', label: 'Parent', level: 1, expanded: true},
132
+ {id: 'c1', label: 'Child 1', level: 2},
133
+ {id: 'c2', label: 'Child 2', level: 2},
134
+ ];
135
+
136
+ it('ArrowRight expands a collapsed parent, then enters the first child', () => {
137
+ render(<Tree collapsed={COLLAPSED} expanded={EXPANDED} />);
138
+ const tree = screen.getByRole('tree');
139
+ screen.getByTestId('p').focus();
140
+
141
+ expect(screen.queryByTestId('c1')).not.toBeInTheDocument();
142
+ fireEvent.keyDown(tree, {key: 'ArrowRight'});
143
+ // Expanded now.
144
+ expect(screen.getByTestId('c1')).toBeInTheDocument();
145
+ // Focus stayed on parent.
146
+ expect(screen.getByTestId('p')).toHaveFocus();
147
+
148
+ fireEvent.keyDown(tree, {key: 'ArrowRight'});
149
+ expect(screen.getByTestId('c1')).toHaveFocus();
150
+ });
151
+
152
+ it('ArrowRight on a leaf is a no-op', () => {
153
+ render(<Tree collapsed={FLAT} />);
154
+ const tree = screen.getByRole('tree');
155
+ screen.getByTestId('a').focus();
156
+ fireEvent.keyDown(tree, {key: 'ArrowRight'});
157
+ expect(screen.getByTestId('a')).toHaveFocus();
158
+ });
159
+
160
+ it('ArrowLeft on a child moves to the parent; on an expanded parent collapses', () => {
161
+ render(<Tree collapsed={COLLAPSED} expanded={EXPANDED} />);
162
+ const tree = screen.getByRole('tree');
163
+ // Start expanded.
164
+ screen.getByTestId('p').focus();
165
+ fireEvent.keyDown(tree, {key: 'ArrowRight'}); // expand
166
+ fireEvent.keyDown(tree, {key: 'ArrowRight'}); // into child 1
167
+ expect(screen.getByTestId('c1')).toHaveFocus();
168
+
169
+ fireEvent.keyDown(tree, {key: 'ArrowLeft'}); // child leaf → parent
170
+ expect(screen.getByTestId('p')).toHaveFocus();
171
+
172
+ fireEvent.keyDown(tree, {key: 'ArrowLeft'}); // expanded parent → collapse
173
+ expect(screen.queryByTestId('c1')).not.toBeInTheDocument();
174
+ });
175
+ });
176
+
177
+ describe('useTreeFocus activation + typeahead', () => {
178
+ it('Enter/Space call onActivate for the focused item', () => {
179
+ const onActivate = vi.fn(() => true);
180
+ render(<Tree collapsed={FLAT} onActivate={onActivate} />);
181
+ const tree = screen.getByRole('tree');
182
+ screen.getByTestId('a').focus();
183
+
184
+ fireEvent.keyDown(tree, {key: 'Enter'});
185
+ expect(onActivate).toHaveBeenCalledWith('a');
186
+
187
+ fireEvent.keyDown(tree, {key: ' '});
188
+ expect(onActivate).toHaveBeenCalledTimes(2);
189
+ });
190
+
191
+ it('Enter toggles expansion when onActivate does not handle it', () => {
192
+ const COLLAPSED: Node[] = [
193
+ {id: 'p', label: 'Parent', level: 1, expanded: false},
194
+ ];
195
+ const EXPANDED: Node[] = [
196
+ {id: 'p', label: 'Parent', level: 1, expanded: true},
197
+ {id: 'c1', label: 'Child 1', level: 2},
198
+ ];
199
+ render(<Tree collapsed={COLLAPSED} expanded={EXPANDED} />);
200
+ const tree = screen.getByRole('tree');
201
+ screen.getByTestId('p').focus();
202
+ expect(screen.queryByTestId('c1')).not.toBeInTheDocument();
203
+ fireEvent.keyDown(tree, {key: 'Enter'});
204
+ expect(screen.getByTestId('c1')).toBeInTheDocument();
205
+ });
206
+
207
+ it('typeahead moves focus to the next item matching typed characters', () => {
208
+ render(<Tree collapsed={FLAT} />);
209
+ const tree = screen.getByRole('tree');
210
+ screen.getByTestId('a').focus();
211
+ fireEvent.keyDown(tree, {key: 'c'});
212
+ expect(screen.getByTestId('c')).toHaveFocus();
213
+ });
214
+ });