@astryxdesign/core 0.7.1-canary.ff40b78 → 0.7.1

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 (348) hide show
  1. package/CHANGELOG.md +116 -0
  2. package/dist/Button/Button.d.ts +1 -1
  3. package/dist/Button/Button.d.ts.map +1 -1
  4. package/dist/Button/Button.js +5 -2
  5. package/dist/Calendar/Calendar.d.ts.map +1 -1
  6. package/dist/Calendar/Calendar.js +191 -32
  7. package/dist/Chat/ChatComposerInput.d.ts +9 -0
  8. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  9. package/dist/Chat/ChatComposerInput.js +5 -0
  10. package/dist/Chat/ChatMessage.d.ts +11 -0
  11. package/dist/Chat/ChatMessage.d.ts.map +1 -1
  12. package/dist/Chat/ChatMessage.js +18 -9
  13. package/dist/Chat/useTriggerMenu.d.ts +9 -2
  14. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  15. package/dist/Chat/useTriggerMenu.js +222 -50
  16. package/dist/CodeBlock/CodeBlock.d.ts +10 -1
  17. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  18. package/dist/CodeBlock/CodeBlock.js +158 -38
  19. package/dist/CodeBlock/tokenizer.d.ts +39 -0
  20. package/dist/CodeBlock/tokenizer.d.ts.map +1 -1
  21. package/dist/CodeBlock/tokenizer.js +108 -0
  22. package/dist/CommandPalette/CommandPalette.d.ts +9 -5
  23. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  24. package/dist/CommandPalette/CommandPalette.js +38 -20
  25. package/dist/CommandPalette/CommandPaletteEmpty.d.ts +4 -3
  26. package/dist/CommandPalette/CommandPaletteEmpty.d.ts.map +1 -1
  27. package/dist/CommandPalette/CommandPaletteEmpty.js +4 -3
  28. package/dist/CommandPalette/CommandPaletteInput.d.ts +5 -4
  29. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  30. package/dist/CommandPalette/CommandPaletteInput.js +35 -18
  31. package/dist/CommandPalette/CommandPaletteItem.d.ts +11 -6
  32. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  33. package/dist/CommandPalette/CommandPaletteItem.js +8 -9
  34. package/dist/CommandPalette/CommandPaletteList.d.ts +4 -2
  35. package/dist/CommandPalette/CommandPaletteList.d.ts.map +1 -1
  36. package/dist/CommandPalette/CommandPaletteList.js +10 -5
  37. package/dist/ComplexSelector/ComplexSelector.d.ts +8 -5
  38. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  39. package/dist/ComplexSelector/ComplexSelector.js +12 -3
  40. package/dist/ContextMenu/ContextMenu.d.ts +3 -3
  41. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  42. package/dist/ContextMenu/ContextMenu.js +62 -48
  43. package/dist/DateInput/DateInput.js +1 -1
  44. package/dist/DateInput/MonthScroller.d.ts.map +1 -1
  45. package/dist/DateInput/MonthScroller.js +12 -4
  46. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  47. package/dist/DateRangeInput/DateRangeInput.js +12 -2
  48. package/dist/Dialog/Dialog.d.ts +1 -1
  49. package/dist/Dialog/Dialog.d.ts.map +1 -1
  50. package/dist/Dialog/Dialog.js +16 -5
  51. package/dist/Dialog/DialogHeader.d.ts +1 -1
  52. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  53. package/dist/Dialog/DialogHeader.js +7 -7
  54. package/dist/Divider/Divider.d.ts +1 -0
  55. package/dist/Divider/Divider.d.ts.map +1 -1
  56. package/dist/Divider/Divider.js +5 -4
  57. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  58. package/dist/DropdownMenu/DropdownMenu.js +161 -65
  59. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +7 -5
  60. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  61. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +12 -5
  62. package/dist/Field/InputClearButton.d.ts +8 -4
  63. package/dist/Field/InputClearButton.d.ts.map +1 -1
  64. package/dist/Field/InputClearButton.js +23 -7
  65. package/dist/FormLayout/FormLayout.d.ts.map +1 -1
  66. package/dist/FormLayout/FormLayout.js +1 -0
  67. package/dist/Grid/Grid.d.ts +13 -4
  68. package/dist/Grid/Grid.d.ts.map +1 -1
  69. package/dist/Grid/Grid.js +7 -1
  70. package/dist/HoverCard/useHoverCard.d.ts +1 -1
  71. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  72. package/dist/HoverCard/useHoverCard.js +67 -49
  73. package/dist/InputGroup/InputGroup.d.ts +2 -4
  74. package/dist/InputGroup/InputGroup.d.ts.map +1 -1
  75. package/dist/InputGroup/InputGroup.js +2 -4
  76. package/dist/InputGroup/groupStyles.d.ts +10 -2
  77. package/dist/InputGroup/groupStyles.d.ts.map +1 -1
  78. package/dist/InputGroup/groupStyles.js +19 -2
  79. package/dist/Layer/useLayerDismissal.d.ts +9 -9
  80. package/dist/Layer/useLayerDismissal.js +9 -9
  81. package/dist/Markdown/Markdown.d.ts.map +1 -1
  82. package/dist/Markdown/Markdown.js +25 -4
  83. package/dist/MobileNav/MobileNav.d.ts +7 -0
  84. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  85. package/dist/MobileNav/MobileNav.js +45 -2
  86. package/dist/MultiSelector/MultiSelector.d.ts +7 -4
  87. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  88. package/dist/MultiSelector/MultiSelector.js +24 -3
  89. package/dist/NumberInput/NumberInput.js +1 -1
  90. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  91. package/dist/PowerSearch/PowerSearchEditPopover.js +6 -0
  92. package/dist/Resizable/ResizeHandle.d.ts +1 -1
  93. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  94. package/dist/Resizable/ResizeHandle.js +83 -15
  95. package/dist/Resizable/useResizable.d.ts +23 -0
  96. package/dist/Resizable/useResizable.d.ts.map +1 -1
  97. package/dist/Resizable/useResizable.js +78 -0
  98. package/dist/Selector/Selector.d.ts +6 -3
  99. package/dist/Selector/Selector.d.ts.map +1 -1
  100. package/dist/Selector/Selector.js +28 -6
  101. package/dist/SideNav/SideNav.d.ts.map +1 -1
  102. package/dist/SideNav/SideNav.js +10 -2
  103. package/dist/Slider/Slider.d.ts.map +1 -1
  104. package/dist/Slider/Slider.js +18 -7
  105. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  106. package/dist/Tokenizer/Tokenizer.js +16 -4
  107. package/dist/Toolbar/Toolbar.d.ts +2 -1
  108. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  109. package/dist/Toolbar/Toolbar.js +3 -1
  110. package/dist/TreeList/TreeList.d.ts.map +1 -1
  111. package/dist/TreeList/TreeList.js +6 -3
  112. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  113. package/dist/TreeList/TreeListItem.js +7 -1
  114. package/dist/astryx.css +22 -0
  115. package/dist/hooks/useScrollOverflow.d.ts.map +1 -1
  116. package/dist/hooks/useScrollOverflow.js +55 -12
  117. package/dist/hooks/useTreeFocus.d.ts +21 -0
  118. package/dist/hooks/useTreeFocus.d.ts.map +1 -1
  119. package/dist/hooks/useTreeFocus.js +40 -2
  120. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  121. package/dist/i18n/generated-locales/en.generated.js +3 -0
  122. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  123. package/dist/i18n/generated-locales/pseudo.generated.js +3 -0
  124. package/dist/reset.a11y.chromium.spec.js +6 -1
  125. package/dist/reset.until-found.a11y.browser.spec.d.ts +2 -0
  126. package/dist/reset.until-found.a11y.browser.spec.d.ts.map +1 -0
  127. package/dist/reset.until-found.a11y.browser.spec.js +71 -0
  128. package/dist/theme/syntax/SyntaxTheme.d.ts.map +1 -1
  129. package/dist/theme/syntax/SyntaxTheme.js +43 -2
  130. package/dist/theme/syntax/defineSyntaxTheme.d.ts +18 -5
  131. package/dist/theme/syntax/defineSyntaxTheme.d.ts.map +1 -1
  132. package/dist/theme/syntax/defineSyntaxTheme.js +38 -9
  133. package/dist/theme/syntax/index.d.ts +1 -1
  134. package/dist/theme/syntax/index.d.ts.map +1 -1
  135. package/dist/theme/syntax/tokens.d.ts +12 -2
  136. package/dist/theme/syntax/tokens.d.ts.map +1 -1
  137. package/dist/theme/syntax/tokens.js +25 -4
  138. package/dist/utils/plainDate.d.ts +12 -0
  139. package/dist/utils/plainDate.d.ts.map +1 -1
  140. package/dist/utils/plainDate.js +63 -10
  141. package/locales/en.json +13 -1
  142. package/locales/pseudo.json +9 -0
  143. package/package.json +4 -4
  144. package/src/AlertDialog/AlertDialog.doc.mjs +6 -0
  145. package/src/AlertDialog/AlertDialog.spec.md +205 -0
  146. package/src/AlertDialog/AlertDialog.test.tsx +3 -14
  147. package/src/AlertDialog/__tests__/AlertDialog.a11y.chromium.spec.ts +115 -0
  148. package/src/AlertDialog/__tests__/AlertDialog.a11y.renders.tsx +52 -0
  149. package/src/AlertDialog/__tests__/AlertDialog.a11y.states.ts +68 -0
  150. package/src/AlertDialog/__tests__/AlertDialog.a11y.test.tsx +144 -0
  151. package/src/Button/Button.test.tsx +37 -0
  152. package/src/Button/Button.tsx +5 -1
  153. package/src/Calendar/Calendar.doc.mjs +234 -60
  154. package/src/Calendar/Calendar.spec.md +209 -0
  155. package/src/Calendar/Calendar.test.tsx +559 -20
  156. package/src/Calendar/Calendar.tsx +258 -41
  157. package/src/Calendar/__tests__/Calendar.a11y.chromium.spec.ts +126 -0
  158. package/src/Calendar/__tests__/Calendar.a11y.known-failures.ts +12 -0
  159. package/src/Calendar/__tests__/Calendar.a11y.renders.tsx +57 -0
  160. package/src/Calendar/__tests__/Calendar.a11y.states.ts +139 -0
  161. package/src/Calendar/__tests__/Calendar.a11y.test.tsx +137 -0
  162. package/src/Carousel/Carousel.test.tsx +54 -71
  163. package/src/Carousel/__tests__/Carousel.a11y.chromium.spec.ts +192 -0
  164. package/src/Carousel/__tests__/Carousel.a11y.known-failures.ts +27 -0
  165. package/src/Carousel/__tests__/Carousel.a11y.renders.tsx +39 -0
  166. package/src/Carousel/__tests__/Carousel.a11y.states.ts +139 -0
  167. package/src/Carousel/__tests__/Carousel.a11y.test.tsx +127 -0
  168. package/src/Chat/ChatComposerInput.doc.mjs +1 -1
  169. package/src/Chat/ChatComposerInput.spec.md +94 -55
  170. package/src/Chat/ChatComposerInput.test.tsx +202 -1
  171. package/src/Chat/ChatComposerInput.tsx +13 -0
  172. package/src/Chat/ChatMessage.doc.mjs +31 -16
  173. package/src/Chat/ChatMessage.spec.md +265 -0
  174. package/src/Chat/ChatMessage.test.tsx +246 -0
  175. package/src/Chat/ChatMessage.tsx +30 -7
  176. package/src/Chat/ChatSystemMessage.spec.md +36 -37
  177. package/src/Chat/ChatSystemMessage.test.tsx +4 -4
  178. package/src/Chat/useTriggerMenu.test.tsx +994 -0
  179. package/src/Chat/useTriggerMenu.tsx +278 -46
  180. package/src/CodeBlock/CodeBlock.doc.mjs +7 -0
  181. package/src/CodeBlock/CodeBlock.spec.md +40 -41
  182. package/src/CodeBlock/CodeBlock.test.tsx +89 -9
  183. package/src/CodeBlock/CodeBlock.tsx +205 -25
  184. package/src/CodeBlock/__tests__/CodeBlockOverflowStop.a11y.chromium.spec.ts +125 -0
  185. package/src/CodeBlock/__tests__/DiffRowPaint.a11y.chromium.spec.ts +93 -0
  186. package/src/CodeBlock/tokenizer.test.ts +112 -0
  187. package/src/CodeBlock/tokenizer.ts +106 -0
  188. package/src/Collapsible/CollapsibleGroup.spec.md +17 -8
  189. package/src/Collapsible/__tests__/CollapsibleGroup.a11y.renders.tsx +45 -0
  190. package/src/Collapsible/__tests__/CollapsibleGroup.a11y.states.ts +139 -0
  191. package/src/Collapsible/__tests__/CollapsibleGroup.a11y.test.tsx +135 -0
  192. package/src/Collapsible/__tests__/CollapsibleGroupAccordion.a11y.chromium.spec.ts +128 -0
  193. package/src/CommandPalette/CommandPalette.doc.mjs +126 -23
  194. package/src/CommandPalette/CommandPalette.spec.md +100 -49
  195. package/src/CommandPalette/CommandPalette.test.tsx +241 -2
  196. package/src/CommandPalette/CommandPalette.tsx +47 -21
  197. package/src/CommandPalette/CommandPaletteEmpty.tsx +4 -3
  198. package/src/CommandPalette/CommandPaletteInput.doc.mjs +61 -4
  199. package/src/CommandPalette/CommandPaletteInput.spec.md +215 -0
  200. package/src/CommandPalette/CommandPaletteInput.test.tsx +375 -51
  201. package/src/CommandPalette/CommandPaletteInput.tsx +74 -21
  202. package/src/CommandPalette/CommandPaletteItem.doc.mjs +73 -22
  203. package/src/CommandPalette/CommandPaletteItem.spec.md +223 -0
  204. package/src/CommandPalette/CommandPaletteItem.test.tsx +200 -5
  205. package/src/CommandPalette/CommandPaletteItem.tsx +16 -11
  206. package/src/CommandPalette/CommandPaletteList.doc.mjs +3 -3
  207. package/src/CommandPalette/CommandPaletteList.spec.md +41 -36
  208. package/src/CommandPalette/CommandPaletteList.test.tsx +80 -0
  209. package/src/CommandPalette/CommandPaletteList.tsx +10 -5
  210. package/src/CommandPalette/__tests__/CommandPalette.a11y.chromium.spec.ts +599 -0
  211. package/src/ComplexSelector/ComplexSelector.doc.mjs +10 -8
  212. package/src/ComplexSelector/ComplexSelector.spec.md +14 -9
  213. package/src/ComplexSelector/ComplexSelector.test.tsx +64 -16
  214. package/src/ComplexSelector/ComplexSelector.tsx +37 -8
  215. package/src/ContextMenu/ContextMenu.doc.mjs +6 -0
  216. package/src/ContextMenu/ContextMenu.spec.md +51 -53
  217. package/src/ContextMenu/ContextMenu.test.tsx +132 -1
  218. package/src/ContextMenu/ContextMenu.tsx +78 -48
  219. package/src/DateInput/DateInput.doc.mjs +52 -0
  220. package/src/DateInput/DateInput.spec.md +27 -15
  221. package/src/DateInput/DateInput.test.tsx +38 -0
  222. package/src/DateInput/DateInput.tsx +1 -1
  223. package/src/DateInput/DateInputTouch.test.tsx +22 -0
  224. package/src/DateInput/MonthScroller.tsx +12 -4
  225. package/src/DateRangeInput/DateRangeInput.test.tsx +49 -0
  226. package/src/DateRangeInput/DateRangeInput.tsx +20 -2
  227. package/src/Dialog/Dialog.test.tsx +27 -1
  228. package/src/Dialog/Dialog.tsx +16 -5
  229. package/src/Dialog/DialogHeader.test.tsx +27 -2
  230. package/src/Dialog/DialogHeader.tsx +7 -8
  231. package/src/Dialog/__tests__/Dialog.a11y.chromium.spec.ts +21 -14
  232. package/src/Divider/Divider.doc.mjs +5 -3
  233. package/src/Divider/Divider.spec.md +140 -54
  234. package/src/Divider/Divider.test.tsx +15 -0
  235. package/src/Divider/Divider.tsx +6 -4
  236. package/src/DropdownMenu/DropdownMenu.doc.mjs +2 -2
  237. package/src/DropdownMenu/DropdownMenu.spec.md +121 -45
  238. package/src/DropdownMenu/DropdownMenu.test.tsx +288 -23
  239. package/src/DropdownMenu/DropdownMenu.tsx +212 -81
  240. package/src/DropdownMenu/DropdownMenuCheckboxItem.doc.mjs +38 -2
  241. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +32 -18
  242. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +146 -1
  243. package/src/DropdownMenu/__tests__/MenuButton.a11y.chromium.spec.ts +136 -0
  244. package/src/DropdownMenu/__tests__/MenuButton.a11y.known-failures.ts +12 -0
  245. package/src/DropdownMenu/__tests__/MenuButton.a11y.renders.tsx +67 -0
  246. package/src/DropdownMenu/__tests__/MenuButton.a11y.states.ts +141 -0
  247. package/src/DropdownMenu/__tests__/MenuButton.a11y.test.tsx +138 -0
  248. package/src/DropdownMenu/__tests__/MenuHeaderFooter.a11y.chromium.spec.ts +40 -5
  249. package/src/Field/InputClearButton.test.tsx +29 -4
  250. package/src/Field/InputClearButton.tsx +26 -8
  251. package/src/FieldStatus/__tests__/StatusMessage.a11y.chromium.spec.ts +21 -14
  252. package/src/FormLayout/FormLayout.tsx +1 -0
  253. package/src/FormLayout/__snapshots__/FormLayout.test.tsx.snap +1 -1
  254. package/src/Grid/Grid.doc.mjs +14 -11
  255. package/src/Grid/Grid.spec.md +50 -32
  256. package/src/Grid/Grid.tsx +13 -4
  257. package/src/HoverCard/HoverCard.test.tsx +46 -1
  258. package/src/HoverCard/useHoverCard.tsx +68 -48
  259. package/src/InputGroup/InputGroup.doc.mjs +11 -1
  260. package/src/InputGroup/InputGroup.tsx +2 -4
  261. package/src/InputGroup/groupStyles.ts +40 -2
  262. package/src/Layer/useLayerDismissal.ts +9 -9
  263. package/src/Link/__tests__/Link.a11y.chromium.spec.ts +142 -0
  264. package/src/Link/__tests__/Link.a11y.renders.tsx +97 -0
  265. package/src/Link/__tests__/Link.a11y.states.ts +104 -0
  266. package/src/Link/__tests__/Link.a11y.test.tsx +127 -0
  267. package/src/Markdown/Markdown.doc.mjs +1 -1
  268. package/src/Markdown/Markdown.fr23.perf.test.ts +161 -40
  269. package/src/Markdown/Markdown.spec.md +45 -23
  270. package/src/Markdown/Markdown.test.tsx +38 -0
  271. package/src/Markdown/Markdown.tsx +24 -2
  272. package/src/Markdown/plugins/semanticFence.test.tsx +3 -2
  273. package/src/MobileNav/MobileNav.tsx +50 -2
  274. package/src/MobileNav/MobileNavFocus.test.tsx +553 -0
  275. package/src/MoreMenu/MoreMenu.spec.md +18 -7
  276. package/src/MoreMenu/MoreMenu.test.tsx +38 -0
  277. package/src/MultiSelector/MultiSelector.doc.mjs +8 -8
  278. package/src/MultiSelector/MultiSelector.spec.md +1 -1
  279. package/src/MultiSelector/MultiSelector.test.tsx +188 -21
  280. package/src/MultiSelector/MultiSelector.tsx +50 -11
  281. package/src/NumberInput/NumberInput.test.tsx +24 -0
  282. package/src/NumberInput/NumberInput.tsx +1 -1
  283. package/src/NumberInput/__tests__/NumberInput.a11y.chromium.spec.ts +22 -15
  284. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +78 -0
  285. package/src/PowerSearch/PowerSearchEditPopover.tsx +6 -0
  286. package/src/Resizable/Resizable.spec.md +41 -9
  287. package/src/Resizable/ResizeHandle.test.tsx +565 -2
  288. package/src/Resizable/ResizeHandle.tsx +126 -21
  289. package/src/Resizable/__tests__/ResizeHandle.a11y.chromium.spec.ts +119 -0
  290. package/src/Resizable/__tests__/ResizeHandle.a11y.known-failures.ts +39 -0
  291. package/src/Resizable/__tests__/ResizeHandle.a11y.renders.tsx +80 -0
  292. package/src/Resizable/__tests__/ResizeHandle.a11y.states.ts +197 -0
  293. package/src/Resizable/__tests__/ResizeHandle.a11y.test.tsx +123 -0
  294. package/src/Resizable/useResizable.ts +95 -0
  295. package/src/Selector/Selector.doc.mjs +7 -6
  296. package/src/Selector/Selector.spec.md +37 -24
  297. package/src/Selector/Selector.test.tsx +176 -10
  298. package/src/Selector/Selector.tsx +59 -14
  299. package/src/Selector/__tests__/Combobox.a11y.known-failures.ts +27 -0
  300. package/src/Selector/__tests__/Combobox.a11y.states.ts +145 -0
  301. package/src/Selector/__tests__/Selector.combobox.a11y.chromium.spec.ts +108 -0
  302. package/src/Selector/__tests__/Selector.combobox.a11y.test.tsx +126 -0
  303. package/src/SideNav/SideNav.test.tsx +35 -0
  304. package/src/SideNav/SideNav.tsx +16 -2
  305. package/src/Slider/Slider.spec.md +43 -17
  306. package/src/Slider/Slider.test.tsx +134 -0
  307. package/src/Slider/Slider.tsx +29 -6
  308. package/src/Slider/__tests__/Slider.a11y.chromium.spec.ts +112 -0
  309. package/src/Slider/__tests__/Slider.a11y.known-failures.ts +12 -0
  310. package/src/Slider/__tests__/Slider.a11y.renders.tsx +42 -0
  311. package/src/Slider/__tests__/Slider.a11y.states.ts +106 -0
  312. package/src/Slider/__tests__/Slider.a11y.test.tsx +105 -0
  313. package/src/Switch/__tests__/Switch.a11y.chromium.spec.ts +25 -18
  314. package/src/TabList/__tests__/Tabs.a11y.chromium.spec.ts +19 -12
  315. package/src/TextInput/__tests__/TextInput.a11y.chromium.spec.ts +21 -14
  316. package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +18 -11
  317. package/src/Tokenizer/Tokenizer.test.tsx +87 -0
  318. package/src/Tokenizer/Tokenizer.tsx +31 -3
  319. package/src/Toolbar/Toolbar.test.tsx +131 -4
  320. package/src/Toolbar/Toolbar.tsx +3 -1
  321. package/src/TreeList/TreeList.spec.md +33 -8
  322. package/src/TreeList/TreeList.test.tsx +72 -0
  323. package/src/TreeList/TreeList.tsx +10 -6
  324. package/src/TreeList/TreeListItem.tsx +5 -0
  325. package/src/TreeList/__tests__/TreeList.a11y.chromium.spec.ts +125 -0
  326. package/src/TreeList/__tests__/TreeList.a11y.renders.tsx +44 -0
  327. package/src/TreeList/__tests__/TreeList.a11y.states.ts +164 -0
  328. package/src/TreeList/__tests__/TreeList.a11y.test.tsx +138 -0
  329. package/src/TreeList/__tests__/TreeListRowNames.a11y.chromium.spec.ts +82 -0
  330. package/src/hooks/useScrollOverflow.test.tsx +222 -0
  331. package/src/hooks/useScrollOverflow.ts +55 -13
  332. package/src/hooks/useTreeFocus.test.tsx +37 -0
  333. package/src/hooks/useTreeFocus.ts +55 -2
  334. package/src/i18n/generated-locales/en.generated.ts +3 -0
  335. package/src/i18n/generated-locales/pseudo.generated.ts +3 -0
  336. package/src/reset.a11y.chromium.spec.ts +9 -1
  337. package/src/reset.css +8 -3
  338. package/src/reset.until-found.a11y.browser.spec.ts +75 -0
  339. package/src/reset.until-found.test.ts +32 -0
  340. package/src/stylexValidation.source-build.test.mjs +42 -0
  341. package/src/theme/syntax/SyntaxTheme.tsx +46 -5
  342. package/src/theme/syntax/defineSyntaxTheme.diff.test.ts +91 -0
  343. package/src/theme/syntax/defineSyntaxTheme.ts +75 -15
  344. package/src/theme/syntax/index.ts +5 -1
  345. package/src/theme/syntax/tokens.ts +32 -2
  346. package/src/theme/syntax/useSyntaxTheme.diff.test.tsx +40 -0
  347. package/src/utils/plainDate.test.ts +147 -0
  348. package/src/utils/plainDate.ts +75 -10
@@ -15,7 +15,7 @@ import {render, screen, fireEvent, waitFor, act} from '@testing-library/react';
15
15
  import userEvent from '@testing-library/user-event';
16
16
  import {useState} from 'react';
17
17
  import * as stylex from '@stylexjs/stylex';
18
- import {DropdownMenu} from './DropdownMenu';
18
+ import {DropdownMenu, type DropdownMenuTriggerProps} from './DropdownMenu';
19
19
  import {DropdownMenuItem} from './DropdownMenuItem';
20
20
  import {DropdownMenuDivider} from './DropdownMenuDivider';
21
21
  import {DropdownMenuGroup} from './DropdownMenuGroup';
@@ -2861,6 +2861,55 @@ describe('DropdownMenu custom trigger', () => {
2861
2861
  );
2862
2862
  });
2863
2863
 
2864
+ it('a disabled custom trigger never opens the bottom sheet', async () => {
2865
+ const user = userEvent.setup();
2866
+ render(
2867
+ <DropdownMenu
2868
+ items={[{label: 'Edit'}, {label: 'Delete'}]}
2869
+ presentation="bottom-sheet"
2870
+ renderTrigger={({ref, ...props}) => (
2871
+ <button type="button" ref={ref} aria-disabled="true" {...props}>
2872
+ Project actions
2873
+ </button>
2874
+ )}
2875
+ />,
2876
+ );
2877
+ const trigger = screen.getByRole('button', {name: 'Project actions'});
2878
+ await user.click(trigger);
2879
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
2880
+ trigger.focus();
2881
+ await user.keyboard('{Enter}');
2882
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
2883
+ expect(
2884
+ screen.queryByRole('dialog', {name: 'Project actions'}),
2885
+ ).not.toBeInTheDocument();
2886
+ });
2887
+
2888
+ it('a disabled custom trigger never opens the menu', async () => {
2889
+ const user = userEvent.setup();
2890
+ render(
2891
+ <DropdownMenu
2892
+ items={[{label: 'Edit'}, {label: 'Delete'}]}
2893
+ renderTrigger={({ref, ...props}) => (
2894
+ <button type="button" ref={ref} aria-disabled="true" {...props}>
2895
+ Row actions
2896
+ </button>
2897
+ )}
2898
+ />,
2899
+ );
2900
+ const trigger = screen.getByRole('button', {name: 'Row actions'});
2901
+ trigger.focus();
2902
+ await user.keyboard('{ArrowDown}');
2903
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
2904
+ await user.pointer([
2905
+ {keys: '[MouseLeft>]', target: trigger},
2906
+ {keys: '[/MouseLeft]'},
2907
+ ]);
2908
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
2909
+ await user.click(trigger);
2910
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
2911
+ });
2912
+
2864
2913
  it('a custom trigger opens from the keyboard and lands on the first row', async () => {
2865
2914
  const user = userEvent.setup();
2866
2915
  render(
@@ -3609,11 +3658,12 @@ describe('DropdownMenu header and footer', () => {
3609
3658
  </button>,
3610
3659
  ],
3611
3660
  ])(
3612
- 'treats a %s footer control as unreachable: Tab from the menu closes it',
3661
+ 'treats a %s footer control as unreachable: Tab from the menu stays on the menu',
3613
3662
  async (_label, control) => {
3614
3663
  const {menu, hide} = await openHidden(null, control);
3615
3664
  fireEvent.keyDown(menu, {key: 'Tab'});
3616
- expect(hide).toHaveBeenCalled();
3665
+ expect(menu).toHaveFocus();
3666
+ expect(hide).not.toHaveBeenCalled();
3617
3667
  },
3618
3668
  );
3619
3669
 
@@ -3625,10 +3675,11 @@ describe('DropdownMenu header and footer', () => {
3625
3675
  </div>,
3626
3676
  );
3627
3677
  fireEvent.keyDown(menu, {key: 'Tab'});
3628
- expect(hide).toHaveBeenCalled();
3678
+ expect(menu).toHaveFocus();
3679
+ expect(hide).not.toHaveBeenCalled();
3629
3680
  });
3630
3681
 
3631
- it('treats a header control inside a display: none wrapper as unreachable: Shift+Tab from a row closes', async () => {
3682
+ it('treats a header control inside a display: none wrapper as unreachable: Shift+Tab from a row stays', async () => {
3632
3683
  const {menu, hide} = await openHidden(
3633
3684
  <div style={{display: 'none'}}>
3634
3685
  <input aria-label="Filter labels" />
@@ -3639,10 +3690,11 @@ describe('DropdownMenu header and footer', () => {
3639
3690
  const row = screen.getByRole('menuitem', {name: 'Bug', hidden: true});
3640
3691
  await waitFor(() => expect(row).toHaveFocus());
3641
3692
  fireEvent.keyDown(row, {key: 'Tab', shiftKey: true});
3642
- expect(hide).toHaveBeenCalled();
3693
+ expect(row).toHaveFocus();
3694
+ expect(hide).not.toHaveBeenCalled();
3643
3695
  });
3644
3696
 
3645
- it('Tab past the last visible footer control closes, ignoring a wrapped hidden one after it', async () => {
3697
+ it('Tab past the last visible footer control wraps to the menu, ignoring a wrapped hidden one after it', async () => {
3646
3698
  const {hide} = await openHidden(
3647
3699
  null,
3648
3700
  <>
@@ -3658,10 +3710,13 @@ describe('DropdownMenu header and footer', () => {
3658
3710
  });
3659
3711
  act(() => create.focus());
3660
3712
  fireEvent.keyDown(create, {key: 'Tab'});
3661
- expect(hide).toHaveBeenCalled();
3713
+ expect(
3714
+ screen.getByRole('menu', {name: 'Labels', hidden: true}),
3715
+ ).toHaveFocus();
3716
+ expect(hide).not.toHaveBeenCalled();
3662
3717
  });
3663
3718
 
3664
- it('Tab from an open sub-menu closes the menu when the footer control is in a hidden wrapper', async () => {
3719
+ it('Tab from an open sub-menu returns to the menu when the footer control is in a hidden wrapper', async () => {
3665
3720
  const {user, menu} = await openHidden(
3666
3721
  null,
3667
3722
  <div style={{display: 'none'}}>
@@ -3681,11 +3736,11 @@ describe('DropdownMenu header and footer', () => {
3681
3736
  await waitFor(() => expect(folder).toHaveFocus());
3682
3737
  fireEvent.keyDown(folder, {key: 'Tab'});
3683
3738
  expect(subTrigger).toHaveAttribute('aria-expanded', 'false');
3684
- // The flyout's own close also hides a popover, so check the root's.
3685
- expect(menu.closest('[popover]')).not.toHaveAttribute('popover-open');
3739
+ expect(menu).toHaveFocus();
3740
+ expect(menu.closest('[popover]')).toHaveAttribute('popover-open');
3686
3741
  });
3687
3742
 
3688
- it('treats a hidden header control as unreachable: Shift+Tab from a row closes the menu', async () => {
3743
+ it('treats a hidden header control as unreachable: Shift+Tab from a row stays on the row', async () => {
3689
3744
  const {menu, hide} = await openHidden(
3690
3745
  <input aria-label="Filter labels" hidden />,
3691
3746
  null,
@@ -3694,10 +3749,11 @@ describe('DropdownMenu header and footer', () => {
3694
3749
  const row = screen.getByRole('menuitem', {name: 'Bug', hidden: true});
3695
3750
  await waitFor(() => expect(row).toHaveFocus());
3696
3751
  fireEvent.keyDown(row, {key: 'Tab', shiftKey: true});
3697
- expect(hide).toHaveBeenCalled();
3752
+ expect(row).toHaveFocus();
3753
+ expect(hide).not.toHaveBeenCalled();
3698
3754
  });
3699
3755
 
3700
- it('Tab past the last visible footer control closes, ignoring a hidden one after it', async () => {
3756
+ it('Tab past the last visible footer control wraps to the menu, ignoring a hidden one after it', async () => {
3701
3757
  const {hide} = await openHidden(
3702
3758
  null,
3703
3759
  <>
@@ -3713,10 +3769,13 @@ describe('DropdownMenu header and footer', () => {
3713
3769
  });
3714
3770
  act(() => create.focus());
3715
3771
  fireEvent.keyDown(create, {key: 'Tab'});
3716
- expect(hide).toHaveBeenCalled();
3772
+ expect(
3773
+ screen.getByRole('menu', {name: 'Labels', hidden: true}),
3774
+ ).toHaveFocus();
3775
+ expect(hide).not.toHaveBeenCalled();
3717
3776
  });
3718
3777
 
3719
- it('Tab from an open sub-menu closes the menu when the footer holds only hidden controls', async () => {
3778
+ it('Tab from an open sub-menu returns to the menu when the footer holds only hidden controls', async () => {
3720
3779
  const {user, menu} = await openHidden(
3721
3780
  null,
3722
3781
  <button type="button" hidden>
@@ -3736,8 +3795,8 @@ describe('DropdownMenu header and footer', () => {
3736
3795
  await waitFor(() => expect(folder).toHaveFocus());
3737
3796
  fireEvent.keyDown(folder, {key: 'Tab'});
3738
3797
  expect(subTrigger).toHaveAttribute('aria-expanded', 'false');
3739
- // The flyout's own close also hides a popover, so check the root's.
3740
- expect(menu.closest('[popover]')).not.toHaveAttribute('popover-open');
3798
+ expect(menu).toHaveFocus();
3799
+ expect(menu.closest('[popover]')).toHaveAttribute('popover-open');
3741
3800
  });
3742
3801
  });
3743
3802
 
@@ -3797,7 +3856,7 @@ describe('DropdownMenu header and footer', () => {
3797
3856
  });
3798
3857
  });
3799
3858
 
3800
- it('Tab past the last footer control closes the menu', async () => {
3859
+ it('Tab past the last footer control wraps to the first header control', async () => {
3801
3860
  await open();
3802
3861
  const hide = vi.mocked(HTMLElement.prototype.hidePopover);
3803
3862
  hide.mockClear();
@@ -3811,11 +3870,13 @@ describe('DropdownMenu header and footer', () => {
3811
3870
  create.focus();
3812
3871
  expect(create).toHaveFocus();
3813
3872
  fireEvent.keyDown(create, {key: 'Tab'});
3814
- expect(hide).toHaveBeenCalled();
3815
- expect(screen.getByRole('button', {name: /Labels/})).toHaveFocus();
3873
+ expect(
3874
+ screen.getByRole('textbox', {name: 'Filter labels', hidden: true}),
3875
+ ).toHaveFocus();
3876
+ expect(hide).not.toHaveBeenCalled();
3816
3877
  });
3817
3878
 
3818
- it('Shift+Tab before the first header control closes the menu', async () => {
3879
+ it('Shift+Tab before the first header control wraps to the last footer control', async () => {
3819
3880
  await open();
3820
3881
  const hide = vi.mocked(HTMLElement.prototype.hidePopover);
3821
3882
  hide.mockClear();
@@ -3829,7 +3890,211 @@ describe('DropdownMenu header and footer', () => {
3829
3890
  field.focus();
3830
3891
  expect(field).toHaveFocus();
3831
3892
  fireEvent.keyDown(field, {key: 'Tab', shiftKey: true});
3832
- expect(hide).toHaveBeenCalled();
3893
+ expect(
3894
+ screen.getByRole('button', {name: 'Create label', hidden: true}),
3895
+ ).toHaveFocus();
3896
+ expect(hide).not.toHaveBeenCalled();
3897
+ });
3898
+
3899
+ it('is a modal dialog named like the menu, holding the menu, and the trigger points at it', async () => {
3900
+ const {menu} = await open();
3901
+ const dialog = screen.getByRole('dialog', {name: 'Labels', hidden: true});
3902
+ expect(dialog).toHaveAttribute('aria-modal', 'true');
3903
+ expect(dialog).toContainElement(menu);
3904
+ expect(dialog).toContainElement(
3905
+ screen.getByRole('textbox', {name: 'Filter labels', hidden: true}),
3906
+ );
3907
+ const trigger = screen.getByRole('button', {name: /Labels/});
3908
+ expect(trigger).toHaveAttribute('aria-haspopup', 'dialog');
3909
+ const controlled = document.getElementById(
3910
+ trigger.getAttribute('aria-controls') ?? '',
3911
+ );
3912
+ expect(controlled).not.toBeNull();
3913
+ expect(controlled).toContainElement(dialog);
3914
+ expect(controlled).not.toContainElement(trigger);
3915
+ });
3916
+
3917
+ it('a custom trigger with a header names the dialog and logs no unnamed-dialog warning', async () => {
3918
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
3919
+ try {
3920
+ const user = userEvent.setup();
3921
+ render(
3922
+ <DropdownMenu
3923
+ header={<input aria-label="Filter labels" />}
3924
+ renderTrigger={props => (
3925
+ <span {...props} role="button" tabIndex={0}>
3926
+ Labels
3927
+ </span>
3928
+ )}>
3929
+ <DropdownMenuItem label="Bug" />
3930
+ </DropdownMenu>,
3931
+ );
3932
+ const trigger = screen.getByRole('button', {name: 'Labels'});
3933
+ expect(trigger).toHaveAttribute('aria-haspopup', 'dialog');
3934
+ await user.click(trigger);
3935
+ expect(
3936
+ screen.getByRole('dialog', {name: 'Labels', hidden: true}),
3937
+ ).toHaveAttribute('aria-modal', 'true');
3938
+ expect(
3939
+ warn.mock.calls.some(args =>
3940
+ String(args[0]).includes('unnamed dialog'),
3941
+ ),
3942
+ ).toBe(false);
3943
+ } finally {
3944
+ warn.mockRestore();
3945
+ }
3946
+ });
3947
+
3948
+ it('an icon-only custom trigger with a header still gets a named dialog', async () => {
3949
+ const user = userEvent.setup();
3950
+ render(
3951
+ <DropdownMenu
3952
+ header={<input aria-label="Filter labels" />}
3953
+ renderTrigger={props => (
3954
+ <button {...props} type="button">
3955
+ <img alt="Labels" src="" />
3956
+ </button>
3957
+ )}>
3958
+ <DropdownMenuItem label="Bug" />
3959
+ </DropdownMenu>,
3960
+ );
3961
+ const trigger = screen.getByRole('button', {name: 'Labels'});
3962
+ expect(trigger).toHaveAttribute('aria-haspopup', 'dialog');
3963
+ await user.click(trigger);
3964
+ expect(
3965
+ screen.getByRole('dialog', {name: 'Labels', hidden: true}),
3966
+ ).toHaveAttribute('aria-modal', 'true');
3967
+ });
3968
+
3969
+ it.each([
3970
+ [
3971
+ 'its own <label>',
3972
+ (props: DropdownMenuTriggerProps) => (
3973
+ <>
3974
+ <label htmlFor={props.id}>Labels</label>
3975
+ <button {...props} type="button" />
3976
+ </>
3977
+ ),
3978
+ ],
3979
+ [
3980
+ "an input button's value",
3981
+ (props: DropdownMenuTriggerProps) => (
3982
+ <input {...props} type="button" value="Labels" />
3983
+ ),
3984
+ ],
3985
+ ])(
3986
+ 'a custom trigger named by %s names the dialog like the menu',
3987
+ async (_label, renderTrigger) => {
3988
+ const user = userEvent.setup();
3989
+ render(
3990
+ <DropdownMenu
3991
+ header={<input aria-label="Filter labels" />}
3992
+ renderTrigger={renderTrigger}>
3993
+ <DropdownMenuItem label="Bug" />
3994
+ </DropdownMenu>,
3995
+ );
3996
+ await user.click(screen.getByRole('button', {name: 'Labels'}));
3997
+ expect(
3998
+ screen.getByRole('dialog', {name: 'Labels', hidden: true}),
3999
+ ).toBeInTheDocument();
4000
+ expect(
4001
+ screen.getByRole('menu', {name: 'Labels', hidden: true}),
4002
+ ).toBeInTheDocument();
4003
+ },
4004
+ );
4005
+
4006
+ it('a custom trigger with no readable name still gets a dialog, named by the default label', async () => {
4007
+ const user = userEvent.setup();
4008
+ render(
4009
+ <DropdownMenu
4010
+ footer={<button type="button">Create label</button>}
4011
+ renderTrigger={props => (
4012
+ <button {...props} type="button" data-testid="bare" />
4013
+ )}>
4014
+ <DropdownMenuItem label="Bug" />
4015
+ </DropdownMenu>,
4016
+ );
4017
+ await user.click(screen.getByTestId('bare'));
4018
+ const dialog = screen.getByRole('dialog', {hidden: true});
4019
+ expect(dialog).toHaveAttribute('aria-modal', 'true');
4020
+ expect(dialog.getAttribute('aria-label')?.trim()).not.toBe('');
4021
+ });
4022
+
4023
+ describe('with a radio group in the header', () => {
4024
+ async function openWithRadios(checked: string | null) {
4025
+ const user = userEvent.setup();
4026
+ render(
4027
+ <DropdownMenu
4028
+ button={{label: 'Labels'}}
4029
+ header={
4030
+ <fieldset>
4031
+ {['Name', 'Date', 'Size'].map(option => (
4032
+ <input
4033
+ key={option}
4034
+ type="radio"
4035
+ name="sort"
4036
+ aria-label={option}
4037
+ defaultChecked={option === checked}
4038
+ />
4039
+ ))}
4040
+ </fieldset>
4041
+ }
4042
+ footer={<button type="button">Create label</button>}>
4043
+ <DropdownMenuItem label="Bug" />
4044
+ </DropdownMenu>,
4045
+ );
4046
+ await user.click(screen.getByRole('button', {name: /Labels/}));
4047
+ const menu = screen.getByRole('menu', {name: 'Labels', hidden: true});
4048
+ await waitFor(() => expect(menu).toHaveFocus());
4049
+ const radio = (name: string) =>
4050
+ screen.getByRole('radio', {name, hidden: true});
4051
+ const create = screen.getByRole('button', {
4052
+ name: 'Create label',
4053
+ hidden: true,
4054
+ });
4055
+ return {menu, radio, create};
4056
+ }
4057
+
4058
+ it('Shift+Tab from the menu lands on the checked radio, and Tab leaves the group in one step', async () => {
4059
+ const {menu, radio} = await openWithRadios('Date');
4060
+ fireEvent.keyDown(menu, {key: 'Tab', shiftKey: true});
4061
+ expect(radio('Date')).toHaveFocus();
4062
+ fireEvent.keyDown(radio('Date'), {key: 'Tab'});
4063
+ expect(menu).toHaveFocus();
4064
+ });
4065
+
4066
+ it('Tab wrapping past the footer lands on the checked radio', async () => {
4067
+ const {radio, create} = await openWithRadios('Size');
4068
+ act(() => create.focus());
4069
+ fireEvent.keyDown(create, {key: 'Tab'});
4070
+ expect(radio('Size')).toHaveFocus();
4071
+ });
4072
+
4073
+ it('with none checked, the group is entered at its first radio forward and its last backward', async () => {
4074
+ const {menu, radio, create} = await openWithRadios(null);
4075
+ fireEvent.keyDown(menu, {key: 'Tab', shiftKey: true});
4076
+ expect(radio('Size')).toHaveFocus();
4077
+ act(() => create.focus());
4078
+ fireEvent.keyDown(create, {key: 'Tab'});
4079
+ expect(radio('Name')).toHaveFocus();
4080
+ fireEvent.keyDown(radio('Name'), {key: 'Tab', shiftKey: true});
4081
+ expect(create).toHaveFocus();
4082
+ });
4083
+ });
4084
+
4085
+ it('without a header or footer, there is no dialog and the trigger points at the menu', async () => {
4086
+ const user = userEvent.setup();
4087
+ render(
4088
+ <DropdownMenu button={{label: 'Actions'}}>
4089
+ <DropdownMenuItem label="Item 1" />
4090
+ </DropdownMenu>,
4091
+ );
4092
+ const trigger = screen.getByRole('button', {name: /Actions/});
4093
+ await user.click(trigger);
4094
+ const menu = screen.getByRole('menu', {name: 'Actions', hidden: true});
4095
+ expect(screen.queryByRole('dialog', {hidden: true})).toBeNull();
4096
+ expect(trigger).toHaveAttribute('aria-haspopup', 'menu');
4097
+ expect(trigger).toHaveAttribute('aria-controls', menu.id);
3833
4098
  });
3834
4099
 
3835
4100
  it('leaves keys typed in the header to the field', async () => {