@astryxdesign/core 0.1.8 → 0.1.9

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 (487) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/dist/AppShell/AppShell.d.ts.map +1 -1
  3. package/dist/AppShell/AppShell.js +6 -2
  4. package/dist/AppShell/AppShellMobileContext.d.ts +8 -0
  5. package/dist/AppShell/AppShellMobileContext.d.ts.map +1 -1
  6. package/dist/Avatar/Avatar.d.ts +18 -2
  7. package/dist/Avatar/Avatar.d.ts.map +1 -1
  8. package/dist/Avatar/Avatar.js +63 -7
  9. package/dist/Avatar/AvatarStatusDot.d.ts +33 -5
  10. package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
  11. package/dist/Avatar/AvatarStatusDot.js +144 -19
  12. package/dist/Banner/Banner.d.ts +9 -1
  13. package/dist/Banner/Banner.d.ts.map +1 -1
  14. package/dist/Banner/Banner.js +31 -2
  15. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +31 -2
  16. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  17. package/dist/Breadcrumbs/BreadcrumbItem.js +208 -6
  18. package/dist/Breadcrumbs/index.d.ts +5 -0
  19. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  20. package/dist/Breadcrumbs/index.js +9 -1
  21. package/dist/Button/Button.d.ts +8 -2
  22. package/dist/Button/Button.d.ts.map +1 -1
  23. package/dist/Button/Button.js +31 -2
  24. package/dist/ButtonGroup/ButtonGroup.d.ts +9 -1
  25. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  26. package/dist/ButtonGroup/ButtonGroup.js +30 -1
  27. package/dist/Calendar/Calendar.d.ts.map +1 -1
  28. package/dist/Calendar/Calendar.js +30 -2
  29. package/dist/Calendar/dayCellUtils.js +1 -1
  30. package/dist/Card/Card.d.ts +8 -2
  31. package/dist/Card/Card.d.ts.map +1 -1
  32. package/dist/Card/Card.js +28 -2
  33. package/dist/Carousel/Carousel.d.ts +4 -1
  34. package/dist/Carousel/Carousel.d.ts.map +1 -1
  35. package/dist/Carousel/Carousel.js +24 -4
  36. package/dist/Chat/ChatComposer.d.ts +8 -0
  37. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  38. package/dist/Chat/ChatComposer.js +46 -7
  39. package/dist/Chat/ChatComposerDrawer.d.ts +2 -0
  40. package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
  41. package/dist/Chat/ChatComposerDrawer.js +11 -1
  42. package/dist/Chat/ChatComposerInput.d.ts +20 -3
  43. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  44. package/dist/Chat/ChatComposerInput.js +35 -3
  45. package/dist/Chat/ChatContext.d.ts +33 -0
  46. package/dist/Chat/ChatContext.d.ts.map +1 -1
  47. package/dist/Chat/ChatContext.js +16 -2
  48. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  49. package/dist/Chat/ChatDictationButton.js +1 -1
  50. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  51. package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
  52. package/dist/Chat/ChatLayoutScrollButton.js +4 -4
  53. package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
  54. package/dist/Chat/ChatSendButton.d.ts.map +1 -1
  55. package/dist/Chat/ChatSendButton.js +7 -1
  56. package/dist/Chat/ChatToolCalls.d.ts +5 -1
  57. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  58. package/dist/Chat/ChatToolCalls.js +18 -9
  59. package/dist/Chat/index.d.ts +2 -1
  60. package/dist/Chat/index.d.ts.map +1 -1
  61. package/dist/Chat/index.js +1 -1
  62. package/dist/Chat/useChatStreamScroll.d.ts +2 -1
  63. package/dist/Chat/useChatStreamScroll.d.ts.map +1 -1
  64. package/dist/Chat/useChatStreamScroll.js +22 -8
  65. package/dist/Chat/useSpeechRecognition.d.ts +0 -1
  66. package/dist/Chat/useSpeechRecognition.d.ts.map +1 -1
  67. package/dist/Chat/useSpeechRecognition.js +1 -1
  68. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  69. package/dist/CheckboxInput/CheckboxInput.js +1 -1
  70. package/dist/CheckboxList/CheckboxListItem.d.ts +20 -1
  71. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  72. package/dist/CheckboxList/CheckboxListItem.js +11 -1
  73. package/dist/ClickableCard/ClickableCard.d.ts +9 -2
  74. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  75. package/dist/ClickableCard/ClickableCard.js +5 -2
  76. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  77. package/dist/CodeBlock/CodeBlock.js +8 -1
  78. package/dist/CommandPalette/CommandPalette.d.ts +1 -1
  79. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  80. package/dist/CommandPalette/CommandPalette.js +3 -1
  81. package/dist/CommandPalette/CommandPaletteGroup.d.ts.map +1 -1
  82. package/dist/CommandPalette/CommandPaletteGroup.js +9 -2
  83. package/dist/CommandPalette/CommandPaletteInput.d.ts +7 -1
  84. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  85. package/dist/CommandPalette/CommandPaletteInput.js +6 -1
  86. package/dist/DateInput/DateInput.d.ts +47 -1
  87. package/dist/DateInput/DateInput.d.ts.map +1 -1
  88. package/dist/DateInput/DateInput.js +26 -2
  89. package/dist/DateInput/index.d.ts +1 -1
  90. package/dist/DateInput/index.d.ts.map +1 -1
  91. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  92. package/dist/DateTimeInput/DateTimeInput.js +10 -1
  93. package/dist/Dialog/Dialog.d.ts +3 -0
  94. package/dist/Dialog/Dialog.d.ts.map +1 -1
  95. package/dist/Dialog/Dialog.js +57 -6
  96. package/dist/Dialog/DialogContext.d.ts +7 -0
  97. package/dist/Dialog/DialogContext.d.ts.map +1 -1
  98. package/dist/Dialog/DialogContext.js +2 -2
  99. package/dist/Dialog/DialogHeader.d.ts +5 -2
  100. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  101. package/dist/Dialog/DialogHeader.js +6 -1
  102. package/dist/Divider/Divider.js +1 -1
  103. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +1 -2
  104. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  105. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +1 -2
  106. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +1 -2
  107. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
  108. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +1 -2
  109. package/dist/Field/Field.d.ts.map +1 -1
  110. package/dist/Field/Field.js +2 -3
  111. package/dist/Field/FieldLabel.d.ts +1 -1
  112. package/dist/Field/FieldLabel.d.ts.map +1 -1
  113. package/dist/Field/FieldLabel.js +42 -15
  114. package/dist/FieldStatus/FieldStatus.d.ts +8 -0
  115. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  116. package/dist/FieldStatus/FieldStatus.js +22 -4
  117. package/dist/FileInput/FileInput.d.ts.map +1 -1
  118. package/dist/FileInput/FileInput.js +10 -2
  119. package/dist/HoverCard/HoverCard.d.ts +9 -1
  120. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  121. package/dist/HoverCard/HoverCard.js +2 -0
  122. package/dist/HoverCard/useHoverCard.d.ts +8 -0
  123. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  124. package/dist/HoverCard/useHoverCard.js +7 -2
  125. package/dist/Icon/Icon.d.ts +7 -2
  126. package/dist/Icon/Icon.d.ts.map +1 -1
  127. package/dist/Item/Item.d.ts +6 -1
  128. package/dist/Item/Item.d.ts.map +1 -1
  129. package/dist/Item/Item.js +22 -1
  130. package/dist/Layer/useLayer.d.ts +6 -0
  131. package/dist/Layer/useLayer.d.ts.map +1 -1
  132. package/dist/Layer/useLayer.js +2 -0
  133. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  134. package/dist/MobileNav/MobileNav.js +6 -1
  135. package/dist/MobileNav/MobileNavToggle.d.ts.map +1 -1
  136. package/dist/MobileNav/MobileNavToggle.js +4 -0
  137. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  138. package/dist/MultiSelector/MultiSelector.js +39 -12
  139. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  140. package/dist/MultiSelector/hooks.js +16 -0
  141. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  142. package/dist/Outline/Outline.d.ts +0 -1
  143. package/dist/Outline/Outline.d.ts.map +1 -1
  144. package/dist/Outline/Outline.js +0 -1
  145. package/dist/OverflowList/OverflowList.d.ts +20 -3
  146. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  147. package/dist/OverflowList/OverflowList.js +29 -4
  148. package/dist/Popover/Popover.d.ts.map +1 -1
  149. package/dist/Popover/Popover.js +3 -2
  150. package/dist/Popover/usePopover.d.ts.map +1 -1
  151. package/dist/Popover/usePopover.js +3 -9
  152. package/dist/PowerSearch/PowerSearchEditPopover.js +1 -1
  153. package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
  154. package/dist/SelectableCard/SelectableCard.d.ts +9 -2
  155. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  156. package/dist/SelectableCard/SelectableCard.js +16 -13
  157. package/dist/Selector/Selector.d.ts.map +1 -1
  158. package/dist/Selector/Selector.js +4 -2
  159. package/dist/Selector/hooks.d.ts.map +1 -1
  160. package/dist/Selector/hooks.js +16 -0
  161. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  162. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  163. package/dist/SideNav/SideNavHeading.js +21 -8
  164. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  165. package/dist/Slider/Slider.d.ts.map +1 -1
  166. package/dist/Slider/Slider.js +19 -2
  167. package/dist/Stack/stack.stylex.d.ts +9 -9
  168. package/dist/Table/BaseTable.d.ts.map +1 -1
  169. package/dist/Table/BaseTable.js +3 -2
  170. package/dist/Table/index.d.ts +2 -0
  171. package/dist/Table/index.d.ts.map +1 -1
  172. package/dist/Table/index.js +1 -0
  173. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  174. package/dist/Table/plugins/rowStatus/index.d.ts +3 -0
  175. package/dist/Table/plugins/rowStatus/index.d.ts.map +1 -0
  176. package/dist/Table/plugins/rowStatus/index.js +3 -0
  177. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts +60 -0
  178. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -0
  179. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +145 -0
  180. package/dist/Table/plugins/selection/useTableSelection.d.ts +13 -0
  181. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  182. package/dist/Table/plugins/selection/useTableSelection.js +4 -1
  183. package/dist/Table/plugins/tree/useTableTreeData.d.ts +16 -0
  184. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  185. package/dist/Table/plugins/tree/useTableTreeData.js +87 -0
  186. package/dist/Table/plugins/tree/useTableTreeState.d.ts +6 -0
  187. package/dist/Table/plugins/tree/useTableTreeState.d.ts.map +1 -1
  188. package/dist/Table/plugins/tree/useTableTreeState.js +22 -1
  189. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  190. package/dist/Table/useBaseTablePlugins.js +5 -3
  191. package/dist/Thumbnail/Thumbnail.d.ts +26 -5
  192. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  193. package/dist/Thumbnail/Thumbnail.js +97 -54
  194. package/dist/Thumbnail/thumbnail.markers.stylex.d.ts +9 -0
  195. package/dist/Thumbnail/thumbnail.markers.stylex.d.ts.map +1 -0
  196. package/dist/Thumbnail/thumbnail.markers.stylex.js +14 -0
  197. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  198. package/dist/TimeInput/TimeInput.js +15 -4
  199. package/dist/Timestamp/Timestamp.d.ts +3 -1
  200. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  201. package/dist/Timestamp/Timestamp.js +8 -6
  202. package/dist/Toast/useToast.d.ts.map +1 -1
  203. package/dist/Toast/useToast.js +2 -5
  204. package/dist/ToggleButton/ToggleButtonGroup.d.ts +0 -1
  205. package/dist/ToggleButton/ToggleButtonGroup.d.ts.map +1 -1
  206. package/dist/ToggleButton/ToggleButtonGroup.js +1 -1
  207. package/dist/Tokenizer/Tokenizer.d.ts +1 -1
  208. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  209. package/dist/Tokenizer/Tokenizer.js +16 -4
  210. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  211. package/dist/TopNav/TopNavHeading.js +18 -8
  212. package/dist/TopNav/TopNavMegaMenu.js +10 -4
  213. package/dist/TopNav/TopNavMegaMenuFeaturedCard.d.ts +9 -1
  214. package/dist/TopNav/TopNavMegaMenuFeaturedCard.d.ts.map +1 -1
  215. package/dist/TopNav/TopNavMegaMenuFeaturedCard.js +8 -1
  216. package/dist/TopNav/TopNavMenu.d.ts +2 -1
  217. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  218. package/dist/TopNav/TopNavMenu.js +68 -6
  219. package/dist/TreeList/TreeList.d.ts +12 -3
  220. package/dist/TreeList/TreeList.d.ts.map +1 -1
  221. package/dist/TreeList/TreeList.js +2 -0
  222. package/dist/TreeList/TreeListBranches.d.ts +5 -0
  223. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  224. package/dist/TreeList/TreeListBranches.js +10 -4
  225. package/dist/TreeList/TreeListItem.d.ts +8 -2
  226. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  227. package/dist/TreeList/TreeListItem.js +27 -7
  228. package/dist/TreeList/TreeListTypes.d.ts +25 -1
  229. package/dist/TreeList/TreeListTypes.d.ts.map +1 -1
  230. package/dist/TreeList/index.d.ts +2 -2
  231. package/dist/TreeList/index.d.ts.map +1 -1
  232. package/dist/astryx.css +28 -18
  233. package/dist/astryx.umd.js +51 -51
  234. package/dist/astryx.umd.js.map +4 -4
  235. package/dist/authoring/doc.d.ts.map +1 -1
  236. package/dist/docs-types.d.ts +8 -0
  237. package/dist/docs-types.d.ts.map +1 -1
  238. package/dist/hooks/computeOverflow.d.ts +47 -0
  239. package/dist/hooks/computeOverflow.d.ts.map +1 -0
  240. package/dist/hooks/computeOverflow.js +213 -0
  241. package/dist/hooks/index.d.ts +1 -0
  242. package/dist/hooks/index.d.ts.map +1 -1
  243. package/dist/hooks/index.js +2 -1
  244. package/dist/hooks/useDevWarning.d.ts +18 -0
  245. package/dist/hooks/useDevWarning.d.ts.map +1 -0
  246. package/dist/hooks/useDevWarning.js +49 -0
  247. package/dist/hooks/useOverflow.d.ts +19 -0
  248. package/dist/hooks/useOverflow.d.ts.map +1 -1
  249. package/dist/hooks/useOverflow.js +36 -25
  250. package/dist/i18n/resolve.d.ts.map +1 -1
  251. package/dist/i18n/resolve.js +7 -18
  252. package/dist/theme/Theme.d.ts.map +1 -1
  253. package/dist/theme/Theme.js +2 -7
  254. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  255. package/dist/theme/generateThemeRules.js +51 -0
  256. package/dist/theme/hct.d.ts +0 -1
  257. package/dist/theme/hct.d.ts.map +1 -1
  258. package/dist/theme/hct.js +1 -1
  259. package/dist/theme/syntax/defineSyntaxTheme.d.ts.map +1 -1
  260. package/dist/theme/syntax/defineSyntaxTheme.js +2 -1
  261. package/dist/utils/devWarning.d.ts +42 -0
  262. package/dist/utils/devWarning.d.ts.map +1 -0
  263. package/dist/utils/devWarning.js +88 -0
  264. package/dist/utils/index.d.ts +3 -1
  265. package/dist/utils/index.d.ts.map +1 -1
  266. package/dist/utils/index.js +3 -2
  267. package/dist/utils/plainDate.d.ts +30 -0
  268. package/dist/utils/plainDate.d.ts.map +1 -1
  269. package/dist/utils/plainDate.js +53 -0
  270. package/dist/utils/types.d.ts +13 -0
  271. package/dist/utils/types.d.ts.map +1 -1
  272. package/locales/en.json +24 -0
  273. package/locales/fr-FR.json +14 -0
  274. package/locales/pseudo.json +18 -0
  275. package/package.json +1 -1
  276. package/src/AppShell/AppShell.tsx +6 -0
  277. package/src/AppShell/AppShellMobileContext.tsx +8 -0
  278. package/src/AppShell/useAppShellMobile.doc.mjs +8 -0
  279. package/src/Avatar/Avatar.doc.mjs +15 -3
  280. package/src/Avatar/Avatar.test.tsx +156 -1
  281. package/src/Avatar/Avatar.tsx +96 -4
  282. package/src/Avatar/AvatarStatusDot.doc.mjs +23 -12
  283. package/src/Avatar/AvatarStatusDot.test.tsx +284 -0
  284. package/src/Avatar/AvatarStatusDot.tsx +178 -21
  285. package/src/Banner/Banner.doc.mjs +9 -0
  286. package/src/Banner/Banner.test.tsx +30 -0
  287. package/src/Banner/Banner.tsx +47 -3
  288. package/src/Breadcrumbs/BreadcrumbItem.doc.mjs +26 -0
  289. package/src/Breadcrumbs/BreadcrumbItem.tsx +333 -14
  290. package/src/Breadcrumbs/Breadcrumbs.doc.mjs +2 -0
  291. package/src/Breadcrumbs/Breadcrumbs.test.tsx +284 -2
  292. package/src/Breadcrumbs/index.ts +27 -0
  293. package/src/Button/Button.doc.mjs +9 -0
  294. package/src/Button/Button.test.tsx +49 -0
  295. package/src/Button/Button.tsx +25 -1
  296. package/src/ButtonGroup/ButtonGroup.doc.mjs +3 -0
  297. package/src/ButtonGroup/ButtonGroup.test.tsx +37 -0
  298. package/src/ButtonGroup/ButtonGroup.tsx +32 -0
  299. package/src/Calendar/Calendar.doc.mjs +12 -1
  300. package/src/Calendar/Calendar.test.tsx +193 -0
  301. package/src/Calendar/Calendar.tsx +38 -16
  302. package/src/Calendar/dayCellUtils.test.ts +5 -0
  303. package/src/Calendar/dayCellUtils.ts +1 -1
  304. package/src/Card/Card.doc.mjs +9 -0
  305. package/src/Card/Card.test.tsx +21 -3
  306. package/src/Card/Card.tsx +35 -2
  307. package/src/Carousel/Carousel.doc.mjs +1 -0
  308. package/src/Carousel/Carousel.test.tsx +53 -0
  309. package/src/Carousel/Carousel.tsx +34 -4
  310. package/src/Chat/ChatComposer.doc.mjs +12 -3
  311. package/src/Chat/ChatComposer.test.tsx +115 -0
  312. package/src/Chat/ChatComposer.tsx +73 -9
  313. package/src/Chat/ChatComposerDrawer.test.tsx +61 -0
  314. package/src/Chat/ChatComposerDrawer.tsx +11 -1
  315. package/src/Chat/ChatComposerInput.doc.mjs +7 -0
  316. package/src/Chat/ChatComposerInput.test.tsx +103 -0
  317. package/src/Chat/ChatComposerInput.tsx +49 -3
  318. package/src/Chat/ChatContext.tsx +37 -10
  319. package/src/Chat/ChatDictationButton.tsx +4 -1
  320. package/src/Chat/ChatLayout.tsx +3 -1
  321. package/src/Chat/ChatLayoutScrollButton.tsx +7 -2
  322. package/src/Chat/ChatMessageMetadata.tsx +1 -2
  323. package/src/Chat/ChatSendButton.tsx +11 -1
  324. package/src/Chat/ChatToolCalls.test.tsx +64 -0
  325. package/src/Chat/ChatToolCalls.tsx +25 -4
  326. package/src/Chat/index.ts +5 -1
  327. package/src/Chat/useChatStreamScroll.test.tsx +49 -0
  328. package/src/Chat/useChatStreamScroll.ts +27 -10
  329. package/src/Chat/useSpeechRecognition.ts +1 -1
  330. package/src/Chat/useTriggerMenu.tsx +3 -1
  331. package/src/CheckboxInput/CheckboxInput.test.tsx +32 -1
  332. package/src/CheckboxInput/CheckboxInput.tsx +1 -1
  333. package/src/CheckboxList/CheckboxList.test.tsx +81 -3
  334. package/src/CheckboxList/CheckboxListItem.doc.mjs +33 -5
  335. package/src/CheckboxList/CheckboxListItem.tsx +41 -5
  336. package/src/ClickableCard/ClickableCard.doc.mjs +2 -0
  337. package/src/ClickableCard/ClickableCard.test.tsx +34 -0
  338. package/src/ClickableCard/ClickableCard.tsx +13 -3
  339. package/src/CodeBlock/CodeBlock.test.tsx +56 -0
  340. package/src/CodeBlock/CodeBlock.tsx +9 -1
  341. package/src/CommandPalette/CommandPalette.doc.mjs +1 -0
  342. package/src/CommandPalette/CommandPalette.tsx +3 -1
  343. package/src/CommandPalette/CommandPaletteGroup.test.tsx +71 -0
  344. package/src/CommandPalette/CommandPaletteGroup.tsx +12 -1
  345. package/src/CommandPalette/CommandPaletteInput.doc.mjs +26 -9
  346. package/src/CommandPalette/CommandPaletteInput.test.tsx +34 -0
  347. package/src/CommandPalette/CommandPaletteInput.tsx +11 -0
  348. package/src/ContextMenu/ContextMenu.test.tsx +193 -0
  349. package/src/DateInput/DateInput.doc.mjs +16 -0
  350. package/src/DateInput/DateInput.test.tsx +108 -0
  351. package/src/DateInput/DateInput.tsx +68 -3
  352. package/src/DateInput/index.ts +1 -0
  353. package/src/DateTimeInput/DateTimeInput.test.tsx +35 -0
  354. package/src/DateTimeInput/DateTimeInput.tsx +14 -2
  355. package/src/Dialog/Dialog.doc.mjs +2 -2
  356. package/src/Dialog/Dialog.test.tsx +77 -4
  357. package/src/Dialog/Dialog.tsx +89 -5
  358. package/src/Dialog/DialogContext.ts +9 -2
  359. package/src/Dialog/DialogHeader.doc.mjs +2 -2
  360. package/src/Dialog/DialogHeader.tsx +9 -2
  361. package/src/Divider/Divider.tsx +2 -2
  362. package/src/DropdownMenu/DropdownMenu.test.tsx +169 -0
  363. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +1 -2
  364. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +1 -2
  365. package/src/Field/Field.test.tsx +57 -12
  366. package/src/Field/Field.tsx +6 -5
  367. package/src/Field/FieldLabel.tsx +8 -0
  368. package/src/FieldStatus/FieldStatus.doc.mjs +17 -5
  369. package/src/FieldStatus/FieldStatus.test.tsx +78 -40
  370. package/src/FieldStatus/FieldStatus.tsx +22 -4
  371. package/src/FileInput/FileInput.test.tsx +52 -3
  372. package/src/FileInput/FileInput.tsx +13 -2
  373. package/src/HoverCard/HoverCard.doc.mjs +13 -0
  374. package/src/HoverCard/HoverCard.test.tsx +21 -2
  375. package/src/HoverCard/HoverCard.tsx +11 -0
  376. package/src/HoverCard/useHoverCard.doc.mjs +2 -0
  377. package/src/HoverCard/useHoverCard.tsx +24 -2
  378. package/src/Icon/Icon.doc.mjs +3 -2
  379. package/src/Icon/Icon.tsx +7 -2
  380. package/src/IconButton/IconButton.doc.mjs +8 -0
  381. package/src/IconButton/IconButton.test.tsx +15 -18
  382. package/src/Item/Item.test.tsx +40 -18
  383. package/src/Item/Item.tsx +38 -2
  384. package/src/Layer/useLayer.tsx +8 -0
  385. package/src/List/List.test.tsx +9 -8
  386. package/src/MobileNav/MobileNav.tsx +12 -1
  387. package/src/MobileNav/MobileNavToggle.test.tsx +116 -0
  388. package/src/MobileNav/MobileNavToggle.tsx +9 -1
  389. package/src/MultiSelector/MultiSelector.doc.mjs +4 -2
  390. package/src/MultiSelector/MultiSelector.test.tsx +172 -3
  391. package/src/MultiSelector/MultiSelector.tsx +55 -12
  392. package/src/MultiSelector/hooks.ts +17 -0
  393. package/src/NumberInput/NumberInput.test.tsx +11 -7
  394. package/src/NumberInput/NumberInput.tsx +1 -2
  395. package/src/Outline/Outline.tsx +0 -1
  396. package/src/OverflowList/OverflowList.doc.mjs +39 -6
  397. package/src/OverflowList/OverflowList.test.tsx +111 -0
  398. package/src/OverflowList/OverflowList.tsx +48 -8
  399. package/src/Popover/Popover.tsx +7 -4
  400. package/src/Popover/usePopover.tsx +9 -13
  401. package/src/PowerSearch/PowerSearch.doc.mjs +14 -0
  402. package/src/PowerSearch/PowerSearch.test.tsx +20 -0
  403. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +67 -0
  404. package/src/PowerSearch/PowerSearchEditPopover.tsx +1 -1
  405. package/src/PowerSearch/usePowerSearchSource.ts +1 -2
  406. package/src/SelectableCard/SelectableCard.doc.mjs +2 -0
  407. package/src/SelectableCard/SelectableCard.test.tsx +46 -4
  408. package/src/SelectableCard/SelectableCard.tsx +29 -16
  409. package/src/Selector/Selector.test.tsx +69 -0
  410. package/src/Selector/Selector.tsx +5 -1
  411. package/src/Selector/hooks.ts +17 -0
  412. package/src/SideNav/SideNav.test.tsx +119 -3
  413. package/src/SideNav/SideNavCollapseButton.tsx +6 -1
  414. package/src/SideNav/SideNavHeading.tsx +24 -8
  415. package/src/SideNav/SideNavItem.tsx +5 -1
  416. package/src/Slider/Slider.test.tsx +75 -2
  417. package/src/Slider/Slider.tsx +21 -5
  418. package/src/Switch/Switch.test.tsx +32 -1
  419. package/src/Table/BaseTable.tsx +3 -5
  420. package/src/Table/index.ts +5 -0
  421. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +6 -2
  422. package/src/Table/plugins/rowStatus/index.ts +7 -0
  423. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +138 -0
  424. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +189 -0
  425. package/src/Table/plugins/selection/useTableSelection.test.tsx +16 -0
  426. package/src/Table/plugins/selection/useTableSelection.tsx +19 -1
  427. package/src/Table/plugins/tree/useTableTreeData.test.tsx +135 -3
  428. package/src/Table/plugins/tree/useTableTreeData.tsx +128 -1
  429. package/src/Table/plugins/tree/useTableTreeState.test.tsx +104 -0
  430. package/src/Table/plugins/tree/useTableTreeState.tsx +46 -2
  431. package/src/Table/useBaseTablePlugins.ts +10 -6
  432. package/src/Table/useTableRowStatus.doc.mjs +30 -0
  433. package/src/Table/useTableSelection.doc.mjs +65 -14
  434. package/src/Table/useTableTreeData.doc.mjs +29 -0
  435. package/src/Table/useTableTreeState.doc.mjs +2 -2
  436. package/src/TextArea/TextArea.test.tsx +9 -1
  437. package/src/Thumbnail/Thumbnail.doc.mjs +19 -10
  438. package/src/Thumbnail/Thumbnail.test.tsx +98 -7
  439. package/src/Thumbnail/Thumbnail.tsx +141 -41
  440. package/src/Thumbnail/thumbnail.markers.stylex.ts +12 -0
  441. package/src/TimeInput/TimeInput.test.tsx +76 -1
  442. package/src/TimeInput/TimeInput.tsx +19 -6
  443. package/src/Timestamp/Timestamp.doc.mjs +4 -4
  444. package/src/Timestamp/Timestamp.test.tsx +32 -0
  445. package/src/Timestamp/Timestamp.tsx +28 -8
  446. package/src/Toast/useToast.tsx +7 -8
  447. package/src/ToggleButton/ToggleButtonGroup.tsx +1 -1
  448. package/src/Tokenizer/Tokenizer.doc.mjs +15 -1
  449. package/src/Tokenizer/Tokenizer.test.tsx +183 -2
  450. package/src/Tokenizer/Tokenizer.tsx +16 -4
  451. package/src/TopNav/TopNav.test.tsx +125 -1
  452. package/src/TopNav/TopNavHeading.tsx +24 -8
  453. package/src/TopNav/TopNavMegaMenu.test.tsx +149 -1
  454. package/src/TopNav/TopNavMegaMenu.tsx +10 -4
  455. package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +3 -3
  456. package/src/TopNav/TopNavMegaMenuFeaturedCard.tsx +19 -2
  457. package/src/TopNav/TopNavMenu.test.tsx +118 -4
  458. package/src/TopNav/TopNavMenu.tsx +83 -5
  459. package/src/TreeList/TreeList.doc.mjs +22 -0
  460. package/src/TreeList/TreeList.test.tsx +232 -0
  461. package/src/TreeList/TreeList.tsx +18 -2
  462. package/src/TreeList/TreeListBranches.tsx +16 -2
  463. package/src/TreeList/TreeListItem.tsx +51 -11
  464. package/src/TreeList/TreeListTypes.ts +27 -1
  465. package/src/TreeList/index.ts +2 -2
  466. package/src/authoring/doc.ts +1 -3
  467. package/src/docs-types.ts +5 -0
  468. package/src/hooks/computeOverflow.test.ts +422 -0
  469. package/src/hooks/computeOverflow.ts +290 -0
  470. package/src/hooks/index.ts +2 -0
  471. package/src/hooks/useDevWarning.test.tsx +45 -0
  472. package/src/hooks/useDevWarning.ts +54 -0
  473. package/src/hooks/useOverflow.test.ts +96 -4
  474. package/src/hooks/useOverflow.ts +74 -33
  475. package/src/i18n/InternationalizationProvider.doc.mjs +99 -2
  476. package/src/i18n/resolve.ts +9 -21
  477. package/src/theme/Theme.tsx +11 -14
  478. package/src/theme/generateThemeRules.test.ts +65 -0
  479. package/src/theme/generateThemeRules.ts +56 -0
  480. package/src/theme/hct.ts +1 -1
  481. package/src/theme/syntax/defineSyntaxTheme.ts +5 -6
  482. package/src/utils/devWarning.test.ts +56 -0
  483. package/src/utils/devWarning.ts +102 -0
  484. package/src/utils/index.ts +6 -0
  485. package/src/utils/plainDate.test.ts +22 -0
  486. package/src/utils/plainDate.ts +61 -0
  487. package/src/utils/types.ts +14 -0
@@ -611,3 +611,196 @@ describe('ContextMenu selectable items', () => {
611
611
  expect(onToggle).toHaveBeenCalledWith(true);
612
612
  });
613
613
  });
614
+
615
+ describe('ContextMenu keyboard access for menuitemradio/menuitemcheckbox (#3829)', () => {
616
+ it('arrow navigation reaches consumer-rendered menuitemradio and menuitemcheckbox items', () => {
617
+ render(
618
+ <ContextMenu
619
+ menuContent={
620
+ <>
621
+ <DropdownMenuItem label="Cut" onClick={() => {}} />
622
+ <div role="menuitemradio" tabIndex={-1} aria-checked="false">
623
+ Newest
624
+ </div>
625
+ <div role="menuitemcheckbox" tabIndex={-1} aria-checked="false">
626
+ Archived
627
+ </div>
628
+ </>
629
+ }>
630
+ <div>Right-click me</div>
631
+ </ContextMenu>,
632
+ );
633
+
634
+ fireEvent.contextMenu(screen.getByText('Right-click me'));
635
+ const menu = screen.getByRole('menu', {hidden: true});
636
+ screen.getByRole('menuitem', {name: 'Cut', hidden: true}).focus();
637
+
638
+ fireEvent.keyDown(menu, {key: 'ArrowDown'});
639
+ expect(
640
+ screen.getByRole('menuitemradio', {name: 'Newest', hidden: true}),
641
+ ).toHaveFocus();
642
+
643
+ fireEvent.keyDown(menu, {key: 'ArrowDown'});
644
+ expect(
645
+ screen.getByRole('menuitemcheckbox', {name: 'Archived', hidden: true}),
646
+ ).toHaveFocus();
647
+ });
648
+
649
+ it('activates a focused menuitemradio with Enter and a menuitemcheckbox with Space', () => {
650
+ const onSelect = vi.fn();
651
+ const onToggle = vi.fn();
652
+ render(
653
+ <ContextMenu
654
+ menuContent={
655
+ <>
656
+ <DropdownMenuItem label="Cut" onClick={() => {}} />
657
+ <div
658
+ role="menuitemradio"
659
+ tabIndex={-1}
660
+ aria-checked="false"
661
+ onClick={onSelect}>
662
+ Newest
663
+ </div>
664
+ <div
665
+ role="menuitemcheckbox"
666
+ tabIndex={-1}
667
+ aria-checked="false"
668
+ onClick={onToggle}>
669
+ Archived
670
+ </div>
671
+ </>
672
+ }>
673
+ <div>Right-click me</div>
674
+ </ContextMenu>,
675
+ );
676
+
677
+ fireEvent.contextMenu(screen.getByText('Right-click me'));
678
+ const menu = screen.getByRole('menu', {hidden: true});
679
+
680
+ screen.getByRole('menuitemradio', {name: 'Newest', hidden: true}).focus();
681
+ fireEvent.keyDown(menu, {key: 'Enter'});
682
+ expect(onSelect).toHaveBeenCalledTimes(1);
683
+
684
+ screen
685
+ .getByRole('menuitemcheckbox', {name: 'Archived', hidden: true})
686
+ .focus();
687
+ fireEvent.keyDown(menu, {key: ' '});
688
+ expect(onToggle).toHaveBeenCalledTimes(1);
689
+ });
690
+
691
+ it('typeahead matches a menuitemradio label', () => {
692
+ render(
693
+ <ContextMenu
694
+ menuContent={
695
+ <>
696
+ <DropdownMenuItem label="Cut" onClick={() => {}} />
697
+ <div role="menuitemradio" tabIndex={-1} aria-checked="false">
698
+ Newest
699
+ </div>
700
+ <div role="menuitemcheckbox" tabIndex={-1} aria-checked="false">
701
+ Archived
702
+ </div>
703
+ </>
704
+ }>
705
+ <div>Right-click me</div>
706
+ </ContextMenu>,
707
+ );
708
+
709
+ fireEvent.contextMenu(screen.getByText('Right-click me'));
710
+ fireEvent.keyDown(screen.getByRole('menu', {hidden: true}), {key: 'n'});
711
+ expect(
712
+ screen.getByRole('menuitemradio', {name: 'Newest', hidden: true}),
713
+ ).toHaveFocus();
714
+ });
715
+
716
+ it('typeahead matches a menuitemcheckbox label', () => {
717
+ render(
718
+ <ContextMenu
719
+ menuContent={
720
+ <>
721
+ <DropdownMenuItem label="Cut" onClick={() => {}} />
722
+ <div role="menuitemradio" tabIndex={-1} aria-checked="false">
723
+ Newest
724
+ </div>
725
+ <div role="menuitemcheckbox" tabIndex={-1} aria-checked="false">
726
+ Archived
727
+ </div>
728
+ </>
729
+ }>
730
+ <div>Right-click me</div>
731
+ </ContextMenu>,
732
+ );
733
+
734
+ fireEvent.contextMenu(screen.getByText('Right-click me'));
735
+ fireEvent.keyDown(screen.getByRole('menu', {hidden: true}), {key: 'a'});
736
+ expect(
737
+ screen.getByRole('menuitemcheckbox', {name: 'Archived', hidden: true}),
738
+ ).toHaveFocus();
739
+ });
740
+
741
+ it('typeahead skips an aria-disabled item and matches the next enabled label', () => {
742
+ render(
743
+ <ContextMenu
744
+ menuContent={
745
+ <>
746
+ <DropdownMenuItem label="Cut" onClick={() => {}} />
747
+ <div role="menuitemradio" tabIndex={-1} aria-disabled="true">
748
+ Newest
749
+ </div>
750
+ <div role="menuitemcheckbox" tabIndex={-1} aria-checked="false">
751
+ Nightly
752
+ </div>
753
+ </>
754
+ }>
755
+ <div>Right-click me</div>
756
+ </ContextMenu>,
757
+ );
758
+
759
+ fireEvent.contextMenu(screen.getByText('Right-click me'));
760
+ // Anchor the search on 'Cut' so typeahead scans forward and meets the
761
+ // disabled 'Newest' (also an 'n' match) before the enabled 'Nightly'.
762
+ // This pins the `:not([aria-disabled="true"])` in MENU_ITEM_SELECTOR: the
763
+ // menus never pass useTypeahead's `isDisabled` option, so that clause is
764
+ // the only thing keeping disabled rows out of the typeahead list. An
765
+ // arrow-key test cannot cover it — useListFocus re-filters disabled items
766
+ // independently, so arrow navigation is guarded twice over.
767
+ // The disabled row keeps tabIndex={-1} on purpose: it stays focusable, so
768
+ // the selector clause is the sole reason focus skips it.
769
+ screen.getByRole('menuitem', {name: 'Cut', hidden: true}).focus();
770
+ fireEvent.keyDown(screen.getByRole('menu', {hidden: true}), {key: 'n'});
771
+ expect(
772
+ screen.getByRole('menuitemcheckbox', {name: 'Nightly', hidden: true}),
773
+ ).toHaveFocus();
774
+ });
775
+
776
+ it('skips aria-disabled menuitemradio and menuitemcheckbox items during arrow navigation', () => {
777
+ render(
778
+ <ContextMenu
779
+ menuContent={
780
+ <>
781
+ <DropdownMenuItem label="Cut" onClick={() => {}} />
782
+ <div role="menuitemradio" tabIndex={-1} aria-disabled="true">
783
+ Newest
784
+ </div>
785
+ <div role="menuitemcheckbox" tabIndex={-1} aria-disabled="true">
786
+ Archived
787
+ </div>
788
+ <div role="menuitemradio" tabIndex={-1} aria-checked="false">
789
+ Oldest
790
+ </div>
791
+ </>
792
+ }>
793
+ <div>Right-click me</div>
794
+ </ContextMenu>,
795
+ );
796
+
797
+ fireEvent.contextMenu(screen.getByText('Right-click me'));
798
+ const menu = screen.getByRole('menu', {hidden: true});
799
+ screen.getByRole('menuitem', {name: 'Cut', hidden: true}).focus();
800
+
801
+ fireEvent.keyDown(menu, {key: 'ArrowDown'});
802
+ expect(
803
+ screen.getByRole('menuitemradio', {name: 'Oldest', hidden: true}),
804
+ ).toHaveFocus();
805
+ });
806
+ });
@@ -135,6 +135,13 @@ export const docs = {
135
135
  'Number of months displayed simultaneously in the calendar popover.',
136
136
  default: '1',
137
137
  },
138
+ {
139
+ name: 'format',
140
+ type: "'date' | 'date_long' | 'date_weekday' | 'system_date' | ((value: ISODateString) => string)",
141
+ description:
142
+ "How the committed date value is displayed. Named values are reused from Timestamp's format vocabulary: 'date' shows 'Mar 21, 2026', 'date_long' shows 'March 21, 2026', 'date_weekday' shows 'Wed, Mar 21, 2026', 'system_date' shows '2026-03-21'. A function receives the ISO value and returns a custom string. Applies only to the committed value, never to text being typed.",
143
+ default: "'date_long'",
144
+ },
138
145
  {
139
146
  name: 'xstyle',
140
147
  type: 'StyleXStyles',
@@ -388,6 +395,13 @@ export const docsZh = {
388
395
  description: '日历弹出层中同时显示的月份数量。',
389
396
  default: '1',
390
397
  },
398
+ {
399
+ name: 'format',
400
+ type: "'date' | 'date_long' | 'date_weekday' | 'system_date' | ((value: ISODateString) => string)",
401
+ description:
402
+ "已选日期的显示格式。命名值复用 Timestamp 的格式词汇:'date' 显示 'Mar 21, 2026','date_long' 显示 'March 21, 2026','date_weekday' 显示 'Wed, Mar 21, 2026','system_date' 显示 '2026-03-21'。函数接收 ISO 值并返回自定义字符串。仅作用于已提交的值,不影响正在输入的文本。",
403
+ default: "'date_long'",
404
+ },
391
405
  {
392
406
  name: 'xstyle',
393
407
  type: 'StyleXStyles',
@@ -481,6 +495,8 @@ export const docsDense = {
481
495
  labelTooltip: 'tooltip text via info icon at label end',
482
496
  hasClear: 'Shows clear button when date is set. Clears value on click.',
483
497
  numberOfMonths: 'months shown simultaneously in calendar popover',
498
+ format:
499
+ "committed-value display: 'date_long' (default, March 21, 2026), 'date' (Mar 21, 2026), 'date_weekday' (Wed, Mar 21, 2026), 'system_date' (2026-03-21), or (iso)=>string; reuses Timestamp vocabulary. Committed value only, not while typing.",
484
500
  xstyle: 'StyleX styles for layout; must be stylex.create() value',
485
501
  },
486
502
  };
@@ -834,4 +834,112 @@ describe('DateInput', () => {
834
834
  expect(input).not.toHaveAttribute('aria-disabled');
835
835
  });
836
836
  });
837
+
838
+ describe('format', () => {
839
+ it('defaults to the long-month date_long shape', () => {
840
+ // Non-breaking default: byte-identical to the historical hardcoded
841
+ // DATE_FORMAT_LONG rendering. `format` now defaults to 'date_long'.
842
+ render(<DateInput label="Date" value="2026-01-25" onChange={() => {}} />);
843
+ expect(screen.getByDisplayValue('January 25, 2026')).toBeInTheDocument();
844
+ });
845
+
846
+ it('renders the long-month shape for explicit format="date_long"', () => {
847
+ // Explicit date_long is identical to the unset default above and to the
848
+ // old hardcoded long-month output — real named parity with Timestamp.
849
+ render(
850
+ <DateInput
851
+ label="Date"
852
+ value="2026-01-25"
853
+ onChange={() => {}}
854
+ format="date_long"
855
+ />,
856
+ );
857
+ expect(screen.getByDisplayValue('January 25, 2026')).toBeInTheDocument();
858
+ });
859
+
860
+ it('renders the short-month shape for format="date"', () => {
861
+ // Same literal + same shape as <Timestamp format="date" />.
862
+ render(
863
+ <DateInput
864
+ label="Date"
865
+ value="2026-01-25"
866
+ onChange={() => {}}
867
+ format="date"
868
+ />,
869
+ );
870
+ expect(screen.getByDisplayValue('Jan 25, 2026')).toBeInTheDocument();
871
+ });
872
+
873
+ it('renders the ISO shape for format="system_date"', () => {
874
+ render(
875
+ <DateInput
876
+ label="Date"
877
+ value="2026-01-25"
878
+ onChange={() => {}}
879
+ format="system_date"
880
+ />,
881
+ );
882
+ expect(screen.getByDisplayValue('2026-01-25')).toBeInTheDocument();
883
+ });
884
+
885
+ it('renders a weekday prefix for format="date_weekday"', () => {
886
+ render(
887
+ <DateInput
888
+ label="Date"
889
+ value="2026-01-25"
890
+ onChange={() => {}}
891
+ format="date_weekday"
892
+ />,
893
+ );
894
+ // 2026-01-25 is a Sunday; assert the weekday-prefixed shape without
895
+ // over-fitting locale punctuation.
896
+ const input = screen.getByRole('combobox');
897
+ expect(input).toHaveValue('Sun, Jan 25, 2026');
898
+ });
899
+
900
+ it('supports a custom function format', () => {
901
+ render(
902
+ <DateInput
903
+ label="Date"
904
+ value="2026-01-25"
905
+ onChange={() => {}}
906
+ format={iso => `custom:${iso}`}
907
+ />,
908
+ );
909
+ expect(screen.getByDisplayValue('custom:2026-01-25')).toBeInTheDocument();
910
+ });
911
+
912
+ it('does not apply format to in-progress typed input', async () => {
913
+ const user = userEvent.setup();
914
+ render(
915
+ <DateInput label="Date" onChange={() => {}} format="system_date" />,
916
+ );
917
+ const input = screen.getByRole('combobox');
918
+ await user.click(input);
919
+ await user.type(input, 'January 25');
920
+ // While typing, the raw text is shown verbatim — not reformatted.
921
+ expect(input).toHaveValue('January 25');
922
+ });
923
+
924
+ it('recomputes the display in format on external value change', () => {
925
+ const {rerender} = render(
926
+ <DateInput
927
+ label="Date"
928
+ value="2026-01-25"
929
+ onChange={() => {}}
930
+ format="date"
931
+ />,
932
+ );
933
+ expect(screen.getByDisplayValue('Jan 25, 2026')).toBeInTheDocument();
934
+ rerender(
935
+ <DateInput
936
+ label="Date"
937
+ value="2026-03-10"
938
+ onChange={() => {}}
939
+ format="date"
940
+ />,
941
+ );
942
+ expect(screen.getByDisplayValue('Mar 10, 2026')).toBeInTheDocument();
943
+ });
944
+ });
837
945
  });
@@ -57,9 +57,9 @@ import {getInputARIA, parseDateInput} from '../utils';
57
57
  import {
58
58
  plainDateFromISO,
59
59
  plainDateToISO,
60
- plainDateFormat,
61
- DATE_FORMAT_LONG,
60
+ formatSharedDate,
62
61
  } from '../utils/plainDate';
62
+ import type {TimestampFormat} from '../Timestamp';
63
63
 
64
64
  const styles = stylex.create({
65
65
  iconButton: {
@@ -127,6 +127,25 @@ const sizeStyles = stylex.create({
127
127
 
128
128
  export type DateInputSize = keyof typeof sizeStyles;
129
129
 
130
+ /**
131
+ * Named display formats for a committed date value. These are the date-only
132
+ * members of Timestamp's `format` vocabulary — reused verbatim (via
133
+ * `Extract`) so the same literal renders the same date shape in both
134
+ * `Timestamp` and `DateInput`:
135
+ * - `'date'`: locale short-month date, e.g. "Mar 21, 2026"
136
+ * - `'date_long'`: locale long-month date, e.g. "March 21, 2026" (the default)
137
+ * - `'date_weekday'`: short weekday + date, e.g. "Wed, Mar 21, 2026"
138
+ * - `'system_date'`: ISO 8601 calendar date, e.g. "2026-03-21"
139
+ *
140
+ * Because `DateInputFormat` is `Extract`ed from `TimestampFormat`, the two
141
+ * types stay in compile-time lockstep: renaming or removing one of these
142
+ * members from `TimestampFormat` breaks this type at build time.
143
+ */
144
+ export type DateInputFormat = Extract<
145
+ TimestampFormat,
146
+ 'date' | 'date_long' | 'date_weekday' | 'system_date'
147
+ >;
148
+
130
149
  // Re-export shared types for convenience
131
150
 
132
151
  export type {
@@ -283,6 +302,37 @@ export interface DateInputProps extends Omit<
283
302
  * @default 1
284
303
  */
285
304
  numberOfMonths?: 1 | 2;
305
+
306
+ /**
307
+ * How the committed date value is displayed in the text field. Accepts a
308
+ * named format reused from `Timestamp`'s `format` vocabulary (so the same
309
+ * literal renders the same date shape in both components) or a function that
310
+ * maps the ISO value to a custom display string.
311
+ *
312
+ * - `'date_long'` (default): long-month date, e.g. "March 21, 2026"
313
+ * - `'date'`: short-month date, e.g. "Mar 21, 2026"
314
+ * - `'date_weekday'`: short weekday + date, e.g. "Wed, Mar 21, 2026"
315
+ * - `'system_date'`: ISO 8601 calendar date, e.g. "2026-03-21"
316
+ * - `(value: ISODateString) => string`: fully custom display string
317
+ *
318
+ * Formatting applies only to the committed value — never to text the user is
319
+ * actively typing. A custom function's output that `parseDateInput` cannot
320
+ * read back can't be re-committed after an edit; external `value` changes
321
+ * always recompute the display from the ISO value.
322
+ *
323
+ * @default 'date_long'
324
+ * @example
325
+ * ```
326
+ * <DateInput label="Ship date" value={date} onChange={setDate} format="date" />
327
+ * <DateInput
328
+ * label="Ship date"
329
+ * value={date}
330
+ * onChange={setDate}
331
+ * format={iso => new Date(iso + 'T00:00').toDateString()}
332
+ * />
333
+ * ```
334
+ */
335
+ format?: DateInputFormat | ((value: ISODateString) => string);
286
336
  }
287
337
 
288
338
  /**
@@ -318,6 +368,7 @@ export function DateInput({
318
368
  labelTooltip,
319
369
  hasClear = false,
320
370
  numberOfMonths = 1,
371
+ format = 'date_long',
321
372
  width,
322
373
  xstyle,
323
374
  className,
@@ -404,12 +455,26 @@ export function DateInput({
404
455
  }
405
456
  }
406
457
 
458
+ // Format a committed ISO value for display. The default `date_long` renders
459
+ // the long-month shape (byte-identical to the historical hardcoded
460
+ // DATE_FORMAT_LONG rendering, so still non-breaking); a function is called
461
+ // with the ISO value; every other named member reuses Timestamp's shared
462
+ // date mapping. Applies ONLY to the committed value, never to in-progress
463
+ // typed input.
464
+ const formatCommittedValue = useCallback(
465
+ (iso: ISODateString): string =>
466
+ typeof format === 'function'
467
+ ? format(iso)
468
+ : formatSharedDate(plainDateFromISO(iso), format),
469
+ [format],
470
+ );
471
+
407
472
  // Display value: pending input if typing, otherwise formatted value
408
473
  const displayValue =
409
474
  pendingInput !== null
410
475
  ? pendingInput
411
476
  : optimisticValue && /^\d{4}-\d{2}-\d{2}$/.test(optimisticValue)
412
- ? plainDateFormat(plainDateFromISO(optimisticValue), DATE_FORMAT_LONG)
477
+ ? formatCommittedValue(optimisticValue)
413
478
  : '';
414
479
 
415
480
  // Check if current input is valid (for styling purposes)
@@ -13,6 +13,7 @@ export {DateInput} from './DateInput';
13
13
  export type {
14
14
  DateInputProps,
15
15
  DateInputSize,
16
+ DateInputFormat,
16
17
  DateInputStatus,
17
18
  DateInputStatusType,
18
19
  } from './DateInput';
@@ -185,6 +185,41 @@ describe('DateTimeInput', () => {
185
185
  );
186
186
  });
187
187
 
188
+ it('opens the calendar popover on ArrowDown (keyboard, forms-13)', () => {
189
+ render(<DateTimeInput label="Meeting" onChange={() => {}} />);
190
+ const input = screen.getByRole('combobox');
191
+ input.focus();
192
+ expect(input).toHaveAttribute('aria-expanded', 'false');
193
+ fireEvent.keyDown(input, {key: 'ArrowDown'});
194
+ expect(input).toHaveAttribute('aria-expanded', 'true');
195
+ // skipAutoFocus keeps focus in the input, per the APG date-picker pattern
196
+ expect(input).toHaveFocus();
197
+ });
198
+
199
+ it('opens the calendar popover on Alt+ArrowDown (keyboard, forms-13)', () => {
200
+ render(<DateTimeInput label="Meeting" onChange={() => {}} />);
201
+ const input = screen.getByRole('combobox');
202
+ fireEvent.keyDown(input, {key: 'ArrowDown', altKey: true});
203
+ expect(input).toHaveAttribute('aria-expanded', 'true');
204
+ });
205
+
206
+ it('does not open on ArrowDown when disabled', () => {
207
+ render(<DateTimeInput label="Meeting" isDisabled onChange={() => {}} />);
208
+ const input = screen.getByRole('combobox');
209
+ fireEvent.keyDown(input, {key: 'ArrowDown'});
210
+ expect(input).toHaveAttribute('aria-expanded', 'false');
211
+ });
212
+
213
+ it('does not re-trigger on ArrowDown when the calendar is already open', () => {
214
+ render(<DateTimeInput label="Meeting" onChange={() => {}} />);
215
+ const input = screen.getByRole('combobox');
216
+ fireEvent.keyDown(input, {key: 'ArrowDown'});
217
+ expect(input).toHaveAttribute('aria-expanded', 'true');
218
+ // A second ArrowDown is a no-op: the calendar stays open
219
+ fireEvent.keyDown(input, {key: 'ArrowDown'});
220
+ expect(input).toHaveAttribute('aria-expanded', 'true');
221
+ });
222
+
188
223
  it('calendar button is focusable and clickable', () => {
189
224
  render(<DateTimeInput label="Meeting" onChange={() => {}} />);
190
225
  const button = getButton('Open calendar');
@@ -719,12 +719,22 @@ export function DateTimeInput({
719
719
  if (e.key === 'Escape' && popover.isOpen) {
720
720
  e.preventDefault();
721
721
  popover.hide();
722
+ } else if (
723
+ (e.key === 'ArrowDown' || (e.altKey && e.key === 'ArrowDown')) &&
724
+ !popover.isOpen
725
+ ) {
726
+ // APG combobox: ArrowDown (and Alt+ArrowDown) opens the calendar
727
+ // popover from the keyboard, keeping focus in the input (forms-13).
728
+ e.preventDefault();
729
+ if (!isEffectivelyDisabled) {
730
+ popover.show({skipAutoFocus: true});
731
+ }
722
732
  } else if (e.key === 'Enter') {
723
733
  e.preventDefault();
724
734
  commitDatePendingInput();
725
735
  }
726
736
  },
727
- [popover, commitDatePendingInput],
737
+ [popover, commitDatePendingInput, isEffectivelyDisabled],
728
738
  );
729
739
 
730
740
  // --- Time handlers ---
@@ -1000,7 +1010,9 @@ export function DateTimeInput({
1000
1010
  disabled={isEffectivelyDisabled && !showsDisabledMessage}
1001
1011
  aria-disabled={showsDisabledMessage ? 'true' : undefined}
1002
1012
  readOnly={showsDisabledMessage || undefined}
1003
- aria-label={timeLabel ?? t('@astryx.dateTimeInput.timeSuffix', {label})}
1013
+ aria-label={
1014
+ timeLabel ?? t('@astryx.dateTimeInput.timeSuffix', {label})
1015
+ }
1004
1016
  aria-describedby={ariaDescribedBy}
1005
1017
  aria-required={isRequired === true ? 'true' : undefined}
1006
1018
  aria-invalid={
@@ -111,7 +111,7 @@ export const docs = {
111
111
  { guidance: false, description: 'Use the fullscreen variant for simple confirmations; it is meant for complex content like editors or long forms.' },
112
112
  ],
113
113
  anatomy: [
114
- {name: 'Header', required: true, description: 'Title, optional subtitle, and close button. The title receives focus on open for accessibility.'},
114
+ {name: 'Header', required: true, description: 'Title, optional subtitle, and close button. The title receives focus on open and labels the dialog via aria-labelledby.'},
115
115
  {name: 'Body', required: true, description: 'The main content area: text, forms, lists, or any layout.'},
116
116
  {name: 'Footer', required: false, description: 'Action buttons like Save/Cancel or Accept/Decline, aligned to the end.'},
117
117
  {name: 'Backdrop', required: true, description: 'Semi-transparent overlay behind the dialog that blocks page interaction.'},
@@ -133,7 +133,7 @@ export const docsZh = {
133
133
  { guidance: false, description: 'Use the fullscreen variant for simple confirmations; it is meant for complex content like editors or long forms.' },
134
134
  ],
135
135
  anatomy: [
136
- {name: 'Header', required: true, description: 'Title, optional subtitle, and close button. The title receives focus on open for accessibility.'},
136
+ {name: 'Header', required: true, description: 'Title, optional subtitle, and close button. The title receives focus on open and labels the dialog via aria-labelledby.'},
137
137
  {name: 'Body', required: true, description: 'The main content area: text, forms, lists, or any layout.'},
138
138
  {name: 'Footer', required: false, description: 'Action buttons like Save/Cancel or Accept/Decline, aligned to the end.'},
139
139
  {name: 'Backdrop', required: true, description: 'Semi-transparent overlay behind the dialog that blocks page interaction.'},
@@ -212,10 +212,7 @@ describe('Dialog', () => {
212
212
 
213
213
  it('forwards additional props to dialog element', () => {
214
214
  render(
215
- <Dialog
216
- isOpen={true}
217
- onOpenChange={() => {}}
218
- data-testid="custom-dialog">
215
+ <Dialog isOpen={true} onOpenChange={() => {}} data-testid="custom-dialog">
219
216
  Content
220
217
  </Dialog>,
221
218
  );
@@ -267,6 +264,82 @@ describe('Dialog', () => {
267
264
  });
268
265
  });
269
266
 
267
+ describe('accessible name', () => {
268
+ it('is labelled by the DialogHeader title by default', () => {
269
+ render(
270
+ <Dialog isOpen={true} onOpenChange={() => {}}>
271
+ <DialogHeader title="Dialog title" />
272
+ </Dialog>,
273
+ );
274
+ const dialog = screen.getByRole('dialog');
275
+ const heading = screen.getByRole('heading', {name: 'Dialog title'});
276
+ expect(heading.id).not.toBe('');
277
+ expect(dialog).toHaveAttribute('aria-labelledby', heading.id);
278
+ expect(dialog).toHaveAccessibleName('Dialog title');
279
+ });
280
+
281
+ it('prefers a consumer-provided aria-label over the header title', () => {
282
+ render(
283
+ <Dialog isOpen={true} onOpenChange={() => {}} aria-label="Custom name">
284
+ <DialogHeader title="Dialog title" />
285
+ </Dialog>,
286
+ );
287
+ const dialog = screen.getByRole('dialog');
288
+ expect(dialog).not.toHaveAttribute('aria-labelledby');
289
+ expect(dialog).toHaveAccessibleName('Custom name');
290
+ });
291
+
292
+ it('prefers a consumer-provided aria-labelledby over the header title', () => {
293
+ render(
294
+ <>
295
+ <span id="external-label">External name</span>
296
+ <Dialog
297
+ isOpen={true}
298
+ onOpenChange={() => {}}
299
+ aria-labelledby="external-label">
300
+ <DialogHeader title="Dialog title" />
301
+ </Dialog>
302
+ </>,
303
+ );
304
+ const dialog = screen.getByRole('dialog');
305
+ expect(dialog).toHaveAttribute('aria-labelledby', 'external-label');
306
+ expect(dialog).toHaveAccessibleName('External name');
307
+ });
308
+
309
+ it('omits aria-labelledby and warns when open with no name source', () => {
310
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
311
+ try {
312
+ render(
313
+ <Dialog isOpen={true} onOpenChange={() => {}}>
314
+ Content
315
+ </Dialog>,
316
+ );
317
+ const dialog = screen.getByRole('dialog');
318
+ expect(dialog).not.toHaveAttribute('aria-labelledby');
319
+ expect(warnSpy).toHaveBeenCalledTimes(1);
320
+ expect(warnSpy).toHaveBeenCalledWith(
321
+ expect.stringContaining('accessible name'),
322
+ );
323
+ } finally {
324
+ warnSpy.mockRestore();
325
+ }
326
+ });
327
+
328
+ it('does not warn when the header provides a title', () => {
329
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
330
+ try {
331
+ render(
332
+ <Dialog isOpen={true} onOpenChange={() => {}}>
333
+ <DialogHeader title="Dialog title" />
334
+ </Dialog>,
335
+ );
336
+ expect(warnSpy).not.toHaveBeenCalled();
337
+ } finally {
338
+ warnSpy.mockRestore();
339
+ }
340
+ });
341
+ });
342
+
270
343
  describe('inner flex wrapper', () => {
271
344
  it('wraps children in a flex container for scroll support', () => {
272
345
  render(