@astryxdesign/core 0.5.0 → 0.5.1-canary.f64186a

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 +222 -0
  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/AspectRatio/AspectRatio.d.ts +6 -6
  6. package/dist/Avatar/Avatar.d.ts +25 -1
  7. package/dist/Avatar/Avatar.d.ts.map +1 -1
  8. package/dist/Avatar/Avatar.js +43 -9
  9. package/dist/Avatar/index.d.ts +2 -2
  10. package/dist/Avatar/index.d.ts.map +1 -1
  11. package/dist/Avatar/index.js +1 -1
  12. package/dist/AvatarGroup/AvatarGroup.d.ts +9 -2
  13. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  14. package/dist/AvatarGroup/AvatarGroup.js +5 -2
  15. package/dist/AvatarGroup/AvatarGroupContext.d.ts +2 -1
  16. package/dist/AvatarGroup/AvatarGroupContext.d.ts.map +1 -1
  17. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  18. package/dist/AvatarGroup/AvatarGroupOverflow.js +11 -7
  19. package/dist/Badge/Badge.d.ts.map +1 -1
  20. package/dist/Badge/Badge.js +26 -2
  21. package/dist/Banner/Banner.d.ts +10 -1
  22. package/dist/Banner/Banner.d.ts.map +1 -1
  23. package/dist/Banner/Banner.js +11 -2
  24. package/dist/Button/Button.d.ts.map +1 -1
  25. package/dist/Button/Button.js +8 -7
  26. package/dist/Calendar/Calendar.d.ts +4 -2
  27. package/dist/Calendar/Calendar.d.ts.map +1 -1
  28. package/dist/Calendar/Calendar.js +15 -21
  29. package/dist/Calendar/hooks/useCalendarDays.js +4 -6
  30. package/dist/Calendar/hooks/useCalendarNavigation.js +4 -6
  31. package/dist/Calendar/styles.d.ts +5 -20
  32. package/dist/Calendar/styles.d.ts.map +1 -1
  33. package/dist/Calendar/styles.js +15 -17
  34. package/dist/Card/Card.d.ts +12 -12
  35. package/dist/Card/Card.d.ts.map +1 -1
  36. package/dist/Card/Card.js +17 -9
  37. package/dist/Card/index.d.ts +35 -1
  38. package/dist/Card/index.d.ts.map +1 -1
  39. package/dist/Card/index.js +21 -1
  40. package/dist/Carousel/Carousel.d.ts.map +1 -1
  41. package/dist/Carousel/Carousel.js +108 -7
  42. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  43. package/dist/CheckboxInput/CheckboxInput.js +7 -1
  44. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  45. package/dist/CodeBlock/CodeBlock.js +15 -4
  46. package/dist/CodeBlock/highlightRanges.js +1 -1
  47. package/dist/CodeBlock/highlightStyles.js +13 -13
  48. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  49. package/dist/CommandPalette/CommandPaletteItem.js +2 -7
  50. package/dist/ComplexSelector/ComplexSelector.d.ts +10 -2
  51. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  52. package/dist/ComplexSelector/ComplexSelector.js +14 -7
  53. package/dist/DateInput/DateInput.d.ts +43 -0
  54. package/dist/DateInput/DateInput.d.ts.map +1 -1
  55. package/dist/DateInput/DateInput.js +22 -5
  56. package/dist/DateInput/MonthScroller.d.ts.map +1 -1
  57. package/dist/DateInput/MonthScroller.js +5 -3
  58. package/dist/DateInput/MonthYearWheels.d.ts.map +1 -1
  59. package/dist/DateInput/MonthYearWheels.js +6 -4
  60. package/dist/DateInput/NativeDateField.d.ts +10 -0
  61. package/dist/DateInput/NativeDateField.d.ts.map +1 -0
  62. package/dist/DateInput/NativeDateField.js +494 -0
  63. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  64. package/dist/DateInput/TouchDateField.js +52 -25
  65. package/dist/DateInput/Wheel.d.ts.map +1 -1
  66. package/dist/DateInput/Wheel.js +1 -0
  67. package/dist/DateInput/index.d.ts +1 -1
  68. package/dist/DateInput/index.d.ts.map +1 -1
  69. package/dist/DateInput/nativeDateSegments.d.ts +22 -0
  70. package/dist/DateInput/nativeDateSegments.d.ts.map +1 -0
  71. package/dist/DateInput/nativeDateSegments.js +111 -0
  72. package/dist/DateRangeInput/DateRangeInput.d.ts +4 -2
  73. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  74. package/dist/DateRangeInput/DateRangeInput.js +3 -5
  75. package/dist/DateTimeInput/DateTimeInput.d.ts +23 -24
  76. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  77. package/dist/DateTimeInput/DateTimeInput.js +35 -10
  78. package/dist/DateTimeInput/TouchDateTimeField.d.ts +6 -0
  79. package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -0
  80. package/dist/DateTimeInput/TouchDateTimeField.js +1021 -0
  81. package/dist/Dialog/Dialog.d.ts +16 -2
  82. package/dist/Dialog/Dialog.d.ts.map +1 -1
  83. package/dist/Dialog/Dialog.js +28 -7
  84. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  85. package/dist/DropdownMenu/DropdownMenu.js +4 -13
  86. package/dist/Field/FieldLabel.d.ts.map +1 -1
  87. package/dist/Field/FieldLabel.js +9 -1
  88. package/dist/Field/InputClearButton.d.ts +9 -3
  89. package/dist/Field/InputClearButton.d.ts.map +1 -1
  90. package/dist/Field/InputClearButton.js +14 -3
  91. package/dist/Heading/Heading.d.ts.map +1 -1
  92. package/dist/Heading/Heading.js +0 -1
  93. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  94. package/dist/HoverCard/HoverCard.js +104 -3
  95. package/dist/HoverCard/useHoverCard.d.ts +14 -2
  96. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  97. package/dist/HoverCard/useHoverCard.js +7 -1
  98. package/dist/Icon/Icon.d.ts +4 -2
  99. package/dist/Icon/Icon.d.ts.map +1 -1
  100. package/dist/Icon/defaultIcons.d.ts +3 -2
  101. package/dist/Icon/defaultIcons.d.ts.map +1 -1
  102. package/dist/Icon/defaultIcons.js +7 -0
  103. package/dist/Icon/globalIconRegistry.d.ts +13 -0
  104. package/dist/Icon/globalIconRegistry.d.ts.map +1 -1
  105. package/dist/Icon/globalIconRegistry.js +34 -6
  106. package/dist/Icon/index.d.ts +1 -1
  107. package/dist/Icon/index.d.ts.map +1 -1
  108. package/dist/Icon/useIcon.d.ts +5 -2
  109. package/dist/Icon/useIcon.d.ts.map +1 -1
  110. package/dist/Icon/useIcon.js +3 -0
  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 +2 -2
  115. package/dist/Kbd/Kbd.d.ts.map +1 -1
  116. package/dist/Kbd/Kbd.js +2 -26
  117. package/dist/Layer/gestureCounter.d.ts +8 -0
  118. package/dist/Layer/gestureCounter.d.ts.map +1 -0
  119. package/dist/Layer/gestureCounter.js +53 -0
  120. package/dist/Layer/useLayer.d.ts +23 -13
  121. package/dist/Layer/useLayer.d.ts.map +1 -1
  122. package/dist/Layer/useLayer.js +146 -24
  123. package/dist/List/List.d.ts +1 -1
  124. package/dist/List/List.d.ts.map +1 -1
  125. package/dist/List/List.js +15 -4
  126. package/dist/Markdown/Markdown.d.ts +1 -1
  127. package/dist/Markdown/Markdown.d.ts.map +1 -1
  128. package/dist/Markdown/Markdown.js +20 -7
  129. package/dist/Markdown/parser.d.ts.map +1 -1
  130. package/dist/Markdown/parser.js +5 -3
  131. package/dist/MetadataList/MetadataListItem.d.ts +1 -1
  132. package/dist/MetadataList/MetadataListItem.d.ts.map +1 -1
  133. package/dist/MetadataList/MetadataListItem.js +4 -1
  134. package/dist/MultiSelector/MultiSelector.d.ts +18 -1
  135. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  136. package/dist/MultiSelector/MultiSelector.js +58 -11
  137. package/dist/MultiSelector/hooks.d.ts +7 -1
  138. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  139. package/dist/MultiSelector/hooks.js +3 -2
  140. package/dist/NavIcon/NavIcon.d.ts.map +1 -1
  141. package/dist/NavIcon/NavIcon.js +5 -1
  142. package/dist/NavItem/navItemStyles.stylex.d.ts +0 -8
  143. package/dist/NavItem/navItemStyles.stylex.d.ts.map +1 -1
  144. package/dist/NavItem/navItemStyles.stylex.js +0 -2
  145. package/dist/NavMenu/NavHeadingMenu.d.ts +1 -1
  146. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  147. package/dist/NavMenu/NavHeadingMenu.js +7 -4
  148. package/dist/NumberInput/NumberInput.d.ts +6 -1
  149. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  150. package/dist/NumberInput/NumberInput.js +95 -126
  151. package/dist/NumberInput/numberInputCommit.d.ts +34 -0
  152. package/dist/NumberInput/numberInputCommit.d.ts.map +1 -0
  153. package/dist/NumberInput/numberInputCommit.js +71 -0
  154. package/dist/NumberInput/numberParser.d.ts +64 -0
  155. package/dist/NumberInput/numberParser.d.ts.map +1 -0
  156. package/dist/NumberInput/numberParser.js +350 -0
  157. package/dist/Outline/Outline.d.ts.map +1 -1
  158. package/dist/Outline/Outline.js +2 -3
  159. package/dist/OverflowList/OverflowList.d.ts +41 -1
  160. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  161. package/dist/OverflowList/OverflowList.js +43 -2
  162. package/dist/Popover/Popover.d.ts.map +1 -1
  163. package/dist/Popover/Popover.js +8 -14
  164. package/dist/Popover/usePopover.d.ts +15 -41
  165. package/dist/Popover/usePopover.d.ts.map +1 -1
  166. package/dist/Popover/usePopover.js +26 -7
  167. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  168. package/dist/PowerSearch/PowerSearchValueEditor.js +91 -18
  169. package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
  170. package/dist/PowerSearch/formatFilterValue.js +4 -2
  171. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  172. package/dist/ProgressBar/ProgressBar.js +18 -5
  173. package/dist/Resizable/useResizable.d.ts +19 -1
  174. package/dist/Resizable/useResizable.d.ts.map +1 -1
  175. package/dist/Resizable/useResizable.js +104 -38
  176. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  177. package/dist/SelectableCard/SelectableCard.js +9 -0
  178. package/dist/Selector/Selector.d.ts +18 -1
  179. package/dist/Selector/Selector.d.ts.map +1 -1
  180. package/dist/Selector/Selector.js +92 -21
  181. package/dist/Selector/hooks.d.ts +7 -1
  182. package/dist/Selector/hooks.d.ts.map +1 -1
  183. package/dist/Selector/hooks.js +3 -2
  184. package/dist/SideNav/SideNav.d.ts +6 -1
  185. package/dist/SideNav/SideNav.d.ts.map +1 -1
  186. package/dist/SideNav/SideNav.js +57 -27
  187. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  188. package/dist/SideNav/SideNavHeading.js +5 -4
  189. package/dist/SideNav/SideNavItem.d.ts +29 -4
  190. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  191. package/dist/SideNav/SideNavItem.js +75 -20
  192. package/dist/Slider/Slider.d.ts.map +1 -1
  193. package/dist/Slider/Slider.js +34 -2
  194. package/dist/Spinner/Spinner.d.ts +3 -1
  195. package/dist/Spinner/Spinner.d.ts.map +1 -1
  196. package/dist/Spinner/Spinner.js +199 -20
  197. package/dist/StatusDot/StatusDot.d.ts.map +1 -1
  198. package/dist/StatusDot/StatusDot.js +6 -1
  199. package/dist/Stepper/Step.d.ts.map +1 -1
  200. package/dist/Stepper/Step.js +7 -6
  201. package/dist/Switch/Switch.d.ts.map +1 -1
  202. package/dist/Switch/Switch.js +5 -1
  203. package/dist/Table/BaseTable.d.ts.map +1 -1
  204. package/dist/Table/BaseTable.js +6 -1
  205. package/dist/Table/Table.d.ts.map +1 -1
  206. package/dist/Table/Table.js +3 -6
  207. package/dist/Table/plugins/filtering/useTableFiltering.d.ts.map +1 -1
  208. package/dist/Table/plugins/filtering/useTableFiltering.js +73 -4
  209. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts +26 -0
  210. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  211. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +34 -3
  212. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  213. package/dist/Table/plugins/selection/useTableSelection.js +25 -1
  214. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  215. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +3 -4
  216. package/dist/Text/Text.d.ts.map +1 -1
  217. package/dist/Text/Text.js +0 -1
  218. package/dist/TextArea/TextArea.d.ts.map +1 -1
  219. package/dist/TextArea/TextArea.js +13 -8
  220. package/dist/Timestamp/Timestamp.d.ts +1 -1
  221. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  222. package/dist/Timestamp/Timestamp.js +10 -18
  223. package/dist/Timestamp/formatInstant.d.ts.map +1 -1
  224. package/dist/Timestamp/formatInstant.js +10 -5
  225. package/dist/Toast/Toast.d.ts +17 -2
  226. package/dist/Toast/Toast.d.ts.map +1 -1
  227. package/dist/Toast/Toast.js +77 -11
  228. package/dist/Toast/ToastViewport.d.ts +1 -0
  229. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  230. package/dist/Toast/ToastViewport.js +76 -21
  231. package/dist/Toast/index.d.ts +1 -1
  232. package/dist/Toast/index.d.ts.map +1 -1
  233. package/dist/Toast/types.d.ts +38 -0
  234. package/dist/Toast/types.d.ts.map +1 -1
  235. package/dist/Toast/useToastGesture.d.ts +26 -0
  236. package/dist/Toast/useToastGesture.d.ts.map +1 -0
  237. package/dist/Toast/useToastGesture.js +223 -0
  238. package/dist/Token/Token.d.ts +1 -1
  239. package/dist/Token/Token.d.ts.map +1 -1
  240. package/dist/Token/Token.js +23 -12
  241. package/dist/Token/TokenLink.d.ts +1 -1
  242. package/dist/Token/TokenLink.d.ts.map +1 -1
  243. package/dist/Token/TokenLink.js +5 -2
  244. package/dist/Tokenizer/Tokenizer.d.ts +12 -1
  245. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  246. package/dist/Tokenizer/Tokenizer.js +31 -21
  247. package/dist/TopNav/TopNavHeading.js +2 -2
  248. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  249. package/dist/TopNav/TopNavItem.js +3 -3
  250. package/dist/TopNav/TopNavMegaMenu.d.ts +1 -1
  251. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  252. package/dist/TopNav/TopNavMegaMenu.js +30 -9
  253. package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
  254. package/dist/TopNav/TopNavMegaMenuItem.js +3 -3
  255. package/dist/TopNav/TopNavMenu.d.ts +1 -1
  256. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  257. package/dist/TopNav/TopNavMenu.js +21 -6
  258. package/dist/TreeList/TreeList.d.ts +1 -1
  259. package/dist/TreeList/TreeList.d.ts.map +1 -1
  260. package/dist/TreeList/TreeList.js +7 -2
  261. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  262. package/dist/TreeList/TreeListItem.js +2 -2
  263. package/dist/Typeahead/BaseTypeahead.d.ts +33 -0
  264. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  265. package/dist/Typeahead/BaseTypeahead.js +51 -8
  266. package/dist/Typeahead/Typeahead.d.ts +8 -1
  267. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  268. package/dist/Typeahead/Typeahead.js +2 -0
  269. package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
  270. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  271. package/dist/Typeahead/TypeaheadItem.js +7 -2
  272. package/dist/astryx.css +87 -22
  273. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  274. package/dist/hooks/useHotkeys.js +3 -25
  275. package/dist/hooks/useOverflow.d.ts.map +1 -1
  276. package/dist/hooks/useOverflow.js +14 -4
  277. package/dist/index.d.ts +1 -1
  278. package/dist/index.d.ts.map +1 -1
  279. package/dist/theme/Theme.d.ts.map +1 -1
  280. package/dist/theme/Theme.js +46 -10
  281. package/dist/theme/defineTheme.d.ts +9 -3
  282. package/dist/theme/defineTheme.d.ts.map +1 -1
  283. package/dist/theme/defineTheme.js +5 -0
  284. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  285. package/dist/theme/derivedVarRegistry.js +24 -4
  286. package/dist/theme/generateThemeRules.d.ts +21 -0
  287. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  288. package/dist/theme/generateThemeRules.js +26 -0
  289. package/dist/theme/syntax/tokens.d.ts +1 -1
  290. package/dist/theme/syntax/tokens.d.ts.map +1 -1
  291. package/dist/theme/syntax/tokens.js +5 -2
  292. package/dist/theme/useTheme.d.ts.map +1 -1
  293. package/dist/theme/useTheme.js +6 -7
  294. package/dist/utils/dateParser.d.ts +5 -8
  295. package/dist/utils/dateParser.d.ts.map +1 -1
  296. package/dist/utils/dateParser.js +10 -11
  297. package/dist/utils/focusOutline.stylex.d.ts +25 -0
  298. package/dist/utils/focusOutline.stylex.d.ts.map +1 -1
  299. package/dist/utils/focusOutline.stylex.js +15 -0
  300. package/dist/utils/interactionOverlay.stylex.d.ts +26 -0
  301. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -0
  302. package/dist/utils/interactionOverlay.stylex.js +42 -0
  303. package/dist/utils/isApplePlatform.d.ts +18 -0
  304. package/dist/utils/isApplePlatform.d.ts.map +1 -0
  305. package/dist/utils/isApplePlatform.js +37 -0
  306. package/dist/utils/plainDate.d.ts +11 -0
  307. package/dist/utils/plainDate.d.ts.map +1 -1
  308. package/dist/utils/plainDate.js +20 -4
  309. package/locales/af-ZA.json +7 -7
  310. package/locales/ar-SA.json +7 -7
  311. package/locales/ca-ES.json +6 -6
  312. package/locales/cs-CZ.json +7 -7
  313. package/locales/da-DK.json +7 -7
  314. package/locales/de-DE.json +7 -7
  315. package/locales/el-GR.json +7 -7
  316. package/locales/en.json +178 -6
  317. package/locales/es-ES.json +6 -6
  318. package/locales/fi-FI.json +7 -7
  319. package/locales/fr-FR.json +7 -7
  320. package/locales/he-IL.json +7 -7
  321. package/locales/hu-HU.json +7 -7
  322. package/locales/it-IT.json +7 -7
  323. package/locales/ja-JP.json +7 -7
  324. package/locales/ko-KR.json +7 -7
  325. package/locales/nl-NL.json +7 -7
  326. package/locales/no-NO.json +7 -7
  327. package/locales/pl-PL.json +7 -7
  328. package/locales/pseudo.json +130 -1
  329. package/locales/pt-BR.json +7 -7
  330. package/locales/pt-PT.json +7 -7
  331. package/locales/ro-RO.json +7 -7
  332. package/locales/ru-RU.json +7 -7
  333. package/locales/sr-SP.json +7 -7
  334. package/locales/sv-SE.json +7 -7
  335. package/locales/tr-TR.json +7 -7
  336. package/locales/uk-UA.json +7 -7
  337. package/locales/vi-VN.json +7 -7
  338. package/locales/zh-CN.json +7 -7
  339. package/locales/zh-TW.json +7 -7
  340. package/package.json +4 -5
  341. package/src/AlertDialog/AlertDialog.doc.mjs +114 -30
  342. package/src/AlertDialog/AlertDialog.test.tsx +211 -2
  343. package/src/AlertDialog/AlertDialog.tsx +62 -20
  344. package/src/AspectRatio/AspectRatio.doc.mjs +3 -3
  345. package/src/AspectRatio/AspectRatio.tsx +6 -6
  346. package/src/Avatar/Avatar.doc.mjs +12 -2
  347. package/src/Avatar/Avatar.test.tsx +80 -0
  348. package/src/Avatar/Avatar.tsx +64 -14
  349. package/src/Avatar/index.ts +2 -2
  350. package/src/AvatarGroup/AvatarGroup.doc.mjs +8 -2
  351. package/src/AvatarGroup/AvatarGroup.test.tsx +24 -0
  352. package/src/AvatarGroup/AvatarGroup.tsx +12 -4
  353. package/src/AvatarGroup/AvatarGroupContext.ts +5 -3
  354. package/src/AvatarGroup/AvatarGroupOverflow.tsx +12 -12
  355. package/src/Badge/Badge.doc.mjs +3 -2
  356. package/src/Badge/Badge.test.tsx +32 -3
  357. package/src/Badge/Badge.tsx +42 -1
  358. package/src/Banner/Banner.doc.mjs +9 -0
  359. package/src/Banner/Banner.test.tsx +76 -11
  360. package/src/Banner/Banner.tsx +21 -2
  361. package/src/Button/Button.doc.mjs +2 -1
  362. package/src/Button/Button.test.tsx +19 -4
  363. package/src/Button/Button.tsx +9 -29
  364. package/src/ButtonGroup/ButtonGroup.doc.mjs +2 -2
  365. package/src/ButtonGroup/ButtonGroup.test.tsx +13 -0
  366. package/src/Calendar/Calendar.doc.mjs +3 -3
  367. package/src/Calendar/Calendar.test.tsx +112 -0
  368. package/src/Calendar/Calendar.tsx +18 -16
  369. package/src/Calendar/hooks/useCalendarDays.ts +4 -4
  370. package/src/Calendar/hooks/useCalendarNavigation.ts +4 -4
  371. package/src/Calendar/styles.ts +48 -42
  372. package/src/Card/Card.doc.mjs +10 -7
  373. package/src/Card/Card.test.tsx +113 -19
  374. package/src/Card/Card.tsx +67 -89
  375. package/src/Card/index.ts +36 -1
  376. package/src/Carousel/Carousel.doc.mjs +3 -0
  377. package/src/Carousel/Carousel.test.tsx +360 -0
  378. package/src/Carousel/Carousel.tsx +116 -15
  379. package/src/CheckboxInput/CheckboxInput.doc.mjs +2 -0
  380. package/src/CheckboxInput/CheckboxInput.test.tsx +15 -0
  381. package/src/CheckboxInput/CheckboxInput.tsx +5 -0
  382. package/src/CodeBlock/CodeBlock.doc.mjs +10 -4
  383. package/src/CodeBlock/CodeBlock.test.tsx +37 -0
  384. package/src/CodeBlock/CodeBlock.tsx +20 -4
  385. package/src/CodeBlock/highlightRanges.ts +1 -1
  386. package/src/CodeBlock/highlightStyles.ts +13 -13
  387. package/src/CommandPalette/CommandPaletteItem.tsx +2 -12
  388. package/src/ComplexSelector/ComplexSelector.doc.mjs +7 -0
  389. package/src/ComplexSelector/ComplexSelector.test.tsx +183 -0
  390. package/src/ComplexSelector/ComplexSelector.tsx +22 -12
  391. package/src/DateInput/DateInput.doc.mjs +16 -0
  392. package/src/DateInput/DateInput.test.tsx +9 -4
  393. package/src/DateInput/DateInput.tsx +54 -3
  394. package/src/DateInput/DateInputTouch.test.tsx +146 -32
  395. package/src/DateInput/MonthScroller.tsx +5 -1
  396. package/src/DateInput/MonthYearWheels.tsx +7 -4
  397. package/src/DateInput/NativeDateField.test.tsx +635 -0
  398. package/src/DateInput/NativeDateField.tsx +641 -0
  399. package/src/DateInput/TouchDateField.tsx +83 -33
  400. package/src/DateInput/Wheel.tsx +1 -0
  401. package/src/DateInput/index.ts +1 -0
  402. package/src/DateInput/nativeDateSegments.ts +127 -0
  403. package/src/DateRangeInput/DateRangeInput.doc.mjs +2 -2
  404. package/src/DateRangeInput/DateRangeInput.test.tsx +46 -0
  405. package/src/DateRangeInput/DateRangeInput.tsx +8 -13
  406. package/src/DateTimeInput/DateTimeInput.doc.mjs +38 -21
  407. package/src/DateTimeInput/DateTimeInput.test.tsx +76 -0
  408. package/src/DateTimeInput/DateTimeInput.tsx +54 -20
  409. package/src/DateTimeInput/DateTimeInputTouch.test.tsx +980 -0
  410. package/src/DateTimeInput/TouchDateTimeField.tsx +1437 -0
  411. package/src/Dialog/Dialog.test.tsx +185 -18
  412. package/src/Dialog/Dialog.tsx +35 -11
  413. package/src/DropdownMenu/DropdownMenu.test.tsx +44 -10
  414. package/src/DropdownMenu/DropdownMenu.tsx +4 -13
  415. package/src/Field/FieldLabel.test.tsx +29 -0
  416. package/src/Field/FieldLabel.tsx +7 -0
  417. package/src/Field/InputClearButton.test.tsx +15 -1
  418. package/src/Field/InputClearButton.tsx +28 -4
  419. package/src/Heading/Heading.tsx +0 -1
  420. package/src/HoverCard/HoverCard.doc.mjs +8 -2
  421. package/src/HoverCard/HoverCard.test.tsx +154 -4
  422. package/src/HoverCard/HoverCard.tsx +137 -3
  423. package/src/HoverCard/useHoverCard.tsx +23 -3
  424. package/src/Icon/Icon.test.tsx +55 -1
  425. package/src/Icon/Icon.tsx +5 -3
  426. package/src/Icon/defaultIcons.tsx +11 -2
  427. package/src/Icon/globalIconRegistry.test.tsx +16 -3
  428. package/src/Icon/globalIconRegistry.tsx +37 -6
  429. package/src/Icon/index.ts +1 -0
  430. package/src/Icon/useIcon.ts +9 -2
  431. package/src/Indicator/CheckboxIndicator.tsx +14 -8
  432. package/src/Indicator/Indicator.doc.mjs +2 -0
  433. package/src/Item/Item.tsx +2 -7
  434. package/src/Kbd/Kbd.test.tsx +0 -17
  435. package/src/Kbd/Kbd.tsx +2 -26
  436. package/src/Layer/gestureCounter.ts +57 -0
  437. package/src/Layer/useLayer.test.tsx +368 -16
  438. package/src/Layer/useLayer.tsx +208 -35
  439. package/src/Layout/__tests__/edgeCompensation.test.tsx +1 -1
  440. package/src/List/List.test.tsx +38 -0
  441. package/src/List/List.tsx +8 -1
  442. package/src/Markdown/Markdown.test.tsx +37 -0
  443. package/src/Markdown/Markdown.tsx +16 -5
  444. package/src/Markdown/parser.test.ts +33 -0
  445. package/src/Markdown/parser.ts +9 -3
  446. package/src/MetadataList/MetadataList.test.tsx +75 -2
  447. package/src/MetadataList/MetadataListItem.tsx +5 -2
  448. package/src/MultiSelector/MultiSelector.doc.mjs +22 -1
  449. package/src/MultiSelector/MultiSelector.test.tsx +205 -0
  450. package/src/MultiSelector/MultiSelector.tsx +103 -16
  451. package/src/MultiSelector/hooks.ts +9 -2
  452. package/src/NavIcon/NavIcon.doc.mjs +8 -2
  453. package/src/NavIcon/NavIcon.test.tsx +9 -0
  454. package/src/NavIcon/NavIcon.tsx +5 -1
  455. package/src/NavItem/navItemStyles.stylex.ts +0 -8
  456. package/src/NavMenu/NavHeadingMenu.test.tsx +30 -0
  457. package/src/NavMenu/NavHeadingMenu.tsx +7 -4
  458. package/src/NumberInput/NumberInput.doc.mjs +26 -7
  459. package/src/NumberInput/NumberInput.test.tsx +563 -8
  460. package/src/NumberInput/NumberInput.tsx +77 -131
  461. package/src/NumberInput/numberInputCommit.test.ts +72 -0
  462. package/src/NumberInput/numberInputCommit.ts +103 -0
  463. package/src/NumberInput/numberParser.docblock.test.ts +234 -0
  464. package/src/NumberInput/numberParser.test.ts +222 -0
  465. package/src/NumberInput/numberParser.ts +403 -0
  466. package/src/Outline/Outline.tsx +2 -4
  467. package/src/OverflowList/OverflowList.doc.mjs +102 -21
  468. package/src/OverflowList/OverflowList.test.tsx +550 -4
  469. package/src/OverflowList/OverflowList.tsx +100 -2
  470. package/src/Pagination/Pagination.test.tsx +3 -2
  471. package/src/Popover/Popover.doc.mjs +2 -2
  472. package/src/Popover/Popover.test.tsx +38 -0
  473. package/src/Popover/Popover.tsx +28 -66
  474. package/src/Popover/usePopover.tsx +44 -5
  475. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +87 -0
  476. package/src/PowerSearch/PowerSearchValueEditor.test.tsx +64 -0
  477. package/src/PowerSearch/PowerSearchValueEditor.tsx +109 -10
  478. package/src/PowerSearch/formatFilterValue.test.ts +16 -1
  479. package/src/PowerSearch/formatFilterValue.ts +2 -0
  480. package/src/ProgressBar/ProgressBar.doc.mjs +42 -6
  481. package/src/ProgressBar/ProgressBar.test.tsx +24 -0
  482. package/src/ProgressBar/ProgressBar.tsx +28 -8
  483. package/src/Resizable/Resizable.doc.mjs +2 -2
  484. package/src/Resizable/useResizable.doc.mjs +21 -2
  485. package/src/Resizable/useResizable.test.ts +421 -0
  486. package/src/Resizable/useResizable.ts +133 -48
  487. package/src/SelectableCard/SelectableCard.doc.mjs +3 -0
  488. package/src/SelectableCard/SelectableCard.tsx +16 -0
  489. package/src/Selector/Selector.doc.mjs +14 -0
  490. package/src/Selector/Selector.source-build.test.mjs +65 -0
  491. package/src/Selector/Selector.test.tsx +301 -0
  492. package/src/Selector/Selector.tsx +145 -32
  493. package/src/Selector/hooks.ts +17 -2
  494. package/src/SideNav/SideNav.doc.mjs +2 -2
  495. package/src/SideNav/SideNav.test.tsx +1069 -6
  496. package/src/SideNav/SideNav.tsx +109 -38
  497. package/src/SideNav/SideNavHeading.tsx +5 -2
  498. package/src/SideNav/SideNavItem.doc.mjs +24 -3
  499. package/src/SideNav/SideNavItem.tsx +134 -44
  500. package/src/Slider/Slider.test.tsx +106 -1
  501. package/src/Slider/Slider.tsx +52 -1
  502. package/src/Spinner/Spinner.doc.mjs +17 -5
  503. package/src/Spinner/Spinner.test.tsx +164 -0
  504. package/src/Spinner/Spinner.tsx +252 -22
  505. package/src/StatusDot/StatusDot.doc.mjs +8 -2
  506. package/src/StatusDot/StatusDot.test.tsx +9 -0
  507. package/src/StatusDot/StatusDot.tsx +7 -1
  508. package/src/Stepper/Step.doc.mjs +1 -1
  509. package/src/Stepper/Step.tsx +7 -14
  510. package/src/Switch/Switch.doc.mjs +2 -0
  511. package/src/Switch/Switch.test.tsx +10 -0
  512. package/src/Switch/Switch.tsx +3 -0
  513. package/src/Table/BaseTable.tsx +6 -1
  514. package/src/Table/Table.doc.mjs +5 -3
  515. package/src/Table/Table.tsx +3 -8
  516. package/src/Table/TableBody.doc.mjs +2 -2
  517. package/src/Table/TableFooter.doc.mjs +1 -1
  518. package/src/Table/TableHeader.doc.mjs +1 -1
  519. package/src/Table/plugins/filtering/useTableFiltering.test.tsx +44 -6
  520. package/src/Table/plugins/filtering/useTableFiltering.tsx +100 -4
  521. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +50 -4
  522. package/src/Table/plugins/selection/useTableSelection.test.tsx +62 -0
  523. package/src/Table/plugins/selection/useTableSelection.tsx +25 -2
  524. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +1 -2
  525. package/src/Table/useTableGroupedRows.doc.mjs +3 -3
  526. package/src/Table/useTablePagination.doc.mjs +1 -1
  527. package/src/Table/useTableSelection.doc.mjs +1 -1
  528. package/src/Text/Text.test.tsx +44 -0
  529. package/src/Text/Text.tsx +0 -1
  530. package/src/TextArea/TextArea.doc.mjs +12 -2
  531. package/src/TextArea/TextArea.test.tsx +11 -0
  532. package/src/TextArea/TextArea.tsx +34 -22
  533. package/src/Timestamp/Timestamp.test.tsx +89 -0
  534. package/src/Timestamp/Timestamp.tsx +8 -8
  535. package/src/Timestamp/formatInstant.ts +5 -0
  536. package/src/Toast/Toast.doc.mjs +213 -44
  537. package/src/Toast/Toast.tsx +118 -23
  538. package/src/Toast/ToastViewport.test.tsx +1434 -8
  539. package/src/Toast/ToastViewport.tsx +85 -14
  540. package/src/Toast/index.ts +2 -0
  541. package/src/Toast/types.ts +43 -5
  542. package/src/Toast/useToastGesture.ts +361 -0
  543. package/src/Token/Token.test.tsx +95 -0
  544. package/src/Token/Token.tsx +19 -17
  545. package/src/Token/TokenLink.tsx +12 -3
  546. package/src/Tokenizer/Tokenizer.doc.mjs +16 -3
  547. package/src/Tokenizer/Tokenizer.test.tsx +161 -0
  548. package/src/Tokenizer/Tokenizer.tsx +49 -24
  549. package/src/TopNav/TopNavHeading.tsx +2 -2
  550. package/src/TopNav/TopNavItem.tsx +3 -7
  551. package/src/TopNav/TopNavMegaMenu.test.tsx +104 -0
  552. package/src/TopNav/TopNavMegaMenu.tsx +47 -4
  553. package/src/TopNav/TopNavMegaMenuItem.tsx +10 -9
  554. package/src/TopNav/TopNavMenu.test.tsx +133 -1
  555. package/src/TopNav/TopNavMenu.tsx +38 -5
  556. package/src/TreeList/TreeList.test.tsx +49 -0
  557. package/src/TreeList/TreeList.tsx +7 -2
  558. package/src/TreeList/TreeListItem.tsx +2 -7
  559. package/src/Typeahead/BaseTypeahead.doc.mjs +13 -0
  560. package/src/Typeahead/BaseTypeahead.tsx +96 -6
  561. package/src/Typeahead/Typeahead.doc.mjs +6 -0
  562. package/src/Typeahead/Typeahead.test.tsx +154 -0
  563. package/src/Typeahead/Typeahead.tsx +9 -0
  564. package/src/Typeahead/TypeaheadItem.test.tsx +43 -0
  565. package/src/Typeahead/TypeaheadItem.tsx +8 -1
  566. package/src/hooks/useAnnounce.doc.mjs +96 -0
  567. package/src/hooks/useDevWarning.doc.mjs +96 -0
  568. package/src/hooks/useHotkeys.test.ts +0 -18
  569. package/src/hooks/useHotkeys.ts +3 -25
  570. package/src/hooks/useIndicatorFocusRing.doc.mjs +104 -0
  571. package/src/hooks/useInputStatusIcon.doc.mjs +142 -0
  572. package/src/hooks/useInteractiveRole.doc.mjs +119 -0
  573. package/src/hooks/useLongPress.doc.mjs +123 -0
  574. package/src/hooks/useOverflow.doc.mjs +58 -17
  575. package/src/hooks/useOverflow.ts +16 -4
  576. package/src/hooks/useTypeahead.doc.mjs +153 -0
  577. package/src/index.ts +2 -0
  578. package/src/raw-import.d.ts +14 -0
  579. package/src/theme/Theme.test.tsx +104 -2
  580. package/src/theme/Theme.tsx +49 -14
  581. package/src/theme/defineTheme.test.ts +23 -0
  582. package/src/theme/defineTheme.ts +12 -3
  583. package/src/theme/derivedVarRegistry.test.ts +87 -3
  584. package/src/theme/derivedVarRegistry.ts +23 -4
  585. package/src/theme/generateThemeRules.test.ts +99 -1
  586. package/src/theme/generateThemeRules.ts +28 -0
  587. package/src/theme/syntax/tokens.ts +5 -2
  588. package/src/theme/useTheme.test.tsx +10 -0
  589. package/src/theme/useTheme.ts +16 -10
  590. package/src/utils/dateParser.test.ts +34 -2
  591. package/src/utils/dateParser.ts +11 -14
  592. package/src/utils/focusOutline.stylex.ts +50 -0
  593. package/src/utils/interactionOverlay.stylex.ts +74 -0
  594. package/src/utils/isApplePlatform.test.ts +61 -0
  595. package/src/utils/isApplePlatform.ts +37 -0
  596. package/src/utils/plainDate.test.ts +46 -0
  597. package/src/utils/plainDate.ts +18 -3
@@ -10,9 +10,12 @@
10
10
  */
11
11
 
12
12
  import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
13
+ import {act, 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';
18
+ import {registerIcons, resetIcons} from '../Icon';
16
19
  import {InputGroup} from '../InputGroup';
17
20
  import {NumberInput} from './NumberInput';
18
21
  import {defineTheme} from '../theme/defineTheme';
@@ -24,6 +27,7 @@ import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
24
27
  // file never match a leftover region.
25
28
  afterEach(() => {
26
29
  __resetLiveRegionsForTest();
30
+ resetIcons();
27
31
  });
28
32
 
29
33
  // Mock showPopover/hidePopover since jsdom does not implement them. Used by the
@@ -268,6 +272,31 @@ describe('NumberInput', () => {
268
272
  expect(input).toHaveAttribute('aria-valuenow', '1234');
269
273
  });
270
274
 
275
+ it('keeps ARIA value text on the committed value while an edit is pending', () => {
276
+ function ControlledNumberInput() {
277
+ const [controlledValue, setControlledValue] = useState(1234);
278
+ return (
279
+ <NumberInput
280
+ label="Revenue"
281
+ value={controlledValue}
282
+ onChange={setControlledValue}
283
+ formatValue={number => `$${number.toLocaleString('en-US')}`}
284
+ />
285
+ );
286
+ }
287
+ render(<ControlledNumberInput />);
288
+ const input = screen.getByRole('spinbutton');
289
+ fireEvent.focus(input);
290
+ fireEvent.input(input, {target: {value: '4200'}});
291
+
292
+ expect(input).toHaveAttribute('aria-valuenow', '1234');
293
+ expect(input).toHaveAttribute('aria-valuetext', '$1,234');
294
+
295
+ fireEvent.blur(input);
296
+ expect(input).toHaveAttribute('aria-valuenow', '4200');
297
+ expect(input).toHaveAttribute('aria-valuetext', '$4,200');
298
+ });
299
+
271
300
  it('shows the raw numeric value while focused and restores formatting on blur', () => {
272
301
  render(
273
302
  <NumberInput
@@ -325,7 +354,7 @@ describe('NumberInput', () => {
325
354
  });
326
355
 
327
356
  describe('onChange validation', () => {
328
- it('calls onChange with valid number when typing', async () => {
357
+ it('commits a valid number on blur', async () => {
329
358
  const user = userEvent.setup();
330
359
  const handleChange = vi.fn();
331
360
  render(
@@ -335,8 +364,10 @@ describe('NumberInput', () => {
335
364
  const input = screen.getByRole('spinbutton');
336
365
  await user.click(input);
337
366
  await user.type(input, '42');
367
+ expect(handleChange).not.toHaveBeenCalled();
338
368
 
339
- expect(handleChange).toHaveBeenCalledWith(4);
369
+ await user.tab();
370
+ expect(handleChange).toHaveBeenCalledTimes(1);
340
371
  expect(handleChange).toHaveBeenCalledWith(42);
341
372
  });
342
373
 
@@ -356,9 +387,7 @@ describe('NumberInput', () => {
356
387
  await user.click(input);
357
388
  await user.type(input, '10');
358
389
 
359
- // 1 is valid (<=5), but 10 is not
360
- expect(handleChange).toHaveBeenCalledWith(1);
361
- expect(handleChange).not.toHaveBeenCalledWith(10);
390
+ expect(handleChange).not.toHaveBeenCalled();
362
391
  });
363
392
 
364
393
  it('does not call onChange when value is below min', async () => {
@@ -397,9 +426,7 @@ describe('NumberInput', () => {
397
426
  await user.click(input);
398
427
  await user.type(input, '3.5');
399
428
 
400
- // 3 is valid, but 3.5 is not
401
- expect(handleChange).toHaveBeenCalledWith(3);
402
- expect(handleChange).not.toHaveBeenCalledWith(3.5);
429
+ expect(handleChange).not.toHaveBeenCalled();
403
430
  });
404
431
 
405
432
  it('calls onChange for decimal when isIntegerOnly is false', async () => {
@@ -412,11 +439,142 @@ describe('NumberInput', () => {
412
439
  const input = screen.getByRole('spinbutton');
413
440
  await user.click(input);
414
441
  await user.type(input, '3.5');
442
+ expect(handleChange).not.toHaveBeenCalled();
415
443
 
444
+ await user.tab();
416
445
  expect(handleChange).toHaveBeenCalledWith(3.5);
417
446
  });
418
447
  });
419
448
 
449
+ describe('invalid draft commit policy', () => {
450
+ function ControlledNumberInput({
451
+ initialValue = 7,
452
+ locale = 'en-US',
453
+ }: {
454
+ initialValue?: number | null;
455
+ locale?: 'en-US' | 'de-DE';
456
+ }) {
457
+ const [controlledValue, setControlledValue] = useState<number | null>(
458
+ initialValue,
459
+ );
460
+ return (
461
+ <InternationalizationProvider locale={locale}>
462
+ <NumberInput
463
+ label="Quantity"
464
+ value={controlledValue}
465
+ onChange={setControlledValue}
466
+ />
467
+ <output data-testid="committed">{String(controlledValue)}</output>
468
+ </InternationalizationProvider>
469
+ );
470
+ }
471
+
472
+ async function exerciseInvalidDraft(
473
+ entry: 'typing' | 'input',
474
+ commit: 'blur' | 'Enter',
475
+ ) {
476
+ const user = userEvent.setup();
477
+ render(<ControlledNumberInput />);
478
+ const input = screen.getByRole('spinbutton');
479
+ await user.click(input);
480
+ await user.clear(input);
481
+ if (entry === 'typing') {
482
+ await user.type(input, '1·234·567');
483
+ } else {
484
+ fireEvent.input(input, {target: {value: '1·234·567'}});
485
+ }
486
+
487
+ expect(input).toHaveValue('1·234·567');
488
+ expect(input).toHaveAttribute('aria-invalid', 'true');
489
+ expect(screen.getByTestId('committed')).toHaveTextContent('7');
490
+
491
+ if (commit === 'blur') {
492
+ await user.tab();
493
+ expect(input).toHaveValue('7');
494
+ expect(input).not.toHaveAttribute('aria-invalid');
495
+ } else {
496
+ await user.keyboard('{Enter}');
497
+ expect(input).toHaveValue('1·234·567');
498
+ expect(input).toHaveAttribute('aria-invalid', 'true');
499
+ }
500
+ expect(screen.getByTestId('committed')).toHaveTextContent('7');
501
+ }
502
+
503
+ it('rejects a sequentially typed invalid draft on blur', async () => {
504
+ await exerciseInvalidDraft('typing', 'blur');
505
+ });
506
+
507
+ it('rejects a one-shot invalid draft on blur', async () => {
508
+ await exerciseInvalidDraft('input', 'blur');
509
+ });
510
+
511
+ it('rejects a sequentially typed invalid draft on Enter', async () => {
512
+ await exerciseInvalidDraft('typing', 'Enter');
513
+ });
514
+
515
+ it('rejects a one-shot invalid draft on Enter', async () => {
516
+ await exerciseInvalidDraft('input', 'Enter');
517
+ });
518
+
519
+ it('commits a valid localized grouped number as one edit', async () => {
520
+ const user = userEvent.setup();
521
+ render(<ControlledNumberInput locale="de-DE" />);
522
+ const input = screen.getByRole('spinbutton');
523
+ await user.click(input);
524
+ await user.clear(input);
525
+ await user.type(input, '1.234.567');
526
+
527
+ expect(screen.getByTestId('committed')).toHaveTextContent('7');
528
+ await user.tab();
529
+ expect(screen.getByTestId('committed')).toHaveTextContent('1234567');
530
+ expect(input).toHaveValue('1234567');
531
+ });
532
+
533
+ it('keeps a controlled update behind an invalid focused draft', () => {
534
+ const onChange = vi.fn();
535
+ const {rerender} = render(
536
+ <NumberInput label="Quantity" value={7} onChange={onChange} />,
537
+ );
538
+ const input = screen.getByRole('spinbutton');
539
+ fireEvent.focus(input);
540
+ fireEvent.input(input, {target: {value: '1·234·567'}});
541
+
542
+ rerender(<NumberInput label="Quantity" value={9} onChange={onChange} />);
543
+ expect(input).toHaveValue('1·234·567');
544
+ fireEvent.blur(input);
545
+
546
+ expect(onChange).not.toHaveBeenCalled();
547
+ expect(input).toHaveValue('9');
548
+ });
549
+
550
+ it('waits for IME composition to finish before committing', () => {
551
+ const onChange = vi.fn();
552
+ render(<NumberInput label="Quantity" value={7} onChange={onChange} />);
553
+ const input = screen.getByRole('spinbutton');
554
+ fireEvent.focus(input);
555
+ fireEvent.input(input, {target: {value: '42'}});
556
+ fireEvent.keyDown(input, {key: 'Enter', isComposing: true});
557
+ expect(onChange).not.toHaveBeenCalled();
558
+
559
+ fireEvent.keyDown(input, {key: 'Enter'});
560
+ expect(onChange).toHaveBeenCalledTimes(1);
561
+ expect(onChange).toHaveBeenCalledWith(42);
562
+ });
563
+
564
+ it('steps from the committed value when the draft is invalid', () => {
565
+ const onChange = vi.fn();
566
+ render(<NumberInput label="Quantity" value={7} onChange={onChange} />);
567
+ const input = screen.getByRole('spinbutton');
568
+ fireEvent.focus(input);
569
+ fireEvent.input(input, {target: {value: '1·234·567'}});
570
+ fireEvent.keyDown(input, {key: 'ArrowUp'});
571
+
572
+ expect(onChange).toHaveBeenCalledTimes(1);
573
+ expect(onChange).toHaveBeenCalledWith(8);
574
+ expect(input).toHaveValue('7');
575
+ });
576
+ });
577
+
420
578
  describe('units prop', () => {
421
579
  it('renders units text when provided', () => {
422
580
  render(
@@ -540,6 +698,233 @@ describe('NumberInput', () => {
540
698
  });
541
699
  });
542
700
 
701
+ describe('range clamping on commit', () => {
702
+ it('commits an over-max entry at max on blur', async () => {
703
+ const user = userEvent.setup();
704
+ const handleChange = vi.fn();
705
+ render(
706
+ <NumberInput
707
+ label="Page"
708
+ value={null}
709
+ onChange={handleChange}
710
+ min={1}
711
+ max={2}
712
+ isIntegerOnly
713
+ />,
714
+ );
715
+
716
+ const input = screen.getByRole('spinbutton');
717
+ await user.click(input);
718
+ await user.type(input, '100');
719
+ handleChange.mockClear();
720
+ await user.tab();
721
+
722
+ expect(handleChange).toHaveBeenCalledWith(2);
723
+ });
724
+
725
+ it('commits an over-max entry at max on Enter', async () => {
726
+ const user = userEvent.setup();
727
+ const handleChange = vi.fn();
728
+ render(
729
+ <NumberInput
730
+ label="Rating"
731
+ value={null}
732
+ onChange={handleChange}
733
+ max={5}
734
+ />,
735
+ );
736
+
737
+ const input = screen.getByRole('spinbutton');
738
+ await user.click(input);
739
+ await user.type(input, '10');
740
+ handleChange.mockClear();
741
+ await user.keyboard('{Enter}');
742
+
743
+ expect(handleChange).toHaveBeenCalledWith(5);
744
+ });
745
+
746
+ it('commits a below-min entry at min', async () => {
747
+ const user = userEvent.setup();
748
+ const handleChange = vi.fn();
749
+ render(
750
+ <NumberInput
751
+ label="Age"
752
+ value={null}
753
+ onChange={handleChange}
754
+ min={0}
755
+ />,
756
+ );
757
+
758
+ const input = screen.getByRole('spinbutton');
759
+ await user.click(input);
760
+ await user.type(input, '-5');
761
+ await user.tab();
762
+
763
+ expect(handleChange).toHaveBeenCalledWith(0);
764
+ });
765
+
766
+ it('does not clamp while typing', async () => {
767
+ const user = userEvent.setup();
768
+ const handleChange = vi.fn();
769
+ render(
770
+ <NumberInput
771
+ label="Rating"
772
+ value={null}
773
+ onChange={handleChange}
774
+ max={5}
775
+ />,
776
+ );
777
+
778
+ const input = screen.getByRole('spinbutton');
779
+ await user.click(input);
780
+ await user.type(input, '10');
781
+
782
+ // The entry stays exactly as typed until it is committed.
783
+ expect(input).toHaveValue('10');
784
+ expect(handleChange).not.toHaveBeenCalledWith(5);
785
+ });
786
+
787
+ it('displays the clamped value after commit', async () => {
788
+ const user = userEvent.setup();
789
+ function ControlledNumberInput() {
790
+ const [value, setValue] = useState<number>(1);
791
+ return (
792
+ <NumberInput
793
+ label="Page"
794
+ value={value}
795
+ onChange={setValue}
796
+ min={1}
797
+ max={2}
798
+ isIntegerOnly
799
+ />
800
+ );
801
+ }
802
+ render(<ControlledNumberInput />);
803
+
804
+ const input = screen.getByRole('spinbutton');
805
+ await user.click(input);
806
+ await user.clear(input);
807
+ await user.type(input, '100');
808
+ await user.tab();
809
+
810
+ expect(input).toHaveValue('2');
811
+ });
812
+
813
+ it('shows the clamped value in the field after Enter', async () => {
814
+ const user = userEvent.setup();
815
+ function ControlledNumberInput() {
816
+ const [value, setValue] = useState<number | null>(null);
817
+ return (
818
+ <NumberInput
819
+ label="Rating"
820
+ value={value}
821
+ onChange={setValue}
822
+ min={1}
823
+ max={5}
824
+ />
825
+ );
826
+ }
827
+ render(<ControlledNumberInput />);
828
+
829
+ const input = screen.getByRole('spinbutton');
830
+ await user.click(input);
831
+ await user.type(input, '10{Enter}');
832
+
833
+ // Still focused: the field must not keep showing the rejected entry.
834
+ expect(input).toHaveValue('5');
835
+ });
836
+
837
+ it('reverts an entry that is not a usable number', async () => {
838
+ const user = userEvent.setup();
839
+ const handleChange = vi.fn();
840
+ render(
841
+ <NumberInput
842
+ label="Count"
843
+ value={null}
844
+ onChange={handleChange}
845
+ max={10}
846
+ isIntegerOnly
847
+ />,
848
+ );
849
+
850
+ const input = screen.getByRole('spinbutton');
851
+ await user.click(input);
852
+ await user.type(input, '12.5');
853
+ handleChange.mockClear();
854
+ await user.tab();
855
+
856
+ expect(handleChange).not.toHaveBeenCalled();
857
+ expect(input).toHaveValue('');
858
+ });
859
+
860
+ it('rounds a fractional max inwards for an integer-only field', async () => {
861
+ const user = userEvent.setup();
862
+ const handleChange = vi.fn();
863
+ render(
864
+ <NumberInput
865
+ label="Count"
866
+ value={null}
867
+ onChange={handleChange}
868
+ max={9.5}
869
+ isIntegerOnly
870
+ />,
871
+ );
872
+
873
+ const input = screen.getByRole('spinbutton');
874
+ await user.click(input);
875
+ await user.type(input, '20');
876
+ handleChange.mockClear();
877
+ await user.tab();
878
+
879
+ expect(handleChange).toHaveBeenCalledWith(9);
880
+ });
881
+
882
+ it('rounds a fractional min inwards for an integer-only field', async () => {
883
+ const user = userEvent.setup();
884
+ const handleChange = vi.fn();
885
+ render(
886
+ <NumberInput
887
+ label="Count"
888
+ value={null}
889
+ onChange={handleChange}
890
+ min={0.5}
891
+ isIntegerOnly
892
+ />,
893
+ );
894
+
895
+ const input = screen.getByRole('spinbutton');
896
+ await user.click(input);
897
+ await user.type(input, '0');
898
+ handleChange.mockClear();
899
+ await user.tab();
900
+
901
+ expect(handleChange).toHaveBeenCalledWith(1);
902
+ });
903
+
904
+ it('does not clamp when no value can satisfy both bounds', async () => {
905
+ const user = userEvent.setup();
906
+ const handleChange = vi.fn();
907
+ render(
908
+ <NumberInput
909
+ label="Count"
910
+ value={null}
911
+ onChange={handleChange}
912
+ min={5}
913
+ max={2}
914
+ />,
915
+ );
916
+
917
+ const input = screen.getByRole('spinbutton');
918
+ await user.click(input);
919
+ await user.type(input, '10');
920
+ handleChange.mockClear();
921
+ await user.tab();
922
+
923
+ expect(handleChange).not.toHaveBeenCalled();
924
+ expect(input).toHaveValue('');
925
+ });
926
+ });
927
+
543
928
  describe('status prop', () => {
544
929
  it('renders with error status icon', () => {
545
930
  const {container} = render(
@@ -817,6 +1202,32 @@ describe('NumberInput', () => {
817
1202
  expect(data.get('revenue')).toBe('1234');
818
1203
  });
819
1204
 
1205
+ it('submits the committed value in formatted mode until an edit commits', () => {
1206
+ function ControlledForm() {
1207
+ const [controlledValue, setControlledValue] = useState(7);
1208
+ return (
1209
+ <form>
1210
+ <NumberInput
1211
+ label="Quantity"
1212
+ htmlName="quantity"
1213
+ value={controlledValue}
1214
+ onChange={setControlledValue}
1215
+ formatValue={String}
1216
+ />
1217
+ </form>
1218
+ );
1219
+ }
1220
+ const {container} = render(<ControlledForm />);
1221
+ const form = container.querySelector('form')!;
1222
+ const input = screen.getByRole('spinbutton');
1223
+ fireEvent.focus(input);
1224
+ fireEvent.input(input, {target: {value: '42'}});
1225
+
1226
+ expect(new FormData(form).get('quantity')).toBe('7');
1227
+ fireEvent.blur(input);
1228
+ expect(new FormData(form).get('quantity')).toBe('42');
1229
+ });
1230
+
820
1231
  it('is excluded from form data when disabled', () => {
821
1232
  const {container} = render(
822
1233
  <form>
@@ -1054,6 +1465,25 @@ describe('NumberInput', () => {
1054
1465
  ).not.toBeInTheDocument();
1055
1466
  });
1056
1467
 
1468
+ it('keeps Tab moving forward when an empty field has an invalid draft', async () => {
1469
+ const user = userEvent.setup();
1470
+ render(
1471
+ <>
1472
+ <NumberInput label="Qty" value={null} onChange={() => {}} hasClear />
1473
+ <button type="button">Next field</button>
1474
+ </>,
1475
+ );
1476
+ const input = screen.getByRole('spinbutton');
1477
+ await user.click(input);
1478
+ await user.type(input, 'invalid');
1479
+ expect(
1480
+ screen.queryByRole('button', {name: 'Clear Qty'}),
1481
+ ).not.toBeInTheDocument();
1482
+
1483
+ await user.tab();
1484
+ expect(screen.getByRole('button', {name: 'Next field'})).toHaveFocus();
1485
+ });
1486
+
1057
1487
  it('does not show clear button when hasClear is false', () => {
1058
1488
  render(<NumberInput label="Qty" value={5} onChange={() => {}} />);
1059
1489
  expect(
@@ -1330,6 +1760,34 @@ describe('NumberInput statusVariant forwarding', () => {
1330
1760
  });
1331
1761
 
1332
1762
  describe('NumberInput stepping', () => {
1763
+ const stepInteractions = [
1764
+ [
1765
+ 'keyboard',
1766
+ (input: HTMLElement, direction: 'up' | 'down') =>
1767
+ fireEvent.keyDown(input, {
1768
+ key: direction === 'up' ? 'ArrowUp' : 'ArrowDown',
1769
+ }),
1770
+ ],
1771
+ [
1772
+ 'wheel',
1773
+ (input: HTMLElement, direction: 'up' | 'down') => {
1774
+ input.focus();
1775
+ act(() => {
1776
+ fireEvent.wheel(input, {deltaY: direction === 'up' ? -100 : 100});
1777
+ });
1778
+ },
1779
+ ],
1780
+ [
1781
+ 'number stepper',
1782
+ (_input: HTMLElement, direction: 'up' | 'down') =>
1783
+ fireEvent.click(
1784
+ screen.getByRole('button', {
1785
+ name: direction === 'up' ? 'Increment Amount' : 'Decrement Amount',
1786
+ }),
1787
+ ),
1788
+ ],
1789
+ ] as const;
1790
+
1333
1791
  it('increments with ArrowUp and decrements with ArrowDown', () => {
1334
1792
  const onChange = vi.fn();
1335
1793
  render(<NumberInput label="Amount" value={5} onChange={onChange} />);
@@ -1464,6 +1922,76 @@ describe('NumberInput stepping', () => {
1464
1922
  expect(onChange).not.toHaveBeenCalled();
1465
1923
  });
1466
1924
 
1925
+ it.each(stepInteractions)(
1926
+ 'clamps at a fractional max after rounding via the %s',
1927
+ (_name, interact) => {
1928
+ const onChange = vi.fn();
1929
+ render(
1930
+ <NumberInput
1931
+ label="Amount"
1932
+ value={99}
1933
+ onChange={onChange}
1934
+ max={99.99}
1935
+ hasNumberSteppers
1936
+ />,
1937
+ );
1938
+
1939
+ interact(screen.getByRole('spinbutton'), 'up');
1940
+
1941
+ expect(onChange).toHaveBeenCalledWith(99.99);
1942
+ expect(onChange).not.toHaveBeenCalledWith(100);
1943
+ },
1944
+ );
1945
+
1946
+ it.each(stepInteractions)(
1947
+ 'clamps at a fractional min after rounding via the %s',
1948
+ (_name, interact) => {
1949
+ const onChange = vi.fn();
1950
+ const min = 4e-13;
1951
+ render(
1952
+ <NumberInput
1953
+ label="Amount"
1954
+ value={0.5}
1955
+ onChange={onChange}
1956
+ min={min}
1957
+ hasNumberSteppers
1958
+ />,
1959
+ );
1960
+
1961
+ interact(screen.getByRole('spinbutton'), 'down');
1962
+
1963
+ expect(onChange).toHaveBeenCalledWith(min);
1964
+ expect(onChange).not.toHaveBeenCalledWith(0);
1965
+ },
1966
+ );
1967
+
1968
+ it('reflects a reached fractional max in the spinbutton and stepper state', () => {
1969
+ function ControlledNumberInput() {
1970
+ const [value, setValue] = useState(99);
1971
+ return (
1972
+ <NumberInput
1973
+ label="Amount"
1974
+ value={value}
1975
+ onChange={setValue}
1976
+ max={99.99}
1977
+ hasNumberSteppers
1978
+ />
1979
+ );
1980
+ }
1981
+ render(<ControlledNumberInput />);
1982
+
1983
+ const input = screen.getByRole('spinbutton');
1984
+ const increment = screen.getByRole('button', {name: 'Increment Amount'});
1985
+ expect(increment).toBeEnabled();
1986
+
1987
+ fireEvent.click(increment);
1988
+
1989
+ expect(input).toHaveValue('99.99');
1990
+ expect(input).toHaveAttribute('aria-valuenow', '99.99');
1991
+ expect(input).not.toHaveAttribute('aria-invalid');
1992
+ expect(increment).toBeDisabled();
1993
+ });
1994
+
1467
1995
  it('allows wheel stepping by default and consumes the focused gesture', () => {
1468
1996
  const onScrollableWheel = vi.fn();
1469
1997
  const onChange = vi.fn();
@@ -1646,6 +2174,33 @@ describe('NumberInput stepping', () => {
1646
2174
  ).toHaveAttribute('tabindex', '-1');
1647
2175
  });
1648
2176
 
2177
+ it('uses the NumberInput extension icon for both stepper buttons', () => {
2178
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
2179
+ registerIcons({
2180
+ chevronDown: <svg data-testid="generic-chevron-down" />,
2181
+ 'numberInput:stepperDown': <svg data-testid="number-stepper-down" />,
2182
+ });
2183
+
2184
+ render(
2185
+ <NumberInput
2186
+ label="Quantity"
2187
+ value={5}
2188
+ onChange={() => {}}
2189
+ hasNumberSteppers
2190
+ />,
2191
+ );
2192
+
2193
+ const stepperIcons = screen.getAllByTestId('number-stepper-down');
2194
+ expect(stepperIcons).toHaveLength(2);
2195
+ for (const icon of stepperIcons) {
2196
+ expect(icon.parentElement).toHaveAttribute('data-size', 'xsm');
2197
+ }
2198
+ expect(
2199
+ screen.queryByTestId('generic-chevron-down'),
2200
+ ).not.toBeInTheDocument();
2201
+ warnSpy.mockRestore();
2202
+ });
2203
+
1649
2204
  it('steps the value and returns focus to the input', () => {
1650
2205
  const onChange = vi.fn();
1651
2206
  render(