@astryxdesign/core 0.6.2 → 0.6.3-canary.039a67e

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 (621) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/dist/Avatar/Avatar.d.ts.map +1 -1
  3. package/dist/Avatar/Avatar.js +33 -12
  4. package/dist/BottomSheet/BottomSheet.d.ts +1 -1
  5. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  6. package/dist/BottomSheet/BottomSheet.js +3 -1
  7. package/dist/BottomSheet/BottomSheetPanel.d.ts +7 -5
  8. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  9. package/dist/BottomSheet/BottomSheetPanel.js +56 -19
  10. package/dist/Button/Button.d.ts.map +1 -1
  11. package/dist/Button/Button.js +13 -28
  12. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  13. package/dist/Chat/ChatComposer.js +10 -5
  14. package/dist/Chat/ChatComposerDrawer.d.ts +19 -5
  15. package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
  16. package/dist/Chat/ChatComposerDrawer.js +48 -23
  17. package/dist/Chat/ChatComposerInput.d.ts +11 -3
  18. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  19. package/dist/Chat/ChatComposerInput.js +63 -16
  20. package/dist/Chat/ChatDictationButton.d.ts +3 -2
  21. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  22. package/dist/Chat/ChatDictationButton.js +6 -4
  23. package/dist/Chat/ChatLayout.d.ts +19 -0
  24. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  25. package/dist/Chat/ChatLayout.js +19 -0
  26. package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
  27. package/dist/Chat/ChatLayoutScrollButton.js +44 -34
  28. package/dist/Chat/ChatMessageBubble.d.ts +2 -0
  29. package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
  30. package/dist/Chat/ChatMessageBubble.js +5 -3
  31. package/dist/Chat/ChatMessageList.d.ts +2 -1
  32. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  33. package/dist/Chat/ChatMessageList.js +4 -3
  34. package/dist/Chat/ChatMessageMetadata.d.ts +5 -4
  35. package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
  36. package/dist/Chat/ChatMessageMetadata.js +12 -9
  37. package/dist/Chat/ChatSendButton.d.ts +2 -1
  38. package/dist/Chat/ChatSendButton.d.ts.map +1 -1
  39. package/dist/Chat/ChatSendButton.js +6 -4
  40. package/dist/Chat/ChatSystemMessage.d.ts +3 -3
  41. package/dist/Chat/ChatSystemMessage.d.ts.map +1 -1
  42. package/dist/Chat/ChatSystemMessage.js +2 -2
  43. package/dist/Chat/ChatTokenizedText.d.ts +6 -6
  44. package/dist/Chat/ChatTokenizedText.js +10 -6
  45. package/dist/Chat/ChatToolCalls.d.ts +7 -9
  46. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  47. package/dist/Chat/ChatToolCalls.js +43 -26
  48. package/dist/Chat/index.d.ts +1 -1
  49. package/dist/Chat/index.d.ts.map +1 -1
  50. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  51. package/dist/CheckboxInput/CheckboxInput.js +12 -2
  52. package/dist/Citation/Citation.d.ts +1 -1
  53. package/dist/Citation/Citation.d.ts.map +1 -1
  54. package/dist/Citation/Citation.js +3 -2
  55. package/dist/CodeBlock/highlightRanges.d.ts.map +1 -1
  56. package/dist/CodeBlock/highlightRanges.js +14 -3
  57. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  58. package/dist/Collapsible/Collapsible.js +6 -1
  59. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  60. package/dist/CommandPalette/CommandPaletteInput.js +1 -1
  61. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  62. package/dist/ComplexSelector/ComplexSelector.js +1 -1
  63. package/dist/DateInput/DateInput.d.ts +25 -5
  64. package/dist/DateInput/DateInput.d.ts.map +1 -1
  65. package/dist/DateInput/DateInput.js +29 -17
  66. package/dist/DateInput/NativeDateField.d.ts.map +1 -1
  67. package/dist/DateInput/NativeDateField.js +16 -16
  68. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  69. package/dist/DateInput/TouchDateField.js +2 -2
  70. package/dist/DateInput/index.d.ts +1 -1
  71. package/dist/DateInput/index.d.ts.map +1 -1
  72. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  73. package/dist/DateRangeInput/DateRangeInput.js +4 -4
  74. package/dist/DateTimeInput/DateTimeInput.d.ts +32 -15
  75. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  76. package/dist/DateTimeInput/DateTimeInput.js +55 -30
  77. package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
  78. package/dist/DateTimeInput/TouchDateTimeField.js +4 -4
  79. package/dist/DateTimeInput/index.d.ts +1 -1
  80. package/dist/DateTimeInput/index.d.ts.map +1 -1
  81. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts +9 -3
  82. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts.map +1 -1
  83. package/dist/DateTimeInput/nativePickerSegmentStyles.js +3 -3
  84. package/dist/Dialog/DialogHeader.d.ts +8 -1
  85. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  86. package/dist/Dialog/DialogHeader.js +13 -4
  87. package/dist/Field/InputClearButton.d.ts +4 -2
  88. package/dist/Field/InputClearButton.d.ts.map +1 -1
  89. package/dist/Field/InputClearButton.js +4 -2
  90. package/dist/Field/PanelSearchInput.d.ts.map +1 -1
  91. package/dist/Field/PanelSearchInput.js +1 -1
  92. package/dist/FileInput/FileInput.d.ts.map +1 -1
  93. package/dist/FileInput/FileInput.js +7 -7
  94. package/dist/Icon/Icon.d.ts +6 -31
  95. package/dist/Icon/Icon.d.ts.map +1 -1
  96. package/dist/Icon/Icon.js +8 -71
  97. package/dist/Icon/IconDefaultSizeContext.d.ts +4 -0
  98. package/dist/Icon/IconDefaultSizeContext.d.ts.map +1 -0
  99. package/dist/Icon/IconDefaultSizeContext.js +18 -0
  100. package/dist/Icon/IconSize.stylex.d.ts +65 -0
  101. package/dist/Icon/IconSize.stylex.d.ts.map +1 -0
  102. package/dist/Icon/IconSize.stylex.js +75 -0
  103. package/dist/Item/Item.d.ts +1 -1
  104. package/dist/Item/Item.d.ts.map +1 -1
  105. package/dist/Item/Item.js +4 -3
  106. package/dist/Kbd/Kbd.d.ts +5 -3
  107. package/dist/Kbd/Kbd.d.ts.map +1 -1
  108. package/dist/Kbd/Kbd.js +36 -42
  109. package/dist/Link/Link.d.ts.map +1 -1
  110. package/dist/Link/Link.js +6 -2
  111. package/dist/Link/useLinkComponent.d.ts +5 -15
  112. package/dist/Link/useLinkComponent.d.ts.map +1 -1
  113. package/dist/Link/useLinkComponent.js +34 -51
  114. package/dist/List/List.d.ts +19 -1
  115. package/dist/List/List.d.ts.map +1 -1
  116. package/dist/List/List.js +9 -2
  117. package/dist/List/ListContext.d.ts +1 -0
  118. package/dist/List/ListContext.d.ts.map +1 -1
  119. package/dist/List/ListContext.js +2 -1
  120. package/dist/List/ListItem.d.ts +1 -1
  121. package/dist/List/ListItem.d.ts.map +1 -1
  122. package/dist/List/ListItem.js +9 -2
  123. package/dist/Markdown/Markdown.d.ts +8 -2
  124. package/dist/Markdown/Markdown.d.ts.map +1 -1
  125. package/dist/Markdown/Markdown.fr23.bench.d.ts +2 -0
  126. package/dist/Markdown/Markdown.fr23.bench.d.ts.map +1 -0
  127. package/dist/Markdown/Markdown.fr23.bench.js +150 -0
  128. package/dist/Markdown/Markdown.fr23.sampling.d.ts +2 -0
  129. package/dist/Markdown/Markdown.fr23.sampling.d.ts.map +1 -0
  130. package/dist/Markdown/Markdown.fr23.sampling.js +43 -0
  131. package/dist/Markdown/Markdown.js +262 -109
  132. package/dist/Markdown/ast.d.ts +162 -0
  133. package/dist/Markdown/ast.d.ts.map +1 -0
  134. package/dist/Markdown/ast.js +74 -0
  135. package/dist/Markdown/index.d.ts +5 -3
  136. package/dist/Markdown/index.d.ts.map +1 -1
  137. package/dist/Markdown/index.js +3 -2
  138. package/dist/Markdown/parser/index.d.ts +10 -0
  139. package/dist/Markdown/parser/index.d.ts.map +1 -0
  140. package/dist/Markdown/parser/index.js +10 -0
  141. package/dist/Markdown/parser.d.ts +126 -58
  142. package/dist/Markdown/parser.d.ts.map +1 -1
  143. package/dist/Markdown/parser.js +726 -177
  144. package/dist/Markdown/plugins/frontmatter.d.ts +32 -0
  145. package/dist/Markdown/plugins/frontmatter.d.ts.map +1 -0
  146. package/dist/Markdown/plugins/frontmatter.js +132 -0
  147. package/dist/Markdown/plugins/index.d.ts +17 -0
  148. package/dist/Markdown/plugins/index.d.ts.map +1 -0
  149. package/dist/Markdown/plugins/index.js +14 -0
  150. package/dist/Markdown/plugins/protocol.d.ts +191 -0
  151. package/dist/Markdown/plugins/protocol.d.ts.map +1 -0
  152. package/dist/Markdown/plugins/protocol.js +957 -0
  153. package/dist/Markdown/plugins/semanticFence.d.ts +36 -0
  154. package/dist/Markdown/plugins/semanticFence.d.ts.map +1 -0
  155. package/dist/Markdown/plugins/semanticFence.js +213 -0
  156. package/dist/Markdown/plugins/softBreaks.d.ts +3 -0
  157. package/dist/Markdown/plugins/softBreaks.d.ts.map +1 -0
  158. package/dist/Markdown/plugins/softBreaks.js +172 -0
  159. package/dist/Markdown/plugins/sourceDecoration.d.ts +76 -0
  160. package/dist/Markdown/plugins/sourceDecoration.d.ts.map +1 -0
  161. package/dist/Markdown/plugins/sourceDecoration.js +393 -0
  162. package/dist/Markdown/plugins/textTransform.d.ts +30 -0
  163. package/dist/Markdown/plugins/textTransform.d.ts.map +1 -0
  164. package/dist/Markdown/plugins/textTransform.js +374 -0
  165. package/dist/Markdown/remark.d.ts +196 -0
  166. package/dist/Markdown/remark.d.ts.map +1 -0
  167. package/dist/Markdown/remark.js +1062 -0
  168. package/dist/Markdown/url.d.ts +6 -0
  169. package/dist/Markdown/url.d.ts.map +1 -0
  170. package/dist/Markdown/url.js +31 -0
  171. package/dist/Markdown/utils.d.ts +4 -2
  172. package/dist/Markdown/utils.d.ts.map +1 -1
  173. package/dist/Markdown/utils.js +2 -1
  174. package/dist/MetadataList/MetadataList.js +3 -3
  175. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  176. package/dist/NumberInput/NumberInput.js +4 -4
  177. package/dist/Outline/index.d.ts +1 -0
  178. package/dist/Outline/index.d.ts.map +1 -1
  179. package/dist/Outline/parseOutlineFromMarkdown.d.ts +7 -1
  180. package/dist/Outline/parseOutlineFromMarkdown.d.ts.map +1 -1
  181. package/dist/Outline/parseOutlineFromMarkdown.js +14 -8
  182. package/dist/Outline/useOutlineFromMarkdown.d.ts +3 -1
  183. package/dist/Outline/useOutlineFromMarkdown.d.ts.map +1 -1
  184. package/dist/Outline/useOutlineFromMarkdown.js +8 -3
  185. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  186. package/dist/PowerSearch/PowerSearchEditPopover.js +46 -30
  187. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  188. package/dist/PowerSearch/PowerSearchValueEditor.js +5 -0
  189. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  190. package/dist/ProgressBar/ProgressBar.js +36 -31
  191. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  192. package/dist/RadioList/RadioListItem.js +13 -1
  193. package/dist/ScrollableArea/ScrollableArea.d.ts +1 -0
  194. package/dist/ScrollableArea/ScrollableArea.d.ts.map +1 -1
  195. package/dist/ScrollableArea/ScrollableArea.js +1 -0
  196. package/dist/SegmentedControl/SegmentedControl.d.ts +1 -1
  197. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  198. package/dist/SegmentedControl/SegmentedControl.js +1 -0
  199. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  200. package/dist/SegmentedControl/SegmentedControlItem.js +5 -5
  201. package/dist/Selector/Selector.d.ts +9 -7
  202. package/dist/Selector/Selector.d.ts.map +1 -1
  203. package/dist/Selector/Selector.js +16 -7
  204. package/dist/SideNav/SideNav.d.ts +2 -1
  205. package/dist/SideNav/SideNav.d.ts.map +1 -1
  206. package/dist/SideNav/SideNav.js +9 -3
  207. package/dist/Slider/Slider.d.ts.map +1 -1
  208. package/dist/Slider/Slider.js +28 -12
  209. package/dist/Spinner/Spinner.d.ts.map +1 -1
  210. package/dist/Spinner/Spinner.js +23 -7
  211. package/dist/Switch/Switch.d.ts.map +1 -1
  212. package/dist/Switch/Switch.js +11 -0
  213. package/dist/TabList/Tab.d.ts +1 -1
  214. package/dist/TabList/Tab.d.ts.map +1 -1
  215. package/dist/TabList/Tab.js +20 -7
  216. package/dist/Table/Table.d.ts +1 -0
  217. package/dist/Table/Table.d.ts.map +1 -1
  218. package/dist/Table/Table.js +29 -13
  219. package/dist/TextArea/TextArea.d.ts.map +1 -1
  220. package/dist/TextArea/TextArea.js +1 -1
  221. package/dist/TextInput/TextInput.d.ts.map +1 -1
  222. package/dist/TextInput/TextInput.js +2 -2
  223. package/dist/TimeInput/TimeInput.d.ts +30 -14
  224. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  225. package/dist/TimeInput/TimeInput.js +61 -13
  226. package/dist/TimeInput/TouchTimeField.d.ts +11 -0
  227. package/dist/TimeInput/TouchTimeField.d.ts.map +1 -0
  228. package/dist/TimeInput/TouchTimeField.js +417 -0
  229. package/dist/TimeInput/index.d.ts +1 -1
  230. package/dist/TimeInput/index.d.ts.map +1 -1
  231. package/dist/Timer/Timer.d.ts +49 -0
  232. package/dist/Timer/Timer.d.ts.map +1 -0
  233. package/dist/Timer/Timer.js +158 -0
  234. package/dist/Timer/index.d.ts +9 -0
  235. package/dist/Timer/index.d.ts.map +1 -0
  236. package/dist/Timer/index.js +10 -0
  237. package/dist/ToggleButton/ToggleButton.d.ts +9 -6
  238. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  239. package/dist/ToggleButton/ToggleButton.js +15 -27
  240. package/dist/TopNav/TopNavMenu.d.ts +1 -1
  241. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  242. package/dist/TopNav/TopNavMenu.js +3 -2
  243. package/dist/TreeList/TreeList.d.ts.map +1 -1
  244. package/dist/TreeList/TreeList.js +3 -0
  245. package/dist/TreeList/TreeListItem.d.ts +6 -1
  246. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  247. package/dist/TreeList/TreeListItem.js +12 -2
  248. package/dist/TreeList/TreeListTypes.d.ts +8 -1
  249. package/dist/TreeList/TreeListTypes.d.ts.map +1 -1
  250. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  251. package/dist/Typeahead/BaseTypeahead.js +16 -7
  252. package/dist/astryx.css +27 -11
  253. package/dist/hooks/scrollKeyboardDelegation.d.ts +3 -0
  254. package/dist/hooks/scrollKeyboardDelegation.d.ts.map +1 -0
  255. package/dist/hooks/scrollKeyboardDelegation.js +146 -0
  256. package/dist/hooks/useClickableContainer.d.ts +10 -1
  257. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  258. package/dist/hooks/useClickableContainer.js +15 -4
  259. package/dist/hooks/useScrollableArea.d.ts +6 -2
  260. package/dist/hooks/useScrollableArea.d.ts.map +1 -1
  261. package/dist/hooks/useScrollableArea.js +17 -5
  262. package/dist/index.d.ts +1 -0
  263. package/dist/index.d.ts.map +1 -1
  264. package/dist/index.js +3 -0
  265. package/dist/theme/declarationBoundary.d.ts +17 -0
  266. package/dist/theme/declarationBoundary.d.ts.map +1 -0
  267. package/dist/theme/declarationBoundary.js +343 -0
  268. package/dist/theme/defineTheme.d.ts +3 -0
  269. package/dist/theme/defineTheme.d.ts.map +1 -1
  270. package/dist/theme/defineTheme.js +4 -1
  271. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  272. package/dist/theme/derivedVarRegistry.js +4 -0
  273. package/dist/theme/generateThemeRules.d.ts +15 -7
  274. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  275. package/dist/theme/generateThemeRules.js +143 -35
  276. package/dist/theme/themeAdaptations.d.ts +5 -3
  277. package/dist/theme/themeAdaptations.d.ts.map +1 -1
  278. package/dist/theme/themeAdaptations.js +33 -5
  279. package/dist/utils/inputPresentation.d.ts +55 -0
  280. package/dist/utils/inputPresentation.d.ts.map +1 -0
  281. package/dist/utils/inputPresentation.js +108 -0
  282. package/dist/utils/interactionOverlay.stylex.d.ts +8 -0
  283. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  284. package/dist/utils/interactionOverlay.stylex.js +9 -0
  285. package/dist/utils/safeUrl.d.ts +11 -0
  286. package/dist/utils/safeUrl.d.ts.map +1 -0
  287. package/dist/utils/safeUrl.js +78 -0
  288. package/locales/af-ZA.json +530 -1
  289. package/locales/ar-SA.json +490 -1
  290. package/locales/ca-ES.json +514 -1
  291. package/locales/cs-CZ.json +498 -1
  292. package/locales/da-DK.json +522 -1
  293. package/locales/de-DE.json +522 -1
  294. package/locales/el-GR.json +498 -1
  295. package/locales/es-ES.json +510 -1
  296. package/locales/fi-FI.json +498 -1
  297. package/locales/fr-FR.json +534 -1
  298. package/locales/he-IL.json +498 -1
  299. package/locales/hu-HU.json +498 -1
  300. package/locales/it-IT.json +510 -1
  301. package/locales/ja-JP.json +490 -1
  302. package/locales/ko-KR.json +498 -1
  303. package/locales/nl-NL.json +534 -1
  304. package/locales/no-NO.json +510 -1
  305. package/locales/pl-PL.json +506 -1
  306. package/locales/pt-BR.json +506 -1
  307. package/locales/pt-PT.json +506 -1
  308. package/locales/ro-RO.json +506 -1
  309. package/locales/ru-RU.json +498 -1
  310. package/locales/sr-SP.json +498 -1
  311. package/locales/sv-SE.json +502 -1
  312. package/locales/tr-TR.json +498 -1
  313. package/locales/uk-UA.json +498 -1
  314. package/locales/vi-VN.json +502 -1
  315. package/locales/zh-CN.json +490 -1
  316. package/locales/zh-TW.json +490 -1
  317. package/package.json +24 -4
  318. package/src/Avatar/Avatar.doc.mjs +1 -1
  319. package/src/Avatar/Avatar.test.tsx +30 -0
  320. package/src/Avatar/Avatar.tsx +38 -14
  321. package/src/BottomSheet/BottomSheet.doc.mjs +8 -1
  322. package/src/BottomSheet/BottomSheet.spec.md +46 -20
  323. package/src/BottomSheet/BottomSheet.test.tsx +6 -3
  324. package/src/BottomSheet/BottomSheet.tsx +3 -1
  325. package/src/BottomSheet/BottomSheetKeyboard.test.tsx +195 -0
  326. package/src/BottomSheet/BottomSheetPanel.test.tsx +11 -1
  327. package/src/BottomSheet/BottomSheetPanel.tsx +49 -16
  328. package/src/BottomSheet/__tests__/BottomSheetKeyboard.a11y.browser.spec.ts +344 -0
  329. package/src/Button/Button.doc.mjs +5 -5
  330. package/src/Button/Button.tsx +15 -13
  331. package/src/Button/__tests__/Button.a11y.chromium.spec.ts +17 -1
  332. package/src/Button/__tests__/Button.a11y.known-failures.ts +0 -29
  333. package/src/Button/__tests__/Button.a11y.renders.tsx +9 -2
  334. package/src/Button/__tests__/Button.a11y.states.ts +9 -0
  335. package/src/Chat/ChatComposer.doc.mjs +14 -0
  336. package/src/Chat/ChatComposer.spec.md +230 -0
  337. package/src/Chat/ChatComposer.test.tsx +57 -0
  338. package/src/Chat/ChatComposer.tsx +21 -9
  339. package/src/Chat/ChatComposerDrawer.doc.mjs +63 -5
  340. package/src/Chat/ChatComposerDrawer.spec.md +267 -0
  341. package/src/Chat/ChatComposerDrawer.test.tsx +81 -12
  342. package/src/Chat/ChatComposerDrawer.tsx +38 -9
  343. package/src/Chat/ChatComposerInput.doc.mjs +114 -26
  344. package/src/Chat/ChatComposerInput.spec.md +185 -0
  345. package/src/Chat/ChatComposerInput.test.tsx +113 -3
  346. package/src/Chat/ChatComposerInput.tsx +78 -13
  347. package/src/Chat/ChatComposerTokenElement.spec.md +152 -0
  348. package/src/Chat/ChatComposerTokenElement.test.tsx +85 -0
  349. package/src/Chat/ChatDictationButton.doc.mjs +3 -3
  350. package/src/Chat/ChatDictationButton.spec.md +160 -0
  351. package/src/Chat/ChatDictationButton.test.tsx +92 -27
  352. package/src/Chat/ChatDictationButton.tsx +8 -6
  353. package/src/Chat/ChatLayout.doc.mjs +12 -4
  354. package/src/Chat/ChatLayout.spec.md +271 -0
  355. package/src/Chat/ChatLayout.test.tsx +28 -0
  356. package/src/Chat/ChatLayout.tsx +19 -0
  357. package/src/Chat/ChatLayoutScrollButton.spec.md +338 -0
  358. package/src/Chat/ChatLayoutScrollButton.test.tsx +31 -0
  359. package/src/Chat/ChatLayoutScrollButton.tsx +31 -14
  360. package/src/Chat/ChatMessageBubble.doc.mjs +28 -15
  361. package/src/Chat/ChatMessageBubble.spec.md +263 -0
  362. package/src/Chat/ChatMessageBubble.test.tsx +93 -0
  363. package/src/Chat/ChatMessageBubble.tsx +8 -3
  364. package/src/Chat/ChatMessageList.spec.md +222 -0
  365. package/src/Chat/ChatMessageList.test.tsx +64 -2
  366. package/src/Chat/ChatMessageList.tsx +4 -3
  367. package/src/Chat/ChatMessageMetadata.doc.mjs +57 -3
  368. package/src/Chat/ChatMessageMetadata.spec.md +220 -0
  369. package/src/Chat/ChatMessageMetadata.test.tsx +34 -0
  370. package/src/Chat/ChatMessageMetadata.tsx +13 -13
  371. package/src/Chat/ChatSendButton.doc.mjs +32 -0
  372. package/src/Chat/ChatSendButton.spec.md +227 -0
  373. package/src/Chat/ChatSendButton.test.tsx +128 -8
  374. package/src/Chat/ChatSendButton.tsx +6 -4
  375. package/src/Chat/ChatSystemMessage.doc.mjs +115 -12
  376. package/src/Chat/ChatSystemMessage.spec.md +225 -0
  377. package/src/Chat/ChatSystemMessage.test.tsx +112 -20
  378. package/src/Chat/ChatSystemMessage.tsx +4 -6
  379. package/src/Chat/ChatTokenizedText.doc.mjs +96 -9
  380. package/src/Chat/ChatTokenizedText.spec.md +190 -0
  381. package/src/Chat/ChatTokenizedText.test.tsx +60 -1
  382. package/src/Chat/ChatTokenizedText.tsx +14 -8
  383. package/src/Chat/ChatToolCalls.doc.mjs +4 -1
  384. package/src/Chat/ChatToolCalls.spec.md +194 -0
  385. package/src/Chat/ChatToolCalls.test.tsx +83 -0
  386. package/src/Chat/ChatToolCalls.tsx +46 -21
  387. package/src/Chat/__tests__/ChatLayoutScrollButton.a11y.chromium.spec.ts +514 -0
  388. package/src/Chat/__tests__/ChatLayoutScrollButtonSurface.a11y.chromium.spec.ts +740 -0
  389. package/src/Chat/__tests__/ChatMessageBubble.a11y.chromium.spec.ts +457 -0
  390. package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.test.ts +91 -0
  391. package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.ts +91 -0
  392. package/src/Chat/__tests__/ChatMessageList.a11y.chromium.spec.ts +456 -0
  393. package/src/Chat/__tests__/ChatMessageMetadata.a11y.chromium.spec.ts +413 -0
  394. package/src/Chat/__tests__/ChatSendButton.a11y.chromium.spec.ts +1522 -0
  395. package/src/Chat/__tests__/ChatSystemMessage.a11y.chromium.spec.ts +1004 -0
  396. package/src/Chat/__tests__/ChatTokenizedText.a11y.chromium.spec.ts +573 -0
  397. package/src/Chat/index.ts +1 -0
  398. package/src/CheckboxInput/CheckboxInput.doc.mjs +11 -0
  399. package/src/CheckboxInput/CheckboxInput.test.tsx +34 -0
  400. package/src/CheckboxInput/CheckboxInput.tsx +21 -1
  401. package/src/Citation/Citation.doc.mjs +2 -2
  402. package/src/Citation/Citation.test.tsx +17 -0
  403. package/src/Citation/Citation.tsx +4 -2
  404. package/src/ClickableCard/ClickableCard.doc.mjs +2 -2
  405. package/src/ClickableCard/ClickableCard.test.tsx +102 -5
  406. package/src/CodeBlock/highlightRanges.test.ts +143 -1
  407. package/src/CodeBlock/highlightRanges.ts +15 -5
  408. package/src/Collapsible/Collapsible.doc.mjs +11 -0
  409. package/src/Collapsible/Collapsible.test.tsx +21 -0
  410. package/src/Collapsible/Collapsible.tsx +5 -0
  411. package/src/CommandPalette/CommandPaletteInput.tsx +6 -1
  412. package/src/ComplexSelector/ComplexSelector.tsx +6 -1
  413. package/src/DateInput/DateInput.doc.mjs +38 -9
  414. package/src/DateInput/DateInput.tsx +59 -17
  415. package/src/DateInput/NativeDateField.tsx +24 -8
  416. package/src/DateInput/Presentation.test.tsx +121 -0
  417. package/src/DateInput/TouchDateField.tsx +6 -2
  418. package/src/DateInput/index.ts +1 -0
  419. package/src/DateRangeInput/DateRangeInput.tsx +6 -1
  420. package/src/DateTimeInput/DateTimeInput.doc.mjs +28 -12
  421. package/src/DateTimeInput/DateTimeInput.tsx +93 -37
  422. package/src/DateTimeInput/Presentation.test.tsx +89 -0
  423. package/src/DateTimeInput/TouchDateTimeField.tsx +6 -1
  424. package/src/DateTimeInput/index.ts +1 -0
  425. package/src/DateTimeInput/nativePickerSegmentStyles.ts +24 -8
  426. package/src/Dialog/Dialog.doc.mjs +2 -0
  427. package/src/Dialog/DialogHeader.doc.mjs +22 -0
  428. package/src/Dialog/DialogHeader.test.tsx +167 -3
  429. package/src/Dialog/DialogHeader.tsx +37 -6
  430. package/src/Dialog/modules/DialogHeader.spec.md +101 -42
  431. package/src/DropdownMenu/DropdownMenu.test.tsx +5 -2
  432. package/src/Field/InputClearButton.test.tsx +17 -3
  433. package/src/Field/InputClearButton.tsx +5 -2
  434. package/src/Field/PanelSearchInput.tsx +5 -2
  435. package/src/FieldStatus/__tests__/StatusMessage.a11y.states.ts +1 -1
  436. package/src/FileInput/FileInput.tsx +11 -2
  437. package/src/Icon/Icon.doc.mjs +7 -5
  438. package/src/Icon/Icon.spec.md +81 -40
  439. package/src/Icon/Icon.test.tsx +6 -3
  440. package/src/Icon/Icon.tsx +20 -68
  441. package/src/Icon/IconDefaultSizeContext.ts +23 -0
  442. package/src/Icon/IconSize.stylex.ts +70 -0
  443. package/src/IconButton/IconButton.doc.mjs +3 -3
  444. package/src/IconButton/IconButton.test.tsx +40 -0
  445. package/src/Item/Item.doc.mjs +6 -2
  446. package/src/Item/Item.tsx +9 -3
  447. package/src/Kbd/Kbd.doc.mjs +3 -3
  448. package/src/Kbd/Kbd.test.tsx +57 -1
  449. package/src/Kbd/Kbd.tsx +57 -37
  450. package/src/Layout/LayoutFooter.doc.mjs +17 -0
  451. package/src/Link/Link.doc.mjs +17 -4
  452. package/src/Link/Link.test.tsx +24 -0
  453. package/src/Link/Link.tsx +5 -0
  454. package/src/Link/LinkProvider.doc.mjs +2 -2
  455. package/src/Link/__tests__/Link.navigation.a11y.chromium.spec.ts +261 -0
  456. package/src/Link/useLinkComponent.test.tsx +333 -0
  457. package/src/Link/useLinkComponent.ts +29 -57
  458. package/src/List/List.doc.mjs +6 -0
  459. package/src/List/List.spec.md +200 -0
  460. package/src/List/List.test.tsx +117 -1
  461. package/src/List/List.tsx +22 -2
  462. package/src/List/ListContext.tsx +3 -1
  463. package/src/List/ListItem.doc.mjs +2 -2
  464. package/src/List/ListItem.source-build.test.mjs +41 -0
  465. package/src/List/ListItem.tsx +26 -2
  466. package/src/Markdown/Markdown.doc.mjs +300 -2
  467. package/src/Markdown/Markdown.fr23.bench.ts +174 -0
  468. package/src/Markdown/Markdown.fr23.perf.test.ts +249 -0
  469. package/src/Markdown/Markdown.fr23.sampling.ts +49 -0
  470. package/src/Markdown/Markdown.helpers.perf.test.ts +489 -0
  471. package/src/Markdown/Markdown.public.test.ts +130 -3
  472. package/src/Markdown/Markdown.renderBoundary.test.tsx +72 -0
  473. package/src/Markdown/Markdown.spec.md +210 -45
  474. package/src/Markdown/Markdown.test.tsx +87 -3
  475. package/src/Markdown/Markdown.tsx +414 -135
  476. package/src/Markdown/ast.test.ts +307 -0
  477. package/src/Markdown/ast.ts +337 -0
  478. package/src/Markdown/incremental.test.ts +43 -0
  479. package/src/Markdown/index.ts +37 -1
  480. package/src/Markdown/modules/remark.spec.md +269 -0
  481. package/src/Markdown/modules/softBreaks.spec.md +137 -0
  482. package/src/Markdown/parser/index.ts +64 -0
  483. package/src/Markdown/parser.public.test.ts +103 -0
  484. package/src/Markdown/parser.test.ts +38 -6
  485. package/src/Markdown/parser.ts +1221 -242
  486. package/src/Markdown/plugins/frontmatter.test.tsx +125 -0
  487. package/src/Markdown/plugins/frontmatter.ts +198 -0
  488. package/src/Markdown/plugins/index.ts +54 -0
  489. package/src/Markdown/plugins/protocol.test.tsx +1022 -0
  490. package/src/Markdown/plugins/protocol.ts +1618 -0
  491. package/src/Markdown/plugins/semanticFence.test.tsx +546 -0
  492. package/src/Markdown/plugins/semanticFence.ts +424 -0
  493. package/src/Markdown/plugins/softBreaks.test.tsx +172 -0
  494. package/src/Markdown/plugins/softBreaks.ts +169 -0
  495. package/src/Markdown/plugins/sourceDecoration.test.tsx +799 -0
  496. package/src/Markdown/plugins/sourceDecoration.ts +539 -0
  497. package/src/Markdown/plugins/textTransform.test.ts +467 -0
  498. package/src/Markdown/plugins/textTransform.ts +554 -0
  499. package/src/Markdown/remark.test.tsx +1877 -0
  500. package/src/Markdown/remark.ts +1574 -0
  501. package/src/Markdown/url.ts +36 -0
  502. package/src/Markdown/utils.ts +13 -1
  503. package/src/MetadataList/MetadataList.test.tsx +4 -2
  504. package/src/MetadataList/MetadataList.tsx +3 -3
  505. package/src/MultiSelector/MultiSelector.test.tsx +1 -35
  506. package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +128 -0
  507. package/src/NumberInput/NumberInput.tsx +6 -1
  508. package/src/Outline/Outline.doc.mjs +7 -5
  509. package/src/Outline/Outline.spec.md +1 -1
  510. package/src/Outline/index.ts +1 -0
  511. package/src/Outline/modules/parseOutlineFromMarkdown.spec.md +142 -0
  512. package/src/Outline/parseOutlineFromMarkdown.ts +40 -10
  513. package/src/Outline/useOutlineFromMarkdown.ts +15 -3
  514. package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
  515. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +150 -1
  516. package/src/PowerSearch/PowerSearchEditPopover.tsx +51 -28
  517. package/src/PowerSearch/PowerSearchValueEditor.test.tsx +51 -0
  518. package/src/PowerSearch/PowerSearchValueEditor.tsx +4 -0
  519. package/src/ProgressBar/ProgressBar.test.tsx +29 -11
  520. package/src/ProgressBar/ProgressBar.tsx +59 -72
  521. package/src/RadioList/RadioList.doc.mjs +11 -0
  522. package/src/RadioList/RadioList.test.tsx +32 -0
  523. package/src/RadioList/RadioListItem.tsx +25 -1
  524. package/src/ScrollableArea/ScrollableArea.tsx +1 -0
  525. package/src/ScrollableArea/modules/useScrollableArea.spec.md +50 -22
  526. package/src/SegmentedControl/SegmentedControl.doc.mjs +2 -2
  527. package/src/SegmentedControl/SegmentedControl.test.tsx +61 -0
  528. package/src/SegmentedControl/SegmentedControl.tsx +2 -1
  529. package/src/SegmentedControl/SegmentedControlItem.tsx +6 -9
  530. package/src/Selector/Selector.doc.mjs +3 -3
  531. package/src/Selector/Selector.spec.md +51 -49
  532. package/src/Selector/Selector.test.tsx +61 -55
  533. package/src/Selector/Selector.tsx +34 -15
  534. package/src/Selector/__tests__/Listbox.a11y.chromium.spec.ts +130 -0
  535. package/src/Selector/__tests__/Listbox.a11y.dom.ts +38 -0
  536. package/src/Selector/__tests__/Listbox.a11y.inventory.test.ts +63 -0
  537. package/src/Selector/__tests__/Listbox.a11y.known-failures.ts +37 -0
  538. package/src/Selector/__tests__/Listbox.a11y.states.ts +232 -0
  539. package/src/Selector/__tests__/Selector.a11y.chromium.spec.ts +45 -0
  540. package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +121 -0
  541. package/src/SideNav/SideNav.doc.mjs +1 -1
  542. package/src/SideNav/SideNav.test.tsx +10 -0
  543. package/src/SideNav/SideNav.tsx +14 -2
  544. package/src/Slider/Slider.doc.mjs +17 -3
  545. package/src/Slider/Slider.test.tsx +221 -0
  546. package/src/Slider/Slider.tsx +40 -9
  547. package/src/Spinner/Spinner.test.tsx +4 -0
  548. package/src/Spinner/Spinner.tsx +31 -18
  549. package/src/Switch/Switch.doc.mjs +11 -0
  550. package/src/Switch/Switch.test.tsx +16 -0
  551. package/src/Switch/Switch.tsx +28 -0
  552. package/src/TabList/Tab.tsx +35 -7
  553. package/src/TabList/TabList.doc.mjs +11 -0
  554. package/src/TabList/TabList.spec.md +204 -0
  555. package/src/TabList/TabList.test.tsx +66 -0
  556. package/src/TabList/__tests__/Tabs.a11y.known-failures.ts +1 -34
  557. package/src/Table/Table.doc.mjs +1 -1
  558. package/src/Table/Table.spec.md +54 -40
  559. package/src/Table/Table.test.tsx +83 -10
  560. package/src/Table/Table.tsx +30 -22
  561. package/src/Table/__tests__/TableScroll.a11y.chromium.spec.ts +137 -0
  562. package/src/TextArea/TextArea.tsx +6 -1
  563. package/src/TextInput/TextInput.test.tsx +20 -1
  564. package/src/TextInput/TextInput.tsx +6 -1
  565. package/src/TimeInput/NativeTimeInput.test.tsx +36 -3
  566. package/src/TimeInput/Presentation.test.tsx +148 -0
  567. package/src/TimeInput/TimeInput.doc.mjs +21 -5
  568. package/src/TimeInput/TimeInput.tsx +102 -11
  569. package/src/TimeInput/TouchTimeField.tsx +572 -0
  570. package/src/TimeInput/index.ts +1 -0
  571. package/src/Timer/Timer.doc.mjs +183 -0
  572. package/src/Timer/Timer.spec.md +215 -0
  573. package/src/Timer/Timer.test.tsx +302 -0
  574. package/src/Timer/Timer.tsx +258 -0
  575. package/src/Timer/index.ts +11 -0
  576. package/src/ToggleButton/ToggleButton.doc.mjs +3 -3
  577. package/src/ToggleButton/ToggleButton.test.tsx +299 -4
  578. package/src/ToggleButton/ToggleButton.tsx +26 -33
  579. package/src/ToggleButton/ToggleButtonGroup.doc.mjs +3 -3
  580. package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +209 -0
  581. package/src/Token/Token.doc.mjs +2 -2
  582. package/src/Tokenizer/Tokenizer.spec.md +142 -75
  583. package/src/TopNav/TopNavMenu.doc.mjs +2 -2
  584. package/src/TopNav/TopNavMenu.test.tsx +30 -0
  585. package/src/TopNav/TopNavMenu.tsx +7 -2
  586. package/src/TreeList/TreeList.doc.mjs +3 -3
  587. package/src/TreeList/TreeList.spec.md +123 -75
  588. package/src/TreeList/TreeList.test.tsx +29 -0
  589. package/src/TreeList/TreeList.tsx +3 -0
  590. package/src/TreeList/TreeListItem.tsx +15 -1
  591. package/src/TreeList/TreeListTypes.ts +11 -1
  592. package/src/Typeahead/BaseTypeahead.spec.md +4 -3
  593. package/src/Typeahead/BaseTypeahead.tsx +21 -7
  594. package/src/Typeahead/Typeahead.test.tsx +53 -0
  595. package/src/__tests__/PressedState.a11y.chromium.spec.ts +813 -0
  596. package/src/__tests__/pressState.ts +93 -0
  597. package/src/hooks/scrollKeyboardDelegation.test.ts +155 -0
  598. package/src/hooks/scrollKeyboardDelegation.ts +233 -0
  599. package/src/hooks/useClickableContainer.test.tsx +236 -0
  600. package/src/hooks/useClickableContainer.ts +20 -4
  601. package/src/hooks/useScrollableArea.doc.mjs +15 -3
  602. package/src/hooks/useScrollableArea.test.tsx +59 -1
  603. package/src/hooks/useScrollableArea.ts +34 -10
  604. package/src/index.ts +3 -0
  605. package/src/inputFontFloor.test.ts +106 -0
  606. package/src/tailwind-theme.css +0 -6
  607. package/src/theme/Theme.test.tsx +32 -0
  608. package/src/theme/declarationBoundary.test.ts +126 -0
  609. package/src/theme/declarationBoundary.ts +347 -0
  610. package/src/theme/defineTheme.ts +4 -0
  611. package/src/theme/derivedVarRegistry.test.ts +7 -0
  612. package/src/theme/derivedVarRegistry.ts +1 -0
  613. package/src/theme/generateThemeRules.test.ts +526 -1
  614. package/src/theme/generateThemeRules.ts +231 -50
  615. package/src/theme/themeAdaptations.test.ts +211 -1
  616. package/src/theme/themeAdaptations.ts +50 -4
  617. package/src/theme/tokenValueCompat.test.ts +4 -1
  618. package/src/utils/inputPresentation.ts +143 -0
  619. package/src/utils/interactionOverlay.stylex.ts +20 -0
  620. package/src/utils/safeUrl.test.ts +88 -0
  621. package/src/utils/safeUrl.ts +85 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/core",
3
- "version": "0.6.2",
3
+ "version": "0.6.3-canary.039a67e",
4
4
  "displayName": "Astryx Core",
5
5
  "description": "The component library. Accessible, themeable React components with built-in spacing, dark mode, and StyleX styling.",
6
6
  "author": "Meta Open Source",
@@ -539,6 +539,11 @@
539
539
  "types": "./dist/TimeInput/index.d.ts",
540
540
  "default": "./dist/TimeInput/index.js"
541
541
  },
542
+ "./Timer": {
543
+ "source": "./src/Timer/index.ts",
544
+ "types": "./dist/Timer/index.d.ts",
545
+ "default": "./dist/Timer/index.js"
546
+ },
542
547
  "./Timestamp": {
543
548
  "source": "./src/Timestamp/index.ts",
544
549
  "types": "./dist/Timestamp/index.d.ts",
@@ -619,6 +624,16 @@
619
624
  "types": "./dist/utils/index.d.ts",
620
625
  "default": "./dist/utils/index.js"
621
626
  },
627
+ "./Markdown/plugins": {
628
+ "source": "./src/Markdown/plugins/index.ts",
629
+ "types": "./dist/Markdown/plugins/index.d.ts",
630
+ "default": "./dist/Markdown/plugins/index.js"
631
+ },
632
+ "./Markdown/parser": {
633
+ "source": "./src/Markdown/parser/index.ts",
634
+ "types": "./dist/Markdown/parser/index.d.ts",
635
+ "default": "./dist/Markdown/parser/index.js"
636
+ },
622
637
  "./Calendar/utils": {
623
638
  "source": "./src/Calendar/utils.ts",
624
639
  "types": "./dist/Calendar/utils.d.ts",
@@ -653,6 +668,11 @@
653
668
  "source": "./src/Typeahead/utils.ts",
654
669
  "types": "./dist/Typeahead/utils.d.ts",
655
670
  "default": "./dist/Typeahead/utils.js"
671
+ },
672
+ "./Markdown/remark": {
673
+ "source": "./src/Markdown/remark.ts",
674
+ "types": "./dist/Markdown/remark.d.ts",
675
+ "default": "./dist/Markdown/remark.js"
656
676
  }
657
677
  },
658
678
  "files": [
@@ -672,6 +692,8 @@
672
692
  "react-dom": ">=19.0.0"
673
693
  },
674
694
  "devDependencies": {
695
+ "@astryxdesign/a11y-spec": "0.6.3-canary.039a67e",
696
+ "@astryxdesign/cli": "0.6.3-canary.039a67e",
675
697
  "@babel/cli": "^7.29.7",
676
698
  "@babel/core": "^7.29.7",
677
699
  "@babel/preset-react": "^7.29.7",
@@ -681,9 +703,7 @@
681
703
  "@testing-library/dom": "^10.0.0",
682
704
  "@testing-library/jest-dom": "^6.6.0",
683
705
  "@testing-library/react": "^16.3.2",
684
- "@types/babel__core": "^7.20.5",
685
- "@astryxdesign/a11y-spec": "0.0.0",
686
- "@astryxdesign/cli": "0.6.2"
706
+ "@types/babel__core": "^7.20.5"
687
707
  },
688
708
  "dependencies": {
689
709
  "intl-messageformat": "^11.2.9"
@@ -23,7 +23,7 @@ export const docs = {
23
23
  ],
24
24
  anatomy: [
25
25
  {name: 'Photo', required: false, description: 'The profile image, loaded from the src URL. Shown when available.'},
26
- {name: 'Initials', required: false, description: 'One or two letters extracted from the name. Shown when no photo is available.'},
26
+ {name: 'Initials', required: false, description: 'The first letter, digit or emoji of the first and last words of the name; punctuation is skipped. Shown when no photo is available. A name with no letters, digits or emoji shows the default icon instead.'},
27
27
  {name: 'Default icon', required: false, description: 'A generic person silhouette. Shown when there is no photo or name.'},
28
28
  {name: 'Status dot', required: false, description: 'A small indicator in the bottom-right corner showing availability (online, away, busy). Each variant pairs colour with a distinct shape so status does not rely on colour alone.'},
29
29
  ],
@@ -103,6 +103,36 @@ describe('Avatar', () => {
103
103
  expect(screen.getByTestId('avatar')).toHaveTextContent('👨‍👩‍👧‍👦A');
104
104
  });
105
105
 
106
+ describe('initials come from letters, digits and emoji, not punctuation', () => {
107
+ it.each([
108
+ ['Northwind Workbench (automation)', 'NA'],
109
+ ['Ada "The Countess" Lovelace', 'AL'],
110
+ ['“Ada” Lovelace', 'AL'],
111
+ ['Ada - Lovelace', 'AL'],
112
+ ['Ada Lovelace —', 'AL'],
113
+ ['Mary-Jane Watson', 'MW'],
114
+ ['(Ada)', 'A'],
115
+ ['Alice', 'A'],
116
+ ['R2 D2', 'RD'],
117
+ ['3M', '3'],
118
+ ['émile zola', 'ÉZ'],
119
+ ['😀 Ada', '😀A'],
120
+ ['Ada (😀)', 'A😀'],
121
+ ])('%s renders %s', (name, expected) => {
122
+ render(<Avatar name={name} data-testid="avatar" />);
123
+ expect(screen.getByTestId('avatar').textContent).toBe(expected);
124
+ });
125
+
126
+ it('falls through to the default icon when no word yields an initial', () => {
127
+ render(<Avatar name="- ()" data-testid="a" />);
128
+ const el = screen.getByTestId('a');
129
+ expect(el.querySelector('svg')).not.toBeNull();
130
+ expect(el).toHaveTextContent('');
131
+ // The name still names the avatar.
132
+ expect(screen.getByRole('img', {name: '- ()'})).toBeInTheDocument();
133
+ });
134
+ });
135
+
106
136
  it('retries a new src after a previous src failed to load', () => {
107
137
  const {rerender} = render(
108
138
  <Avatar name="Ada" src="https://example.com/broken.jpg" />,
@@ -384,25 +384,46 @@ export interface AvatarProps extends BaseProps<HTMLDivElement> {
384
384
  }
385
385
 
386
386
  /**
387
- * Generates initials from a name string.
388
- * Takes the first letter of the first two words.
387
+ * A character that can stand for a word in initials: a letter, a digit, or an
388
+ * emoji. Punctuation and other symbols, such as `(`, `"` or `-`, cannot.
389
+ */
390
+ const INITIAL_CHARACTER =
391
+ /[\p{L}\p{N}\p{Extended_Pictographic}\p{Regional_Indicator}]/u;
392
+
393
+ /**
394
+ * The first character of a word that can stand for it in initials, skipping
395
+ * leading punctuation, or '' when the word has none (a lone `-` or `()`).
396
+ */
397
+ function initialCharacter(word: string): string {
398
+ const index = word.search(INITIAL_CHARACTER);
399
+ return index === -1 ? '' : firstCharacter(word.slice(index));
400
+ }
401
+
402
+ /**
403
+ * Generates initials from a name string: the first letter, digit or emoji of
404
+ * the first and last words. Words with none of those, such as a lone `-`, are
405
+ * skipped, and leading punctuation within a word is ignored. Returns '' when
406
+ * no word yields an initial.
389
407
  * @example
390
408
  * ```
391
- * getInitials('John Doe')
392
- * getInitials('Alice')
409
+ * getInitials('John Doe') // 'JD'
410
+ * getInitials('Alice') // 'A'
411
+ * getInitials('Northwind Workbench (automation)') // 'NA'
393
412
  * ```
394
413
  */
395
414
  function getInitials(name: string): string {
396
- const words = name.trim().split(/\s+/);
397
- if (words.length === 0) {
415
+ const initials = name
416
+ .trim()
417
+ .split(/\s+/)
418
+ .map(initialCharacter)
419
+ .filter(initial => initial !== '');
420
+ if (initials.length === 0) {
398
421
  return '';
399
422
  }
400
- if (words.length === 1) {
401
- return firstCharacter(words[0]).toUpperCase();
423
+ if (initials.length === 1) {
424
+ return initials[0].toUpperCase();
402
425
  }
403
- return (
404
- firstCharacter(words[0]) + firstCharacter(words[words.length - 1])
405
- ).toUpperCase();
426
+ return (initials[0] + initials[initials.length - 1]).toUpperCase();
406
427
  }
407
428
 
408
429
  /**
@@ -554,8 +575,11 @@ export function Avatar({
554
575
  // truthy), leaving an empty plate behind a blank accessible name.
555
576
  const meaningfulName = meaningful(name);
556
577
  const meaningfulAlt = meaningful(alt);
557
- const showInitials = !showImage && !showFallbackImage && meaningfulName;
558
- const showIcon = !showImage && !showFallbackImage && !meaningfulName;
578
+ // A name made only of punctuation ("—", "()") still names the avatar, but
579
+ // yields no initials; it falls back to the default icon, not an empty plate.
580
+ const initials = meaningfulName ? getInitials(meaningfulName) : '';
581
+ const showInitials = !showImage && !showFallbackImage && initials !== '';
582
+ const showIcon = !showImage && !showFallbackImage && initials === '';
559
583
 
560
584
  // A meaningful accessible name comes from `alt`/`name`, composed with the
561
585
  // status element's `label` when one is present ("Jane Doe, Online") — the
@@ -753,7 +777,7 @@ export function Avatar({
753
777
  dynamicStyles.fontSize(numericSize),
754
778
  ),
755
779
  )}>
756
- {getInitials(meaningfulName)}
780
+ {initials}
757
781
  </div>
758
782
  )}
759
783
  {showIcon && (
@@ -1,5 +1,12 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
+ /**
4
+ * @file BottomSheet.doc.mjs
5
+ * @input BottomSheet props, sheet anatomy, and focus-time keyboard scrolling behavior
6
+ * @output Consumer documentation and examples for BottomSheet
7
+ * @position CLI and rendered component documentation
8
+ */
9
+
3
10
  /** @type {import('@astryxdesign/cli/authoring').ComponentAnatomyElement[]} */
4
11
  const anatomy = [
5
12
  {
@@ -130,7 +137,7 @@ export const docs = {
130
137
  name: 'children',
131
138
  type: 'ReactNode',
132
139
  description:
133
- "Sheet content, rendered below the grab handle in a scrollable area. If it includes a text-entry control that can bring up the mobile keyboard, use height='tall' and keep the sheet fully expanded while editing.",
140
+ "Sheet content in a scrollable area. The named body is keyboard reachable while overflowing. Forward Tab entry may move directly to the first native link or button; input controls, composite widgets, and nested scroll areas retain the body stop. Shift+Tab from a delegated first child skips the body. Fitting content adds no body stop. The internal observed content box preserves block flow and percentage heights. If it includes a text-entry control that can bring up the mobile keyboard, use height='tall' and keep the sheet fully expanded while editing.",
134
141
  required: true,
135
142
  },
136
143
  {
@@ -14,6 +14,9 @@ verified_by:
14
14
  [
15
15
  packages/core/src/BottomSheet/BottomSheet.test.tsx,
16
16
  packages/core/src/BottomSheet/BottomSheetPanel.test.tsx,
17
+ packages/core/src/BottomSheet/BottomSheetKeyboard.test.tsx,
18
+ packages/core/src/BottomSheet/__tests__/BottomSheetKeyboard.a11y.browser.spec.ts,
19
+ apps/storybook/stories/BottomSheet.stories.tsx,
17
20
  scripts/check-knowledge.mjs,
18
21
  ]
19
22
  modules: []
@@ -22,7 +25,7 @@ design_specs: []
22
25
  architecture:
23
26
  [architecture:component-theming-surface, architecture:layer-runtime]
24
27
  contributing: []
25
- system_specs: []
28
+ system_specs: [spec:AST-025]
26
29
  ---
27
30
 
28
31
  # BottomSheet component contract
@@ -30,14 +33,15 @@ system_specs: []
30
33
  ## Intent
31
34
 
32
35
  BottomSheet presents caller-provided content in a panel that rises from the
33
- bottom edge. This draft records current consumer anatomy and theming reachability
34
- without changing runtime behavior, styling, targets, or public API.
36
+ bottom edge. This draft records current consumer anatomy, keyboard-reachable
37
+ scrolling, and theming reachability without changing public API.
35
38
 
36
39
  ## Compatibility and migration
37
40
 
38
41
  - Released default preserved: `yes`
39
- - Compatibility class: additive documentation only; runtime, DOM, styling,
40
- targets, and public API remain unchanged
42
+ - Compatibility class: additive internal content box and shared scroll behavior;
43
+ public props, defaults, targets, and caller child ordering inside the content box
44
+ remain unchanged
41
45
  - Controlled/uncontrolled behavior: unchanged
42
46
  - Migration decision: none
43
47
 
@@ -48,7 +52,8 @@ Consumer migration instructions belong in consumer docs and release notes.
48
52
  **Owns**
49
53
 
50
54
  - The visual Sheet panel and its current `bottom-sheet` target.
51
- - The scrolling Content area and decorative Handle inside the panel.
55
+ - The scrolling Content area, its shared viewport/content-box integration, and
56
+ automatic keyboard-path selection.
52
57
  - Standalone sheet presentation, including its optional native-dialog Scrim.
53
58
 
54
59
  **Does not own / non-goals**
@@ -67,10 +72,11 @@ in `BottomSheet.doc.mjs`.
67
72
 
68
73
  ## Behavioral and layout contract
69
74
 
70
- | ID | Candidate invariant | Basis | Draft review state |
71
- | --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------- | -------------------------------------------------- |
72
- | FR1 | The current presented sheet contains one Sheet panel, one scrolling Content area, and one decorative Handle; a Scrim is present only in scrim-backed presentation. | Current source, docs, and tests | Verified current behavior; no new behavior decided |
73
- | FR2 | The Sheet panel carries `bottom-sheet`; Content area, Handle, and Scrim carry no BottomSheet public target. | Current source, docs, and tests | Verified current behavior; no target change |
75
+ | ID | Candidate invariant | Basis | Draft review state |
76
+ | --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | -------------------------------------------------- |
77
+ | FR1 | The current presented sheet contains one Sheet panel, one scrolling Content area, and one decorative Handle; a Scrim is present only in scrim-backed presentation. | Current source, docs, and tests | Verified current behavior; no new behavior decided |
78
+ | FR2 | The Sheet panel carries `bottom-sheet`; Content area, Handle, and Scrim carry no BottomSheet public target. | Current source, docs, and tests | Verified current behavior; no target change |
79
+ | FR3 | Content renders in one real observed content box. Effective overflow, containment, and keyboard access come from `useScrollableArea`; the named overflowing body delegates forward Tab entry to a safe first sequential native link/button. | `spec:AST-025`; issue #5207; tests | Focus-time prototype for owner review |
74
80
 
75
81
  ### Allowed variation
76
82
 
@@ -96,8 +102,14 @@ in `BottomSheet.doc.mjs`.
96
102
 
97
103
  ## Accessibility contract
98
104
 
99
- This draft does not change or extend BottomSheet's existing dialog naming,
100
- focus, Handle, keyboard, or dismissal behavior.
105
+ - **AR1 — Keyboard scroll path.** The named body is a tab stop while effectively
106
+ overflowing. On forward Tab entry, the shared hook may delegate to the first
107
+ sequential native link/button outside navigation-key-owning surfaces. It keeps
108
+ the body stop for excluded content and skips the body on reverse traversal
109
+ from the delegated first child.
110
+ - **AR2 — Focus continuity.** Content, overflow, or eligibility changes MUST NOT
111
+ move focus. Pointer/programmatic body focus never delegates. A focused body
112
+ losing overflow remains programmatically focusable.
101
113
 
102
114
  ## Design relationships
103
115
 
@@ -143,18 +155,20 @@ a decision that they must remain unthemeable.
143
155
  mapping, and factual `none` dispositions.
144
156
  - `architecture:layer-runtime` owns the current native-dialog host distinction
145
157
  and records that sheets retain local scrim and swipe behavior.
158
+ - `spec:AST-025` owns effective scroll measurement, overflow and containment,
159
+ automatic keyboard-owner selection, and focus continuity.
146
160
  - `family:overlay-dismissal` owns shared Escape and platform-close ordering and
147
161
  records BottomSheet's current local-only adoption gap. This anatomy backfill
148
162
  does not migrate that runtime behavior.
149
163
 
150
164
  ## Verification map
151
165
 
152
- | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
153
- | ------------------- | ------------------------------------------------------------------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------- | --------------------------- |
154
- | FR1 | `BottomSheet.test.tsx` render, content, Handle, and scrim behavior suites | Modal, non-modal, and switcher presentations | Removing a documented part fails existing content, structure, or dismissal assertions. | `audit:BottomSheet/anatomy` |
155
- | FR2 | Panel target assertion, source inspection, and theming target inventories | Sheet panel, Content area, Handle, and Scrim | Removing the panel target or documenting an unshipped child target fails evidence or inventory. | `audit:BottomSheet/theming` |
156
- | Layout evidence | `BottomSheetPanel.test.tsx` | Floating Handle and scrolling Content area | Reordering or merging the stable parts fails existing panel structure and style assertions. | `audit:BottomSheet/anatomy` |
157
- | Theming anatomy map | `scripts/check-knowledge.mjs` | Canonical anatomy and current target inventory | Missing, extra, prefixed, stale, or multiply assigned mappings fail repository validation. | `audit:BottomSheet/theming` |
166
+ | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
167
+ | ------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------- | --------------------------------- |
168
+ | FR1 | `BottomSheet.test.tsx` render, content, Handle, and scrim behavior suites | Modal, non-modal, and switcher presentations | Removing a documented part fails existing content, structure, or dismissal assertions. | `audit:BottomSheet/anatomy` |
169
+ | FR2 | Panel target assertion, source inspection, and theming target inventories | Sheet panel, Content area, Handle, and Scrim | Removing the panel target or documenting an unshipped child target fails evidence or inventory. | `audit:BottomSheet/theming` |
170
+ | FR3, AR1–AR2 | `BottomSheetKeyboard.test.tsx`; `BottomSheetKeyboard.a11y.browser.spec.ts`; open Storybook stories | fitting/overflow, plain text, usable/unusable/dynamic descendants, focus | Missing/duplicate keyboard access, stale ownership, focus movement, or broken content-box layout fails. | `audit:BottomSheet/accessibility` | | Layout evidence | `BottomSheetPanel.test.tsx` | Floating Handle and scrolling Content area | Reordering or merging the stable parts fails existing panel structure and style assertions. | `audit:BottomSheet/anatomy` |
171
+ | Theming anatomy map | `scripts/check-knowledge.mjs` | Canonical anatomy and current target inventory | Missing, extra, prefixed, stale, or multiply assigned mappings fail repository validation. | `audit:BottomSheet/theming` |
158
172
 
159
173
  Existing tests directly assert the Sheet panel target, Content area placement,
160
174
  Handle structure, and scrim behavior. Source and public target metadata confirm
@@ -162,8 +176,20 @@ that the other three parts have no public target.
162
176
 
163
177
  ## Decision log
164
178
 
165
- None. This draft records current facts and introduces no component-local design,
166
- API, theming, or layer-system decision.
179
+ ### DEC-1 — BottomSheet adopts automatic shared keyboard ownership
180
+
181
+ **Reference:** `component:BottomSheet/DEC-1`
182
+ **Decider:** cixzhang, 2026-09-13
183
+
184
+ BottomSheet supplies its existing label to
185
+ `keyboardAccess.owner="contentOrViewport"`. The shared hook keeps the overflowing
186
+ body named and tabbable, delegating forward keyboard entry to a safe first native
187
+ link/button. Pointer/programmatic focus and excluded content keep the body path. BottomSheet retains `stickyContainment="always"` because the
188
+ body deliberately remains the sheet-local Sticky boundary.
189
+
190
+ A real flow-root content box supplies the hook's observation seam while preserving
191
+ representative margin, float, percentage-height, flex/grid-child, and hug-height
192
+ behavior.
167
193
 
168
194
  ## Open questions
169
195
 
@@ -3,7 +3,7 @@
3
3
  /**
4
4
  * @file BottomSheet.test.tsx
5
5
  * @input Uses vitest, @testing-library/react, BottomSheet component
6
- * @output Unit tests for BottomSheet component behavior
6
+ * @output Unit tests for BottomSheet behavior and observed content anatomy
7
7
  * @position Core testing; validates BottomSheet.tsx implementation
8
8
  *
9
9
  * SYNC: When BottomSheet.tsx changes, update tests to match new behavior
@@ -290,7 +290,7 @@ describe('BottomSheet', () => {
290
290
  expect(onClick).toHaveBeenCalledTimes(1);
291
291
  });
292
292
 
293
- it('keeps consumer content as the last scroll-body child', () => {
293
+ it('keeps consumer content first and last inside the observed content box', () => {
294
294
  render(
295
295
  <BottomSheet isOpen onOpenChange={() => {}} label="Filters">
296
296
  <div data-testid="consumer-content">Sheet content</div>
@@ -300,7 +300,10 @@ describe('BottomSheet', () => {
300
300
  const consumer = screen.getByTestId('consumer-content');
301
301
 
302
302
  expect(body.children).toHaveLength(1);
303
- expect(body.lastElementChild).toBe(consumer);
303
+ const content = body.lastElementChild;
304
+ expect(content).toHaveAttribute('data-scroll-content');
305
+ expect(content?.lastElementChild).toBe(consumer);
306
+ expect(consumer.matches(':first-child')).toBe(true);
304
307
  expect(consumer.matches(':last-child')).toBe(true);
305
308
  });
306
309
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  /**
6
6
  * @file BottomSheet.tsx
7
- * @input Uses React, StyleX, core hooks/utils, BottomSheetPanel, BottomSheetSwitcherContext
7
+ * @input Uses React, StyleX, core hooks/utils, named BottomSheetPanel, BottomSheetSwitcherContext
8
8
  * @output Exports BottomSheet component and BottomSheetProps
9
9
  * @position Public BottomSheet router plus private standalone/switcher hosts
10
10
  *
@@ -379,6 +379,7 @@ function StandaloneBottomSheet({
379
379
  ref={ref}
380
380
  state={panelState}
381
381
  height={height}
382
+ label={label}
382
383
  snapPoints={snapPoints}
383
384
  isSwipeDismissAllowed={purpose === 'info'}
384
385
  isPageScrollLocked={shouldPresent && hasScrim}
@@ -537,6 +538,7 @@ function SwitcherBottomSheetItem({
537
538
  ref={ref}
538
539
  state={panelState}
539
540
  height={height}
541
+ label={label}
540
542
  snapPoints={snapPoints}
541
543
  isSwipeDismissAllowed={purpose === 'info'}
542
544
  isPageScrollLocked={hasScrim}
@@ -0,0 +1,195 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file BottomSheetKeyboard.test.tsx
5
+ * @input BottomSheetPanel, real shared scroll behavior, and explicit DOM geometry
6
+ * @output Regression coverage for viewport access, entry-time eligibility, and focus continuity
7
+ * @position BottomSheet integration tests; browser stories verify native key scrolling
8
+ */
9
+
10
+ import {act, fireEvent, render, screen, waitFor} from '@testing-library/react';
11
+ import {useState, type ReactNode} from 'react';
12
+ import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
13
+ import {BottomSheetPanel} from './BottomSheetPanel';
14
+
15
+ function Fixture({children}: {children: ReactNode}) {
16
+ return (
17
+ <BottomSheetPanel
18
+ label="Reading details"
19
+ state={{kind: 'open', entering: false}}
20
+ height="capped"
21
+ onDismiss={() => {}}
22
+ onScrimOpacity={() => {}}>
23
+ {children}
24
+ </BottomSheetPanel>
25
+ );
26
+ }
27
+
28
+ function body() {
29
+ return screen.getByRole('group', {name: 'Reading details'});
30
+ }
31
+
32
+ async function measure(scrollHeight: number, scrollWidth = 300) {
33
+ const viewport = body();
34
+ // jsdom does not resolve the shared hook's dynamic StyleX CSS variable.
35
+ viewport.style.overflowY = 'auto';
36
+ viewport.style.overflowX = 'auto';
37
+ for (const [key, value] of Object.entries({
38
+ clientHeight: 100,
39
+ clientWidth: 300,
40
+ scrollHeight,
41
+ scrollWidth,
42
+ })) {
43
+ Object.defineProperty(viewport, key, {configurable: true, value});
44
+ }
45
+ fireEvent(window, new Event('resize'));
46
+ await waitFor(() => {
47
+ expect(viewport.dataset.scrollableBlock === 'true').toBe(
48
+ scrollHeight > 101,
49
+ );
50
+ });
51
+ }
52
+
53
+ beforeEach(() => {
54
+ // jsdom has no layout. Visibility-specific cases still use real computed
55
+ // style/hidden/inert checks; native layout is covered in Storybook.
56
+ vi.spyOn(HTMLElement.prototype, 'getClientRects').mockReturnValue([
57
+ new DOMRect(0, 0, 20, 20),
58
+ ] as unknown as DOMRectList);
59
+ });
60
+
61
+ afterEach(() => {
62
+ vi.restoreAllMocks();
63
+ });
64
+
65
+ describe('BottomSheet keyboard scrolling', () => {
66
+ it('adds a named body tab stop only while text-only content overflows', async () => {
67
+ render(<Fixture>Reading details body</Fixture>);
68
+ await measure(100);
69
+ expect(body()).not.toHaveAttribute('tabindex');
70
+ await measure(300);
71
+ expect(body()).toHaveAttribute('tabindex', '0');
72
+ expect(body()).toHaveStyle({overscrollBehaviorY: 'contain'});
73
+ });
74
+
75
+ it('preserves current body focus when overflow disappears', async () => {
76
+ render(<Fixture>Reading details body</Fixture>);
77
+ await measure(300);
78
+ act(() => body().focus());
79
+ await measure(100);
80
+ expect(body()).toHaveFocus();
81
+ expect(body()).toHaveAttribute('tabindex', '-1');
82
+ expect(body()).toHaveStyle({overscrollBehaviorY: 'auto'});
83
+ });
84
+
85
+ it('retains keyboard access to content that overflows only horizontally', async () => {
86
+ render(<Fixture>Wide reading content</Fixture>);
87
+ await measure(100, 600);
88
+ await waitFor(() =>
89
+ expect(body()).toHaveAttribute('data-scrollable-inline', 'true'),
90
+ );
91
+ expect(body()).toHaveAttribute('tabindex', '0');
92
+ });
93
+
94
+ it('keeps the named viewport available alongside focusable content', async () => {
95
+ render(
96
+ <Fixture>
97
+ <button type="button">Continue reading</button>
98
+ </Fixture>,
99
+ );
100
+ await measure(300);
101
+ expect(body()).toHaveAttribute('tabindex', '0');
102
+ act(() => screen.getByRole('button').focus());
103
+ expect(screen.getByRole('button')).toHaveFocus();
104
+ });
105
+
106
+ it.each([
107
+ <button type="button" key="disabled" disabled>
108
+ Unavailable
109
+ </button>,
110
+ <button type="button" key="hidden" hidden>
111
+ Hidden
112
+ </button>,
113
+ <button type="button" key="negative" tabIndex={-2}>
114
+ Programmatic only
115
+ </button>,
116
+ <button type="button" key="visibility" style={{visibility: 'hidden'}}>
117
+ Hidden by CSS
118
+ </button>,
119
+ <div key="inert" inert>
120
+ <button type="button">Inert</button>
121
+ </div>,
122
+ <fieldset key="fieldset" disabled>
123
+ <button type="button">Disabled by fieldset</button>
124
+ </fieldset>,
125
+ ])(
126
+ 'ignores a descendant that cannot supply sequential keyboard access (%#)',
127
+ async child => {
128
+ render(<Fixture>{child}</Fixture>);
129
+ await measure(300);
130
+ expect(body()).toHaveAttribute('tabindex', '0');
131
+ },
132
+ );
133
+
134
+ it('does not move focus when a nested child changes', async () => {
135
+ let setHasControl: (value: boolean) => void = () => {};
136
+ function AsyncContent() {
137
+ const [hasControl, update] = useState(false);
138
+ setHasControl = update;
139
+ return hasControl ? (
140
+ <button type="button">Loaded action</button>
141
+ ) : (
142
+ <p>Loading text</p>
143
+ );
144
+ }
145
+ render(
146
+ <Fixture>
147
+ <AsyncContent />
148
+ </Fixture>,
149
+ );
150
+ await measure(300);
151
+ act(() => body().focus());
152
+ act(() => setHasControl(true));
153
+ expect(body()).toHaveAttribute('tabindex', '0');
154
+ expect(body()).toHaveFocus();
155
+ act(() => setHasControl(false));
156
+ await waitFor(() => expect(body()).toHaveAttribute('tabindex', '0'));
157
+ });
158
+
159
+ it('retains the viewport when an existing descendant becomes disabled', async () => {
160
+ render(
161
+ <Fixture>
162
+ <button type="button">Continue reading</button>
163
+ </Fixture>,
164
+ );
165
+ await measure(300);
166
+ screen.getByRole('button').setAttribute('disabled', '');
167
+ await waitFor(() => expect(body()).toHaveAttribute('tabindex', '0'));
168
+ });
169
+
170
+ it.each(['content', 'ancestor'])(
171
+ 'retains the viewport when %s data attributes change child visibility',
172
+ async target => {
173
+ const {container} = render(
174
+ <Fixture>
175
+ <style>
176
+ {'[data-actions="hidden"] button { visibility: hidden; }'}
177
+ </style>
178
+ <div data-testid="conditional-content">
179
+ <button type="button">Conditional action</button>
180
+ </div>
181
+ </Fixture>,
182
+ );
183
+ await measure(300);
184
+ expect(body()).toHaveAttribute('tabindex', '0');
185
+ const owner =
186
+ target === 'content'
187
+ ? screen.getByTestId('conditional-content')
188
+ : container;
189
+ owner.setAttribute('data-actions', 'hidden');
190
+ await waitFor(() => expect(body()).toHaveAttribute('tabindex', '0'));
191
+ owner.removeAttribute('data-actions');
192
+ expect(body()).toHaveAttribute('tabindex', '0');
193
+ },
194
+ );
195
+ });