@astryxdesign/core 0.4.7 → 0.5.0-canary.055e75f

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 (597) hide show
  1. package/CHANGELOG.md +161 -1
  2. package/dist/AlertDialog/AlertDialog.d.ts +1 -1
  3. package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
  4. package/dist/AlertDialog/AlertDialog.js +45 -20
  5. package/dist/AppShell/AppShell.d.ts.map +1 -1
  6. package/dist/AppShell/AppShell.js +3 -2
  7. package/dist/AspectRatio/AspectRatio.d.ts +24 -0
  8. package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
  9. package/dist/AspectRatio/AspectRatio.js +23 -4
  10. package/dist/Avatar/Avatar.d.ts +25 -1
  11. package/dist/Avatar/Avatar.d.ts.map +1 -1
  12. package/dist/Avatar/Avatar.js +58 -19
  13. package/dist/Avatar/index.d.ts +2 -2
  14. package/dist/Avatar/index.d.ts.map +1 -1
  15. package/dist/Avatar/index.js +1 -1
  16. package/dist/AvatarGroup/AvatarGroup.d.ts +9 -2
  17. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  18. package/dist/AvatarGroup/AvatarGroup.js +8 -4
  19. package/dist/AvatarGroup/AvatarGroupContext.d.ts +2 -1
  20. package/dist/AvatarGroup/AvatarGroupContext.d.ts.map +1 -1
  21. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  22. package/dist/AvatarGroup/AvatarGroupOverflow.js +10 -6
  23. package/dist/Banner/Banner.d.ts +40 -15
  24. package/dist/Banner/Banner.d.ts.map +1 -1
  25. package/dist/Banner/Banner.js +51 -18
  26. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  27. package/dist/BottomSheet/BottomSheet.js +17 -4
  28. package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
  29. package/dist/BottomSheet/BottomSheetSwitcher.js +3 -2
  30. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  31. package/dist/Breadcrumbs/BreadcrumbItem.js +11 -10
  32. package/dist/Button/Button.d.ts.map +1 -1
  33. package/dist/Button/Button.js +11 -6
  34. package/dist/ButtonGroup/ButtonGroup.d.ts +5 -1
  35. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  36. package/dist/ButtonGroup/ButtonGroup.js +26 -7
  37. package/dist/Calendar/Calendar.d.ts +3 -1
  38. package/dist/Calendar/Calendar.d.ts.map +1 -1
  39. package/dist/Calendar/Calendar.js +14 -15
  40. package/dist/Calendar/getInitialFocusDate.d.ts +46 -0
  41. package/dist/Calendar/getInitialFocusDate.d.ts.map +1 -0
  42. package/dist/Calendar/getInitialFocusDate.js +62 -0
  43. package/dist/Calendar/styles.d.ts +5 -14
  44. package/dist/Calendar/styles.d.ts.map +1 -1
  45. package/dist/Calendar/styles.js +15 -15
  46. package/dist/Card/Card.d.ts +6 -9
  47. package/dist/Card/Card.d.ts.map +1 -1
  48. package/dist/Card/Card.js +6 -6
  49. package/dist/Carousel/Carousel.d.ts.map +1 -1
  50. package/dist/Carousel/Carousel.js +3 -3
  51. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  52. package/dist/Chat/ChatComposerInput.js +2 -3
  53. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  54. package/dist/Chat/ChatLayout.js +3 -3
  55. package/dist/Chat/ChatMessageList.d.ts +22 -3
  56. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  57. package/dist/Chat/ChatMessageList.js +6 -3
  58. package/dist/Chat/ChatTokenizedText.d.ts +2 -0
  59. package/dist/Chat/ChatTokenizedText.d.ts.map +1 -1
  60. package/dist/Chat/ChatTokenizedText.js +11 -6
  61. package/dist/Chat/useChatComposerTokens.js +1 -1
  62. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  63. package/dist/CheckboxInput/CheckboxInput.js +10 -3
  64. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  65. package/dist/ClickableCard/ClickableCard.js +3 -3
  66. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  67. package/dist/CodeBlock/CodeBlock.js +15 -4
  68. package/dist/CodeBlock/highlightRanges.js +1 -1
  69. package/dist/CodeBlock/highlightStyles.js +13 -13
  70. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  71. package/dist/CommandPalette/CommandPaletteInput.js +3 -2
  72. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  73. package/dist/CommandPalette/CommandPaletteItem.js +3 -2
  74. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  75. package/dist/ContextMenu/ContextMenu.js +4 -3
  76. package/dist/DateInput/DateInput.d.ts +43 -0
  77. package/dist/DateInput/DateInput.d.ts.map +1 -1
  78. package/dist/DateInput/DateInput.js +22 -2
  79. package/dist/DateInput/NativeDateField.d.ts +10 -0
  80. package/dist/DateInput/NativeDateField.d.ts.map +1 -0
  81. package/dist/DateInput/NativeDateField.js +494 -0
  82. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  83. package/dist/DateInput/TouchDateField.js +82 -23
  84. package/dist/DateInput/index.d.ts +1 -1
  85. package/dist/DateInput/index.d.ts.map +1 -1
  86. package/dist/DateInput/nativeDateSegments.d.ts +22 -0
  87. package/dist/DateInput/nativeDateSegments.d.ts.map +1 -0
  88. package/dist/DateInput/nativeDateSegments.js +111 -0
  89. package/dist/DateTimeInput/DateTimeInput.d.ts +21 -1
  90. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  91. package/dist/DateTimeInput/DateTimeInput.js +406 -12
  92. package/dist/DateTimeInput/index.d.ts +1 -1
  93. package/dist/DateTimeInput/index.d.ts.map +1 -1
  94. package/dist/Dialog/Dialog.d.ts +21 -1
  95. package/dist/Dialog/Dialog.d.ts.map +1 -1
  96. package/dist/Dialog/Dialog.js +110 -58
  97. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  98. package/dist/DropdownMenu/DropdownMenuSubMenu.js +6 -5
  99. package/dist/Field/FieldLabel.d.ts.map +1 -1
  100. package/dist/Field/FieldLabel.js +9 -1
  101. package/dist/FileInput/FileInput.d.ts.map +1 -1
  102. package/dist/FileInput/FileInput.js +3 -2
  103. package/dist/Heading/Heading.d.ts.map +1 -1
  104. package/dist/Heading/Heading.js +6 -2
  105. package/dist/HoverCard/HoverCard.d.ts +6 -0
  106. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  107. package/dist/HoverCard/HoverCard.js +104 -3
  108. package/dist/HoverCard/useHoverCard.d.ts +19 -2
  109. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  110. package/dist/HoverCard/useHoverCard.js +74 -26
  111. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
  112. package/dist/Indicator/CheckboxIndicator.js +6 -2
  113. package/dist/Item/Item.d.ts.map +1 -1
  114. package/dist/Item/Item.js +4 -2
  115. package/dist/Kbd/Kbd.d.ts.map +1 -1
  116. package/dist/Kbd/Kbd.js +2 -19
  117. package/dist/Layer/LayerDepthContext.d.ts +52 -0
  118. package/dist/Layer/LayerDepthContext.d.ts.map +1 -0
  119. package/dist/Layer/LayerDepthContext.js +66 -0
  120. package/dist/Layer/index.d.ts +3 -0
  121. package/dist/Layer/index.d.ts.map +1 -1
  122. package/dist/Layer/index.js +4 -0
  123. package/dist/Layer/layerStack.d.ts +98 -0
  124. package/dist/Layer/layerStack.d.ts.map +1 -0
  125. package/dist/Layer/layerStack.js +306 -0
  126. package/dist/Layer/useLayerDismissal.d.ts +93 -0
  127. package/dist/Layer/useLayerDismissal.d.ts.map +1 -0
  128. package/dist/Layer/useLayerDismissal.js +91 -0
  129. package/dist/Lightbox/Lightbox.d.ts +1 -0
  130. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  131. package/dist/Lightbox/Lightbox.js +103 -81
  132. package/dist/Markdown/Markdown.d.ts.map +1 -1
  133. package/dist/Markdown/Markdown.js +9 -4
  134. package/dist/Markdown/index.d.ts +1 -1
  135. package/dist/Markdown/index.d.ts.map +1 -1
  136. package/dist/Markdown/parser.d.ts +38 -1
  137. package/dist/Markdown/parser.d.ts.map +1 -1
  138. package/dist/Markdown/parser.js +182 -32
  139. package/dist/MobileNav/MobileNav.d.ts +6 -1
  140. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  141. package/dist/MobileNav/MobileNav.js +61 -36
  142. package/dist/MultiSelector/MultiSelector.d.ts +32 -1
  143. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  144. package/dist/MultiSelector/MultiSelector.js +63 -11
  145. package/dist/MultiSelector/index.d.ts +1 -1
  146. package/dist/MultiSelector/index.d.ts.map +1 -1
  147. package/dist/NavIcon/NavIcon.d.ts.map +1 -1
  148. package/dist/NavIcon/NavIcon.js +5 -1
  149. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  150. package/dist/NavMenu/NavHeadingMenu.js +3 -2
  151. package/dist/NumberInput/NumberInput.d.ts +6 -1
  152. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  153. package/dist/NumberInput/NumberInput.js +54 -104
  154. package/dist/NumberInput/numberInputCommit.d.ts +34 -0
  155. package/dist/NumberInput/numberInputCommit.d.ts.map +1 -0
  156. package/dist/NumberInput/numberInputCommit.js +71 -0
  157. package/dist/NumberInput/numberParser.d.ts +64 -0
  158. package/dist/NumberInput/numberParser.d.ts.map +1 -0
  159. package/dist/NumberInput/numberParser.js +350 -0
  160. package/dist/Outline/Outline.d.ts.map +1 -1
  161. package/dist/Outline/Outline.js +3 -2
  162. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  163. package/dist/OverflowList/OverflowList.js +3 -2
  164. package/dist/Overlay/Overlay.d.ts.map +1 -1
  165. package/dist/Overlay/Overlay.js +3 -2
  166. package/dist/Popover/usePopover.d.ts.map +1 -1
  167. package/dist/Popover/usePopover.js +20 -17
  168. package/dist/PowerSearch/PowerSearch.d.ts +8 -2
  169. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  170. package/dist/PowerSearch/PowerSearch.js +19 -6
  171. package/dist/PowerSearch/PowerSearchEditPopover.d.ts +3 -1
  172. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  173. package/dist/PowerSearch/PowerSearchEditPopover.js +28 -6
  174. package/dist/PowerSearch/PowerSearchValueEditor.d.ts +3 -1
  175. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  176. package/dist/PowerSearch/PowerSearchValueEditor.js +110 -27
  177. package/dist/PowerSearch/types.d.ts +2 -0
  178. package/dist/PowerSearch/types.d.ts.map +1 -1
  179. package/dist/PowerSearch/usePowerSearchSource.d.ts +6 -1
  180. package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
  181. package/dist/PowerSearch/usePowerSearchSource.js +14 -5
  182. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  183. package/dist/ProgressBar/ProgressBar.js +18 -5
  184. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  185. package/dist/Resizable/ResizeHandle.js +3 -2
  186. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  187. package/dist/SegmentedControl/SegmentedControl.js +3 -2
  188. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  189. package/dist/SelectableCard/SelectableCard.js +3 -3
  190. package/dist/Selector/Selector.d.ts +17 -0
  191. package/dist/Selector/Selector.d.ts.map +1 -1
  192. package/dist/Selector/Selector.js +81 -14
  193. package/dist/SideNav/SideNav.d.ts.map +1 -1
  194. package/dist/SideNav/SideNav.js +4 -3
  195. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  196. package/dist/SideNav/SideNavHeading.js +8 -7
  197. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  198. package/dist/SideNav/SideNavItem.js +4 -3
  199. package/dist/Slider/Slider.d.ts.map +1 -1
  200. package/dist/Slider/Slider.js +37 -5
  201. package/dist/Spinner/Spinner.d.ts +3 -3
  202. package/dist/Spinner/Spinner.d.ts.map +1 -1
  203. package/dist/Spinner/Spinner.js +130 -78
  204. package/dist/StatusDot/StatusDot.d.ts.map +1 -1
  205. package/dist/StatusDot/StatusDot.js +6 -1
  206. package/dist/Stepper/Step.d.ts +140 -0
  207. package/dist/Stepper/Step.d.ts.map +1 -0
  208. package/dist/Stepper/Step.js +1045 -0
  209. package/dist/Stepper/StepStatus.d.ts +24 -0
  210. package/dist/Stepper/StepStatus.d.ts.map +1 -0
  211. package/dist/Stepper/StepStatus.js +1 -0
  212. package/dist/Stepper/Stepper.d.ts +93 -0
  213. package/dist/Stepper/Stepper.d.ts.map +1 -0
  214. package/dist/Stepper/Stepper.js +184 -0
  215. package/dist/Stepper/StepperContext.d.ts +41 -0
  216. package/dist/Stepper/StepperContext.d.ts.map +1 -0
  217. package/dist/Stepper/StepperContext.js +34 -0
  218. package/dist/Stepper/index.d.ts +8 -0
  219. package/dist/Stepper/index.d.ts.map +1 -0
  220. package/dist/Stepper/index.js +7 -0
  221. package/dist/Stepper/stepper.stylex.d.ts +15 -0
  222. package/dist/Stepper/stepper.stylex.d.ts.map +1 -0
  223. package/dist/Stepper/stepper.stylex.js +20 -0
  224. package/dist/Switch/Switch.d.ts.map +1 -1
  225. package/dist/Switch/Switch.js +8 -3
  226. package/dist/TabList/Tab.d.ts +15 -2
  227. package/dist/TabList/Tab.d.ts.map +1 -1
  228. package/dist/TabList/Tab.js +32 -7
  229. package/dist/TabList/TabList.d.ts +46 -7
  230. package/dist/TabList/TabList.d.ts.map +1 -1
  231. package/dist/TabList/TabList.js +353 -23
  232. package/dist/TabList/TabListContext.d.ts +11 -1
  233. package/dist/TabList/TabListContext.d.ts.map +1 -1
  234. package/dist/TabList/TabListContext.js +11 -2
  235. package/dist/TabList/TabMenu.d.ts.map +1 -1
  236. package/dist/TabList/TabMenu.js +3 -2
  237. package/dist/TabList/index.d.ts +2 -2
  238. package/dist/TabList/index.d.ts.map +1 -1
  239. package/dist/Table/BaseTable.d.ts.map +1 -1
  240. package/dist/Table/BaseTable.js +10 -2
  241. package/dist/Table/Table.d.ts.map +1 -1
  242. package/dist/Table/Table.js +3 -6
  243. package/dist/Table/TableRow.d.ts +16 -5
  244. package/dist/Table/TableRow.d.ts.map +1 -1
  245. package/dist/Table/TableRow.js +16 -5
  246. package/dist/Table/plugins/filtering/useTableFiltering.d.ts.map +1 -1
  247. package/dist/Table/plugins/filtering/useTableFiltering.js +73 -4
  248. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  249. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +20 -8
  250. package/dist/Table/plugins/pagination/useTablePagination.d.ts +3 -0
  251. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  252. package/dist/Table/plugins/pagination/useTablePagination.js +21 -4
  253. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  254. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +7 -2
  255. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -1
  256. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +10 -3
  257. package/dist/Table/plugins/selection/useTableSelection.d.ts +16 -0
  258. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  259. package/dist/Table/plugins/selection/useTableSelection.js +19 -5
  260. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  261. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +3 -4
  262. package/dist/Table/types.d.ts +22 -4
  263. package/dist/Table/types.d.ts.map +1 -1
  264. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  265. package/dist/Table/useBaseTablePlugins.js +5 -0
  266. package/dist/Text/Text.d.ts.map +1 -1
  267. package/dist/Text/Text.js +6 -2
  268. package/dist/TextArea/TextArea.d.ts.map +1 -1
  269. package/dist/TextArea/TextArea.js +9 -3
  270. package/dist/TextInput/TextInput.d.ts.map +1 -1
  271. package/dist/TextInput/TextInput.js +3 -2
  272. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  273. package/dist/TimeInput/TimeInput.js +3 -2
  274. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  275. package/dist/Timestamp/Timestamp.js +5 -3
  276. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  277. package/dist/Toast/ToastViewport.js +27 -8
  278. package/dist/Token/TokenLink.d.ts.map +1 -1
  279. package/dist/Token/TokenLink.js +2 -2
  280. package/dist/Tokenizer/Tokenizer.d.ts +3 -1
  281. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  282. package/dist/Tokenizer/Tokenizer.js +2 -0
  283. package/dist/Tooltip/Tooltip.d.ts +5 -0
  284. package/dist/Tooltip/Tooltip.d.ts.map +1 -1
  285. package/dist/Tooltip/useTooltip.d.ts +5 -0
  286. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  287. package/dist/Tooltip/useTooltip.js +39 -24
  288. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  289. package/dist/TopNav/TopNavHeading.js +5 -4
  290. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  291. package/dist/TopNav/TopNavMegaMenu.js +3 -3
  292. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  293. package/dist/TopNav/TopNavMenu.js +4 -3
  294. package/dist/Typeahead/BaseTypeahead.d.ts +2 -0
  295. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  296. package/dist/Typeahead/BaseTypeahead.js +68 -29
  297. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  298. package/dist/Typeahead/Typeahead.js +3 -2
  299. package/dist/astryx.css +98 -7
  300. package/dist/hooks/index.d.ts +1 -0
  301. package/dist/hooks/index.d.ts.map +1 -1
  302. package/dist/hooks/index.js +1 -0
  303. package/dist/hooks/useFocusTrap.d.ts +7 -4
  304. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  305. package/dist/hooks/useFocusTrap.js +60 -95
  306. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  307. package/dist/hooks/useHotkeys.js +3 -18
  308. package/dist/hooks/useMergedRefs.d.ts +18 -0
  309. package/dist/hooks/useMergedRefs.d.ts.map +1 -0
  310. package/dist/hooks/useMergedRefs.js +25 -0
  311. package/dist/index.d.ts +1 -0
  312. package/dist/index.d.ts.map +1 -1
  313. package/dist/index.js +1 -0
  314. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  315. package/dist/theme/derivedVarRegistry.js +24 -4
  316. package/dist/theme/syntax/tokens.d.ts +1 -1
  317. package/dist/theme/syntax/tokens.d.ts.map +1 -1
  318. package/dist/theme/syntax/tokens.js +5 -2
  319. package/dist/utils/groupItems.d.ts +5 -1
  320. package/dist/utils/groupItems.d.ts.map +1 -1
  321. package/dist/utils/groupItems.js +9 -9
  322. package/dist/utils/index.d.ts +1 -1
  323. package/dist/utils/index.d.ts.map +1 -1
  324. package/dist/utils/isApplePlatform.d.ts +18 -0
  325. package/dist/utils/isApplePlatform.d.ts.map +1 -0
  326. package/dist/utils/isApplePlatform.js +37 -0
  327. package/dist/utils/sharedResizeObserver.d.ts.map +1 -1
  328. package/dist/utils/sharedResizeObserver.js +10 -1
  329. package/dist/utils/timeParser.d.ts +1 -1
  330. package/dist/utils/timeParser.d.ts.map +1 -1
  331. package/dist/utils/timeParser.js +26 -12
  332. package/locales/en.json +154 -10
  333. package/locales/pseudo.json +108 -0
  334. package/package.json +8 -3
  335. package/src/AlertDialog/AlertDialog.doc.mjs +114 -30
  336. package/src/AlertDialog/AlertDialog.test.tsx +211 -2
  337. package/src/AlertDialog/AlertDialog.tsx +62 -20
  338. package/src/AppShell/AppShell.tsx +3 -2
  339. package/src/AspectRatio/AspectRatio.doc.mjs +6 -3
  340. package/src/AspectRatio/AspectRatio.test.tsx +57 -10
  341. package/src/AspectRatio/AspectRatio.tsx +40 -1
  342. package/src/Avatar/Avatar.doc.mjs +14 -3
  343. package/src/Avatar/Avatar.test.tsx +97 -0
  344. package/src/Avatar/Avatar.tsx +125 -52
  345. package/src/Avatar/index.ts +2 -2
  346. package/src/AvatarGroup/AvatarGroup.doc.mjs +8 -2
  347. package/src/AvatarGroup/AvatarGroup.test.tsx +24 -0
  348. package/src/AvatarGroup/AvatarGroup.tsx +15 -6
  349. package/src/AvatarGroup/AvatarGroupContext.ts +5 -3
  350. package/src/AvatarGroup/AvatarGroupOverflow.tsx +10 -4
  351. package/src/Banner/Banner.doc.mjs +13 -11
  352. package/src/Banner/Banner.test.tsx +102 -15
  353. package/src/Banner/Banner.tsx +72 -25
  354. package/src/BottomSheet/BottomSheet.test.tsx +123 -0
  355. package/src/BottomSheet/BottomSheet.tsx +17 -4
  356. package/src/BottomSheet/BottomSheetSwitcher.tsx +3 -2
  357. package/src/Breadcrumbs/BreadcrumbItem.tsx +9 -5
  358. package/src/Button/Button.doc.mjs +24 -1
  359. package/src/Button/Button.test.tsx +32 -4
  360. package/src/Button/Button.tsx +12 -5
  361. package/src/ButtonGroup/ButtonGroup.doc.mjs +10 -1
  362. package/src/ButtonGroup/ButtonGroup.test.tsx +404 -1
  363. package/src/ButtonGroup/ButtonGroup.tsx +23 -5
  364. package/src/Calendar/Calendar.doc.mjs +4 -3
  365. package/src/Calendar/Calendar.test.tsx +103 -0
  366. package/src/Calendar/Calendar.tsx +19 -16
  367. package/src/Calendar/getInitialFocusDate.test.ts +94 -0
  368. package/src/Calendar/getInitialFocusDate.ts +93 -0
  369. package/src/Calendar/styles.ts +49 -31
  370. package/src/Card/Card.doc.mjs +10 -7
  371. package/src/Card/Card.test.tsx +44 -19
  372. package/src/Card/Card.tsx +12 -32
  373. package/src/Carousel/Carousel.tsx +3 -2
  374. package/src/Chat/ChatComposerInput.tsx +5 -1
  375. package/src/Chat/ChatLayout.tsx +3 -2
  376. package/src/Chat/ChatMessageList.doc.mjs +9 -1
  377. package/src/Chat/ChatMessageList.test.tsx +46 -0
  378. package/src/Chat/ChatMessageList.tsx +28 -4
  379. package/src/Chat/ChatTokenizedText.tsx +17 -10
  380. package/src/Chat/useChatComposerTokens.ts +1 -1
  381. package/src/CheckboxInput/CheckboxInput.doc.mjs +2 -0
  382. package/src/CheckboxInput/CheckboxInput.test.tsx +15 -0
  383. package/src/CheckboxInput/CheckboxInput.tsx +8 -2
  384. package/src/ClickableCard/ClickableCard.tsx +3 -2
  385. package/src/CodeBlock/CodeBlock.doc.mjs +16 -4
  386. package/src/CodeBlock/CodeBlock.test.tsx +37 -0
  387. package/src/CodeBlock/CodeBlock.tsx +20 -4
  388. package/src/CodeBlock/highlightRanges.ts +1 -1
  389. package/src/CodeBlock/highlightStyles.ts +13 -13
  390. package/src/CommandPalette/CommandPaletteInput.tsx +3 -2
  391. package/src/CommandPalette/CommandPaletteItem.tsx +3 -2
  392. package/src/ContextMenu/ContextMenu.doc.mjs +5 -0
  393. package/src/ContextMenu/ContextMenu.tsx +4 -3
  394. package/src/DateInput/DateInput.doc.mjs +16 -0
  395. package/src/DateInput/DateInput.tsx +55 -2
  396. package/src/DateInput/DateInputTouch.test.tsx +146 -32
  397. package/src/DateInput/NativeDateField.test.tsx +635 -0
  398. package/src/DateInput/NativeDateField.tsx +641 -0
  399. package/src/DateInput/TouchDateField.tsx +108 -24
  400. package/src/DateInput/index.ts +1 -0
  401. package/src/DateInput/nativeDateSegments.ts +127 -0
  402. package/src/DateTimeInput/DateTimeInput.doc.mjs +18 -0
  403. package/src/DateTimeInput/DateTimeInput.test.tsx +1088 -0
  404. package/src/DateTimeInput/DateTimeInput.tsx +556 -24
  405. package/src/DateTimeInput/index.ts +1 -0
  406. package/src/Dialog/Dialog.doc.mjs +8 -3
  407. package/src/Dialog/Dialog.test.tsx +429 -2
  408. package/src/Dialog/Dialog.tsx +133 -58
  409. package/src/DropdownMenu/DropdownMenu.doc.mjs +6 -0
  410. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +8 -5
  411. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +8 -11
  412. package/src/Field/Field.doc.mjs +13 -2
  413. package/src/Field/FieldLabel.test.tsx +29 -0
  414. package/src/Field/FieldLabel.tsx +7 -0
  415. package/src/FileInput/FileInput.tsx +3 -2
  416. package/src/Heading/Heading.test.tsx +23 -1
  417. package/src/Heading/Heading.tsx +6 -2
  418. package/src/HoverCard/HoverCard.doc.mjs +13 -2
  419. package/src/HoverCard/HoverCard.test.tsx +154 -4
  420. package/src/HoverCard/HoverCard.tsx +143 -3
  421. package/src/HoverCard/useHoverCard.tsx +104 -33
  422. package/src/Indicator/CheckboxIndicator.tsx +14 -8
  423. package/src/Indicator/Indicator.doc.mjs +2 -0
  424. package/src/Item/Item.tsx +5 -4
  425. package/src/Kbd/Kbd.tsx +2 -19
  426. package/src/Layer/LayerDepthContext.tsx +62 -0
  427. package/src/Layer/index.ts +9 -0
  428. package/src/Layer/layerDismissalFamilies.test.tsx +359 -0
  429. package/src/Layer/layerDismissalInvariants.test.tsx +417 -0
  430. package/src/Layer/layerStack.ts +362 -0
  431. package/src/Layer/useLayer.test.tsx +62 -14
  432. package/src/Layer/useLayerDismissal.test.tsx +469 -0
  433. package/src/Layer/useLayerDismissal.ts +173 -0
  434. package/src/Layout/Layout.doc.mjs +2 -2
  435. package/src/Lightbox/Lightbox.doc.mjs +12 -0
  436. package/src/Lightbox/Lightbox.tsx +124 -102
  437. package/src/Link/Link.doc.mjs +39 -0
  438. package/src/Markdown/Markdown.doc.mjs +5 -0
  439. package/src/Markdown/Markdown.tsx +9 -4
  440. package/src/Markdown/incremental.test.ts +16 -2
  441. package/src/Markdown/index.ts +1 -0
  442. package/src/Markdown/parser.perf.test.ts +96 -1
  443. package/src/Markdown/parser.test.ts +178 -2
  444. package/src/Markdown/parser.ts +253 -32
  445. package/src/MobileNav/MobileNav.tsx +62 -36
  446. package/src/MoreMenu/MoreMenu.doc.mjs +7 -1
  447. package/src/MultiSelector/MultiSelector.doc.mjs +49 -1
  448. package/src/MultiSelector/MultiSelector.test.tsx +277 -0
  449. package/src/MultiSelector/MultiSelector.tsx +142 -11
  450. package/src/MultiSelector/index.ts +1 -0
  451. package/src/NavIcon/NavIcon.doc.mjs +8 -2
  452. package/src/NavIcon/NavIcon.test.tsx +9 -0
  453. package/src/NavIcon/NavIcon.tsx +5 -1
  454. package/src/NavMenu/NavHeadingMenu.tsx +3 -2
  455. package/src/NumberInput/NumberInput.doc.mjs +31 -7
  456. package/src/NumberInput/NumberInput.test.tsx +436 -8
  457. package/src/NumberInput/NumberInput.tsx +62 -114
  458. package/src/NumberInput/numberInputCommit.test.ts +72 -0
  459. package/src/NumberInput/numberInputCommit.ts +103 -0
  460. package/src/NumberInput/numberParser.docblock.test.ts +234 -0
  461. package/src/NumberInput/numberParser.test.ts +222 -0
  462. package/src/NumberInput/numberParser.ts +403 -0
  463. package/src/Outline/Outline.tsx +3 -2
  464. package/src/OverflowList/OverflowList.tsx +3 -2
  465. package/src/Overlay/Overlay.tsx +3 -2
  466. package/src/Pagination/Pagination.test.tsx +3 -2
  467. package/src/Popover/usePopover.tsx +31 -28
  468. package/src/PowerSearch/PowerSearch.doc.mjs +160 -28
  469. package/src/PowerSearch/PowerSearch.test.tsx +240 -1
  470. package/src/PowerSearch/PowerSearch.tsx +34 -5
  471. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +288 -0
  472. package/src/PowerSearch/PowerSearchEditPopover.tsx +29 -2
  473. package/src/PowerSearch/PowerSearchValueEditor.test.tsx +111 -1
  474. package/src/PowerSearch/PowerSearchValueEditor.tsx +124 -10
  475. package/src/PowerSearch/types.ts +2 -0
  476. package/src/PowerSearch/usePowerSearchSource.test.ts +82 -2
  477. package/src/PowerSearch/usePowerSearchSource.ts +13 -3
  478. package/src/ProgressBar/ProgressBar.doc.mjs +42 -6
  479. package/src/ProgressBar/ProgressBar.test.tsx +24 -0
  480. package/src/ProgressBar/ProgressBar.tsx +28 -8
  481. package/src/Resizable/ResizeHandle.tsx +3 -2
  482. package/src/SegmentedControl/SegmentedControl.tsx +3 -2
  483. package/src/SelectableCard/SelectableCard.tsx +3 -2
  484. package/src/Selector/Selector.doc.mjs +26 -1
  485. package/src/Selector/Selector.test.tsx +311 -1
  486. package/src/Selector/Selector.tsx +139 -16
  487. package/src/SideNav/SideNav.doc.mjs +6 -6
  488. package/src/SideNav/SideNav.tsx +4 -2
  489. package/src/SideNav/SideNavHeading.tsx +12 -12
  490. package/src/SideNav/SideNavItem.tsx +4 -2
  491. package/src/Slider/Slider.test.tsx +106 -1
  492. package/src/Slider/Slider.tsx +55 -3
  493. package/src/Spinner/Spinner.test.tsx +147 -1
  494. package/src/Spinner/Spinner.tsx +120 -101
  495. package/src/StatusDot/StatusDot.doc.mjs +8 -2
  496. package/src/StatusDot/StatusDot.test.tsx +9 -0
  497. package/src/StatusDot/StatusDot.tsx +7 -1
  498. package/src/Stepper/Step.doc.mjs +98 -0
  499. package/src/Stepper/Step.tsx +1638 -0
  500. package/src/Stepper/StepStatus.ts +26 -0
  501. package/src/Stepper/Stepper.doc.mjs +370 -0
  502. package/src/Stepper/Stepper.test.tsx +1024 -0
  503. package/src/Stepper/Stepper.tsx +258 -0
  504. package/src/Stepper/StepperContext.ts +71 -0
  505. package/src/Stepper/index.ts +18 -0
  506. package/src/Stepper/stepper.stylex.ts +19 -0
  507. package/src/Switch/Switch.doc.mjs +2 -0
  508. package/src/Switch/Switch.test.tsx +10 -0
  509. package/src/Switch/Switch.tsx +6 -2
  510. package/src/TabList/Tab.doc.mjs +13 -1
  511. package/src/TabList/Tab.tsx +67 -7
  512. package/src/TabList/TabList.doc.mjs +22 -6
  513. package/src/TabList/TabList.test.tsx +559 -0
  514. package/src/TabList/TabList.tsx +490 -22
  515. package/src/TabList/TabListContext.ts +13 -2
  516. package/src/TabList/TabMenu.tsx +3 -2
  517. package/src/TabList/index.ts +6 -2
  518. package/src/Table/BaseTable.tsx +12 -4
  519. package/src/Table/Table.doc.mjs +9 -2
  520. package/src/Table/Table.test.tsx +132 -14
  521. package/src/Table/Table.tsx +3 -8
  522. package/src/Table/TableBody.doc.mjs +40 -0
  523. package/src/Table/TableFooter.doc.mjs +40 -0
  524. package/src/Table/TableHeader.doc.mjs +40 -0
  525. package/src/Table/TableRow.tsx +16 -5
  526. package/src/Table/plugins/filtering/useTableFiltering.test.tsx +44 -6
  527. package/src/Table/plugins/filtering/useTableFiltering.tsx +100 -4
  528. package/src/Table/plugins/groupedRows/useTableGroupedRows-perf.test.tsx +112 -0
  529. package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +100 -0
  530. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +17 -8
  531. package/src/Table/plugins/pagination/useTablePagination.test.tsx +34 -4
  532. package/src/Table/plugins/pagination/useTablePagination.tsx +35 -3
  533. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +9 -2
  534. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +13 -7
  535. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +12 -3
  536. package/src/Table/plugins/selection/useTableSelection.test.tsx +76 -0
  537. package/src/Table/plugins/selection/useTableSelection.tsx +40 -7
  538. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +1 -2
  539. package/src/Table/types.ts +22 -4
  540. package/src/Table/useBaseTablePlugins.ts +5 -0
  541. package/src/Table/useTableGroupedRows.doc.mjs +5 -4
  542. package/src/Table/useTablePagination.doc.mjs +7 -0
  543. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  544. package/src/Table/useTableSelection.doc.mjs +31 -0
  545. package/src/Text/Text.test.tsx +23 -1
  546. package/src/Text/Text.tsx +6 -2
  547. package/src/TextArea/TextArea.doc.mjs +8 -2
  548. package/src/TextArea/TextArea.test.tsx +11 -0
  549. package/src/TextArea/TextArea.tsx +15 -8
  550. package/src/TextInput/TextInput.doc.mjs +10 -0
  551. package/src/TextInput/TextInput.tsx +3 -2
  552. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  553. package/src/TimeInput/TimeInput.tsx +2 -2
  554. package/src/Timestamp/Timestamp.test.tsx +18 -0
  555. package/src/Timestamp/Timestamp.tsx +4 -2
  556. package/src/Toast/Toast.doc.mjs +6 -0
  557. package/src/Toast/ToastViewport.tsx +17 -1
  558. package/src/Token/TokenLink.tsx +2 -2
  559. package/src/Tokenizer/Tokenizer.doc.mjs +27 -0
  560. package/src/Tokenizer/Tokenizer.tsx +4 -0
  561. package/src/Toolbar/Toolbar.doc.mjs +5 -0
  562. package/src/Tooltip/Tooltip.doc.mjs +5 -0
  563. package/src/Tooltip/Tooltip.test.tsx +32 -0
  564. package/src/Tooltip/Tooltip.tsx +5 -0
  565. package/src/Tooltip/useTooltip.tsx +47 -24
  566. package/src/TopNav/TopNavHeading.tsx +5 -4
  567. package/src/TopNav/TopNavMegaMenu.tsx +3 -2
  568. package/src/TopNav/TopNavMenu.tsx +4 -3
  569. package/src/Typeahead/BaseTypeahead.doc.mjs +11 -0
  570. package/src/Typeahead/BaseTypeahead.tsx +85 -33
  571. package/src/Typeahead/Typeahead.doc.mjs +5 -0
  572. package/src/Typeahead/Typeahead.test.tsx +49 -0
  573. package/src/Typeahead/Typeahead.tsx +3 -2
  574. package/src/hooks/index.ts +2 -0
  575. package/src/hooks/useContainerReveal.doc.mjs +2 -2
  576. package/src/hooks/useFocusTrap.test.tsx +14 -0
  577. package/src/hooks/useFocusTrap.ts +62 -111
  578. package/src/hooks/useFocusTrapEscapeShim.test.tsx +165 -0
  579. package/src/hooks/useHotkeys.ts +3 -18
  580. package/src/hooks/useMergedRefs.doc.mjs +79 -0
  581. package/src/hooks/useMergedRefs.test.tsx +62 -0
  582. package/src/hooks/useMergedRefs.ts +36 -0
  583. package/src/index.ts +1 -0
  584. package/src/raw-import.d.ts +14 -0
  585. package/src/theme/MediaTheme.doc.mjs +5 -5
  586. package/src/theme/derivedVarRegistry.test.ts +19 -3
  587. package/src/theme/derivedVarRegistry.ts +23 -4
  588. package/src/theme/generateThemeRules.test.ts +37 -0
  589. package/src/theme/syntax/tokens.ts +5 -2
  590. package/src/utils/groupItems.ts +12 -6
  591. package/src/utils/index.ts +1 -1
  592. package/src/utils/isApplePlatform.test.ts +61 -0
  593. package/src/utils/isApplePlatform.ts +37 -0
  594. package/src/utils/sharedResizeObserver.ts +10 -2
  595. package/src/utils/timeParser.test.ts +6 -1
  596. package/src/utils/timeParser.ts +35 -13
  597. package/src/__tests__/inputWidthContract.test.tsx +0 -69
@@ -10,9 +10,11 @@
10
10
  */
11
11
 
12
12
  import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
13
+ import {useState} from 'react';
13
14
  import {render, screen, fireEvent, waitFor} from '@testing-library/react';
14
15
  import userEvent from '@testing-library/user-event';
15
16
  import {TestIcon} from '../__tests__/TestIcon';
17
+ import {InternationalizationProvider} from '../i18n';
16
18
  import {InputGroup} from '../InputGroup';
17
19
  import {NumberInput} from './NumberInput';
18
20
  import {defineTheme} from '../theme/defineTheme';
@@ -268,6 +270,31 @@ describe('NumberInput', () => {
268
270
  expect(input).toHaveAttribute('aria-valuenow', '1234');
269
271
  });
270
272
 
273
+ it('keeps ARIA value text on the committed value while an edit is pending', () => {
274
+ function ControlledNumberInput() {
275
+ const [controlledValue, setControlledValue] = useState(1234);
276
+ return (
277
+ <NumberInput
278
+ label="Revenue"
279
+ value={controlledValue}
280
+ onChange={setControlledValue}
281
+ formatValue={number => `$${number.toLocaleString('en-US')}`}
282
+ />
283
+ );
284
+ }
285
+ render(<ControlledNumberInput />);
286
+ const input = screen.getByRole('spinbutton');
287
+ fireEvent.focus(input);
288
+ fireEvent.input(input, {target: {value: '4200'}});
289
+
290
+ expect(input).toHaveAttribute('aria-valuenow', '1234');
291
+ expect(input).toHaveAttribute('aria-valuetext', '$1,234');
292
+
293
+ fireEvent.blur(input);
294
+ expect(input).toHaveAttribute('aria-valuenow', '4200');
295
+ expect(input).toHaveAttribute('aria-valuetext', '$4,200');
296
+ });
297
+
271
298
  it('shows the raw numeric value while focused and restores formatting on blur', () => {
272
299
  render(
273
300
  <NumberInput
@@ -325,7 +352,7 @@ describe('NumberInput', () => {
325
352
  });
326
353
 
327
354
  describe('onChange validation', () => {
328
- it('calls onChange with valid number when typing', async () => {
355
+ it('commits a valid number on blur', async () => {
329
356
  const user = userEvent.setup();
330
357
  const handleChange = vi.fn();
331
358
  render(
@@ -335,8 +362,10 @@ describe('NumberInput', () => {
335
362
  const input = screen.getByRole('spinbutton');
336
363
  await user.click(input);
337
364
  await user.type(input, '42');
365
+ expect(handleChange).not.toHaveBeenCalled();
338
366
 
339
- expect(handleChange).toHaveBeenCalledWith(4);
367
+ await user.tab();
368
+ expect(handleChange).toHaveBeenCalledTimes(1);
340
369
  expect(handleChange).toHaveBeenCalledWith(42);
341
370
  });
342
371
 
@@ -356,9 +385,7 @@ describe('NumberInput', () => {
356
385
  await user.click(input);
357
386
  await user.type(input, '10');
358
387
 
359
- // 1 is valid (<=5), but 10 is not
360
- expect(handleChange).toHaveBeenCalledWith(1);
361
- expect(handleChange).not.toHaveBeenCalledWith(10);
388
+ expect(handleChange).not.toHaveBeenCalled();
362
389
  });
363
390
 
364
391
  it('does not call onChange when value is below min', async () => {
@@ -397,9 +424,7 @@ describe('NumberInput', () => {
397
424
  await user.click(input);
398
425
  await user.type(input, '3.5');
399
426
 
400
- // 3 is valid, but 3.5 is not
401
- expect(handleChange).toHaveBeenCalledWith(3);
402
- expect(handleChange).not.toHaveBeenCalledWith(3.5);
427
+ expect(handleChange).not.toHaveBeenCalled();
403
428
  });
404
429
 
405
430
  it('calls onChange for decimal when isIntegerOnly is false', async () => {
@@ -412,11 +437,142 @@ describe('NumberInput', () => {
412
437
  const input = screen.getByRole('spinbutton');
413
438
  await user.click(input);
414
439
  await user.type(input, '3.5');
440
+ expect(handleChange).not.toHaveBeenCalled();
415
441
 
442
+ await user.tab();
416
443
  expect(handleChange).toHaveBeenCalledWith(3.5);
417
444
  });
418
445
  });
419
446
 
447
+ describe('invalid draft commit policy', () => {
448
+ function ControlledNumberInput({
449
+ initialValue = 7,
450
+ locale = 'en-US',
451
+ }: {
452
+ initialValue?: number | null;
453
+ locale?: 'en-US' | 'de-DE';
454
+ }) {
455
+ const [controlledValue, setControlledValue] = useState<number | null>(
456
+ initialValue,
457
+ );
458
+ return (
459
+ <InternationalizationProvider locale={locale}>
460
+ <NumberInput
461
+ label="Quantity"
462
+ value={controlledValue}
463
+ onChange={setControlledValue}
464
+ />
465
+ <output data-testid="committed">{String(controlledValue)}</output>
466
+ </InternationalizationProvider>
467
+ );
468
+ }
469
+
470
+ async function exerciseInvalidDraft(
471
+ entry: 'typing' | 'input',
472
+ commit: 'blur' | 'Enter',
473
+ ) {
474
+ const user = userEvent.setup();
475
+ render(<ControlledNumberInput />);
476
+ const input = screen.getByRole('spinbutton');
477
+ await user.click(input);
478
+ await user.clear(input);
479
+ if (entry === 'typing') {
480
+ await user.type(input, '1·234·567');
481
+ } else {
482
+ fireEvent.input(input, {target: {value: '1·234·567'}});
483
+ }
484
+
485
+ expect(input).toHaveValue('1·234·567');
486
+ expect(input).toHaveAttribute('aria-invalid', 'true');
487
+ expect(screen.getByTestId('committed')).toHaveTextContent('7');
488
+
489
+ if (commit === 'blur') {
490
+ await user.tab();
491
+ expect(input).toHaveValue('7');
492
+ expect(input).not.toHaveAttribute('aria-invalid');
493
+ } else {
494
+ await user.keyboard('{Enter}');
495
+ expect(input).toHaveValue('1·234·567');
496
+ expect(input).toHaveAttribute('aria-invalid', 'true');
497
+ }
498
+ expect(screen.getByTestId('committed')).toHaveTextContent('7');
499
+ }
500
+
501
+ it('rejects a sequentially typed invalid draft on blur', async () => {
502
+ await exerciseInvalidDraft('typing', 'blur');
503
+ });
504
+
505
+ it('rejects a one-shot invalid draft on blur', async () => {
506
+ await exerciseInvalidDraft('input', 'blur');
507
+ });
508
+
509
+ it('rejects a sequentially typed invalid draft on Enter', async () => {
510
+ await exerciseInvalidDraft('typing', 'Enter');
511
+ });
512
+
513
+ it('rejects a one-shot invalid draft on Enter', async () => {
514
+ await exerciseInvalidDraft('input', 'Enter');
515
+ });
516
+
517
+ it('commits a valid localized grouped number as one edit', async () => {
518
+ const user = userEvent.setup();
519
+ render(<ControlledNumberInput locale="de-DE" />);
520
+ const input = screen.getByRole('spinbutton');
521
+ await user.click(input);
522
+ await user.clear(input);
523
+ await user.type(input, '1.234.567');
524
+
525
+ expect(screen.getByTestId('committed')).toHaveTextContent('7');
526
+ await user.tab();
527
+ expect(screen.getByTestId('committed')).toHaveTextContent('1234567');
528
+ expect(input).toHaveValue('1234567');
529
+ });
530
+
531
+ it('keeps a controlled update behind an invalid focused draft', () => {
532
+ const onChange = vi.fn();
533
+ const {rerender} = render(
534
+ <NumberInput label="Quantity" value={7} onChange={onChange} />,
535
+ );
536
+ const input = screen.getByRole('spinbutton');
537
+ fireEvent.focus(input);
538
+ fireEvent.input(input, {target: {value: '1·234·567'}});
539
+
540
+ rerender(<NumberInput label="Quantity" value={9} onChange={onChange} />);
541
+ expect(input).toHaveValue('1·234·567');
542
+ fireEvent.blur(input);
543
+
544
+ expect(onChange).not.toHaveBeenCalled();
545
+ expect(input).toHaveValue('9');
546
+ });
547
+
548
+ it('waits for IME composition to finish before committing', () => {
549
+ const onChange = vi.fn();
550
+ render(<NumberInput label="Quantity" value={7} onChange={onChange} />);
551
+ const input = screen.getByRole('spinbutton');
552
+ fireEvent.focus(input);
553
+ fireEvent.input(input, {target: {value: '42'}});
554
+ fireEvent.keyDown(input, {key: 'Enter', isComposing: true});
555
+ expect(onChange).not.toHaveBeenCalled();
556
+
557
+ fireEvent.keyDown(input, {key: 'Enter'});
558
+ expect(onChange).toHaveBeenCalledTimes(1);
559
+ expect(onChange).toHaveBeenCalledWith(42);
560
+ });
561
+
562
+ it('steps from the committed value when the draft is invalid', () => {
563
+ const onChange = vi.fn();
564
+ render(<NumberInput label="Quantity" value={7} onChange={onChange} />);
565
+ const input = screen.getByRole('spinbutton');
566
+ fireEvent.focus(input);
567
+ fireEvent.input(input, {target: {value: '1·234·567'}});
568
+ fireEvent.keyDown(input, {key: 'ArrowUp'});
569
+
570
+ expect(onChange).toHaveBeenCalledTimes(1);
571
+ expect(onChange).toHaveBeenCalledWith(8);
572
+ expect(input).toHaveValue('7');
573
+ });
574
+ });
575
+
420
576
  describe('units prop', () => {
421
577
  it('renders units text when provided', () => {
422
578
  render(
@@ -540,6 +696,233 @@ describe('NumberInput', () => {
540
696
  });
541
697
  });
542
698
 
699
+ describe('range clamping on commit', () => {
700
+ it('commits an over-max entry at max on blur', async () => {
701
+ const user = userEvent.setup();
702
+ const handleChange = vi.fn();
703
+ render(
704
+ <NumberInput
705
+ label="Page"
706
+ value={null}
707
+ onChange={handleChange}
708
+ min={1}
709
+ max={2}
710
+ isIntegerOnly
711
+ />,
712
+ );
713
+
714
+ const input = screen.getByRole('spinbutton');
715
+ await user.click(input);
716
+ await user.type(input, '100');
717
+ handleChange.mockClear();
718
+ await user.tab();
719
+
720
+ expect(handleChange).toHaveBeenCalledWith(2);
721
+ });
722
+
723
+ it('commits an over-max entry at max on Enter', async () => {
724
+ const user = userEvent.setup();
725
+ const handleChange = vi.fn();
726
+ render(
727
+ <NumberInput
728
+ label="Rating"
729
+ value={null}
730
+ onChange={handleChange}
731
+ max={5}
732
+ />,
733
+ );
734
+
735
+ const input = screen.getByRole('spinbutton');
736
+ await user.click(input);
737
+ await user.type(input, '10');
738
+ handleChange.mockClear();
739
+ await user.keyboard('{Enter}');
740
+
741
+ expect(handleChange).toHaveBeenCalledWith(5);
742
+ });
743
+
744
+ it('commits a below-min entry at min', async () => {
745
+ const user = userEvent.setup();
746
+ const handleChange = vi.fn();
747
+ render(
748
+ <NumberInput
749
+ label="Age"
750
+ value={null}
751
+ onChange={handleChange}
752
+ min={0}
753
+ />,
754
+ );
755
+
756
+ const input = screen.getByRole('spinbutton');
757
+ await user.click(input);
758
+ await user.type(input, '-5');
759
+ await user.tab();
760
+
761
+ expect(handleChange).toHaveBeenCalledWith(0);
762
+ });
763
+
764
+ it('does not clamp while typing', async () => {
765
+ const user = userEvent.setup();
766
+ const handleChange = vi.fn();
767
+ render(
768
+ <NumberInput
769
+ label="Rating"
770
+ value={null}
771
+ onChange={handleChange}
772
+ max={5}
773
+ />,
774
+ );
775
+
776
+ const input = screen.getByRole('spinbutton');
777
+ await user.click(input);
778
+ await user.type(input, '10');
779
+
780
+ // The entry stays exactly as typed until it is committed.
781
+ expect(input).toHaveValue('10');
782
+ expect(handleChange).not.toHaveBeenCalledWith(5);
783
+ });
784
+
785
+ it('displays the clamped value after commit', async () => {
786
+ const user = userEvent.setup();
787
+ function ControlledNumberInput() {
788
+ const [value, setValue] = useState<number>(1);
789
+ return (
790
+ <NumberInput
791
+ label="Page"
792
+ value={value}
793
+ onChange={setValue}
794
+ min={1}
795
+ max={2}
796
+ isIntegerOnly
797
+ />
798
+ );
799
+ }
800
+ render(<ControlledNumberInput />);
801
+
802
+ const input = screen.getByRole('spinbutton');
803
+ await user.click(input);
804
+ await user.clear(input);
805
+ await user.type(input, '100');
806
+ await user.tab();
807
+
808
+ expect(input).toHaveValue('2');
809
+ });
810
+
811
+ it('shows the clamped value in the field after Enter', async () => {
812
+ const user = userEvent.setup();
813
+ function ControlledNumberInput() {
814
+ const [value, setValue] = useState<number | null>(null);
815
+ return (
816
+ <NumberInput
817
+ label="Rating"
818
+ value={value}
819
+ onChange={setValue}
820
+ min={1}
821
+ max={5}
822
+ />
823
+ );
824
+ }
825
+ render(<ControlledNumberInput />);
826
+
827
+ const input = screen.getByRole('spinbutton');
828
+ await user.click(input);
829
+ await user.type(input, '10{Enter}');
830
+
831
+ // Still focused: the field must not keep showing the rejected entry.
832
+ expect(input).toHaveValue('5');
833
+ });
834
+
835
+ it('reverts an entry that is not a usable number', async () => {
836
+ const user = userEvent.setup();
837
+ const handleChange = vi.fn();
838
+ render(
839
+ <NumberInput
840
+ label="Count"
841
+ value={null}
842
+ onChange={handleChange}
843
+ max={10}
844
+ isIntegerOnly
845
+ />,
846
+ );
847
+
848
+ const input = screen.getByRole('spinbutton');
849
+ await user.click(input);
850
+ await user.type(input, '12.5');
851
+ handleChange.mockClear();
852
+ await user.tab();
853
+
854
+ expect(handleChange).not.toHaveBeenCalled();
855
+ expect(input).toHaveValue('');
856
+ });
857
+
858
+ it('rounds a fractional max inwards for an integer-only field', async () => {
859
+ const user = userEvent.setup();
860
+ const handleChange = vi.fn();
861
+ render(
862
+ <NumberInput
863
+ label="Count"
864
+ value={null}
865
+ onChange={handleChange}
866
+ max={9.5}
867
+ isIntegerOnly
868
+ />,
869
+ );
870
+
871
+ const input = screen.getByRole('spinbutton');
872
+ await user.click(input);
873
+ await user.type(input, '20');
874
+ handleChange.mockClear();
875
+ await user.tab();
876
+
877
+ expect(handleChange).toHaveBeenCalledWith(9);
878
+ });
879
+
880
+ it('rounds a fractional min inwards for an integer-only field', async () => {
881
+ const user = userEvent.setup();
882
+ const handleChange = vi.fn();
883
+ render(
884
+ <NumberInput
885
+ label="Count"
886
+ value={null}
887
+ onChange={handleChange}
888
+ min={0.5}
889
+ isIntegerOnly
890
+ />,
891
+ );
892
+
893
+ const input = screen.getByRole('spinbutton');
894
+ await user.click(input);
895
+ await user.type(input, '0');
896
+ handleChange.mockClear();
897
+ await user.tab();
898
+
899
+ expect(handleChange).toHaveBeenCalledWith(1);
900
+ });
901
+
902
+ it('does not clamp when no value can satisfy both bounds', async () => {
903
+ const user = userEvent.setup();
904
+ const handleChange = vi.fn();
905
+ render(
906
+ <NumberInput
907
+ label="Count"
908
+ value={null}
909
+ onChange={handleChange}
910
+ min={5}
911
+ max={2}
912
+ />,
913
+ );
914
+
915
+ const input = screen.getByRole('spinbutton');
916
+ await user.click(input);
917
+ await user.type(input, '10');
918
+ handleChange.mockClear();
919
+ await user.tab();
920
+
921
+ expect(handleChange).not.toHaveBeenCalled();
922
+ expect(input).toHaveValue('');
923
+ });
924
+ });
925
+
543
926
  describe('status prop', () => {
544
927
  it('renders with error status icon', () => {
545
928
  const {container} = render(
@@ -817,6 +1200,32 @@ describe('NumberInput', () => {
817
1200
  expect(data.get('revenue')).toBe('1234');
818
1201
  });
819
1202
 
1203
+ it('submits the committed value in formatted mode until an edit commits', () => {
1204
+ function ControlledForm() {
1205
+ const [controlledValue, setControlledValue] = useState(7);
1206
+ return (
1207
+ <form>
1208
+ <NumberInput
1209
+ label="Quantity"
1210
+ htmlName="quantity"
1211
+ value={controlledValue}
1212
+ onChange={setControlledValue}
1213
+ formatValue={String}
1214
+ />
1215
+ </form>
1216
+ );
1217
+ }
1218
+ const {container} = render(<ControlledForm />);
1219
+ const form = container.querySelector('form')!;
1220
+ const input = screen.getByRole('spinbutton');
1221
+ fireEvent.focus(input);
1222
+ fireEvent.input(input, {target: {value: '42'}});
1223
+
1224
+ expect(new FormData(form).get('quantity')).toBe('7');
1225
+ fireEvent.blur(input);
1226
+ expect(new FormData(form).get('quantity')).toBe('42');
1227
+ });
1228
+
820
1229
  it('is excluded from form data when disabled', () => {
821
1230
  const {container} = render(
822
1231
  <form>
@@ -1054,6 +1463,25 @@ describe('NumberInput', () => {
1054
1463
  ).not.toBeInTheDocument();
1055
1464
  });
1056
1465
 
1466
+ it('keeps Tab moving forward when an empty field has an invalid draft', async () => {
1467
+ const user = userEvent.setup();
1468
+ render(
1469
+ <>
1470
+ <NumberInput label="Qty" value={null} onChange={() => {}} hasClear />
1471
+ <button type="button">Next field</button>
1472
+ </>,
1473
+ );
1474
+ const input = screen.getByRole('spinbutton');
1475
+ await user.click(input);
1476
+ await user.type(input, 'invalid');
1477
+ expect(
1478
+ screen.queryByRole('button', {name: 'Clear Qty'}),
1479
+ ).not.toBeInTheDocument();
1480
+
1481
+ await user.tab();
1482
+ expect(screen.getByRole('button', {name: 'Next field'})).toHaveFocus();
1483
+ });
1484
+
1057
1485
  it('does not show clear button when hasClear is false', () => {
1058
1486
  render(<NumberInput label="Qty" value={5} onChange={() => {}} />);
1059
1487
  expect(