@astryxdesign/core 0.1.9 → 0.2.0-canary.136a00f

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 (560) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/dist/Avatar/Avatar.d.ts +42 -3
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +190 -59
  5. package/dist/Avatar/AvatarStatusDot.d.ts +6 -4
  6. package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
  7. package/dist/AvatarGroup/AvatarGroup.d.ts +7 -1
  8. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  9. package/dist/AvatarGroup/AvatarGroup.js +58 -6
  10. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
  11. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  12. package/dist/AvatarGroup/AvatarGroupOverflow.js +7 -2
  13. package/dist/Banner/Banner.js +2 -2
  14. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  15. package/dist/Breadcrumbs/BreadcrumbItem.js +11 -4
  16. package/dist/Breadcrumbs/index.d.ts +1 -0
  17. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  18. package/dist/Breadcrumbs/index.js +2 -1
  19. package/dist/Button/Button.d.ts +1 -1
  20. package/dist/Button/Button.d.ts.map +1 -1
  21. package/dist/Button/Button.js +6 -4
  22. package/dist/Calendar/Calendar.d.ts.map +1 -1
  23. package/dist/Calendar/Calendar.js +50 -9
  24. package/dist/Calendar/dayCellUtils.d.ts +4 -4
  25. package/dist/Calendar/dayCellUtils.js +2 -2
  26. package/dist/Calendar/styles.d.ts +30 -31
  27. package/dist/Calendar/styles.d.ts.map +1 -1
  28. package/dist/Calendar/styles.js +30 -31
  29. package/dist/Card/Card.js +1 -1
  30. package/dist/Carousel/Carousel.d.ts.map +1 -1
  31. package/dist/Carousel/Carousel.js +22 -12
  32. package/dist/Chat/ChatComposerInput.js +1 -1
  33. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  34. package/dist/Chat/ChatDictationButton.js +3 -1
  35. package/dist/Chat/ChatLayout.js +4 -4
  36. package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
  37. package/dist/Chat/ChatMessageBubble.js +8 -8
  38. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  39. package/dist/Chat/ChatToolCalls.js +10 -3
  40. package/dist/Chat/useTriggerMenu.js +2 -2
  41. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  42. package/dist/CheckboxInput/CheckboxInput.js +6 -6
  43. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  44. package/dist/CheckboxList/CheckboxListItem.js +19 -4
  45. package/dist/Citation/Citation.d.ts +22 -1
  46. package/dist/Citation/Citation.d.ts.map +1 -1
  47. package/dist/Citation/Citation.js +20 -6
  48. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  49. package/dist/CodeBlock/CodeBlock.js +16 -10
  50. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  51. package/dist/Collapsible/Collapsible.js +3 -1
  52. package/dist/CommandPalette/CommandPalette.d.ts +1 -1
  53. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  54. package/dist/CommandPalette/CommandPalette.js +42 -3
  55. package/dist/CommandPalette/CommandPaletteItem.js +1 -1
  56. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  57. package/dist/ContextMenu/ContextMenu.js +23 -5
  58. package/dist/ContextMenu/index.d.ts +1 -0
  59. package/dist/ContextMenu/index.d.ts.map +1 -1
  60. package/dist/ContextMenu/index.js +3 -1
  61. package/dist/DateInput/DateInput.d.ts +10 -2
  62. package/dist/DateInput/DateInput.d.ts.map +1 -1
  63. package/dist/DateInput/DateInput.js +33 -19
  64. package/dist/DateRangeInput/DateRangeInput.d.ts +10 -2
  65. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  66. package/dist/DateRangeInput/DateRangeInput.js +23 -20
  67. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  68. package/dist/DateTimeInput/DateTimeInput.js +23 -18
  69. package/dist/Divider/Divider.d.ts +2 -2
  70. package/dist/Divider/Divider.d.ts.map +1 -1
  71. package/dist/Divider/Divider.js +13 -0
  72. package/dist/DropdownMenu/DropdownMenu.d.ts +6 -0
  73. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  74. package/dist/DropdownMenu/DropdownMenu.js +22 -5
  75. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  76. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +4 -1
  77. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  78. package/dist/DropdownMenu/DropdownMenuItem.js +5 -2
  79. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +8 -11
  80. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
  81. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +3 -1
  82. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +1 -1
  83. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  84. package/dist/DropdownMenu/DropdownMenuRadioItem.js +5 -2
  85. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +104 -0
  86. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -0
  87. package/dist/DropdownMenu/DropdownMenuSubMenu.js +427 -0
  88. package/dist/DropdownMenu/index.d.ts +10 -0
  89. package/dist/DropdownMenu/index.d.ts.map +1 -1
  90. package/dist/DropdownMenu/index.js +13 -0
  91. package/dist/DropdownMenu/menuItemHover.d.ts +23 -0
  92. package/dist/DropdownMenu/menuItemHover.d.ts.map +1 -0
  93. package/dist/DropdownMenu/menuItemHover.js +33 -0
  94. package/dist/DropdownMenu/menuItemRoles.d.ts +8 -0
  95. package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -1
  96. package/dist/DropdownMenu/menuItemRoles.js +10 -1
  97. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  98. package/dist/DropdownMenu/renderDropdownItems.js +21 -6
  99. package/dist/Field/Field.d.ts +3 -1
  100. package/dist/Field/Field.d.ts.map +1 -1
  101. package/dist/Field/Field.js +4 -1
  102. package/dist/Field/FieldLabel.d.ts +2 -1
  103. package/dist/Field/FieldLabel.d.ts.map +1 -1
  104. package/dist/Field/FieldLabel.js +9 -5
  105. package/dist/Field/inputStyles.stylex.d.ts +1 -0
  106. package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
  107. package/dist/Field/inputStyles.stylex.js +1 -0
  108. package/dist/FieldStatus/FieldStatus.d.ts +10 -0
  109. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  110. package/dist/FieldStatus/FieldStatus.js +39 -3
  111. package/dist/FileInput/FileInput.d.ts +10 -2
  112. package/dist/FileInput/FileInput.d.ts.map +1 -1
  113. package/dist/FileInput/FileInput.js +93 -65
  114. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  115. package/dist/HoverCard/useHoverCard.js +10 -4
  116. package/dist/Icon/Icon.d.ts +15 -1
  117. package/dist/Icon/Icon.d.ts.map +1 -1
  118. package/dist/Icon/Icon.js +19 -3
  119. package/dist/Item/Item.d.ts +16 -3
  120. package/dist/Item/Item.d.ts.map +1 -1
  121. package/dist/Item/Item.js +31 -6
  122. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  123. package/dist/Layer/layerAnimations.stylex.js +10 -2
  124. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  125. package/dist/Lightbox/Lightbox.js +19 -13
  126. package/dist/List/List.d.ts.map +1 -1
  127. package/dist/List/List.js +6 -3
  128. package/dist/List/ListItem.d.ts +11 -1
  129. package/dist/List/ListItem.d.ts.map +1 -1
  130. package/dist/List/ListItem.js +2 -0
  131. package/dist/Markdown/Markdown.d.ts.map +1 -1
  132. package/dist/Markdown/Markdown.js +7 -0
  133. package/dist/MetadataList/MetadataList.d.ts +1 -1
  134. package/dist/MetadataList/MetadataList.d.ts.map +1 -1
  135. package/dist/MetadataList/MetadataList.js +4 -2
  136. package/dist/MultiSelector/MultiSelector.d.ts +9 -1
  137. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  138. package/dist/MultiSelector/MultiSelector.js +80 -42
  139. package/dist/NavMenu/NavHeadingMenuItem.js +1 -1
  140. package/dist/NumberInput/NumberInput.d.ts +10 -2
  141. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  142. package/dist/NumberInput/NumberInput.js +22 -17
  143. package/dist/Outline/Outline.js +2 -2
  144. package/dist/Pagination/Pagination.d.ts.map +1 -1
  145. package/dist/Pagination/Pagination.js +11 -3
  146. package/dist/PowerSearch/PowerSearch.d.ts +9 -2
  147. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  148. package/dist/PowerSearch/PowerSearch.js +2 -0
  149. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  150. package/dist/ProgressBar/ProgressBar.js +6 -1
  151. package/dist/RadioList/RadioList.d.ts +2 -2
  152. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  153. package/dist/RadioList/RadioListItem.js +5 -5
  154. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  155. package/dist/Resizable/ResizeHandle.js +49 -19
  156. package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
  157. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  158. package/dist/SegmentedControl/SegmentedControlItem.js +5 -3
  159. package/dist/Selector/Selector.d.ts +8 -0
  160. package/dist/Selector/Selector.d.ts.map +1 -1
  161. package/dist/Selector/Selector.js +66 -17
  162. package/dist/SideNav/SideNav.d.ts.map +1 -1
  163. package/dist/SideNav/SideNav.js +5 -5
  164. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  165. package/dist/SideNav/SideNavCollapseButton.js +13 -10
  166. package/dist/SideNav/SideNavItem.d.ts +1 -1
  167. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  168. package/dist/SideNav/SideNavItem.js +12 -10
  169. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  170. package/dist/Skeleton/Skeleton.js +2 -2
  171. package/dist/Slider/Slider.d.ts.map +1 -1
  172. package/dist/Slider/Slider.js +87 -34
  173. package/dist/Spinner/Spinner.d.ts +3 -2
  174. package/dist/Spinner/Spinner.d.ts.map +1 -1
  175. package/dist/Spinner/Spinner.js +11 -2
  176. package/dist/Switch/Switch.d.ts +8 -1
  177. package/dist/Switch/Switch.d.ts.map +1 -1
  178. package/dist/Switch/Switch.js +171 -91
  179. package/dist/TabList/Tab.js +2 -2
  180. package/dist/TabList/TabList.d.ts +2 -11
  181. package/dist/TabList/TabList.d.ts.map +1 -1
  182. package/dist/TabList/TabList.js +5 -9
  183. package/dist/TabList/TabListContext.d.ts +0 -5
  184. package/dist/TabList/TabListContext.d.ts.map +1 -1
  185. package/dist/TabList/TabListContext.js +0 -5
  186. package/dist/TabList/TabMenu.js +1 -1
  187. package/dist/Table/BaseTable.d.ts.map +1 -1
  188. package/dist/Table/BaseTable.js +30 -4
  189. package/dist/Table/TableCell.js +3 -3
  190. package/dist/Table/TableHeaderCell.js +3 -3
  191. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  192. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +15 -11
  193. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  194. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +37 -27
  195. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  196. package/dist/Table/plugins/selection/useTableSelection.js +3 -0
  197. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  198. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +15 -7
  199. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  200. package/dist/Table/plugins/tree/useTableTreeData.js +29 -23
  201. package/dist/Table/types.d.ts +32 -0
  202. package/dist/Table/types.d.ts.map +1 -1
  203. package/dist/TextArea/TextArea.d.ts +10 -1
  204. package/dist/TextArea/TextArea.d.ts.map +1 -1
  205. package/dist/TextArea/TextArea.js +18 -19
  206. package/dist/TextInput/TextInput.d.ts +10 -2
  207. package/dist/TextInput/TextInput.d.ts.map +1 -1
  208. package/dist/TextInput/TextInput.js +20 -17
  209. package/dist/Thumbnail/Thumbnail.js +2 -2
  210. package/dist/TimeInput/TimeInput.d.ts +10 -2
  211. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  212. package/dist/TimeInput/TimeInput.js +17 -17
  213. package/dist/Timestamp/Timestamp.d.ts +31 -3
  214. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  215. package/dist/Timestamp/Timestamp.js +47 -76
  216. package/dist/Timestamp/formatInstant.d.ts +41 -0
  217. package/dist/Timestamp/formatInstant.d.ts.map +1 -0
  218. package/dist/Timestamp/formatInstant.js +177 -0
  219. package/dist/Timestamp/index.d.ts +1 -0
  220. package/dist/Timestamp/index.d.ts.map +1 -1
  221. package/dist/Timestamp/tooltipEntries.d.ts +56 -0
  222. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -0
  223. package/dist/Timestamp/tooltipEntries.js +163 -0
  224. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  225. package/dist/Toast/ToastViewport.js +61 -9
  226. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  227. package/dist/ToggleButton/ToggleButton.js +3 -1
  228. package/dist/Token/Token.d.ts +34 -4
  229. package/dist/Token/Token.d.ts.map +1 -1
  230. package/dist/Token/Token.js +85 -45
  231. package/dist/Token/TokenLink.d.ts +62 -0
  232. package/dist/Token/TokenLink.d.ts.map +1 -0
  233. package/dist/Token/TokenLink.js +76 -0
  234. package/dist/Token/index.d.ts +1 -1
  235. package/dist/Token/index.d.ts.map +1 -1
  236. package/dist/Tokenizer/Tokenizer.d.ts +9 -2
  237. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  238. package/dist/Tokenizer/Tokenizer.js +4 -2
  239. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  240. package/dist/Tooltip/useTooltip.js +17 -1
  241. package/dist/TopNav/TopNavItem.d.ts +3 -1
  242. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  243. package/dist/TopNav/TopNavItem.js +30 -3
  244. package/dist/TreeList/TreeList.d.ts.map +1 -1
  245. package/dist/TreeList/TreeList.js +1 -0
  246. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  247. package/dist/TreeList/TreeListBranches.js +8 -6
  248. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  249. package/dist/TreeList/TreeListItem.js +32 -14
  250. package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
  251. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  252. package/dist/Typeahead/BaseTypeahead.js +22 -11
  253. package/dist/Typeahead/Typeahead.d.ts +9 -2
  254. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  255. package/dist/Typeahead/Typeahead.js +11 -2
  256. package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
  257. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  258. package/dist/Typeahead/TypeaheadItem.js +20 -10
  259. package/dist/astryx.css +70 -30
  260. package/dist/astryx.umd.js +51 -51
  261. package/dist/astryx.umd.js.map +4 -4
  262. package/dist/hooks/index.d.ts +2 -0
  263. package/dist/hooks/index.d.ts.map +1 -1
  264. package/dist/hooks/index.js +1 -0
  265. package/dist/hooks/isRtlElement.d.ts +18 -0
  266. package/dist/hooks/isRtlElement.d.ts.map +1 -0
  267. package/dist/hooks/isRtlElement.js +27 -0
  268. package/dist/hooks/useAnnounce.d.ts +3 -0
  269. package/dist/hooks/useAnnounce.d.ts.map +1 -1
  270. package/dist/hooks/useAnnounce.js +39 -1
  271. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  272. package/dist/hooks/useFocusTrap.js +52 -11
  273. package/dist/hooks/useGridFocus.d.ts +6 -1
  274. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  275. package/dist/hooks/useGridFocus.js +12 -9
  276. package/dist/hooks/useHotkeys.d.ts +9 -0
  277. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  278. package/dist/hooks/useHotkeys.js +9 -0
  279. package/dist/hooks/useInputStatusIcon.d.ts +38 -0
  280. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -0
  281. package/dist/hooks/useInputStatusIcon.js +158 -0
  282. package/dist/hooks/useKeyboardHint.d.ts +1 -1
  283. package/dist/hooks/useKeyboardHint.d.ts.map +1 -1
  284. package/dist/hooks/useKeyboardHint.js +4 -2
  285. package/dist/hooks/useListFocus.d.ts +39 -1
  286. package/dist/hooks/useListFocus.d.ts.map +1 -1
  287. package/dist/hooks/useListFocus.js +54 -9
  288. package/dist/i18n/InternationalizationContext.d.ts +1 -0
  289. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  290. package/dist/i18n/InternationalizationContext.js +3 -0
  291. package/dist/i18n/InternationalizationProvider.d.ts +8 -1
  292. package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
  293. package/dist/i18n/InternationalizationProvider.js +5 -1
  294. package/dist/i18n/getLocaleDirection.d.ts +18 -0
  295. package/dist/i18n/getLocaleDirection.d.ts.map +1 -0
  296. package/dist/i18n/getLocaleDirection.js +29 -0
  297. package/dist/i18n/index.d.ts +6 -0
  298. package/dist/i18n/index.d.ts.map +1 -1
  299. package/dist/i18n/index.js +7 -1
  300. package/dist/i18n/useDirection.d.ts +2 -0
  301. package/dist/i18n/useDirection.d.ts.map +1 -0
  302. package/dist/i18n/useDirection.js +27 -0
  303. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  304. package/dist/theme/derivedVarRegistry.js +13 -0
  305. package/dist/utils/index.d.ts +1 -0
  306. package/dist/utils/index.d.ts.map +1 -1
  307. package/dist/utils/index.js +2 -1
  308. package/dist/utils/plainDate.d.ts +17 -0
  309. package/dist/utils/plainDate.d.ts.map +1 -1
  310. package/dist/utils/plainDate.js +23 -4
  311. package/dist/utils/rtlStyles.d.ts +24 -0
  312. package/dist/utils/rtlStyles.d.ts.map +1 -0
  313. package/dist/utils/rtlStyles.js +27 -0
  314. package/locales/en.json +112 -0
  315. package/locales/pseudo.json +84 -0
  316. package/package.json +6 -1
  317. package/src/Avatar/Avatar.doc.mjs +50 -3
  318. package/src/Avatar/Avatar.test.tsx +218 -0
  319. package/src/Avatar/Avatar.tsx +285 -79
  320. package/src/Avatar/AvatarStatusDot.doc.mjs +4 -4
  321. package/src/Avatar/AvatarStatusDot.tsx +6 -4
  322. package/src/AvatarGroup/AvatarGroup.doc.mjs +2 -0
  323. package/src/AvatarGroup/AvatarGroup.test.tsx +162 -2
  324. package/src/AvatarGroup/AvatarGroup.tsx +58 -3
  325. package/src/AvatarGroup/AvatarGroupOverflow.tsx +5 -2
  326. package/src/Banner/Banner.tsx +6 -6
  327. package/src/Breadcrumbs/BreadcrumbItem.tsx +10 -10
  328. package/src/Breadcrumbs/Breadcrumbs.test.tsx +52 -1
  329. package/src/Breadcrumbs/index.ts +4 -0
  330. package/src/Button/Button.doc.mjs +0 -4
  331. package/src/Button/Button.test.tsx +16 -0
  332. package/src/Button/Button.tsx +6 -4
  333. package/src/Calendar/Calendar.test.tsx +170 -20
  334. package/src/Calendar/Calendar.tsx +73 -16
  335. package/src/Calendar/dayCellUtils.test.ts +18 -18
  336. package/src/Calendar/dayCellUtils.ts +2 -2
  337. package/src/Calendar/styles.ts +33 -39
  338. package/src/Card/Card.tsx +1 -1
  339. package/src/Carousel/Carousel.tsx +25 -6
  340. package/src/Chat/ChatComposer.doc.mjs +2 -2
  341. package/src/Chat/ChatComposerInput.doc.mjs +1 -1
  342. package/src/Chat/ChatComposerInput.tsx +2 -2
  343. package/src/Chat/ChatDictationButton.tsx +7 -1
  344. package/src/Chat/ChatLayout.tsx +4 -4
  345. package/src/Chat/ChatMessageBubble.tsx +15 -10
  346. package/src/Chat/ChatSystemMessage.test.tsx +5 -0
  347. package/src/Chat/ChatToolCalls.tsx +9 -2
  348. package/src/Chat/useTriggerMenu.tsx +1 -1
  349. package/src/CheckboxInput/CheckboxInput.doc.mjs +6 -0
  350. package/src/CheckboxInput/CheckboxInput.test.tsx +23 -0
  351. package/src/CheckboxInput/CheckboxInput.tsx +21 -6
  352. package/src/CheckboxList/CheckboxList.doc.mjs +6 -0
  353. package/src/CheckboxList/CheckboxList.test.tsx +94 -3
  354. package/src/CheckboxList/CheckboxListItem.tsx +17 -10
  355. package/src/Citation/Citation.doc.mjs +4 -3
  356. package/src/Citation/Citation.test.tsx +97 -0
  357. package/src/Citation/Citation.tsx +43 -11
  358. package/src/CodeBlock/CodeBlock.test.tsx +17 -0
  359. package/src/CodeBlock/CodeBlock.tsx +38 -11
  360. package/src/Collapsible/Collapsible.doc.mjs +1 -0
  361. package/src/Collapsible/Collapsible.test.tsx +7 -0
  362. package/src/Collapsible/Collapsible.tsx +16 -6
  363. package/src/CommandPalette/CommandPalette.test.tsx +95 -1
  364. package/src/CommandPalette/CommandPalette.tsx +41 -2
  365. package/src/CommandPalette/CommandPaletteItem.tsx +1 -1
  366. package/src/ContextMenu/ContextMenu.test.tsx +59 -2
  367. package/src/ContextMenu/ContextMenu.tsx +22 -11
  368. package/src/ContextMenu/index.ts +5 -0
  369. package/src/DateInput/DateInput.doc.mjs +25 -0
  370. package/src/DateInput/DateInput.test.tsx +230 -0
  371. package/src/DateInput/DateInput.tsx +51 -29
  372. package/src/DateRangeInput/DateRangeInput.doc.mjs +16 -0
  373. package/src/DateRangeInput/DateRangeInput.test.tsx +140 -0
  374. package/src/DateRangeInput/DateRangeInput.tsx +42 -30
  375. package/src/DateTimeInput/DateTimeInput.doc.mjs +22 -1
  376. package/src/DateTimeInput/DateTimeInput.test.tsx +112 -0
  377. package/src/DateTimeInput/DateTimeInput.tsx +44 -39
  378. package/src/Divider/Divider.test.tsx +32 -8
  379. package/src/Divider/Divider.tsx +14 -1
  380. package/src/DropdownMenu/DropdownMenu.test.tsx +60 -0
  381. package/src/DropdownMenu/DropdownMenu.tsx +31 -12
  382. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +8 -2
  383. package/src/DropdownMenu/DropdownMenuItem.tsx +9 -3
  384. package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +4 -12
  385. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +9 -10
  386. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +9 -3
  387. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +20 -2
  388. package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +88 -0
  389. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +635 -0
  390. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +553 -0
  391. package/src/DropdownMenu/index.ts +16 -0
  392. package/src/DropdownMenu/menuItemHover.ts +39 -0
  393. package/src/DropdownMenu/menuItemRoles.ts +9 -0
  394. package/src/DropdownMenu/renderDropdownItems.tsx +29 -15
  395. package/src/Field/Field.tsx +14 -9
  396. package/src/Field/FieldLabel.test.tsx +25 -0
  397. package/src/Field/FieldLabel.tsx +12 -3
  398. package/src/Field/inputStyles.stylex.ts +2 -0
  399. package/src/FieldStatus/FieldStatus.test.tsx +72 -0
  400. package/src/FieldStatus/FieldStatus.tsx +48 -2
  401. package/src/FileInput/FileInput.doc.mjs +23 -2
  402. package/src/FileInput/FileInput.test.tsx +104 -13
  403. package/src/FileInput/FileInput.tsx +106 -79
  404. package/src/HoverCard/useHoverCard.tsx +15 -3
  405. package/src/Icon/Icon.doc.mjs +13 -0
  406. package/src/Icon/Icon.test.tsx +89 -0
  407. package/src/Icon/Icon.tsx +51 -3
  408. package/src/Item/Item.doc.mjs +5 -0
  409. package/src/Item/Item.test.tsx +88 -0
  410. package/src/Item/Item.tsx +57 -11
  411. package/src/Layer/layerAnimations.stylex.ts +23 -0
  412. package/src/Lightbox/Lightbox.tsx +15 -7
  413. package/src/List/List.test.tsx +17 -29
  414. package/src/List/List.tsx +6 -1
  415. package/src/List/ListItem.doc.mjs +12 -0
  416. package/src/List/ListItem.tsx +13 -0
  417. package/src/Markdown/Markdown.test.tsx +21 -0
  418. package/src/Markdown/Markdown.tsx +4 -0
  419. package/src/MetadataList/MetadataList.test.tsx +24 -0
  420. package/src/MetadataList/MetadataList.tsx +6 -2
  421. package/src/MultiSelector/MultiSelector.doc.mjs +20 -0
  422. package/src/MultiSelector/MultiSelector.test.tsx +402 -0
  423. package/src/MultiSelector/MultiSelector.tsx +120 -59
  424. package/src/NavMenu/NavHeadingMenuItem.tsx +1 -1
  425. package/src/NumberInput/NumberInput.doc.mjs +21 -0
  426. package/src/NumberInput/NumberInput.test.tsx +71 -0
  427. package/src/NumberInput/NumberInput.tsx +35 -26
  428. package/src/Outline/Outline.test.tsx +12 -12
  429. package/src/Outline/Outline.tsx +2 -2
  430. package/src/Pagination/Pagination.tsx +11 -3
  431. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  432. package/src/PowerSearch/PowerSearch.test.tsx +23 -0
  433. package/src/PowerSearch/PowerSearch.tsx +10 -1
  434. package/src/ProgressBar/ProgressBar.test.tsx +34 -0
  435. package/src/ProgressBar/ProgressBar.tsx +16 -1
  436. package/src/RadioList/RadioList.doc.mjs +6 -0
  437. package/src/RadioList/RadioList.test.tsx +26 -4
  438. package/src/RadioList/RadioList.tsx +2 -2
  439. package/src/RadioList/RadioListItem.tsx +12 -5
  440. package/src/Resizable/ResizeHandle.test.tsx +94 -0
  441. package/src/Resizable/ResizeHandle.tsx +70 -20
  442. package/src/SegmentedControl/SegmentedControl.test.tsx +28 -0
  443. package/src/SegmentedControl/SegmentedControlItem.tsx +22 -2
  444. package/src/Selector/Selector.doc.mjs +15 -0
  445. package/src/Selector/Selector.test.tsx +370 -0
  446. package/src/Selector/Selector.tsx +88 -20
  447. package/src/SideNav/SideNav.tsx +1 -8
  448. package/src/SideNav/SideNavCollapseButton.tsx +9 -7
  449. package/src/SideNav/SideNavItem.tsx +5 -3
  450. package/src/Skeleton/Skeleton.test.tsx +16 -0
  451. package/src/Skeleton/Skeleton.tsx +13 -1
  452. package/src/Slider/Slider.doc.mjs +6 -0
  453. package/src/Slider/Slider.test.tsx +240 -12
  454. package/src/Slider/Slider.tsx +96 -23
  455. package/src/Spinner/Spinner.test.tsx +10 -8
  456. package/src/Spinner/Spinner.tsx +15 -5
  457. package/src/Switch/Switch.doc.mjs +142 -42
  458. package/src/Switch/Switch.test.tsx +111 -0
  459. package/src/Switch/Switch.tsx +149 -32
  460. package/src/TabList/Tab.tsx +2 -2
  461. package/src/TabList/TabList.doc.mjs +0 -6
  462. package/src/TabList/TabList.test.tsx +3 -15
  463. package/src/TabList/TabList.tsx +6 -17
  464. package/src/TabList/TabListContext.ts +0 -6
  465. package/src/TabList/TabMenu.tsx +2 -2
  466. package/src/Table/BaseTable.tsx +34 -2
  467. package/src/Table/Table.doc.mjs +11 -0
  468. package/src/Table/Table.test.tsx +85 -0
  469. package/src/Table/TableCell.tsx +3 -3
  470. package/src/Table/TableHeaderCell.tsx +3 -3
  471. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +9 -6
  472. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +19 -0
  473. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +27 -18
  474. package/src/Table/plugins/selection/useTableSelection.test.tsx +61 -3
  475. package/src/Table/plugins/selection/useTableSelection.tsx +3 -0
  476. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +38 -9
  477. package/src/Table/plugins/tree/useTableTreeData.tsx +17 -13
  478. package/src/Table/types.ts +33 -0
  479. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  480. package/src/TextArea/TextArea.doc.mjs +21 -0
  481. package/src/TextArea/TextArea.test.tsx +23 -0
  482. package/src/TextArea/TextArea.tsx +27 -26
  483. package/src/TextInput/TextInput.doc.mjs +21 -0
  484. package/src/TextInput/TextInput.test.tsx +150 -0
  485. package/src/TextInput/TextInput.tsx +28 -25
  486. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  487. package/src/Thumbnail/Thumbnail.tsx +1 -1
  488. package/src/TimeInput/TimeInput.doc.mjs +21 -0
  489. package/src/TimeInput/TimeInput.test.tsx +23 -0
  490. package/src/TimeInput/TimeInput.tsx +24 -25
  491. package/src/Timestamp/Timestamp.doc.mjs +32 -6
  492. package/src/Timestamp/Timestamp.test.tsx +371 -0
  493. package/src/Timestamp/Timestamp.tsx +96 -93
  494. package/src/Timestamp/formatInstant.ts +218 -0
  495. package/src/Timestamp/index.ts +4 -0
  496. package/src/Timestamp/tooltipEntries.test.ts +552 -0
  497. package/src/Timestamp/tooltipEntries.ts +210 -0
  498. package/src/Toast/ToastViewport.test.tsx +218 -8
  499. package/src/Toast/ToastViewport.tsx +71 -12
  500. package/src/ToggleButton/ToggleButton.test.tsx +24 -0
  501. package/src/ToggleButton/ToggleButton.tsx +19 -1
  502. package/src/Token/Token.test.tsx +166 -15
  503. package/src/Token/Token.tsx +104 -47
  504. package/src/Token/TokenLink.tsx +107 -0
  505. package/src/Token/index.ts +1 -1
  506. package/src/Tokenizer/Tokenizer.doc.mjs +23 -1
  507. package/src/Tokenizer/Tokenizer.test.tsx +23 -0
  508. package/src/Tokenizer/Tokenizer.tsx +14 -2
  509. package/src/Tooltip/Tooltip.test.tsx +46 -0
  510. package/src/Tooltip/useTooltip.tsx +20 -0
  511. package/src/TopNav/TopNav.test.tsx +93 -2
  512. package/src/TopNav/TopNavItem.doc.mjs +3 -3
  513. package/src/TopNav/TopNavItem.tsx +35 -6
  514. package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +1 -1
  515. package/src/TreeList/TreeList.doc.mjs +6 -0
  516. package/src/TreeList/TreeList.test.tsx +50 -10
  517. package/src/TreeList/TreeList.tsx +5 -0
  518. package/src/TreeList/TreeListBranches.tsx +8 -6
  519. package/src/TreeList/TreeListItem.tsx +33 -11
  520. package/src/Typeahead/BaseTypeahead.tsx +42 -15
  521. package/src/Typeahead/Typeahead.doc.mjs +13 -0
  522. package/src/Typeahead/Typeahead.test.tsx +112 -0
  523. package/src/Typeahead/Typeahead.tsx +17 -1
  524. package/src/Typeahead/TypeaheadItem.tsx +2 -1
  525. package/src/__tests__/forcedColors.ts +60 -0
  526. package/src/__tests__/inputWidthContract.test.tsx +62 -0
  527. package/src/doc-mjs.d.ts +19 -0
  528. package/src/hooks/index.ts +6 -0
  529. package/src/hooks/isRtlElement.ts +27 -0
  530. package/src/hooks/useAnnounce.test.tsx +75 -2
  531. package/src/hooks/useAnnounce.ts +47 -1
  532. package/src/hooks/useFocusTrap.test.tsx +104 -0
  533. package/src/hooks/useFocusTrap.ts +61 -12
  534. package/src/hooks/useGridFocus.doc.mjs +3 -3
  535. package/src/hooks/useGridFocus.test.tsx +41 -0
  536. package/src/hooks/useGridFocus.ts +21 -10
  537. package/src/hooks/useHotkeys.ts +9 -0
  538. package/src/hooks/useInputStatusIcon.test.tsx +162 -0
  539. package/src/hooks/useInputStatusIcon.tsx +224 -0
  540. package/src/hooks/useKeyboardHint.test.tsx +22 -0
  541. package/src/hooks/useKeyboardHint.tsx +6 -2
  542. package/src/hooks/useListFocus.doc.mjs +25 -3
  543. package/src/hooks/useListFocus.test.tsx +153 -1
  544. package/src/hooks/useListFocus.ts +102 -9
  545. package/src/i18n/InternationalizationContext.ts +4 -0
  546. package/src/i18n/InternationalizationProvider.tsx +12 -2
  547. package/src/i18n/__tests__/getLocaleDirection.test.ts +40 -0
  548. package/src/i18n/__tests__/useDirection.test.tsx +66 -0
  549. package/src/i18n/getLocaleDirection.ts +29 -0
  550. package/src/i18n/index.ts +6 -0
  551. package/src/i18n/useDirection.ts +29 -0
  552. package/src/reset.css +0 -26
  553. package/src/reset.test.ts +60 -0
  554. package/src/theme/derivedVarRegistry.test.ts +1 -0
  555. package/src/theme/derivedVarRegistry.ts +6 -0
  556. package/src/theme/generateThemeRules.test.ts +42 -0
  557. package/src/utils/index.ts +2 -0
  558. package/src/utils/plainDate.test.ts +24 -6
  559. package/src/utils/plainDate.ts +18 -4
  560. package/src/utils/rtlStyles.ts +27 -0
@@ -37,6 +37,7 @@ import {
37
37
  inputStatusBorderStyles,
38
38
  inputStatusHoverShadowStyles,
39
39
  inputWrapperStyles,
40
+ type FieldStatusVariant,
40
41
  } from '../Field';
41
42
  import {Divider} from '../Divider';
42
43
  import {layerAnimations} from '../Layer/layerAnimations.stylex';
@@ -253,7 +254,7 @@ const styles = stylex.create({
253
254
  backgroundColor: 'transparent',
254
255
  border: 'none',
255
256
  cursor: 'pointer',
256
- textAlign: 'left',
257
+ textAlign: 'start',
257
258
  outline: 'none',
258
259
  },
259
260
  itemContent: {
@@ -433,6 +434,13 @@ interface SelectorPropsBase<
433
434
  * If message is provided, displays a message box below the selector.
434
435
  */
435
436
  status?: SelectorStatus;
437
+ /**
438
+ * How the status message is placed relative to the input.
439
+ * - 'attached': message overlaps directly below the input (bordered treatment)
440
+ * - 'detached': message floats below as a separate element with spacing
441
+ * @default 'attached'
442
+ */
443
+ statusVariant?: FieldStatusVariant;
436
444
 
437
445
  /**
438
446
  * Width of the field. Numbers are treated as pixels, strings are used as-is
@@ -537,6 +545,21 @@ function DefaultOption({option}: {option: SelectorOptionData}) {
537
545
  );
538
546
  }
539
547
 
548
+ // Case-insensitive substring match for a single option. The one predicate used
549
+ // by both the flat filter (count + keyboard nav) and the grouped renderer, so
550
+ // what is shown while searching stays in lockstep with the announced count.
551
+ function optionMatchesQuery(
552
+ option: SelectorOptionData,
553
+ query: string,
554
+ ): boolean {
555
+ if (!query) {
556
+ return true;
557
+ }
558
+ return (option.label ?? option.value)
559
+ .toLowerCase()
560
+ .includes(query.toLowerCase());
561
+ }
562
+
540
563
  // Case-insensitive substring filter over the selectable options. Shared by the
541
564
  // `filteredItems` memo (rendering) and the search-change handler, which needs
542
565
  // the count for the *next* query synchronously to announce it exactly once per
@@ -548,10 +571,7 @@ function filterOptionsByQuery(
548
571
  if (!query) {
549
572
  return items;
550
573
  }
551
- const q = query.toLowerCase();
552
- return items.filter(item =>
553
- (item.label ?? item.value).toLowerCase().includes(q),
554
- );
574
+ return items.filter(item => optionMatchesQuery(item, query));
555
575
  }
556
576
 
557
577
  /**
@@ -588,6 +608,7 @@ export function Selector<T extends SelectorOptionType>(
588
608
  placeholder: placeholderFromProps,
589
609
  size: sizeProp,
590
610
  status,
611
+ statusVariant = 'attached',
591
612
  labelTooltip,
592
613
  startIcon,
593
614
  htmlName,
@@ -938,16 +959,22 @@ export function Selector<T extends SelectorOptionType>(
938
959
 
939
960
  // Render all options (handling sections/dividers)
940
961
  const renderOptions = useCallback(() => {
941
- // When search is active, render filtered items flat (no sections/dividers)
942
- if (hasSearch && searchQuery) {
943
- if (filteredItems.length === 0) {
944
- return [
945
- <div key="empty" {...stylex.props(styles.emptyState)}>
946
- No results found
947
- </div>,
948
- ];
949
- }
950
- return filteredItems.map((item, index) => renderItem(item, index));
962
+ const isSearching = hasSearch && Boolean(searchQuery);
963
+
964
+ // Nothing matched across every group/option: show the empty state.
965
+ if (isSearching && filteredItems.length === 0) {
966
+ // role="presentation" keeps the message out of the listbox's
967
+ // accessibility tree (role="listbox" only permits option/group
968
+ // children); the no-results outcome is announced via the
969
+ // result-count live region instead.
970
+ return [
971
+ <div
972
+ key="empty"
973
+ role="presentation"
974
+ {...stylex.props(styles.emptyState)}>
975
+ No results found
976
+ </div>,
977
+ ];
951
978
  }
952
979
 
953
980
  let flatIndex = 0;
@@ -957,13 +984,27 @@ export function Selector<T extends SelectorOptionType>(
957
984
  const option = options[i];
958
985
 
959
986
  if (isDivider(option)) {
987
+ // While searching, a standalone divider between groups would orphan
988
+ // itself once its neighbors are filtered out, so skip it.
989
+ if (isSearching) {
990
+ continue;
991
+ }
960
992
  elements.push(<Divider key={`divider-${i}`} xstyle={styles.divider} />);
961
993
  } else if (isSection(option)) {
962
994
  const sectionItems: ReactNode[] = [];
963
995
  for (const opt of option.options) {
964
- sectionItems.push(renderItem(normalizeOption(opt), flatIndex));
996
+ const normalized = normalizeOption(opt);
997
+ if (isSearching && !optionMatchesQuery(normalized, searchQuery)) {
998
+ continue;
999
+ }
1000
+ sectionItems.push(renderItem(normalized, flatIndex));
965
1001
  flatIndex++;
966
1002
  }
1003
+ // Hide a group entirely (header + wrapper) when none of its items
1004
+ // match the query, so no header is left standing over nothing.
1005
+ if (sectionItems.length === 0) {
1006
+ continue;
1007
+ }
967
1008
  if (option.title) {
968
1009
  elements.push(
969
1010
  <Divider
@@ -979,7 +1020,11 @@ export function Selector<T extends SelectorOptionType>(
979
1020
  </div>,
980
1021
  );
981
1022
  } else if (isOptionData(option)) {
982
- elements.push(renderItem(normalizeOption(option), flatIndex));
1023
+ const normalized = normalizeOption(option);
1024
+ if (isSearching && !optionMatchesQuery(normalized, searchQuery)) {
1025
+ continue;
1026
+ }
1027
+ elements.push(renderItem(normalized, flatIndex));
983
1028
  flatIndex++;
984
1029
  }
985
1030
  }
@@ -1008,7 +1053,7 @@ export function Selector<T extends SelectorOptionType>(
1008
1053
  isDisabled && inputWrapperStyles.disabled,
1009
1054
  !selectedItem && styles.triggerPlaceholder,
1010
1055
  status && inputStatusBorderStyles[status.type],
1011
- status && inputStatusHoverShadowStyles[status.type],
1056
+ status && !isDisabled && inputStatusHoverShadowStyles[status.type],
1012
1057
  inputGroup && groupStyles.inGroup,
1013
1058
  xstyle,
1014
1059
  ),
@@ -1071,7 +1116,17 @@ export function Selector<T extends SelectorOptionType>(
1071
1116
  onClick={handleClear}
1072
1117
  aria-label={t('@astryx.selector.clearLabel', {label})}
1073
1118
  {...stylex.props(styles.clearButton)}>
1074
- <Icon icon="close" size="sm" color="secondary" />
1119
+ <Icon
1120
+ icon="close"
1121
+ size="sm"
1122
+ color="secondary"
1123
+ // Stable theme target on the clear glyph itself, so a theme can
1124
+ // restyle just this icon (color, size, hover) via `defineTheme`.
1125
+ // Same-element rules in @layer astryx-theme win over the icon's
1126
+ // own base color/size, which a button-level target could not
1127
+ // reach.
1128
+ {...themeProps('selector-clear-icon')}
1129
+ />
1075
1130
  </button>
1076
1131
  )}
1077
1132
  <span
@@ -1087,7 +1142,19 @@ export function Selector<T extends SelectorOptionType>(
1087
1142
  color={STATUS_ICON_COLOR_MAP[status.type]}
1088
1143
  />
1089
1144
  ) : (
1090
- <Icon icon="chevronDown" size="sm" color="inherit" />
1145
+ <Icon
1146
+ icon="chevronDown"
1147
+ size="sm"
1148
+ color="inherit"
1149
+ // Stable theme target on the chevron glyph itself, so a theme can
1150
+ // restyle just this icon (color, size, hover) — and its
1151
+ // open/closed state — via `defineTheme`. Same-element rules in
1152
+ // @layer astryx-theme win over the icon's own base color/size,
1153
+ // which a button-level target could not reach.
1154
+ {...themeProps('selector-indicator-icon', {
1155
+ state: popover.isOpen ? 'expanded' : 'collapsed',
1156
+ })}
1157
+ />
1091
1158
  )}
1092
1159
  </span>
1093
1160
  </div>
@@ -1154,6 +1221,7 @@ export function Selector<T extends SelectorOptionType>(
1154
1221
  }
1155
1222
  : undefined
1156
1223
  }
1224
+ statusVariant={statusVariant}
1157
1225
  labelTooltip={labelTooltip}
1158
1226
  width={width}>
1159
1227
  {selectorContent}
@@ -31,7 +31,7 @@ import {
31
31
  import type {BaseProps} from '../BaseProps';
32
32
  import * as stylex from '@stylexjs/stylex';
33
33
  import type {StyleXStyles} from '@stylexjs/stylex';
34
- import {borderVars, colorVars, spacingVars} from '../theme/tokens.stylex';
34
+ import {spacingVars} from '../theme/tokens.stylex';
35
35
  import {mergeProps, mergeRefs} from '../utils';
36
36
  import {
37
37
  SideNavCollapseContext,
@@ -123,9 +123,6 @@ const styles = stylex.create({
123
123
  paddingInline: spacingVars['--spacing-2'],
124
124
  paddingBlockStart: spacingVars['--spacing-1'],
125
125
  paddingBlockEnd: spacingVars['--spacing-2'],
126
- borderBlockStartWidth: borderVars['--border-width'],
127
- borderBlockStartStyle: 'solid',
128
- borderBlockStartColor: colorVars['--color-border'],
129
126
  },
130
127
  footerRow: {
131
128
  display: 'flex',
@@ -145,7 +142,6 @@ const styles = stylex.create({
145
142
  alignItems: 'center',
146
143
  },
147
144
  stickyBottomCollapsed: {
148
- borderBlockStart: 'none',
149
145
  paddingBlockStart: 0,
150
146
  },
151
147
  // Drawer footer — pushed to bottom of the scrollable content area
@@ -155,9 +151,6 @@ const styles = stylex.create({
155
151
  marginBlockStart: 'auto',
156
152
  gap: spacingVars['--spacing-2'],
157
153
  paddingBlockStart: spacingVars['--spacing-2'],
158
- borderBlockStartWidth: borderVars['--border-width'],
159
- borderBlockStartStyle: 'solid',
160
- borderBlockStartColor: colorVars['--color-border'],
161
154
  },
162
155
  drawerFooterIcons: {
163
156
  display: 'flex',
@@ -23,7 +23,7 @@ import {durationVars, easeVars} from '../theme/tokens.stylex';
23
23
  import {getIcon} from '../Icon/globalIconRegistry';
24
24
  import {Button} from '../Button';
25
25
  import type {BaseProps} from '../BaseProps';
26
- import {composeEventHandlers} from '../utils';
26
+ import {composeEventHandlers, rtlStyles} from '../utils';
27
27
  import {
28
28
  useSideNavCollapse,
29
29
  type SideNavCollapseState,
@@ -131,12 +131,14 @@ export function SideNavCollapseButton({
131
131
  onClick={composeEventHandlers(onClickProp, toggle)}
132
132
  icon={
133
133
  children ?? (
134
- <span
135
- {...stylex.props(
136
- styles.chevron,
137
- isCollapsed && styles.chevronCollapsed,
138
- )}>
139
- {getIcon('chevronLeft')}
134
+ <span {...stylex.props(rtlStyles.mirror)}>
135
+ <span
136
+ {...stylex.props(
137
+ styles.chevron,
138
+ isCollapsed && styles.chevronCollapsed,
139
+ )}>
140
+ {getIcon('chevronLeft')}
141
+ </span>
140
142
  </span>
141
143
  )
142
144
  }
@@ -356,6 +356,8 @@ export function SideNavItem({
356
356
  size = 'md',
357
357
  'data-testid': testId,
358
358
  ref,
359
+ xstyle,
360
+ ...rest
359
361
  }: SideNavItemProps) {
360
362
  const t = useTranslator();
361
363
  const {isCollapsed} = useSideNavCollapse();
@@ -502,7 +504,7 @@ export function SideNavItem({
502
504
  // Items with children: popover trigger + popover
503
505
  if (hasChildren) {
504
506
  return (
505
- <div {...stylex.props(styles.root)}>
507
+ <div {...stylex.props(styles.root, xstyle)}>
506
508
  <button
507
509
  ref={mergeRefs(ref, popover.triggerRef)}
508
510
  type="button"
@@ -554,7 +556,7 @@ export function SideNavItem({
554
556
  );
555
557
 
556
558
  return (
557
- <div ref={itemRef} {...stylex.props(styles.root)}>
559
+ <div ref={itemRef} {...stylex.props(styles.root, xstyle)}>
558
560
  {collapsedElement}
559
561
  <Tooltip content={label} placement="end" anchorRef={itemRef} />
560
562
  </div>
@@ -670,7 +672,7 @@ export function SideNavItem({
670
672
  }
671
673
 
672
674
  const item = (
673
- <div ref={itemRef} {...stylex.props(styles.root)}>
675
+ <div ref={itemRef} {...stylex.props(styles.root, xstyle)}>
674
676
  {itemElement}
675
677
  {hasChildren && !isCollapsed && (
676
678
  <div
@@ -3,6 +3,7 @@
3
3
  import {describe, it, expect} from 'vitest';
4
4
  import {render, screen} from '@testing-library/react';
5
5
  import {Skeleton} from './Skeleton';
6
+ import {getForcedColorsRules} from '../__tests__/forcedColors';
6
7
 
7
8
  describe('Skeleton', () => {
8
9
  it('renders a placeholder element', () => {
@@ -23,3 +24,18 @@ describe('Skeleton', () => {
23
24
  expect(screen.getByTestId('sk')).toHaveAttribute('aria-hidden', 'false');
24
25
  });
25
26
  });
27
+
28
+ // jsdom cannot emulate forced-colors rendering, so these assert that the
29
+ // compiled output includes the forced-colors rules; visual behavior needs
30
+ // manual verification under Windows High Contrast.
31
+ describe('forced colors (WCAG 1.4.11)', () => {
32
+ it('compiles forced-colors overrides so the placeholder stays visible under Windows High Contrast', () => {
33
+ render(<Skeleton width={200} height={20} data-testid="sk" />);
34
+ const css = getForcedColorsRules();
35
+ // The painted fill is stripped to Canvas (invisible); GrayText is a
36
+ // system color, so it survives forcing.
37
+ expect(css).toContain('background-color: graytext;');
38
+ // The resting 0.25 opacity is lifted so the static placeholder reads.
39
+ expect(css).toContain('opacity: 1;');
40
+ });
41
+ });
@@ -55,8 +55,20 @@ const styles = stylex.create({
55
55
  backgroundColor: {
56
56
  default: colorVars['--color-skeleton'],
57
57
  '@media (prefers-contrast: more)': `color-mix(in srgb, ${colorVars['--color-skeleton']}, ${colorVars['--color-text-primary']} 30%)`,
58
+ // Forced colors (Windows High Contrast) strips painted backgrounds,
59
+ // which would make the placeholder invisible. GrayText is a system
60
+ // color, so it survives forcing and keeps the placeholder visible
61
+ // (WCAG 1.4.11). Listed after prefers-contrast so it wins when both
62
+ // media features are active.
63
+ '@media (forced-colors: active)': 'GrayText',
64
+ },
65
+ opacity: {
66
+ default: 0.25,
67
+ // The resting 0.25 opacity would render GrayText nearly invisible on
68
+ // Canvas; full opacity keeps the static placeholder perceivable (the
69
+ // fade animation still pulses when motion is allowed).
70
+ '@media (forced-colors: active)': 1,
58
71
  },
59
- opacity: 0.25,
60
72
  },
61
73
  animate: {
62
74
  animationDirection: 'alternate',
@@ -141,6 +141,12 @@ export const docs = {
141
141
  type: 'string',
142
142
  description: 'Tooltip text for an info icon displayed next to the label.',
143
143
  },
144
+ {
145
+ name: 'width',
146
+ type: 'SizeValue',
147
+ description:
148
+ 'Width of the field (number = pixels, string used as-is, e.g. "100%"). Sizes the whole field (label, control, and status) so they stay aligned.',
149
+ },
144
150
  {
145
151
  name: 'xstyle',
146
152
  type: 'StyleXStyles',
@@ -9,6 +9,7 @@
9
9
  * SYNC: When Slider.tsx changes, update tests to match new behavior
10
10
  */
11
11
 
12
+ import {useState} from 'react';
12
13
  import {describe, it, expect, vi, beforeEach} from 'vitest';
13
14
  import {render, screen, act, fireEvent, waitFor} from '@testing-library/react';
14
15
  import userEvent from '@testing-library/user-event';
@@ -50,23 +51,17 @@ beforeEach(() => {
50
51
  describe('Slider', () => {
51
52
  // --- Aria labels ---
52
53
 
53
- it('single thumb uses label as aria-label', () => {
54
+ it('single thumb takes its accessible name from the label', () => {
54
55
  render(<Slider label="Volume" value={50} />);
55
56
  const slider = screen.getByRole('slider');
56
- expect(slider).toHaveAttribute('aria-label', 'Volume');
57
+ expect(slider).toHaveAccessibleName('Volume');
57
58
  });
58
59
 
59
- it('range thumbs have correct aria-labels', () => {
60
+ it('range thumbs have individual names composing with the group label', () => {
60
61
  render(<Slider label="Price range" value={[20, 80] as [number, number]} />);
61
62
  const sliders = screen.getAllByRole('slider');
62
- expect(sliders[0]).toHaveAttribute(
63
- 'aria-label',
64
- 'Price range, minimum value',
65
- );
66
- expect(sliders[1]).toHaveAttribute(
67
- 'aria-label',
68
- 'Price range, maximum value',
69
- );
63
+ expect(sliders[0]).toHaveAccessibleName('Minimum value');
64
+ expect(sliders[1]).toHaveAccessibleName('Maximum value');
70
65
  });
71
66
 
72
67
  it('sets aria-valuetext with formatValue', () => {
@@ -94,7 +89,9 @@ describe('Slider', () => {
94
89
  render(<Slider label="Volume" value={value} min={0} max={100} />);
95
90
  const slider = screen.getByRole('slider');
96
91
  expect(slider).toHaveAttribute('aria-valuenow', String(expectedValue));
97
- expect(slider).toHaveStyle({left: expectedPosition});
92
+ // Thumb positions via the logical `inset-inline-start` so it mirrors
93
+ // under RTL (see RTL Phase 4). In LTR this resolves to the left edge.
94
+ expect(slider).toHaveStyle({insetInlineStart: expectedPosition});
98
95
  },
99
96
  );
100
97
 
@@ -110,6 +107,102 @@ describe('Slider', () => {
110
107
  const sliders = screen.getAllByRole('slider');
111
108
  expect(sliders[0]).toHaveAttribute('aria-valuenow', '25');
112
109
  expect(sliders[1]).toHaveAttribute('aria-valuenow', '75');
110
+ // Per the APG multi-thumb pattern, each thumb's bounds are constrained by
111
+ // its sibling: the lower thumb can't exceed the upper thumb's value and
112
+ // the upper thumb can't go below the lower thumb's value.
113
+ expect(sliders[0]).toHaveAttribute('aria-valuemin', '0');
114
+ expect(sliders[0]).toHaveAttribute('aria-valuemax', '75');
115
+ expect(sliders[1]).toHaveAttribute('aria-valuemin', '25');
116
+ expect(sliders[1]).toHaveAttribute('aria-valuemax', '100');
117
+ });
118
+
119
+ it('range thumb bounds update after moving a thumb', async () => {
120
+ const user = userEvent.setup();
121
+ function ControlledRange() {
122
+ const [value, setValue] = useState<[number, number]>([25, 75]);
123
+ return (
124
+ <Slider
125
+ label="Range"
126
+ value={value}
127
+ onChange={setValue}
128
+ min={0}
129
+ max={100}
130
+ />
131
+ );
132
+ }
133
+ render(<ControlledRange />);
134
+ const sliders = screen.getAllByRole('slider');
135
+ act(() => {
136
+ sliders[0].focus();
137
+ });
138
+ await user.keyboard('{ArrowRight}');
139
+ expect(sliders[0]).toHaveAttribute('aria-valuenow', '26');
140
+ // The upper thumb's floor tracks the lower thumb's new value.
141
+ expect(sliders[1]).toHaveAttribute('aria-valuemin', '26');
142
+ expect(sliders[0]).toHaveAttribute('aria-valuemax', '75');
143
+ });
144
+
145
+ it('range thumb bounds include the minStepsBetweenThumbs gap', () => {
146
+ render(
147
+ <Slider
148
+ label="Range"
149
+ value={[20, 80] as [number, number]}
150
+ min={0}
151
+ max={100}
152
+ step={5}
153
+ minStepsBetweenThumbs={2}
154
+ />,
155
+ );
156
+ const sliders = screen.getAllByRole('slider');
157
+ // minGap = 2 steps * 5 = 10
158
+ expect(sliders[0]).toHaveAttribute('aria-valuemax', '70');
159
+ expect(sliders[1]).toHaveAttribute('aria-valuemin', '30');
160
+ });
161
+
162
+ // --- Label association ---
163
+
164
+ // A <label htmlFor> must point at an existing form-associated element;
165
+ // div[role="slider"] is not labelable, so the Slider label must render as a
166
+ // group label instead (WCAG 1.3.1).
167
+ const LABELABLE_TAGS = new Set([
168
+ 'BUTTON',
169
+ 'INPUT',
170
+ 'METER',
171
+ 'OUTPUT',
172
+ 'PROGRESS',
173
+ 'SELECT',
174
+ 'TEXTAREA',
175
+ ]);
176
+
177
+ function expectNoOrphanedLabels(container: HTMLElement) {
178
+ for (const labelEl of container.querySelectorAll('label[for]')) {
179
+ const target = document.getElementById(labelEl.getAttribute('for')!);
180
+ expect(target).not.toBeNull();
181
+ expect(LABELABLE_TAGS.has(target!.tagName)).toBe(true);
182
+ }
183
+ }
184
+
185
+ it('single mode renders the label as a group label naming the thumb', () => {
186
+ const {container} = render(<Slider label="Volume" value={50} />);
187
+ expectNoOrphanedLabels(container);
188
+ const slider = screen.getByRole('slider');
189
+ const labelledBy = slider.getAttribute('aria-labelledby');
190
+ expect(labelledBy).toBeTruthy();
191
+ expect(document.getElementById(labelledBy!)).toHaveTextContent('Volume');
192
+ expect(slider).toHaveAccessibleName('Volume');
193
+ });
194
+
195
+ it('range mode labels the slider group via aria-labelledby', () => {
196
+ const {container} = render(
197
+ <Slider label="Price range" value={[20, 80] as [number, number]} />,
198
+ );
199
+ expectNoOrphanedLabels(container);
200
+ const group = screen.getByRole('group', {name: 'Price range'});
201
+ const labelledBy = group.getAttribute('aria-labelledby');
202
+ expect(labelledBy).toBeTruthy();
203
+ expect(document.getElementById(labelledBy!)).toHaveTextContent(
204
+ 'Price range',
205
+ );
113
206
  });
114
207
 
115
208
  it('sets aria-orientation for vertical', () => {
@@ -301,6 +394,58 @@ describe('Slider', () => {
301
394
  expect(handleChangeEnd).toHaveBeenCalledWith(55);
302
395
  });
303
396
 
397
+ // --- Fractional step precision ---
398
+
399
+ it('emits exact decimal values for fractional steps on keyboard', async () => {
400
+ const user = userEvent.setup();
401
+ const handleChange = vi.fn();
402
+ const handleChangeEnd = vi.fn();
403
+ render(
404
+ <Slider
405
+ label="Opacity"
406
+ value={0.2}
407
+ min={0}
408
+ max={1}
409
+ step={0.1}
410
+ onChange={handleChange}
411
+ onChangeEnd={handleChangeEnd}
412
+ />,
413
+ );
414
+ const slider = screen.getByRole('slider');
415
+ act(() => {
416
+ slider.focus();
417
+ });
418
+ await user.keyboard('{ArrowRight}');
419
+ // 0.2 + 0.1 must not surface binary float error (0.30000000000000004)
420
+ expect(handleChange).toHaveBeenCalledWith(0.3);
421
+ expect(handleChangeEnd).toHaveBeenCalledWith(0.3);
422
+ });
423
+
424
+ it('emits exact decimal values for fractional steps in range mode', async () => {
425
+ const user = userEvent.setup();
426
+ const handleChange = vi.fn();
427
+ const handleChangeEnd = vi.fn();
428
+ render(
429
+ <Slider
430
+ label="Range"
431
+ value={[0.2, 0.6] as [number, number]}
432
+ min={0}
433
+ max={1}
434
+ step={0.1}
435
+ onChange={handleChange}
436
+ onChangeEnd={handleChangeEnd}
437
+ />,
438
+ );
439
+ const sliders = screen.getAllByRole('slider');
440
+ act(() => {
441
+ sliders[1].focus();
442
+ });
443
+ await user.keyboard('{ArrowRight}');
444
+ // 0.6 + 0.1 snaps to 7 * 0.1, which is 0.7000000000000001 without rounding
445
+ expect(handleChange).toHaveBeenCalledWith([0.2, 0.7]);
446
+ expect(handleChangeEnd).toHaveBeenCalledWith([0.2, 0.7]);
447
+ });
448
+
304
449
  it('fires onChangeEnd on keyboard Home/End with correct value', async () => {
305
450
  const user = userEvent.setup();
306
451
  const handleChangeEnd = vi.fn();
@@ -416,6 +561,89 @@ describe('Slider', () => {
416
561
  expect(document.activeElement).toBe(slider);
417
562
  });
418
563
 
564
+ // --- RTL pointer value mapping (RTL Phase 4) ---
565
+ // Contract: the inline-start edge (value = min) is the RIGHT edge under RTL,
566
+ // so the same physical click maps to the mirrored value relative to LTR.
567
+ // `valueDisplay="none"` keeps the thumb a direct child of the track element
568
+ // (no Tooltip wrapper) so the mocked rect lands on trackRef.
569
+
570
+ it('maps a track click to the LTR value in the default direction', () => {
571
+ const handleChange = vi.fn();
572
+ render(
573
+ <Slider
574
+ label="Volume"
575
+ value={0}
576
+ min={0}
577
+ max={100}
578
+ valueDisplay="none"
579
+ onChange={handleChange}
580
+ />,
581
+ );
582
+ const track = screen.getByRole('slider').parentElement!;
583
+ track.getBoundingClientRect = () =>
584
+ ({
585
+ left: 0,
586
+ top: 0,
587
+ right: 200,
588
+ bottom: 20,
589
+ width: 200,
590
+ height: 20,
591
+ x: 0,
592
+ y: 0,
593
+ toJSON: () => {},
594
+ });
595
+
596
+ // Click at 25% of the track from the left (x=50 of 200) → value 25 in LTR.
597
+ fireEvent.pointerDown(track, {clientX: 50, clientY: 10, pointerId: 1});
598
+ expect(handleChange).toHaveBeenLastCalledWith(25);
599
+ });
600
+
601
+ it('mirrors a track click to the RTL value when the track is rtl', () => {
602
+ const handleChange = vi.fn();
603
+ render(
604
+ <Slider
605
+ label="Volume"
606
+ value={0}
607
+ min={0}
608
+ max={100}
609
+ valueDisplay="none"
610
+ onChange={handleChange}
611
+ />,
612
+ );
613
+ const track = screen.getByRole('slider').parentElement!;
614
+ track.getBoundingClientRect = () =>
615
+ ({
616
+ left: 0,
617
+ top: 0,
618
+ right: 200,
619
+ bottom: 20,
620
+ width: 200,
621
+ height: 20,
622
+ x: 0,
623
+ y: 0,
624
+ toJSON: () => {},
625
+ });
626
+
627
+ // Force the track's computed direction to rtl (isRtlElement reads this).
628
+ const realGetComputedStyle = window.getComputedStyle;
629
+ const gcsSpy = vi
630
+ .spyOn(window, 'getComputedStyle')
631
+ .mockImplementation((el: Element, pseudo?: string | null) => {
632
+ if (el === track) {
633
+ return {direction: 'rtl'} as CSSStyleDeclaration;
634
+ }
635
+ return realGetComputedStyle(el, pseudo ?? undefined);
636
+ });
637
+
638
+ // Same physical click at 25% from the left (x=50). Under RTL the inline
639
+ // start is the right edge, so fraction = (right - x)/width = 150/200 = 0.75
640
+ // → value 75 (the mirror of the LTR value 25).
641
+ fireEvent.pointerDown(track, {clientX: 50, clientY: 10, pointerId: 1});
642
+ expect(handleChange).toHaveBeenLastCalledWith(75);
643
+
644
+ gcsSpy.mockRestore();
645
+ });
646
+
419
647
  // --- Mark label click snapping ---
420
648
 
421
649
  it('clicking a mark label snaps to that mark value, not pointer position', () => {