@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
@@ -38,6 +38,7 @@ import {Theme} from '../theme/Theme';
38
38
  import {generateThemeCSS} from '../theme/generateThemeRules';
39
39
  import {spacingVars} from '../theme/tokens.stylex';
40
40
  import {selectorPresentationStyles} from './selectorPresentation.stylex';
41
+ import {groupStyles} from '../InputGroup/groupStyles';
41
42
 
42
43
  function generateThemeTestCSS(theme: Parameters<typeof generateThemeCSS>[0]) {
43
44
  const {prose, component} = generateThemeCSS(theme);
@@ -605,7 +606,7 @@ describe('Selector', () => {
605
606
  const inputDropdownClass = screen.getByRole('listbox', h).className;
606
607
  unmount();
607
608
 
608
- render(
609
+ const ghostRender = render(
609
610
  <Selector
610
611
  label="Ghost fruit"
611
612
  options={OPTIONS}
@@ -616,10 +617,24 @@ describe('Selector', () => {
616
617
  );
617
618
  await user.click(screen.getByRole('combobox'));
618
619
  const ghostDropdownClass = screen.getByRole('listbox', h).className;
620
+ ghostRender.unmount();
621
+
622
+ render(
623
+ <Selector
624
+ label="Secondary fruit"
625
+ options={OPTIONS}
626
+ value="Banana"
627
+ variant="secondary"
628
+ onChange={() => {}}
629
+ />,
630
+ );
631
+ await user.click(screen.getByRole('combobox'));
632
+ const secondaryDropdownClass = screen.getByRole('listbox', h).className;
619
633
 
620
634
  // The bordered input gets one extra StyleX rule for its border-width
621
- // correction; the borderless ghost keeps the base menu inset.
635
+ // correction; the borderless secondary/ghost keep the base menu inset.
622
636
  expect(inputDropdownClass).not.toBe(ghostDropdownClass);
637
+ expect(secondaryDropdownClass).toBe(ghostDropdownClass);
623
638
  });
624
639
 
625
640
  it('does not apply selected-item overlay offset when placement is explicit', async () => {
@@ -822,6 +837,58 @@ describe('Selector', () => {
822
837
  expect(onChange).toHaveBeenCalledWith(null);
823
838
  });
824
839
 
840
+ it('returns focus to the trigger when the clear button is activated by keyboard', async () => {
841
+ function Controlled() {
842
+ const [value, setValue] = useState<string | null>('Banana');
843
+ return (
844
+ <Selector
845
+ label="Fruit"
846
+ options={OPTIONS}
847
+ value={value}
848
+ onChange={setValue}
849
+ hasClear
850
+ />
851
+ );
852
+ }
853
+ const user = userEvent.setup();
854
+ render(<Controlled />);
855
+ screen.getByRole('button', {name: 'Clear Fruit'}).focus();
856
+ await user.keyboard('{Enter}');
857
+ expect(
858
+ screen.queryByRole('button', {name: 'Clear Fruit'}),
859
+ ).not.toBeInTheDocument();
860
+ expect(screen.getByRole('combobox')).toHaveFocus();
861
+ });
862
+
863
+ it('leaves focus in the search input when the clear button is clicked with the list open', async () => {
864
+ function Controlled() {
865
+ const [value, setValue] = useState<string | null>('Banana');
866
+ return (
867
+ <Selector
868
+ label="Fruit"
869
+ options={OPTIONS}
870
+ value={value}
871
+ onChange={setValue}
872
+ hasClear
873
+ hasSearch
874
+ />
875
+ );
876
+ }
877
+ const user = userEvent.setup();
878
+ render(<Controlled />);
879
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
880
+ const search = screen.getByRole('combobox', {
881
+ name: 'Search options',
882
+ hidden: true,
883
+ });
884
+ search.focus();
885
+ await user.click(screen.getByRole('button', {name: 'Clear Fruit'}));
886
+ expect(
887
+ screen.queryByRole('button', {name: 'Clear Fruit'}),
888
+ ).not.toBeInTheDocument();
889
+ expect(search).toHaveFocus();
890
+ });
891
+
825
892
  it('clears the value via Delete on the focused trigger', async () => {
826
893
  const user = userEvent.setup();
827
894
  const onChange = vi.fn();
@@ -924,7 +991,10 @@ describe('Selector', () => {
924
991
  expect(screen.getByRole('combobox', h)).toBeInTheDocument();
925
992
  });
926
993
 
927
- it('wires the search input as the combobox with activedescendant (comboboxes-4)', async () => {
994
+ // The search input's expanded state, popup relationship, and active option
995
+ // are bound to the shared Combobox contract (Selector.combobox.a11y.test.tsx,
996
+ // search-active); this keeps only the Selector-specific wiring.
997
+ it('moves combobox semantics to the list-autocomplete search input (comboboxes-4)', async () => {
928
998
  const user = userEvent.setup();
929
999
  render(
930
1000
  <Selector
@@ -941,12 +1011,6 @@ describe('Selector', () => {
941
1011
  await user.click(triggerBtn);
942
1012
  const search = screen.getByRole('combobox', h);
943
1013
  expect(search).toHaveAttribute('aria-autocomplete', 'list');
944
- expect(search).toHaveAttribute('aria-expanded', 'true');
945
- expect(search).toHaveAttribute('aria-controls');
946
- // ArrowDown moves the highlight; the search input reports it via
947
- // aria-activedescendant (previously silent on the trigger).
948
- await user.keyboard('{ArrowDown}');
949
- expect(search).toHaveAttribute('aria-activedescendant');
950
1014
  });
951
1015
 
952
1016
  it('PageDown/PageUp jump the highlight to the last/first filtered option', async () => {
@@ -1285,7 +1349,6 @@ describe('Selector', () => {
1285
1349
  <Selector label="Fruit" options={[]} onChange={() => {}} isLoading />,
1286
1350
  );
1287
1351
  const trigger = screen.getByRole('combobox', {name: 'Fruit'});
1288
- expect(trigger).toHaveAttribute('aria-busy', 'true');
1289
1352
  expect(screen.getByRole('status', {name: 'Loading'})).toBeInTheDocument();
1290
1353
 
1291
1354
  await user.click(trigger);
@@ -2448,6 +2511,67 @@ describe('Selector', () => {
2448
2511
  expect(screen.getByText('#')).toBeInTheDocument();
2449
2512
  });
2450
2513
 
2514
+ it.each(['input', 'secondary'] as const)(
2515
+ 'gives a grouped %s trigger the connected group geometry in every position',
2516
+ variant => {
2517
+ const groupClasses = stylex
2518
+ .props(groupStyles.inGroup)
2519
+ .className!.split(' ');
2520
+ const selector = (label: string) => (
2521
+ <Selector
2522
+ label={label}
2523
+ isLabelHidden
2524
+ variant={variant}
2525
+ options={OPTIONS}
2526
+ />
2527
+ );
2528
+ render(
2529
+ <>
2530
+ <InputGroup label="First">
2531
+ {selector('Channel first')}
2532
+ <InputGroupText>#</InputGroupText>
2533
+ </InputGroup>
2534
+ <InputGroup label="Middle">
2535
+ <InputGroupText>#</InputGroupText>
2536
+ {selector('Channel middle')}
2537
+ <InputGroupText>@</InputGroupText>
2538
+ </InputGroup>
2539
+ <InputGroup label="Last">
2540
+ <InputGroupText>#</InputGroupText>
2541
+ {selector('Channel last')}
2542
+ </InputGroup>
2543
+ </>,
2544
+ );
2545
+
2546
+ // The group's row height, seam radii, and focus stacking all come from
2547
+ // these classes; their :first-child and last-item selectors resolve the
2548
+ // position. A secondary trigger keeps its fill but must not keep its
2549
+ // own standalone edges or width.
2550
+ for (const position of ['first', 'middle', 'last']) {
2551
+ const trigger = screen.getByRole('combobox', {
2552
+ name: new RegExp(`Channel ${position}`),
2553
+ });
2554
+ const box = trigger.closest('.astryx-selector') as HTMLElement;
2555
+ const classes = new Set(box.className.split(' '));
2556
+ expect(groupClasses.every(c => classes.has(c))).toBe(true);
2557
+ }
2558
+ },
2559
+ );
2560
+
2561
+ it('keeps a standalone secondary trigger out of the group geometry', () => {
2562
+ const groupClasses = stylex
2563
+ .props(groupStyles.inGroup)
2564
+ .className!.split(' ');
2565
+ render(
2566
+ <Selector label="Channel" variant="secondary" options={OPTIONS} />,
2567
+ );
2568
+ const box = screen
2569
+ .getByRole('combobox')
2570
+ .closest('.astryx-selector') as HTMLElement;
2571
+ const classes = new Set(box.className.split(' '));
2572
+ expect(groupClasses.some(c => classes.has(c))).toBe(false);
2573
+ });
2574
+
2451
2575
  it('keeps disabled reasons described when grouped', () => {
2452
2576
  render(
2453
2577
  <InputGroup label="Destination">
@@ -3083,6 +3207,48 @@ describe('Selector statusVariant forwarding', () => {
3083
3207
  );
3084
3208
  });
3085
3209
 
3210
+ it('detaches attached status by default for the secondary variant', () => {
3211
+ const {container} = render(
3212
+ <Selector
3213
+ label="Fruit"
3214
+ options={['Apple', 'Banana']}
3215
+ variant="secondary"
3216
+ status={{type: 'error', message: 'Required'}}
3217
+ />,
3218
+ );
3219
+ expect(container.querySelector('.astryx-selector')).toHaveAttribute(
3220
+ 'data-variant',
3221
+ 'secondary',
3222
+ );
3223
+ expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
3224
+ 'data-variant',
3225
+ 'detached',
3226
+ );
3227
+ });
3228
+
3229
+ it('paints the secondary trigger with its own trigger style', () => {
3230
+ const inputRender = render(
3231
+ <Selector label="Fruit" options={['Apple', 'Banana']} value="Banana" />,
3232
+ );
3233
+ const inputTriggerClass =
3234
+ inputRender.container.querySelector('.astryx-selector')!.className;
3235
+ inputRender.unmount();
3236
+
3237
+ const secondaryRender = render(
3238
+ <Selector
3239
+ label="Fruit"
3240
+ options={['Apple', 'Banana']}
3241
+ value="Banana"
3242
+ variant="secondary"
3243
+ />,
3244
+ );
3245
+ // The neutral-filled secondary trigger must not reuse the bordered
3246
+ // input trigger's classes; it carries the secondary trigger style.
3247
+ expect(
3248
+ secondaryRender.container.querySelector('.astryx-selector')!.className,
3249
+ ).not.toBe(inputTriggerClass);
3250
+ });
3251
+
3086
3252
  it('uses a status tooltip for ghost selectors when requested', () => {
3087
3253
  const {container} = render(
3088
3254
  <Selector
@@ -6,7 +6,7 @@
6
6
  * @file Selector.tsx
7
7
  * @input Uses React, StyleX, adaptive selection surfaces, theme-resolved
8
8
  * indicators, Field, and InputGroup context
9
- * @output Exports Selector with content-derived option-mark layout and token-sized single-line triggers
9
+ * @output Exports Selector with input, secondary, and ghost trigger treatments, content-derived option-mark layout, and token-sized single-line triggers
10
10
  * @position Core implementation; consumed by index.ts
11
11
  *
12
12
  * SYNC: When modified, update:
@@ -260,6 +260,37 @@ const styles = stylex.create({
260
260
  '[data-astryx-press="on"]': 'scale(0.98)',
261
261
  },
262
262
  },
263
+ // Secondary is ghost's chrome with the resting fill of a secondary
264
+ // button (`--color-neutral`): no border, no shadow, content width, so a
265
+ // selector in a nav reads as the sibling of the secondary buttons and
266
+ // segmented controls beside it instead of as a form field. The hover and
267
+ // pressed answers still come from the shared interaction overlay, layered
268
+ // over the fill via backgroundImage.
269
+ triggerSecondary: {
270
+ width: 'auto',
271
+ borderWidth: 0,
272
+ lineHeight: `max(${spacingVars['--spacing-5']}, 20px, 1rem)`,
273
+ backgroundColor: colorVars['--color-neutral'],
274
+ boxShadow: {
275
+ default: 'none',
276
+ ':hover:not(:focus-within):where(:not(:disabled,[aria-disabled="true"]))':
277
+ {
278
+ '@media (hover: hover)': 'none',
279
+ },
280
+ ':focus-within': 'none',
281
+ },
282
+ fontWeight: fontWeightVars['--font-weight-medium'],
283
+ transitionProperty:
284
+ 'background-image, background-color, color, opacity, transform',
285
+ transform: {
286
+ default: 'scale(1)',
287
+ ':active': {
288
+ default: 'scale(0.98)',
289
+ '@media (pointer: coarse)': 'scale(1)',
290
+ },
291
+ '[data-astryx-press="on"]': 'scale(0.98)',
292
+ },
293
+ },
263
294
  triggerGhostDisabled: {
264
295
  backgroundImage: 'none',
265
296
  transform: {
@@ -311,7 +342,7 @@ const styles = stylex.create({
311
342
  },
312
343
  dropdownInput: {
313
344
  // The input trigger's text inset includes its border. Mirror that extra
314
- // pixel in the menu; the borderless ghost variant needs no correction.
345
+ // pixel in the menu; the borderless secondary/ghost variants need no correction.
315
346
  paddingInline: `calc(${spacingVars['--spacing-1']} + ${borderVars['--border-width']})`,
316
347
  },
317
348
  // Scroll ownership by the browser's own signal, as the menus declare it: a
@@ -512,7 +543,7 @@ const STATUS_BUTTON_LABEL_KEY: Record<SelectorStatusType, string> = {
512
543
 
513
544
  export type SelectorSize = 'sm' | 'md' | 'lg';
514
545
 
515
- export type SelectorVariant = 'input' | 'ghost';
546
+ export type SelectorVariant = 'input' | 'secondary' | 'ghost';
516
547
 
517
548
  export type SelectorPresentation = AdaptivePresentation;
518
549
 
@@ -655,6 +686,9 @@ interface SelectorPropsBase<
655
686
  /**
656
687
  * Visual style of the selector trigger.
657
688
  * - 'input': bordered input-style trigger for forms
689
+ * - 'secondary': matches the theme's secondary buttons (borderless and
690
+ * neutral-filled by default), for navs and toolbars where the selector
691
+ * sits beside secondary actions
658
692
  * - 'ghost': borderless trigger matching ghost buttons, for toolbars
659
693
  * @default 'input'
660
694
  */
@@ -671,7 +705,7 @@ interface SelectorPropsBase<
671
705
  * - 'attached': message overlaps directly below the bordered input (input variant only)
672
706
  * - 'detached': message floats below as a separate element with spacing
673
707
  * - 'tooltip': message is exposed from the on-field status icon
674
- * @default 'attached' for input selectors; 'detached' for ghost selectors
708
+ * @default 'attached' for input selectors; 'detached' for secondary/ghost selectors
675
709
  */
676
710
  statusVariant?: FieldStatusVariant;
677
711
 
@@ -957,7 +991,7 @@ export function Selector<T extends SelectorOptionType>(
957
991
  const hasClear = hasClearProp === true;
958
992
  const size = useSize(sizeProp, 'md');
959
993
  const effectiveStatusVariant =
960
- variant === 'ghost' && statusVariant === 'attached'
994
+ variant !== 'input' && statusVariant === 'attached'
961
995
  ? 'detached'
962
996
  : statusVariant;
963
997
  const isEffectivelyReadOnly = isReadOnly && !isDisabled;
@@ -1381,6 +1415,14 @@ export function Selector<T extends SelectorOptionType>(
1381
1415
  (e: React.MouseEvent) => {
1382
1416
  e.stopPropagation(); // Don't open dropdown
1383
1417
  clearValue();
1418
+ // A keyboard activation (detail 0) leaves focus on the clear button,
1419
+ // which unmounts once the value is null and would drop focus to <body>;
1420
+ // hand it to the trigger as `hasClear` documents. A pointer press never
1421
+ // focuses the button, so there is no focus to repair, and moving it
1422
+ // would pull it out of an open list's search input.
1423
+ if (e.detail === 0) {
1424
+ triggerRef.current?.focus();
1425
+ }
1384
1426
  },
1385
1427
  [clearValue],
1386
1428
  );
@@ -1408,7 +1450,7 @@ export function Selector<T extends SelectorOptionType>(
1408
1450
  {...themeProps('selector-search')}
1409
1451
  xstyle={
1410
1452
  surface.activePresentation === 'popover' &&
1411
- variant !== 'ghost' &&
1453
+ variant === 'input' &&
1412
1454
  styles.searchRowInput
1413
1455
  }
1414
1456
  // When hasSearch is set, focus moves into this input on open, so it —
@@ -1753,7 +1795,7 @@ export function Selector<T extends SelectorOptionType>(
1753
1795
  ? styles.touchPanYPinchZoom
1754
1796
  : styles.touchPinchZoom,
1755
1797
  surface.activePresentation === 'popover' &&
1756
- variant !== 'ghost' &&
1798
+ variant === 'input' &&
1757
1799
  styles.dropdownInput,
1758
1800
  )}>
1759
1801
  {renderOptions()}
@@ -1791,7 +1833,7 @@ export function Selector<T extends SelectorOptionType>(
1791
1833
  styles.dropdown,
1792
1834
  listboxHasOverflow ? styles.touchPanYPinchZoom : styles.touchPinchZoom,
1793
1835
  surface.activePresentation === 'popover' &&
1794
- variant !== 'ghost' &&
1836
+ variant === 'input' &&
1795
1837
  styles.dropdownInput,
1796
1838
  surface.activePresentation === 'popover' &&
1797
1839
  !isPositioned &&
@@ -1884,24 +1926,27 @@ export function Selector<T extends SelectorOptionType>(
1884
1926
  styles.triggerContainer,
1885
1927
  sizeStyles[size],
1886
1928
  variant === 'ghost' && styles.triggerGhost,
1887
- variant === 'ghost' && interactionOverlayStyles.backgroundImage,
1888
- variant === 'ghost' && focusOutlineStyles.focusWithin,
1929
+ variant === 'secondary' && styles.triggerSecondary,
1930
+ variant !== 'input' && interactionOverlayStyles.backgroundImage,
1931
+ variant !== 'input' && focusOutlineStyles.focusWithin,
1889
1932
  surface.isTriggerFocusRingSuppressed &&
1890
1933
  selectorPresentationStyles.pointerRestoredFocus,
1891
1934
  isDisabled && inputWrapperStyles.disabled,
1892
1935
  isEffectivelyReadOnly && styles.triggerReadOnly,
1893
- variant === 'ghost' && isDisabled && styles.triggerGhostDisabled,
1894
- variant === 'ghost' &&
1936
+ variant !== 'input' && isDisabled && styles.triggerGhostDisabled,
1937
+ variant !== 'input' &&
1895
1938
  isEffectivelyReadOnly &&
1896
1939
  styles.triggerGhostReadOnly,
1897
1940
  !selectedItem && styles.triggerPlaceholder,
1898
- variant !== 'ghost' &&
1941
+ variant === 'input' &&
1899
1942
  status &&
1900
1943
  inputStatusBorderStyles[status.type],
1901
- variant !== 'ghost' &&
1944
+ variant === 'input' &&
1902
1945
  status &&
1903
1946
  !isDisabled &&
1904
1947
  inputStatusHoverShadowStyles[status.type],
1948
+ // Grouped secondary triggers keep their neutral fill but take the
1949
+ // group's row height, connected edge radii, and focus stacking.
1905
1950
  variant !== 'ghost' && inputGroup && groupStyles.inGroup,
1906
1951
  inputGroup && styles.triggerInGroup,
1907
1952
  controlXstyle,
@@ -0,0 +1,27 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Combobox.a11y.known-failures.ts
5
+ * @input Uses KnownFailure from @astryxdesign/a11y-spec
6
+ * @output Current public-safe Selector Combobox binding failures
7
+ * @position AST-021 debt records; operational ownership remains outside public source.
8
+ */
9
+
10
+ import type {KnownFailure} from '@astryxdesign/a11y-spec';
11
+
12
+ export const SELECTOR_COMBOBOX_KNOWN_FAILURES: ReadonlyArray<KnownFailure> = [
13
+ {
14
+ expectation: 'combobox.popup.relationship',
15
+ binding: 'Selector.trigger',
16
+ state: 'sheet-open',
17
+ evidenceLayer: 'dom',
18
+ failureEquals:
19
+ 'aria-controls does not reference the rendered combobox popup',
20
+ standardsReference:
21
+ 'WAI-ARIA 1.2 combobox: aria-controls refers to the displayed popup element; WCAG 2.2 1.3.1 Info and Relationships (Level A)',
22
+ userImpact:
23
+ 'On touch screens the trigger announces that it opens a dialog, but its controls relationship points past that dialog to the list inside it, so assistive technology that follows the relationship reaches the list without the sheet that hosts it.',
24
+ reason:
25
+ 'The trigger declares the sheet dialog as its popup kind while aria-controls names the inner listbox. Pointing both at one popup changes Selector runtime semantics under AR1, which needs its own decision; this test-only binding records the gap.',
26
+ },
27
+ ];
@@ -0,0 +1,145 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * Explicit Selector states bound to the reusable Combobox contract, plus the
5
+ * states that stay out of it and why.
6
+ */
7
+
8
+ import type {ComboboxStateFacts} from '@astryxdesign/a11y-spec';
9
+
10
+ export interface SelectorComboboxScenario {
11
+ readonly id:
12
+ 'closed' | 'open' | 'read-only' | 'busy' | 'sheet-open' | 'search-active';
13
+ /** `trigger` is the Selector button; `search` is the popup's search input. */
14
+ readonly binding: 'Selector.trigger' | 'Selector.search';
15
+ readonly storyId: string;
16
+ readonly open: boolean;
17
+ /** Move the highlight to the first option from the search input. */
18
+ readonly highlightFirst?: boolean;
19
+ readonly props: {
20
+ readonly isReadOnly?: boolean;
21
+ readonly isLoading?: boolean;
22
+ readonly value?: string;
23
+ readonly presentation?: 'bottom-sheet';
24
+ readonly hasSearch?: boolean;
25
+ };
26
+ readonly facts: ComboboxStateFacts;
27
+ }
28
+
29
+ const baseFacts: ComboboxStateFacts = {
30
+ name: 'Fruit',
31
+ expanded: false,
32
+ popupRole: 'listbox',
33
+ popupVisible: false,
34
+ activeDescendant: false,
35
+ readOnly: false,
36
+ busy: false,
37
+ inertUnderOwnModal: false,
38
+ };
39
+
40
+ export const SELECTOR_COMBOBOX_SCENARIOS: ReadonlyArray<SelectorComboboxScenario> =
41
+ [
42
+ {
43
+ id: 'closed',
44
+ binding: 'Selector.trigger',
45
+ storyId: 'a11y-combobox-pattern--selector-closed',
46
+ open: false,
47
+ props: {},
48
+ facts: baseFacts,
49
+ },
50
+ {
51
+ id: 'open',
52
+ binding: 'Selector.trigger',
53
+ storyId: 'a11y-combobox-pattern--selector-open',
54
+ open: true,
55
+ props: {},
56
+ facts: {
57
+ ...baseFacts,
58
+ expanded: true,
59
+ popupVisible: true,
60
+ activeDescendant: true,
61
+ },
62
+ },
63
+ {
64
+ id: 'read-only',
65
+ binding: 'Selector.trigger',
66
+ storyId: 'a11y-combobox-pattern--selector-read-only',
67
+ open: false,
68
+ props: {isReadOnly: true, value: 'apple'},
69
+ facts: {...baseFacts, readOnly: true},
70
+ },
71
+ {
72
+ id: 'busy',
73
+ binding: 'Selector.trigger',
74
+ storyId: 'a11y-combobox-pattern--selector-busy',
75
+ open: false,
76
+ props: {isLoading: true},
77
+ facts: {...baseFacts, busy: true},
78
+ },
79
+ {
80
+ // AR1: the touch presentation's popup is a modal dialog that contains
81
+ // the listbox, and the trigger declares aria-haspopup="dialog".
82
+ id: 'sheet-open',
83
+ binding: 'Selector.trigger',
84
+ storyId: 'a11y-combobox-pattern--selector-sheet-open',
85
+ open: true,
86
+ props: {presentation: 'bottom-sheet'},
87
+ facts: {
88
+ ...baseFacts,
89
+ expanded: true,
90
+ popupRole: 'dialog',
91
+ popupVisible: true,
92
+ activeDescendant: true,
93
+ // The modal sheet makes the trigger inert, so the browser leaves it
94
+ // out of the accessibility tree until the sheet closes.
95
+ inertUnderOwnModal: true,
96
+ },
97
+ },
98
+ {
99
+ // With hasSearch, focus moves into the popup's search input, which owns
100
+ // the combobox role and reports the highlighted option.
101
+ id: 'search-active',
102
+ binding: 'Selector.search',
103
+ storyId: 'a11y-combobox-pattern--selector-search-active',
104
+ open: true,
105
+ highlightFirst: true,
106
+ props: {hasSearch: true},
107
+ facts: {
108
+ ...baseFacts,
109
+ name: 'Search options',
110
+ expanded: true,
111
+ popupVisible: true,
112
+ activeDescendant: true,
113
+ },
114
+ },
115
+ ];
116
+
117
+ /** Selector states that change a combobox outcome but are not bound here. */
118
+ export const SELECTOR_COMBOBOX_EXCLUSIONS = [
119
+ {
120
+ owner: 'Selector trigger with hasSearch',
121
+ reason:
122
+ 'Editable search mode moves combobox semantics to the popup search input, bound as Selector.search; the trigger is not a combobox in that mode.',
123
+ },
124
+ {
125
+ owner: 'Selector search input inside the bottom sheet',
126
+ reason:
127
+ 'The search input keeps the semantics bound in search-active; the sheet changes only its modal host, which the modal-dialog owner covers.',
128
+ },
129
+ {
130
+ owner: 'Selector interaction suites',
131
+ reason:
132
+ 'Keyboard activation, arrow/Home/End/typeahead movement, Escape, Tab, and selection policy are verified by Selector.test.tsx (AR3); this contract encodes semantics only.',
133
+ },
134
+ {
135
+ owner: 'Disabled and disabledMessage triggers',
136
+ reason:
137
+ 'Availability state belongs to the disabled-state owners; this contract has no disabled expectation.',
138
+ },
139
+ {
140
+ owner:
141
+ 'MultiSelector, Typeahead, Tokenizer, PowerSearch, ComplexSelector, CommandPalette',
142
+ reason:
143
+ 'Later bounded adopters; this first binding is limited to Selector.',
144
+ },
145
+ ] as const;