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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (560) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/dist/Avatar/Avatar.d.ts +42 -3
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +190 -59
  5. package/dist/Avatar/AvatarStatusDot.d.ts +6 -4
  6. package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
  7. package/dist/AvatarGroup/AvatarGroup.d.ts +7 -1
  8. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  9. package/dist/AvatarGroup/AvatarGroup.js +58 -6
  10. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
  11. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  12. package/dist/AvatarGroup/AvatarGroupOverflow.js +7 -2
  13. package/dist/Banner/Banner.js +2 -2
  14. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  15. package/dist/Breadcrumbs/BreadcrumbItem.js +11 -4
  16. package/dist/Breadcrumbs/index.d.ts +1 -0
  17. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  18. package/dist/Breadcrumbs/index.js +2 -1
  19. package/dist/Button/Button.d.ts +1 -1
  20. package/dist/Button/Button.d.ts.map +1 -1
  21. package/dist/Button/Button.js +6 -4
  22. package/dist/Calendar/Calendar.d.ts.map +1 -1
  23. package/dist/Calendar/Calendar.js +50 -9
  24. package/dist/Calendar/dayCellUtils.d.ts +4 -4
  25. package/dist/Calendar/dayCellUtils.js +2 -2
  26. package/dist/Calendar/styles.d.ts +30 -31
  27. package/dist/Calendar/styles.d.ts.map +1 -1
  28. package/dist/Calendar/styles.js +30 -31
  29. package/dist/Card/Card.js +1 -1
  30. package/dist/Carousel/Carousel.d.ts.map +1 -1
  31. package/dist/Carousel/Carousel.js +22 -12
  32. package/dist/Chat/ChatComposerInput.js +1 -1
  33. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  34. package/dist/Chat/ChatDictationButton.js +3 -1
  35. package/dist/Chat/ChatLayout.js +4 -4
  36. package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
  37. package/dist/Chat/ChatMessageBubble.js +8 -8
  38. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  39. package/dist/Chat/ChatToolCalls.js +10 -3
  40. package/dist/Chat/useTriggerMenu.js +2 -2
  41. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  42. package/dist/CheckboxInput/CheckboxInput.js +6 -6
  43. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  44. package/dist/CheckboxList/CheckboxListItem.js +19 -4
  45. package/dist/Citation/Citation.d.ts +22 -1
  46. package/dist/Citation/Citation.d.ts.map +1 -1
  47. package/dist/Citation/Citation.js +20 -6
  48. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  49. package/dist/CodeBlock/CodeBlock.js +16 -10
  50. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  51. package/dist/Collapsible/Collapsible.js +3 -1
  52. package/dist/CommandPalette/CommandPalette.d.ts +1 -1
  53. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  54. package/dist/CommandPalette/CommandPalette.js +42 -3
  55. package/dist/CommandPalette/CommandPaletteItem.js +1 -1
  56. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  57. package/dist/ContextMenu/ContextMenu.js +23 -5
  58. package/dist/ContextMenu/index.d.ts +1 -0
  59. package/dist/ContextMenu/index.d.ts.map +1 -1
  60. package/dist/ContextMenu/index.js +3 -1
  61. package/dist/DateInput/DateInput.d.ts +10 -2
  62. package/dist/DateInput/DateInput.d.ts.map +1 -1
  63. package/dist/DateInput/DateInput.js +33 -19
  64. package/dist/DateRangeInput/DateRangeInput.d.ts +10 -2
  65. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  66. package/dist/DateRangeInput/DateRangeInput.js +23 -20
  67. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  68. package/dist/DateTimeInput/DateTimeInput.js +23 -18
  69. package/dist/Divider/Divider.d.ts +2 -2
  70. package/dist/Divider/Divider.d.ts.map +1 -1
  71. package/dist/Divider/Divider.js +13 -0
  72. package/dist/DropdownMenu/DropdownMenu.d.ts +6 -0
  73. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  74. package/dist/DropdownMenu/DropdownMenu.js +22 -5
  75. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  76. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +4 -1
  77. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  78. package/dist/DropdownMenu/DropdownMenuItem.js +5 -2
  79. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +8 -11
  80. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
  81. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +3 -1
  82. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +1 -1
  83. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  84. package/dist/DropdownMenu/DropdownMenuRadioItem.js +5 -2
  85. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +104 -0
  86. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -0
  87. package/dist/DropdownMenu/DropdownMenuSubMenu.js +427 -0
  88. package/dist/DropdownMenu/index.d.ts +10 -0
  89. package/dist/DropdownMenu/index.d.ts.map +1 -1
  90. package/dist/DropdownMenu/index.js +13 -0
  91. package/dist/DropdownMenu/menuItemHover.d.ts +23 -0
  92. package/dist/DropdownMenu/menuItemHover.d.ts.map +1 -0
  93. package/dist/DropdownMenu/menuItemHover.js +33 -0
  94. package/dist/DropdownMenu/menuItemRoles.d.ts +8 -0
  95. package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -1
  96. package/dist/DropdownMenu/menuItemRoles.js +10 -1
  97. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  98. package/dist/DropdownMenu/renderDropdownItems.js +21 -6
  99. package/dist/Field/Field.d.ts +3 -1
  100. package/dist/Field/Field.d.ts.map +1 -1
  101. package/dist/Field/Field.js +4 -1
  102. package/dist/Field/FieldLabel.d.ts +2 -1
  103. package/dist/Field/FieldLabel.d.ts.map +1 -1
  104. package/dist/Field/FieldLabel.js +9 -5
  105. package/dist/Field/inputStyles.stylex.d.ts +1 -0
  106. package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
  107. package/dist/Field/inputStyles.stylex.js +1 -0
  108. package/dist/FieldStatus/FieldStatus.d.ts +10 -0
  109. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  110. package/dist/FieldStatus/FieldStatus.js +39 -3
  111. package/dist/FileInput/FileInput.d.ts +10 -2
  112. package/dist/FileInput/FileInput.d.ts.map +1 -1
  113. package/dist/FileInput/FileInput.js +93 -65
  114. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  115. package/dist/HoverCard/useHoverCard.js +10 -4
  116. package/dist/Icon/Icon.d.ts +15 -1
  117. package/dist/Icon/Icon.d.ts.map +1 -1
  118. package/dist/Icon/Icon.js +19 -3
  119. package/dist/Item/Item.d.ts +16 -3
  120. package/dist/Item/Item.d.ts.map +1 -1
  121. package/dist/Item/Item.js +31 -6
  122. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  123. package/dist/Layer/layerAnimations.stylex.js +10 -2
  124. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  125. package/dist/Lightbox/Lightbox.js +19 -13
  126. package/dist/List/List.d.ts.map +1 -1
  127. package/dist/List/List.js +6 -3
  128. package/dist/List/ListItem.d.ts +11 -1
  129. package/dist/List/ListItem.d.ts.map +1 -1
  130. package/dist/List/ListItem.js +2 -0
  131. package/dist/Markdown/Markdown.d.ts.map +1 -1
  132. package/dist/Markdown/Markdown.js +7 -0
  133. package/dist/MetadataList/MetadataList.d.ts +1 -1
  134. package/dist/MetadataList/MetadataList.d.ts.map +1 -1
  135. package/dist/MetadataList/MetadataList.js +4 -2
  136. package/dist/MultiSelector/MultiSelector.d.ts +9 -1
  137. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  138. package/dist/MultiSelector/MultiSelector.js +80 -42
  139. package/dist/NavMenu/NavHeadingMenuItem.js +1 -1
  140. package/dist/NumberInput/NumberInput.d.ts +10 -2
  141. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  142. package/dist/NumberInput/NumberInput.js +22 -17
  143. package/dist/Outline/Outline.js +2 -2
  144. package/dist/Pagination/Pagination.d.ts.map +1 -1
  145. package/dist/Pagination/Pagination.js +11 -3
  146. package/dist/PowerSearch/PowerSearch.d.ts +9 -2
  147. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  148. package/dist/PowerSearch/PowerSearch.js +2 -0
  149. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  150. package/dist/ProgressBar/ProgressBar.js +6 -1
  151. package/dist/RadioList/RadioList.d.ts +2 -2
  152. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  153. package/dist/RadioList/RadioListItem.js +5 -5
  154. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  155. package/dist/Resizable/ResizeHandle.js +49 -19
  156. package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
  157. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  158. package/dist/SegmentedControl/SegmentedControlItem.js +5 -3
  159. package/dist/Selector/Selector.d.ts +8 -0
  160. package/dist/Selector/Selector.d.ts.map +1 -1
  161. package/dist/Selector/Selector.js +66 -17
  162. package/dist/SideNav/SideNav.d.ts.map +1 -1
  163. package/dist/SideNav/SideNav.js +5 -5
  164. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  165. package/dist/SideNav/SideNavCollapseButton.js +13 -10
  166. package/dist/SideNav/SideNavItem.d.ts +1 -1
  167. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  168. package/dist/SideNav/SideNavItem.js +12 -10
  169. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  170. package/dist/Skeleton/Skeleton.js +2 -2
  171. package/dist/Slider/Slider.d.ts.map +1 -1
  172. package/dist/Slider/Slider.js +87 -34
  173. package/dist/Spinner/Spinner.d.ts +3 -2
  174. package/dist/Spinner/Spinner.d.ts.map +1 -1
  175. package/dist/Spinner/Spinner.js +11 -2
  176. package/dist/Switch/Switch.d.ts +8 -1
  177. package/dist/Switch/Switch.d.ts.map +1 -1
  178. package/dist/Switch/Switch.js +171 -91
  179. package/dist/TabList/Tab.js +2 -2
  180. package/dist/TabList/TabList.d.ts +2 -11
  181. package/dist/TabList/TabList.d.ts.map +1 -1
  182. package/dist/TabList/TabList.js +5 -9
  183. package/dist/TabList/TabListContext.d.ts +0 -5
  184. package/dist/TabList/TabListContext.d.ts.map +1 -1
  185. package/dist/TabList/TabListContext.js +0 -5
  186. package/dist/TabList/TabMenu.js +1 -1
  187. package/dist/Table/BaseTable.d.ts.map +1 -1
  188. package/dist/Table/BaseTable.js +30 -4
  189. package/dist/Table/TableCell.js +3 -3
  190. package/dist/Table/TableHeaderCell.js +3 -3
  191. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  192. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +15 -11
  193. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  194. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +37 -27
  195. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  196. package/dist/Table/plugins/selection/useTableSelection.js +3 -0
  197. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  198. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +15 -7
  199. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  200. package/dist/Table/plugins/tree/useTableTreeData.js +29 -23
  201. package/dist/Table/types.d.ts +32 -0
  202. package/dist/Table/types.d.ts.map +1 -1
  203. package/dist/TextArea/TextArea.d.ts +10 -1
  204. package/dist/TextArea/TextArea.d.ts.map +1 -1
  205. package/dist/TextArea/TextArea.js +18 -19
  206. package/dist/TextInput/TextInput.d.ts +10 -2
  207. package/dist/TextInput/TextInput.d.ts.map +1 -1
  208. package/dist/TextInput/TextInput.js +20 -17
  209. package/dist/Thumbnail/Thumbnail.js +2 -2
  210. package/dist/TimeInput/TimeInput.d.ts +10 -2
  211. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  212. package/dist/TimeInput/TimeInput.js +17 -17
  213. package/dist/Timestamp/Timestamp.d.ts +31 -3
  214. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  215. package/dist/Timestamp/Timestamp.js +47 -76
  216. package/dist/Timestamp/formatInstant.d.ts +41 -0
  217. package/dist/Timestamp/formatInstant.d.ts.map +1 -0
  218. package/dist/Timestamp/formatInstant.js +177 -0
  219. package/dist/Timestamp/index.d.ts +1 -0
  220. package/dist/Timestamp/index.d.ts.map +1 -1
  221. package/dist/Timestamp/tooltipEntries.d.ts +56 -0
  222. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -0
  223. package/dist/Timestamp/tooltipEntries.js +163 -0
  224. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  225. package/dist/Toast/ToastViewport.js +61 -9
  226. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  227. package/dist/ToggleButton/ToggleButton.js +3 -1
  228. package/dist/Token/Token.d.ts +34 -4
  229. package/dist/Token/Token.d.ts.map +1 -1
  230. package/dist/Token/Token.js +85 -45
  231. package/dist/Token/TokenLink.d.ts +62 -0
  232. package/dist/Token/TokenLink.d.ts.map +1 -0
  233. package/dist/Token/TokenLink.js +76 -0
  234. package/dist/Token/index.d.ts +1 -1
  235. package/dist/Token/index.d.ts.map +1 -1
  236. package/dist/Tokenizer/Tokenizer.d.ts +9 -2
  237. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  238. package/dist/Tokenizer/Tokenizer.js +4 -2
  239. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  240. package/dist/Tooltip/useTooltip.js +17 -1
  241. package/dist/TopNav/TopNavItem.d.ts +3 -1
  242. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  243. package/dist/TopNav/TopNavItem.js +30 -3
  244. package/dist/TreeList/TreeList.d.ts.map +1 -1
  245. package/dist/TreeList/TreeList.js +1 -0
  246. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  247. package/dist/TreeList/TreeListBranches.js +8 -6
  248. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  249. package/dist/TreeList/TreeListItem.js +32 -14
  250. package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
  251. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  252. package/dist/Typeahead/BaseTypeahead.js +22 -11
  253. package/dist/Typeahead/Typeahead.d.ts +9 -2
  254. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  255. package/dist/Typeahead/Typeahead.js +11 -2
  256. package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
  257. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  258. package/dist/Typeahead/TypeaheadItem.js +20 -10
  259. package/dist/astryx.css +70 -30
  260. package/dist/astryx.umd.js +51 -51
  261. package/dist/astryx.umd.js.map +4 -4
  262. package/dist/hooks/index.d.ts +2 -0
  263. package/dist/hooks/index.d.ts.map +1 -1
  264. package/dist/hooks/index.js +1 -0
  265. package/dist/hooks/isRtlElement.d.ts +18 -0
  266. package/dist/hooks/isRtlElement.d.ts.map +1 -0
  267. package/dist/hooks/isRtlElement.js +27 -0
  268. package/dist/hooks/useAnnounce.d.ts +3 -0
  269. package/dist/hooks/useAnnounce.d.ts.map +1 -1
  270. package/dist/hooks/useAnnounce.js +39 -1
  271. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  272. package/dist/hooks/useFocusTrap.js +52 -11
  273. package/dist/hooks/useGridFocus.d.ts +6 -1
  274. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  275. package/dist/hooks/useGridFocus.js +12 -9
  276. package/dist/hooks/useHotkeys.d.ts +9 -0
  277. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  278. package/dist/hooks/useHotkeys.js +9 -0
  279. package/dist/hooks/useInputStatusIcon.d.ts +38 -0
  280. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -0
  281. package/dist/hooks/useInputStatusIcon.js +158 -0
  282. package/dist/hooks/useKeyboardHint.d.ts +1 -1
  283. package/dist/hooks/useKeyboardHint.d.ts.map +1 -1
  284. package/dist/hooks/useKeyboardHint.js +4 -2
  285. package/dist/hooks/useListFocus.d.ts +39 -1
  286. package/dist/hooks/useListFocus.d.ts.map +1 -1
  287. package/dist/hooks/useListFocus.js +54 -9
  288. package/dist/i18n/InternationalizationContext.d.ts +1 -0
  289. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  290. package/dist/i18n/InternationalizationContext.js +3 -0
  291. package/dist/i18n/InternationalizationProvider.d.ts +8 -1
  292. package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
  293. package/dist/i18n/InternationalizationProvider.js +5 -1
  294. package/dist/i18n/getLocaleDirection.d.ts +18 -0
  295. package/dist/i18n/getLocaleDirection.d.ts.map +1 -0
  296. package/dist/i18n/getLocaleDirection.js +29 -0
  297. package/dist/i18n/index.d.ts +6 -0
  298. package/dist/i18n/index.d.ts.map +1 -1
  299. package/dist/i18n/index.js +7 -1
  300. package/dist/i18n/useDirection.d.ts +2 -0
  301. package/dist/i18n/useDirection.d.ts.map +1 -0
  302. package/dist/i18n/useDirection.js +27 -0
  303. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  304. package/dist/theme/derivedVarRegistry.js +13 -0
  305. package/dist/utils/index.d.ts +1 -0
  306. package/dist/utils/index.d.ts.map +1 -1
  307. package/dist/utils/index.js +2 -1
  308. package/dist/utils/plainDate.d.ts +17 -0
  309. package/dist/utils/plainDate.d.ts.map +1 -1
  310. package/dist/utils/plainDate.js +23 -4
  311. package/dist/utils/rtlStyles.d.ts +24 -0
  312. package/dist/utils/rtlStyles.d.ts.map +1 -0
  313. package/dist/utils/rtlStyles.js +27 -0
  314. package/locales/en.json +112 -0
  315. package/locales/pseudo.json +84 -0
  316. package/package.json +6 -1
  317. package/src/Avatar/Avatar.doc.mjs +50 -3
  318. package/src/Avatar/Avatar.test.tsx +218 -0
  319. package/src/Avatar/Avatar.tsx +285 -79
  320. package/src/Avatar/AvatarStatusDot.doc.mjs +4 -4
  321. package/src/Avatar/AvatarStatusDot.tsx +6 -4
  322. package/src/AvatarGroup/AvatarGroup.doc.mjs +2 -0
  323. package/src/AvatarGroup/AvatarGroup.test.tsx +162 -2
  324. package/src/AvatarGroup/AvatarGroup.tsx +58 -3
  325. package/src/AvatarGroup/AvatarGroupOverflow.tsx +5 -2
  326. package/src/Banner/Banner.tsx +6 -6
  327. package/src/Breadcrumbs/BreadcrumbItem.tsx +10 -10
  328. package/src/Breadcrumbs/Breadcrumbs.test.tsx +52 -1
  329. package/src/Breadcrumbs/index.ts +4 -0
  330. package/src/Button/Button.doc.mjs +0 -4
  331. package/src/Button/Button.test.tsx +16 -0
  332. package/src/Button/Button.tsx +6 -4
  333. package/src/Calendar/Calendar.test.tsx +170 -20
  334. package/src/Calendar/Calendar.tsx +73 -16
  335. package/src/Calendar/dayCellUtils.test.ts +18 -18
  336. package/src/Calendar/dayCellUtils.ts +2 -2
  337. package/src/Calendar/styles.ts +33 -39
  338. package/src/Card/Card.tsx +1 -1
  339. package/src/Carousel/Carousel.tsx +25 -6
  340. package/src/Chat/ChatComposer.doc.mjs +2 -2
  341. package/src/Chat/ChatComposerInput.doc.mjs +1 -1
  342. package/src/Chat/ChatComposerInput.tsx +2 -2
  343. package/src/Chat/ChatDictationButton.tsx +7 -1
  344. package/src/Chat/ChatLayout.tsx +4 -4
  345. package/src/Chat/ChatMessageBubble.tsx +15 -10
  346. package/src/Chat/ChatSystemMessage.test.tsx +5 -0
  347. package/src/Chat/ChatToolCalls.tsx +9 -2
  348. package/src/Chat/useTriggerMenu.tsx +1 -1
  349. package/src/CheckboxInput/CheckboxInput.doc.mjs +6 -0
  350. package/src/CheckboxInput/CheckboxInput.test.tsx +23 -0
  351. package/src/CheckboxInput/CheckboxInput.tsx +21 -6
  352. package/src/CheckboxList/CheckboxList.doc.mjs +6 -0
  353. package/src/CheckboxList/CheckboxList.test.tsx +94 -3
  354. package/src/CheckboxList/CheckboxListItem.tsx +17 -10
  355. package/src/Citation/Citation.doc.mjs +4 -3
  356. package/src/Citation/Citation.test.tsx +97 -0
  357. package/src/Citation/Citation.tsx +43 -11
  358. package/src/CodeBlock/CodeBlock.test.tsx +17 -0
  359. package/src/CodeBlock/CodeBlock.tsx +38 -11
  360. package/src/Collapsible/Collapsible.doc.mjs +1 -0
  361. package/src/Collapsible/Collapsible.test.tsx +7 -0
  362. package/src/Collapsible/Collapsible.tsx +16 -6
  363. package/src/CommandPalette/CommandPalette.test.tsx +95 -1
  364. package/src/CommandPalette/CommandPalette.tsx +41 -2
  365. package/src/CommandPalette/CommandPaletteItem.tsx +1 -1
  366. package/src/ContextMenu/ContextMenu.test.tsx +59 -2
  367. package/src/ContextMenu/ContextMenu.tsx +22 -11
  368. package/src/ContextMenu/index.ts +5 -0
  369. package/src/DateInput/DateInput.doc.mjs +25 -0
  370. package/src/DateInput/DateInput.test.tsx +230 -0
  371. package/src/DateInput/DateInput.tsx +51 -29
  372. package/src/DateRangeInput/DateRangeInput.doc.mjs +16 -0
  373. package/src/DateRangeInput/DateRangeInput.test.tsx +140 -0
  374. package/src/DateRangeInput/DateRangeInput.tsx +42 -30
  375. package/src/DateTimeInput/DateTimeInput.doc.mjs +22 -1
  376. package/src/DateTimeInput/DateTimeInput.test.tsx +112 -0
  377. package/src/DateTimeInput/DateTimeInput.tsx +44 -39
  378. package/src/Divider/Divider.test.tsx +32 -8
  379. package/src/Divider/Divider.tsx +14 -1
  380. package/src/DropdownMenu/DropdownMenu.test.tsx +60 -0
  381. package/src/DropdownMenu/DropdownMenu.tsx +31 -12
  382. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +8 -2
  383. package/src/DropdownMenu/DropdownMenuItem.tsx +9 -3
  384. package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +4 -12
  385. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +9 -10
  386. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +9 -3
  387. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +20 -2
  388. package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +88 -0
  389. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +635 -0
  390. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +553 -0
  391. package/src/DropdownMenu/index.ts +16 -0
  392. package/src/DropdownMenu/menuItemHover.ts +39 -0
  393. package/src/DropdownMenu/menuItemRoles.ts +9 -0
  394. package/src/DropdownMenu/renderDropdownItems.tsx +29 -15
  395. package/src/Field/Field.tsx +14 -9
  396. package/src/Field/FieldLabel.test.tsx +25 -0
  397. package/src/Field/FieldLabel.tsx +12 -3
  398. package/src/Field/inputStyles.stylex.ts +2 -0
  399. package/src/FieldStatus/FieldStatus.test.tsx +72 -0
  400. package/src/FieldStatus/FieldStatus.tsx +48 -2
  401. package/src/FileInput/FileInput.doc.mjs +23 -2
  402. package/src/FileInput/FileInput.test.tsx +104 -13
  403. package/src/FileInput/FileInput.tsx +106 -79
  404. package/src/HoverCard/useHoverCard.tsx +15 -3
  405. package/src/Icon/Icon.doc.mjs +13 -0
  406. package/src/Icon/Icon.test.tsx +89 -0
  407. package/src/Icon/Icon.tsx +51 -3
  408. package/src/Item/Item.doc.mjs +5 -0
  409. package/src/Item/Item.test.tsx +88 -0
  410. package/src/Item/Item.tsx +57 -11
  411. package/src/Layer/layerAnimations.stylex.ts +23 -0
  412. package/src/Lightbox/Lightbox.tsx +15 -7
  413. package/src/List/List.test.tsx +17 -29
  414. package/src/List/List.tsx +6 -1
  415. package/src/List/ListItem.doc.mjs +12 -0
  416. package/src/List/ListItem.tsx +13 -0
  417. package/src/Markdown/Markdown.test.tsx +21 -0
  418. package/src/Markdown/Markdown.tsx +4 -0
  419. package/src/MetadataList/MetadataList.test.tsx +24 -0
  420. package/src/MetadataList/MetadataList.tsx +6 -2
  421. package/src/MultiSelector/MultiSelector.doc.mjs +20 -0
  422. package/src/MultiSelector/MultiSelector.test.tsx +402 -0
  423. package/src/MultiSelector/MultiSelector.tsx +120 -59
  424. package/src/NavMenu/NavHeadingMenuItem.tsx +1 -1
  425. package/src/NumberInput/NumberInput.doc.mjs +21 -0
  426. package/src/NumberInput/NumberInput.test.tsx +71 -0
  427. package/src/NumberInput/NumberInput.tsx +35 -26
  428. package/src/Outline/Outline.test.tsx +12 -12
  429. package/src/Outline/Outline.tsx +2 -2
  430. package/src/Pagination/Pagination.tsx +11 -3
  431. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  432. package/src/PowerSearch/PowerSearch.test.tsx +23 -0
  433. package/src/PowerSearch/PowerSearch.tsx +10 -1
  434. package/src/ProgressBar/ProgressBar.test.tsx +34 -0
  435. package/src/ProgressBar/ProgressBar.tsx +16 -1
  436. package/src/RadioList/RadioList.doc.mjs +6 -0
  437. package/src/RadioList/RadioList.test.tsx +26 -4
  438. package/src/RadioList/RadioList.tsx +2 -2
  439. package/src/RadioList/RadioListItem.tsx +12 -5
  440. package/src/Resizable/ResizeHandle.test.tsx +94 -0
  441. package/src/Resizable/ResizeHandle.tsx +70 -20
  442. package/src/SegmentedControl/SegmentedControl.test.tsx +28 -0
  443. package/src/SegmentedControl/SegmentedControlItem.tsx +22 -2
  444. package/src/Selector/Selector.doc.mjs +15 -0
  445. package/src/Selector/Selector.test.tsx +370 -0
  446. package/src/Selector/Selector.tsx +88 -20
  447. package/src/SideNav/SideNav.tsx +1 -8
  448. package/src/SideNav/SideNavCollapseButton.tsx +9 -7
  449. package/src/SideNav/SideNavItem.tsx +5 -3
  450. package/src/Skeleton/Skeleton.test.tsx +16 -0
  451. package/src/Skeleton/Skeleton.tsx +13 -1
  452. package/src/Slider/Slider.doc.mjs +6 -0
  453. package/src/Slider/Slider.test.tsx +240 -12
  454. package/src/Slider/Slider.tsx +96 -23
  455. package/src/Spinner/Spinner.test.tsx +10 -8
  456. package/src/Spinner/Spinner.tsx +15 -5
  457. package/src/Switch/Switch.doc.mjs +142 -42
  458. package/src/Switch/Switch.test.tsx +111 -0
  459. package/src/Switch/Switch.tsx +149 -32
  460. package/src/TabList/Tab.tsx +2 -2
  461. package/src/TabList/TabList.doc.mjs +0 -6
  462. package/src/TabList/TabList.test.tsx +3 -15
  463. package/src/TabList/TabList.tsx +6 -17
  464. package/src/TabList/TabListContext.ts +0 -6
  465. package/src/TabList/TabMenu.tsx +2 -2
  466. package/src/Table/BaseTable.tsx +34 -2
  467. package/src/Table/Table.doc.mjs +11 -0
  468. package/src/Table/Table.test.tsx +85 -0
  469. package/src/Table/TableCell.tsx +3 -3
  470. package/src/Table/TableHeaderCell.tsx +3 -3
  471. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +9 -6
  472. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +19 -0
  473. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +27 -18
  474. package/src/Table/plugins/selection/useTableSelection.test.tsx +61 -3
  475. package/src/Table/plugins/selection/useTableSelection.tsx +3 -0
  476. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +38 -9
  477. package/src/Table/plugins/tree/useTableTreeData.tsx +17 -13
  478. package/src/Table/types.ts +33 -0
  479. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  480. package/src/TextArea/TextArea.doc.mjs +21 -0
  481. package/src/TextArea/TextArea.test.tsx +23 -0
  482. package/src/TextArea/TextArea.tsx +27 -26
  483. package/src/TextInput/TextInput.doc.mjs +21 -0
  484. package/src/TextInput/TextInput.test.tsx +150 -0
  485. package/src/TextInput/TextInput.tsx +28 -25
  486. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  487. package/src/Thumbnail/Thumbnail.tsx +1 -1
  488. package/src/TimeInput/TimeInput.doc.mjs +21 -0
  489. package/src/TimeInput/TimeInput.test.tsx +23 -0
  490. package/src/TimeInput/TimeInput.tsx +24 -25
  491. package/src/Timestamp/Timestamp.doc.mjs +32 -6
  492. package/src/Timestamp/Timestamp.test.tsx +371 -0
  493. package/src/Timestamp/Timestamp.tsx +96 -93
  494. package/src/Timestamp/formatInstant.ts +218 -0
  495. package/src/Timestamp/index.ts +4 -0
  496. package/src/Timestamp/tooltipEntries.test.ts +552 -0
  497. package/src/Timestamp/tooltipEntries.ts +210 -0
  498. package/src/Toast/ToastViewport.test.tsx +218 -8
  499. package/src/Toast/ToastViewport.tsx +71 -12
  500. package/src/ToggleButton/ToggleButton.test.tsx +24 -0
  501. package/src/ToggleButton/ToggleButton.tsx +19 -1
  502. package/src/Token/Token.test.tsx +166 -15
  503. package/src/Token/Token.tsx +104 -47
  504. package/src/Token/TokenLink.tsx +107 -0
  505. package/src/Token/index.ts +1 -1
  506. package/src/Tokenizer/Tokenizer.doc.mjs +23 -1
  507. package/src/Tokenizer/Tokenizer.test.tsx +23 -0
  508. package/src/Tokenizer/Tokenizer.tsx +14 -2
  509. package/src/Tooltip/Tooltip.test.tsx +46 -0
  510. package/src/Tooltip/useTooltip.tsx +20 -0
  511. package/src/TopNav/TopNav.test.tsx +93 -2
  512. package/src/TopNav/TopNavItem.doc.mjs +3 -3
  513. package/src/TopNav/TopNavItem.tsx +35 -6
  514. package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +1 -1
  515. package/src/TreeList/TreeList.doc.mjs +6 -0
  516. package/src/TreeList/TreeList.test.tsx +50 -10
  517. package/src/TreeList/TreeList.tsx +5 -0
  518. package/src/TreeList/TreeListBranches.tsx +8 -6
  519. package/src/TreeList/TreeListItem.tsx +33 -11
  520. package/src/Typeahead/BaseTypeahead.tsx +42 -15
  521. package/src/Typeahead/Typeahead.doc.mjs +13 -0
  522. package/src/Typeahead/Typeahead.test.tsx +112 -0
  523. package/src/Typeahead/Typeahead.tsx +17 -1
  524. package/src/Typeahead/TypeaheadItem.tsx +2 -1
  525. package/src/__tests__/forcedColors.ts +60 -0
  526. package/src/__tests__/inputWidthContract.test.tsx +62 -0
  527. package/src/doc-mjs.d.ts +19 -0
  528. package/src/hooks/index.ts +6 -0
  529. package/src/hooks/isRtlElement.ts +27 -0
  530. package/src/hooks/useAnnounce.test.tsx +75 -2
  531. package/src/hooks/useAnnounce.ts +47 -1
  532. package/src/hooks/useFocusTrap.test.tsx +104 -0
  533. package/src/hooks/useFocusTrap.ts +61 -12
  534. package/src/hooks/useGridFocus.doc.mjs +3 -3
  535. package/src/hooks/useGridFocus.test.tsx +41 -0
  536. package/src/hooks/useGridFocus.ts +21 -10
  537. package/src/hooks/useHotkeys.ts +9 -0
  538. package/src/hooks/useInputStatusIcon.test.tsx +162 -0
  539. package/src/hooks/useInputStatusIcon.tsx +224 -0
  540. package/src/hooks/useKeyboardHint.test.tsx +22 -0
  541. package/src/hooks/useKeyboardHint.tsx +6 -2
  542. package/src/hooks/useListFocus.doc.mjs +25 -3
  543. package/src/hooks/useListFocus.test.tsx +153 -1
  544. package/src/hooks/useListFocus.ts +102 -9
  545. package/src/i18n/InternationalizationContext.ts +4 -0
  546. package/src/i18n/InternationalizationProvider.tsx +12 -2
  547. package/src/i18n/__tests__/getLocaleDirection.test.ts +40 -0
  548. package/src/i18n/__tests__/useDirection.test.tsx +66 -0
  549. package/src/i18n/getLocaleDirection.ts +29 -0
  550. package/src/i18n/index.ts +6 -0
  551. package/src/i18n/useDirection.ts +29 -0
  552. package/src/reset.css +0 -26
  553. package/src/reset.test.ts +60 -0
  554. package/src/theme/derivedVarRegistry.test.ts +1 -0
  555. package/src/theme/derivedVarRegistry.ts +6 -0
  556. package/src/theme/generateThemeRules.test.ts +42 -0
  557. package/src/utils/index.ts +2 -0
  558. package/src/utils/plainDate.test.ts +24 -6
  559. package/src/utils/plainDate.ts +18 -4
  560. package/src/utils/rtlStyles.ts +27 -0
@@ -45,11 +45,17 @@ export const docs = {
45
45
  'Whether to show a tooltip with the full date/time on hover when displaying relative time.',
46
46
  default: 'true',
47
47
  },
48
+ {
49
+ name: 'tooltipEntries',
50
+ type: 'ReadonlyArray<{timezoneID?: string; format?: TimestampTooltipFormat; label?: string}>',
51
+ description:
52
+ "Lines to show in the hover tooltip, so one instant can be read in several time zones and/or formats at once. Each entry is one line, in the order given. Omit timezoneID (or pass 'local') for the viewer's own zone; format defaults to the full absolute style and also accepts 'full' alongside every non-relative TimestampFormat. Configuring entries also attaches the tooltip to absolute formats, which otherwise have none.",
53
+ },
48
54
  {
49
55
  name: 'isTimezoneShown',
50
56
  type: 'boolean',
51
57
  description:
52
- 'Whether to append the timezone abbreviation. Applies to date_time, time, system_date_time, and system_time formats.',
58
+ 'Whether to append the timezone abbreviation to the visible text. Applies to the date_time and time formats; system_* formats stay machine-readable and never carry one. Use tooltipEntries to control the tooltip\'s time zones.',
53
59
  default: 'false',
54
60
  },
55
61
  {
@@ -94,14 +100,19 @@ export const docs = {
94
100
  {guidance: true, description: 'Use the auto format in feeds and lists so recent items show "2 hours ago" and older items show the full date automatically.'},
95
101
  {guidance: true, description: 'Keep formatting consistent within the same list or table; mixing relative and absolute timestamps in the same column confuses scanning.'},
96
102
  {guidance: true, description: 'Enable isTimezoneShown when the audience spans multiple time zones, like a global team calendar or audit log.'},
103
+ {guidance: true, description: 'Use tooltipEntries when readers must compare zones, such as an incident log where the reader\'s time and the event\'s origin zone both matter.'},
104
+ {guidance: true, description: 'Label every entry once a tooltip shows more than one zone; a bare abbreviation is not always recognizable (Tokyo renders as "GMT+9", not "JST").'},
97
105
  {guidance: true, description: 'Use isLive for active dashboards or real-time feeds so the relative time stays accurate without a page refresh.'},
98
106
  {guidance: false, description: 'Don\'t display raw Unix timestamps or ISO strings to users; always pass them through Timestamp to get a human-readable format.'},
99
107
  {guidance: false, description: 'Avoid system_date or system_time formats in user-facing UI; they are meant for developer tools, logs, and machine-readable contexts.'},
100
108
  {guidance: false, description: 'Don\'t disable the tooltip on relative timestamps; users expect to hover for the full date when they see "3 hours ago".'},
109
+ {guidance: false, description: 'Don\'t stack many zones into one tooltip; it is capped at 300px wide and long labelled lines wrap. Two or three entries read well.'},
110
+ {guidance: false, description: 'Don\'t pass a fixed-offset abbreviation like "EST" as timezoneID; it is a valid identifier but never observes daylight saving, so it reads an hour wrong for half the year. Use the region id, "America/New_York".'},
101
111
  ],
102
112
  anatomy: [
103
113
  {name: 'Formatted text', required: true, description: 'The rendered date, time, or relative label like "2 hours ago" or "Mar 21, 2025".'},
104
- {name: 'Tooltip', required: false, description: 'A hover card showing the full absolute date and time when the display is relative.'},
114
+ {name: 'Tooltip', required: false, description: 'A hover card showing the full absolute date and time when the display is relative, or the lines configured via tooltipEntries.'},
115
+ {name: 'Tooltip entry', required: false, description: 'One line of the tooltip: an optional label beside the instant rendered in one time zone and format.'},
105
116
  ],
106
117
  },
107
118
  };
@@ -113,7 +124,10 @@ export const docsZh = {
113
124
  format: "\u663e\u793a\u683c\u5f0f\u3002'relative' \u663e\u793a '2\u5c0f\u65f6\u524d'\uff0c'date' \u663e\u793a\u65e5\u671f\uff0c'date_long' \u663e\u793a\u957f\u6708\u4efd\u65e5\u671f\uff0c'date_weekday' \u663e\u793a\u661f\u671f+\u65e5\u671f\uff0c'auto' \u6839\u636e\u65f6\u95f4\u8fdc\u8fd1\u81ea\u52a8\u5207\u6362\u3002",
114
125
  autoThreshold: "auto \u683c\u5f0f\u4ece\u76f8\u5bf9\u65f6\u95f4\u5207\u6362\u5230 date_time \u7684\u9608\u503c\u79d2\u6570\u3002",
115
126
  hasTooltip: '\u60ac\u505c\u65f6\u662f\u5426\u663e\u793a\u5305\u542b\u5b8c\u6574\u65e5\u671f/\u65f6\u95f4\u7684\u5de5\u5177\u63d0\u793a\uff08\u76f8\u5bf9\u65f6\u95f4\u6a21\u5f0f\uff09\u3002',
116
- isTimezoneShown: '\u662f\u5426\u9644\u52a0\u65f6\u533a\u7f29\u5199\u3002',
127
+ tooltipEntries:
128
+ '\u5de5\u5177\u63d0\u793a\u4e2d\u8981\u663e\u793a\u7684\u884c\uff0c\u7528\u4e8e\u540c\u65f6\u5c55\u793a\u591a\u4e2a\u65f6\u533a\u548c/\u6216\u591a\u79cd\u683c\u5f0f\u3002\u6bcf\u9879\u4e3a\u4e00\u884c\uff0c\u6309\u987a\u5e8f\u6e32\u67d3\uff1b\u7701\u7565 timezoneID\uff08\u6216\u4f20\u5165 \'local\'\uff09\u8868\u793a\u67e5\u770b\u8005\u672c\u5730\u65f6\u533a\u3002\u914d\u7f6e\u540e\uff0c\u7edd\u5bf9\u65f6\u95f4\u683c\u5f0f\u4e5f\u4f1a\u663e\u793a\u5de5\u5177\u63d0\u793a\u3002',
129
+ isTimezoneShown:
130
+ '\u662f\u5426\u5728\u53ef\u89c1\u6587\u672c\u540e\u9644\u52a0\u65f6\u533a\u7f29\u5199\uff08\u4ec5 date_time \u548c time\uff1bsystem_* \u683c\u5f0f\u4e0d\u9644\u52a0\uff09\u3002',
117
131
  isLive: '\u76f8\u5bf9\u65f6\u95f4\u662f\u5426\u5b9e\u65f6\u66f4\u65b0\u3002',
118
132
  type: '\u6765\u81ea Text \u7684\u8bed\u4e49\u6587\u672c\u7c7b\u578b\u3002',
119
133
  size: '\u663e\u5f0f\u5b57\u4f53\u5927\u5c0f\u8986\u76d6\u3002',
@@ -127,21 +141,26 @@ export const docsZh = {
127
141
  {guidance: true, description: 'Use the auto format in feeds and lists so recent items show "2 hours ago" and older items show the full date automatically.'},
128
142
  {guidance: true, description: 'Keep formatting consistent within the same list or table; mixing relative and absolute timestamps in the same column confuses scanning.'},
129
143
  {guidance: true, description: 'Enable isTimezoneShown when the audience spans multiple time zones, like a global team calendar or audit log.'},
144
+ {guidance: true, description: 'Use tooltipEntries when readers must compare zones, such as an incident log where the reader\'s time and the event\'s origin zone both matter.'},
145
+ {guidance: true, description: 'Label every entry once a tooltip shows more than one zone; a bare abbreviation is not always recognizable (Tokyo renders as "GMT+9", not "JST").'},
130
146
  {guidance: true, description: 'Use isLive for active dashboards or real-time feeds so the relative time stays accurate without a page refresh.'},
131
147
  {guidance: false, description: 'Don\'t display raw Unix timestamps or ISO strings to users; always pass them through Timestamp to get a human-readable format.'},
132
148
  {guidance: false, description: 'Avoid system_date or system_time formats in user-facing UI; they are meant for developer tools, logs, and machine-readable contexts.'},
133
149
  {guidance: false, description: 'Don\'t disable the tooltip on relative timestamps; users expect to hover for the full date when they see "3 hours ago".'},
150
+ {guidance: false, description: 'Don\'t stack many zones into one tooltip; it is capped at 300px wide and long labelled lines wrap. Two or three entries read well.'},
151
+ {guidance: false, description: 'Don\'t pass a fixed-offset abbreviation like "EST" as timezoneID; it is a valid identifier but never observes daylight saving, so it reads an hour wrong for half the year. Use the region id, "America/New_York".'},
134
152
  ],
135
153
  anatomy: [
136
154
  {name: 'Formatted text', required: true, description: 'The rendered date, time, or relative label like "2 hours ago" or "Mar 21, 2025".'},
137
- {name: 'Tooltip', required: false, description: 'A hover card showing the full absolute date and time when the display is relative.'},
155
+ {name: 'Tooltip', required: false, description: 'A hover card showing the full absolute date and time when the display is relative, or the lines configured via tooltipEntries.'},
156
+ {name: 'Tooltip entry', required: false, description: 'One line of the tooltip: an optional label beside the instant rendered in one time zone and format.'},
138
157
  ],
139
158
  },
140
159
  };
141
160
 
142
161
  /** @type {import('../docs-types').TranslationDoc} */
143
162
  export const docsDense = {
144
- description: 'formatted timestamp with relative/absolute/auto modes, live updates, timezone display',
163
+ description: 'formatted timestamp with relative/absolute/auto modes, live updates, timezone display, multi-zone tooltip',
145
164
  usage: {
146
165
  description:
147
166
  'Timestamp formats a date or time into readable text. Use for creation dates, update times, or schedules; relative for recency, absolute for precision, auto to switch automatically.',
@@ -149,10 +168,14 @@ export const docsDense = {
149
168
  {guidance: true, description: 'Auto format in feeds and lists; recent shows relative, older shows date_time.'},
150
169
  {guidance: true, description: 'Consistent formatting within the same list or table column.'},
151
170
  {guidance: true, description: 'isTimezoneShown for multi-timezone audiences.'},
171
+ {guidance: true, description: 'tooltipEntries to compare zones when the reader time and event zone both matter.'},
172
+ {guidance: true, description: 'Label each entry once a tooltip shows more than one zone; a bare abbreviation is not always recognizable (Tokyo renders "GMT+9", not "JST").'},
152
173
  {guidance: true, description: 'isLive for dashboards so relative time stays current.'},
153
174
  {guidance: false, description: 'Don\'t show raw Unix timestamps or ISO strings; always use Timestamp.'},
154
175
  {guidance: false, description: 'Avoid system_* formats in user-facing UI; those are for dev tools and logs.'},
155
176
  {guidance: false, description: 'Don\'t disable tooltip on relative timestamps; users expect the full date on hover.'},
177
+ {guidance: false, description: 'Don\'t stack many zones in one tooltip; it caps at 300px and long labels wrap. Two or three read well.'},
178
+ {guidance: false, description: 'Don\'t use fixed-offset ids like "EST" for timezoneID; they skip DST. Use region ids like "America/New_York".'},
156
179
  ],
157
180
  },
158
181
  propDescriptions: {
@@ -160,7 +183,10 @@ export const docsDense = {
160
183
  format: "display mode: 'relative', 'auto', 'date', 'date_long', 'date_weekday', 'date_time', 'time', 'system_date', 'system_date_time', 'system_time'",
161
184
  autoThreshold: 'seconds threshold for auto relative\u2192date_time switch',
162
185
  hasTooltip: 'show full time tooltip on hover (relative mode)',
163
- isTimezoneShown: 'append timezone abbreviation',
186
+ tooltipEntries:
187
+ "tooltip lines across zones/formats: [{timezoneID?, format?, label?}]; timezoneID omitted or 'local' = viewer zone, format defaults to 'full'; also enables the tooltip on absolute formats",
188
+ isTimezoneShown:
189
+ 'append timezone abbreviation to visible text (date_time and time only)',
164
190
  isLive: 'live-update relative time',
165
191
  type: 'Text semantic type',
166
192
  size: 'font size override',
@@ -4,6 +4,7 @@ import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
4
4
  import {render, screen, act, waitFor} from '@testing-library/react';
5
5
  import userEvent from '@testing-library/user-event';
6
6
  import {Timestamp} from './Timestamp';
7
+ import {formatTooltipLines} from './tooltipEntries';
7
8
 
8
9
  describe('Timestamp', () => {
9
10
  beforeEach(() => {
@@ -487,4 +488,374 @@ describe('Timestamp', () => {
487
488
  expect(label).not.toContain('ago');
488
489
  });
489
490
  });
491
+
492
+ // --- Multi-zone / multi-format tooltip entries ---
493
+
494
+ describe('tooltipEntries', () => {
495
+ // A fixed instant that lands on a different calendar day in Tokyo, so a
496
+ // zone-blind implementation is visible in the output.
497
+ const VALUE = '2026-02-19T17:00:00Z';
498
+
499
+ const originalShowPopover = HTMLElement.prototype.showPopover;
500
+ const originalHidePopover = HTMLElement.prototype.hidePopover;
501
+
502
+ beforeEach(() => {
503
+ // The tooltip is lazy-loaded; real timers let the dynamic import and
504
+ // RTL's findBy* resolve (the outer beforeEach installs fake timers).
505
+ vi.useRealTimers();
506
+ HTMLElement.prototype.showPopover = vi.fn();
507
+ HTMLElement.prototype.hidePopover = vi.fn();
508
+ });
509
+
510
+ afterEach(() => {
511
+ HTMLElement.prototype.showPopover = originalShowPopover;
512
+ HTMLElement.prototype.hidePopover = originalHidePopover;
513
+ });
514
+
515
+ it('leaves the default tooltip as a single unwrapped line', async () => {
516
+ render(
517
+ <Timestamp
518
+ value={Date.now() / 1000 - 3600}
519
+ format="relative"
520
+ data-testid="ts"
521
+ />,
522
+ );
523
+ const layer = await screen.findByRole('tooltip', {hidden: true});
524
+ // No entries configured — the tooltip content stays the bare string it
525
+ // has always been, with no list wrapper introduced around it.
526
+ expect(layer.querySelector('dl')).toBeNull();
527
+
528
+ const normalize = (s: string) => s.replace(/\s+/g, ' ');
529
+ expect(normalize(layer.textContent ?? '')).toContain(
530
+ normalize(screen.getByTestId('ts').getAttribute('aria-label') ?? '\0'),
531
+ );
532
+ });
533
+
534
+ it('renders one tooltip line per configured entry', async () => {
535
+ render(
536
+ <Timestamp
537
+ value={VALUE}
538
+ format="relative"
539
+ tooltipEntries={[
540
+ {label: 'Local'},
541
+ {timezoneID: 'UTC', label: 'UTC'},
542
+ {timezoneID: 'Asia/Tokyo', label: 'Tokyo'},
543
+ ]}
544
+ />,
545
+ );
546
+ const layer = await screen.findByRole('tooltip', {hidden: true});
547
+ expect(layer.querySelectorAll('dd')).toHaveLength(3);
548
+ expect(layer.textContent).toContain('Local');
549
+ expect(layer.textContent).toContain('UTC');
550
+ expect(layer.textContent).toContain('Tokyo');
551
+ });
552
+
553
+ it('renders each entry in the time zone it names', async () => {
554
+ render(
555
+ <Timestamp
556
+ value={VALUE}
557
+ format="relative"
558
+ tooltipEntries={[
559
+ {timezoneID: 'UTC', format: 'system_date_time', label: 'UTC'},
560
+ {
561
+ timezoneID: 'Asia/Tokyo',
562
+ format: 'system_date_time',
563
+ label: 'Tokyo',
564
+ },
565
+ ]}
566
+ />,
567
+ );
568
+ const layer = await screen.findByRole('tooltip', {hidden: true});
569
+ const values = Array.from(layer.querySelectorAll('dd')).map(
570
+ el => el.textContent,
571
+ );
572
+ // Machine formats are locale- and host-timezone-independent, so these
573
+ // hold on any developer machine and on CI.
574
+ expect(values).toEqual(['2026-02-19 17:00:00', '2026-02-20 02:00:00']);
575
+ });
576
+
577
+ it('shows a tooltip for absolute formats once entries are configured', async () => {
578
+ render(
579
+ <Timestamp
580
+ value={VALUE}
581
+ format="date_time"
582
+ tooltipEntries={[{timezoneID: 'UTC', label: 'UTC'}]}
583
+ data-testid="ts"
584
+ />,
585
+ );
586
+ // Without entries an absolute format has no tooltip at all; configuring
587
+ // entries must not be silently ignored.
588
+ const layer = await screen.findByRole('tooltip', {hidden: true});
589
+ expect(layer.textContent).toContain('UTC');
590
+ // ...and the anchor becomes keyboard-reachable, as it is for relative.
591
+ expect(screen.getByTestId('ts')).toHaveAttribute('tabindex', '0');
592
+ });
593
+
594
+ it('treats an empty entry list exactly like no configuration', () => {
595
+ render(
596
+ <Timestamp
597
+ value={VALUE}
598
+ format="date_time"
599
+ tooltipEntries={[]}
600
+ data-testid="ts"
601
+ />,
602
+ );
603
+ // `hasTooltip` stays the only on/off switch — an empty array is not a
604
+ // second way to spell "off", and it must not widen the gate either.
605
+ expect(screen.getByTestId('ts')).not.toHaveAttribute('tabindex');
606
+ });
607
+
608
+ it('still honors hasTooltip={false} when entries are configured', () => {
609
+ render(
610
+ <Timestamp
611
+ value={VALUE}
612
+ format="date_time"
613
+ hasTooltip={false}
614
+ tooltipEntries={[{timezoneID: 'UTC'}]}
615
+ data-testid="ts"
616
+ />,
617
+ );
618
+ expect(screen.getByTestId('ts')).not.toHaveAttribute('tabindex');
619
+ });
620
+
621
+ it('leaves the accessible name unchanged when entries are configured', async () => {
622
+ const value = Date.now() / 1000 - 3600;
623
+ const {unmount} = render(
624
+ <Timestamp value={value} format="relative" data-testid="a" />,
625
+ );
626
+ const before = screen.getByTestId('a').getAttribute('aria-label');
627
+ unmount();
628
+
629
+ render(
630
+ <Timestamp
631
+ value={value}
632
+ format="relative"
633
+ tooltipEntries={[{timezoneID: 'UTC', label: 'UTC'}]}
634
+ data-testid="b"
635
+ />,
636
+ );
637
+ // The accessible name stays the canonical absolute time in the viewer's
638
+ // own zone; the extra zones reach assistive tech through the tooltip's
639
+ // aria-describedby, not by being stuffed into the name.
640
+ expect(screen.getByTestId('b').getAttribute('aria-label')).toBe(before);
641
+ });
642
+
643
+ it('does not add an accessible name to absolute formats with entries', () => {
644
+ render(
645
+ <Timestamp
646
+ value={VALUE}
647
+ format="date_time"
648
+ tooltipEntries={[{timezoneID: 'UTC'}]}
649
+ data-testid="ts"
650
+ />,
651
+ );
652
+ expect(screen.getByTestId('ts').getAttribute('aria-label')).toBeNull();
653
+ });
654
+
655
+ it('renders nothing for an unparseable value even with entries', () => {
656
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
657
+ try {
658
+ const {container} = render(
659
+ <Timestamp
660
+ value="not-a-date"
661
+ tooltipEntries={[{timezoneID: 'UTC', label: 'UTC'}]}
662
+ />,
663
+ );
664
+ // The invalid-value bail-out runs before any tooltip work, so there is
665
+ // no half-rendered tooltip anchored to nothing.
666
+ expect(container).toBeEmptyDOMElement();
667
+ } finally {
668
+ warn.mockRestore();
669
+ }
670
+ });
671
+
672
+ it('shows configured entries when auto resolves to an absolute format', async () => {
673
+ render(
674
+ <Timestamp
675
+ value={VALUE}
676
+ format="auto"
677
+ autoThreshold={0}
678
+ tooltipEntries={[
679
+ {timezoneID: 'UTC', format: 'system_date_time', label: 'UTC'},
680
+ ]}
681
+ data-testid="ts"
682
+ />,
683
+ );
684
+ // autoThreshold={0} forces the absolute branch regardless of the clock.
685
+ const layer = await screen.findByRole('tooltip', {hidden: true});
686
+ expect(layer.textContent).toContain('2026-02-19 17:00:00');
687
+ expect(screen.getByTestId('ts')).toHaveAttribute('tabindex', '0');
688
+ });
689
+
690
+ it('accepts a unix-seconds value alongside entries', async () => {
691
+ render(
692
+ <Timestamp
693
+ value={Date.parse(VALUE) / 1000}
694
+ format="relative"
695
+ tooltipEntries={[
696
+ {timezoneID: 'UTC', format: 'system_date_time', label: 'UTC'},
697
+ ]}
698
+ />,
699
+ );
700
+ const layer = await screen.findByRole('tooltip', {hidden: true});
701
+ expect(layer.textContent).toContain('2026-02-19 17:00:00');
702
+ });
703
+
704
+ it('pairs exactly one label cell with one value cell per entry', async () => {
705
+ render(
706
+ <Timestamp
707
+ value={VALUE}
708
+ format="relative"
709
+ tooltipEntries={[
710
+ {timezoneID: 'UTC', label: 'UTC'},
711
+ {timezoneID: 'Asia/Tokyo'},
712
+ {timezoneID: 'Europe/London', label: 'London'},
713
+ ]}
714
+ />,
715
+ );
716
+ const layer = await screen.findByRole('tooltip', {hidden: true});
717
+ // An unlabeled entry still emits its label cell, so the two-column grid
718
+ // stays aligned and the <dl> stays valid markup.
719
+ expect(layer.querySelectorAll('dt')).toHaveLength(3);
720
+ expect(layer.querySelectorAll('dd')).toHaveLength(3);
721
+ expect(layer.querySelectorAll('dt')[1].textContent).toBe('');
722
+ });
723
+
724
+ it('gives the tab stop back when entries are removed', () => {
725
+ const {rerender} = render(
726
+ <Timestamp
727
+ value={VALUE}
728
+ format="date_time"
729
+ tooltipEntries={[{timezoneID: 'UTC'}]}
730
+ data-testid="ts"
731
+ />,
732
+ );
733
+ expect(screen.getByTestId('ts')).toHaveAttribute('tabindex', '0');
734
+
735
+ rerender(<Timestamp value={VALUE} format="date_time" data-testid="ts" />);
736
+ // The widened gate is driven purely by entry presence, so dropping the
737
+ // prop must also drop the tab stop rather than stranding one.
738
+ expect(screen.getByTestId('ts')).not.toHaveAttribute('tabindex');
739
+ });
740
+
741
+ it('describes an absolute-format timestamp with its tooltip', async () => {
742
+ render(
743
+ <Timestamp
744
+ value={VALUE}
745
+ format="date_time"
746
+ tooltipEntries={[{timezoneID: 'UTC', label: 'UTC'}]}
747
+ data-testid="ts"
748
+ />,
749
+ );
750
+ const layer = await screen.findByRole('tooltip', {hidden: true});
751
+ const el = screen.getByTestId('ts');
752
+ // An absolute format carries no aria-label, so aria-describedby is the
753
+ // only route the configured zones have to assistive tech.
754
+ expect(el.getAttribute('aria-label')).toBeNull();
755
+ await waitFor(() => {
756
+ expect(el.getAttribute('aria-describedby')).toBe(layer.id);
757
+ });
758
+ expect(layer.id).toBeTruthy();
759
+ });
760
+
761
+ it('leaves no tooltip and no description when hasTooltip is false', () => {
762
+ render(
763
+ <Timestamp
764
+ value={VALUE}
765
+ format="relative"
766
+ hasTooltip={false}
767
+ tooltipEntries={[{timezoneID: 'UTC', label: 'UTC'}]}
768
+ data-testid="ts"
769
+ />,
770
+ );
771
+ expect(
772
+ screen.queryByRole('tooltip', {hidden: true}),
773
+ ).not.toBeInTheDocument();
774
+ // No orphaned reference pointing at a tooltip that was never rendered.
775
+ expect(
776
+ screen.getByTestId('ts').getAttribute('aria-describedby'),
777
+ ).toBeNull();
778
+ });
779
+
780
+ it('does not crash on an unknown time zone', async () => {
781
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
782
+ try {
783
+ render(
784
+ <Timestamp
785
+ value={VALUE}
786
+ format="relative"
787
+ tooltipEntries={[
788
+ {timezoneID: 'Not/AZone', format: 'system_date_time'},
789
+ {timezoneID: 'UTC', format: 'system_date_time'},
790
+ ]}
791
+ />,
792
+ );
793
+ const layer = await screen.findByRole('tooltip', {hidden: true});
794
+ const values = Array.from(layer.querySelectorAll('dd')).map(
795
+ el => el.textContent,
796
+ );
797
+ expect(values).toHaveLength(2);
798
+ expect(values[1]).toBe('2026-02-19 17:00:00');
799
+ expect(warn).toHaveBeenCalledWith(expect.stringContaining('Not/AZone'));
800
+ } finally {
801
+ warn.mockRestore();
802
+ }
803
+ });
804
+ });
805
+
806
+ describe('one formatter behind both surfaces', () => {
807
+ // The rendered text and a tooltip line are two views of the same instant.
808
+ // They are meant to come from one formatter parameterized by zone, not two
809
+ // that happen to agree: a format added or reshaped on one surface and not
810
+ // the other is a silent drift, invisible until someone compares them.
811
+ // These assertions compare the two surfaces directly, so re-forking either
812
+ // one fails here. Locale- and timezone-agnostic: nothing is pinned to a
813
+ // literal, only the two paths to each other.
814
+ const VALUE = '2026-02-19T17:00:00Z';
815
+
816
+ const SHARED_FORMATS = [
817
+ 'date',
818
+ 'date_long',
819
+ 'date_weekday',
820
+ 'date_time',
821
+ 'time',
822
+ 'system_date',
823
+ 'system_date_time',
824
+ 'system_time',
825
+ ] as const;
826
+
827
+ it.each(SHARED_FORMATS)(
828
+ 'renders %s identically as text and as a zone-less tooltip line',
829
+ format => {
830
+ render(
831
+ <Timestamp
832
+ value={VALUE}
833
+ format={format}
834
+ hasTooltip={false}
835
+ data-testid="ts"
836
+ />,
837
+ );
838
+ const [line] = formatTooltipLines(new Date(VALUE), [{format}]);
839
+ expect(line.value).toBe(screen.getByTestId('ts').textContent);
840
+ },
841
+ );
842
+
843
+ it('renders the full style identically as the aria-label and as a line', () => {
844
+ // 'full' is the one member with no visible-text counterpart — it backs
845
+ // the accessible name of a relative timestamp and the tooltip's default
846
+ // line. Those two must not drift apart either.
847
+ render(
848
+ <Timestamp
849
+ value={VALUE}
850
+ format="relative"
851
+ hasTooltip={false}
852
+ data-testid="ts"
853
+ />,
854
+ );
855
+ const [line] = formatTooltipLines(new Date(VALUE), [{format: 'full'}]);
856
+ expect(line.value).toBe(
857
+ screen.getByTestId('ts').getAttribute('aria-label'),
858
+ );
859
+ });
860
+ });
490
861
  });