@astryxdesign/core 0.3.0 → 0.4.0

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 (551) hide show
  1. package/CHANGELOG.md +156 -0
  2. package/dist/AlertDialog/AlertDialog.d.ts +10 -2
  3. package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
  4. package/dist/AlertDialog/AlertDialog.js +22 -3
  5. package/dist/AppShell/AppShell.d.ts +2 -2
  6. package/dist/AppShell/AppShell.d.ts.map +1 -1
  7. package/dist/AppShell/AppShell.js +40 -9
  8. package/dist/AspectRatio/AspectRatio.d.ts +13 -0
  9. package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
  10. package/dist/AspectRatio/AspectRatio.js +9 -0
  11. package/dist/Avatar/Avatar.d.ts.map +1 -1
  12. package/dist/Avatar/Avatar.js +33 -20
  13. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  14. package/dist/AvatarGroup/AvatarGroupOverflow.js +2 -3
  15. package/dist/Banner/Banner.d.ts.map +1 -1
  16. package/dist/Banner/Banner.js +15 -14
  17. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  18. package/dist/Breadcrumbs/BreadcrumbItem.js +21 -8
  19. package/dist/Breadcrumbs/index.d.ts +2 -1
  20. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  21. package/dist/Breadcrumbs/index.js +1 -0
  22. package/dist/Button/Button.d.ts.map +1 -1
  23. package/dist/Button/Button.js +11 -15
  24. package/dist/Calendar/Calendar.d.ts.map +1 -1
  25. package/dist/Calendar/Calendar.js +2 -1
  26. package/dist/Calendar/styles.d.ts +0 -2
  27. package/dist/Calendar/styles.d.ts.map +1 -1
  28. package/dist/Calendar/styles.js +0 -2
  29. package/dist/Carousel/Carousel.d.ts.map +1 -1
  30. package/dist/Carousel/Carousel.js +8 -12
  31. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  32. package/dist/Chat/ChatDictationButton.js +2 -1
  33. package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
  34. package/dist/Chat/ChatLayoutScrollButton.js +3 -1
  35. package/dist/Chat/ChatSendButton.d.ts.map +1 -1
  36. package/dist/Chat/ChatSendButton.js +4 -3
  37. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  38. package/dist/Chat/ChatToolCalls.js +17 -17
  39. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  40. package/dist/Chat/useTriggerMenu.js +2 -6
  41. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  42. package/dist/CheckboxInput/CheckboxInput.js +36 -125
  43. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  44. package/dist/ClickableCard/ClickableCard.js +3 -11
  45. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  46. package/dist/CodeBlock/CodeBlock.js +84 -67
  47. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  48. package/dist/Collapsible/Collapsible.js +35 -15
  49. package/dist/CommandPalette/CommandPaletteItem.d.ts +1 -1
  50. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  51. package/dist/CommandPalette/CommandPaletteItem.js +6 -3
  52. package/dist/CommandPalette/CommandPaletteList.js +1 -1
  53. package/dist/ComplexSelector/ComplexSelector.d.ts +1 -1
  54. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  55. package/dist/ComplexSelector/ComplexSelector.js +35 -33
  56. package/dist/ContextMenu/ContextMenu.d.ts +2 -2
  57. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  58. package/dist/ContextMenu/ContextMenu.js +1 -1
  59. package/dist/ContextMenu/index.d.ts +2 -1
  60. package/dist/ContextMenu/index.d.ts.map +1 -1
  61. package/dist/ContextMenu/index.js +1 -0
  62. package/dist/DateInput/DateInput.d.ts +9 -2
  63. package/dist/DateInput/DateInput.d.ts.map +1 -1
  64. package/dist/DateInput/DateInput.js +24 -23
  65. package/dist/DateRangeInput/DateRangeInput.d.ts +9 -2
  66. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  67. package/dist/DateRangeInput/DateRangeInput.js +11 -16
  68. package/dist/DateTimeInput/DateTimeInput.d.ts +9 -1
  69. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  70. package/dist/DateTimeInput/DateTimeInput.js +22 -17
  71. package/dist/Dialog/Dialog.d.ts.map +1 -1
  72. package/dist/Dialog/Dialog.js +4 -4
  73. package/dist/DropdownMenu/DropdownMenu.d.ts +33 -8
  74. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  75. package/dist/DropdownMenu/DropdownMenu.js +62 -20
  76. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +8 -11
  77. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  78. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +32 -31
  79. package/dist/DropdownMenu/DropdownMenuDivider.d.ts +25 -0
  80. package/dist/DropdownMenu/DropdownMenuDivider.d.ts.map +1 -0
  81. package/dist/DropdownMenu/DropdownMenuDivider.js +66 -0
  82. package/dist/DropdownMenu/DropdownMenuItem.d.ts +15 -2
  83. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  84. package/dist/DropdownMenu/DropdownMenuItem.js +20 -8
  85. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +5 -5
  86. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  87. package/dist/DropdownMenu/DropdownMenuRadioItem.js +24 -67
  88. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  89. package/dist/DropdownMenu/DropdownMenuSubMenu.js +2 -8
  90. package/dist/DropdownMenu/index.d.ts +2 -1
  91. package/dist/DropdownMenu/index.d.ts.map +1 -1
  92. package/dist/DropdownMenu/index.js +4 -0
  93. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  94. package/dist/DropdownMenu/renderDropdownItems.js +35 -27
  95. package/dist/EmptyState/EmptyState.d.ts.map +1 -1
  96. package/dist/EmptyState/EmptyState.js +9 -5
  97. package/dist/Field/InputClearButton.d.ts +16 -3
  98. package/dist/Field/InputClearButton.d.ts.map +1 -1
  99. package/dist/Field/InputClearButton.js +15 -4
  100. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  101. package/dist/HoverCard/useHoverCard.js +3 -19
  102. package/dist/Indicator/CheckIndicator.d.ts +25 -0
  103. package/dist/Indicator/CheckIndicator.d.ts.map +1 -0
  104. package/dist/Indicator/CheckIndicator.js +155 -0
  105. package/dist/Indicator/CheckboxIndicator.d.ts +22 -0
  106. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -0
  107. package/dist/Indicator/CheckboxIndicator.js +173 -0
  108. package/dist/Indicator/RadioIndicator.d.ts +23 -0
  109. package/dist/Indicator/RadioIndicator.d.ts.map +1 -0
  110. package/dist/Indicator/RadioIndicator.js +137 -0
  111. package/dist/Indicator/index.d.ts +24 -0
  112. package/dist/Indicator/index.d.ts.map +1 -0
  113. package/dist/Indicator/index.js +26 -0
  114. package/dist/Indicator/indicator.markers.stylex.d.ts +16 -0
  115. package/dist/Indicator/indicator.markers.stylex.d.ts.map +1 -0
  116. package/dist/Indicator/indicator.markers.stylex.js +21 -0
  117. package/dist/Indicator/indicatorRegistry.d.ts +71 -0
  118. package/dist/Indicator/indicatorRegistry.d.ts.map +1 -0
  119. package/dist/Indicator/indicatorRegistry.js +93 -0
  120. package/dist/Indicator/types.d.ts +167 -0
  121. package/dist/Indicator/types.d.ts.map +1 -0
  122. package/dist/Indicator/types.js +1 -0
  123. package/dist/Indicator/useIndicator.d.ts +18 -0
  124. package/dist/Indicator/useIndicator.d.ts.map +1 -0
  125. package/dist/Indicator/useIndicator.js +30 -0
  126. package/dist/Item/Item.d.ts.map +1 -1
  127. package/dist/Item/Item.js +4 -8
  128. package/dist/Layer/useLayer.d.ts +14 -0
  129. package/dist/Layer/useLayer.d.ts.map +1 -1
  130. package/dist/Layer/useLayer.js +25 -2
  131. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  132. package/dist/Lightbox/Lightbox.js +56 -53
  133. package/dist/Link/Link.d.ts.map +1 -1
  134. package/dist/Link/Link.js +4 -5
  135. package/dist/MobileNav/MobileNav.js +8 -8
  136. package/dist/MoreMenu/MoreMenu.d.ts +14 -1
  137. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  138. package/dist/MoreMenu/MoreMenu.js +4 -0
  139. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  140. package/dist/MultiSelector/MultiSelector.js +78 -75
  141. package/dist/NumberInput/NumberInput.d.ts +27 -1
  142. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  143. package/dist/NumberInput/NumberInput.js +230 -42
  144. package/dist/Outline/Outline.d.ts.map +1 -1
  145. package/dist/Outline/Outline.js +2 -3
  146. package/dist/Pagination/Pagination.d.ts.map +1 -1
  147. package/dist/Pagination/Pagination.js +61 -63
  148. package/dist/Popover/Popover.d.ts.map +1 -1
  149. package/dist/Popover/Popover.js +6 -8
  150. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  151. package/dist/PowerSearch/PowerSearch.js +1 -1
  152. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  153. package/dist/PowerSearch/PowerSearchEditPopover.js +7 -0
  154. package/dist/ProgressBar/ProgressBar.d.ts +11 -4
  155. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  156. package/dist/ProgressBar/ProgressBar.js +96 -6
  157. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  158. package/dist/RadioList/RadioListItem.js +29 -84
  159. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  160. package/dist/Resizable/ResizeHandle.js +2 -3
  161. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  162. package/dist/SegmentedControl/SegmentedControlItem.js +2 -3
  163. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  164. package/dist/SelectableCard/SelectableCard.js +3 -11
  165. package/dist/Selector/Selector.d.ts +2 -1
  166. package/dist/Selector/Selector.d.ts.map +1 -1
  167. package/dist/Selector/Selector.js +164 -94
  168. package/dist/Selector/hooks.d.ts +18 -6
  169. package/dist/Selector/hooks.d.ts.map +1 -1
  170. package/dist/Selector/hooks.js +57 -40
  171. package/dist/SideNav/SideNav.d.ts.map +1 -1
  172. package/dist/SideNav/SideNav.js +1 -1
  173. package/dist/SideNav/SideNavCollapseButton.d.ts +1 -1
  174. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  175. package/dist/SideNav/SideNavCollapseButton.js +29 -14
  176. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  177. package/dist/SideNav/SideNavHeading.js +56 -20
  178. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  179. package/dist/SideNav/SideNavItem.js +28 -22
  180. package/dist/Stack/stack.stylex.d.ts +3 -3
  181. package/dist/Switch/Switch.js +11 -10
  182. package/dist/TabList/Tab.d.ts.map +1 -1
  183. package/dist/TabList/Tab.js +2 -3
  184. package/dist/TabList/TabMenu.d.ts.map +1 -1
  185. package/dist/TabList/TabMenu.js +41 -19
  186. package/dist/Table/BaseTable.d.ts.map +1 -1
  187. package/dist/Table/BaseTable.js +8 -0
  188. package/dist/Table/index.d.ts +1 -1
  189. package/dist/Table/index.d.ts.map +1 -1
  190. package/dist/Table/index.js +1 -1
  191. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts +1 -1
  192. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  193. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +22 -18
  194. package/dist/Table/plugins/rowExpansion/index.d.ts +1 -1
  195. package/dist/Table/plugins/rowExpansion/index.d.ts.map +1 -1
  196. package/dist/Table/plugins/rowExpansion/index.js +1 -1
  197. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts +41 -76
  198. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  199. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +94 -345
  200. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  201. package/dist/Table/plugins/sortable/useTableSortable.js +23 -4
  202. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  203. package/dist/Table/plugins/tree/useTableTreeData.js +29 -33
  204. package/dist/Table/tableContextMenu.d.ts.map +1 -1
  205. package/dist/Table/tableContextMenu.js +2 -1
  206. package/dist/Table/types.d.ts +11 -0
  207. package/dist/Table/types.d.ts.map +1 -1
  208. package/dist/TextArea/TextArea.d.ts +10 -1
  209. package/dist/TextArea/TextArea.d.ts.map +1 -1
  210. package/dist/TextArea/TextArea.js +16 -6
  211. package/dist/TextInput/TextInput.d.ts +10 -1
  212. package/dist/TextInput/TextInput.d.ts.map +1 -1
  213. package/dist/TextInput/TextInput.js +13 -19
  214. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  215. package/dist/Thumbnail/Thumbnail.js +32 -26
  216. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  217. package/dist/TimeInput/TimeInput.js +7 -15
  218. package/dist/Timestamp/TimestampHoverCard.d.ts.map +1 -1
  219. package/dist/Timestamp/TimestampHoverCard.js +16 -34
  220. package/dist/Token/Token.d.ts.map +1 -1
  221. package/dist/Token/Token.js +20 -11
  222. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  223. package/dist/Tokenizer/Tokenizer.js +4 -2
  224. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  225. package/dist/Tooltip/useTooltip.js +3 -19
  226. package/dist/TopNav/TopNav.d.ts.map +1 -1
  227. package/dist/TopNav/TopNav.js +6 -5
  228. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  229. package/dist/TopNav/TopNavHeading.js +54 -16
  230. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  231. package/dist/TopNav/TopNavItem.js +3 -9
  232. package/dist/TopNav/TopNavMegaMenu.d.ts +7 -0
  233. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  234. package/dist/TopNav/TopNavMegaMenu.js +146 -60
  235. package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
  236. package/dist/TopNav/TopNavMegaMenuItem.js +22 -3
  237. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  238. package/dist/TopNav/TopNavMenu.js +83 -38
  239. package/dist/TreeList/TreeList.d.ts.map +1 -1
  240. package/dist/TreeList/TreeList.js +13 -0
  241. package/dist/TreeList/TreeListBranches.d.ts +1 -1
  242. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  243. package/dist/TreeList/TreeListBranches.js +14 -4
  244. package/dist/TreeList/TreeListItem.d.ts +11 -1
  245. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  246. package/dist/TreeList/TreeListItem.js +57 -30
  247. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  248. package/dist/Typeahead/BaseTypeahead.js +16 -8
  249. package/dist/astryx.css +74 -85
  250. package/dist/astryx.umd.js +56 -56
  251. package/dist/astryx.umd.js.map +4 -4
  252. package/dist/hooks/containerReveal.stylex.d.ts +61 -0
  253. package/dist/hooks/containerReveal.stylex.d.ts.map +1 -0
  254. package/dist/hooks/containerReveal.stylex.js +63 -0
  255. package/dist/hooks/index.d.ts +3 -0
  256. package/dist/hooks/index.d.ts.map +1 -1
  257. package/dist/hooks/index.js +2 -0
  258. package/dist/hooks/useClipboard.d.ts +59 -0
  259. package/dist/hooks/useClipboard.d.ts.map +1 -0
  260. package/dist/hooks/useClipboard.js +106 -0
  261. package/dist/hooks/useContainerReveal.d.ts +14 -8
  262. package/dist/hooks/useContainerReveal.d.ts.map +1 -1
  263. package/dist/hooks/useContainerReveal.js +28 -69
  264. package/dist/hooks/useIndicatorFocusRing.d.ts +28 -0
  265. package/dist/hooks/useIndicatorFocusRing.d.ts.map +1 -0
  266. package/dist/hooks/useIndicatorFocusRing.js +96 -0
  267. package/dist/hooks/useLongPress.d.ts.map +1 -1
  268. package/dist/hooks/useLongPress.js +12 -2
  269. package/dist/hooks/useScrollLock.d.ts.map +1 -1
  270. package/dist/hooks/useScrollLock.js +33 -18
  271. package/dist/hooks/useStreamingText.d.ts +18 -3
  272. package/dist/hooks/useStreamingText.d.ts.map +1 -1
  273. package/dist/hooks/useStreamingText.js +47 -7
  274. package/dist/hooks/useTreeFocus.d.ts.map +1 -1
  275. package/dist/hooks/useTreeFocus.js +9 -3
  276. package/dist/hooks/useTypeahead.d.ts +3 -2
  277. package/dist/hooks/useTypeahead.d.ts.map +1 -1
  278. package/dist/hooks/useTypeahead.js +19 -7
  279. package/dist/index.d.ts +1 -0
  280. package/dist/index.d.ts.map +1 -1
  281. package/dist/index.js +1 -0
  282. package/dist/theme/defineTheme.d.ts +16 -0
  283. package/dist/theme/defineTheme.d.ts.map +1 -1
  284. package/dist/theme/defineTheme.js +8 -0
  285. package/dist/theme/derivedVarRegistry.d.ts +8 -0
  286. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  287. package/dist/theme/derivedVarRegistry.js +14 -13
  288. package/dist/theme/expandColorScale.d.ts +3 -1
  289. package/dist/theme/expandColorScale.d.ts.map +1 -1
  290. package/dist/theme/expandColorScale.js +17 -7
  291. package/dist/theme/expandTypeScale.d.ts.map +1 -1
  292. package/dist/theme/expandTypeScale.js +17 -0
  293. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  294. package/dist/theme/generateThemeRules.js +11 -0
  295. package/dist/theme/index.d.ts +1 -0
  296. package/dist/theme/index.d.ts.map +1 -1
  297. package/dist/utils/focusOutline.stylex.d.ts +107 -0
  298. package/dist/utils/focusOutline.stylex.d.ts.map +1 -0
  299. package/dist/utils/focusOutline.stylex.js +130 -0
  300. package/dist/utils/focusReturn.d.ts +24 -0
  301. package/dist/utils/focusReturn.d.ts.map +1 -0
  302. package/dist/utils/focusReturn.js +29 -0
  303. package/dist/utils/index.d.ts +1 -0
  304. package/dist/utils/index.d.ts.map +1 -1
  305. package/dist/utils/index.js +1 -0
  306. package/dist/utils/mergeRefs.d.ts.map +1 -1
  307. package/dist/utils/mergeRefs.js +7 -5
  308. package/dist/utils/rtlStyles.d.ts +2 -2
  309. package/dist/utils/rtlStyles.js +2 -2
  310. package/dist/utils/themeProps.d.ts +20 -1
  311. package/dist/utils/themeProps.d.ts.map +1 -1
  312. package/dist/utils/themeProps.js +8 -2
  313. package/locales/en.json +12 -0
  314. package/locales/pseudo.json +9 -0
  315. package/package.json +7 -2
  316. package/src/AlertDialog/AlertDialog.doc.mjs +16 -3
  317. package/src/AlertDialog/AlertDialog.test.tsx +208 -1
  318. package/src/AlertDialog/AlertDialog.tsx +21 -3
  319. package/src/AppShell/AppShell.doc.mjs +11 -0
  320. package/src/AppShell/AppShell.test.tsx +139 -0
  321. package/src/AppShell/AppShell.tsx +32 -22
  322. package/src/AspectRatio/AspectRatio.doc.mjs +27 -3
  323. package/src/AspectRatio/AspectRatio.test.tsx +30 -0
  324. package/src/AspectRatio/AspectRatio.tsx +13 -0
  325. package/src/Avatar/Avatar.doc.mjs +2 -13
  326. package/src/Avatar/Avatar.test.tsx +33 -8
  327. package/src/Avatar/Avatar.tsx +52 -50
  328. package/src/AvatarGroup/AvatarGroupOverflow.tsx +2 -9
  329. package/src/Banner/Banner.tsx +9 -6
  330. package/src/Breadcrumbs/BreadcrumbItem.tsx +16 -6
  331. package/src/Breadcrumbs/index.ts +5 -1
  332. package/src/Button/Button.tsx +21 -41
  333. package/src/Calendar/Calendar.tsx +2 -1
  334. package/src/Calendar/styles.ts +0 -8
  335. package/src/Carousel/Carousel.doc.mjs +3 -0
  336. package/src/Carousel/Carousel.tsx +10 -6
  337. package/src/Chat/Chat.doc.mjs +2 -0
  338. package/src/Chat/ChatDictationButton.tsx +7 -1
  339. package/src/Chat/ChatLayoutScrollButton.test.tsx +22 -0
  340. package/src/Chat/ChatLayoutScrollButton.tsx +8 -1
  341. package/src/Chat/ChatSendButton.tsx +2 -3
  342. package/src/Chat/ChatToolCalls.test.tsx +35 -0
  343. package/src/Chat/ChatToolCalls.tsx +36 -16
  344. package/src/Chat/useTriggerMenu.tsx +2 -5
  345. package/src/CheckboxInput/CheckboxInput.doc.mjs +4 -2
  346. package/src/CheckboxInput/CheckboxInput.test.tsx +115 -0
  347. package/src/CheckboxInput/CheckboxInput.tsx +45 -202
  348. package/src/ClickableCard/ClickableCard.tsx +6 -11
  349. package/src/CodeBlock/CodeBlock.doc.mjs +3 -0
  350. package/src/CodeBlock/CodeBlock.test.tsx +91 -0
  351. package/src/CodeBlock/CodeBlock.tsx +57 -80
  352. package/src/Collapsible/Collapsible.tsx +22 -18
  353. package/src/CommandPalette/CommandPalette.test.tsx +29 -0
  354. package/src/CommandPalette/CommandPaletteItem.tsx +7 -4
  355. package/src/CommandPalette/CommandPaletteList.tsx +2 -2
  356. package/src/ComplexSelector/ComplexSelector.doc.mjs +42 -0
  357. package/src/ComplexSelector/ComplexSelector.test.tsx +64 -0
  358. package/src/ComplexSelector/ComplexSelector.tsx +46 -29
  359. package/src/ContextMenu/ContextMenu.doc.mjs +1 -1
  360. package/src/ContextMenu/ContextMenu.test.tsx +41 -0
  361. package/src/ContextMenu/ContextMenu.tsx +3 -3
  362. package/src/ContextMenu/index.ts +5 -1
  363. package/src/DateInput/DateInput.doc.mjs +18 -3
  364. package/src/DateInput/DateInput.test.tsx +93 -11
  365. package/src/DateInput/DateInput.tsx +41 -19
  366. package/src/DateRangeInput/DateRangeInput.doc.mjs +10 -2
  367. package/src/DateRangeInput/DateRangeInput.test.tsx +110 -14
  368. package/src/DateRangeInput/DateRangeInput.tsx +24 -12
  369. package/src/DateTimeInput/DateTimeInput.doc.mjs +16 -2
  370. package/src/DateTimeInput/DateTimeInput.test.tsx +75 -0
  371. package/src/DateTimeInput/DateTimeInput.tsx +35 -8
  372. package/src/Dialog/Dialog.tsx +2 -9
  373. package/src/DropdownMenu/DropdownMenu.doc.mjs +5 -9
  374. package/src/DropdownMenu/DropdownMenu.test.tsx +534 -4
  375. package/src/DropdownMenu/DropdownMenu.tsx +113 -52
  376. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +27 -30
  377. package/src/DropdownMenu/DropdownMenuDivider.doc.mjs +34 -0
  378. package/src/DropdownMenu/DropdownMenuDivider.tsx +81 -0
  379. package/src/DropdownMenu/DropdownMenuItem.doc.mjs +39 -4
  380. package/src/DropdownMenu/DropdownMenuItem.tsx +48 -10
  381. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +30 -76
  382. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +25 -19
  383. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +44 -14
  384. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +1 -5
  385. package/src/DropdownMenu/index.ts +8 -1
  386. package/src/DropdownMenu/renderDropdownItems.tsx +33 -34
  387. package/src/EmptyState/EmptyState.doc.mjs +4 -0
  388. package/src/EmptyState/EmptyState.test.tsx +62 -0
  389. package/src/EmptyState/EmptyState.tsx +16 -6
  390. package/src/Field/Field.doc.mjs +1 -0
  391. package/src/Field/InputClearButton.test.tsx +117 -0
  392. package/src/Field/InputClearButton.tsx +30 -3
  393. package/src/HoverCard/useHoverCard.tsx +2 -23
  394. package/src/Icon/Icon.doc.mjs +2 -1
  395. package/src/Indicator/CheckIndicator.tsx +174 -0
  396. package/src/Indicator/CheckboxIndicator.tsx +257 -0
  397. package/src/Indicator/Indicator.doc.mjs +344 -0
  398. package/src/Indicator/Indicator.test.tsx +393 -0
  399. package/src/Indicator/RadioIndicator.tsx +206 -0
  400. package/src/Indicator/index.ts +45 -0
  401. package/src/Indicator/indicator.markers.stylex.ts +19 -0
  402. package/src/Indicator/indicatorRegistry.test.tsx +106 -0
  403. package/src/Indicator/indicatorRegistry.ts +120 -0
  404. package/src/Indicator/types.ts +186 -0
  405. package/src/Indicator/useIndicator.ts +39 -0
  406. package/src/Item/Item.doc.mjs +1 -1
  407. package/src/Item/Item.tsx +7 -14
  408. package/src/Layer/useLayer.doc.mjs +2 -2
  409. package/src/Layer/useLayer.test.tsx +85 -0
  410. package/src/Layer/useLayer.tsx +39 -1
  411. package/src/Lightbox/Lightbox.doc.mjs +1 -1
  412. package/src/Lightbox/Lightbox.tsx +43 -38
  413. package/src/Link/Link.tsx +4 -11
  414. package/src/MobileNav/MobileNav.tsx +3 -3
  415. package/src/MoreMenu/MoreMenu.doc.mjs +31 -0
  416. package/src/MoreMenu/MoreMenu.test.tsx +68 -1
  417. package/src/MoreMenu/MoreMenu.tsx +19 -0
  418. package/src/MultiSelector/MultiSelector.doc.mjs +9 -1
  419. package/src/MultiSelector/MultiSelector.test.tsx +255 -16
  420. package/src/MultiSelector/MultiSelector.tsx +103 -99
  421. package/src/NumberInput/NumberInput.doc.mjs +265 -50
  422. package/src/NumberInput/NumberInput.test.tsx +726 -21
  423. package/src/NumberInput/NumberInput.tsx +353 -59
  424. package/src/Outline/Outline.tsx +2 -5
  425. package/src/Pagination/Pagination.doc.mjs +1 -1
  426. package/src/Pagination/Pagination.test.tsx +22 -7
  427. package/src/Pagination/Pagination.tsx +38 -35
  428. package/src/Popover/Popover.tsx +6 -7
  429. package/src/PowerSearch/PowerSearch.tsx +1 -1
  430. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +42 -0
  431. package/src/PowerSearch/PowerSearchEditPopover.tsx +7 -0
  432. package/src/ProgressBar/ProgressBar.doc.mjs +27 -5
  433. package/src/ProgressBar/ProgressBar.test.tsx +224 -1
  434. package/src/ProgressBar/ProgressBar.tsx +124 -22
  435. package/src/RadioList/RadioList.doc.mjs +4 -2
  436. package/src/RadioList/RadioListItem.tsx +31 -130
  437. package/src/Resizable/ResizeHandle.tsx +2 -9
  438. package/src/SegmentedControl/SegmentedControlItem.tsx +2 -9
  439. package/src/SelectableCard/SelectableCard.tsx +4 -9
  440. package/src/Selector/Selector.doc.mjs +7 -4
  441. package/src/Selector/Selector.test.tsx +935 -25
  442. package/src/Selector/Selector.tsx +189 -106
  443. package/src/Selector/hooks.ts +77 -50
  444. package/src/SideNav/SideNav.test.tsx +46 -0
  445. package/src/SideNav/SideNav.tsx +1 -0
  446. package/src/SideNav/SideNavCollapseButton.tsx +15 -10
  447. package/src/SideNav/SideNavHeading.tsx +48 -10
  448. package/src/SideNav/SideNavItem.tsx +20 -12
  449. package/src/Stack/Stack.doc.mjs +110 -0
  450. package/src/StatusDot/StatusDot.doc.mjs +12 -3
  451. package/src/Switch/Switch.test.tsx +32 -0
  452. package/src/Switch/Switch.tsx +8 -8
  453. package/src/TabList/Tab.tsx +2 -9
  454. package/src/TabList/TabMenu.tsx +14 -21
  455. package/src/Table/BaseTable.tsx +11 -0
  456. package/src/Table/index.ts +1 -4
  457. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +30 -14
  458. package/src/Table/plugins/rowExpansion/index.ts +1 -4
  459. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +125 -175
  460. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +115 -458
  461. package/src/Table/plugins/sortable/useTableSortable.tsx +2 -6
  462. package/src/Table/plugins/tree/useTableTreeData.tsx +43 -24
  463. package/src/Table/tableContextMenu.test.tsx +27 -0
  464. package/src/Table/tableContextMenu.tsx +1 -0
  465. package/src/Table/types.ts +11 -0
  466. package/src/Table/useTableGroupedRows.doc.mjs +1 -1
  467. package/src/Table/useTableRowExpansion.doc.mjs +43 -66
  468. package/src/TextArea/TextArea.doc.mjs +42 -2
  469. package/src/TextArea/TextArea.test.tsx +219 -0
  470. package/src/TextArea/TextArea.tsx +34 -7
  471. package/src/TextInput/TextInput.doc.mjs +18 -2
  472. package/src/TextInput/TextInput.test.tsx +189 -0
  473. package/src/TextInput/TextInput.tsx +25 -31
  474. package/src/Thumbnail/Thumbnail.tsx +2 -9
  475. package/src/TimeInput/TimeInput.doc.mjs +2 -2
  476. package/src/TimeInput/TimeInput.test.tsx +30 -3
  477. package/src/TimeInput/TimeInput.tsx +9 -26
  478. package/src/Timestamp/Timestamp.doc.mjs +4 -3
  479. package/src/Timestamp/TimestampHoverCard.tsx +13 -35
  480. package/src/Token/Token.tsx +5 -28
  481. package/src/Tokenizer/Tokenizer.doc.mjs +2 -2
  482. package/src/Tokenizer/Tokenizer.test.tsx +45 -3
  483. package/src/Tokenizer/Tokenizer.tsx +10 -2
  484. package/src/Tooltip/useTooltip.tsx +2 -23
  485. package/src/TopNav/TopNav.tsx +1 -3
  486. package/src/TopNav/TopNavHeading.tsx +42 -13
  487. package/src/TopNav/TopNavItem.tsx +3 -21
  488. package/src/TopNav/TopNavMegaMenu.test.tsx +433 -33
  489. package/src/TopNav/TopNavMegaMenu.tsx +118 -53
  490. package/src/TopNav/TopNavMegaMenuItem.tsx +2 -9
  491. package/src/TopNav/TopNavMenu.tsx +34 -37
  492. package/src/TreeList/TreeList.doc.mjs +5 -0
  493. package/src/TreeList/TreeList.test.tsx +206 -7
  494. package/src/TreeList/TreeList.tsx +22 -0
  495. package/src/TreeList/TreeListBranches.tsx +25 -2
  496. package/src/TreeList/TreeListItem.tsx +89 -52
  497. package/src/Typeahead/BaseTypeahead.tsx +19 -6
  498. package/src/Typeahead/Typeahead.doc.mjs +1 -0
  499. package/src/Typeahead/Typeahead.test.tsx +76 -3
  500. package/src/hooks/containerReveal.stylex.ts +102 -0
  501. package/src/hooks/index.ts +4 -0
  502. package/src/hooks/useClipboard.doc.mjs +87 -0
  503. package/src/hooks/useClipboard.test.tsx +151 -0
  504. package/src/hooks/useClipboard.ts +135 -0
  505. package/src/hooks/useContainerReveal.doc.mjs +7 -7
  506. package/src/hooks/useContainerReveal.test.tsx +42 -62
  507. package/src/hooks/useContainerReveal.ts +36 -91
  508. package/src/hooks/useImageMode.test.ts +1 -1
  509. package/src/hooks/useIndicatorFocusRing.tsx +120 -0
  510. package/src/hooks/useLongPress.test.tsx +18 -0
  511. package/src/hooks/useLongPress.ts +12 -2
  512. package/src/hooks/useScrollLock.test.ts +72 -0
  513. package/src/hooks/useScrollLock.ts +51 -19
  514. package/src/hooks/useStreamingText.test.ts +46 -1
  515. package/src/hooks/useStreamingText.ts +49 -7
  516. package/src/hooks/useTreeFocus.test.tsx +50 -0
  517. package/src/hooks/useTreeFocus.ts +14 -3
  518. package/src/hooks/useTypeahead.test.tsx +150 -0
  519. package/src/hooks/useTypeahead.ts +24 -9
  520. package/src/i18n/InternationalizationProvider.doc.mjs +7 -2
  521. package/src/index.ts +1 -0
  522. package/src/theme/defineTheme.ts +24 -0
  523. package/src/theme/derivedVarRegistry.test.ts +21 -1
  524. package/src/theme/derivedVarRegistry.ts +19 -6
  525. package/src/theme/expandColorScale.test.ts +14 -0
  526. package/src/theme/expandColorScale.ts +24 -9
  527. package/src/theme/expandTypeScale.test.ts +43 -0
  528. package/src/theme/expandTypeScale.ts +17 -0
  529. package/src/theme/generateThemeRules.test.ts +61 -20
  530. package/src/theme/generateThemeRules.ts +11 -0
  531. package/src/theme/index.ts +13 -0
  532. package/src/utils/focusOutline.stylex.ts +163 -0
  533. package/src/utils/focusReturn.test.ts +42 -0
  534. package/src/utils/focusReturn.ts +31 -0
  535. package/src/utils/index.ts +1 -0
  536. package/src/utils/mergeRefs.test.ts +48 -0
  537. package/src/utils/mergeRefs.ts +12 -6
  538. package/src/utils/rtlStyles.ts +2 -2
  539. package/src/utils/themeProps.ts +26 -4
  540. package/dist/CheckboxInput/checkbox.markers.stylex.d.ts +0 -9
  541. package/dist/CheckboxInput/checkbox.markers.stylex.d.ts.map +0 -1
  542. package/dist/CheckboxInput/checkbox.markers.stylex.js +0 -14
  543. package/dist/RadioList/radio.markers.stylex.d.ts +0 -7
  544. package/dist/RadioList/radio.markers.stylex.d.ts.map +0 -1
  545. package/dist/RadioList/radio.markers.stylex.js +0 -12
  546. package/dist/hooks/containerReveal.pool.stylex.d.ts +0 -303
  547. package/dist/hooks/containerReveal.pool.stylex.d.ts.map +0 -1
  548. package/dist/hooks/containerReveal.pool.stylex.js +0 -292
  549. package/src/CheckboxInput/checkbox.markers.stylex.ts +0 -12
  550. package/src/RadioList/radio.markers.stylex.ts +0 -10
  551. package/src/hooks/containerReveal.pool.stylex.ts +0 -571
@@ -0,0 +1,393 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import {describe, expect, it} from 'vitest';
4
+ import {render, renderHook, screen} from '@testing-library/react';
5
+ import type {PropsWithChildren, ReactNode} from 'react';
6
+ import {Theme} from '../theme/Theme';
7
+ import {defineTheme} from '../theme/defineTheme';
8
+ import {CheckboxIndicator} from './CheckboxIndicator';
9
+ import {CheckIndicator} from './CheckIndicator';
10
+ import {RadioIndicator} from './RadioIndicator';
11
+ import {getIndicator} from './indicatorRegistry';
12
+ import {useIndicator} from './useIndicator';
13
+ import type {IndicatorProps} from './types';
14
+
15
+ function createThemeWrapper(theme: ReturnType<typeof defineTheme>) {
16
+ function ThemeWrapper({children}: PropsWithChildren): ReactNode {
17
+ return <Theme theme={theme}>{children}</Theme>;
18
+ }
19
+ return ThemeWrapper;
20
+ }
21
+
22
+ describe('default indicators', () => {
23
+ it('renders the checkbox theme target with state and size', () => {
24
+ render(<CheckboxIndicator state="indeterminate" size="sm" />);
25
+
26
+ const box = document.querySelector('.astryx-checkbox');
27
+ expect(box).toBeInTheDocument();
28
+ expect(box).toHaveAttribute('data-checked', 'indeterminate');
29
+ expect(box).toHaveAttribute('data-size', 'sm');
30
+ // Decorative: the owning control keeps the role and accessible name.
31
+ expect(box).toHaveAttribute('aria-hidden', 'true');
32
+ });
33
+
34
+ it('renders the radio dot only when checked, in both states', () => {
35
+ const {rerender} = render(<RadioIndicator state="unchecked" />);
36
+
37
+ // The circle draws in the unchecked state — this is what lets a radio act
38
+ // as a selection indicator where an icon would render nothing.
39
+ expect(document.querySelector('.astryx-radio')).toBeInTheDocument();
40
+ expect(document.querySelector('.astryx-radio-dot')).not.toBeInTheDocument();
41
+
42
+ rerender(<RadioIndicator state="checked" />);
43
+ expect(document.querySelector('.astryx-radio-dot')).toBeInTheDocument();
44
+ expect(document.querySelector('.astryx-radio')).toHaveAttribute(
45
+ 'data-checked',
46
+ 'checked',
47
+ );
48
+ });
49
+
50
+ it('renders children instead of the state mark', () => {
51
+ render(
52
+ <CheckboxIndicator state="checked">
53
+ <span data-testid="busy" />
54
+ </CheckboxIndicator>,
55
+ );
56
+
57
+ expect(screen.getByTestId('busy')).toBeInTheDocument();
58
+ });
59
+
60
+ it('reflects the disabled state for theme targeting', () => {
61
+ render(<RadioIndicator state="checked" isDisabled />);
62
+
63
+ expect(document.querySelector('.astryx-radio')).toHaveAttribute(
64
+ 'data-disabled',
65
+ 'disabled',
66
+ );
67
+ });
68
+ });
69
+
70
+ /**
71
+ * CheckIndicator is the one indicator that draws nothing in a state, which is
72
+ * what makes it the default selection mark — and what made its two escape
73
+ * hatches (props and children) easy to get wrong. Both are pinned here.
74
+ */
75
+ describe('CheckIndicator', () => {
76
+ it('draws the mark when checked and nothing when not', () => {
77
+ const {container, rerender} = render(<CheckIndicator state="checked" />);
78
+
79
+ const mark = container.querySelector('.astryx-icon');
80
+ expect(mark).toBeInTheDocument();
81
+ expect(mark).toHaveAttribute('aria-hidden', 'true');
82
+
83
+ rerender(<CheckIndicator state="unchecked" />);
84
+ // No empty box beside an unchosen row — the reason a check is the default.
85
+ expect(container).toBeEmptyDOMElement();
86
+ });
87
+
88
+ it('renders children INSTEAD of the mark, in both states', () => {
89
+ // A host shows a pending Spinner through `children` in whatever state the
90
+ // row is in, and an unchosen row is the common one. The unchecked case
91
+ // used to render nothing at all.
92
+ for (const state of ['checked', 'unchecked'] as const) {
93
+ const {unmount} = render(
94
+ <CheckIndicator state={state}>
95
+ <span data-testid="busy" />
96
+ </CheckIndicator>,
97
+ );
98
+
99
+ expect(screen.getByTestId('busy'), `state=${state}`).toBeInTheDocument();
100
+ // The mark is replaced, not accompanied.
101
+ expect(document.querySelector('.astryx-icon')).not.toBeInTheDocument();
102
+ unmount();
103
+ }
104
+ });
105
+
106
+ it('forwards caller props and styling on both render paths', () => {
107
+ // Same contract whether or not children are present: a data-testid, an id
108
+ // and an xstyle-driven class must survive either branch.
109
+ const {container, rerender} = render(
110
+ <CheckIndicator state="checked" data-testid="mark" id="pinned" />,
111
+ );
112
+
113
+ expect(screen.getByTestId('mark')).toHaveAttribute('id', 'pinned');
114
+
115
+ rerender(
116
+ <CheckIndicator
117
+ state="checked"
118
+ data-testid="mark"
119
+ id="pinned"
120
+ className="host-target">
121
+ <span data-testid="busy" />
122
+ </CheckIndicator>,
123
+ );
124
+
125
+ const withChildren = screen.getByTestId('mark');
126
+ expect(withChildren).toHaveAttribute('id', 'pinned');
127
+ expect(withChildren).toHaveClass('host-target');
128
+ expect(withChildren).toHaveAttribute('aria-hidden', 'true');
129
+ expect(container.querySelector('[data-testid="busy"]')).toBeInTheDocument();
130
+ });
131
+ });
132
+
133
+ /**
134
+ * The busy idiom a host actually writes is `children={isBusy && <Spinner/>}`,
135
+ * which passes `false` when it is not busy. `false` is non-null and is not
136
+ * caught by `??`, so both a `!= null` guard and a `children ?? mark` fallback
137
+ * take the children path, render nothing in it, and delete the state mark
138
+ * (#4893). Every indicator gets the same case, because all three had the bug.
139
+ */
140
+ describe('falsy children never suppress the state mark (#4893)', () => {
141
+ const cases = [
142
+ {
143
+ name: 'CheckIndicator',
144
+ render: (children: ReactNode) =>
145
+ render(<CheckIndicator state="checked">{children}</CheckIndicator>),
146
+ markSelector: '.astryx-icon',
147
+ },
148
+ {
149
+ name: 'CheckboxIndicator',
150
+ render: (children: ReactNode) =>
151
+ render(
152
+ <CheckboxIndicator state="checked">{children}</CheckboxIndicator>,
153
+ ),
154
+ markSelector: 'svg',
155
+ },
156
+ {
157
+ name: 'RadioIndicator',
158
+ render: (children: ReactNode) =>
159
+ render(<RadioIndicator state="checked">{children}</RadioIndicator>),
160
+ markSelector: '.astryx-radio-indicator-dot',
161
+ },
162
+ ] as const;
163
+
164
+ // Everything React renders as nothing. `0` is deliberately absent: it
165
+ // renders the visible text "0", so it IS content and must replace the mark.
166
+ const emptyValues = [
167
+ ['false — the `isBusy && …` idiom', false],
168
+ ['null', null],
169
+ ['undefined', undefined],
170
+ ['empty string', ''],
171
+ ] as const;
172
+
173
+ for (const {name, render: renderCase, markSelector} of cases) {
174
+ for (const [label, child] of emptyValues) {
175
+ it(`${name} keeps its mark when children is ${label}`, () => {
176
+ const {container} = renderCase(child);
177
+
178
+ expect(
179
+ container.querySelector(markSelector),
180
+ `${name} lost its mark to a falsy child`,
181
+ ).toBeInTheDocument();
182
+ });
183
+ }
184
+
185
+ it(`${name} still lets real children replace the mark`, () => {
186
+ // The negative control: without this, "always render the mark" would
187
+ // pass every case above and break the busy state instead.
188
+ const {container} = renderCase(<span data-testid="busy" />);
189
+
190
+ expect(
191
+ container.querySelector('[data-testid="busy"]'),
192
+ ).toBeInTheDocument();
193
+ expect(container.querySelector(markSelector)).not.toBeInTheDocument();
194
+ });
195
+ }
196
+
197
+ it('treats 0 as content, not as empty', () => {
198
+ // isRenderable's documented edge: 0 renders the character "0".
199
+ const {container} = render(
200
+ <CheckIndicator state="checked">{0}</CheckIndicator>,
201
+ );
202
+
203
+ expect(container.textContent).toBe('0');
204
+ expect(container.querySelector('.astryx-icon')).not.toBeInTheDocument();
205
+ });
206
+ });
207
+
208
+ /**
209
+ * An indicator is decorative BY CONTRACT: the owning control supplies the role
210
+ * and the accessible name, so an indicator that is also announced says the same
211
+ * thing twice (#4918).
212
+ *
213
+ * The contract is held in two places, because neither covers the whole thing:
214
+ *
215
+ * - `IndicatorProps` omits the a11y props, which makes `role` a compile
216
+ * error. It does NOT make `aria-hidden` one: TypeScript exempts JSX
217
+ * attribute names that are not valid JS identifiers from excess-property
218
+ * checking, so anything hyphenated slips through. That is a language rule,
219
+ * not a gap in our types — the second test below is the proof, and it will
220
+ * start failing the day TS changes its mind, which is when we can drop the
221
+ * ordering.
222
+ * - So each component emits its own `aria-hidden` AFTER `{...rest}`, which is
223
+ * what actually keeps a caller from un-hiding it. Nothing is stripped.
224
+ */
225
+ describe('the decorative contract (#4918)', () => {
226
+ it('rejects `role` and `tabIndex` at compile time', () => {
227
+ // Both are valid JS identifiers, so excess-property checking applies and
228
+ // the type alone is enough — no runtime guard needed. `tabIndex` matters
229
+ // because the element is unconditionally aria-hidden: a tab stop on it is
230
+ // a focusable node in a hidden subtree (axe `aria-hidden-focus`).
231
+ const rejected = [
232
+ // @ts-expect-error — the owning control holds the role.
233
+ <CheckIndicator key="a" state="checked" role="checkbox" />,
234
+ // @ts-expect-error — the owning control holds the focus.
235
+ <CheckboxIndicator key="b" state="checked" tabIndex={0} />,
236
+ ];
237
+
238
+ expect(rejected).toHaveLength(2);
239
+ });
240
+
241
+ it('cannot reject a hyphenated a11y attribute — TS exempts those', () => {
242
+ // NO @ts-expect-error here, deliberately: this compiles, and the comment
243
+ // above explains why. Runtime order is what makes it harmless.
244
+ const accepted = <CheckIndicator state="checked" aria-label="inert" />;
245
+
246
+ expect(accepted).toBeTruthy();
247
+ });
248
+
249
+ it('cannot reject a SPREAD either — which is the ordinary host idiom', () => {
250
+ // Also no @ts-expect-error, also deliberate. A spread bypasses
251
+ // excess-property checking for every member, so even `role` and `tabIndex`
252
+ // — the two the type catches as literals — get through this way.
253
+ //
254
+ // Left alone on purpose. Nothing in the repo spreads a hostile object at an
255
+ // indicator, `aria-hidden` is settled by attribute order regardless, and a
256
+ // forwarded `role`/`aria-label` is inert inside a hidden subtree. Guarding
257
+ // it at runtime would cost a module to defend a case no call site reaches.
258
+ const hostile = {role: 'checkbox', tabIndex: 0};
259
+ const accepted = <CheckIndicator state="checked" {...hostile} />;
260
+
261
+ expect(accepted).toBeTruthy();
262
+ });
263
+
264
+ const cases = [
265
+ {
266
+ name: 'CheckIndicator (glyph path)',
267
+ render: (p: Record<string, unknown>) => (
268
+ <CheckIndicator state="checked" {...p} />
269
+ ),
270
+ },
271
+ {
272
+ name: 'CheckIndicator (children path)',
273
+ render: (p: Record<string, unknown>) => (
274
+ <CheckIndicator state="checked" {...p}>
275
+ <b />
276
+ </CheckIndicator>
277
+ ),
278
+ },
279
+ {
280
+ name: 'CheckboxIndicator',
281
+ render: (p: Record<string, unknown>) => (
282
+ <CheckboxIndicator state="checked" {...p} />
283
+ ),
284
+ },
285
+ {
286
+ name: 'RadioIndicator',
287
+ render: (p: Record<string, unknown>) => (
288
+ <RadioIndicator state="checked" {...p} />
289
+ ),
290
+ },
291
+ ] as const;
292
+
293
+ for (const {name, render: renderCase} of cases) {
294
+ it(`${name} stays aria-hidden even when a caller passes false`, () => {
295
+ const {container} = render(renderCase({'aria-hidden': 'false'}));
296
+
297
+ expect(container.firstElementChild).toHaveAttribute(
298
+ 'aria-hidden',
299
+ 'true',
300
+ );
301
+ });
302
+
303
+ it(`${name} still forwards ordinary props`, () => {
304
+ // The negative control: order must not turn into "drop everything".
305
+ const {container} = render(
306
+ renderCase({'data-testid': 'ind', id: 'pinned', dir: 'rtl'}),
307
+ );
308
+ const el = container.firstElementChild;
309
+
310
+ expect(el).toHaveAttribute('data-testid', 'ind');
311
+ expect(el).toHaveAttribute('id', 'pinned');
312
+ expect(el).toHaveAttribute('dir', 'rtl');
313
+ });
314
+ }
315
+ });
316
+
317
+ describe('useIndicator', () => {
318
+ it('returns the built-in indicator without a theme override', () => {
319
+ const {result} = renderHook(() => useIndicator('checkbox'));
320
+
321
+ expect(result.current).toBe(CheckboxIndicator);
322
+ });
323
+
324
+ it('resolves an indicator component from the nearest theme', () => {
325
+ function BrandCheckbox({state}: IndicatorProps) {
326
+ return <span data-testid="brand">{state}</span>;
327
+ }
328
+ const theme = defineTheme({
329
+ name: 'brand-indicators',
330
+ indicators: {checkbox: BrandCheckbox},
331
+ });
332
+
333
+ const {result} = renderHook(() => useIndicator('checkbox'), {
334
+ wrapper: createThemeWrapper(theme),
335
+ });
336
+
337
+ expect(result.current).toBe(BrandCheckbox);
338
+ // Unmapped indicators keep the built-in.
339
+ expect(getIndicator('radio', theme)).toBe(RadioIndicator);
340
+ });
341
+ });
342
+
343
+ /**
344
+ * A theme target is public API. Renaming one to follow the
345
+ * `<component>-kebab` convention (`checkbox` → `checkbox-indicator`) would
346
+ * silently break every theme styling the old name — the CSS still compiles, it
347
+ * just stops matching. So both names are emitted for a deprecation window, and
348
+ * these tests pin that promise from both ends: the new name exists, and the
349
+ * old one has not quietly disappeared.
350
+ */
351
+ describe('renamed theme targets stay non-breaking', () => {
352
+ const cases = [
353
+ {
354
+ name: 'CheckboxIndicator',
355
+ render: () => <CheckboxIndicator state="checked" />,
356
+ current: 'astryx-checkbox-indicator',
357
+ legacy: 'astryx-checkbox',
358
+ },
359
+ {
360
+ name: 'RadioIndicator',
361
+ render: () => <RadioIndicator state="checked" />,
362
+ current: 'astryx-radio-indicator',
363
+ legacy: 'astryx-radio',
364
+ },
365
+ ] as const;
366
+
367
+ for (const {name, render: renderCase, current, legacy} of cases) {
368
+ it(`${name} emits both the current and the legacy target`, () => {
369
+ const {container} = render(renderCase());
370
+ const el = container.querySelector(`.${current}`);
371
+ expect(el, `${name} should render ${current}`).toBeInTheDocument();
372
+ expect(el, `${name} must keep emitting ${legacy}`).toHaveClass(legacy);
373
+ });
374
+ }
375
+
376
+ it('keeps the legacy dot target on the radio mark', () => {
377
+ const {container} = render(<RadioIndicator state="checked" />);
378
+ const dot = container.querySelector('.astryx-radio-indicator-dot');
379
+ expect(dot).toBeInTheDocument();
380
+ expect(dot).toHaveClass('astryx-radio-dot');
381
+ });
382
+
383
+ it('puts both names on ONE element, so either selector wins equally', () => {
384
+ // If the legacy class were moved to a wrapper instead, an old theme's
385
+ // rules would land on a different box than a new theme's — same-element
386
+ // is what makes the two names interchangeable.
387
+ const {container} = render(<CheckboxIndicator state="unchecked" />);
388
+ expect(container.querySelectorAll('.astryx-checkbox')).toHaveLength(1);
389
+ expect(container.querySelector('.astryx-checkbox')).toBe(
390
+ container.querySelector('.astryx-checkbox-indicator'),
391
+ );
392
+ });
393
+ });
@@ -0,0 +1,206 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file RadioIndicator.tsx
5
+ * @input Indicator state props
6
+ * @output Exports RadioIndicator — the default radio selection visual
7
+ * @position Decorative radio visual shared by RadioList, decorative menu
8
+ * markers, and any selection slot themed to use a radio
9
+ */
10
+
11
+ import * as stylex from '@stylexjs/stylex';
12
+ import {
13
+ borderVars,
14
+ colorVars,
15
+ durationVars,
16
+ easeVars,
17
+ radiusVars,
18
+ } from '../theme/tokens.stylex';
19
+ import {isRenderable, mergeProps, themeProps} from '../utils';
20
+ import {indicatorScope} from './indicator.markers.stylex';
21
+ import type {IndicatorProps} from './types';
22
+
23
+ const styles = stylex.create({
24
+ circle: {
25
+ boxSizing: 'border-box',
26
+ display: 'flex',
27
+ alignItems: 'center',
28
+ justifyContent: 'center',
29
+ flexShrink: 0,
30
+ borderWidth: borderVars['--border-width'],
31
+ borderStyle: 'solid',
32
+ // A circle, in tokens: --radius-full is the house "fully rounded" value
33
+ // (9999px) and renders pixel-identical to 50% on a square box, which the
34
+ // size styles below guarantee. Themeable through --radius-full, unlike a
35
+ // raw 50%.
36
+ borderRadius: radiusVars['--radius-full'],
37
+ transitionProperty: 'background-color, border-color',
38
+ transitionDuration: {
39
+ default: durationVars['--duration-fast'],
40
+ '@media (prefers-reduced-motion: reduce)': '0s',
41
+ },
42
+ transitionTimingFunction: easeVars['--ease-standard'],
43
+ },
44
+ unchecked: {
45
+ borderColor: {
46
+ default: colorVars['--color-border-emphasized'],
47
+ [stylex.when.ancestor(':hover', indicatorScope)]: {
48
+ '@media (hover: hover)': `color-mix(in srgb, ${colorVars['--color-border-emphasized']}, ${colorVars['--color-tint-hover']} 20%)`,
49
+ },
50
+ },
51
+ backgroundColor: {
52
+ default: colorVars['--color-background-surface'],
53
+ [stylex.when.ancestor(':hover', indicatorScope)]: {
54
+ '@media (hover: hover)': `color-mix(in srgb, ${colorVars['--color-background-surface']}, ${colorVars['--color-tint-hover']} 5%)`,
55
+ },
56
+ },
57
+ },
58
+ checked: {
59
+ borderColor: {
60
+ default: colorVars['--color-accent'],
61
+ [stylex.when.ancestor(':hover', indicatorScope)]: {
62
+ '@media (hover: hover)': `color-mix(in srgb, ${colorVars['--color-accent']}, ${colorVars['--color-tint-hover']} 15%)`,
63
+ },
64
+ },
65
+ backgroundColor: {
66
+ default: colorVars['--color-accent'],
67
+ [stylex.when.ancestor(':hover', indicatorScope)]: {
68
+ '@media (hover: hover)': `color-mix(in srgb, ${colorVars['--color-accent']}, ${colorVars['--color-tint-hover']} 15%)`,
69
+ },
70
+ },
71
+ },
72
+ disabled: {
73
+ opacity: 0.5,
74
+ borderColor: {
75
+ default: colorVars['--color-border'],
76
+ [stylex.when.ancestor(':hover', indicatorScope)]: {
77
+ '@media (hover: hover)': colorVars['--color-border'],
78
+ },
79
+ },
80
+ },
81
+ disabledUnchecked: {
82
+ backgroundColor: {
83
+ default: colorVars['--color-background-muted'],
84
+ [stylex.when.ancestor(':hover', indicatorScope)]: {
85
+ '@media (hover: hover)': colorVars['--color-background-muted'],
86
+ },
87
+ },
88
+ },
89
+ dot: {
90
+ borderRadius: radiusVars['--radius-full'],
91
+ backgroundColor: {
92
+ default: colorVars['--color-on-accent'],
93
+ // Forced colors (Windows High Contrast) strips painted backgrounds,
94
+ // which would make the selected dot invisible — checked and unchecked
95
+ // radios would look identical. CanvasText keeps the dot perceivable on
96
+ // the Canvas circle fill (WCAG 1.4.11).
97
+ '@media (forced-colors: active)': 'CanvasText',
98
+ },
99
+ },
100
+ });
101
+
102
+ const circleSizeStyles = stylex.create({
103
+ sm: {
104
+ width: 20,
105
+ height: 20,
106
+ },
107
+ md: {
108
+ width: 24,
109
+ height: 24,
110
+ },
111
+ });
112
+
113
+ const dotSizeStyles = stylex.create({
114
+ sm: {
115
+ width: 8,
116
+ height: 8,
117
+ },
118
+ md: {
119
+ width: 10,
120
+ height: 10,
121
+ },
122
+ });
123
+
124
+ /**
125
+ * The default radio visual: a circle with a filled inner dot when selected.
126
+ *
127
+ * Decorative and non-interactive — it renders `aria-hidden` and owns no input,
128
+ * role, or focus behavior. Themes replace it wholesale through
129
+ * `defineTheme({indicators: {radio: MyRadio}})`, or restyle it through the
130
+ * `radio` / `radio-dot` theme targets like any other component.
131
+ *
132
+ * Unlike an icon, a radio draws in *both* states — an empty circle when
133
+ * unchecked. That is what makes it usable as a selection indicator in
134
+ * components whose default is "a checkmark when selected, nothing otherwise".
135
+ *
136
+ * @example
137
+ * ```
138
+ * <RadioIndicator state="checked" size="md" />
139
+ * ```
140
+ */
141
+ export function RadioIndicator({
142
+ state,
143
+ size = 'md',
144
+ isDisabled = false,
145
+ children,
146
+ ref,
147
+ className,
148
+ style,
149
+ xstyle,
150
+ ...rest
151
+ }: IndicatorProps<'singleSelection'>) {
152
+ // A radio has no partial state; anything other than unchecked reads as
153
+ // selected.
154
+ const isChecked = state !== 'unchecked';
155
+
156
+ return (
157
+ <span
158
+ // `{...rest}` first, own contract after. TypeScript cannot reject a
159
+ // hyphenated JSX attribute (see IndicatorProps), so attribute order is
160
+ // what actually keeps a caller from un-hiding a decorative element —
161
+ // rubric P3, "owned aria-* set after {...rest}".
162
+ {...rest}
163
+ ref={ref}
164
+ aria-hidden="true"
165
+ {...mergeProps(
166
+ themeProps(
167
+ 'radio-indicator',
168
+ {
169
+ size,
170
+ checked: isChecked ? 'checked' : null,
171
+ disabled: isDisabled ? 'disabled' : null,
172
+ },
173
+ // `radio` was the target before indicators existed; themes styling
174
+ // it keep working until the next major.
175
+ {legacyNames: ['radio']},
176
+ ),
177
+ stylex.props(
178
+ styles.circle,
179
+ circleSizeStyles[size],
180
+ isChecked ? styles.checked : styles.unchecked,
181
+ isDisabled && styles.disabled,
182
+ isDisabled && !isChecked && styles.disabledUnchecked,
183
+ xstyle,
184
+ ),
185
+ className,
186
+ style,
187
+ )}>
188
+ {isRenderable(children)
189
+ ? children
190
+ : isChecked && (
191
+ <span
192
+ {...mergeProps(
193
+ themeProps(
194
+ 'radio-indicator-dot',
195
+ {size},
196
+ {legacyNames: ['radio-dot']},
197
+ ),
198
+ stylex.props(styles.dot, dotSizeStyles[size]),
199
+ )}
200
+ />
201
+ )}
202
+ </span>
203
+ );
204
+ }
205
+
206
+ RadioIndicator.displayName = 'RadioIndicator';
@@ -0,0 +1,45 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file index.ts
5
+ * @input None (re-export barrel)
6
+ * @output Public indicator surface: components, registry, hook, types
7
+ * @position Entry point for the stateful control-visual layer
8
+ *
9
+ * Indicators are the componentized form of stateful control visuals — the
10
+ * checkbox box, the radio circle, the mark on a chosen option. They are
11
+ * decorative: the owning component keeps the role, state, and focus behavior,
12
+ * while the indicator turns state into a picture.
13
+ *
14
+ * That makes them themeable through CSS targets like any other component, and
15
+ * replaceable wholesale by name through `defineTheme({indicators})` — replace
16
+ * `check` and every single-selection mark in the app follows.
17
+ */
18
+
19
+ export {CheckboxIndicator} from './CheckboxIndicator';
20
+ export {CheckIndicator} from './CheckIndicator';
21
+ export {RadioIndicator} from './RadioIndicator';
22
+
23
+ // Registry (RSC-compatible, no 'use client')
24
+ export {defaultIndicators, getIndicator} from './indicatorRegistry';
25
+ export type {
26
+ CoreIndicatorName,
27
+ IndicatorRegistrySource,
28
+ } from './indicatorRegistry';
29
+
30
+ export {useIndicator} from './useIndicator';
31
+
32
+ export {indicatorScope} from './indicator.markers.stylex';
33
+
34
+ export type {
35
+ IndicatorComponent,
36
+ IndicatorFamily,
37
+ IndicatorFamilyMap,
38
+ IndicatorMap,
39
+ IndicatorName,
40
+ IndicatorNameOfFamily,
41
+ IndicatorProps,
42
+ IndicatorRegistry,
43
+ IndicatorSize,
44
+ IndicatorState,
45
+ } from './types';
@@ -0,0 +1,19 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import * as stylex from '@stylexjs/stylex';
4
+
5
+ /**
6
+ * Scoped marker for indicator ancestor selectors.
7
+ *
8
+ * An owner (CheckboxInput's row, RadioListItem's row) applies this marker to
9
+ * the element whose hover and focus should drive the indicator's appearance.
10
+ * The indicator reads it through `stylex.when.ancestor()`, so interaction
11
+ * state stays in CSS instead of being threaded through React props — and
12
+ * owners that should *not* tint their indicator on hover (decorative menu
13
+ * markers, listbox options) simply don't apply the marker.
14
+ *
15
+ * Owners apply it only while enabled, so disabled controls get no hover
16
+ * feedback.
17
+ */
18
+ export const indicatorScope: ReturnType<typeof stylex.defineMarker> =
19
+ stylex.defineMarker();