@astryxdesign/core 0.4.7 → 0.5.0-canary.32a62fd

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 (554) hide show
  1. package/CHANGELOG.md +161 -1
  2. package/dist/AppShell/AppShell.d.ts.map +1 -1
  3. package/dist/AppShell/AppShell.js +3 -2
  4. package/dist/AspectRatio/AspectRatio.d.ts +24 -0
  5. package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
  6. package/dist/AspectRatio/AspectRatio.js +23 -4
  7. package/dist/Avatar/Avatar.d.ts +25 -1
  8. package/dist/Avatar/Avatar.d.ts.map +1 -1
  9. package/dist/Avatar/Avatar.js +58 -19
  10. package/dist/Avatar/index.d.ts +2 -2
  11. package/dist/Avatar/index.d.ts.map +1 -1
  12. package/dist/Avatar/index.js +1 -1
  13. package/dist/AvatarGroup/AvatarGroup.d.ts +9 -2
  14. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  15. package/dist/AvatarGroup/AvatarGroup.js +8 -4
  16. package/dist/AvatarGroup/AvatarGroupContext.d.ts +2 -1
  17. package/dist/AvatarGroup/AvatarGroupContext.d.ts.map +1 -1
  18. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  19. package/dist/AvatarGroup/AvatarGroupOverflow.js +10 -6
  20. package/dist/Banner/Banner.d.ts +40 -15
  21. package/dist/Banner/Banner.d.ts.map +1 -1
  22. package/dist/Banner/Banner.js +51 -18
  23. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  24. package/dist/BottomSheet/BottomSheet.js +17 -4
  25. package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
  26. package/dist/BottomSheet/BottomSheetSwitcher.js +3 -2
  27. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  28. package/dist/Breadcrumbs/BreadcrumbItem.js +11 -10
  29. package/dist/Button/Button.d.ts.map +1 -1
  30. package/dist/Button/Button.js +5 -4
  31. package/dist/ButtonGroup/ButtonGroup.d.ts +5 -1
  32. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  33. package/dist/ButtonGroup/ButtonGroup.js +26 -7
  34. package/dist/Calendar/Calendar.d.ts +3 -1
  35. package/dist/Calendar/Calendar.d.ts.map +1 -1
  36. package/dist/Calendar/Calendar.js +13 -14
  37. package/dist/Calendar/getInitialFocusDate.d.ts +46 -0
  38. package/dist/Calendar/getInitialFocusDate.d.ts.map +1 -0
  39. package/dist/Calendar/getInitialFocusDate.js +62 -0
  40. package/dist/Carousel/Carousel.d.ts.map +1 -1
  41. package/dist/Carousel/Carousel.js +3 -3
  42. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  43. package/dist/Chat/ChatComposerInput.js +2 -3
  44. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  45. package/dist/Chat/ChatLayout.js +3 -3
  46. package/dist/Chat/ChatMessageList.d.ts +22 -3
  47. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  48. package/dist/Chat/ChatMessageList.js +6 -3
  49. package/dist/Chat/ChatTokenizedText.d.ts +2 -0
  50. package/dist/Chat/ChatTokenizedText.d.ts.map +1 -1
  51. package/dist/Chat/ChatTokenizedText.js +11 -6
  52. package/dist/Chat/useChatComposerTokens.js +1 -1
  53. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  54. package/dist/CheckboxInput/CheckboxInput.js +10 -3
  55. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  56. package/dist/ClickableCard/ClickableCard.js +3 -3
  57. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  58. package/dist/CodeBlock/CodeBlock.js +15 -4
  59. package/dist/CodeBlock/highlightRanges.js +1 -1
  60. package/dist/CodeBlock/highlightStyles.js +13 -13
  61. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  62. package/dist/CommandPalette/CommandPaletteInput.js +3 -2
  63. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  64. package/dist/CommandPalette/CommandPaletteItem.js +3 -2
  65. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  66. package/dist/ContextMenu/ContextMenu.js +4 -3
  67. package/dist/DateInput/DateInput.d.ts +43 -0
  68. package/dist/DateInput/DateInput.d.ts.map +1 -1
  69. package/dist/DateInput/DateInput.js +22 -2
  70. package/dist/DateInput/NativeDateField.d.ts +10 -0
  71. package/dist/DateInput/NativeDateField.d.ts.map +1 -0
  72. package/dist/DateInput/NativeDateField.js +477 -0
  73. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  74. package/dist/DateInput/TouchDateField.js +38 -4
  75. package/dist/DateInput/index.d.ts +1 -1
  76. package/dist/DateInput/index.d.ts.map +1 -1
  77. package/dist/DateTimeInput/DateTimeInput.d.ts +21 -1
  78. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  79. package/dist/DateTimeInput/DateTimeInput.js +406 -12
  80. package/dist/DateTimeInput/index.d.ts +1 -1
  81. package/dist/DateTimeInput/index.d.ts.map +1 -1
  82. package/dist/Dialog/Dialog.d.ts +7 -1
  83. package/dist/Dialog/Dialog.d.ts.map +1 -1
  84. package/dist/Dialog/Dialog.js +89 -58
  85. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  86. package/dist/DropdownMenu/DropdownMenuSubMenu.js +6 -5
  87. package/dist/Field/FieldLabel.d.ts.map +1 -1
  88. package/dist/Field/FieldLabel.js +9 -1
  89. package/dist/FileInput/FileInput.d.ts.map +1 -1
  90. package/dist/FileInput/FileInput.js +3 -2
  91. package/dist/Heading/Heading.d.ts.map +1 -1
  92. package/dist/Heading/Heading.js +6 -2
  93. package/dist/HoverCard/HoverCard.d.ts +6 -0
  94. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  95. package/dist/HoverCard/HoverCard.js +52 -3
  96. package/dist/HoverCard/useHoverCard.d.ts +16 -2
  97. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  98. package/dist/HoverCard/useHoverCard.js +74 -26
  99. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
  100. package/dist/Indicator/CheckboxIndicator.js +6 -2
  101. package/dist/Item/Item.d.ts.map +1 -1
  102. package/dist/Item/Item.js +4 -2
  103. package/dist/Kbd/Kbd.d.ts.map +1 -1
  104. package/dist/Kbd/Kbd.js +2 -19
  105. package/dist/Layer/LayerDepthContext.d.ts +52 -0
  106. package/dist/Layer/LayerDepthContext.d.ts.map +1 -0
  107. package/dist/Layer/LayerDepthContext.js +66 -0
  108. package/dist/Layer/index.d.ts +3 -0
  109. package/dist/Layer/index.d.ts.map +1 -1
  110. package/dist/Layer/index.js +4 -0
  111. package/dist/Layer/layerStack.d.ts +98 -0
  112. package/dist/Layer/layerStack.d.ts.map +1 -0
  113. package/dist/Layer/layerStack.js +306 -0
  114. package/dist/Layer/useLayerDismissal.d.ts +93 -0
  115. package/dist/Layer/useLayerDismissal.d.ts.map +1 -0
  116. package/dist/Layer/useLayerDismissal.js +91 -0
  117. package/dist/Lightbox/Lightbox.d.ts +1 -0
  118. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  119. package/dist/Lightbox/Lightbox.js +103 -81
  120. package/dist/Markdown/index.d.ts +1 -1
  121. package/dist/Markdown/index.d.ts.map +1 -1
  122. package/dist/Markdown/parser.d.ts +38 -1
  123. package/dist/Markdown/parser.d.ts.map +1 -1
  124. package/dist/Markdown/parser.js +177 -29
  125. package/dist/MobileNav/MobileNav.d.ts +6 -1
  126. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  127. package/dist/MobileNav/MobileNav.js +61 -36
  128. package/dist/MultiSelector/MultiSelector.d.ts +15 -1
  129. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  130. package/dist/MultiSelector/MultiSelector.js +17 -6
  131. package/dist/MultiSelector/index.d.ts +1 -1
  132. package/dist/MultiSelector/index.d.ts.map +1 -1
  133. package/dist/NavIcon/NavIcon.d.ts.map +1 -1
  134. package/dist/NavIcon/NavIcon.js +5 -1
  135. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  136. package/dist/NavMenu/NavHeadingMenu.js +3 -2
  137. package/dist/NumberInput/NumberInput.d.ts +1 -0
  138. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  139. package/dist/NumberInput/NumberInput.js +27 -37
  140. package/dist/NumberInput/numberParser.d.ts +43 -0
  141. package/dist/NumberInput/numberParser.d.ts.map +1 -0
  142. package/dist/NumberInput/numberParser.js +293 -0
  143. package/dist/Outline/Outline.d.ts.map +1 -1
  144. package/dist/Outline/Outline.js +3 -2
  145. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  146. package/dist/OverflowList/OverflowList.js +3 -2
  147. package/dist/Overlay/Overlay.d.ts.map +1 -1
  148. package/dist/Overlay/Overlay.js +3 -2
  149. package/dist/Popover/usePopover.d.ts.map +1 -1
  150. package/dist/Popover/usePopover.js +20 -17
  151. package/dist/PowerSearch/PowerSearch.d.ts +8 -2
  152. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  153. package/dist/PowerSearch/PowerSearch.js +19 -6
  154. package/dist/PowerSearch/PowerSearchEditPopover.d.ts +3 -1
  155. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  156. package/dist/PowerSearch/PowerSearchEditPopover.js +28 -6
  157. package/dist/PowerSearch/PowerSearchValueEditor.d.ts +3 -1
  158. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  159. package/dist/PowerSearch/PowerSearchValueEditor.js +19 -9
  160. package/dist/PowerSearch/types.d.ts +2 -0
  161. package/dist/PowerSearch/types.d.ts.map +1 -1
  162. package/dist/PowerSearch/usePowerSearchSource.d.ts +6 -1
  163. package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
  164. package/dist/PowerSearch/usePowerSearchSource.js +14 -5
  165. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  166. package/dist/ProgressBar/ProgressBar.js +18 -5
  167. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  168. package/dist/Resizable/ResizeHandle.js +3 -2
  169. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  170. package/dist/SegmentedControl/SegmentedControl.js +3 -2
  171. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  172. package/dist/SelectableCard/SelectableCard.js +3 -3
  173. package/dist/Selector/Selector.d.ts.map +1 -1
  174. package/dist/Selector/Selector.js +5 -0
  175. package/dist/SideNav/SideNav.d.ts.map +1 -1
  176. package/dist/SideNav/SideNav.js +4 -3
  177. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  178. package/dist/SideNav/SideNavHeading.js +8 -7
  179. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  180. package/dist/SideNav/SideNavItem.js +4 -3
  181. package/dist/Slider/Slider.d.ts.map +1 -1
  182. package/dist/Slider/Slider.js +3 -3
  183. package/dist/Spinner/Spinner.d.ts +3 -3
  184. package/dist/Spinner/Spinner.d.ts.map +1 -1
  185. package/dist/Spinner/Spinner.js +130 -78
  186. package/dist/StatusDot/StatusDot.d.ts.map +1 -1
  187. package/dist/StatusDot/StatusDot.js +6 -1
  188. package/dist/Stepper/Step.d.ts +140 -0
  189. package/dist/Stepper/Step.d.ts.map +1 -0
  190. package/dist/Stepper/Step.js +1045 -0
  191. package/dist/Stepper/StepStatus.d.ts +24 -0
  192. package/dist/Stepper/StepStatus.d.ts.map +1 -0
  193. package/dist/Stepper/StepStatus.js +1 -0
  194. package/dist/Stepper/Stepper.d.ts +93 -0
  195. package/dist/Stepper/Stepper.d.ts.map +1 -0
  196. package/dist/Stepper/Stepper.js +184 -0
  197. package/dist/Stepper/StepperContext.d.ts +41 -0
  198. package/dist/Stepper/StepperContext.d.ts.map +1 -0
  199. package/dist/Stepper/StepperContext.js +34 -0
  200. package/dist/Stepper/index.d.ts +8 -0
  201. package/dist/Stepper/index.d.ts.map +1 -0
  202. package/dist/Stepper/index.js +7 -0
  203. package/dist/Stepper/stepper.stylex.d.ts +15 -0
  204. package/dist/Stepper/stepper.stylex.d.ts.map +1 -0
  205. package/dist/Stepper/stepper.stylex.js +20 -0
  206. package/dist/Switch/Switch.d.ts.map +1 -1
  207. package/dist/Switch/Switch.js +8 -3
  208. package/dist/TabList/Tab.d.ts +15 -2
  209. package/dist/TabList/Tab.d.ts.map +1 -1
  210. package/dist/TabList/Tab.js +32 -7
  211. package/dist/TabList/TabList.d.ts +46 -7
  212. package/dist/TabList/TabList.d.ts.map +1 -1
  213. package/dist/TabList/TabList.js +353 -23
  214. package/dist/TabList/TabListContext.d.ts +11 -1
  215. package/dist/TabList/TabListContext.d.ts.map +1 -1
  216. package/dist/TabList/TabListContext.js +11 -2
  217. package/dist/TabList/TabMenu.d.ts.map +1 -1
  218. package/dist/TabList/TabMenu.js +3 -2
  219. package/dist/TabList/index.d.ts +2 -2
  220. package/dist/TabList/index.d.ts.map +1 -1
  221. package/dist/Table/BaseTable.d.ts.map +1 -1
  222. package/dist/Table/BaseTable.js +10 -2
  223. package/dist/Table/Table.d.ts.map +1 -1
  224. package/dist/Table/Table.js +3 -6
  225. package/dist/Table/TableRow.d.ts +16 -5
  226. package/dist/Table/TableRow.d.ts.map +1 -1
  227. package/dist/Table/TableRow.js +16 -5
  228. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  229. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +20 -8
  230. package/dist/Table/plugins/pagination/useTablePagination.d.ts +3 -0
  231. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  232. package/dist/Table/plugins/pagination/useTablePagination.js +21 -4
  233. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  234. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +7 -2
  235. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -1
  236. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +10 -3
  237. package/dist/Table/plugins/selection/useTableSelection.d.ts +16 -0
  238. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  239. package/dist/Table/plugins/selection/useTableSelection.js +19 -5
  240. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  241. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +3 -4
  242. package/dist/Table/types.d.ts +22 -4
  243. package/dist/Table/types.d.ts.map +1 -1
  244. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  245. package/dist/Table/useBaseTablePlugins.js +5 -0
  246. package/dist/Text/Text.d.ts.map +1 -1
  247. package/dist/Text/Text.js +6 -2
  248. package/dist/TextArea/TextArea.d.ts.map +1 -1
  249. package/dist/TextArea/TextArea.js +9 -3
  250. package/dist/TextInput/TextInput.d.ts.map +1 -1
  251. package/dist/TextInput/TextInput.js +3 -2
  252. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  253. package/dist/TimeInput/TimeInput.js +3 -2
  254. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  255. package/dist/Timestamp/Timestamp.js +5 -3
  256. package/dist/Token/TokenLink.d.ts.map +1 -1
  257. package/dist/Token/TokenLink.js +2 -2
  258. package/dist/Tokenizer/Tokenizer.d.ts +3 -1
  259. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  260. package/dist/Tokenizer/Tokenizer.js +2 -0
  261. package/dist/Tooltip/Tooltip.d.ts +5 -0
  262. package/dist/Tooltip/Tooltip.d.ts.map +1 -1
  263. package/dist/Tooltip/useTooltip.d.ts +5 -0
  264. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  265. package/dist/Tooltip/useTooltip.js +39 -24
  266. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  267. package/dist/TopNav/TopNavHeading.js +5 -4
  268. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  269. package/dist/TopNav/TopNavMegaMenu.js +3 -3
  270. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  271. package/dist/TopNav/TopNavMenu.js +4 -3
  272. package/dist/Typeahead/BaseTypeahead.d.ts +2 -0
  273. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  274. package/dist/Typeahead/BaseTypeahead.js +68 -29
  275. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  276. package/dist/Typeahead/Typeahead.js +3 -2
  277. package/dist/astryx.css +94 -7
  278. package/dist/hooks/index.d.ts +1 -0
  279. package/dist/hooks/index.d.ts.map +1 -1
  280. package/dist/hooks/index.js +1 -0
  281. package/dist/hooks/useFocusTrap.d.ts +7 -4
  282. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  283. package/dist/hooks/useFocusTrap.js +60 -95
  284. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  285. package/dist/hooks/useHotkeys.js +3 -18
  286. package/dist/hooks/useMergedRefs.d.ts +18 -0
  287. package/dist/hooks/useMergedRefs.d.ts.map +1 -0
  288. package/dist/hooks/useMergedRefs.js +25 -0
  289. package/dist/index.d.ts +1 -0
  290. package/dist/index.d.ts.map +1 -1
  291. package/dist/index.js +1 -0
  292. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  293. package/dist/theme/derivedVarRegistry.js +24 -4
  294. package/dist/theme/syntax/tokens.d.ts +1 -1
  295. package/dist/theme/syntax/tokens.d.ts.map +1 -1
  296. package/dist/theme/syntax/tokens.js +5 -2
  297. package/dist/utils/groupItems.d.ts +5 -1
  298. package/dist/utils/groupItems.d.ts.map +1 -1
  299. package/dist/utils/groupItems.js +9 -9
  300. package/dist/utils/index.d.ts +1 -1
  301. package/dist/utils/index.d.ts.map +1 -1
  302. package/dist/utils/isApplePlatform.d.ts +18 -0
  303. package/dist/utils/isApplePlatform.d.ts.map +1 -0
  304. package/dist/utils/isApplePlatform.js +37 -0
  305. package/dist/utils/sharedResizeObserver.d.ts.map +1 -1
  306. package/dist/utils/sharedResizeObserver.js +10 -1
  307. package/dist/utils/timeParser.d.ts +1 -1
  308. package/dist/utils/timeParser.d.ts.map +1 -1
  309. package/dist/utils/timeParser.js +26 -12
  310. package/locales/en.json +31 -7
  311. package/locales/pseudo.json +18 -0
  312. package/package.json +8 -3
  313. package/src/AppShell/AppShell.tsx +3 -2
  314. package/src/AspectRatio/AspectRatio.doc.mjs +6 -3
  315. package/src/AspectRatio/AspectRatio.test.tsx +57 -10
  316. package/src/AspectRatio/AspectRatio.tsx +40 -1
  317. package/src/Avatar/Avatar.doc.mjs +14 -3
  318. package/src/Avatar/Avatar.test.tsx +97 -0
  319. package/src/Avatar/Avatar.tsx +125 -52
  320. package/src/Avatar/index.ts +2 -2
  321. package/src/AvatarGroup/AvatarGroup.doc.mjs +8 -2
  322. package/src/AvatarGroup/AvatarGroup.test.tsx +24 -0
  323. package/src/AvatarGroup/AvatarGroup.tsx +15 -6
  324. package/src/AvatarGroup/AvatarGroupContext.ts +5 -3
  325. package/src/AvatarGroup/AvatarGroupOverflow.tsx +10 -4
  326. package/src/Banner/Banner.doc.mjs +13 -11
  327. package/src/Banner/Banner.test.tsx +102 -15
  328. package/src/Banner/Banner.tsx +72 -25
  329. package/src/BottomSheet/BottomSheet.test.tsx +123 -0
  330. package/src/BottomSheet/BottomSheet.tsx +17 -4
  331. package/src/BottomSheet/BottomSheetSwitcher.tsx +3 -2
  332. package/src/Breadcrumbs/BreadcrumbItem.tsx +9 -5
  333. package/src/Button/Button.doc.mjs +22 -0
  334. package/src/Button/Button.test.tsx +13 -0
  335. package/src/Button/Button.tsx +5 -4
  336. package/src/ButtonGroup/ButtonGroup.doc.mjs +10 -1
  337. package/src/ButtonGroup/ButtonGroup.test.tsx +391 -1
  338. package/src/ButtonGroup/ButtonGroup.tsx +23 -5
  339. package/src/Calendar/Calendar.doc.mjs +4 -3
  340. package/src/Calendar/Calendar.test.tsx +52 -0
  341. package/src/Calendar/Calendar.tsx +18 -15
  342. package/src/Calendar/getInitialFocusDate.test.ts +94 -0
  343. package/src/Calendar/getInitialFocusDate.ts +93 -0
  344. package/src/Carousel/Carousel.tsx +3 -2
  345. package/src/Chat/ChatComposerInput.tsx +5 -1
  346. package/src/Chat/ChatLayout.tsx +3 -2
  347. package/src/Chat/ChatMessageList.doc.mjs +9 -1
  348. package/src/Chat/ChatMessageList.test.tsx +46 -0
  349. package/src/Chat/ChatMessageList.tsx +28 -4
  350. package/src/Chat/ChatTokenizedText.tsx +17 -10
  351. package/src/Chat/useChatComposerTokens.ts +1 -1
  352. package/src/CheckboxInput/CheckboxInput.doc.mjs +2 -0
  353. package/src/CheckboxInput/CheckboxInput.test.tsx +15 -0
  354. package/src/CheckboxInput/CheckboxInput.tsx +8 -2
  355. package/src/ClickableCard/ClickableCard.tsx +3 -2
  356. package/src/CodeBlock/CodeBlock.doc.mjs +16 -4
  357. package/src/CodeBlock/CodeBlock.test.tsx +37 -0
  358. package/src/CodeBlock/CodeBlock.tsx +20 -4
  359. package/src/CodeBlock/highlightRanges.ts +1 -1
  360. package/src/CodeBlock/highlightStyles.ts +13 -13
  361. package/src/CommandPalette/CommandPaletteInput.tsx +3 -2
  362. package/src/CommandPalette/CommandPaletteItem.tsx +3 -2
  363. package/src/ContextMenu/ContextMenu.doc.mjs +5 -0
  364. package/src/ContextMenu/ContextMenu.tsx +4 -3
  365. package/src/DateInput/DateInput.doc.mjs +16 -0
  366. package/src/DateInput/DateInput.tsx +55 -2
  367. package/src/DateInput/DateInputTouch.test.tsx +68 -9
  368. package/src/DateInput/NativeDateField.test.tsx +565 -0
  369. package/src/DateInput/NativeDateField.tsx +623 -0
  370. package/src/DateInput/TouchDateField.tsx +38 -4
  371. package/src/DateInput/index.ts +1 -0
  372. package/src/DateTimeInput/DateTimeInput.doc.mjs +18 -0
  373. package/src/DateTimeInput/DateTimeInput.test.tsx +1088 -0
  374. package/src/DateTimeInput/DateTimeInput.tsx +556 -24
  375. package/src/DateTimeInput/index.ts +1 -0
  376. package/src/Dialog/Dialog.doc.mjs +8 -3
  377. package/src/Dialog/Dialog.test.tsx +261 -1
  378. package/src/Dialog/Dialog.tsx +109 -58
  379. package/src/DropdownMenu/DropdownMenu.doc.mjs +6 -0
  380. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +8 -5
  381. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +8 -11
  382. package/src/Field/Field.doc.mjs +13 -2
  383. package/src/Field/FieldLabel.test.tsx +29 -0
  384. package/src/Field/FieldLabel.tsx +7 -0
  385. package/src/FileInput/FileInput.tsx +3 -2
  386. package/src/Heading/Heading.test.tsx +23 -1
  387. package/src/Heading/Heading.tsx +6 -2
  388. package/src/HoverCard/HoverCard.doc.mjs +13 -2
  389. package/src/HoverCard/HoverCard.test.tsx +101 -4
  390. package/src/HoverCard/HoverCard.tsx +71 -3
  391. package/src/HoverCard/useHoverCard.tsx +101 -33
  392. package/src/Indicator/CheckboxIndicator.tsx +14 -8
  393. package/src/Indicator/Indicator.doc.mjs +2 -0
  394. package/src/Item/Item.tsx +5 -4
  395. package/src/Kbd/Kbd.tsx +2 -19
  396. package/src/Layer/LayerDepthContext.tsx +62 -0
  397. package/src/Layer/index.ts +9 -0
  398. package/src/Layer/layerDismissalFamilies.test.tsx +359 -0
  399. package/src/Layer/layerDismissalInvariants.test.tsx +417 -0
  400. package/src/Layer/layerStack.ts +362 -0
  401. package/src/Layer/useLayerDismissal.test.tsx +469 -0
  402. package/src/Layer/useLayerDismissal.ts +173 -0
  403. package/src/Layout/Layout.doc.mjs +2 -2
  404. package/src/Lightbox/Lightbox.doc.mjs +12 -0
  405. package/src/Lightbox/Lightbox.tsx +124 -102
  406. package/src/Link/Link.doc.mjs +39 -0
  407. package/src/Markdown/Markdown.doc.mjs +5 -0
  408. package/src/Markdown/incremental.test.ts +16 -2
  409. package/src/Markdown/index.ts +1 -0
  410. package/src/Markdown/parser.perf.test.ts +96 -1
  411. package/src/Markdown/parser.test.ts +145 -2
  412. package/src/Markdown/parser.ts +244 -29
  413. package/src/MobileNav/MobileNav.tsx +62 -36
  414. package/src/MoreMenu/MoreMenu.doc.mjs +7 -1
  415. package/src/MultiSelector/MultiSelector.doc.mjs +27 -0
  416. package/src/MultiSelector/MultiSelector.test.tsx +106 -0
  417. package/src/MultiSelector/MultiSelector.tsx +50 -6
  418. package/src/MultiSelector/index.ts +1 -0
  419. package/src/NavIcon/NavIcon.doc.mjs +8 -2
  420. package/src/NavIcon/NavIcon.test.tsx +9 -0
  421. package/src/NavIcon/NavIcon.tsx +5 -1
  422. package/src/NavMenu/NavHeadingMenu.tsx +3 -2
  423. package/src/NumberInput/NumberInput.doc.mjs +20 -0
  424. package/src/NumberInput/NumberInput.tsx +28 -29
  425. package/src/NumberInput/numberParser.test.ts +137 -0
  426. package/src/NumberInput/numberParser.ts +346 -0
  427. package/src/Outline/Outline.tsx +3 -2
  428. package/src/OverflowList/OverflowList.tsx +3 -2
  429. package/src/Overlay/Overlay.tsx +3 -2
  430. package/src/Popover/usePopover.tsx +31 -28
  431. package/src/PowerSearch/PowerSearch.doc.mjs +160 -28
  432. package/src/PowerSearch/PowerSearch.test.tsx +240 -1
  433. package/src/PowerSearch/PowerSearch.tsx +34 -5
  434. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +201 -0
  435. package/src/PowerSearch/PowerSearchEditPopover.tsx +29 -2
  436. package/src/PowerSearch/PowerSearchValueEditor.test.tsx +47 -1
  437. package/src/PowerSearch/PowerSearchValueEditor.tsx +15 -0
  438. package/src/PowerSearch/types.ts +2 -0
  439. package/src/PowerSearch/usePowerSearchSource.test.ts +82 -2
  440. package/src/PowerSearch/usePowerSearchSource.ts +13 -3
  441. package/src/ProgressBar/ProgressBar.doc.mjs +42 -6
  442. package/src/ProgressBar/ProgressBar.test.tsx +24 -0
  443. package/src/ProgressBar/ProgressBar.tsx +28 -8
  444. package/src/Resizable/ResizeHandle.tsx +3 -2
  445. package/src/SegmentedControl/SegmentedControl.tsx +3 -2
  446. package/src/SelectableCard/SelectableCard.tsx +3 -2
  447. package/src/Selector/Selector.doc.mjs +12 -1
  448. package/src/Selector/Selector.test.tsx +45 -1
  449. package/src/Selector/Selector.tsx +11 -1
  450. package/src/SideNav/SideNav.doc.mjs +6 -6
  451. package/src/SideNav/SideNav.tsx +4 -2
  452. package/src/SideNav/SideNavHeading.tsx +12 -12
  453. package/src/SideNav/SideNavItem.tsx +4 -2
  454. package/src/Slider/Slider.tsx +3 -2
  455. package/src/Spinner/Spinner.test.tsx +147 -1
  456. package/src/Spinner/Spinner.tsx +120 -101
  457. package/src/StatusDot/StatusDot.doc.mjs +8 -2
  458. package/src/StatusDot/StatusDot.test.tsx +9 -0
  459. package/src/StatusDot/StatusDot.tsx +7 -1
  460. package/src/Stepper/Step.doc.mjs +98 -0
  461. package/src/Stepper/Step.tsx +1638 -0
  462. package/src/Stepper/StepStatus.ts +26 -0
  463. package/src/Stepper/Stepper.doc.mjs +370 -0
  464. package/src/Stepper/Stepper.test.tsx +1024 -0
  465. package/src/Stepper/Stepper.tsx +258 -0
  466. package/src/Stepper/StepperContext.ts +71 -0
  467. package/src/Stepper/index.ts +18 -0
  468. package/src/Stepper/stepper.stylex.ts +19 -0
  469. package/src/Switch/Switch.doc.mjs +2 -0
  470. package/src/Switch/Switch.test.tsx +10 -0
  471. package/src/Switch/Switch.tsx +6 -2
  472. package/src/TabList/Tab.doc.mjs +13 -1
  473. package/src/TabList/Tab.tsx +67 -7
  474. package/src/TabList/TabList.doc.mjs +22 -6
  475. package/src/TabList/TabList.test.tsx +559 -0
  476. package/src/TabList/TabList.tsx +490 -22
  477. package/src/TabList/TabListContext.ts +13 -2
  478. package/src/TabList/TabMenu.tsx +3 -2
  479. package/src/TabList/index.ts +6 -2
  480. package/src/Table/BaseTable.tsx +12 -4
  481. package/src/Table/Table.doc.mjs +9 -2
  482. package/src/Table/Table.test.tsx +132 -14
  483. package/src/Table/Table.tsx +3 -8
  484. package/src/Table/TableBody.doc.mjs +40 -0
  485. package/src/Table/TableFooter.doc.mjs +40 -0
  486. package/src/Table/TableHeader.doc.mjs +40 -0
  487. package/src/Table/TableRow.tsx +16 -5
  488. package/src/Table/plugins/groupedRows/useTableGroupedRows-perf.test.tsx +112 -0
  489. package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +100 -0
  490. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +17 -8
  491. package/src/Table/plugins/pagination/useTablePagination.test.tsx +34 -4
  492. package/src/Table/plugins/pagination/useTablePagination.tsx +35 -3
  493. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +9 -2
  494. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +13 -7
  495. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +12 -3
  496. package/src/Table/plugins/selection/useTableSelection.test.tsx +76 -0
  497. package/src/Table/plugins/selection/useTableSelection.tsx +40 -7
  498. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +1 -2
  499. package/src/Table/types.ts +22 -4
  500. package/src/Table/useBaseTablePlugins.ts +5 -0
  501. package/src/Table/useTableGroupedRows.doc.mjs +5 -4
  502. package/src/Table/useTablePagination.doc.mjs +7 -0
  503. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  504. package/src/Table/useTableSelection.doc.mjs +31 -0
  505. package/src/Text/Text.test.tsx +23 -1
  506. package/src/Text/Text.tsx +6 -2
  507. package/src/TextArea/TextArea.doc.mjs +8 -2
  508. package/src/TextArea/TextArea.test.tsx +11 -0
  509. package/src/TextArea/TextArea.tsx +15 -8
  510. package/src/TextInput/TextInput.doc.mjs +10 -0
  511. package/src/TextInput/TextInput.tsx +3 -2
  512. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  513. package/src/TimeInput/TimeInput.tsx +2 -2
  514. package/src/Timestamp/Timestamp.tsx +4 -2
  515. package/src/Toast/Toast.doc.mjs +6 -0
  516. package/src/Token/TokenLink.tsx +2 -2
  517. package/src/Tokenizer/Tokenizer.doc.mjs +27 -0
  518. package/src/Tokenizer/Tokenizer.tsx +4 -0
  519. package/src/Toolbar/Toolbar.doc.mjs +5 -0
  520. package/src/Tooltip/Tooltip.doc.mjs +5 -0
  521. package/src/Tooltip/Tooltip.test.tsx +32 -0
  522. package/src/Tooltip/Tooltip.tsx +5 -0
  523. package/src/Tooltip/useTooltip.tsx +47 -24
  524. package/src/TopNav/TopNavHeading.tsx +5 -4
  525. package/src/TopNav/TopNavMegaMenu.tsx +3 -2
  526. package/src/TopNav/TopNavMenu.tsx +4 -3
  527. package/src/Typeahead/BaseTypeahead.doc.mjs +11 -0
  528. package/src/Typeahead/BaseTypeahead.tsx +85 -33
  529. package/src/Typeahead/Typeahead.doc.mjs +5 -0
  530. package/src/Typeahead/Typeahead.test.tsx +49 -0
  531. package/src/Typeahead/Typeahead.tsx +3 -2
  532. package/src/hooks/index.ts +2 -0
  533. package/src/hooks/useContainerReveal.doc.mjs +2 -2
  534. package/src/hooks/useFocusTrap.test.tsx +14 -0
  535. package/src/hooks/useFocusTrap.ts +62 -111
  536. package/src/hooks/useFocusTrapEscapeShim.test.tsx +165 -0
  537. package/src/hooks/useHotkeys.ts +3 -18
  538. package/src/hooks/useMergedRefs.doc.mjs +79 -0
  539. package/src/hooks/useMergedRefs.test.tsx +62 -0
  540. package/src/hooks/useMergedRefs.ts +36 -0
  541. package/src/index.ts +1 -0
  542. package/src/theme/MediaTheme.doc.mjs +5 -5
  543. package/src/theme/derivedVarRegistry.test.ts +19 -3
  544. package/src/theme/derivedVarRegistry.ts +23 -4
  545. package/src/theme/generateThemeRules.test.ts +37 -0
  546. package/src/theme/syntax/tokens.ts +5 -2
  547. package/src/utils/groupItems.ts +12 -6
  548. package/src/utils/index.ts +1 -1
  549. package/src/utils/isApplePlatform.test.ts +61 -0
  550. package/src/utils/isApplePlatform.ts +37 -0
  551. package/src/utils/sharedResizeObserver.ts +10 -2
  552. package/src/utils/timeParser.test.ts +6 -1
  553. package/src/utils/timeParser.ts +35 -13
  554. package/src/__tests__/inputWidthContract.test.tsx +0 -69
@@ -0,0 +1,565 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file NativeDateField.test.tsx
5
+ * @input Uses vitest, @testing-library/react, DateInput
6
+ * @output Unit tests for the OS-picker surface (`nativePicker`)
7
+ * @position Testing; validates NativeDateField.tsx
8
+ *
9
+ * Every test renders through `DateInput` with a pointer stubbed, so the
10
+ * surface selection in DateInput.tsx is exercised too. `nativePicker`
11
+ * defaults to `'touch'`, so a coarse pointer alone gets the native control.
12
+ *
13
+ * The iOS behaviours these pin — the picker detaching from the field on a
14
+ * programmatic write, React's synthetic change not firing for the picker's
15
+ * edits, `text-overflow` being inert on a flex box — were all measured on a
16
+ * real device and none reproduce in jsdom. Where a test asserts a CSS rule
17
+ * rather than a behaviour, that is why.
18
+ *
19
+ * SYNC: When NativeDateField.tsx changes, update tests to match new behavior
20
+ */
21
+
22
+ import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
23
+ import {render, screen, fireEvent} from '@testing-library/react';
24
+ import {DateInput} from './DateInput';
25
+ import {InternationalizationProvider} from '../i18n';
26
+
27
+ const HOVER_CAPABLE = /\(\s*hover\s*:\s*hover\s*\)/;
28
+
29
+ /**
30
+ * Astryx's touch picker mounts a month scroller that observes its own size;
31
+ * jsdom ships no ResizeObserver. Only the `nativePicker="never"` test reaches
32
+ * it, but stubbing it for the file keeps that test honest about which surface
33
+ * it got.
34
+ */
35
+ class MockResizeObserver {
36
+ observe() {}
37
+ unobserve() {}
38
+ disconnect() {}
39
+ }
40
+
41
+ /** Point `(pointer: coarse)` at a touch or mouse device. */
42
+ function stubPointer(isCoarse: boolean): void {
43
+ vi.stubGlobal('matchMedia', (query: string) => ({
44
+ matches: /pointer:\s*coarse/.test(query)
45
+ ? isCoarse
46
+ : /pointer:\s*fine/.test(query)
47
+ ? !isCoarse
48
+ : HOVER_CAPABLE.test(query),
49
+ media: query,
50
+ onchange: null,
51
+ addListener: () => {},
52
+ removeListener: () => {},
53
+ addEventListener: () => {},
54
+ removeEventListener: () => {},
55
+ dispatchEvent: () => false,
56
+ }));
57
+ }
58
+
59
+ function getInput(): HTMLInputElement {
60
+ const input = document.querySelector('input');
61
+ if (!input) {
62
+ throw new Error('DateInput rendered no input element');
63
+ }
64
+ return input;
65
+ }
66
+
67
+ /** The CSS rules that apply to an element, for the paint-level assertions. */
68
+ function rulesFor(el: Element): string {
69
+ const classes = new Set(el.className.split(/\s+/).filter(Boolean));
70
+ return Array.from(document.styleSheets)
71
+ .flatMap(sheet => {
72
+ try {
73
+ return Array.from(sheet.cssRules);
74
+ } catch {
75
+ return [];
76
+ }
77
+ })
78
+ .map(rule => rule.cssText)
79
+ .filter(text => [...classes].some(cls => text.includes(`.${cls}`)))
80
+ .join(' ');
81
+ }
82
+
83
+ beforeEach(() => {
84
+ vi.stubGlobal('ResizeObserver', MockResizeObserver);
85
+ });
86
+
87
+ afterEach(() => {
88
+ vi.unstubAllGlobals();
89
+ vi.restoreAllMocks();
90
+ });
91
+
92
+ describe('DateInput nativePicker', () => {
93
+ // ===========================================================================
94
+ // Which surface renders
95
+ // ===========================================================================
96
+
97
+ it('renders the native control on touch by default', () => {
98
+ stubPointer(true);
99
+ render(<DateInput label="Date" onChange={() => {}} />);
100
+
101
+ expect(getInput()).toHaveAttribute('type', 'date');
102
+ });
103
+
104
+ it('keeps the calendar popover on a mouse-driven device', () => {
105
+ stubPointer(false);
106
+ render(<DateInput label="Date" onChange={() => {}} />);
107
+
108
+ const input = getInput();
109
+ expect(input).toHaveAttribute('type', 'text');
110
+ expect(input).toHaveAttribute('role', 'combobox');
111
+ });
112
+
113
+ it('falls back to Astryx\u2019s touch picker when told never', () => {
114
+ stubPointer(true);
115
+ render(<DateInput label="Date" nativePicker="never" onChange={() => {}} />);
116
+
117
+ const input = getInput();
118
+ expect(input).toHaveAttribute('type', 'text');
119
+ expect(input).toHaveAttribute('role', 'combobox');
120
+ });
121
+
122
+ it('uses the native control on a mouse device when told always', () => {
123
+ stubPointer(false);
124
+ render(
125
+ <DateInput label="Date" nativePicker="always" onChange={() => {}} />,
126
+ );
127
+
128
+ expect(getInput()).toHaveAttribute('type', 'date');
129
+ });
130
+
131
+ it('drops the popup ARIA the other surfaces carry', () => {
132
+ // There is no in-page popup to describe: the OS draws the picker.
133
+ stubPointer(true);
134
+ render(<DateInput label="Date" onChange={() => {}} />);
135
+
136
+ const input = getInput();
137
+ expect(input).not.toHaveAttribute('role', 'combobox');
138
+ expect(input).not.toHaveAttribute('aria-expanded');
139
+ expect(input).not.toHaveAttribute('aria-haspopup');
140
+ });
141
+
142
+ // ===========================================================================
143
+ // Value round-trip
144
+ // ===========================================================================
145
+
146
+ it('keeps the control\u2019s own value ISO', () => {
147
+ stubPointer(true);
148
+ render(
149
+ <DateInput
150
+ label="Date"
151
+ value="2026-01-25"
152
+ format="date_long"
153
+ onChange={() => {}}
154
+ />,
155
+ );
156
+
157
+ // ISO is the only form the control accepts, and what the picker reads and
158
+ // writes; `format` rides on the overlay instead.
159
+ expect(getInput()).toHaveValue('2026-01-25');
160
+ expect(screen.getByText('January 25, 2026')).toBeInTheDocument();
161
+ });
162
+
163
+ it('fires onChange with the ISO date the control reports', () => {
164
+ stubPointer(true);
165
+ const onChange = vi.fn();
166
+ render(<DateInput label="Date" onChange={onChange} />);
167
+
168
+ fireEvent.change(getInput(), {target: {value: '2026-03-21'}});
169
+
170
+ expect(onChange).toHaveBeenCalledExactlyOnceWith('2026-03-21');
171
+ });
172
+
173
+ it('fires onChange with undefined when the control is emptied', () => {
174
+ stubPointer(true);
175
+ const onChange = vi.fn();
176
+ render(
177
+ <DateInput label="Date" value="2026-03-21" onChange={onChange} />,
178
+ );
179
+
180
+ fireEvent.change(getInput(), {target: {value: ''}});
181
+
182
+ expect(onChange).toHaveBeenCalledWith(undefined);
183
+ });
184
+
185
+ it('commits an edit React\u2019s synthetic change does not see', () => {
186
+ // The iOS failure, reproduced: the picker edits the field and a native
187
+ // `input` event fires, but React's synthetic `onChange` never runs — so
188
+ // React re-renders and writes its stale value back over the picker's.
189
+ // Assigning `.value` first updates React's internal value tracker, which
190
+ // is what makes React skip the synthetic event; the native listener still
191
+ // sees the real one.
192
+ stubPointer(true);
193
+ const onChange = vi.fn();
194
+ render(
195
+ <DateInput label="Date" value="2026-03-21" onChange={onChange} />,
196
+ );
197
+
198
+ const input = getInput();
199
+ input.value = '2026-03-09';
200
+ input.dispatchEvent(new Event('input', {bubbles: true}));
201
+
202
+ expect(onChange).toHaveBeenCalledExactlyOnceWith('2026-03-09');
203
+ });
204
+
205
+ it('fires one change when both commit paths see the same edit', () => {
206
+ stubPointer(true);
207
+ const onChange = vi.fn();
208
+ render(
209
+ <DateInput label="Date" value="2026-03-21" onChange={onChange} />,
210
+ );
211
+
212
+ fireEvent.change(getInput(), {target: {value: '2026-03-09'}});
213
+
214
+ expect(onChange).toHaveBeenCalledExactlyOnceWith('2026-03-09');
215
+ });
216
+
217
+ it('does not write to the control while it has focus', () => {
218
+ // The iOS bug this guards: while the picker sheet is open, ANY
219
+ // programmatic write to the field detaches the sheet from it, and the
220
+ // user's pick silently stops reaching the input.
221
+ stubPointer(true);
222
+ const {rerender} = render(
223
+ <DateInput label="Date" value="2026-03-21" onChange={() => {}} />,
224
+ );
225
+ const input = getInput();
226
+
227
+ fireEvent.focus(input);
228
+ rerender(
229
+ <DateInput label="Date" value="2026-12-25" onChange={() => {}} />,
230
+ );
231
+
232
+ expect(input).toHaveValue('2026-03-21');
233
+ });
234
+
235
+ it('applies an external value once the control loses focus', () => {
236
+ stubPointer(true);
237
+ const {rerender} = render(
238
+ <DateInput label="Date" value="2026-03-21" onChange={() => {}} />,
239
+ );
240
+ const input = getInput();
241
+
242
+ fireEvent.focus(input);
243
+ rerender(
244
+ <DateInput label="Date" value="2026-12-25" onChange={() => {}} />,
245
+ );
246
+ fireEvent.blur(input);
247
+
248
+ expect(input).toHaveValue('2026-12-25');
249
+ });
250
+
251
+ // ===========================================================================
252
+ // Constraints
253
+ // ===========================================================================
254
+
255
+ it('forwards min and max to the native control', () => {
256
+ stubPointer(true);
257
+ render(
258
+ <DateInput
259
+ label="Date"
260
+ min="2026-01-01"
261
+ max="2026-12-31"
262
+ onChange={() => {}}
263
+ />,
264
+ );
265
+
266
+ const input = getInput();
267
+ expect(input).toHaveAttribute('min', '2026-01-01');
268
+ expect(input).toHaveAttribute('max', '2026-12-31');
269
+ });
270
+
271
+ it('refuses an out-of-range date and announces it', () => {
272
+ // iOS does not enforce min/max in its picker — it lets the user land on
273
+ // any date — so the refusal has to happen here.
274
+ stubPointer(true);
275
+ const onChange = vi.fn();
276
+ render(
277
+ <DateInput
278
+ label="Date"
279
+ min="2026-03-10"
280
+ max="2026-03-20"
281
+ value="2026-03-15"
282
+ onChange={onChange}
283
+ />,
284
+ );
285
+
286
+ fireEvent.change(getInput(), {target: {value: '2026-03-25'}});
287
+
288
+ expect(onChange).not.toHaveBeenCalled();
289
+ expect(screen.getByRole('alert')).toHaveTextContent('Invalid date');
290
+ expect(getInput()).toHaveAttribute('aria-invalid', 'true');
291
+ });
292
+
293
+ it('refuses a date dateConstraints rejects', () => {
294
+ stubPointer(true);
295
+ const onChange = vi.fn();
296
+ render(
297
+ <DateInput
298
+ label="Date"
299
+ // 2026-03-22 is a Sunday.
300
+ dateConstraints={[date => date.getDay() !== 0]}
301
+ value="2026-03-23"
302
+ onChange={onChange}
303
+ />,
304
+ );
305
+
306
+ fireEvent.change(getInput(), {target: {value: '2026-03-22'}});
307
+
308
+ expect(onChange).not.toHaveBeenCalled();
309
+ expect(screen.getByRole('alert')).toHaveTextContent('Invalid date');
310
+ });
311
+
312
+ it('drops the rejection once the field reverts', () => {
313
+ // The refused date is reverted the moment focus leaves, so the field is
314
+ // showing a valid date again. Marking that date invalid would be a lie
315
+ // about data the user never chose.
316
+ stubPointer(true);
317
+ render(
318
+ <DateInput
319
+ label="Date"
320
+ dateConstraints={[date => date.getDay() !== 0]}
321
+ value="2026-03-23"
322
+ onChange={() => {}}
323
+ />,
324
+ );
325
+
326
+ const input = getInput();
327
+ fireEvent.focus(input);
328
+ fireEvent.change(input, {target: {value: '2026-03-22'}});
329
+ expect(input).toHaveAttribute('aria-invalid', 'true');
330
+
331
+ fireEvent.blur(input);
332
+
333
+ expect(input).toHaveValue('2026-03-23');
334
+ expect(input).not.toHaveAttribute('aria-invalid');
335
+ expect(screen.getByRole('alert')).toHaveTextContent('');
336
+ });
337
+
338
+ // ===========================================================================
339
+ // The text overlay: format, placeholder, paint
340
+ // ===========================================================================
341
+
342
+ it('honours every named format', () => {
343
+ stubPointer(true);
344
+ const {rerender} = render(
345
+ <DateInput
346
+ label="Date"
347
+ value="2026-01-25"
348
+ format="date"
349
+ onChange={() => {}}
350
+ />,
351
+ );
352
+ expect(screen.getByText('Jan 25, 2026')).toBeInTheDocument();
353
+
354
+ rerender(
355
+ <DateInput
356
+ label="Date"
357
+ value="2026-01-25"
358
+ format="date_weekday"
359
+ onChange={() => {}}
360
+ />,
361
+ );
362
+ expect(screen.getByText('Sun, Jan 25, 2026')).toBeInTheDocument();
363
+ });
364
+
365
+ it('honours a function format', () => {
366
+ stubPointer(true);
367
+ render(
368
+ <DateInput
369
+ label="Date"
370
+ value="2026-01-25"
371
+ format={iso => `ships ${iso}`}
372
+ onChange={() => {}}
373
+ />,
374
+ );
375
+
376
+ expect(screen.getByText('ships 2026-01-25')).toBeInTheDocument();
377
+ });
378
+
379
+ it('formats in the provider locale', () => {
380
+ stubPointer(true);
381
+ render(
382
+ <InternationalizationProvider locale="de-DE">
383
+ <DateInput label="Date" value="2026-03-21" onChange={() => {}} />
384
+ </InternationalizationProvider>,
385
+ );
386
+
387
+ expect(screen.getByText('21. März 2026')).toBeInTheDocument();
388
+ expect(getInput()).toHaveValue('2026-03-21');
389
+ });
390
+
391
+ it('keeps painting the value while the picker is open', () => {
392
+ // The OS picker has no segments to reveal, so `format` holds throughout.
393
+ stubPointer(true);
394
+ render(
395
+ <DateInput label="Date" value="2026-01-25" onChange={() => {}} />,
396
+ );
397
+
398
+ fireEvent.focus(getInput());
399
+
400
+ expect(screen.getByText('January 25, 2026')).toBeInTheDocument();
401
+ });
402
+
403
+ it('shows the placeholder when empty, and drops it when filled', () => {
404
+ stubPointer(true);
405
+ const {rerender} = render(
406
+ <DateInput label="Date" onChange={() => {}} />,
407
+ );
408
+ expect(screen.getByText('Select a date')).toBeInTheDocument();
409
+
410
+ rerender(
411
+ <DateInput label="Date" value="2026-01-25" onChange={() => {}} />,
412
+ );
413
+
414
+ expect(screen.queryByText('Select a date')).toBeNull();
415
+ });
416
+
417
+ it('keeps the overlay out of the accessibility tree', () => {
418
+ // The input still holds the value and carries the label, so announcing
419
+ // the overlay too would double-speak.
420
+ stubPointer(true);
421
+ render(
422
+ <DateInput label="Date" value="2026-01-25" onChange={() => {}} />,
423
+ );
424
+
425
+ expect(screen.getByText('January 25, 2026')).toHaveAttribute(
426
+ 'aria-hidden',
427
+ 'true',
428
+ );
429
+ });
430
+
431
+ it('hides the engine\u2019s own text under the overlay', () => {
432
+ stubPointer(true);
433
+ render(
434
+ <DateInput label="Date" value="2026-01-25" onChange={() => {}} />,
435
+ );
436
+
437
+ const rules = rulesFor(getInput());
438
+ // One transparent colour covers Chromium's `::-webkit-datetime-edit` and
439
+ // Firefox's plain text; `-webkit-text-fill-color` is what wins in WebKit.
440
+ expect(rules).toContain('color: transparent');
441
+ expect(rules).toContain('-webkit-text-fill-color: transparent');
442
+ });
443
+
444
+ it('bounds the overlay so a long date cannot paint past it', () => {
445
+ stubPointer(true);
446
+ render(
447
+ <DateInput
448
+ label="Date"
449
+ value="2026-09-30"
450
+ hasClear
451
+ onChange={() => {}}
452
+ />,
453
+ );
454
+
455
+ const rules = rulesFor(screen.getByText('September 30, 2026'));
456
+ // Without the end inset the overlay is shrink-to-fit and a long date runs
457
+ // over the clear button — measured 24px across it on an iPhone.
458
+ expect(rules).toContain('inset-inline-end: 0');
459
+ // `text-overflow` only applies to a BLOCK container: on a flex one a
460
+ // too-long date hard-clips mid-glyph instead (measured identical to
461
+ // `text-overflow: clip` in WebKit and Chromium). Centring then comes from
462
+ // the line box, so the overlay must carry the input's own leading.
463
+ expect(rules).toContain('display: block');
464
+ expect(rules).toContain('text-overflow: ellipsis');
465
+ expect(rules).toContain('line-height: var(--text-body-leading)');
466
+ });
467
+
468
+ // ===========================================================================
469
+ // Toggle, clear, disabled
470
+ // ===========================================================================
471
+
472
+ it('asks the browser for its picker from the toggle button', () => {
473
+ stubPointer(true);
474
+ render(<DateInput label="Date" onChange={() => {}} />);
475
+
476
+ const input = getInput();
477
+ const showPicker = vi.fn();
478
+ // jsdom implements no picker; attach one so the call is observable.
479
+ (input as HTMLInputElement & {showPicker: () => void}).showPicker =
480
+ showPicker;
481
+
482
+ fireEvent.click(screen.getByLabelText('Open calendar'));
483
+
484
+ expect(showPicker).toHaveBeenCalledTimes(1);
485
+ expect(input).toHaveFocus();
486
+ });
487
+
488
+ it('survives a browser that refuses showPicker', () => {
489
+ // Chrome throws NotAllowedError without transient user activation, and
490
+ // iOS implements no showPicker for type=date at all. Focus is the
491
+ // fallback, and on iOS it is the whole mechanism.
492
+ stubPointer(true);
493
+ render(<DateInput label="Date" onChange={() => {}} />);
494
+
495
+ const input = getInput();
496
+ (input as HTMLInputElement & {showPicker: () => void}).showPicker = () => {
497
+ throw new DOMException('not allowed', 'NotAllowedError');
498
+ };
499
+
500
+ expect(() =>
501
+ fireEvent.click(screen.getByLabelText('Open calendar')),
502
+ ).not.toThrow();
503
+ expect(input).toHaveFocus();
504
+ });
505
+
506
+ it('clears the value without taking focus back', () => {
507
+ // Focusing a date control is what raises the OS picker, so reclaiming
508
+ // focus would pop the wheel the clear tap just dismissed.
509
+ stubPointer(true);
510
+ const onChange = vi.fn();
511
+ render(
512
+ <DateInput
513
+ label="Date"
514
+ value="2026-03-21"
515
+ hasClear
516
+ onChange={onChange}
517
+ />,
518
+ );
519
+
520
+ fireEvent.click(screen.getByLabelText('Clear Date'));
521
+
522
+ expect(onChange).toHaveBeenCalledWith(undefined);
523
+ expect(getInput()).not.toHaveFocus();
524
+ });
525
+
526
+ it('disables the control and its toggle when isDisabled', () => {
527
+ stubPointer(true);
528
+ render(<DateInput label="Date" isDisabled onChange={() => {}} />);
529
+
530
+ expect(getInput()).toBeDisabled();
531
+ expect(screen.getByLabelText('Open calendar')).toBeDisabled();
532
+ });
533
+
534
+ it('ignores a change while disabled', () => {
535
+ stubPointer(true);
536
+ const onChange = vi.fn();
537
+ render(
538
+ <DateInput
539
+ label="Date"
540
+ isDisabled
541
+ disabledMessage="Ask an editor"
542
+ onChange={onChange}
543
+ />,
544
+ );
545
+
546
+ // With a disabledMessage the field stays focusable via aria-disabled, so
547
+ // the mutation guard is what has to hold.
548
+ fireEvent.change(getInput(), {target: {value: '2026-03-21'}});
549
+
550
+ expect(onChange).not.toHaveBeenCalled();
551
+ });
552
+
553
+ it('keeps the field labelled, required-marked, and ref-forwarded', () => {
554
+ stubPointer(true);
555
+ const ref = vi.fn();
556
+ render(
557
+ <DateInput ref={ref} label="Event date" isRequired onChange={() => {}} />,
558
+ );
559
+
560
+ const input = screen.getByLabelText(/Event date/);
561
+ expect(input).toHaveAttribute('type', 'date');
562
+ expect(input).toHaveAttribute('aria-required', 'true');
563
+ expect(ref).toHaveBeenCalledWith(expect.any(HTMLInputElement));
564
+ });
565
+ });