@astryxdesign/core 0.6.2 → 0.6.3-canary.0788c49

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 (558) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/dist/BottomSheet/BottomSheet.d.ts +1 -1
  3. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  4. package/dist/BottomSheet/BottomSheet.js +3 -1
  5. package/dist/BottomSheet/BottomSheetPanel.d.ts +7 -5
  6. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  7. package/dist/BottomSheet/BottomSheetPanel.js +56 -19
  8. package/dist/Button/Button.d.ts.map +1 -1
  9. package/dist/Button/Button.js +13 -28
  10. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  11. package/dist/Chat/ChatComposer.js +10 -5
  12. package/dist/Chat/ChatComposerDrawer.d.ts +19 -5
  13. package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
  14. package/dist/Chat/ChatComposerDrawer.js +48 -23
  15. package/dist/Chat/ChatComposerInput.d.ts +11 -3
  16. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  17. package/dist/Chat/ChatComposerInput.js +63 -16
  18. package/dist/Chat/ChatDictationButton.d.ts +3 -2
  19. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  20. package/dist/Chat/ChatDictationButton.js +6 -4
  21. package/dist/Chat/ChatLayout.d.ts +19 -0
  22. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  23. package/dist/Chat/ChatLayout.js +19 -0
  24. package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
  25. package/dist/Chat/ChatLayoutScrollButton.js +44 -34
  26. package/dist/Chat/ChatMessageBubble.d.ts +2 -0
  27. package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
  28. package/dist/Chat/ChatMessageBubble.js +5 -3
  29. package/dist/Chat/ChatMessageList.d.ts +2 -1
  30. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  31. package/dist/Chat/ChatMessageList.js +4 -3
  32. package/dist/Chat/ChatMessageMetadata.d.ts +5 -4
  33. package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
  34. package/dist/Chat/ChatMessageMetadata.js +12 -9
  35. package/dist/Chat/ChatSendButton.d.ts +2 -1
  36. package/dist/Chat/ChatSendButton.d.ts.map +1 -1
  37. package/dist/Chat/ChatSendButton.js +6 -4
  38. package/dist/Chat/index.d.ts +1 -1
  39. package/dist/Chat/index.d.ts.map +1 -1
  40. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  41. package/dist/CheckboxInput/CheckboxInput.js +12 -2
  42. package/dist/Citation/Citation.d.ts +1 -1
  43. package/dist/Citation/Citation.d.ts.map +1 -1
  44. package/dist/Citation/Citation.js +3 -2
  45. package/dist/CodeBlock/highlightRanges.d.ts.map +1 -1
  46. package/dist/CodeBlock/highlightRanges.js +14 -3
  47. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  48. package/dist/Collapsible/Collapsible.js +6 -1
  49. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  50. package/dist/CommandPalette/CommandPaletteInput.js +1 -1
  51. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  52. package/dist/ComplexSelector/ComplexSelector.js +1 -1
  53. package/dist/DateInput/DateInput.d.ts.map +1 -1
  54. package/dist/DateInput/DateInput.js +4 -4
  55. package/dist/DateInput/NativeDateField.d.ts.map +1 -1
  56. package/dist/DateInput/NativeDateField.js +16 -16
  57. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  58. package/dist/DateInput/TouchDateField.js +2 -2
  59. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  60. package/dist/DateRangeInput/DateRangeInput.js +4 -4
  61. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  62. package/dist/DateTimeInput/DateTimeInput.js +8 -8
  63. package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
  64. package/dist/DateTimeInput/TouchDateTimeField.js +4 -4
  65. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts +9 -3
  66. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts.map +1 -1
  67. package/dist/DateTimeInput/nativePickerSegmentStyles.js +3 -3
  68. package/dist/Dialog/DialogHeader.d.ts +8 -1
  69. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  70. package/dist/Dialog/DialogHeader.js +13 -4
  71. package/dist/Field/InputClearButton.d.ts +4 -2
  72. package/dist/Field/InputClearButton.d.ts.map +1 -1
  73. package/dist/Field/InputClearButton.js +4 -2
  74. package/dist/Field/PanelSearchInput.d.ts.map +1 -1
  75. package/dist/Field/PanelSearchInput.js +1 -1
  76. package/dist/FileInput/FileInput.d.ts.map +1 -1
  77. package/dist/FileInput/FileInput.js +7 -7
  78. package/dist/Icon/Icon.d.ts +6 -31
  79. package/dist/Icon/Icon.d.ts.map +1 -1
  80. package/dist/Icon/Icon.js +8 -71
  81. package/dist/Icon/IconDefaultSizeContext.d.ts +4 -0
  82. package/dist/Icon/IconDefaultSizeContext.d.ts.map +1 -0
  83. package/dist/Icon/IconDefaultSizeContext.js +18 -0
  84. package/dist/Icon/IconSize.stylex.d.ts +65 -0
  85. package/dist/Icon/IconSize.stylex.d.ts.map +1 -0
  86. package/dist/Icon/IconSize.stylex.js +75 -0
  87. package/dist/Item/Item.d.ts +1 -1
  88. package/dist/Item/Item.d.ts.map +1 -1
  89. package/dist/Item/Item.js +4 -3
  90. package/dist/Kbd/Kbd.d.ts +5 -3
  91. package/dist/Kbd/Kbd.d.ts.map +1 -1
  92. package/dist/Kbd/Kbd.js +36 -42
  93. package/dist/Link/Link.d.ts.map +1 -1
  94. package/dist/Link/Link.js +6 -2
  95. package/dist/Link/useLinkComponent.d.ts +5 -15
  96. package/dist/Link/useLinkComponent.d.ts.map +1 -1
  97. package/dist/Link/useLinkComponent.js +34 -51
  98. package/dist/List/List.d.ts +19 -1
  99. package/dist/List/List.d.ts.map +1 -1
  100. package/dist/List/List.js +9 -2
  101. package/dist/List/ListContext.d.ts +1 -0
  102. package/dist/List/ListContext.d.ts.map +1 -1
  103. package/dist/List/ListContext.js +2 -1
  104. package/dist/List/ListItem.d.ts +1 -1
  105. package/dist/List/ListItem.d.ts.map +1 -1
  106. package/dist/List/ListItem.js +9 -2
  107. package/dist/Markdown/Markdown.d.ts +8 -2
  108. package/dist/Markdown/Markdown.d.ts.map +1 -1
  109. package/dist/Markdown/Markdown.fr23.bench.d.ts +2 -0
  110. package/dist/Markdown/Markdown.fr23.bench.d.ts.map +1 -0
  111. package/dist/Markdown/Markdown.fr23.bench.js +150 -0
  112. package/dist/Markdown/Markdown.fr23.sampling.d.ts +2 -0
  113. package/dist/Markdown/Markdown.fr23.sampling.d.ts.map +1 -0
  114. package/dist/Markdown/Markdown.fr23.sampling.js +43 -0
  115. package/dist/Markdown/Markdown.js +262 -109
  116. package/dist/Markdown/ast.d.ts +162 -0
  117. package/dist/Markdown/ast.d.ts.map +1 -0
  118. package/dist/Markdown/ast.js +74 -0
  119. package/dist/Markdown/index.d.ts +5 -3
  120. package/dist/Markdown/index.d.ts.map +1 -1
  121. package/dist/Markdown/index.js +3 -2
  122. package/dist/Markdown/parser/index.d.ts +10 -0
  123. package/dist/Markdown/parser/index.d.ts.map +1 -0
  124. package/dist/Markdown/parser/index.js +10 -0
  125. package/dist/Markdown/parser.d.ts +126 -58
  126. package/dist/Markdown/parser.d.ts.map +1 -1
  127. package/dist/Markdown/parser.js +726 -177
  128. package/dist/Markdown/plugins/frontmatter.d.ts +32 -0
  129. package/dist/Markdown/plugins/frontmatter.d.ts.map +1 -0
  130. package/dist/Markdown/plugins/frontmatter.js +132 -0
  131. package/dist/Markdown/plugins/index.d.ts +17 -0
  132. package/dist/Markdown/plugins/index.d.ts.map +1 -0
  133. package/dist/Markdown/plugins/index.js +14 -0
  134. package/dist/Markdown/plugins/protocol.d.ts +191 -0
  135. package/dist/Markdown/plugins/protocol.d.ts.map +1 -0
  136. package/dist/Markdown/plugins/protocol.js +957 -0
  137. package/dist/Markdown/plugins/semanticFence.d.ts +36 -0
  138. package/dist/Markdown/plugins/semanticFence.d.ts.map +1 -0
  139. package/dist/Markdown/plugins/semanticFence.js +213 -0
  140. package/dist/Markdown/plugins/softBreaks.d.ts +3 -0
  141. package/dist/Markdown/plugins/softBreaks.d.ts.map +1 -0
  142. package/dist/Markdown/plugins/softBreaks.js +172 -0
  143. package/dist/Markdown/plugins/sourceDecoration.d.ts +76 -0
  144. package/dist/Markdown/plugins/sourceDecoration.d.ts.map +1 -0
  145. package/dist/Markdown/plugins/sourceDecoration.js +393 -0
  146. package/dist/Markdown/plugins/textTransform.d.ts +30 -0
  147. package/dist/Markdown/plugins/textTransform.d.ts.map +1 -0
  148. package/dist/Markdown/plugins/textTransform.js +374 -0
  149. package/dist/Markdown/remark.d.ts +196 -0
  150. package/dist/Markdown/remark.d.ts.map +1 -0
  151. package/dist/Markdown/remark.js +1062 -0
  152. package/dist/Markdown/url.d.ts +6 -0
  153. package/dist/Markdown/url.d.ts.map +1 -0
  154. package/dist/Markdown/url.js +31 -0
  155. package/dist/Markdown/utils.d.ts +4 -2
  156. package/dist/Markdown/utils.d.ts.map +1 -1
  157. package/dist/Markdown/utils.js +2 -1
  158. package/dist/MetadataList/MetadataList.js +3 -3
  159. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  160. package/dist/NumberInput/NumberInput.js +4 -4
  161. package/dist/Outline/index.d.ts +1 -0
  162. package/dist/Outline/index.d.ts.map +1 -1
  163. package/dist/Outline/parseOutlineFromMarkdown.d.ts +7 -1
  164. package/dist/Outline/parseOutlineFromMarkdown.d.ts.map +1 -1
  165. package/dist/Outline/parseOutlineFromMarkdown.js +14 -8
  166. package/dist/Outline/useOutlineFromMarkdown.d.ts +3 -1
  167. package/dist/Outline/useOutlineFromMarkdown.d.ts.map +1 -1
  168. package/dist/Outline/useOutlineFromMarkdown.js +8 -3
  169. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  170. package/dist/PowerSearch/PowerSearchEditPopover.js +46 -30
  171. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  172. package/dist/PowerSearch/PowerSearchValueEditor.js +5 -0
  173. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  174. package/dist/ProgressBar/ProgressBar.js +36 -31
  175. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  176. package/dist/RadioList/RadioListItem.js +13 -1
  177. package/dist/ScrollableArea/ScrollableArea.d.ts +1 -0
  178. package/dist/ScrollableArea/ScrollableArea.d.ts.map +1 -1
  179. package/dist/ScrollableArea/ScrollableArea.js +1 -0
  180. package/dist/SegmentedControl/SegmentedControl.d.ts +1 -1
  181. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  182. package/dist/SegmentedControl/SegmentedControl.js +1 -0
  183. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  184. package/dist/SegmentedControl/SegmentedControlItem.js +5 -5
  185. package/dist/Selector/Selector.d.ts +9 -7
  186. package/dist/Selector/Selector.d.ts.map +1 -1
  187. package/dist/Selector/Selector.js +16 -7
  188. package/dist/SideNav/SideNav.d.ts +2 -1
  189. package/dist/SideNav/SideNav.d.ts.map +1 -1
  190. package/dist/SideNav/SideNav.js +9 -3
  191. package/dist/Slider/Slider.d.ts.map +1 -1
  192. package/dist/Slider/Slider.js +28 -12
  193. package/dist/Spinner/Spinner.d.ts.map +1 -1
  194. package/dist/Spinner/Spinner.js +23 -7
  195. package/dist/Switch/Switch.d.ts.map +1 -1
  196. package/dist/Switch/Switch.js +11 -0
  197. package/dist/TabList/Tab.d.ts +1 -1
  198. package/dist/TabList/Tab.d.ts.map +1 -1
  199. package/dist/TabList/Tab.js +20 -7
  200. package/dist/Table/Table.d.ts +1 -0
  201. package/dist/Table/Table.d.ts.map +1 -1
  202. package/dist/Table/Table.js +29 -13
  203. package/dist/TextArea/TextArea.d.ts.map +1 -1
  204. package/dist/TextArea/TextArea.js +1 -1
  205. package/dist/TextInput/TextInput.d.ts.map +1 -1
  206. package/dist/TextInput/TextInput.js +2 -2
  207. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  208. package/dist/TimeInput/TimeInput.js +4 -4
  209. package/dist/Timer/Timer.d.ts +49 -0
  210. package/dist/Timer/Timer.d.ts.map +1 -0
  211. package/dist/Timer/Timer.js +158 -0
  212. package/dist/Timer/index.d.ts +9 -0
  213. package/dist/Timer/index.d.ts.map +1 -0
  214. package/dist/Timer/index.js +10 -0
  215. package/dist/ToggleButton/ToggleButton.d.ts +9 -6
  216. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  217. package/dist/ToggleButton/ToggleButton.js +15 -27
  218. package/dist/TopNav/TopNavMenu.d.ts +1 -1
  219. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  220. package/dist/TopNav/TopNavMenu.js +3 -2
  221. package/dist/TreeList/TreeList.d.ts.map +1 -1
  222. package/dist/TreeList/TreeList.js +3 -0
  223. package/dist/TreeList/TreeListItem.d.ts +6 -1
  224. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  225. package/dist/TreeList/TreeListItem.js +12 -2
  226. package/dist/TreeList/TreeListTypes.d.ts +8 -1
  227. package/dist/TreeList/TreeListTypes.d.ts.map +1 -1
  228. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  229. package/dist/Typeahead/BaseTypeahead.js +16 -7
  230. package/dist/astryx.css +27 -11
  231. package/dist/hooks/scrollKeyboardDelegation.d.ts +3 -0
  232. package/dist/hooks/scrollKeyboardDelegation.d.ts.map +1 -0
  233. package/dist/hooks/scrollKeyboardDelegation.js +146 -0
  234. package/dist/hooks/useClickableContainer.d.ts +10 -1
  235. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  236. package/dist/hooks/useClickableContainer.js +15 -4
  237. package/dist/hooks/useScrollableArea.d.ts +6 -2
  238. package/dist/hooks/useScrollableArea.d.ts.map +1 -1
  239. package/dist/hooks/useScrollableArea.js +17 -5
  240. package/dist/index.d.ts +1 -0
  241. package/dist/index.d.ts.map +1 -1
  242. package/dist/index.js +3 -0
  243. package/dist/theme/declarationBoundary.d.ts +17 -0
  244. package/dist/theme/declarationBoundary.d.ts.map +1 -0
  245. package/dist/theme/declarationBoundary.js +343 -0
  246. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  247. package/dist/theme/derivedVarRegistry.js +4 -0
  248. package/dist/theme/generateThemeRules.d.ts +13 -7
  249. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  250. package/dist/theme/generateThemeRules.js +140 -34
  251. package/dist/utils/interactionOverlay.stylex.d.ts +8 -0
  252. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  253. package/dist/utils/interactionOverlay.stylex.js +9 -0
  254. package/dist/utils/safeUrl.d.ts +11 -0
  255. package/dist/utils/safeUrl.d.ts.map +1 -0
  256. package/dist/utils/safeUrl.js +78 -0
  257. package/locales/af-ZA.json +530 -1
  258. package/locales/ar-SA.json +490 -1
  259. package/locales/ca-ES.json +514 -1
  260. package/locales/cs-CZ.json +498 -1
  261. package/locales/da-DK.json +522 -1
  262. package/locales/de-DE.json +522 -1
  263. package/locales/el-GR.json +498 -1
  264. package/locales/es-ES.json +510 -1
  265. package/locales/fi-FI.json +498 -1
  266. package/locales/fr-FR.json +534 -1
  267. package/locales/he-IL.json +498 -1
  268. package/locales/hu-HU.json +498 -1
  269. package/locales/it-IT.json +510 -1
  270. package/locales/ja-JP.json +490 -1
  271. package/locales/ko-KR.json +498 -1
  272. package/locales/nl-NL.json +534 -1
  273. package/locales/no-NO.json +510 -1
  274. package/locales/pl-PL.json +506 -1
  275. package/locales/pt-BR.json +506 -1
  276. package/locales/pt-PT.json +506 -1
  277. package/locales/ro-RO.json +506 -1
  278. package/locales/ru-RU.json +498 -1
  279. package/locales/sr-SP.json +498 -1
  280. package/locales/sv-SE.json +502 -1
  281. package/locales/tr-TR.json +498 -1
  282. package/locales/uk-UA.json +498 -1
  283. package/locales/vi-VN.json +502 -1
  284. package/locales/zh-CN.json +490 -1
  285. package/locales/zh-TW.json +490 -1
  286. package/package.json +24 -4
  287. package/src/BottomSheet/BottomSheet.doc.mjs +8 -1
  288. package/src/BottomSheet/BottomSheet.spec.md +46 -20
  289. package/src/BottomSheet/BottomSheet.test.tsx +6 -3
  290. package/src/BottomSheet/BottomSheet.tsx +3 -1
  291. package/src/BottomSheet/BottomSheetKeyboard.test.tsx +195 -0
  292. package/src/BottomSheet/BottomSheetPanel.test.tsx +11 -1
  293. package/src/BottomSheet/BottomSheetPanel.tsx +49 -16
  294. package/src/BottomSheet/__tests__/BottomSheetKeyboard.a11y.browser.spec.ts +344 -0
  295. package/src/Button/Button.doc.mjs +5 -5
  296. package/src/Button/Button.tsx +15 -13
  297. package/src/Button/__tests__/Button.a11y.chromium.spec.ts +17 -1
  298. package/src/Button/__tests__/Button.a11y.known-failures.ts +0 -29
  299. package/src/Button/__tests__/Button.a11y.renders.tsx +9 -2
  300. package/src/Button/__tests__/Button.a11y.states.ts +9 -0
  301. package/src/Chat/ChatComposer.doc.mjs +14 -0
  302. package/src/Chat/ChatComposer.spec.md +230 -0
  303. package/src/Chat/ChatComposer.test.tsx +57 -0
  304. package/src/Chat/ChatComposer.tsx +21 -9
  305. package/src/Chat/ChatComposerDrawer.doc.mjs +63 -5
  306. package/src/Chat/ChatComposerDrawer.spec.md +267 -0
  307. package/src/Chat/ChatComposerDrawer.test.tsx +81 -12
  308. package/src/Chat/ChatComposerDrawer.tsx +38 -9
  309. package/src/Chat/ChatComposerInput.doc.mjs +114 -26
  310. package/src/Chat/ChatComposerInput.spec.md +185 -0
  311. package/src/Chat/ChatComposerInput.test.tsx +113 -3
  312. package/src/Chat/ChatComposerInput.tsx +78 -13
  313. package/src/Chat/ChatComposerTokenElement.spec.md +152 -0
  314. package/src/Chat/ChatComposerTokenElement.test.tsx +85 -0
  315. package/src/Chat/ChatDictationButton.doc.mjs +3 -3
  316. package/src/Chat/ChatDictationButton.spec.md +160 -0
  317. package/src/Chat/ChatDictationButton.test.tsx +92 -27
  318. package/src/Chat/ChatDictationButton.tsx +8 -6
  319. package/src/Chat/ChatLayout.doc.mjs +12 -4
  320. package/src/Chat/ChatLayout.spec.md +271 -0
  321. package/src/Chat/ChatLayout.test.tsx +28 -0
  322. package/src/Chat/ChatLayout.tsx +19 -0
  323. package/src/Chat/ChatLayoutScrollButton.spec.md +338 -0
  324. package/src/Chat/ChatLayoutScrollButton.test.tsx +31 -0
  325. package/src/Chat/ChatLayoutScrollButton.tsx +31 -14
  326. package/src/Chat/ChatMessageBubble.doc.mjs +28 -15
  327. package/src/Chat/ChatMessageBubble.spec.md +263 -0
  328. package/src/Chat/ChatMessageBubble.test.tsx +93 -0
  329. package/src/Chat/ChatMessageBubble.tsx +8 -3
  330. package/src/Chat/ChatMessageList.spec.md +222 -0
  331. package/src/Chat/ChatMessageList.test.tsx +64 -2
  332. package/src/Chat/ChatMessageList.tsx +4 -3
  333. package/src/Chat/ChatMessageMetadata.doc.mjs +57 -3
  334. package/src/Chat/ChatMessageMetadata.spec.md +220 -0
  335. package/src/Chat/ChatMessageMetadata.test.tsx +34 -0
  336. package/src/Chat/ChatMessageMetadata.tsx +13 -13
  337. package/src/Chat/ChatSendButton.doc.mjs +32 -0
  338. package/src/Chat/ChatSendButton.spec.md +227 -0
  339. package/src/Chat/ChatSendButton.test.tsx +128 -8
  340. package/src/Chat/ChatSendButton.tsx +6 -4
  341. package/src/Chat/__tests__/ChatLayoutScrollButton.a11y.chromium.spec.ts +514 -0
  342. package/src/Chat/__tests__/ChatLayoutScrollButtonSurface.a11y.chromium.spec.ts +740 -0
  343. package/src/Chat/__tests__/ChatMessageBubble.a11y.chromium.spec.ts +457 -0
  344. package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.test.ts +91 -0
  345. package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.ts +91 -0
  346. package/src/Chat/__tests__/ChatMessageList.a11y.chromium.spec.ts +456 -0
  347. package/src/Chat/__tests__/ChatMessageMetadata.a11y.chromium.spec.ts +413 -0
  348. package/src/Chat/__tests__/ChatSendButton.a11y.chromium.spec.ts +1522 -0
  349. package/src/Chat/index.ts +1 -0
  350. package/src/CheckboxInput/CheckboxInput.doc.mjs +11 -0
  351. package/src/CheckboxInput/CheckboxInput.test.tsx +34 -0
  352. package/src/CheckboxInput/CheckboxInput.tsx +21 -1
  353. package/src/Citation/Citation.doc.mjs +2 -2
  354. package/src/Citation/Citation.test.tsx +17 -0
  355. package/src/Citation/Citation.tsx +4 -2
  356. package/src/ClickableCard/ClickableCard.doc.mjs +2 -2
  357. package/src/ClickableCard/ClickableCard.test.tsx +102 -5
  358. package/src/CodeBlock/highlightRanges.test.ts +143 -1
  359. package/src/CodeBlock/highlightRanges.ts +15 -5
  360. package/src/Collapsible/Collapsible.doc.mjs +11 -0
  361. package/src/Collapsible/Collapsible.test.tsx +21 -0
  362. package/src/Collapsible/Collapsible.tsx +5 -0
  363. package/src/CommandPalette/CommandPaletteInput.tsx +6 -1
  364. package/src/ComplexSelector/ComplexSelector.tsx +6 -1
  365. package/src/DateInput/DateInput.tsx +6 -1
  366. package/src/DateInput/NativeDateField.tsx +24 -8
  367. package/src/DateInput/TouchDateField.tsx +6 -2
  368. package/src/DateRangeInput/DateRangeInput.tsx +6 -1
  369. package/src/DateTimeInput/DateTimeInput.tsx +6 -1
  370. package/src/DateTimeInput/TouchDateTimeField.tsx +6 -1
  371. package/src/DateTimeInput/nativePickerSegmentStyles.ts +24 -8
  372. package/src/Dialog/Dialog.doc.mjs +2 -0
  373. package/src/Dialog/DialogHeader.doc.mjs +22 -0
  374. package/src/Dialog/DialogHeader.test.tsx +167 -3
  375. package/src/Dialog/DialogHeader.tsx +37 -6
  376. package/src/Dialog/modules/DialogHeader.spec.md +101 -42
  377. package/src/DropdownMenu/DropdownMenu.test.tsx +5 -2
  378. package/src/Field/InputClearButton.test.tsx +17 -3
  379. package/src/Field/InputClearButton.tsx +5 -2
  380. package/src/Field/PanelSearchInput.tsx +5 -2
  381. package/src/FieldStatus/__tests__/StatusMessage.a11y.states.ts +1 -1
  382. package/src/FileInput/FileInput.tsx +11 -2
  383. package/src/Icon/Icon.doc.mjs +7 -5
  384. package/src/Icon/Icon.spec.md +81 -40
  385. package/src/Icon/Icon.test.tsx +6 -3
  386. package/src/Icon/Icon.tsx +20 -68
  387. package/src/Icon/IconDefaultSizeContext.ts +23 -0
  388. package/src/Icon/IconSize.stylex.ts +70 -0
  389. package/src/IconButton/IconButton.doc.mjs +3 -3
  390. package/src/IconButton/IconButton.test.tsx +40 -0
  391. package/src/Item/Item.doc.mjs +6 -2
  392. package/src/Item/Item.tsx +9 -3
  393. package/src/Kbd/Kbd.doc.mjs +3 -3
  394. package/src/Kbd/Kbd.test.tsx +57 -1
  395. package/src/Kbd/Kbd.tsx +57 -37
  396. package/src/Layout/LayoutFooter.doc.mjs +17 -0
  397. package/src/Link/Link.doc.mjs +17 -4
  398. package/src/Link/Link.test.tsx +24 -0
  399. package/src/Link/Link.tsx +5 -0
  400. package/src/Link/LinkProvider.doc.mjs +2 -2
  401. package/src/Link/__tests__/Link.navigation.a11y.chromium.spec.ts +261 -0
  402. package/src/Link/useLinkComponent.test.tsx +333 -0
  403. package/src/Link/useLinkComponent.ts +29 -57
  404. package/src/List/List.doc.mjs +6 -0
  405. package/src/List/List.spec.md +200 -0
  406. package/src/List/List.test.tsx +117 -1
  407. package/src/List/List.tsx +22 -2
  408. package/src/List/ListContext.tsx +3 -1
  409. package/src/List/ListItem.doc.mjs +2 -2
  410. package/src/List/ListItem.source-build.test.mjs +41 -0
  411. package/src/List/ListItem.tsx +26 -2
  412. package/src/Markdown/Markdown.doc.mjs +300 -2
  413. package/src/Markdown/Markdown.fr23.bench.ts +174 -0
  414. package/src/Markdown/Markdown.fr23.perf.test.ts +249 -0
  415. package/src/Markdown/Markdown.fr23.sampling.ts +49 -0
  416. package/src/Markdown/Markdown.helpers.perf.test.ts +489 -0
  417. package/src/Markdown/Markdown.public.test.ts +130 -3
  418. package/src/Markdown/Markdown.renderBoundary.test.tsx +72 -0
  419. package/src/Markdown/Markdown.spec.md +210 -45
  420. package/src/Markdown/Markdown.test.tsx +87 -3
  421. package/src/Markdown/Markdown.tsx +414 -135
  422. package/src/Markdown/ast.test.ts +307 -0
  423. package/src/Markdown/ast.ts +337 -0
  424. package/src/Markdown/incremental.test.ts +43 -0
  425. package/src/Markdown/index.ts +37 -1
  426. package/src/Markdown/modules/remark.spec.md +269 -0
  427. package/src/Markdown/modules/softBreaks.spec.md +137 -0
  428. package/src/Markdown/parser/index.ts +64 -0
  429. package/src/Markdown/parser.public.test.ts +103 -0
  430. package/src/Markdown/parser.test.ts +38 -6
  431. package/src/Markdown/parser.ts +1221 -242
  432. package/src/Markdown/plugins/frontmatter.test.tsx +125 -0
  433. package/src/Markdown/plugins/frontmatter.ts +198 -0
  434. package/src/Markdown/plugins/index.ts +54 -0
  435. package/src/Markdown/plugins/protocol.test.tsx +1022 -0
  436. package/src/Markdown/plugins/protocol.ts +1618 -0
  437. package/src/Markdown/plugins/semanticFence.test.tsx +546 -0
  438. package/src/Markdown/plugins/semanticFence.ts +424 -0
  439. package/src/Markdown/plugins/softBreaks.test.tsx +172 -0
  440. package/src/Markdown/plugins/softBreaks.ts +169 -0
  441. package/src/Markdown/plugins/sourceDecoration.test.tsx +799 -0
  442. package/src/Markdown/plugins/sourceDecoration.ts +539 -0
  443. package/src/Markdown/plugins/textTransform.test.ts +467 -0
  444. package/src/Markdown/plugins/textTransform.ts +554 -0
  445. package/src/Markdown/remark.test.tsx +1877 -0
  446. package/src/Markdown/remark.ts +1574 -0
  447. package/src/Markdown/url.ts +36 -0
  448. package/src/Markdown/utils.ts +13 -1
  449. package/src/MetadataList/MetadataList.test.tsx +4 -2
  450. package/src/MetadataList/MetadataList.tsx +3 -3
  451. package/src/MultiSelector/MultiSelector.test.tsx +1 -35
  452. package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +128 -0
  453. package/src/NumberInput/NumberInput.tsx +6 -1
  454. package/src/Outline/Outline.doc.mjs +7 -5
  455. package/src/Outline/Outline.spec.md +1 -1
  456. package/src/Outline/index.ts +1 -0
  457. package/src/Outline/modules/parseOutlineFromMarkdown.spec.md +142 -0
  458. package/src/Outline/parseOutlineFromMarkdown.ts +40 -10
  459. package/src/Outline/useOutlineFromMarkdown.ts +15 -3
  460. package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
  461. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +150 -1
  462. package/src/PowerSearch/PowerSearchEditPopover.tsx +51 -28
  463. package/src/PowerSearch/PowerSearchValueEditor.test.tsx +51 -0
  464. package/src/PowerSearch/PowerSearchValueEditor.tsx +4 -0
  465. package/src/ProgressBar/ProgressBar.test.tsx +29 -11
  466. package/src/ProgressBar/ProgressBar.tsx +59 -72
  467. package/src/RadioList/RadioList.doc.mjs +11 -0
  468. package/src/RadioList/RadioList.test.tsx +32 -0
  469. package/src/RadioList/RadioListItem.tsx +25 -1
  470. package/src/ScrollableArea/ScrollableArea.tsx +1 -0
  471. package/src/ScrollableArea/modules/useScrollableArea.spec.md +50 -22
  472. package/src/SegmentedControl/SegmentedControl.doc.mjs +2 -2
  473. package/src/SegmentedControl/SegmentedControl.test.tsx +61 -0
  474. package/src/SegmentedControl/SegmentedControl.tsx +2 -1
  475. package/src/SegmentedControl/SegmentedControlItem.tsx +6 -9
  476. package/src/Selector/Selector.doc.mjs +3 -3
  477. package/src/Selector/Selector.spec.md +51 -49
  478. package/src/Selector/Selector.test.tsx +61 -55
  479. package/src/Selector/Selector.tsx +34 -15
  480. package/src/Selector/__tests__/Listbox.a11y.chromium.spec.ts +130 -0
  481. package/src/Selector/__tests__/Listbox.a11y.dom.ts +38 -0
  482. package/src/Selector/__tests__/Listbox.a11y.inventory.test.ts +63 -0
  483. package/src/Selector/__tests__/Listbox.a11y.known-failures.ts +37 -0
  484. package/src/Selector/__tests__/Listbox.a11y.states.ts +232 -0
  485. package/src/Selector/__tests__/Selector.a11y.chromium.spec.ts +45 -0
  486. package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +121 -0
  487. package/src/SideNav/SideNav.doc.mjs +1 -1
  488. package/src/SideNav/SideNav.test.tsx +10 -0
  489. package/src/SideNav/SideNav.tsx +14 -2
  490. package/src/Slider/Slider.doc.mjs +17 -3
  491. package/src/Slider/Slider.test.tsx +221 -0
  492. package/src/Slider/Slider.tsx +40 -9
  493. package/src/Spinner/Spinner.test.tsx +4 -0
  494. package/src/Spinner/Spinner.tsx +31 -18
  495. package/src/Switch/Switch.doc.mjs +11 -0
  496. package/src/Switch/Switch.test.tsx +16 -0
  497. package/src/Switch/Switch.tsx +28 -0
  498. package/src/TabList/Tab.tsx +35 -7
  499. package/src/TabList/TabList.doc.mjs +11 -0
  500. package/src/TabList/TabList.spec.md +204 -0
  501. package/src/TabList/TabList.test.tsx +66 -0
  502. package/src/TabList/__tests__/Tabs.a11y.known-failures.ts +1 -34
  503. package/src/Table/Table.doc.mjs +1 -1
  504. package/src/Table/Table.spec.md +54 -40
  505. package/src/Table/Table.test.tsx +83 -10
  506. package/src/Table/Table.tsx +30 -22
  507. package/src/Table/__tests__/TableScroll.a11y.chromium.spec.ts +137 -0
  508. package/src/TextArea/TextArea.tsx +6 -1
  509. package/src/TextInput/TextInput.test.tsx +20 -1
  510. package/src/TextInput/TextInput.tsx +6 -1
  511. package/src/TimeInput/TimeInput.tsx +6 -1
  512. package/src/Timer/Timer.doc.mjs +183 -0
  513. package/src/Timer/Timer.spec.md +215 -0
  514. package/src/Timer/Timer.test.tsx +302 -0
  515. package/src/Timer/Timer.tsx +258 -0
  516. package/src/Timer/index.ts +11 -0
  517. package/src/ToggleButton/ToggleButton.doc.mjs +3 -3
  518. package/src/ToggleButton/ToggleButton.test.tsx +299 -4
  519. package/src/ToggleButton/ToggleButton.tsx +26 -33
  520. package/src/ToggleButton/ToggleButtonGroup.doc.mjs +3 -3
  521. package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +209 -0
  522. package/src/Token/Token.doc.mjs +2 -2
  523. package/src/Tokenizer/Tokenizer.spec.md +142 -75
  524. package/src/TopNav/TopNavMenu.doc.mjs +2 -2
  525. package/src/TopNav/TopNavMenu.test.tsx +30 -0
  526. package/src/TopNav/TopNavMenu.tsx +7 -2
  527. package/src/TreeList/TreeList.doc.mjs +3 -3
  528. package/src/TreeList/TreeList.spec.md +123 -75
  529. package/src/TreeList/TreeList.test.tsx +29 -0
  530. package/src/TreeList/TreeList.tsx +3 -0
  531. package/src/TreeList/TreeListItem.tsx +15 -1
  532. package/src/TreeList/TreeListTypes.ts +11 -1
  533. package/src/Typeahead/BaseTypeahead.spec.md +4 -3
  534. package/src/Typeahead/BaseTypeahead.tsx +21 -7
  535. package/src/Typeahead/Typeahead.test.tsx +53 -0
  536. package/src/__tests__/PressedState.a11y.chromium.spec.ts +813 -0
  537. package/src/__tests__/pressState.ts +93 -0
  538. package/src/hooks/scrollKeyboardDelegation.test.ts +155 -0
  539. package/src/hooks/scrollKeyboardDelegation.ts +233 -0
  540. package/src/hooks/useClickableContainer.test.tsx +236 -0
  541. package/src/hooks/useClickableContainer.ts +20 -4
  542. package/src/hooks/useScrollableArea.doc.mjs +15 -3
  543. package/src/hooks/useScrollableArea.test.tsx +59 -1
  544. package/src/hooks/useScrollableArea.ts +34 -10
  545. package/src/index.ts +3 -0
  546. package/src/inputFontFloor.test.ts +106 -0
  547. package/src/tailwind-theme.css +0 -6
  548. package/src/theme/Theme.test.tsx +32 -0
  549. package/src/theme/declarationBoundary.test.ts +126 -0
  550. package/src/theme/declarationBoundary.ts +347 -0
  551. package/src/theme/derivedVarRegistry.test.ts +7 -0
  552. package/src/theme/derivedVarRegistry.ts +1 -0
  553. package/src/theme/generateThemeRules.test.ts +526 -1
  554. package/src/theme/generateThemeRules.ts +228 -50
  555. package/src/theme/tokenValueCompat.test.ts +4 -1
  556. package/src/utils/interactionOverlay.stylex.ts +20 -0
  557. package/src/utils/safeUrl.test.ts +88 -0
  558. package/src/utils/safeUrl.ts +85 -0
@@ -10,7 +10,14 @@ import {
10
10
  afterAll,
11
11
  afterEach,
12
12
  } from 'vitest';
13
- import {render, screen, fireEvent, act} from '@testing-library/react';
13
+ import {
14
+ render,
15
+ screen,
16
+ fireEvent,
17
+ act,
18
+ waitFor,
19
+ within,
20
+ } from '@testing-library/react';
14
21
  import React, {useState} from 'react';
15
22
  import * as stylex from '@stylexjs/stylex';
16
23
  import {spacingVars} from '../theme/tokens.stylex';
@@ -170,6 +177,148 @@ describe('PowerSearch', () => {
170
177
  expect(popoverText).toContain('equals');
171
178
  });
172
179
 
180
+ describe('value editor identity follows the field and operator', () => {
181
+ // Two fields with the same value type, so an unkeyed editor would be
182
+ // reused across the switch and keep the first field's menu contents.
183
+ const sameTypeConfig: PowerSearchConfig = {
184
+ name: 'test-same-type',
185
+ fields: [
186
+ {
187
+ key: 'creator',
188
+ label: 'Creator',
189
+ operators: [
190
+ {
191
+ key: 'is_any_of',
192
+ label: 'is any of',
193
+ value: {
194
+ type: 'enum_list',
195
+ values: [{value: 'creator-1', label: 'Creator Person'}],
196
+ },
197
+ },
198
+ ],
199
+ },
200
+ {
201
+ key: 'owner',
202
+ label: 'Owner',
203
+ operators: [
204
+ {
205
+ key: 'is_any_of',
206
+ label: 'is any of',
207
+ value: {
208
+ type: 'enum_list',
209
+ values: [{value: 'owner-1', label: 'Owner Person'}],
210
+ },
211
+ },
212
+ ],
213
+ },
214
+ ],
215
+ };
216
+
217
+ const creatorOption = () =>
218
+ screen.queryByRole('option', {name: 'Creator Person', hidden: true});
219
+ const ownerOption = () =>
220
+ screen.queryByRole('option', {name: 'Owner Person', hidden: true});
221
+
222
+ it('replaces an open value menu when the field changes', async () => {
223
+ function Harness() {
224
+ const internalConfig = useInternalConfig(sameTypeConfig);
225
+ return (
226
+ <PowerSearchEditPopover
227
+ config={internalConfig}
228
+ filter={{
229
+ field: 'creator',
230
+ operator: 'is_any_of',
231
+ value: {type: 'enum_list', value: []},
232
+ }}
233
+ mode="edit"
234
+ onSave={() => {}}
235
+ onCancel={() => {}}
236
+ />
237
+ );
238
+ }
239
+
240
+ render(<Harness />);
241
+
242
+ // Open the Creator value menu and leave it open.
243
+ fireEvent.focus(screen.getByRole('combobox', {name: 'Values'}));
244
+ await waitFor(() => expect(creatorOption()).toBeInTheDocument());
245
+
246
+ // Switch the field underneath the open menu.
247
+ fireEvent.click(screen.getByRole('combobox', {name: 'Field'}));
248
+ fireEvent.click(
249
+ screen.getByRole('option', {name: 'Owner', hidden: true}),
250
+ );
251
+ act(() => flushRAF());
252
+
253
+ // The old field's options are gone, the new field's are shown.
254
+ expect(creatorOption()).not.toBeInTheDocument();
255
+ await waitFor(() => expect(ownerOption()).toBeInTheDocument());
256
+ });
257
+
258
+ it('replaces an open value menu on a nested sub-filter row', async () => {
259
+ const nestedConfig: PowerSearchConfig = {
260
+ name: 'test-nested-same-type',
261
+ fields: [
262
+ {
263
+ key: 'group',
264
+ label: 'Group',
265
+ operators: [
266
+ {key: 'all_of', label: 'all of', value: {type: 'nested'}},
267
+ ],
268
+ },
269
+ ...sameTypeConfig.fields,
270
+ ],
271
+ };
272
+
273
+ function Harness() {
274
+ const internalConfig = useInternalConfig(nestedConfig);
275
+ return (
276
+ <PowerSearchEditPopover
277
+ config={internalConfig}
278
+ filter={{
279
+ field: 'group',
280
+ operator: 'all_of',
281
+ value: {
282
+ type: 'nested',
283
+ value: [
284
+ {
285
+ field: 'creator',
286
+ operator: 'is_any_of',
287
+ value: {type: 'enum_list', value: []},
288
+ },
289
+ ],
290
+ },
291
+ }}
292
+ mode="edit"
293
+ onSave={() => {}}
294
+ onCancel={() => {}}
295
+ />
296
+ );
297
+ }
298
+
299
+ render(<Harness />);
300
+
301
+ fireEvent.focus(screen.getByRole('combobox', {name: 'Values'}));
302
+ await waitFor(() => expect(creatorOption()).toBeInTheDocument());
303
+
304
+ // The nested row's own field selector is the one currently on
305
+ // "Creator"; the root selector stays on "Group".
306
+ const rowField = screen
307
+ .getAllByRole('combobox', {name: 'Field'})
308
+ .find(el => el.textContent === 'Creator');
309
+ expect(rowField).toBeDefined();
310
+ fireEvent.click(rowField!);
311
+ const rowListbox = document.getElementById(
312
+ rowField!.getAttribute('aria-controls')!,
313
+ )!;
314
+ fireEvent.click(
315
+ within(rowListbox).getByRole('option', {name: 'Owner', hidden: true}),
316
+ );
317
+
318
+ expect(creatorOption()).not.toBeInTheDocument();
319
+ });
320
+ });
321
+
173
322
  it('edit popover shows correct filter after removing a preceding filter', () => {
174
323
  const filters: PowerSearchFilter[] = [
175
324
  {field: 'status', operator: 'is', value: {type: 'string', value: 'open'}},
@@ -22,7 +22,10 @@ import {TreeList, type TreeListItemData} from '../TreeList';
22
22
  import {useTranslator} from '../i18n';
23
23
  import {isImeKeyEvent} from '../utils/ime';
24
24
  import {spacingVars, typeScaleVars} from '../theme/tokens.stylex';
25
- import {PowerSearchValueEditor} from './PowerSearchValueEditor';
25
+ import {
26
+ PowerSearchValueEditor,
27
+ type PowerSearchValueEditorProps,
28
+ } from './PowerSearchValueEditor';
26
29
  import {resolveOperatorLabel} from './resolveOperatorLabel';
27
30
  import type {InternalConfig} from './useInternalConfig';
28
31
  import type {
@@ -356,6 +359,7 @@ function NestedSubFilterRow({
356
359
  {operatorValue && !isEmptyType && !isNestedType && (
357
360
  <div {...stylex.props(styles.nestedRowValueEditor)}>
358
361
  <PowerSearchValueEditor
362
+ key={valueEditorKey(subFilter)}
359
363
  operatorValue={operatorValue}
360
364
  filterValue={subFilter.value}
361
365
  onChange={handleValueChange}
@@ -606,6 +610,42 @@ function NestedEditor({
606
610
  return <TreeList items={items} density="balanced" />;
607
611
  }
608
612
 
613
+ // =============================================================================
614
+ // Value editor cell
615
+ // =============================================================================
616
+
617
+ // A field switch must replace the editor, not update it in place: a reused
618
+ // editor keeps its open menu and old results.
619
+ function valueEditorKey(filter: {field: string; operator?: string}): string {
620
+ return `${filter.field}\u0000${filter.operator ?? ''}`;
621
+ }
622
+
623
+ // Keyed on field + operator, so this also mounts on a field switch, not just
624
+ // when the popover opens; the focus handoff below runs for both.
625
+ function ValueEditorCell(props: PowerSearchValueEditorProps) {
626
+ const ref = useRef<HTMLDivElement>(null);
627
+
628
+ useEffect(() => {
629
+ const frame = requestAnimationFrame(() => {
630
+ const container = ref.current;
631
+ if (!container) {
632
+ return;
633
+ }
634
+ const focusable = container.querySelector<HTMLElement>(
635
+ 'input:not([disabled]), button:not([disabled]), textarea:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])',
636
+ );
637
+ focusable?.focus();
638
+ });
639
+ return () => cancelAnimationFrame(frame);
640
+ }, []);
641
+
642
+ return (
643
+ <div ref={ref} {...stylex.props(styles.valueEditor)}>
644
+ <PowerSearchValueEditor {...props} />
645
+ </div>
646
+ );
647
+ }
648
+
609
649
  // =============================================================================
610
650
  // Main popover
611
651
  // =============================================================================
@@ -625,22 +665,6 @@ export function PowerSearchEditPopover({
625
665
  saveButtonLabelFromProps ?? t('@astryx.powersearch.editor.apply');
626
666
  const [partialFilter, setPartialFilter] =
627
667
  useState<PartialFilter>(initialFilter);
628
- const valueEditorRef = useRef<HTMLDivElement>(null);
629
-
630
- // Focus the first focusable element inside the value editor after mount
631
- useEffect(() => {
632
- const frame = requestAnimationFrame(() => {
633
- const container = valueEditorRef.current;
634
- if (!container) {
635
- return;
636
- }
637
- const focusable = container.querySelector<HTMLElement>(
638
- 'input:not([disabled]), button:not([disabled]), textarea:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])',
639
- );
640
- focusable?.focus();
641
- });
642
- return () => cancelAnimationFrame(frame);
643
- }, []);
644
668
 
645
669
  const currentOperator = partialFilter.operator
646
670
  ? config.getOperator(partialFilter.field, partialFilter.operator)
@@ -854,17 +878,16 @@ export function PowerSearchEditPopover({
854
878
  </div>
855
879
  )}
856
880
  {operatorValue && !isEmptyType && (
857
- <div ref={valueEditorRef} {...stylex.props(styles.valueEditor)}>
858
- <PowerSearchValueEditor
859
- operatorValue={operatorValue}
860
- filterValue={partialFilter.value}
861
- onChange={handleValueChange}
862
- onEnter={handleSave}
863
- config={config}
864
- maxMenuItems={maxMenuItems}
865
- isDisabled={isReadOnly}
866
- />
867
- </div>
881
+ <ValueEditorCell
882
+ key={valueEditorKey(partialFilter)}
883
+ operatorValue={operatorValue}
884
+ filterValue={partialFilter.value}
885
+ onChange={handleValueChange}
886
+ onEnter={handleSave}
887
+ config={config}
888
+ maxMenuItems={maxMenuItems}
889
+ isDisabled={isReadOnly}
890
+ />
868
891
  )}
869
892
  </HStack>
870
893
  </div>
@@ -630,3 +630,54 @@ describe('maxMenuItems', () => {
630
630
  await expectCapped({type: 'entity_list', searchSource: source});
631
631
  });
632
632
  });
633
+
634
+ // =============================================================================
635
+ // EnumListEditor shows the whole closed vocabulary
636
+ // =============================================================================
637
+
638
+ describe('EnumListEditor', () => {
639
+ const values = Array.from({length: 12}, (_, index) => ({
640
+ value: `option-${index}`,
641
+ label: `Option ${index}`,
642
+ }));
643
+
644
+ it('shows every value on focus instead of the typeahead default of 10', async () => {
645
+ render(
646
+ <PowerSearchValueEditor
647
+ operatorValue={{type: 'enum_list', values}}
648
+ filterValue={undefined}
649
+ onChange={vi.fn()}
650
+ config={stubConfig}
651
+ />,
652
+ );
653
+
654
+ fireEvent.focus(screen.getByRole('combobox'));
655
+ await act(async () => {
656
+ await new Promise(resolve => setTimeout(resolve, 50));
657
+ });
658
+
659
+ expect(screen.getAllByRole('option', {hidden: true})).toHaveLength(12);
660
+ expect(screen.getByText('Option 11')).toBeInTheDocument();
661
+ });
662
+
663
+ it('shows every matching value when searching', async () => {
664
+ render(
665
+ <PowerSearchValueEditor
666
+ operatorValue={{type: 'enum_list', values}}
667
+ filterValue={undefined}
668
+ onChange={vi.fn()}
669
+ config={stubConfig}
670
+ />,
671
+ );
672
+
673
+ await act(async () => {
674
+ fireEvent.change(screen.getByRole('combobox'), {
675
+ target: {value: 'Option'},
676
+ });
677
+ await new Promise(resolve => setTimeout(resolve, 200));
678
+ });
679
+
680
+ expect(screen.getAllByRole('option', {hidden: true})).toHaveLength(12);
681
+ expect(screen.getByText('Option 11')).toBeInTheDocument();
682
+ });
683
+ });
@@ -619,6 +619,10 @@ function EnumListEditor({
619
619
  placeholder={t('@astryx.powersearch.valueEditor.selectValuesPlaceholder')}
620
620
  hasEntriesOnFocus
621
621
  debounceMs={0}
622
+ // A closed vocabulary: every value is a valid choice, so the menu shows
623
+ // all of them instead of BaseTypeahead's 10-suggestion default, which
624
+ // would silently drop valid choices with no way to reach them.
625
+ maxMenuItems={items.length}
622
626
  />
623
627
  );
624
628
  }
@@ -1,5 +1,12 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
+ /**
4
+ * @file ProgressBar.test.tsx
5
+ * @input Uses Vitest, Testing Library, and ProgressBar
6
+ * @output DOM regressions for values, labels, themes, and external mark triggers
7
+ * @position Colocated unit tests; mark geometry is checked in Storybook
8
+ */
9
+
3
10
  import {describe, it, expect, beforeAll} from 'vitest';
4
11
  import {render, screen, waitFor} from '@testing-library/react';
5
12
  import {ProgressBar} from './ProgressBar';
@@ -558,6 +565,21 @@ describe('ProgressBar', () => {
558
565
  expect(styleClasses(accentMarks[1])).toBe(styleClasses(errorMarks[1]));
559
566
  });
560
567
 
568
+ it('keeps tabbable target marks outside the progressbar subtree', () => {
569
+ const {container} = render(
570
+ <ProgressBar
571
+ value={50}
572
+ label="Progress"
573
+ marks={[{value: 80, label: 'Goal'}]}
574
+ />,
575
+ );
576
+ const progressbar = screen.getByRole('progressbar', {name: 'Progress'});
577
+ const mark = container.querySelector(MARK)!;
578
+ expect(mark).toHaveAttribute('tabindex', '0');
579
+ expect(progressbar.querySelector('[tabindex="0"]')).toBeNull();
580
+ expect(mark.closest('[role="progressbar"]')).toBeNull();
581
+ });
582
+
561
583
  it('renders every mark as a focusable trigger (label is required, never decorative)', () => {
562
584
  const {container} = render(
563
585
  <ProgressBar
@@ -605,10 +627,8 @@ describe('ProgressBar', () => {
605
627
  });
606
628
 
607
629
  it('keeps the progressbar element free of role="img"/aria-label children', () => {
608
- // Marks are children of role="progressbar" (unchanged DOM), but a
609
- // mark uses a Tooltip (aria-describedby) rather than a
610
- // role="img"+aria-label child, so nothing muddies what SRs announce for
611
- // the bar.
630
+ // Marks stay outside the progressbar's presentational subtree; their
631
+ // Tooltip descriptions must not change the bar's own semantics.
612
632
  const {container} = render(
613
633
  <ProgressBar
614
634
  value={50}
@@ -617,9 +637,7 @@ describe('ProgressBar', () => {
617
637
  />,
618
638
  );
619
639
  const progressbar = screen.getByRole('progressbar');
620
- // Mark is a child of the progressbar (DOM unchanged from main).
621
- expect(progressbar.querySelector(MARK)).not.toBeNull();
622
- // But it is not a labeled graphic that pollutes the a11y subtree.
640
+ expect(progressbar.querySelector(MARK)).toBeNull();
623
641
  expect(progressbar.querySelector('[role="img"]')).toBeNull();
624
642
  expect(progressbar.querySelector('[aria-label]')).toBeNull();
625
643
  expect(container.querySelectorAll(MARK)).toHaveLength(1);
@@ -637,9 +655,9 @@ describe('ProgressBar', () => {
637
655
  expect(progressbar.getAttribute('aria-valuetext')).toBe('50%');
638
656
  });
639
657
 
640
- it('renders marks as children of the progressbar (unchanged DOM)', () => {
641
- // Marks stay children of role="progressbar", after the fill — the same
642
- // shape as main. The fill remains the first child.
658
+ it('preserves the fill inside the progressbar when marks are rendered outside it', () => {
659
+ // Only the fill belongs to the semantic track; the focusable marks
660
+ // remain independently available outside its presentational subtree.
643
661
  const {container} = render(
644
662
  <ProgressBar
645
663
  value={50}
@@ -652,7 +670,7 @@ describe('ProgressBar', () => {
652
670
  expect(fill.style.width).toBe('50%');
653
671
  expect(fill.classList.contains('astryx-progressbar-mark')).toBe(false);
654
672
  const mark = container.querySelector<HTMLElement>(MARK)!;
655
- expect(mark.closest('[role="progressbar"]')).toBe(progressbar);
673
+ expect(progressbar).not.toContainElement(mark);
656
674
  expect(container.querySelectorAll(MARK)).toHaveLength(1);
657
675
  });
658
676
 
@@ -215,26 +215,23 @@ const styles = stylex.create({
215
215
  whiteSpace: 'nowrap',
216
216
  borderWidth: 0,
217
217
  },
218
- // The `role="progressbar"` element. In determinate mode it does NOT clip its
219
- // content (`overflow` stays visible) so a themed mark taller than the bar can
220
- // overhang it; the determinate fill rounds its own corners via `border-radius`
221
- // (see `fill`) and is always inside the track box, so dropping the clip does
222
- // not change its appearance at any progress. Indeterminate mode re-adds the
223
- // clip via `trackClipped` (see below) — its sliding fill travels outside the
224
- // track and must be clipped, and marks are ignored while indeterminate, so
225
- // there is nothing to overhang.
226
- track: {
218
+ // The in-flow track determines this containing block's actual height.
219
+ // Marks share its geometry without depending on the label row's height or
220
+ // entering the progressbar's presentational subtree. Keep it unclipped so
221
+ // themed tall marks can overhang the track symmetrically.
222
+ trackContainer: {
227
223
  position: 'relative',
228
224
  width: '100%',
225
+ },
226
+ // The semantic rail holds only the fill, which rounds its own corners.
227
+ track: {
228
+ width: '100%',
229
229
  height: '8px',
230
230
  backgroundColor: colorVars['--color-background-muted'],
231
231
  borderRadius: radiusVars['--radius-full'],
232
232
  },
233
- // Indeterminate-only clip. The indeterminate fill slides from translateX
234
- // -100% to 250%, so it deliberately overshoots the track on both sides and
235
- // relies on the track clipping it to the visible window. Applied only when
236
- // `isIndeterminate` (marks are suppressed then, so nothing needs to overhang)
237
- // so it never re-clips a themed tall mark in determinate mode.
233
+ // The indeterminate fill overshoots the track on both sides and must be
234
+ // clipped to the visible rail. Marks are suppressed in this mode.
238
235
  trackClipped: {
239
236
  overflow: 'hidden',
240
237
  },
@@ -260,11 +257,10 @@ const styles = stylex.create({
260
257
  animationTimingFunction: 'ease-in-out',
261
258
  animationIterationCount: 'infinite',
262
259
  },
263
- // A mark is a vertical tick centered on the track, a child of the
264
- // `role="progressbar"` element (unchanged DOM). The track no longer clips, so
265
- // its height — 8px by default — may exceed the bar and overhang; the centering
266
- // translate keeps any overhang symmetric. Positioned horizontally via
267
- // `insetInlineStart`; the translate mirrors under RTL.
260
+ // A mark is a vertical tick centered on the track via their shared positioning container.
261
+ // It stays outside role="progressbar" so it remains independently focusable.
262
+ // The centering translate keeps themed overhang symmetric;
263
+ // insetInlineStart and the mirrored translate preserve RTL.
268
264
  //
269
265
  // The dimensions read private vars rather than being plain declarations: a
270
266
  // theme writes `width`/`height` on the `progressbar-mark` target as usual and
@@ -518,59 +514,50 @@ export function ProgressBar({
518
514
  <VisuallyHidden id={labelId}>{label}</VisuallyHidden>
519
515
  )}
520
516
 
521
- {/* Progress track — the `role="progressbar"` element, holding the fill
522
- and marks as its children (unchanged DOM shape). It no longer clips
523
- (`overflow` is visible), so a themed taller mark can overhang it;
524
- the fill preserves its rounded shape via its own `border-radius`. */}
525
- <div
526
- role="progressbar"
527
- aria-valuenow={isIndeterminate ? undefined : clampedValue}
528
- aria-valuemin={isIndeterminate ? undefined : 0}
529
- aria-valuemax={isIndeterminate ? undefined : safeMax}
530
- aria-labelledby={labelId}
531
- aria-valuetext={isIndeterminate ? undefined : valueText}
532
- {...mergeProps(
533
- themeProps('progress-bar-track', undefined, {
534
- legacyNames: ['progressbar-track'],
535
- }),
536
- stylex.props(styles.track, isIndeterminate && styles.trackClipped),
537
- )}>
538
- {isIndeterminate ? (
539
- <div
540
- {...mergeProps(
541
- themeProps(
542
- 'progress-bar-fill',
543
- {variant: fillVariant},
544
- {legacyNames: ['progressbar-fill']},
545
- ),
546
- stylex.props(
547
- styles.indeterminateFill,
548
- variantStyles[fillVariant],
549
- ),
550
- )}
551
- />
552
- ) : (
553
- <div
554
- {...mergeProps(
555
- themeProps(
556
- 'progress-bar-fill',
557
- {variant: fillVariant},
558
- {legacyNames: ['progressbar-fill']},
559
- ),
560
- stylex.props(styles.fill, variantStyles[fillVariant]),
561
- )}
562
- style={{width: `${percentage}%`}}
563
- />
564
- )}
565
- {/* Target marks — children of the progressbar element (unchanged),
566
- layered above the fill so they show whether progress is below or
567
- past them. A mark's color follows what it sits on: inside the fill
568
- it uses the fill variant's on-color, out on the bare track it uses
569
- the emphasized divider color. Each mark is labeled, so it is a
570
- focusable Tooltip trigger: the label is visible on hover/focus and
571
- names the mark for assistive tech via the Tooltip's
572
- aria-describedby, without adding a labeled child to the
573
- progressbar's own a11y subtree. */}
517
+ <div {...stylex.props(styles.trackContainer)}>
518
+ <div
519
+ role="progressbar"
520
+ aria-valuenow={isIndeterminate ? undefined : clampedValue}
521
+ aria-valuemin={isIndeterminate ? undefined : 0}
522
+ aria-valuemax={isIndeterminate ? undefined : safeMax}
523
+ aria-labelledby={labelId}
524
+ aria-valuetext={isIndeterminate ? undefined : valueText}
525
+ {...mergeProps(
526
+ themeProps('progress-bar-track', undefined, {
527
+ legacyNames: ['progressbar-track'],
528
+ }),
529
+ stylex.props(styles.track, isIndeterminate && styles.trackClipped),
530
+ )}>
531
+ {isIndeterminate ? (
532
+ <div
533
+ {...mergeProps(
534
+ themeProps(
535
+ 'progress-bar-fill',
536
+ {variant: fillVariant},
537
+ {legacyNames: ['progressbar-fill']},
538
+ ),
539
+ stylex.props(
540
+ styles.indeterminateFill,
541
+ variantStyles[fillVariant],
542
+ ),
543
+ )}
544
+ />
545
+ ) : (
546
+ <div
547
+ {...mergeProps(
548
+ themeProps(
549
+ 'progress-bar-fill',
550
+ {variant: fillVariant},
551
+ {legacyNames: ['progressbar-fill']},
552
+ ),
553
+ stylex.props(styles.fill, variantStyles[fillVariant]),
554
+ )}
555
+ style={{width: `${percentage}%`}}
556
+ />
557
+ )}
558
+ </div>
559
+ {/* Marks overlay the fill but live outside the semantic progressbar.
560
+ Their Tooltip descriptions remain independently reachable on focus. */}
574
561
  {resolvedMarks.map(mark => {
575
562
  // The tick element. It is both the Tooltip's anchor and the Suspense
576
563
  // fallback shown while the lazy Tooltip chunk loads, so the tick is
@@ -132,6 +132,17 @@ export const docs = {
132
132
  {name: 'RadioListItem'},
133
133
  ],
134
134
  usage: {
135
+ accessibility: [
136
+ {
137
+ name: 'Radio circle',
138
+ category: 'Color contrast',
139
+ criterion: '1.4.11 Non-text Contrast',
140
+ requirement: '3:1',
141
+ states: ['Rest', 'Hover', 'Pointer down', 'Selected'],
142
+ description:
143
+ 'The circle edge (unselected) and fill (selected) must have at least 3:1 contrast with the surface behind them. For Hover and Pointer down, measure the final colors after the tint and the pressed overlay are applied.',
144
+ },
145
+ ],
135
146
  description:
136
147
  'A group of options where only one can be selected at a time. All options are visible at once, making it easy to compare choices. Use it when users need to pick one option from a small set.',
137
148
  bestPractices: [
@@ -15,6 +15,7 @@
15
15
  import {describe, it, expect, vi, beforeEach} from 'vitest';
16
16
  import {render, screen, fireEvent, waitFor} from '@testing-library/react';
17
17
  import userEvent from '@testing-library/user-event';
18
+ import {hasPressedArm} from '../__tests__/pressState';
18
19
  import {RadioList} from './RadioList';
19
20
  import {RadioListItem} from './RadioListItem';
20
21
  import {getForcedColorsRules} from '../__tests__/forcedColors';
@@ -856,3 +857,34 @@ describe('forced colors (WCAG 1.4.11)', () => {
856
857
  expect(getForcedColorsRules()).toContain('background-color: canvastext;');
857
858
  });
858
859
  });
860
+
861
+ describe('pressed state', () => {
862
+ it('paints the pressed overlay over the radio indicator while the row is pressed', () => {
863
+ const {container} = render(
864
+ <RadioList label="Plan" value="a" onChange={() => {}}>
865
+ <RadioListItem label="Option A" value="a" />
866
+ <RadioListItem label="Option B" value="b" />
867
+ </RadioList>,
868
+ );
869
+ const circle = container.querySelector('.astryx-radio-indicator');
870
+ const wrapper = circle?.parentElement?.parentElement;
871
+ if (wrapper == null) {
872
+ throw new Error('the radio has no indicator wrapper to press');
873
+ }
874
+ expect(hasPressedArm(wrapper)).toBe(true);
875
+ });
876
+
877
+ it('does not expose a pressed arm on a disabled radio', () => {
878
+ const {container} = render(
879
+ <RadioList label="Plan" value="a" onChange={() => {}}>
880
+ <RadioListItem label="Unavailable" value="b" isDisabled />
881
+ </RadioList>,
882
+ );
883
+ const circle = container.querySelector('.astryx-radio-indicator');
884
+ const wrapper = circle?.parentElement?.parentElement;
885
+ if (wrapper == null) {
886
+ throw new Error('the radio has no indicator wrapper');
887
+ }
888
+ expect(hasPressedArm(wrapper)).toBe(false);
889
+ });
890
+ });