@astryxdesign/core 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (567) hide show
  1. package/CHANGELOG.md +165 -0
  2. package/README.md +12 -12
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +11 -5
  5. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +1 -1
  6. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  7. package/dist/Breadcrumbs/BreadcrumbItem.js +24 -5
  8. package/dist/Button/Button.d.ts.map +1 -1
  9. package/dist/Button/Button.js +2 -4
  10. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  11. package/dist/ButtonGroup/ButtonGroup.js +0 -1
  12. package/dist/Calendar/Calendar.d.ts +5 -4
  13. package/dist/Calendar/Calendar.d.ts.map +1 -1
  14. package/dist/Calendar/Calendar.js +82 -43
  15. package/dist/Calendar/hooks/index.d.ts +0 -2
  16. package/dist/Calendar/hooks/index.d.ts.map +1 -1
  17. package/dist/Calendar/hooks/index.js +1 -2
  18. package/dist/Calendar/index.d.ts +3 -3
  19. package/dist/Calendar/index.d.ts.map +1 -1
  20. package/dist/Calendar/index.js +1 -1
  21. package/dist/Calendar/styles.d.ts +3 -9
  22. package/dist/Calendar/styles.d.ts.map +1 -1
  23. package/dist/Calendar/styles.js +3 -11
  24. package/dist/Carousel/Carousel.d.ts.map +1 -1
  25. package/dist/Carousel/Carousel.js +17 -3
  26. package/dist/Center/Center.d.ts +11 -1
  27. package/dist/Center/Center.d.ts.map +1 -1
  28. package/dist/Center/Center.js +9 -3
  29. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  30. package/dist/Chat/ChatComposerInput.js +0 -1
  31. package/dist/Chat/ChatMessageList.d.ts +14 -1
  32. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  33. package/dist/Chat/ChatMessageList.js +2 -0
  34. package/dist/Chat/useTriggerMenu.d.ts +8 -1
  35. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  36. package/dist/Chat/useTriggerMenu.js +15 -3
  37. package/dist/CheckboxInput/CheckboxInput.d.ts +23 -2
  38. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  39. package/dist/CheckboxInput/CheckboxInput.js +51 -9
  40. package/dist/CheckboxList/CheckboxList.d.ts +13 -1
  41. package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
  42. package/dist/CheckboxList/CheckboxList.js +39 -9
  43. package/dist/CheckboxList/CheckboxListContext.d.ts +7 -0
  44. package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
  45. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  46. package/dist/CheckboxList/CheckboxListItem.js +0 -1
  47. package/dist/Citation/Citation.d.ts.map +1 -1
  48. package/dist/Citation/Citation.js +9 -5
  49. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  50. package/dist/CodeBlock/CodeBlock.js +51 -37
  51. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  52. package/dist/CommandPalette/CommandPalette.js +5 -5
  53. package/dist/ContextMenu/ContextMenu.d.ts +4 -5
  54. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  55. package/dist/ContextMenu/ContextMenu.js +93 -11
  56. package/dist/DateInput/DateInput.d.ts +23 -1
  57. package/dist/DateInput/DateInput.d.ts.map +1 -1
  58. package/dist/DateInput/DateInput.js +167 -99
  59. package/dist/DateRangeInput/DateRangeInput.d.ts +23 -1
  60. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  61. package/dist/DateRangeInput/DateRangeInput.js +42 -9
  62. package/dist/DateTimeInput/DateTimeInput.d.ts +35 -2
  63. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  64. package/dist/DateTimeInput/DateTimeInput.js +78 -15
  65. package/dist/Dialog/Dialog.d.ts.map +1 -1
  66. package/dist/Dialog/Dialog.js +12 -0
  67. package/dist/DropdownMenu/DropdownMenu.d.ts +1 -8
  68. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  69. package/dist/DropdownMenu/DropdownMenu.js +42 -14
  70. package/dist/Field/Field.d.ts +19 -2
  71. package/dist/Field/Field.d.ts.map +1 -1
  72. package/dist/Field/Field.js +4 -0
  73. package/dist/Field/FieldLabel.d.ts +21 -2
  74. package/dist/Field/FieldLabel.d.ts.map +1 -1
  75. package/dist/Field/FieldLabel.js +41 -26
  76. package/dist/FileInput/FileInput.d.ts +22 -1
  77. package/dist/FileInput/FileInput.d.ts.map +1 -1
  78. package/dist/FileInput/FileInput.js +56 -10
  79. package/dist/Grid/Grid.d.ts +11 -1
  80. package/dist/Grid/Grid.d.ts.map +1 -1
  81. package/dist/Grid/Grid.js +32 -6
  82. package/dist/InputGroup/InputGroup.d.ts +4 -3
  83. package/dist/InputGroup/InputGroup.d.ts.map +1 -1
  84. package/dist/InputGroup/InputGroup.js +16 -6
  85. package/dist/InputGroup/InputGroupContext.d.ts +4 -0
  86. package/dist/InputGroup/InputGroupContext.d.ts.map +1 -1
  87. package/dist/InputGroup/InputGroupContext.js +2 -2
  88. package/dist/InputGroup/groupStyles.d.ts +1 -1
  89. package/dist/InputGroup/groupStyles.d.ts.map +1 -1
  90. package/dist/InputGroup/groupStyles.js +3 -3
  91. package/dist/InputGroup/index.d.ts +1 -0
  92. package/dist/InputGroup/index.d.ts.map +1 -1
  93. package/dist/Kbd/Kbd.d.ts.map +1 -1
  94. package/dist/Kbd/Kbd.js +33 -1
  95. package/dist/Layer/layerAnimations.stylex.d.ts +4 -0
  96. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  97. package/dist/Layer/layerAnimations.stylex.js +8 -4
  98. package/dist/Layer/useLayer.d.ts +10 -0
  99. package/dist/Layer/useLayer.d.ts.map +1 -1
  100. package/dist/Layer/useLayer.js +64 -9
  101. package/dist/Layout/Layout.d.ts +4 -11
  102. package/dist/Layout/Layout.d.ts.map +1 -1
  103. package/dist/Layout/Layout.js +10 -11
  104. package/dist/Layout/LayoutFooter.d.ts +2 -2
  105. package/dist/Layout/LayoutFooter.d.ts.map +1 -1
  106. package/dist/Layout/LayoutHeader.d.ts +2 -2
  107. package/dist/Layout/LayoutHeader.d.ts.map +1 -1
  108. package/dist/Layout/LayoutPanel.d.ts +2 -2
  109. package/dist/Layout/LayoutPanel.d.ts.map +1 -1
  110. package/dist/Layout/padding.stylex.d.ts +51 -0
  111. package/dist/Layout/padding.stylex.d.ts.map +1 -1
  112. package/dist/Layout/padding.stylex.js +63 -0
  113. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  114. package/dist/Lightbox/Lightbox.js +4 -2
  115. package/dist/Link/Link.d.ts +16 -1
  116. package/dist/Link/Link.d.ts.map +1 -1
  117. package/dist/Link/Link.js +18 -7
  118. package/dist/Markdown/Markdown.d.ts.map +1 -1
  119. package/dist/Markdown/Markdown.js +7 -0
  120. package/dist/MoreMenu/MoreMenu.d.ts +1 -7
  121. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  122. package/dist/MoreMenu/MoreMenu.js +0 -2
  123. package/dist/MultiSelector/MultiSelector.d.ts +27 -2
  124. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  125. package/dist/MultiSelector/MultiSelector.js +141 -42
  126. package/dist/MultiSelector/hooks.d.ts +12 -1
  127. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  128. package/dist/MultiSelector/hooks.js +14 -1
  129. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  130. package/dist/NavMenu/NavHeadingMenu.js +34 -3
  131. package/dist/NumberInput/NumberInput.d.ts +23 -2
  132. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  133. package/dist/NumberInput/NumberInput.js +62 -12
  134. package/dist/Pagination/Pagination.d.ts +1 -1
  135. package/dist/Pagination/Pagination.d.ts.map +1 -1
  136. package/dist/Pagination/Pagination.js +15 -1
  137. package/dist/Popover/Popover.d.ts +18 -2
  138. package/dist/Popover/Popover.d.ts.map +1 -1
  139. package/dist/Popover/Popover.js +5 -2
  140. package/dist/Popover/usePopover.d.ts +38 -3
  141. package/dist/Popover/usePopover.d.ts.map +1 -1
  142. package/dist/Popover/usePopover.js +24 -9
  143. package/dist/PowerSearch/PowerSearch.d.ts +11 -1
  144. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  145. package/dist/PowerSearch/PowerSearch.js +6 -1
  146. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  147. package/dist/ProgressBar/ProgressBar.js +3 -5
  148. package/dist/RadioList/RadioList.d.ts +20 -2
  149. package/dist/RadioList/RadioList.d.ts.map +1 -1
  150. package/dist/RadioList/RadioList.js +105 -9
  151. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  152. package/dist/RadioList/RadioListItem.js +13 -2
  153. package/dist/SegmentedControl/SegmentedControl.d.ts +13 -2
  154. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  155. package/dist/SegmentedControl/SegmentedControl.js +78 -44
  156. package/dist/SegmentedControl/SegmentedControlContext.d.ts +7 -0
  157. package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
  158. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  159. package/dist/SegmentedControl/SegmentedControlItem.js +13 -2
  160. package/dist/Selector/Selector.d.ts +24 -1
  161. package/dist/Selector/Selector.d.ts.map +1 -1
  162. package/dist/Selector/Selector.js +122 -43
  163. package/dist/Selector/hooks.d.ts +8 -1
  164. package/dist/Selector/hooks.d.ts.map +1 -1
  165. package/dist/Selector/hooks.js +14 -1
  166. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  167. package/dist/SideNav/SideNavHeading.js +2 -0
  168. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  169. package/dist/SideNav/SideNavItem.js +1 -1
  170. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  171. package/dist/Skeleton/Skeleton.js +8 -2
  172. package/dist/Slider/Slider.d.ts +21 -0
  173. package/dist/Slider/Slider.d.ts.map +1 -1
  174. package/dist/Slider/Slider.js +36 -9
  175. package/dist/Spinner/Spinner.d.ts.map +1 -1
  176. package/dist/Spinner/Spinner.js +1 -1
  177. package/dist/Stack/Stack.d.ts +38 -23
  178. package/dist/Stack/Stack.d.ts.map +1 -1
  179. package/dist/Stack/Stack.js +25 -1
  180. package/dist/Stack/StackItem.d.ts +12 -23
  181. package/dist/Stack/StackItem.d.ts.map +1 -1
  182. package/dist/Stack/StackItem.js +8 -1
  183. package/dist/Switch/Switch.d.ts +23 -2
  184. package/dist/Switch/Switch.d.ts.map +1 -1
  185. package/dist/Switch/Switch.js +41 -11
  186. package/dist/TabList/Tab.d.ts.map +1 -1
  187. package/dist/TabList/Tab.js +6 -0
  188. package/dist/TabList/TabList.d.ts +16 -4
  189. package/dist/TabList/TabList.d.ts.map +1 -1
  190. package/dist/TabList/TabList.js +87 -10
  191. package/dist/TabList/TabListContext.d.ts +5 -0
  192. package/dist/TabList/TabListContext.d.ts.map +1 -1
  193. package/dist/TabList/TabListContext.js +5 -0
  194. package/dist/TabList/TabMenu.d.ts.map +1 -1
  195. package/dist/TabList/TabMenu.js +6 -1
  196. package/dist/Table/BaseTable.d.ts.map +1 -1
  197. package/dist/Table/BaseTable.js +11 -8
  198. package/dist/Table/Table.d.ts.map +1 -1
  199. package/dist/Table/Table.js +10 -1
  200. package/dist/Table/TableCell.d.ts +8 -1
  201. package/dist/Table/TableCell.d.ts.map +1 -1
  202. package/dist/Table/TableCell.js +10 -10
  203. package/dist/Table/TableHeaderCell.d.ts +8 -1
  204. package/dist/Table/TableHeaderCell.d.ts.map +1 -1
  205. package/dist/Table/TableHeaderCell.js +16 -16
  206. package/dist/Table/index.d.ts +2 -1
  207. package/dist/Table/index.d.ts.map +1 -1
  208. package/dist/Table/index.js +2 -2
  209. package/dist/Table/plugins/pagination/paginateData.d.ts +2 -1
  210. package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
  211. package/dist/Table/plugins/pagination/paginateData.js +8 -3
  212. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  213. package/dist/Table/plugins/pagination/useTablePagination.js +6 -1
  214. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  215. package/dist/Table/plugins/sortable/useTableSortable.js +59 -3
  216. package/dist/Table/tableContextMenu.d.ts +33 -0
  217. package/dist/Table/tableContextMenu.d.ts.map +1 -0
  218. package/dist/Table/tableContextMenu.js +133 -0
  219. package/dist/Table/types.d.ts +66 -0
  220. package/dist/Table/types.d.ts.map +1 -1
  221. package/dist/TextArea/TextArea.d.ts +22 -1
  222. package/dist/TextArea/TextArea.d.ts.map +1 -1
  223. package/dist/TextArea/TextArea.js +41 -9
  224. package/dist/TextInput/TextInput.d.ts +23 -2
  225. package/dist/TextInput/TextInput.d.ts.map +1 -1
  226. package/dist/TextInput/TextInput.js +55 -10
  227. package/dist/TimeInput/TimeInput.d.ts +23 -1
  228. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  229. package/dist/TimeInput/TimeInput.js +147 -76
  230. package/dist/Toast/Toast.d.ts.map +1 -1
  231. package/dist/Toast/Toast.js +14 -0
  232. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  233. package/dist/Toast/ToastViewport.js +131 -3
  234. package/dist/Tokenizer/Tokenizer.d.ts +12 -1
  235. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  236. package/dist/Tokenizer/Tokenizer.js +26 -4
  237. package/dist/Toolbar/Toolbar.d.ts +3 -3
  238. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  239. package/dist/Toolbar/Toolbar.js +51 -7
  240. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  241. package/dist/Tooltip/useTooltip.js +56 -9
  242. package/dist/TopNav/TopNavHeading.d.ts +7 -1
  243. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  244. package/dist/TopNav/TopNavHeading.js +8 -0
  245. package/dist/TreeList/TreeList.d.ts +1 -1
  246. package/dist/TreeList/TreeList.d.ts.map +1 -1
  247. package/dist/TreeList/TreeList.js +72 -2
  248. package/dist/TreeList/TreeListItem.d.ts +11 -1
  249. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  250. package/dist/TreeList/TreeListItem.js +51 -12
  251. package/dist/TreeList/treeListItem.markers.stylex.d.ts +16 -0
  252. package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +1 -0
  253. package/dist/TreeList/treeListItem.markers.stylex.js +22 -0
  254. package/dist/Typeahead/BaseTypeahead.d.ts +13 -0
  255. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  256. package/dist/Typeahead/BaseTypeahead.js +69 -6
  257. package/dist/Typeahead/Typeahead.d.ts +26 -3
  258. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  259. package/dist/Typeahead/Typeahead.js +65 -29
  260. package/dist/VisuallyHidden/VisuallyHidden.d.ts +61 -0
  261. package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
  262. package/dist/VisuallyHidden/VisuallyHidden.js +65 -0
  263. package/dist/VisuallyHidden/index.d.ts +11 -0
  264. package/dist/VisuallyHidden/index.d.ts.map +1 -0
  265. package/dist/VisuallyHidden/index.js +13 -0
  266. package/dist/astryx.css +18 -0
  267. package/dist/astryx.umd.js +55 -55
  268. package/dist/astryx.umd.js.map +4 -4
  269. package/dist/hooks/focusableSelector.d.ts +22 -0
  270. package/dist/hooks/focusableSelector.d.ts.map +1 -0
  271. package/dist/hooks/focusableSelector.js +24 -0
  272. package/dist/hooks/index.d.ts +12 -2
  273. package/dist/hooks/index.d.ts.map +1 -1
  274. package/dist/hooks/index.js +7 -2
  275. package/dist/hooks/useAnnounce.d.ts +40 -0
  276. package/dist/hooks/useAnnounce.d.ts.map +1 -0
  277. package/dist/hooks/useAnnounce.js +142 -0
  278. package/dist/hooks/useClickableContainer.d.ts +16 -1
  279. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  280. package/dist/hooks/useClickableContainer.js +16 -4
  281. package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
  282. package/dist/hooks/useEntryAnimation.js +4 -4
  283. package/dist/hooks/useFocusTrap.d.ts +18 -0
  284. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  285. package/dist/hooks/useFocusTrap.js +87 -3
  286. package/dist/hooks/useGridFocus.d.ts +88 -1
  287. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  288. package/dist/hooks/useGridFocus.js +261 -62
  289. package/dist/hooks/useKeyboardHint.d.ts +82 -0
  290. package/dist/hooks/useKeyboardHint.d.ts.map +1 -0
  291. package/dist/hooks/useKeyboardHint.js +220 -0
  292. package/dist/hooks/useListFocus.d.ts +78 -7
  293. package/dist/hooks/useListFocus.d.ts.map +1 -1
  294. package/dist/hooks/useListFocus.js +305 -52
  295. package/dist/hooks/useLongPress.d.ts +21 -0
  296. package/dist/hooks/useLongPress.d.ts.map +1 -0
  297. package/dist/hooks/useLongPress.js +89 -0
  298. package/dist/hooks/useTreeFocus.d.ts +157 -0
  299. package/dist/hooks/useTreeFocus.d.ts.map +1 -0
  300. package/dist/hooks/useTreeFocus.js +337 -0
  301. package/dist/hooks/useTypeahead.d.ts +63 -0
  302. package/dist/hooks/useTypeahead.d.ts.map +1 -0
  303. package/dist/hooks/useTypeahead.js +117 -0
  304. package/dist/index.d.ts +1 -0
  305. package/dist/index.d.ts.map +1 -1
  306. package/dist/index.js +1 -0
  307. package/dist/utils/dateTypes.d.ts +12 -0
  308. package/dist/utils/dateTypes.d.ts.map +1 -1
  309. package/dist/utils/dateTypes.js +41 -1
  310. package/dist/utils/index.d.ts +4 -0
  311. package/dist/utils/index.d.ts.map +1 -1
  312. package/dist/utils/index.js +3 -1
  313. package/dist/utils/inputAria.d.ts +29 -0
  314. package/dist/utils/inputAria.d.ts.map +1 -0
  315. package/dist/utils/inputAria.js +30 -0
  316. package/dist/utils/timeParser.d.ts.map +1 -1
  317. package/dist/utils/timeParser.js +5 -2
  318. package/docs.mjs +2 -2
  319. package/package.json +12 -8
  320. package/src/Avatar/Avatar.test.tsx +37 -0
  321. package/src/Avatar/Avatar.tsx +10 -5
  322. package/src/Breadcrumbs/BreadcrumbItem.tsx +25 -7
  323. package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -10
  324. package/src/Button/Button.tsx +3 -16
  325. package/src/ButtonGroup/ButtonGroup.test.tsx +9 -9
  326. package/src/ButtonGroup/ButtonGroup.tsx +0 -1
  327. package/src/Calendar/Calendar.doc.mjs +4 -4
  328. package/src/Calendar/Calendar.test.tsx +168 -30
  329. package/src/Calendar/Calendar.tsx +121 -66
  330. package/src/Calendar/hooks/index.ts +0 -6
  331. package/src/Calendar/index.ts +1 -3
  332. package/src/Calendar/styles.ts +4 -9
  333. package/src/Carousel/Carousel.doc.mjs +7 -1
  334. package/src/Carousel/Carousel.test.tsx +20 -0
  335. package/src/Carousel/Carousel.tsx +16 -1
  336. package/src/Center/Center.doc.mjs +12 -2
  337. package/src/Center/Center.tsx +28 -3
  338. package/src/Chat/ChatComposerInput.test.tsx +26 -7
  339. package/src/Chat/ChatComposerInput.tsx +0 -1
  340. package/src/Chat/ChatDictationButton.doc.mjs +1 -1
  341. package/src/Chat/ChatMessageList.doc.mjs +8 -0
  342. package/src/Chat/ChatMessageList.test.tsx +18 -0
  343. package/src/Chat/ChatMessageList.tsx +16 -0
  344. package/src/Chat/useTriggerMenu.tsx +22 -4
  345. package/src/CheckboxInput/CheckboxInput.doc.mjs +10 -0
  346. package/src/CheckboxInput/CheckboxInput.test.tsx +172 -27
  347. package/src/CheckboxInput/CheckboxInput.tsx +78 -6
  348. package/src/CheckboxList/CheckboxList.doc.mjs +9 -0
  349. package/src/CheckboxList/CheckboxList.test.tsx +192 -26
  350. package/src/CheckboxList/CheckboxList.tsx +56 -3
  351. package/src/CheckboxList/CheckboxListContext.tsx +7 -0
  352. package/src/CheckboxList/CheckboxListItem.tsx +0 -3
  353. package/src/Citation/Citation.doc.mjs +10 -0
  354. package/src/Citation/Citation.test.tsx +121 -0
  355. package/src/Citation/Citation.tsx +15 -3
  356. package/src/CodeBlock/CodeBlock.test.tsx +115 -0
  357. package/src/CodeBlock/CodeBlock.tsx +63 -33
  358. package/src/CommandPalette/CommandPalette.test.tsx +47 -1
  359. package/src/CommandPalette/CommandPalette.tsx +9 -21
  360. package/src/ContextMenu/ContextMenu.doc.mjs +4 -4
  361. package/src/ContextMenu/ContextMenu.test.tsx +155 -1
  362. package/src/ContextMenu/ContextMenu.tsx +113 -16
  363. package/src/DateInput/DateInput.doc.mjs +286 -56
  364. package/src/DateInput/DateInput.test.tsx +248 -2
  365. package/src/DateInput/DateInput.tsx +196 -100
  366. package/src/DateRangeInput/DateRangeInput.doc.mjs +251 -46
  367. package/src/DateRangeInput/DateRangeInput.test.tsx +182 -20
  368. package/src/DateRangeInput/DateRangeInput.tsx +63 -5
  369. package/src/DateTimeInput/DateTimeInput.doc.mjs +368 -67
  370. package/src/DateTimeInput/DateTimeInput.test.tsx +279 -11
  371. package/src/DateTimeInput/DateTimeInput.tsx +125 -17
  372. package/src/Dialog/Dialog.doc.mjs +2 -2
  373. package/src/Dialog/Dialog.tsx +12 -0
  374. package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -8
  375. package/src/DropdownMenu/DropdownMenu.test.tsx +56 -42
  376. package/src/DropdownMenu/DropdownMenu.tsx +52 -18
  377. package/src/Field/Field.doc.mjs +59 -18
  378. package/src/Field/Field.test.tsx +32 -0
  379. package/src/Field/Field.tsx +23 -5
  380. package/src/Field/FieldLabel.tsx +55 -17
  381. package/src/FileInput/FileInput.doc.mjs +17 -0
  382. package/src/FileInput/FileInput.test.tsx +283 -49
  383. package/src/FileInput/FileInput.tsx +76 -4
  384. package/src/Grid/Grid.doc.mjs +17 -4
  385. package/src/Grid/Grid.test.tsx +43 -30
  386. package/src/Grid/Grid.tsx +38 -3
  387. package/src/Icon/Icon.doc.mjs +3 -3
  388. package/src/InputGroup/InputGroup.doc.mjs +111 -23
  389. package/src/InputGroup/InputGroup.test.tsx +257 -60
  390. package/src/InputGroup/InputGroup.tsx +23 -5
  391. package/src/InputGroup/InputGroupContext.ts +9 -4
  392. package/src/InputGroup/groupStyles.ts +7 -1
  393. package/src/InputGroup/index.ts +1 -0
  394. package/src/Kbd/Kbd.test.tsx +22 -4
  395. package/src/Kbd/Kbd.tsx +35 -3
  396. package/src/Layer/layerAnimations.stylex.ts +20 -4
  397. package/src/Layer/useLayer.doc.mjs +5 -0
  398. package/src/Layer/useLayer.test.tsx +111 -0
  399. package/src/Layer/useLayer.tsx +90 -9
  400. package/src/Layout/Layout.tsx +8 -16
  401. package/src/Layout/LayoutFooter.doc.mjs +4 -4
  402. package/src/Layout/LayoutFooter.tsx +3 -3
  403. package/src/Layout/LayoutHeader.doc.mjs +4 -4
  404. package/src/Layout/LayoutHeader.tsx +3 -3
  405. package/src/Layout/LayoutPanel.tsx +3 -3
  406. package/src/Layout/padding.stylex.ts +52 -0
  407. package/src/Lightbox/Lightbox.doc.mjs +9 -0
  408. package/src/Lightbox/Lightbox.test.tsx +53 -5
  409. package/src/Lightbox/Lightbox.tsx +9 -4
  410. package/src/Link/Link.doc.mjs +14 -0
  411. package/src/Link/Link.test.tsx +50 -0
  412. package/src/Link/Link.tsx +39 -4
  413. package/src/Markdown/Markdown.test.tsx +11 -0
  414. package/src/Markdown/Markdown.tsx +7 -0
  415. package/src/MetadataList/MetadataListItem.doc.mjs +4 -0
  416. package/src/MoreMenu/MoreMenu.doc.mjs +2 -17
  417. package/src/MoreMenu/MoreMenu.tsx +0 -9
  418. package/src/MultiSelector/MultiSelector.doc.mjs +30 -0
  419. package/src/MultiSelector/MultiSelector.test.tsx +375 -8
  420. package/src/MultiSelector/MultiSelector.tsx +194 -48
  421. package/src/MultiSelector/hooks.ts +27 -0
  422. package/src/NavMenu/NavHeadingMenu.test.tsx +77 -1
  423. package/src/NavMenu/NavHeadingMenu.tsx +50 -3
  424. package/src/NumberInput/NumberInput.doc.mjs +17 -0
  425. package/src/NumberInput/NumberInput.test.tsx +242 -40
  426. package/src/NumberInput/NumberInput.tsx +89 -11
  427. package/src/Pagination/Pagination.doc.mjs +3 -3
  428. package/src/Pagination/Pagination.test.tsx +117 -5
  429. package/src/Pagination/Pagination.tsx +21 -1
  430. package/src/Popover/Popover.doc.mjs +26 -0
  431. package/src/Popover/Popover.test.tsx +121 -0
  432. package/src/Popover/Popover.tsx +23 -2
  433. package/src/Popover/usePopover.doc.mjs +9 -3
  434. package/src/Popover/usePopover.tsx +70 -12
  435. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  436. package/src/PowerSearch/PowerSearch.test.tsx +97 -1
  437. package/src/PowerSearch/PowerSearch.tsx +16 -3
  438. package/src/ProgressBar/ProgressBar.test.tsx +34 -26
  439. package/src/ProgressBar/ProgressBar.tsx +3 -4
  440. package/src/RadioList/RadioList.doc.mjs +9 -0
  441. package/src/RadioList/RadioList.test.tsx +252 -16
  442. package/src/RadioList/RadioList.tsx +166 -10
  443. package/src/RadioList/RadioListItem.tsx +14 -2
  444. package/src/SegmentedControl/SegmentedControl.doc.mjs +9 -0
  445. package/src/SegmentedControl/SegmentedControl.test.tsx +214 -42
  446. package/src/SegmentedControl/SegmentedControl.tsx +102 -54
  447. package/src/SegmentedControl/SegmentedControlContext.ts +7 -0
  448. package/src/SegmentedControl/SegmentedControlItem.tsx +14 -1
  449. package/src/SelectableCard/SelectableCard.doc.mjs +1 -1
  450. package/src/Selector/Selector.doc.mjs +26 -0
  451. package/src/Selector/Selector.test.tsx +387 -15
  452. package/src/Selector/Selector.tsx +175 -67
  453. package/src/Selector/hooks.ts +23 -0
  454. package/src/SideNav/SideNav.test.tsx +48 -0
  455. package/src/SideNav/SideNavHeading.tsx +8 -2
  456. package/src/SideNav/SideNavItem.tsx +2 -4
  457. package/src/Skeleton/Skeleton.test.tsx +25 -0
  458. package/src/Skeleton/Skeleton.tsx +11 -1
  459. package/src/Slider/Slider.doc.mjs +15 -0
  460. package/src/Slider/Slider.test.tsx +155 -2
  461. package/src/Slider/Slider.tsx +53 -5
  462. package/src/Spinner/Spinner.tsx +7 -1
  463. package/src/Stack/Stack.doc.mjs +77 -0
  464. package/src/Stack/Stack.test.tsx +55 -5
  465. package/src/Stack/Stack.tsx +73 -24
  466. package/src/Stack/StackItem.test.tsx +23 -0
  467. package/src/Stack/StackItem.tsx +20 -23
  468. package/src/Switch/Switch.doc.mjs +15 -0
  469. package/src/Switch/Switch.test.tsx +162 -27
  470. package/src/Switch/Switch.tsx +61 -21
  471. package/src/TabList/Tab.tsx +6 -0
  472. package/src/TabList/TabList.doc.mjs +6 -0
  473. package/src/TabList/TabList.test.tsx +251 -0
  474. package/src/TabList/TabList.tsx +103 -12
  475. package/src/TabList/TabListContext.ts +7 -3
  476. package/src/TabList/TabMenu.tsx +6 -3
  477. package/src/Table/BaseTable.tsx +21 -17
  478. package/src/Table/Table.doc.mjs +2 -0
  479. package/src/Table/Table.test.tsx +10 -0
  480. package/src/Table/Table.tsx +8 -0
  481. package/src/Table/TableCell.tsx +25 -23
  482. package/src/Table/TableHeaderCell.tsx +28 -29
  483. package/src/Table/index.ts +3 -2
  484. package/src/Table/plugins/pagination/paginateData.ts +10 -3
  485. package/src/Table/plugins/pagination/useTablePagination.test.tsx +16 -0
  486. package/src/Table/plugins/pagination/useTablePagination.tsx +8 -1
  487. package/src/Table/plugins/sortable/useTableSortable.test.tsx +91 -2
  488. package/src/Table/plugins/sortable/useTableSortable.tsx +53 -1
  489. package/src/Table/tableContextMenu.test.tsx +157 -0
  490. package/src/Table/tableContextMenu.tsx +142 -0
  491. package/src/Table/types.ts +74 -0
  492. package/src/Table/useTableColumnResize.doc.mjs +3 -3
  493. package/src/Table/useTableStickyColumns.doc.mjs +2 -2
  494. package/src/Text/Text.doc.mjs +18 -18
  495. package/src/TextArea/TextArea.doc.mjs +17 -0
  496. package/src/TextArea/TextArea.test.tsx +167 -35
  497. package/src/TextArea/TextArea.tsx +62 -15
  498. package/src/TextInput/TextInput.doc.mjs +17 -0
  499. package/src/TextInput/TextInput.test.tsx +168 -34
  500. package/src/TextInput/TextInput.tsx +77 -9
  501. package/src/Thumbnail/Thumbnail.doc.mjs +4 -4
  502. package/src/TimeInput/TimeInput.doc.mjs +340 -60
  503. package/src/TimeInput/TimeInput.test.tsx +255 -7
  504. package/src/TimeInput/TimeInput.tsx +176 -70
  505. package/src/Timestamp/Timestamp.doc.mjs +19 -19
  506. package/src/Toast/Toast.tsx +14 -0
  507. package/src/Toast/ToastViewport.test.tsx +214 -0
  508. package/src/Toast/ToastViewport.tsx +167 -21
  509. package/src/Tokenizer/Tokenizer.doc.mjs +29 -14
  510. package/src/Tokenizer/Tokenizer.test.tsx +105 -1
  511. package/src/Tokenizer/Tokenizer.tsx +37 -1
  512. package/src/Toolbar/Toolbar.test.tsx +109 -6
  513. package/src/Toolbar/Toolbar.tsx +59 -5
  514. package/src/Tooltip/Tooltip.test.tsx +68 -0
  515. package/src/Tooltip/useTooltip.tsx +55 -8
  516. package/src/TopNav/TopNav.test.tsx +50 -0
  517. package/src/TopNav/TopNavHeading.tsx +17 -1
  518. package/src/TreeList/TreeList.test.tsx +295 -6
  519. package/src/TreeList/TreeList.tsx +73 -1
  520. package/src/TreeList/TreeListItem.tsx +51 -5
  521. package/src/TreeList/treeListItem.markers.stylex.ts +20 -0
  522. package/src/Typeahead/BaseTypeahead.tsx +93 -9
  523. package/src/Typeahead/Typeahead.doc.mjs +141 -30
  524. package/src/Typeahead/Typeahead.test.tsx +331 -1
  525. package/src/Typeahead/Typeahead.tsx +105 -33
  526. package/src/VisuallyHidden/VisuallyHidden.doc.mjs +80 -0
  527. package/src/VisuallyHidden/VisuallyHidden.test.tsx +76 -0
  528. package/src/VisuallyHidden/VisuallyHidden.tsx +113 -0
  529. package/src/VisuallyHidden/index.ts +15 -0
  530. package/src/docs-script.test.mjs +50 -0
  531. package/src/hooks/focusableSelector.ts +25 -0
  532. package/src/hooks/index.ts +28 -2
  533. package/src/hooks/useAnnounce.test.tsx +101 -0
  534. package/src/hooks/useAnnounce.ts +168 -0
  535. package/src/hooks/useClickableContainer.ts +16 -4
  536. package/src/hooks/useEntryAnimation.ts +16 -4
  537. package/src/hooks/useFocusTrap.test.tsx +132 -0
  538. package/src/hooks/useFocusTrap.ts +98 -4
  539. package/src/hooks/useGridFocus.doc.mjs +40 -2
  540. package/src/hooks/useGridFocus.test.tsx +132 -0
  541. package/src/hooks/useGridFocus.ts +343 -67
  542. package/src/hooks/useKeyboardHint.doc.mjs +101 -0
  543. package/src/hooks/useKeyboardHint.test.tsx +70 -0
  544. package/src/hooks/useKeyboardHint.tsx +332 -0
  545. package/src/hooks/useListFocus.doc.mjs +50 -10
  546. package/src/hooks/useListFocus.test.tsx +375 -0
  547. package/src/hooks/useListFocus.ts +395 -57
  548. package/src/hooks/useLongPress.test.tsx +134 -0
  549. package/src/hooks/useLongPress.ts +119 -0
  550. package/src/hooks/useTreeFocus.doc.mjs +140 -0
  551. package/src/hooks/useTreeFocus.test.tsx +214 -0
  552. package/src/hooks/useTreeFocus.ts +559 -0
  553. package/src/hooks/useTypeahead.test.tsx +109 -0
  554. package/src/hooks/useTypeahead.ts +183 -0
  555. package/src/index.ts +1 -0
  556. package/src/theme/derivedVarRegistry.test.ts +10 -5
  557. package/src/utils/dateTypes.test.ts +40 -0
  558. package/src/utils/dateTypes.ts +40 -0
  559. package/src/utils/index.ts +9 -0
  560. package/src/utils/inputAria.test.ts +39 -0
  561. package/src/utils/inputAria.ts +58 -0
  562. package/src/utils/timeParser.test.ts +10 -0
  563. package/src/utils/timeParser.ts +5 -2
  564. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts +0 -57
  565. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts.map +0 -1
  566. package/dist/Calendar/hooks/useCalendarRovingTabindex.js +0 -96
  567. package/src/Calendar/hooks/useCalendarRovingTabindex.ts +0 -118
@@ -10,12 +10,43 @@
10
10
  */
11
11
 
12
12
  import {useState} from 'react';
13
- import {describe, it, expect, vi} from 'vitest';
14
- import {render, screen, fireEvent} from '@testing-library/react';
13
+ import {describe, it, expect, vi, beforeEach} from 'vitest';
14
+ import {render, screen, fireEvent, waitFor} from '@testing-library/react';
15
15
  import userEvent from '@testing-library/user-event';
16
16
  import {MagnifyingGlassIcon} from '@heroicons/react/24/outline';
17
17
  import {TextArea} from './TextArea';
18
18
 
19
+ // Mock showPopover/hidePopover since jsdom does not implement them. Used by the
20
+ // disabledMessage tooltip.
21
+ beforeEach(() => {
22
+ HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) {
23
+ this.setAttribute('popover-open', '');
24
+ const event = new Event('toggle', {bubbles: false});
25
+ Object.defineProperty(event, 'newState', {value: 'open'});
26
+ this.dispatchEvent(event);
27
+ });
28
+ HTMLElement.prototype.hidePopover = vi.fn(function (this: HTMLElement) {
29
+ this.removeAttribute('popover-open');
30
+ const event = new Event('toggle', {bubbles: false});
31
+ Object.defineProperty(event, 'newState', {value: 'closed'});
32
+ this.dispatchEvent(event);
33
+ });
34
+ const originalMatches = HTMLElement.prototype.matches;
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
+ (HTMLElement.prototype as any).matches = function (
37
+ selector: string,
38
+ ): boolean {
39
+ if (selector === ':popover-open') {
40
+ return this.hasAttribute('popover-open');
41
+ }
42
+ return originalMatches.call(this, selector);
43
+ };
44
+ });
45
+
46
+ // jsdom popover content is in the DOM but may not be "visible" in the
47
+ // accessibility tree. Use hidden: true to find it.
48
+ const h = {hidden: true} as const;
49
+
19
50
  describe('TextArea', () => {
20
51
  it('renders with label', () => {
21
52
  render(<TextArea label="Description" value="" onChange={() => {}} />);
@@ -39,9 +70,7 @@ describe('TextArea', () => {
39
70
  it('calls onChange with value and event when typing', async () => {
40
71
  const user = userEvent.setup();
41
72
  const handleChange = vi.fn();
42
- render(
43
- <TextArea label="Description" value="" onChange={handleChange} />,
44
- );
73
+ render(<TextArea label="Description" value="" onChange={handleChange} />);
45
74
 
46
75
  const textarea = screen.getByRole('textbox');
47
76
  await user.type(textarea, 'Hi');
@@ -73,24 +102,14 @@ describe('TextArea', () => {
73
102
  it('forwards ref correctly', () => {
74
103
  const ref = vi.fn();
75
104
  render(
76
- <TextArea
77
- ref={ref}
78
- label="Description"
79
- value=""
80
- onChange={() => {}}
81
- />,
105
+ <TextArea ref={ref} label="Description" value="" onChange={() => {}} />,
82
106
  );
83
107
  expect(ref).toHaveBeenCalledWith(expect.any(HTMLTextAreaElement));
84
108
  });
85
109
 
86
110
  it('visually hides label when isLabelHidden is true', () => {
87
111
  render(
88
- <TextArea
89
- label="Comments"
90
- isLabelHidden
91
- value=""
92
- onChange={() => {}}
93
- />,
112
+ <TextArea label="Comments" isLabelHidden value="" onChange={() => {}} />,
94
113
  );
95
114
  const label = screen.getByText('Comments');
96
115
  expect(label).toBeInTheDocument();
@@ -133,12 +152,7 @@ describe('TextArea', () => {
133
152
 
134
153
  it('is disabled when isDisabled is true', () => {
135
154
  render(
136
- <TextArea
137
- label="Description"
138
- isDisabled
139
- value=""
140
- onChange={() => {}}
141
- />,
155
+ <TextArea label="Description" isDisabled value="" onChange={() => {}} />,
142
156
  );
143
157
  expect(screen.getByRole('textbox')).toBeDisabled();
144
158
  });
@@ -150,12 +164,7 @@ describe('TextArea', () => {
150
164
 
151
165
  it('shows aria-busy when isLoading is true', () => {
152
166
  render(
153
- <TextArea
154
- label="Description"
155
- isLoading
156
- value=""
157
- onChange={() => {}}
158
- />,
167
+ <TextArea label="Description" isLoading value="" onChange={() => {}} />,
159
168
  );
160
169
  expect(screen.getByRole('textbox')).toHaveAttribute('aria-busy', 'true');
161
170
  expect(screen.getByRole('textbox')).not.toBeDisabled();
@@ -340,12 +349,7 @@ describe('TextArea', () => {
340
349
 
341
350
  it('renders with size="lg"', () => {
342
351
  render(
343
- <TextArea
344
- label="Description"
345
- value=""
346
- onChange={() => {}}
347
- size="lg"
348
- />,
352
+ <TextArea label="Description" value="" onChange={() => {}} size="lg" />,
349
353
  );
350
354
  expect(screen.getByLabelText('Description')).toBeInTheDocument();
351
355
  });
@@ -595,4 +599,132 @@ describe('TextArea', () => {
595
599
  expect(textarea).toHaveFocus();
596
600
  });
597
601
  });
602
+
603
+ describe('disabledMessage', () => {
604
+ it('shows the reason tooltip on hover when disabled with a reason', async () => {
605
+ render(
606
+ <TextArea
607
+ label="Notes"
608
+ value=""
609
+ onChange={() => {}}
610
+ isDisabled
611
+ disabledMessage="You need the Editor role"
612
+ />,
613
+ );
614
+
615
+ const textarea = screen.getByRole('textbox');
616
+ const container = textarea.parentElement as HTMLElement;
617
+ const tooltip = screen.getByRole('tooltip', h);
618
+ expect(tooltip).toHaveTextContent('You need the Editor role');
619
+
620
+ fireEvent.mouseEnter(container);
621
+ await waitFor(() => {
622
+ expect(tooltip).toHaveAttribute('popover-open');
623
+ });
624
+
625
+ fireEvent.mouseLeave(container);
626
+ await waitFor(() => {
627
+ expect(tooltip).not.toHaveAttribute('popover-open');
628
+ });
629
+ });
630
+
631
+ it('shows the reason tooltip on keyboard focus', async () => {
632
+ const user = userEvent.setup();
633
+ render(
634
+ <TextArea
635
+ label="Notes"
636
+ value=""
637
+ onChange={() => {}}
638
+ isDisabled
639
+ disabledMessage="You need the Editor role"
640
+ />,
641
+ );
642
+
643
+ const tooltip = screen.getByRole('tooltip', h);
644
+ await user.tab();
645
+ expect(screen.getByRole('textbox')).toHaveFocus();
646
+ await waitFor(() => {
647
+ expect(tooltip).toHaveAttribute('popover-open');
648
+ });
649
+ });
650
+
651
+ it('does not render a tooltip when not disabled', () => {
652
+ render(
653
+ <TextArea
654
+ label="Notes"
655
+ value=""
656
+ onChange={() => {}}
657
+ disabledMessage="You need the Editor role"
658
+ />,
659
+ );
660
+ expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
661
+ });
662
+
663
+ it('does not render a tooltip when disabled without a reason', () => {
664
+ render(
665
+ <TextArea label="Notes" value="" onChange={() => {}} isDisabled />,
666
+ );
667
+ expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
668
+ });
669
+
670
+ it('keeps the textarea focusable via aria-disabled when a reason is provided', () => {
671
+ render(
672
+ <TextArea
673
+ label="Notes"
674
+ value=""
675
+ onChange={() => {}}
676
+ isDisabled
677
+ disabledMessage="You need the Editor role"
678
+ />,
679
+ );
680
+ const textarea = screen.getByRole('textbox');
681
+ expect(textarea).not.toBeDisabled();
682
+ expect(textarea).toHaveAttribute('aria-disabled', 'true');
683
+ expect(textarea).toHaveAttribute('readonly');
684
+ });
685
+
686
+ it('links the reason tooltip from the textarea via aria-describedby', () => {
687
+ render(
688
+ <TextArea
689
+ label="Notes"
690
+ value=""
691
+ onChange={() => {}}
692
+ isDisabled
693
+ disabledMessage="You need the Editor role"
694
+ />,
695
+ );
696
+ const textarea = screen.getByRole('textbox');
697
+ const tooltip = screen.getByRole('tooltip', h);
698
+ expect(textarea.getAttribute('aria-describedby')).toContain(tooltip.id);
699
+ });
700
+
701
+ it('blocks value changes while focusable-disabled', async () => {
702
+ const user = userEvent.setup();
703
+ const onChange = vi.fn();
704
+ render(
705
+ <TextArea
706
+ label="Notes"
707
+ value=""
708
+ onChange={onChange}
709
+ isDisabled
710
+ disabledMessage="You need the Editor role"
711
+ />,
712
+ );
713
+
714
+ const textarea = screen.getByRole('textbox');
715
+ await user.click(textarea);
716
+ await user.keyboard('hello');
717
+ expect(onChange).not.toHaveBeenCalled();
718
+ expect(textarea).toHaveValue('');
719
+ });
720
+
721
+ it('remains natively disabled when disabled without a reason', () => {
722
+ render(
723
+ <TextArea label="Notes" value="" onChange={() => {}} isDisabled />,
724
+ );
725
+ const textarea = screen.getByRole('textbox');
726
+ expect(textarea).toBeDisabled();
727
+ expect(textarea).not.toHaveAttribute('aria-disabled');
728
+ });
729
+ });
598
730
  });
@@ -43,12 +43,14 @@ import {
43
43
  } from '../Field';
44
44
  import {Icon, renderIconSlot, type IconType} from '../Icon';
45
45
  import {Spinner} from '../Spinner';
46
+ import {useTooltip} from '../Tooltip';
46
47
  import {mergeProps, mergeRefs} from '../utils';
47
48
  import type {BaseProps} from '../BaseProps';
48
49
  import type {SizeValue} from '../utils/types';
49
50
  import {useInputContainer} from '../hooks/useInputContainer';
50
51
  import {useSize} from '../SizeContext/SizeContext';
51
52
  import {themeProps} from '../utils/themeProps';
53
+ import {VisuallyHidden} from '../VisuallyHidden';
52
54
 
53
55
  const COUNTER_WARNING_THRESHOLD = 0.8;
54
56
 
@@ -93,17 +95,6 @@ const styles = stylex.create({
93
95
  counterError: {
94
96
  color: colorVars['--color-error'],
95
97
  },
96
- srOnly: {
97
- position: 'absolute',
98
- width: '1px',
99
- height: '1px',
100
- padding: 0,
101
- margin: '-1px',
102
- overflow: 'hidden',
103
- clip: 'rect(0,0,0,0)',
104
- whiteSpace: 'nowrap',
105
- borderWidth: 0,
106
- },
107
98
  statusIcon: {
108
99
  position: 'absolute',
109
100
  top: spacingVars['--spacing-2'],
@@ -199,6 +190,27 @@ export interface TextAreaProps extends Omit<
199
190
  * @default false
200
191
  */
201
192
  isDisabled?: boolean;
193
+ /**
194
+ * Explains why the textarea is disabled. When set together with
195
+ * `isDisabled`, the textarea shows a tooltip with this text on hover and
196
+ * keyboard focus, and stays focusable (via `aria-disabled`) so the reason is
197
+ * discoverable by keyboard and assistive technology. The field cannot be
198
+ * edited (it becomes read-only) while disabled.
199
+ *
200
+ * Use this instead of wrapping a disabled textarea in `Tooltip` — disabled
201
+ * controls don't emit the pointer events an external tooltip needs.
202
+ *
203
+ * @example
204
+ * ```
205
+ * <TextArea
206
+ * label="Notes"
207
+ * value={notes}
208
+ * isDisabled
209
+ * disabledMessage="Notes are locked after submission"
210
+ * />
211
+ * ```
212
+ */
213
+ disabledMessage?: string;
202
214
  /**
203
215
  * Status indicator for the textarea.
204
216
  * When set, displays a colored border and status icon.
@@ -284,6 +296,7 @@ export function TextArea({
284
296
  placeholder,
285
297
  rows = 3,
286
298
  isDisabled = false,
299
+ disabledMessage,
287
300
  status,
288
301
  labelTooltip,
289
302
  startIcon,
@@ -314,6 +327,20 @@ export function TextArea({
314
327
  const [optimisticValue, setOptimisticValue] = useOptimistic(value);
315
328
  const isBusy = isLoading || optimisticValue !== value;
316
329
 
330
+ // Disabled-reason tooltip. Disabled controls swallow pointer events, so the
331
+ // tooltip listeners attach to the textarea container (which already exists)
332
+ // and the textarea stays perceivable via aria-disabled instead of the native
333
+ // disabled attribute. The field is made read-only so it can't be typed into,
334
+ // and value mutation is blocked by the isDisabled guard in handleChange.
335
+ const showsDisabledMessage = isDisabled && !!disabledMessage;
336
+ const disabledMessageTooltip = useTooltip({
337
+ placement: 'above',
338
+ // The container div is not naturally focusable; focusin bubbles up from
339
+ // the textarea, so always attach focus listeners.
340
+ focusTrigger: 'always',
341
+ isEnabled: showsDisabledMessage,
342
+ });
343
+
317
344
  const statusIconMap: Record<TextAreaStatusType, IconName> = {
318
345
  warning: 'warning',
319
346
  error: 'error',
@@ -334,11 +361,18 @@ export function TextArea({
334
361
  description ? descriptionID : null,
335
362
  status?.message ? statusMessageID : null,
336
363
  maxLength != null ? counterID : null,
364
+ showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
337
365
  ]
338
366
  .filter(Boolean)
339
367
  .join(' ') || undefined;
340
368
 
341
369
  const handleChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
370
+ // Value can't change while showing a disabled message (the field is
371
+ // read-only and non-native-disabled), but guard the handler too so the
372
+ // optimistic value and callbacks never fire.
373
+ if (isDisabled) {
374
+ return;
375
+ }
342
376
  const newValue = e.target.value;
343
377
  onChange?.(newValue, e);
344
378
  if (changeAction && !e.defaultPrevented) {
@@ -381,7 +415,13 @@ export function TextArea({
381
415
  labelTooltip={labelTooltip}
382
416
  width={width}>
383
417
  <div
384
- ref={containerRef}
418
+ ref={el => {
419
+ containerRef.current = el;
420
+ // Anchor + hover/focus listeners for the disabled-message tooltip.
421
+ // Handlers are gated internally by isEnabled, and anchor names
422
+ // compose, so attaching unconditionally is safe.
423
+ disabledMessageTooltip.ref(el);
424
+ }}
385
425
  onClick={handleWrapperClick}
386
426
  onMouseUp={handleWrapperMouseUp}
387
427
  {...mergeProps(
@@ -413,7 +453,12 @@ export function TextArea({
413
453
  onBlur={onBlur}
414
454
  placeholder={placeholder}
415
455
  rows={rows}
416
- disabled={isDisabled}
456
+ // With a disabledMessage the textarea keeps focusability via
457
+ // aria-disabled so the reason is focus-discoverable; readOnly + the
458
+ // handleChange guard keep the value from changing.
459
+ disabled={isDisabled && !showsDisabledMessage}
460
+ aria-disabled={showsDisabledMessage ? 'true' : undefined}
461
+ readOnly={showsDisabledMessage || undefined}
417
462
  spellCheck={hasSpellCheck}
418
463
  autoFocus={hasAutoFocus}
419
464
  data-autofocus={hasAutoFocus || undefined}
@@ -451,15 +496,17 @@ export function TextArea({
451
496
  optimisticValue.length > maxLength && styles.counterError,
452
497
  )}>
453
498
  {optimisticValue.length}/{maxLength}
454
- <span aria-live="polite" {...stylex.props(styles.srOnly)}>
499
+ <VisuallyHidden aria-live="polite">
455
500
  {optimisticValue.length >= maxLength * COUNTER_WARNING_THRESHOLD
456
501
  ? optimisticValue.length > maxLength
457
502
  ? `${optimisticValue.length - maxLength} characters over limit`
458
503
  : `${maxLength - optimisticValue.length} characters remaining`
459
504
  : ''}
460
- </span>
505
+ </VisuallyHidden>
461
506
  </div>
462
507
  )}
508
+ {showsDisabledMessage &&
509
+ disabledMessageTooltip.renderTooltip(disabledMessage)}
463
510
  </Field>
464
511
  );
465
512
  }
@@ -78,6 +78,12 @@ export const docs = {
78
78
  'Disables the input, preventing interaction and dimming the element.',
79
79
  default: 'false',
80
80
  },
81
+ {
82
+ name: 'disabledMessage',
83
+ type: 'string',
84
+ description:
85
+ 'Explains why the input is disabled. With isDisabled, shows a tooltip on hover/keyboard focus and keeps the input focusable via aria-disabled (the field becomes read-only). Use this instead of wrapping a disabled TextInput in Tooltip — disabled controls swallow the hover events an external Tooltip needs.',
86
+ },
81
87
  {
82
88
  name: 'isLoading',
83
89
  type: 'boolean',
@@ -144,6 +150,7 @@ export const docs = {
144
150
  {guidance: false, description: "Don't use placeholder text as a replacement for a label; placeholders disappear on focus and are not reliably read by screen readers."},
145
151
  {guidance: false, description: "Don't use TextInput for multi-line content like comments or descriptions; use TextArea instead."},
146
152
  {guidance: false, description: "Don't mark every field as required; only flag mandatory fields so users are not overwhelmed by validation errors."},
153
+ {guidance: false, description: "Don't wrap a disabled TextInput in Tooltip to explain why it's disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead."},
147
154
  ],
148
155
  anatomy: [
149
156
  {name: 'Label', required: true, description: 'Text that identifies the field. Always rendered for accessibility even when visually hidden.'},
@@ -231,6 +238,12 @@ export const docsZh = {
231
238
  '禁用输入框,阻止交互并使元素变暗。',
232
239
  default: 'false',
233
240
  },
241
+ {
242
+ name: 'disabledMessage',
243
+ type: 'string',
244
+ description:
245
+ '说明输入框被禁用的原因。与 isDisabled 一起使用时,在悬停/键盘聚焦时显示工具提示,并通过 aria-disabled 保持输入框可聚焦(字段变为只读)。请使用此属性,而不是用 Tooltip 包裹已禁用的 TextInput——已禁用的控件会吞掉外部 Tooltip 所需的悬停事件。',
246
+ },
234
247
  {
235
248
  name: 'isLoading',
236
249
  type: 'boolean',
@@ -296,6 +309,7 @@ export const docsZh = {
296
309
  {guidance: false, description: "Don't use placeholder text as a replacement for a label; placeholders disappear on focus and are not reliably read by screen readers."},
297
310
  {guidance: false, description: "Don't use TextInput for multi-line content like comments or descriptions; use TextArea instead."},
298
311
  {guidance: false, description: "Don't mark every field as required; only flag mandatory fields so users are not overwhelmed by validation errors."},
312
+ {guidance: false, description: "Don't wrap a disabled TextInput in Tooltip to explain why it's disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead."},
299
313
  ],
300
314
  anatomy: [
301
315
  {name: 'Label', required: true, description: 'Text that identifies the field. Always rendered for accessibility even when visually hidden.'},
@@ -323,6 +337,7 @@ export const docsDense = {
323
337
  {guidance: false, description: "Don't use placeholder text as a replacement for a label; placeholders disappear on focus and are not reliably read by screen readers."},
324
338
  {guidance: false, description: "Don't use TextInput for multi-line content like comments or descriptions; use TextArea instead."},
325
339
  {guidance: false, description: "Don't mark every field as required; only flag mandatory fields so users are not overwhelmed by validation errors."},
340
+ {guidance: false, description: "Don't wrap a disabled TextInput in Tooltip to explain why it's disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead."},
326
341
  ],
327
342
  anatomy: [
328
343
  {name: 'Label', required: true, description: 'Text that identifies the field. Always rendered for accessibility even when visually hidden.'},
@@ -346,6 +361,8 @@ export const docsDense = {
346
361
  isOptional: 'Shows "Optional" indicator. Mutually exclusive w/ isRequired.',
347
362
  isRequired: 'Shows "Required" indicator+sets aria-required. Mutually exclusive w/ isOptional.',
348
363
  isDisabled: 'Disables input, prevents interaction, dims element.',
364
+ disabledMessage:
365
+ 'Explains why input is disabled. With isDisabled, shows tooltip on hover/focus + keeps input focusable via aria-disabled (field becomes read-only). Use instead of wrapping a disabled TextInput in Tooltip.',
349
366
  isLoading: 'Loading state w/ spinner+aria-busy.',
350
367
  placeholder: 'Placeholder when input empty.',
351
368
  labelTooltip: 'Tooltip in info icon at label end.',