@astryxdesign/core 0.6.2 → 0.6.3-canary.0899f21

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 (549) 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.js +1 -1
  36. package/dist/Chat/index.d.ts +1 -1
  37. package/dist/Chat/index.d.ts.map +1 -1
  38. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  39. package/dist/CheckboxInput/CheckboxInput.js +12 -2
  40. package/dist/Citation/Citation.d.ts +1 -1
  41. package/dist/Citation/Citation.d.ts.map +1 -1
  42. package/dist/Citation/Citation.js +3 -2
  43. package/dist/CodeBlock/highlightRanges.d.ts.map +1 -1
  44. package/dist/CodeBlock/highlightRanges.js +14 -3
  45. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  46. package/dist/Collapsible/Collapsible.js +6 -1
  47. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  48. package/dist/CommandPalette/CommandPaletteInput.js +1 -1
  49. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  50. package/dist/ComplexSelector/ComplexSelector.js +1 -1
  51. package/dist/DateInput/DateInput.d.ts.map +1 -1
  52. package/dist/DateInput/DateInput.js +4 -4
  53. package/dist/DateInput/NativeDateField.d.ts.map +1 -1
  54. package/dist/DateInput/NativeDateField.js +16 -16
  55. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  56. package/dist/DateInput/TouchDateField.js +2 -2
  57. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  58. package/dist/DateRangeInput/DateRangeInput.js +4 -4
  59. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  60. package/dist/DateTimeInput/DateTimeInput.js +8 -8
  61. package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
  62. package/dist/DateTimeInput/TouchDateTimeField.js +4 -4
  63. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts +9 -3
  64. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts.map +1 -1
  65. package/dist/DateTimeInput/nativePickerSegmentStyles.js +3 -3
  66. package/dist/Dialog/DialogHeader.d.ts +8 -1
  67. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  68. package/dist/Dialog/DialogHeader.js +13 -4
  69. package/dist/Field/InputClearButton.d.ts +4 -2
  70. package/dist/Field/InputClearButton.d.ts.map +1 -1
  71. package/dist/Field/InputClearButton.js +4 -2
  72. package/dist/Field/PanelSearchInput.d.ts.map +1 -1
  73. package/dist/Field/PanelSearchInput.js +1 -1
  74. package/dist/FileInput/FileInput.d.ts.map +1 -1
  75. package/dist/FileInput/FileInput.js +7 -7
  76. package/dist/Icon/Icon.d.ts +6 -31
  77. package/dist/Icon/Icon.d.ts.map +1 -1
  78. package/dist/Icon/Icon.js +8 -71
  79. package/dist/Icon/IconDefaultSizeContext.d.ts +4 -0
  80. package/dist/Icon/IconDefaultSizeContext.d.ts.map +1 -0
  81. package/dist/Icon/IconDefaultSizeContext.js +18 -0
  82. package/dist/Icon/IconSize.stylex.d.ts +65 -0
  83. package/dist/Icon/IconSize.stylex.d.ts.map +1 -0
  84. package/dist/Icon/IconSize.stylex.js +75 -0
  85. package/dist/Item/Item.d.ts +1 -1
  86. package/dist/Item/Item.d.ts.map +1 -1
  87. package/dist/Item/Item.js +4 -3
  88. package/dist/Kbd/Kbd.d.ts +5 -3
  89. package/dist/Kbd/Kbd.d.ts.map +1 -1
  90. package/dist/Kbd/Kbd.js +36 -42
  91. package/dist/Link/Link.d.ts.map +1 -1
  92. package/dist/Link/Link.js +6 -2
  93. package/dist/Link/useLinkComponent.d.ts +5 -15
  94. package/dist/Link/useLinkComponent.d.ts.map +1 -1
  95. package/dist/Link/useLinkComponent.js +34 -51
  96. package/dist/List/List.d.ts +19 -1
  97. package/dist/List/List.d.ts.map +1 -1
  98. package/dist/List/List.js +9 -2
  99. package/dist/List/ListContext.d.ts +1 -0
  100. package/dist/List/ListContext.d.ts.map +1 -1
  101. package/dist/List/ListContext.js +2 -1
  102. package/dist/List/ListItem.d.ts +1 -1
  103. package/dist/List/ListItem.d.ts.map +1 -1
  104. package/dist/List/ListItem.js +9 -2
  105. package/dist/Markdown/Markdown.d.ts +8 -2
  106. package/dist/Markdown/Markdown.d.ts.map +1 -1
  107. package/dist/Markdown/Markdown.fr23.bench.d.ts +2 -0
  108. package/dist/Markdown/Markdown.fr23.bench.d.ts.map +1 -0
  109. package/dist/Markdown/Markdown.fr23.bench.js +150 -0
  110. package/dist/Markdown/Markdown.fr23.sampling.d.ts +2 -0
  111. package/dist/Markdown/Markdown.fr23.sampling.d.ts.map +1 -0
  112. package/dist/Markdown/Markdown.fr23.sampling.js +43 -0
  113. package/dist/Markdown/Markdown.js +262 -109
  114. package/dist/Markdown/ast.d.ts +162 -0
  115. package/dist/Markdown/ast.d.ts.map +1 -0
  116. package/dist/Markdown/ast.js +74 -0
  117. package/dist/Markdown/index.d.ts +5 -3
  118. package/dist/Markdown/index.d.ts.map +1 -1
  119. package/dist/Markdown/index.js +3 -2
  120. package/dist/Markdown/parser/index.d.ts +10 -0
  121. package/dist/Markdown/parser/index.d.ts.map +1 -0
  122. package/dist/Markdown/parser/index.js +10 -0
  123. package/dist/Markdown/parser.d.ts +126 -58
  124. package/dist/Markdown/parser.d.ts.map +1 -1
  125. package/dist/Markdown/parser.js +716 -167
  126. package/dist/Markdown/plugins/frontmatter.d.ts +32 -0
  127. package/dist/Markdown/plugins/frontmatter.d.ts.map +1 -0
  128. package/dist/Markdown/plugins/frontmatter.js +132 -0
  129. package/dist/Markdown/plugins/index.d.ts +17 -0
  130. package/dist/Markdown/plugins/index.d.ts.map +1 -0
  131. package/dist/Markdown/plugins/index.js +14 -0
  132. package/dist/Markdown/plugins/protocol.d.ts +191 -0
  133. package/dist/Markdown/plugins/protocol.d.ts.map +1 -0
  134. package/dist/Markdown/plugins/protocol.js +957 -0
  135. package/dist/Markdown/plugins/semanticFence.d.ts +36 -0
  136. package/dist/Markdown/plugins/semanticFence.d.ts.map +1 -0
  137. package/dist/Markdown/plugins/semanticFence.js +213 -0
  138. package/dist/Markdown/plugins/softBreaks.d.ts +3 -0
  139. package/dist/Markdown/plugins/softBreaks.d.ts.map +1 -0
  140. package/dist/Markdown/plugins/softBreaks.js +172 -0
  141. package/dist/Markdown/plugins/sourceDecoration.d.ts +76 -0
  142. package/dist/Markdown/plugins/sourceDecoration.d.ts.map +1 -0
  143. package/dist/Markdown/plugins/sourceDecoration.js +393 -0
  144. package/dist/Markdown/plugins/textTransform.d.ts +30 -0
  145. package/dist/Markdown/plugins/textTransform.d.ts.map +1 -0
  146. package/dist/Markdown/plugins/textTransform.js +374 -0
  147. package/dist/Markdown/remark.d.ts +196 -0
  148. package/dist/Markdown/remark.d.ts.map +1 -0
  149. package/dist/Markdown/remark.js +1062 -0
  150. package/dist/Markdown/url.d.ts +6 -0
  151. package/dist/Markdown/url.d.ts.map +1 -0
  152. package/dist/Markdown/url.js +31 -0
  153. package/dist/Markdown/utils.d.ts +4 -2
  154. package/dist/Markdown/utils.d.ts.map +1 -1
  155. package/dist/Markdown/utils.js +2 -1
  156. package/dist/MetadataList/MetadataList.js +3 -3
  157. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  158. package/dist/NumberInput/NumberInput.js +4 -4
  159. package/dist/Outline/index.d.ts +1 -0
  160. package/dist/Outline/index.d.ts.map +1 -1
  161. package/dist/Outline/parseOutlineFromMarkdown.d.ts +7 -1
  162. package/dist/Outline/parseOutlineFromMarkdown.d.ts.map +1 -1
  163. package/dist/Outline/parseOutlineFromMarkdown.js +14 -8
  164. package/dist/Outline/useOutlineFromMarkdown.d.ts +3 -1
  165. package/dist/Outline/useOutlineFromMarkdown.d.ts.map +1 -1
  166. package/dist/Outline/useOutlineFromMarkdown.js +8 -3
  167. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  168. package/dist/PowerSearch/PowerSearchEditPopover.js +46 -30
  169. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  170. package/dist/PowerSearch/PowerSearchValueEditor.js +5 -0
  171. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  172. package/dist/ProgressBar/ProgressBar.js +36 -31
  173. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  174. package/dist/RadioList/RadioListItem.js +13 -1
  175. package/dist/ScrollableArea/ScrollableArea.d.ts +1 -0
  176. package/dist/ScrollableArea/ScrollableArea.d.ts.map +1 -1
  177. package/dist/ScrollableArea/ScrollableArea.js +1 -0
  178. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  179. package/dist/SegmentedControl/SegmentedControlItem.js +5 -5
  180. package/dist/Selector/Selector.d.ts +9 -7
  181. package/dist/Selector/Selector.d.ts.map +1 -1
  182. package/dist/Selector/Selector.js +16 -7
  183. package/dist/SideNav/SideNav.d.ts +2 -1
  184. package/dist/SideNav/SideNav.d.ts.map +1 -1
  185. package/dist/SideNav/SideNav.js +9 -3
  186. package/dist/Slider/Slider.d.ts.map +1 -1
  187. package/dist/Slider/Slider.js +28 -12
  188. package/dist/Spinner/Spinner.d.ts.map +1 -1
  189. package/dist/Spinner/Spinner.js +23 -7
  190. package/dist/Switch/Switch.d.ts.map +1 -1
  191. package/dist/Switch/Switch.js +11 -0
  192. package/dist/TabList/Tab.d.ts +1 -1
  193. package/dist/TabList/Tab.d.ts.map +1 -1
  194. package/dist/TabList/Tab.js +20 -7
  195. package/dist/Table/Table.d.ts +1 -0
  196. package/dist/Table/Table.d.ts.map +1 -1
  197. package/dist/Table/Table.js +29 -13
  198. package/dist/TextArea/TextArea.d.ts.map +1 -1
  199. package/dist/TextArea/TextArea.js +1 -1
  200. package/dist/TextInput/TextInput.d.ts.map +1 -1
  201. package/dist/TextInput/TextInput.js +2 -2
  202. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  203. package/dist/TimeInput/TimeInput.js +4 -4
  204. package/dist/Timer/Timer.d.ts +49 -0
  205. package/dist/Timer/Timer.d.ts.map +1 -0
  206. package/dist/Timer/Timer.js +158 -0
  207. package/dist/Timer/index.d.ts +9 -0
  208. package/dist/Timer/index.d.ts.map +1 -0
  209. package/dist/Timer/index.js +10 -0
  210. package/dist/ToggleButton/ToggleButton.d.ts +9 -6
  211. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  212. package/dist/ToggleButton/ToggleButton.js +15 -27
  213. package/dist/TopNav/TopNavMenu.d.ts +1 -1
  214. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  215. package/dist/TopNav/TopNavMenu.js +3 -2
  216. package/dist/TreeList/TreeList.d.ts.map +1 -1
  217. package/dist/TreeList/TreeList.js +3 -0
  218. package/dist/TreeList/TreeListItem.d.ts +6 -1
  219. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  220. package/dist/TreeList/TreeListItem.js +12 -2
  221. package/dist/TreeList/TreeListTypes.d.ts +8 -1
  222. package/dist/TreeList/TreeListTypes.d.ts.map +1 -1
  223. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  224. package/dist/Typeahead/BaseTypeahead.js +16 -7
  225. package/dist/astryx.css +27 -11
  226. package/dist/hooks/scrollKeyboardDelegation.d.ts +3 -0
  227. package/dist/hooks/scrollKeyboardDelegation.d.ts.map +1 -0
  228. package/dist/hooks/scrollKeyboardDelegation.js +146 -0
  229. package/dist/hooks/useClickableContainer.d.ts +10 -1
  230. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  231. package/dist/hooks/useClickableContainer.js +15 -4
  232. package/dist/hooks/useScrollableArea.d.ts +6 -2
  233. package/dist/hooks/useScrollableArea.d.ts.map +1 -1
  234. package/dist/hooks/useScrollableArea.js +17 -5
  235. package/dist/index.d.ts +1 -0
  236. package/dist/index.d.ts.map +1 -1
  237. package/dist/index.js +3 -0
  238. package/dist/theme/declarationBoundary.d.ts +17 -0
  239. package/dist/theme/declarationBoundary.d.ts.map +1 -0
  240. package/dist/theme/declarationBoundary.js +343 -0
  241. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  242. package/dist/theme/derivedVarRegistry.js +4 -0
  243. package/dist/theme/generateThemeRules.d.ts +13 -7
  244. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  245. package/dist/theme/generateThemeRules.js +140 -34
  246. package/dist/utils/interactionOverlay.stylex.d.ts +8 -0
  247. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  248. package/dist/utils/interactionOverlay.stylex.js +9 -0
  249. package/dist/utils/safeUrl.d.ts +11 -0
  250. package/dist/utils/safeUrl.d.ts.map +1 -0
  251. package/dist/utils/safeUrl.js +78 -0
  252. package/locales/af-ZA.json +530 -1
  253. package/locales/ar-SA.json +490 -1
  254. package/locales/ca-ES.json +514 -1
  255. package/locales/cs-CZ.json +498 -1
  256. package/locales/da-DK.json +522 -1
  257. package/locales/de-DE.json +522 -1
  258. package/locales/el-GR.json +498 -1
  259. package/locales/es-ES.json +510 -1
  260. package/locales/fi-FI.json +498 -1
  261. package/locales/fr-FR.json +534 -1
  262. package/locales/he-IL.json +498 -1
  263. package/locales/hu-HU.json +498 -1
  264. package/locales/it-IT.json +510 -1
  265. package/locales/ja-JP.json +490 -1
  266. package/locales/ko-KR.json +498 -1
  267. package/locales/nl-NL.json +534 -1
  268. package/locales/no-NO.json +510 -1
  269. package/locales/pl-PL.json +506 -1
  270. package/locales/pt-BR.json +506 -1
  271. package/locales/pt-PT.json +506 -1
  272. package/locales/ro-RO.json +506 -1
  273. package/locales/ru-RU.json +498 -1
  274. package/locales/sr-SP.json +498 -1
  275. package/locales/sv-SE.json +502 -1
  276. package/locales/tr-TR.json +498 -1
  277. package/locales/uk-UA.json +498 -1
  278. package/locales/vi-VN.json +502 -1
  279. package/locales/zh-CN.json +490 -1
  280. package/locales/zh-TW.json +490 -1
  281. package/package.json +24 -4
  282. package/src/BottomSheet/BottomSheet.doc.mjs +8 -1
  283. package/src/BottomSheet/BottomSheet.spec.md +46 -20
  284. package/src/BottomSheet/BottomSheet.test.tsx +6 -3
  285. package/src/BottomSheet/BottomSheet.tsx +3 -1
  286. package/src/BottomSheet/BottomSheetKeyboard.test.tsx +195 -0
  287. package/src/BottomSheet/BottomSheetPanel.test.tsx +11 -1
  288. package/src/BottomSheet/BottomSheetPanel.tsx +49 -16
  289. package/src/BottomSheet/__tests__/BottomSheetKeyboard.a11y.browser.spec.ts +344 -0
  290. package/src/Button/Button.doc.mjs +5 -5
  291. package/src/Button/Button.tsx +15 -13
  292. package/src/Button/__tests__/Button.a11y.chromium.spec.ts +17 -1
  293. package/src/Button/__tests__/Button.a11y.known-failures.ts +0 -29
  294. package/src/Button/__tests__/Button.a11y.renders.tsx +9 -2
  295. package/src/Button/__tests__/Button.a11y.states.ts +9 -0
  296. package/src/Chat/ChatComposer.doc.mjs +14 -0
  297. package/src/Chat/ChatComposer.spec.md +230 -0
  298. package/src/Chat/ChatComposer.test.tsx +57 -0
  299. package/src/Chat/ChatComposer.tsx +21 -9
  300. package/src/Chat/ChatComposerDrawer.doc.mjs +63 -5
  301. package/src/Chat/ChatComposerDrawer.spec.md +267 -0
  302. package/src/Chat/ChatComposerDrawer.test.tsx +81 -12
  303. package/src/Chat/ChatComposerDrawer.tsx +38 -9
  304. package/src/Chat/ChatComposerInput.doc.mjs +114 -26
  305. package/src/Chat/ChatComposerInput.spec.md +185 -0
  306. package/src/Chat/ChatComposerInput.test.tsx +113 -3
  307. package/src/Chat/ChatComposerInput.tsx +78 -13
  308. package/src/Chat/ChatComposerTokenElement.spec.md +152 -0
  309. package/src/Chat/ChatComposerTokenElement.test.tsx +85 -0
  310. package/src/Chat/ChatDictationButton.doc.mjs +3 -3
  311. package/src/Chat/ChatDictationButton.spec.md +160 -0
  312. package/src/Chat/ChatDictationButton.test.tsx +92 -27
  313. package/src/Chat/ChatDictationButton.tsx +8 -6
  314. package/src/Chat/ChatLayout.doc.mjs +12 -4
  315. package/src/Chat/ChatLayout.spec.md +271 -0
  316. package/src/Chat/ChatLayout.test.tsx +28 -0
  317. package/src/Chat/ChatLayout.tsx +19 -0
  318. package/src/Chat/ChatLayoutScrollButton.spec.md +338 -0
  319. package/src/Chat/ChatLayoutScrollButton.test.tsx +31 -0
  320. package/src/Chat/ChatLayoutScrollButton.tsx +31 -14
  321. package/src/Chat/ChatMessageBubble.doc.mjs +28 -15
  322. package/src/Chat/ChatMessageBubble.spec.md +263 -0
  323. package/src/Chat/ChatMessageBubble.test.tsx +93 -0
  324. package/src/Chat/ChatMessageBubble.tsx +8 -3
  325. package/src/Chat/ChatMessageList.spec.md +222 -0
  326. package/src/Chat/ChatMessageList.test.tsx +64 -2
  327. package/src/Chat/ChatMessageList.tsx +4 -3
  328. package/src/Chat/ChatMessageMetadata.doc.mjs +57 -3
  329. package/src/Chat/ChatMessageMetadata.spec.md +220 -0
  330. package/src/Chat/ChatMessageMetadata.test.tsx +34 -0
  331. package/src/Chat/ChatMessageMetadata.tsx +13 -13
  332. package/src/Chat/ChatSendButton.test.tsx +1 -4
  333. package/src/Chat/ChatSendButton.tsx +1 -1
  334. package/src/Chat/__tests__/ChatLayoutScrollButton.a11y.chromium.spec.ts +514 -0
  335. package/src/Chat/__tests__/ChatLayoutScrollButtonSurface.a11y.chromium.spec.ts +740 -0
  336. package/src/Chat/__tests__/ChatMessageBubble.a11y.chromium.spec.ts +457 -0
  337. package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.test.ts +91 -0
  338. package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.ts +91 -0
  339. package/src/Chat/__tests__/ChatMessageList.a11y.chromium.spec.ts +456 -0
  340. package/src/Chat/__tests__/ChatMessageMetadata.a11y.chromium.spec.ts +413 -0
  341. package/src/Chat/index.ts +1 -0
  342. package/src/CheckboxInput/CheckboxInput.doc.mjs +11 -0
  343. package/src/CheckboxInput/CheckboxInput.test.tsx +34 -0
  344. package/src/CheckboxInput/CheckboxInput.tsx +21 -1
  345. package/src/Citation/Citation.doc.mjs +2 -2
  346. package/src/Citation/Citation.test.tsx +17 -0
  347. package/src/Citation/Citation.tsx +4 -2
  348. package/src/ClickableCard/ClickableCard.doc.mjs +2 -2
  349. package/src/ClickableCard/ClickableCard.test.tsx +102 -5
  350. package/src/CodeBlock/highlightRanges.test.ts +143 -1
  351. package/src/CodeBlock/highlightRanges.ts +15 -5
  352. package/src/Collapsible/Collapsible.doc.mjs +11 -0
  353. package/src/Collapsible/Collapsible.test.tsx +21 -0
  354. package/src/Collapsible/Collapsible.tsx +5 -0
  355. package/src/CommandPalette/CommandPaletteInput.tsx +6 -1
  356. package/src/ComplexSelector/ComplexSelector.tsx +6 -1
  357. package/src/DateInput/DateInput.tsx +6 -1
  358. package/src/DateInput/NativeDateField.tsx +24 -8
  359. package/src/DateInput/TouchDateField.tsx +6 -2
  360. package/src/DateRangeInput/DateRangeInput.tsx +6 -1
  361. package/src/DateTimeInput/DateTimeInput.tsx +6 -1
  362. package/src/DateTimeInput/TouchDateTimeField.tsx +6 -1
  363. package/src/DateTimeInput/nativePickerSegmentStyles.ts +24 -8
  364. package/src/Dialog/Dialog.doc.mjs +2 -0
  365. package/src/Dialog/DialogHeader.doc.mjs +22 -0
  366. package/src/Dialog/DialogHeader.test.tsx +167 -3
  367. package/src/Dialog/DialogHeader.tsx +37 -6
  368. package/src/Dialog/modules/DialogHeader.spec.md +101 -42
  369. package/src/DropdownMenu/DropdownMenu.test.tsx +5 -2
  370. package/src/Field/InputClearButton.test.tsx +17 -3
  371. package/src/Field/InputClearButton.tsx +5 -2
  372. package/src/Field/PanelSearchInput.tsx +5 -2
  373. package/src/FieldStatus/__tests__/StatusMessage.a11y.states.ts +1 -1
  374. package/src/FileInput/FileInput.tsx +11 -2
  375. package/src/Icon/Icon.doc.mjs +7 -5
  376. package/src/Icon/Icon.spec.md +81 -40
  377. package/src/Icon/Icon.test.tsx +6 -3
  378. package/src/Icon/Icon.tsx +20 -68
  379. package/src/Icon/IconDefaultSizeContext.ts +23 -0
  380. package/src/Icon/IconSize.stylex.ts +70 -0
  381. package/src/IconButton/IconButton.doc.mjs +3 -3
  382. package/src/IconButton/IconButton.test.tsx +40 -0
  383. package/src/Item/Item.doc.mjs +6 -2
  384. package/src/Item/Item.tsx +9 -3
  385. package/src/Kbd/Kbd.doc.mjs +3 -3
  386. package/src/Kbd/Kbd.test.tsx +57 -1
  387. package/src/Kbd/Kbd.tsx +57 -37
  388. package/src/Layout/LayoutFooter.doc.mjs +17 -0
  389. package/src/Link/Link.doc.mjs +17 -4
  390. package/src/Link/Link.test.tsx +24 -0
  391. package/src/Link/Link.tsx +5 -0
  392. package/src/Link/LinkProvider.doc.mjs +2 -2
  393. package/src/Link/__tests__/Link.navigation.a11y.chromium.spec.ts +261 -0
  394. package/src/Link/useLinkComponent.test.tsx +333 -0
  395. package/src/Link/useLinkComponent.ts +29 -57
  396. package/src/List/List.doc.mjs +6 -0
  397. package/src/List/List.spec.md +200 -0
  398. package/src/List/List.test.tsx +117 -1
  399. package/src/List/List.tsx +22 -2
  400. package/src/List/ListContext.tsx +3 -1
  401. package/src/List/ListItem.doc.mjs +2 -2
  402. package/src/List/ListItem.source-build.test.mjs +41 -0
  403. package/src/List/ListItem.tsx +26 -2
  404. package/src/Markdown/Markdown.doc.mjs +300 -2
  405. package/src/Markdown/Markdown.fr23.bench.ts +174 -0
  406. package/src/Markdown/Markdown.fr23.perf.test.ts +249 -0
  407. package/src/Markdown/Markdown.fr23.sampling.ts +49 -0
  408. package/src/Markdown/Markdown.helpers.perf.test.ts +489 -0
  409. package/src/Markdown/Markdown.public.test.ts +130 -3
  410. package/src/Markdown/Markdown.renderBoundary.test.tsx +72 -0
  411. package/src/Markdown/Markdown.spec.md +203 -45
  412. package/src/Markdown/Markdown.test.tsx +63 -0
  413. package/src/Markdown/Markdown.tsx +414 -135
  414. package/src/Markdown/ast.test.ts +307 -0
  415. package/src/Markdown/ast.ts +337 -0
  416. package/src/Markdown/incremental.test.ts +12 -0
  417. package/src/Markdown/index.ts +37 -1
  418. package/src/Markdown/modules/remark.spec.md +269 -0
  419. package/src/Markdown/modules/softBreaks.spec.md +137 -0
  420. package/src/Markdown/parser/index.ts +64 -0
  421. package/src/Markdown/parser.public.test.ts +103 -0
  422. package/src/Markdown/parser.test.ts +20 -0
  423. package/src/Markdown/parser.ts +1180 -235
  424. package/src/Markdown/plugins/frontmatter.test.tsx +125 -0
  425. package/src/Markdown/plugins/frontmatter.ts +198 -0
  426. package/src/Markdown/plugins/index.ts +54 -0
  427. package/src/Markdown/plugins/protocol.test.tsx +1022 -0
  428. package/src/Markdown/plugins/protocol.ts +1618 -0
  429. package/src/Markdown/plugins/semanticFence.test.tsx +546 -0
  430. package/src/Markdown/plugins/semanticFence.ts +424 -0
  431. package/src/Markdown/plugins/softBreaks.test.tsx +172 -0
  432. package/src/Markdown/plugins/softBreaks.ts +169 -0
  433. package/src/Markdown/plugins/sourceDecoration.test.tsx +799 -0
  434. package/src/Markdown/plugins/sourceDecoration.ts +539 -0
  435. package/src/Markdown/plugins/textTransform.test.ts +467 -0
  436. package/src/Markdown/plugins/textTransform.ts +554 -0
  437. package/src/Markdown/remark.test.tsx +1877 -0
  438. package/src/Markdown/remark.ts +1574 -0
  439. package/src/Markdown/url.ts +36 -0
  440. package/src/Markdown/utils.ts +13 -1
  441. package/src/MetadataList/MetadataList.test.tsx +4 -2
  442. package/src/MetadataList/MetadataList.tsx +3 -3
  443. package/src/MultiSelector/MultiSelector.test.tsx +1 -35
  444. package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +128 -0
  445. package/src/NumberInput/NumberInput.tsx +6 -1
  446. package/src/Outline/Outline.doc.mjs +7 -5
  447. package/src/Outline/Outline.spec.md +1 -1
  448. package/src/Outline/index.ts +1 -0
  449. package/src/Outline/modules/parseOutlineFromMarkdown.spec.md +142 -0
  450. package/src/Outline/parseOutlineFromMarkdown.ts +40 -10
  451. package/src/Outline/useOutlineFromMarkdown.ts +15 -3
  452. package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
  453. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +150 -1
  454. package/src/PowerSearch/PowerSearchEditPopover.tsx +51 -28
  455. package/src/PowerSearch/PowerSearchValueEditor.test.tsx +51 -0
  456. package/src/PowerSearch/PowerSearchValueEditor.tsx +4 -0
  457. package/src/ProgressBar/ProgressBar.test.tsx +29 -11
  458. package/src/ProgressBar/ProgressBar.tsx +59 -72
  459. package/src/RadioList/RadioList.doc.mjs +11 -0
  460. package/src/RadioList/RadioList.test.tsx +32 -0
  461. package/src/RadioList/RadioListItem.tsx +25 -1
  462. package/src/ScrollableArea/ScrollableArea.tsx +1 -0
  463. package/src/ScrollableArea/modules/useScrollableArea.spec.md +50 -22
  464. package/src/SegmentedControl/SegmentedControl.doc.mjs +2 -2
  465. package/src/SegmentedControl/SegmentedControl.test.tsx +31 -0
  466. package/src/SegmentedControl/SegmentedControlItem.tsx +6 -9
  467. package/src/Selector/Selector.doc.mjs +2 -2
  468. package/src/Selector/Selector.spec.md +51 -49
  469. package/src/Selector/Selector.test.tsx +61 -55
  470. package/src/Selector/Selector.tsx +34 -15
  471. package/src/Selector/__tests__/Listbox.a11y.chromium.spec.ts +130 -0
  472. package/src/Selector/__tests__/Listbox.a11y.dom.ts +38 -0
  473. package/src/Selector/__tests__/Listbox.a11y.inventory.test.ts +63 -0
  474. package/src/Selector/__tests__/Listbox.a11y.known-failures.ts +37 -0
  475. package/src/Selector/__tests__/Listbox.a11y.states.ts +232 -0
  476. package/src/Selector/__tests__/Selector.a11y.chromium.spec.ts +45 -0
  477. package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +121 -0
  478. package/src/SideNav/SideNav.doc.mjs +1 -1
  479. package/src/SideNav/SideNav.test.tsx +10 -0
  480. package/src/SideNav/SideNav.tsx +14 -2
  481. package/src/Slider/Slider.doc.mjs +17 -3
  482. package/src/Slider/Slider.test.tsx +221 -0
  483. package/src/Slider/Slider.tsx +40 -9
  484. package/src/Spinner/Spinner.test.tsx +4 -0
  485. package/src/Spinner/Spinner.tsx +31 -18
  486. package/src/Switch/Switch.doc.mjs +11 -0
  487. package/src/Switch/Switch.test.tsx +16 -0
  488. package/src/Switch/Switch.tsx +28 -0
  489. package/src/TabList/Tab.tsx +35 -7
  490. package/src/TabList/TabList.doc.mjs +11 -0
  491. package/src/TabList/TabList.spec.md +204 -0
  492. package/src/TabList/TabList.test.tsx +66 -0
  493. package/src/TabList/__tests__/Tabs.a11y.known-failures.ts +1 -34
  494. package/src/Table/Table.doc.mjs +1 -1
  495. package/src/Table/Table.spec.md +54 -40
  496. package/src/Table/Table.test.tsx +83 -10
  497. package/src/Table/Table.tsx +30 -22
  498. package/src/Table/__tests__/TableScroll.a11y.chromium.spec.ts +137 -0
  499. package/src/TextArea/TextArea.tsx +6 -1
  500. package/src/TextInput/TextInput.test.tsx +20 -1
  501. package/src/TextInput/TextInput.tsx +6 -1
  502. package/src/TimeInput/TimeInput.tsx +6 -1
  503. package/src/Timer/Timer.doc.mjs +183 -0
  504. package/src/Timer/Timer.spec.md +215 -0
  505. package/src/Timer/Timer.test.tsx +302 -0
  506. package/src/Timer/Timer.tsx +258 -0
  507. package/src/Timer/index.ts +11 -0
  508. package/src/ToggleButton/ToggleButton.doc.mjs +3 -3
  509. package/src/ToggleButton/ToggleButton.test.tsx +299 -4
  510. package/src/ToggleButton/ToggleButton.tsx +26 -33
  511. package/src/ToggleButton/ToggleButtonGroup.doc.mjs +3 -3
  512. package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +209 -0
  513. package/src/Token/Token.doc.mjs +2 -2
  514. package/src/Tokenizer/Tokenizer.spec.md +142 -75
  515. package/src/TopNav/TopNavMenu.doc.mjs +2 -2
  516. package/src/TopNav/TopNavMenu.test.tsx +30 -0
  517. package/src/TopNav/TopNavMenu.tsx +7 -2
  518. package/src/TreeList/TreeList.doc.mjs +3 -3
  519. package/src/TreeList/TreeList.spec.md +123 -75
  520. package/src/TreeList/TreeList.test.tsx +29 -0
  521. package/src/TreeList/TreeList.tsx +3 -0
  522. package/src/TreeList/TreeListItem.tsx +15 -1
  523. package/src/TreeList/TreeListTypes.ts +11 -1
  524. package/src/Typeahead/BaseTypeahead.spec.md +4 -3
  525. package/src/Typeahead/BaseTypeahead.tsx +21 -7
  526. package/src/Typeahead/Typeahead.test.tsx +53 -0
  527. package/src/__tests__/PressedState.a11y.chromium.spec.ts +813 -0
  528. package/src/__tests__/pressState.ts +93 -0
  529. package/src/hooks/scrollKeyboardDelegation.test.ts +155 -0
  530. package/src/hooks/scrollKeyboardDelegation.ts +233 -0
  531. package/src/hooks/useClickableContainer.test.tsx +236 -0
  532. package/src/hooks/useClickableContainer.ts +20 -4
  533. package/src/hooks/useScrollableArea.doc.mjs +15 -3
  534. package/src/hooks/useScrollableArea.test.tsx +59 -1
  535. package/src/hooks/useScrollableArea.ts +34 -10
  536. package/src/index.ts +3 -0
  537. package/src/inputFontFloor.test.ts +106 -0
  538. package/src/tailwind-theme.css +0 -6
  539. package/src/theme/Theme.test.tsx +32 -0
  540. package/src/theme/declarationBoundary.test.ts +126 -0
  541. package/src/theme/declarationBoundary.ts +347 -0
  542. package/src/theme/derivedVarRegistry.test.ts +7 -0
  543. package/src/theme/derivedVarRegistry.ts +1 -0
  544. package/src/theme/generateThemeRules.test.ts +526 -1
  545. package/src/theme/generateThemeRules.ts +228 -50
  546. package/src/theme/tokenValueCompat.test.ts +4 -1
  547. package/src/utils/interactionOverlay.stylex.ts +20 -0
  548. package/src/utils/safeUrl.test.ts +88 -0
  549. package/src/utils/safeUrl.ts +85 -0
@@ -23,6 +23,7 @@ import * as stylex from '@stylexjs/stylex';
23
23
  import type {StyleXStyles} from '@stylexjs/stylex';
24
24
  import type {BaseProps} from '../BaseProps';
25
25
  import {RadioListContext} from './RadioList';
26
+ import {colorVars, radiusVars} from '../theme/tokens.stylex';
26
27
  import {mergeProps, isRenderable, rtlStyles} from '../utils';
27
28
  import {indicatorScope} from '../Indicator/indicator.markers.stylex';
28
29
  import {useIndicatorFocusRing} from '../hooks/useIndicatorFocusRing';
@@ -39,6 +40,25 @@ const styles = stylex.create({
39
40
  flexShrink: 0,
40
41
  isolation: 'isolate',
41
42
  },
43
+ // The row remains the large press target, but nested links and buttons keep
44
+ // their independent action and do not make the radio appear pressed. This
45
+ // owner-drawn layer also survives a theme replacing the indicator component.
46
+ indicatorPressOverlay: {
47
+ '::after': {
48
+ content: '""',
49
+ position: 'absolute',
50
+ inset: 0,
51
+ borderRadius: radiusVars['--radius-full'],
52
+ pointerEvents: 'none',
53
+ backgroundColor: {
54
+ default: 'transparent',
55
+ [stylex.when.ancestor(
56
+ ':active:not(:has(a:active,button:active,[role="button"]:active,[role="link"]:active))',
57
+ indicatorScope,
58
+ )]: colorVars['--color-overlay-pressed'],
59
+ },
60
+ },
61
+ },
42
62
  input: {
43
63
  position: 'absolute',
44
64
  top: '50%',
@@ -222,7 +242,11 @@ export function RadioListItem({
222
242
 
223
243
  const radioCircle = (
224
244
  <div
225
- {...stylex.props(styles.radioWrapper, wrapperSizeStyles[size])}
245
+ {...stylex.props(
246
+ styles.radioWrapper,
247
+ wrapperSizeStyles[size],
248
+ !isDisabled && styles.indicatorPressOverlay,
249
+ )}
226
250
  {...focusProps}>
227
251
  <input
228
252
  ref={radioRef}
@@ -12,6 +12,7 @@
12
12
  * - /packages/core/src/ScrollableArea/ScrollableArea.test.tsx
13
13
  * - /packages/core/src/ScrollableArea/ScrollableArea.doc.mjs
14
14
  * - /apps/storybook/stories/ScrollableArea.stories.tsx
15
+ * - /packages/cli/assets/templates/blocks/components/ScrollableArea/ (showcase blocks)
15
16
  */
16
17
 
17
18
  import type {ReactNode} from 'react';
@@ -10,7 +10,12 @@ approved_by: cixzhang
10
10
  approved_at: 2026-09-11
11
11
  owners: [cixzhang]
12
12
  review_triggers: [public-api, behavior, accessibility]
13
- verified_by: [packages/core/src/hooks/useScrollableArea.test.tsx]
13
+ verified_by:
14
+ [
15
+ packages/core/src/hooks/useScrollableArea.test.tsx,
16
+ packages/core/src/hooks/scrollKeyboardDelegation.test.ts,
17
+ packages/core/src/BottomSheet/__tests__/BottomSheetKeyboard.a11y.browser.spec.ts,
18
+ ]
14
19
  parent_component: component:ScrollableArea
15
20
  references: [architecture:public-component-api, spec:AST-025/DEC-1]
16
21
  ---
@@ -47,24 +52,25 @@ and scroll chaining without inserting structure.
47
52
 
48
53
  ## Public API and concepts
49
54
 
50
- | Concept | Closed values or states | Meaning | Default | Owner | Stability |
51
- | -------------- | ---------------------------------- | ------------------------------------- | ------------------------- | ----------------------------------------- | --------- |
52
- | axis | `inline`, `block`, `both` | requested logical scroll intent | required | `spec:AST-025` | stable |
53
- | keyboard owner | `content`, named `viewport` | where keyboard scrolling is reached | required | `spec:AST-025` | stable |
54
- | overscroll | `allow`, `contain` | edge propagation on effective axes | `allow` | `spec:AST-025` | stable |
55
- | axis state | `isScrollable`, `atStart`, `atEnd` | effective ownership and logical edges | inactive, both edges true | `module:ScrollableArea/useScrollableArea` | stable |
55
+ | Concept | Closed values or states | Meaning | Default | Owner | Stability |
56
+ | -------------- | ------------------------------------------------------ | --------------------------------------- | ------------------------- | ----------------------------------------- | --------- |
57
+ | axis | `inline`, `block`, `both` | requested logical scroll intent | required | `spec:AST-025` | stable |
58
+ | keyboard owner | `content`, named `viewport`, named `contentOrViewport` | fixed or automatic keyboard scroll path | required | `spec:AST-025` | stable |
59
+ | overscroll | `allow`, `contain` | edge propagation on effective axes | `allow` | `spec:AST-025` | stable |
60
+ | axis state | `isScrollable`, `atStart`, `atEnd` | effective ownership and logical edges | inactive, both edges true | `module:ScrollableArea/useScrollableArea` | stable |
56
61
 
57
62
  ## Behavioral contract
58
63
 
59
- | ID | Invariant | Basis | Acceptance and implementation state |
60
- | --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- | --------------------------------------- |
61
- | FR1 | An axis MUST be effective only when requested, computed overflow is scroll-capable, and geometry exceeds 1px. | `spec:AST-025` FR1–FR4 | implemented |
62
- | FR2 | Viewport and content geometry MUST be observed; invalidations MUST coalesce and preserve the last valid state while unmeasurable. | `spec:AST-025` FR5–FR9 | implemented |
63
- | FR3 | Logical edges and behavior-owned physical overflow MUST map through direction and horizontal, vertical, or sideways writing modes without requiring adopters to recreate the mapping. | `spec:AST-025` FR1, FR3–FR4 | implemented |
64
- | FR4 | The nearest registered effective owner MUST win independently by axis. | `spec:AST-025` FR10, FR18 | implemented privately for future Sticky |
65
- | FR5 | Viewport keyboard props and overscroll containment MUST appear only while applicable requested axes are effective. | `spec:AST-025` FR12–FR14 | implemented |
66
- | FR6 | Prop getters MUST compose refs and preserve caller handlers/classes/styles/ARIA while behavior-owned accessibility and non-cancellable behavior win conflicts. | `spec:AST-025` IR4 | implemented |
67
- | FR7 | The viewport prop getter MUST consume caller `xstyle` and internally compose geometry-driven `clip`/`auto`/`hidden` overflow plus explicit fitting Sticky containment. | `spec:AST-025` FR21 | implemented |
64
+ | ID | Invariant | Basis | Acceptance and implementation state |
65
+ | --- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- | --------------------------------------- |
66
+ | FR1 | An axis MUST be effective only when requested, computed overflow is scroll-capable, and geometry exceeds 1px. | `spec:AST-025` FR1–FR4 | implemented |
67
+ | FR2 | Viewport and content geometry MUST be observed; invalidations MUST coalesce and preserve the last valid state while unmeasurable. | `spec:AST-025` FR5–FR9 | implemented |
68
+ | FR3 | Logical edges and behavior-owned physical overflow MUST map through direction and horizontal, vertical, or sideways writing modes without requiring adopters to recreate the mapping. | `spec:AST-025` FR1, FR3–FR4 | implemented |
69
+ | FR4 | The nearest registered effective owner MUST win independently by axis. | `spec:AST-025` FR10, FR18 | implemented privately for future Sticky |
70
+ | FR5 | Viewport keyboard props and overscroll containment MUST appear only while applicable requested axes are effective. | `spec:AST-025` FR12–FR14 | implemented |
71
+ | FR6 | Prop getters MUST compose refs and preserve caller handlers/classes/styles/ARIA while behavior-owned accessibility and non-cancellable behavior win conflicts. | `spec:AST-025` IR4 | implemented |
72
+ | FR7 | The viewport prop getter MUST consume caller `xstyle` and internally compose geometry-driven `clip`/`auto`/`hidden` overflow plus explicit fitting Sticky containment. | `spec:AST-025` FR21 | implemented |
73
+ | FR8 | `contentOrViewport` MUST retain the named overflowing viewport, inspect the first sequential descendant on forward Tab entry, and delegate only to a native link/button outside navigation-key-owning surfaces. Reverse traversal from the delegated first child skips the viewport. Pointer/programmatic focus never delegates; eligibility is checked again on the next entry. | `spec:AST-025` FR12–FR13 | implemented |
68
74
 
69
75
  ### Transformation and precedence order
70
76
 
@@ -82,9 +88,20 @@ and scroll chaining without inserting structure.
82
88
 
83
89
  ## Accessibility contract
84
90
 
85
- - **AR1 — Named viewport owner.** Viewport-owned access requires a label and adds
86
- the viewport to the tab order only while an applicable axis is effective.
87
- - **AR2 — Focus continuity.** State changes never move or blur focus.
91
+ - **AR1 — Named viewport owner.** Fixed viewport and automatic access require a
92
+ label and retain `tabIndex=0` while effectively scrollable. Automatic access
93
+ delegates only on forward keyboard entry; the descendant retains its own name
94
+ inside the named viewport group/region.
95
+ - **AR2 — Focus continuity.** Content, eligibility, and geometry changes never
96
+ move or blur focus. A viewport losing overflow while focused remains
97
+ programmatically focusable. Reverse traversal from the delegated first child
98
+ skips the viewport, including after visiting later descendants.
99
+ - **AR3 — Conservative eligibility.** Inspect the first sequential target; do not
100
+ search past an excluded one for a link/button. Composite roles, editable
101
+ surfaces, native or registered nested scroll owners, positive tab ordering,
102
+ popup triggers, and unproven interactive roles retain the viewport path.
103
+ - **AR4 — Native scrolling.** Delegation MUST preserve Arrow/Page access to both
104
+ scroll edges without implementing JavaScript scrolling.
88
105
 
89
106
  ## Design relationships
90
107
 
@@ -97,9 +114,9 @@ No visual representation is owned by this hook.
97
114
 
98
115
  ## Verification map
99
116
 
100
- | Contract | Verification | Representative states | Mutation or failure expectation |
101
- | ---------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
102
- | FR1–FR6, AR1–AR2 | `useScrollableArea.test.tsx` plus real-browser Storybook probe | horizontal/vertical/sideways, LTR/RTL, fit/overflow/edge, nested owners, focused loss | style-only or geometry-only ownership, physical-edge leakage, stale state, ref loss, or dead containment fails |
117
+ | Contract | Verification | Representative states | Mutation or failure expectation |
118
+ | ---------------- | ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
119
+ | FR1–FR8, AR1–AR4 | `useScrollableArea.test.tsx`, `scrollKeyboardDelegation.test.ts`, and `BottomSheetKeyboard.a11y.browser.spec.ts` | horizontal/vertical/sideways, LTR/RTL, fit/overflow/edge, fixed/automatic keyboard owners, dynamic descendant eligibility, nested owners, focused loss | style-only or geometry-only ownership, physical-edge leakage, duplicate/missing keyboard path, stale state, ref loss, or dead containment fails |
103
120
 
104
121
  ## Decision log
105
122
 
@@ -111,6 +128,17 @@ No visual representation is owned by this hook.
111
128
  One returned prop object per owned element keeps refs, accessibility, handlers,
112
129
  and behavior together without making consumer spread order part of correctness.
113
130
 
131
+ ### DEC-2 — Automatic ownership is shared behavior
132
+
133
+ **Reference:** `module:ScrollableArea/useScrollableArea/DEC-2`
134
+ **Decider:** cixzhang, 2026-09-13
135
+
136
+ `contentOrViewport` retains the named overflowing viewport and delegates only on
137
+ forward Tab entry to a safe first sequential native link/button. This replaces
138
+ continuous keyboard-eligibility observation. Geometry observation is unchanged;
139
+ keyboard eligibility adds no subtree or ancestor observers and is inspected anew
140
+ at each entry. BottomSheet is a proving composition of this shared behavior.
141
+
114
142
  ## Open questions
115
143
 
116
144
  None.
@@ -129,9 +129,9 @@ export const docs = {
129
129
  category: 'Color contrast',
130
130
  criterion: '1.4.3 Contrast (Minimum)',
131
131
  requirement: '4.5:1',
132
- states: ['Rest', 'Hover', 'Selected'],
132
+ states: ['Rest', 'Hover', 'Pointer down', 'Selected'],
133
133
  description:
134
- 'Each label must have at least 4.5:1 contrast with its segment background. Check unselected, Hover, and selected colors as they appear on screen.',
134
+ 'Each label must have at least 4.5:1 contrast with its segment background. Check unselected, Hover, Pointer down, and selected colors as they appear on screen. For Hover and Pointer down, measure the final background after the overlay is applied.',
135
135
  },
136
136
  {
137
137
  name: 'Essential icon',
@@ -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 {SegmentedControl} from './SegmentedControl';
19
20
  import {SegmentedControlItem} from './SegmentedControlItem';
20
21
  import {
@@ -672,3 +673,33 @@ describe('forced colors (WCAG 1.4.11)', () => {
672
673
  expect(getAllInjectedCss()).toContain('forced-color-adjust: none;');
673
674
  });
674
675
  });
676
+
677
+ describe('pressed state', () => {
678
+ it('paints the pressed overlay on a segment while it is pressed', () => {
679
+ render(
680
+ <SegmentedControl value="grid" onChange={() => {}} label="View mode">
681
+ <SegmentedControlItem value="grid" label="Grid" />
682
+ <SegmentedControlItem value="list" label="List" />
683
+ </SegmentedControl>,
684
+ );
685
+ // The unselected segment is the one a press can change; it carries the
686
+ // system's hover and pressed overlay.
687
+ expect(hasPressedArm(screen.getByRole('radio', {name: 'List'}))).toBe(true);
688
+ // The selected segment keeps its raised surface as it is.
689
+ expect(hasPressedArm(screen.getByRole('radio', {name: 'Grid'}))).toBe(
690
+ false,
691
+ );
692
+ });
693
+
694
+ it('does not press a disabled segment', () => {
695
+ render(
696
+ <SegmentedControl value="grid" onChange={() => {}} label="View mode">
697
+ <SegmentedControlItem value="grid" label="Grid" />
698
+ <SegmentedControlItem value="list" label="List" isDisabled />
699
+ </SegmentedControl>,
700
+ );
701
+ expect(hasPressedArm(screen.getByRole('radio', {name: 'List'}))).toBe(
702
+ false,
703
+ );
704
+ });
705
+ });
@@ -33,6 +33,7 @@ import {mergeProps, composeEventHandlers} from '../utils';
33
33
  import type {BaseProps} from '../BaseProps';
34
34
  import {themeProps} from '../utils/themeProps';
35
35
  import {focusOutlineProps} from '../utils/focusOutline.stylex';
36
+ import {interactionOverlayStyles} from '../utils/interactionOverlay.stylex';
36
37
 
37
38
  export interface SegmentedControlItemProps extends BaseProps<HTMLButtonElement> {
38
39
  ref?: React.Ref<HTMLButtonElement>;
@@ -91,14 +92,6 @@ const styles = stylex.create({
91
92
  transitionDuration: durationVars['--duration-fast'],
92
93
  transitionTimingFunction: easeVars['--ease-standard'],
93
94
  },
94
- hover: {
95
- backgroundColor: {
96
- default: null,
97
- ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
98
- '@media (hover: hover)': colorVars['--color-overlay-hover'],
99
- },
100
- },
101
- },
102
95
  selected: {
103
96
  // Forced colors (Windows High Contrast) strips the painted surface fill
104
97
  // and box shadow, which would leave the selected segment with no state
@@ -248,7 +241,11 @@ export function SegmentedControlItem({
248
241
  sizeStyles[size],
249
242
  isFill && styles.fill,
250
243
  isSelected && styles.selected,
251
- !isSelected && !isItemDisabled && styles.hover,
244
+ // The shared hover and pressed overlay, on the segments a press can
245
+ // change: the selected segment keeps its raised surface as it is.
246
+ !isSelected &&
247
+ !isItemDisabled &&
248
+ interactionOverlayStyles.backgroundColor,
252
249
  isItemDisabled && styles.disabled,
253
250
  xstyle,
254
251
  ),
@@ -83,7 +83,7 @@ const anatomy = [
83
83
  name: 'Option selection indicator',
84
84
  required: false,
85
85
  description:
86
- 'Resolved selection mark rendered for each option in its checked or unchecked state.',
86
+ 'Resolved selection mark rendered for each option in its checked or unchecked state. Its layout space collapses when the resolved indicator draws nothing.',
87
87
  },
88
88
  {
89
89
  name: 'Option divider',
@@ -330,7 +330,7 @@ export const docs = {
330
330
  name: 'indicatorPosition',
331
331
  type: "'start' | 'end'",
332
332
  description:
333
- 'Which edge of the option row carries the selected mark. start reserves a mark column ahead of every label so they stay aligned, the way a native menu does; end is the house convention shared with Typeahead and CommandPalette.',
333
+ 'Which logical edge of the option row carries a rendered selection mark. An empty mark consumes no space, so selected and unselected labels may shift or have different available width. end is the house convention shared with Typeahead and CommandPalette.',
334
334
  default: "'end'",
335
335
  },
336
336
  {
@@ -14,6 +14,10 @@ verified_by:
14
14
  [
15
15
  packages/core/src/Selector/Selector.test.tsx,
16
16
  packages/core/src/Selector/Selector.source-build.test.mjs,
17
+ packages/core/src/Selector/__tests__/Selector.listbox.a11y.test.tsx,
18
+ packages/core/src/Selector/__tests__/Listbox.a11y.chromium.spec.ts,
19
+ apps/storybook/stories/Selector.stories.tsx,
20
+ .github/scripts/story-play-guard.js,
17
21
  ]
18
22
  modules: []
19
23
  families: [family:input-fields]
@@ -42,8 +46,8 @@ connection between the closed trigger and its selection surface.
42
46
  ## Compatibility and migration
43
47
 
44
48
  - Released defaults and behavior remain unchanged by this record.
45
- - `indicatorPosition` defaults to `end`; every option row currently reserves the
46
- indicator column at the configured logical edge.
49
+ - `indicatorPosition` defaults to `end`; a rendered selection mark occupies
50
+ space at that logical edge, while an empty resolved indicator occupies none.
47
51
  - `presentation` defaults to `popover`. `bottom-sheet` is an explicit modal
48
52
  presentation, and `adaptive` selects it on compact coarse-pointer screens.
49
53
  - `hasClear` changes the value contract to include `null`; that distinction is
@@ -51,9 +55,7 @@ connection between the closed trigger and its selection surface.
51
55
  - `isReadOnly` is additive and defaults to `false`. It preserves the selected
52
56
  value, focus, and form participation while removing selection-surface and
53
57
  editing affordances. `isDisabled` takes precedence when both are set.
54
- - `spec:AST-004/DEC-1` accepts a future change that collapses an empty indicator
55
- column. It is not shipped behavior and does not replace FR3 until implementation,
56
- visual verification, and an update to this current contract land together.
58
+ - `spec:AST-004/DEC-1` governs the state-derived indicator-space behavior in FR3.
57
59
 
58
60
  ## Ownership boundary
59
61
 
@@ -84,7 +86,7 @@ and examples remain in `Selector.doc.mjs`.
84
86
  | ---------------------- | -------------------------------------- | -------------------------------------------------------- | ----------------------------------------- | --------- | -------- | --------- | ------------------------------- |
85
87
  | trigger variant | `input`, `ghost` | Form-field or toolbar presentation | All trigger states | `input` | Selector | released | TypeScript rejects other values |
86
88
  | size | `sm`, `md`, `lg` | Trigger and option-row density | All presentations | `md` | Selector | released | TypeScript rejects other values |
87
- | selected-mark position | `start`, `end` | Logical edge containing the reserved selection column | Every option row | `end` | Selector | released | TypeScript rejects other values |
89
+ | selected-mark position | `start`, `end` | Logical edge containing a rendered selection mark | Every option row | `end` | Selector | released | TypeScript rejects other values |
88
90
  | presentation | `popover`, `bottom-sheet`, `adaptive` | Anchored pointer surface or modal compact-touch surface | All trigger variants | `popover` | Selector | released | TypeScript rejects other values |
89
91
  | popup semantics | `listbox`; modal dialog containing one | Semantics follow the active presentation | Popover; bottom sheet | `listbox` | Selector | released | No separate role prop is public |
90
92
  | option-row state | `selected`, `disabled` | Stable theming state on each option row | Every rendered option | neither | Selector | released | Unknown states are not emitted |
@@ -94,20 +96,20 @@ and examples remain in `Selector.doc.mjs`.
94
96
 
95
97
  These requirements describe shipped behavior on current `main`.
96
98
 
97
- | ID | Shipped invariant | Evidence |
98
- | --- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
99
- | FR1 | Selecting an enabled option updates the one selected value, closes the active presentation, and returns the trigger to its stable closed state. | Consumer docs and selection interaction tests |
100
- | FR2 | Without explicit placement, a non-search popover aligns the selected row over the trigger and clamps it to the viewport. Search popovers and explicit placement use normal Layer positioning. | Consumer docs, implementation, and geometry tests |
101
- | FR3 | Every option row reserves one selection-mark column at `indicatorPosition`, even when the default unchecked indicator draws nothing. The column has a minimum width and can grow for a larger themed replacement. | `itemMarkColumn`, indicator-position tests, and theme tests |
102
- | FR4 | `popover` uses an anchored Popover. `bottom-sheet` uses a modal BottomSheet. `adaptive` resolves to the modal bottom sheet on compact coarse-pointer screens and Popover otherwise. | Presentation controller and adaptive-presentation tests |
103
- | FR5 | While `isLoading` is true, the trigger exposes busy state and the listbox suppresses empty and no-results output. | Loading, empty-state, and announcement tests |
104
- | FR6 | While `isReadOnly` is true, the selected value remains focusable and form-submittable, while the selection surface, clear action, disclosure indicator, and every value-change path are unavailable. | Read-only interaction, form, and accessibility tests |
99
+ | ID | Shipped invariant | Evidence |
100
+ | --- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
101
+ | FR1 | Selecting an enabled option updates the one selected value, closes the active presentation, and returns the trigger to its stable closed state. | Consumer docs and selection interaction tests |
102
+ | FR2 | Without explicit placement, a non-search popover aligns the selected row over the trigger and clamps it to the viewport. Search popovers and explicit placement use normal Layer positioning. | Consumer docs, implementation, and geometry tests |
103
+ | FR3 | An option whose resolved selection indicator draws no content reserves no mark-column space. Visible selected or themed replacement indicators remain in layout at `indicatorPosition`; the resulting state-dependent label position or available width is intentional, and row content keeps its existing overflow behavior. | `itemMarkColumn`, focused indicator tests, and Chromium evidence stories |
104
+ | FR4 | `popover` uses an anchored Popover. `bottom-sheet` uses a modal BottomSheet. `adaptive` resolves to the modal bottom sheet on compact coarse-pointer screens and Popover otherwise. | Presentation controller and adaptive-presentation tests |
105
+ | FR5 | While `isLoading` is true, the trigger exposes busy state and the listbox suppresses empty and no-results output. | Loading, empty-state, and announcement tests |
106
+ | FR6 | While `isReadOnly` is true, the selected value remains focusable and form-submittable, while the selection surface, clear action, disclosure indicator, and every value-change path are unavailable. | Read-only interaction, form, and accessibility tests |
105
107
 
106
108
  ### Allowed variation
107
109
 
108
- - **AV1 — Indicator rendering.** A theme may replace the check indicator. The
109
- replacement may draw in both selected and unselected states; the current row
110
- still reserves its indicator column.
110
+ - **AV1 — Indicator rendering.** A theme may replace the check indicator. A
111
+ replacement that draws in both selected and unselected states keeps its mark
112
+ space in both states; only resolved empty output collapses.
111
113
  - **AV2 — Option content.** `renderOption` may replace visible option content,
112
114
  while Selector keeps row role, selection, disabled state, navigation, and
113
115
  theming state.
@@ -116,17 +118,17 @@ These requirements describe shipped behavior on current `main`.
116
118
 
117
119
  ### Representative states
118
120
 
119
- | State | Required invariant | Allowed variation |
120
- | ------------------------ | ----------------------------------------------------------------------------------------- | ------------------------------------------------------ |
121
- | closed with no value | Label and placeholder identify the field | Consumer placeholder text |
122
- | closed with a value | Selected option is represented in the trigger | Custom `renderValue` content |
123
- | pointer / popover | Anchored surface exposes the listbox without modal-dialog semantics | Default or explicit placement |
124
- | compact coarse pointer | BottomSheet exposes a modal dialog containing the listbox | Explicit `bottom-sheet` or resolved `adaptive` |
125
- | searching | Visible options, keyboard navigation, and announced result count use one filter | Consumer search and empty text |
126
- | loading | Trigger is busy; empty and no-results output is suppressed | Consumer loading duration |
127
- | disabled with reason | Trigger remains focusable enough to expose the reason while activation stays blocked | Consumer reason text |
128
- | read-only | Value stays focusable and submittable; menu, clear, and disclosure affordances are absent | Value rendering, status, and busy presentation |
129
- | selected/unselected rows | Row semantics and theming state are correct; both reserve the indicator column | Start/end position and themed indicator representation |
121
+ | State | Required invariant | Allowed variation |
122
+ | ------------------------ | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
123
+ | closed with no value | Label and placeholder identify the field | Consumer placeholder text |
124
+ | closed with a value | Selected option is represented in the trigger | Custom `renderValue` content |
125
+ | pointer / popover | Anchored surface exposes the listbox without modal-dialog semantics | Default or explicit placement |
126
+ | compact coarse pointer | BottomSheet exposes a modal dialog containing the listbox | Explicit `bottom-sheet` or resolved `adaptive` |
127
+ | searching | Visible options, keyboard navigation, and announced result count use one filter | Consumer search and empty text |
128
+ | loading | Trigger is busy; empty and no-results output is suppressed | Consumer loading duration |
129
+ | disabled with reason | Trigger remains focusable enough to expose the reason while activation stays blocked | Consumer reason text |
130
+ | read-only | Value stays focusable and submittable; menu, clear, and disclosure affordances are absent | Value rendering, status, and busy presentation |
131
+ | selected/unselected rows | Empty marks consume no width; visible marks retain space at the logical edge | State-dependent label position/width and themed indicators |
130
132
 
131
133
  ### Transformation and precedence order
132
134
 
@@ -174,12 +176,12 @@ These requirements describe shipped behavior on current `main`.
174
176
 
175
177
  No current design spec is linked.
176
178
 
177
- | Anatomy or state | Current representation requirement | Representation authority | Hierarchy role | Component contract |
178
- | -------------------------------- | --------------------------------------------------------------------------------------- | -------------------------- | ----------------------- | ------------------ |
179
- | selected mark and option spacing | Every row reserves the indicator column, preserving label alignment and available width | existing shipped behavior | supporting | FR3 |
180
- | input versus ghost trigger | none recorded | existing released behavior | form or toolbar control | Public concepts |
179
+ | Anatomy or state | Current representation requirement | Representation authority | Hierarchy role | Component contract |
180
+ | -------------------------------- | -------------------------------------------------------------------------------------------------- | -------------------------- | ----------------------- | ------------------ |
181
+ | selected mark and option spacing | Empty resolved marks collapse; visible marks retain space, so row label geometry may vary by state | `spec:AST-004/DEC-1` | supporting | FR3 |
182
+ | input versus ghost trigger | none recorded | existing released behavior | form or toolbar control | Public concepts |
181
183
 
182
- The approved but unimplemented replacement for the first row is owned by
184
+ The first row implements the state-derived spacing decision in
183
185
  `spec:AST-004/DEC-1`.
184
186
 
185
187
  ### Theming anatomy
@@ -255,16 +257,15 @@ content stays outside that target, while the targeted option row remains.
255
257
  current anatomy row.
256
258
 
257
259
  This map records only shipped reachability. It does not treat the accepted,
258
- unimplemented option-source behavior in `spec:AST-001` or indicator-space change
259
- in `spec:AST-004` as current runtime behavior.
260
+ unimplemented option-source behavior in `spec:AST-001` as current runtime behavior.
261
+ `spec:AST-004` governs FR3's shipped indicator-space behavior without adding a
262
+ new theming target.
260
263
 
261
264
  ## Family and system relationships
262
265
 
263
266
  - The current architecture links in frontmatter own public API, theming, icon
264
267
  resolution, interaction modality, and Layer behavior used by Selector.
265
- - `spec:AST-004/DEC-1` owns the accepted future indicator-space change. Its
266
- `accepted` phase is direction for implementation, not evidence that the runtime
267
- has changed.
268
+ - `spec:AST-004/DEC-1` owns FR3's shipped indicator-space behavior.
268
269
  - `spec:AST-011/DEC-1` owns the additive read-only state: caller-owned policy can
269
270
  preserve a focusable, submittable value without exposing selection controls.
270
271
  - `family:input-fields` owns family-wide state display, behavior, appearance,
@@ -274,20 +275,21 @@ in `spec:AST-004` as current runtime behavior.
274
275
 
275
276
  ## Verification map
276
277
 
277
- | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
278
- | --------------------- | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | -------------------------------- |
279
- | FR1, AR2, AR3 | `Selector.test.tsx` selection, focus, and keyboard suites | closed/open, search/non-search, disabled option | Removing selection wiring, focus movement, or keyboard behavior fails the named interaction tests | `audit:Selector/behavior` |
280
- | FR2 | placement and selected-item geometry tests | default, explicit, RTL, transformed entry | Using the wrong positioning model fails the expected position-area or margin | `audit:Selector/behavior` |
281
- | FR3, AV1 | `itemMarkColumn` source inspection plus indicator-position and replacement-indicator tests | start/end, selected/unselected, check/radio | Removing the wrapper fails row-structure tests; changing its reserved width requires source/layout review | `audit:Selector/design-rendered` |
282
- | FR4, AR1, AR2 | presentation tests plus `aria-haspopup` source review | pointer, compact coarse pointer, search/non-search | Wrong Popover/dialog roles or focus destinations fail tests; `aria-haspopup` values require source/a11y review | `audit:Selector/accessibility` |
283
- | FR5 | loading, empty-state, and live-region tests | empty options, unmatched search, loading | Empty/no-results output appears or is announced while loading | `audit:Selector/behavior` |
284
- | FR6, AR4 | read-only interaction, form, ARIA, and theme-state tests | search/non-search, clearable, open→read-only, disabled precedence | A value changes, popup or edit affordance remains, form value disappears, or read-only semantics are absent | `audit:Selector/accessibility` |
285
- | source-build contract | `Selector.source-build.test.mjs` | package source compiled by consumer Babel | Moving evaluated StyleX values outside the supported source form fails compilation | `audit:Selector/code-health` |
278
+ | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
279
+ | --------------------- | -------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- |
280
+ | FR1, AR2, AR3 | `Selector.test.tsx` selection, focus, and keyboard suites | closed/open, search/non-search, disabled option | Removing selection wiring, focus movement, or keyboard behavior fails the named interaction tests | `audit:Selector/behavior` |
281
+ | FR2 | placement and selected-item geometry tests | default, explicit, RTL, transformed entry | Using the wrong positioning model fails the expected position-area or margin | `audit:Selector/behavior` |
282
+ | FR3, AV1 | Focused `Selector.test.tsx` computed-display checks plus guarded Chromium Storybook matrix and stable visual stories | popover/bottom sheet, narrow/wide, start/end, LTR/RTL, selected/unselected, check/radio | Empty output reserves width, visible output collapses, logical placement flips incorrectly, or row content overflows | `audit:Selector/design-rendered` |
283
+ | FR4, AR1, AR2 | presentation tests plus `aria-haspopup` source review | pointer, compact coarse pointer, search/non-search | Wrong Popover/dialog roles or focus destinations fail tests; `aria-haspopup` values require source/a11y review | `audit:Selector/accessibility` |
284
+ | AR1, AV2 | `Selector.listbox.a11y.test.tsx` and `Listbox.a11y.chromium.spec.ts` against the shared Listbox contract | popover/sheet, flat/grouped, selected/unselected, disabled, search, loading, custom RTL | A new or wider loss of role, listbox/option name, state, or ownership gates; the exact unnamed no-search sheet state remains known debt | `audit:Selector/accessibility` |
285
+ | FR5 | loading, empty-state, and live-region tests | empty options, unmatched search, loading | Empty/no-results output appears or is announced while loading | `audit:Selector/behavior` |
286
+ | FR6, AR4 | read-only interaction, form, ARIA, and theme-state tests | search/non-search, clearable, open→read-only, disabled precedence | A value changes, popup or edit affordance remains, form value disappears, or read-only semantics are absent | `audit:Selector/accessibility` |
287
+ | source-build contract | `Selector.source-build.test.mjs` | package source compiled by consumer Babel | Moving evaluated StyleX values outside the supported source form fails compilation | `audit:Selector/code-health` |
286
288
 
287
289
  ## Decision log
288
290
 
289
- No component-local future decision is recorded here. Accepted unimplemented work
290
- is owned by `spec:AST-004`.
291
+ No component-local future decision is recorded here. FR3 implements the
292
+ system decision owned by `spec:AST-004/DEC-1`.
291
293
 
292
294
  ## Open questions
293
295
 
@@ -3728,67 +3728,73 @@ describe('Selector indicatorPosition', () => {
3728
3728
  ).toBeTruthy();
3729
3729
  });
3730
3730
 
3731
- it('reserves the mark column on every row, at either position', () => {
3732
- // The default check draws nothing when unchecked, so without a reserved
3733
- // column the chosen row would be laid out differently from the rest —
3734
- // indented at the start, truncating earlier at the end. Every row is two
3735
- // children wide either way, so a row's geometry does not depend on whether
3736
- // it happens to be the chosen one.
3737
- const {unmount} = render(
3738
- <Selector
3739
- label="Fruit"
3740
- options={OPTIONS}
3741
- value="Banana"
3742
- onChange={() => {}}
3743
- indicatorPosition="start"
3744
- isDefaultOpen
3745
- />,
3746
- );
3747
- for (const row of openRows()) {
3748
- expect(row.children).toHaveLength(2);
3749
- }
3750
- unmount();
3751
-
3752
- render(
3753
- <Selector
3754
- label="Fruit"
3755
- options={OPTIONS}
3756
- value="Banana"
3757
- onChange={() => {}}
3758
- isDefaultOpen
3759
- />,
3760
- );
3761
- for (const row of openRows()) {
3762
- expect(row.children).toHaveLength(2);
3763
- }
3764
- });
3765
-
3766
- it('positions a themed replacement indicator the same way', () => {
3767
- const theme = defineTheme({
3768
- name: 'selector-start-radio-mark-test',
3769
- indicators: {check: RadioIndicator},
3770
- });
3771
- render(
3772
- <Theme theme={theme}>
3731
+ it.each(['start', 'end'] as const)(
3732
+ 'collapses an empty default mark at the logical %s edge',
3733
+ indicatorPosition => {
3734
+ render(
3773
3735
  <Selector
3774
3736
  label="Fruit"
3775
3737
  options={OPTIONS}
3776
3738
  value="Banana"
3777
3739
  onChange={() => {}}
3778
- indicatorPosition="start"
3740
+ indicatorPosition={indicatorPosition}
3779
3741
  isDefaultOpen
3780
- />
3781
- </Theme>,
3782
- );
3783
- for (const row of openRows()) {
3784
- const radio = row.querySelector('.astryx-radio')!;
3785
- const content = row.querySelector('.astryx-selector-option')!;
3786
- expect(
3787
- content.compareDocumentPosition(radio) &
3788
- Node.DOCUMENT_POSITION_PRECEDING,
3789
- ).toBeTruthy();
3790
- }
3791
- });
3742
+ />,
3743
+ );
3744
+
3745
+ for (const row of openRows()) {
3746
+ const isSelected = row.getAttribute('aria-selected') === 'true';
3747
+ const markColumn =
3748
+ indicatorPosition === 'start'
3749
+ ? row.firstElementChild
3750
+ : row.lastElementChild;
3751
+ expect(markColumn).not.toBeNull();
3752
+ expect(markColumn).toHaveStyle({
3753
+ display: isSelected ? 'inline-flex' : 'none',
3754
+ });
3755
+ }
3756
+ },
3757
+ );
3758
+
3759
+ it.each(['start', 'end'] as const)(
3760
+ 'keeps a themed indicator in layout at the logical %s edge',
3761
+ indicatorPosition => {
3762
+ const theme = defineTheme({
3763
+ name: `selector-${indicatorPosition}-radio-mark-test`,
3764
+ indicators: {check: RadioIndicator},
3765
+ });
3766
+ render(
3767
+ <Theme theme={theme}>
3768
+ <Selector
3769
+ label="Fruit"
3770
+ options={OPTIONS}
3771
+ value="Banana"
3772
+ onChange={() => {}}
3773
+ indicatorPosition={indicatorPosition}
3774
+ isDefaultOpen
3775
+ />
3776
+ </Theme>,
3777
+ );
3778
+
3779
+ for (const row of openRows()) {
3780
+ const markColumn =
3781
+ indicatorPosition === 'start'
3782
+ ? row.firstElementChild
3783
+ : row.lastElementChild;
3784
+ const radio = row.querySelector('.astryx-radio');
3785
+ const content = row.querySelector('.astryx-selector-option');
3786
+ expect(markColumn).toHaveStyle({display: 'inline-flex'});
3787
+ expect(radio).not.toBeNull();
3788
+ expect(content).not.toBeNull();
3789
+ expect(
3790
+ content!.compareDocumentPosition(radio!) &
3791
+ (indicatorPosition === 'start'
3792
+ ? Node.DOCUMENT_POSITION_PRECEDING
3793
+ : Node.DOCUMENT_POSITION_FOLLOWING),
3794
+ ).toBeTruthy();
3795
+ }
3796
+ },
3797
+ );
3792
3798
  });
3793
3799
 
3794
3800
  describe('Selector popup theme target', () => {