@astryxdesign/core 0.5.2-canary.2da7a39 → 0.5.2-canary.2e51213

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 (375) hide show
  1. package/dist/AppShell/AppShell.d.ts +2 -2
  2. package/dist/AppShell/AppShell.d.ts.map +1 -1
  3. package/dist/AppShell/AppShell.js +10 -7
  4. package/dist/Banner/Banner.d.ts +5 -0
  5. package/dist/Banner/Banner.d.ts.map +1 -1
  6. package/dist/Banner/Banner.js +7 -2
  7. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +3 -2
  8. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  9. package/dist/Breadcrumbs/BreadcrumbItem.js +22 -15
  10. package/dist/Carousel/Carousel.d.ts.map +1 -1
  11. package/dist/Carousel/Carousel.js +2 -2
  12. package/dist/Chat/ChatComposer.d.ts +2 -2
  13. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  14. package/dist/Chat/ChatComposer.js +21 -2
  15. package/dist/Chat/ChatToolCalls.d.ts +1 -1
  16. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  17. package/dist/Chat/ChatToolCalls.js +12 -34
  18. package/dist/Chat/useChatNewMessages.js +3 -3
  19. package/dist/CheckboxList/CheckboxListItem.d.ts +5 -3
  20. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  21. package/dist/DateTimeInput/DateTimeInput.d.ts +47 -13
  22. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  23. package/dist/DateTimeInput/DateTimeInput.js +269 -162
  24. package/dist/DateTimeInput/NativeDateSegment.d.ts +24 -0
  25. package/dist/DateTimeInput/NativeDateSegment.d.ts.map +1 -0
  26. package/dist/DateTimeInput/NativeDateSegment.js +235 -0
  27. package/dist/DateTimeInput/NativeTimeSegment.d.ts +30 -0
  28. package/dist/DateTimeInput/NativeTimeSegment.d.ts.map +1 -0
  29. package/dist/DateTimeInput/NativeTimeSegment.js +248 -0
  30. package/dist/DateTimeInput/index.d.ts +1 -1
  31. package/dist/DateTimeInput/index.d.ts.map +1 -1
  32. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts +95 -0
  33. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts.map +1 -0
  34. package/dist/DateTimeInput/nativePickerSegmentStyles.js +103 -0
  35. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  36. package/dist/DropdownMenu/DropdownMenu.js +2 -4
  37. package/dist/Field/Field.d.ts.map +1 -1
  38. package/dist/Field/Field.js +10 -4
  39. package/dist/Field/PanelSearchInput.d.ts.map +1 -1
  40. package/dist/Field/PanelSearchInput.js +3 -5
  41. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  42. package/dist/FieldStatus/FieldStatus.js +4 -2
  43. package/dist/Layer/useTouchTrigger.d.ts.map +1 -1
  44. package/dist/Layer/useTouchTrigger.js +2 -4
  45. package/dist/Layout/Layout.d.ts +7 -8
  46. package/dist/Layout/Layout.d.ts.map +1 -1
  47. package/dist/Layout/Layout.js +7 -8
  48. package/dist/MultiSelector/MultiSelector.d.ts +11 -1
  49. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  50. package/dist/MultiSelector/MultiSelector.js +89 -45
  51. package/dist/RadioList/RadioListItem.d.ts +30 -5
  52. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  53. package/dist/RadioList/RadioListItem.js +17 -4
  54. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  55. package/dist/Resizable/ResizeHandle.js +102 -44
  56. package/dist/Resizable/index.d.ts +2 -0
  57. package/dist/Resizable/index.d.ts.map +1 -1
  58. package/dist/Resizable/index.js +1 -0
  59. package/dist/Resizable/useResizable.d.ts +89 -8
  60. package/dist/Resizable/useResizable.d.ts.map +1 -1
  61. package/dist/Resizable/useResizable.js +521 -38
  62. package/dist/Resizable/utils.d.ts +35 -0
  63. package/dist/Resizable/utils.d.ts.map +1 -0
  64. package/dist/Resizable/utils.js +27 -0
  65. package/dist/Selector/Selector.d.ts +10 -0
  66. package/dist/Selector/Selector.d.ts.map +1 -1
  67. package/dist/Selector/Selector.js +105 -57
  68. package/dist/Selector/useSelectorPresentation.d.ts +1 -0
  69. package/dist/Selector/useSelectorPresentation.d.ts.map +1 -1
  70. package/dist/Selector/useSelectorPresentation.js +17 -2
  71. package/dist/Slider/Slider.d.ts.map +1 -1
  72. package/dist/Slider/Slider.js +3 -5
  73. package/dist/Spinner/Spinner.d.ts.map +1 -1
  74. package/dist/Spinner/Spinner.js +9 -10
  75. package/dist/Stepper/Step.d.ts +2 -1
  76. package/dist/Stepper/Step.d.ts.map +1 -1
  77. package/dist/Stepper/Step.js +75 -11
  78. package/dist/Stepper/Stepper.d.ts.map +1 -1
  79. package/dist/Stepper/Stepper.js +1 -0
  80. package/dist/TabList/TabList.d.ts.map +1 -1
  81. package/dist/TabList/TabList.js +2 -3
  82. package/dist/Table/index.d.ts +16 -1
  83. package/dist/Table/index.d.ts.map +1 -1
  84. package/dist/Table/index.js +4 -0
  85. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  86. package/dist/Table/plugins/columnResize/useTableColumnResize.js +8 -9
  87. package/dist/Table/plugins/rowStatus/index.d.ts +1 -1
  88. package/dist/Table/plugins/rowStatus/index.d.ts.map +1 -1
  89. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts +11 -30
  90. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -1
  91. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +108 -62
  92. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  93. package/dist/Table/plugins/sortable/useTableSortable.js +9 -1
  94. package/dist/Text/useTruncation.d.ts +1 -1
  95. package/dist/Text/useTruncation.d.ts.map +1 -1
  96. package/dist/Text/useTruncation.js +8 -7
  97. package/dist/TimeInput/TimeInput.d.ts +16 -1
  98. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  99. package/dist/TimeInput/TimeInput.js +98 -60
  100. package/dist/TimeInput/index.d.ts +1 -1
  101. package/dist/TimeInput/index.d.ts.map +1 -1
  102. package/dist/Timestamp/Timestamp.d.ts +2 -2
  103. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  104. package/dist/Timestamp/Timestamp.js +4 -145
  105. package/dist/Timestamp/formatRelativeTime.d.ts +20 -0
  106. package/dist/Timestamp/formatRelativeTime.d.ts.map +1 -0
  107. package/dist/Timestamp/formatRelativeTime.js +81 -0
  108. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  109. package/dist/Toast/ToastViewport.js +125 -77
  110. package/dist/Tokenizer/Tokenizer.d.ts +0 -38
  111. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  112. package/dist/Tokenizer/Tokenizer.js +94 -26
  113. package/dist/Tokenizer/useEndLaneReserve.d.ts +31 -0
  114. package/dist/Tokenizer/useEndLaneReserve.d.ts.map +1 -0
  115. package/dist/Tokenizer/useEndLaneReserve.js +136 -0
  116. package/dist/Tooltip/useTooltip.d.ts +1 -1
  117. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  118. package/dist/Tooltip/useTooltip.js +53 -42
  119. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  120. package/dist/Typeahead/BaseTypeahead.js +85 -30
  121. package/dist/Typeahead/Typeahead.d.ts +0 -21
  122. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  123. package/dist/Typeahead/Typeahead.js +108 -67
  124. package/dist/Typeahead/busyIndicatorLane.d.ts +62 -0
  125. package/dist/Typeahead/busyIndicatorLane.d.ts.map +1 -0
  126. package/dist/Typeahead/busyIndicatorLane.js +98 -0
  127. package/dist/astryx.css +25 -7
  128. package/dist/hooks/useFocusReturnVisibility.d.ts.map +1 -1
  129. package/dist/hooks/useFocusReturnVisibility.js +3 -5
  130. package/dist/hooks/useIsomorphicLayoutEffect.d.ts +2 -2
  131. package/dist/hooks/useIsomorphicLayoutEffect.d.ts.map +1 -1
  132. package/dist/hooks/useOverflow.js +11 -11
  133. package/dist/hooks/useScrollOverflow.d.ts.map +1 -1
  134. package/dist/hooks/useScrollOverflow.js +7 -4
  135. package/dist/theme/defineTheme.d.ts +13 -0
  136. package/dist/theme/defineTheme.d.ts.map +1 -1
  137. package/dist/theme/defineTheme.js +7 -0
  138. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  139. package/dist/theme/generateThemeRules.js +1 -1
  140. package/dist/theme/localTokens.d.ts +18 -0
  141. package/dist/theme/localTokens.d.ts.map +1 -0
  142. package/dist/theme/localTokens.js +162 -0
  143. package/dist/utils/interactionModality.d.ts +6 -28
  144. package/dist/utils/interactionModality.d.ts.map +1 -1
  145. package/dist/utils/interactionModality.js +72 -37
  146. package/dist/utils/sharedResizeObserver.d.ts +17 -9
  147. package/dist/utils/sharedResizeObserver.d.ts.map +1 -1
  148. package/dist/utils/sharedResizeObserver.js +54 -12
  149. package/locales/en.json +8 -0
  150. package/locales/pseudo.json +6 -0
  151. package/package.json +7 -2
  152. package/src/AlertDialog/useImperativeAlertDialog.test.tsx +163 -0
  153. package/src/AppShell/AppShell.doc.mjs +4 -3
  154. package/src/AppShell/AppShell.test.tsx +21 -0
  155. package/src/AppShell/AppShell.tsx +12 -7
  156. package/src/Banner/Banner.doc.mjs +5 -1
  157. package/src/Banner/Banner.tsx +12 -1
  158. package/src/BottomSheet/BottomSheet.doc.mjs +31 -0
  159. package/src/BottomSheet/BottomSheet.spec.md +178 -0
  160. package/src/Breadcrumbs/BreadcrumbItem.tsx +18 -9
  161. package/src/Breadcrumbs/Breadcrumbs.doc.mjs +4 -1
  162. package/src/Breadcrumbs/Breadcrumbs.test.tsx +12 -0
  163. package/src/Button/Button.doc.mjs +56 -0
  164. package/src/ButtonGroup/ButtonGroup.doc.mjs +47 -0
  165. package/src/Calendar/hooks/useCalendarConstraints.test.ts +153 -0
  166. package/src/Calendar/hooks/useCalendarDays.test.ts +265 -0
  167. package/src/Calendar/hooks/useCalendarNavigation.test.ts +252 -0
  168. package/src/Carousel/Carousel.test.tsx +83 -0
  169. package/src/Carousel/Carousel.tsx +8 -2
  170. package/src/Chat/ChatComposer.doc.mjs +7 -4
  171. package/src/Chat/ChatComposer.test.tsx +84 -2
  172. package/src/Chat/ChatComposer.tsx +45 -3
  173. package/src/Chat/ChatSendButton.test.tsx +189 -0
  174. package/src/Chat/ChatToolCalls.doc.mjs +1 -1
  175. package/src/Chat/ChatToolCalls.test.tsx +33 -1
  176. package/src/Chat/ChatToolCalls.tsx +13 -34
  177. package/src/Chat/useChatNewMessages.ts +3 -3
  178. package/src/Chat/useChatPasteAsToken.test.ts +191 -0
  179. package/src/CheckboxList/CheckboxList.doc.mjs +172 -31
  180. package/src/CheckboxList/CheckboxList.spec.md +215 -0
  181. package/src/CheckboxList/CheckboxList.test.tsx +45 -0
  182. package/src/CheckboxList/CheckboxListItem.doc.mjs +11 -9
  183. package/src/CheckboxList/CheckboxListItem.tsx +5 -3
  184. package/src/Collapsible/useCollapsible.test.tsx +236 -0
  185. package/src/CommandPalette/CommandPalette.doc.mjs +72 -0
  186. package/src/CommandPalette/CommandPalette.spec.md +224 -0
  187. package/src/CommandPalette/CommandPaletteEmpty.test.tsx +101 -0
  188. package/src/ComplexSelector/ComplexSelector.doc.mjs +42 -0
  189. package/src/ComplexSelector/ComplexSelector.spec.md +190 -0
  190. package/src/ContextMenu/ContextMenu.doc.mjs +48 -0
  191. package/src/ContextMenu/ContextMenu.spec.md +207 -0
  192. package/src/DateTimeInput/DateTimeInput.doc.mjs +43 -23
  193. package/src/DateTimeInput/DateTimeInput.tsx +388 -181
  194. package/src/DateTimeInput/DateTimeInputTouch.test.tsx +10 -2
  195. package/src/DateTimeInput/NativeDateSegment.tsx +308 -0
  196. package/src/DateTimeInput/NativePickerSegments.test.tsx +626 -0
  197. package/src/DateTimeInput/NativeTimeSegment.tsx +331 -0
  198. package/src/DateTimeInput/index.ts +1 -0
  199. package/src/DateTimeInput/nativePickerSegmentStyles.ts +120 -0
  200. package/src/Divider/Divider.doc.mjs +24 -0
  201. package/src/Divider/Divider.spec.md +157 -0
  202. package/src/DropdownMenu/DropdownMenu.doc.mjs +104 -0
  203. package/src/DropdownMenu/DropdownMenu.spec.md +246 -0
  204. package/src/DropdownMenu/DropdownMenu.tsx +2 -4
  205. package/src/Field/Field.doc.mjs +1 -0
  206. package/src/Field/Field.test.tsx +39 -0
  207. package/src/Field/Field.tsx +18 -1
  208. package/src/Field/InputClearButton.test.tsx +68 -6
  209. package/src/Field/PanelSearchInput.tsx +3 -5
  210. package/src/FieldStatus/FieldStatus.doc.mjs +21 -0
  211. package/src/FieldStatus/FieldStatus.spec.md +213 -0
  212. package/src/FieldStatus/FieldStatus.test.tsx +17 -0
  213. package/src/FieldStatus/FieldStatus.tsx +7 -2
  214. package/src/Grid/Grid.doc.mjs +19 -0
  215. package/src/Grid/Grid.spec.md +162 -0
  216. package/src/Icon/Icon.doc.mjs +196 -48
  217. package/src/Icon/Icon.spec.md +192 -0
  218. package/src/IconButton/IconButton.doc.mjs +38 -0
  219. package/src/Kbd/Kbd.doc.mjs +18 -0
  220. package/src/Kbd/Kbd.spec.md +142 -0
  221. package/src/Layer/useTouchTrigger.ts +2 -4
  222. package/src/Layout/Layout.doc.mjs +57 -17
  223. package/src/Layout/Layout.spec.md +235 -0
  224. package/src/Layout/Layout.test.tsx +1 -1
  225. package/src/Layout/Layout.tsx +7 -8
  226. package/src/Layout/LayoutHeader.doc.mjs +9 -0
  227. package/src/Layout/LayoutPanel.doc.mjs +4 -3
  228. package/src/Lightbox/Lightbox.doc.mjs +149 -33
  229. package/src/Lightbox/Lightbox.spec.md +198 -0
  230. package/src/Lightbox/useLightbox.test.tsx +263 -0
  231. package/src/Markdown/Markdown.doc.mjs +60 -0
  232. package/src/Markdown/Markdown.spec.md +207 -0
  233. package/src/MobileNav/MobileNav.doc.mjs +140 -32
  234. package/src/MobileNav/MobileNav.spec.md +209 -0
  235. package/src/MoreMenu/MoreMenu.doc.mjs +42 -0
  236. package/src/MoreMenu/MoreMenu.spec.md +222 -0
  237. package/src/MultiSelector/MultiSelector.doc.mjs +124 -1
  238. package/src/MultiSelector/MultiSelector.spec.md +293 -0
  239. package/src/MultiSelector/MultiSelector.test.tsx +305 -2
  240. package/src/MultiSelector/MultiSelector.tsx +124 -55
  241. package/src/NavIcon/NavIcon.doc.mjs +17 -0
  242. package/src/NavIcon/NavIcon.spec.md +134 -0
  243. package/src/NavMenu/NavHeadingMenu.spec.md +210 -0
  244. package/src/NavMenu/NavMenu.doc.mjs +37 -0
  245. package/src/Outline/Outline.doc.mjs +35 -0
  246. package/src/Outline/Outline.spec.md +162 -0
  247. package/src/OverflowList/OverflowList.doc.mjs +24 -0
  248. package/src/OverflowList/OverflowList.spec.md +157 -0
  249. package/src/Pagination/Pagination.doc.mjs +74 -0
  250. package/src/Pagination/Pagination.spec.md +226 -0
  251. package/src/Popover/Popover.doc.mjs +264 -65
  252. package/src/Popover/Popover.spec.md +421 -0
  253. package/src/Popover/usePopover.doc.mjs +208 -33
  254. package/src/PowerSearch/PowerSearch.spec.md +291 -0
  255. package/src/PowerSearch/PowerSearchFilterEditor.test.tsx +253 -0
  256. package/src/PowerSearch/PowerSearchToken.test.tsx +228 -0
  257. package/src/ProgressBar/ProgressBar.doc.mjs +198 -38
  258. package/src/ProgressBar/ProgressBar.spec.md +261 -0
  259. package/src/RadioList/RadioList.test.tsx +136 -2
  260. package/src/RadioList/RadioListItem.doc.mjs +30 -11
  261. package/src/RadioList/RadioListItem.tsx +44 -8
  262. package/src/Resizable/Resizable.doc.mjs +130 -24
  263. package/src/Resizable/Resizable.spec.md +246 -0
  264. package/src/Resizable/ResizeHandle.test.tsx +177 -56
  265. package/src/Resizable/ResizeHandle.tsx +128 -45
  266. package/src/Resizable/index.ts +2 -0
  267. package/src/Resizable/shippedDirections.test.ts +462 -0
  268. package/src/Resizable/useResizable.doc.mjs +124 -29
  269. package/src/Resizable/useResizable.test.ts +1277 -3
  270. package/src/Resizable/useResizable.ts +760 -48
  271. package/src/Resizable/utils.test.ts +74 -0
  272. package/src/Resizable/utils.ts +35 -0
  273. package/src/Section/Section.doc.mjs +17 -0
  274. package/src/Section/Section.spec.md +139 -0
  275. package/src/SegmentedControl/SegmentedControl.doc.mjs +83 -0
  276. package/src/SegmentedControl/SegmentedControl.spec.md +152 -0
  277. package/src/Selector/Selector.doc.mjs +133 -97
  278. package/src/Selector/Selector.spec.md +300 -0
  279. package/src/Selector/Selector.test.tsx +318 -1
  280. package/src/Selector/Selector.tsx +127 -60
  281. package/src/Selector/useSelectorPresentation.ts +24 -4
  282. package/src/SideNav/SideNav.test.tsx +3 -3
  283. package/src/SideNav/SideNavCollapseButton.doc.mjs +8 -0
  284. package/src/SideNav/SideNavItem.doc.mjs +1 -1
  285. package/src/Skeleton/Skeleton.doc.mjs +12 -0
  286. package/src/Skeleton/Skeleton.spec.md +127 -0
  287. package/src/Slider/Slider.doc.mjs +184 -30
  288. package/src/Slider/Slider.spec.md +224 -0
  289. package/src/Slider/Slider.tsx +2 -5
  290. package/src/Spinner/Spinner.doc.mjs +5 -5
  291. package/src/Spinner/Spinner.test.tsx +32 -7
  292. package/src/Spinner/Spinner.tsx +46 -14
  293. package/src/Stack/Stack.doc.mjs +22 -0
  294. package/src/Stack/Stack.spec.md +155 -0
  295. package/src/StatusDot/StatusDot.doc.mjs +17 -0
  296. package/src/StatusDot/StatusDot.spec.md +138 -0
  297. package/src/Stepper/Step.doc.mjs +1 -1
  298. package/src/Stepper/Step.tsx +118 -5
  299. package/src/Stepper/Stepper.doc.mjs +51 -1
  300. package/src/Stepper/Stepper.spec.md +245 -0
  301. package/src/Stepper/Stepper.test.tsx +315 -0
  302. package/src/Stepper/Stepper.tsx +8 -0
  303. package/src/Switch/Switch.doc.mjs +40 -0
  304. package/src/Switch/Switch.spec.md +175 -0
  305. package/src/TabList/TabList.tsx +2 -3
  306. package/src/Table/Table.doc.mjs +102 -24
  307. package/src/Table/Table.perf.test.tsx +51 -1
  308. package/src/Table/Table.spec.md +325 -0
  309. package/src/Table/TableHeaderCell.test.tsx +291 -0
  310. package/src/Table/index.ts +19 -4
  311. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +8 -12
  312. package/src/Table/plugins/filtering/useTableFilterState.test.tsx +143 -0
  313. package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +2 -1
  314. package/src/Table/plugins/rowStatus/index.ts +1 -4
  315. package/src/Table/plugins/rowStatus/useTableRowStatus.public.test.ts +92 -0
  316. package/src/Table/plugins/rowStatus/useTableRowStatus.spec.md +354 -0
  317. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +431 -56
  318. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +155 -84
  319. package/src/Table/plugins/sortable/useTableSortable.test.tsx +72 -0
  320. package/src/Table/plugins/sortable/useTableSortable.tsx +18 -1
  321. package/src/Table/useTableGroupedRows.doc.mjs +2 -2
  322. package/src/Table/useTableRowStatus.doc.mjs +5 -5
  323. package/src/Text/Text.doc.mjs +190 -43
  324. package/src/Text/Text.spec.md +206 -0
  325. package/src/Text/useTruncation.ts +8 -7
  326. package/src/TextArea/TextArea.doc.mjs +255 -45
  327. package/src/TextArea/TextArea.spec.md +205 -0
  328. package/src/TimeInput/NativeTimeInput.test.tsx +269 -0
  329. package/src/TimeInput/TimeInput.doc.mjs +47 -23
  330. package/src/TimeInput/TimeInput.tsx +109 -50
  331. package/src/TimeInput/index.ts +1 -0
  332. package/src/Timestamp/Timestamp.doc.mjs +2 -2
  333. package/src/Timestamp/Timestamp.test.tsx +75 -3
  334. package/src/Timestamp/Timestamp.tsx +7 -153
  335. package/src/Timestamp/formatRelativeTime.ts +96 -0
  336. package/src/Toast/ToastViewport.test.tsx +194 -0
  337. package/src/Toast/ToastViewport.tsx +136 -58
  338. package/src/ToggleButton/ToggleButton.doc.mjs +56 -0
  339. package/src/Tokenizer/Tokenizer.doc.mjs +2 -0
  340. package/src/Tokenizer/Tokenizer.spec.md +293 -0
  341. package/src/Tokenizer/Tokenizer.test.tsx +335 -1
  342. package/src/Tokenizer/Tokenizer.tsx +129 -46
  343. package/src/Tokenizer/useEndLaneReserve.ts +146 -0
  344. package/src/Toolbar/Toolbar.doc.mjs +19 -0
  345. package/src/Toolbar/Toolbar.spec.md +174 -0
  346. package/src/Tooltip/Tooltip.doc.mjs +17 -0
  347. package/src/Tooltip/Tooltip.spec.md +142 -0
  348. package/src/Tooltip/Tooltip.test.tsx +21 -1
  349. package/src/Tooltip/useTooltip.test.tsx +318 -0
  350. package/src/Tooltip/useTooltip.tsx +60 -39
  351. package/src/TreeList/TreeList.doc.mjs +58 -0
  352. package/src/TreeList/TreeList.spec.md +224 -0
  353. package/src/Typeahead/BaseTypeahead.tsx +96 -25
  354. package/src/Typeahead/Typeahead.doc.mjs +89 -1
  355. package/src/Typeahead/Typeahead.spec.md +257 -0
  356. package/src/Typeahead/Typeahead.test.tsx +502 -1
  357. package/src/Typeahead/Typeahead.tsx +187 -69
  358. package/src/Typeahead/TypeaheadItem.test.tsx +93 -2
  359. package/src/Typeahead/busyIndicatorLane.tsx +109 -0
  360. package/src/__tests__/stylexDeclarations.ts +51 -0
  361. package/src/hooks/useFocusReturnVisibility.ts +3 -5
  362. package/src/hooks/useOverflow.ts +11 -11
  363. package/src/hooks/useScrollLock.test.ts +103 -0
  364. package/src/hooks/useScrollOverflow.ts +7 -4
  365. package/src/i18n/useTranslator.test.tsx +197 -0
  366. package/src/theme/defineTheme.test.ts +227 -0
  367. package/src/theme/defineTheme.ts +30 -0
  368. package/src/theme/derivedVarRegistry.test.ts +6 -0
  369. package/src/theme/generateThemeRules.ts +4 -1
  370. package/src/theme/localTokens.ts +272 -0
  371. package/src/theme/themingTargets.test.ts +126 -28
  372. package/src/utils/interactionModality.test.ts +83 -0
  373. package/src/utils/interactionModality.ts +88 -36
  374. package/src/utils/sharedResizeObserver.test.ts +124 -5
  375. package/src/utils/sharedResizeObserver.ts +60 -14
@@ -20,8 +20,15 @@ import {
20
20
  } from 'vitest';
21
21
  import {render, screen, fireEvent, waitFor, act} from '@testing-library/react';
22
22
  import userEvent from '@testing-library/user-event';
23
+ import {Profiler, type ProfilerOnRenderCallback} from 'react';
23
24
  import {Typeahead} from './Typeahead';
25
+ import {readFile} from 'node:fs/promises';
26
+ import {resolve} from 'node:path';
24
27
  import {BaseTypeahead} from './BaseTypeahead';
28
+ import {
29
+ BusyIndicatorLaneProvider,
30
+ createBusyIndicatorLane,
31
+ } from './busyIndicatorLane';
25
32
  import type {SearchSource, SearchableItem} from './types';
26
33
  import {InternationalizationProvider} from '../i18n';
27
34
 
@@ -707,6 +714,126 @@ describe('Typeahead size', () => {
707
714
  });
708
715
 
709
716
  describe('BaseTypeahead hasEntriesOnFocus', () => {
717
+ it('does not commit a loading cycle for an empty synchronous bootstrap', async () => {
718
+ const bootstrap = vi.fn((): SearchableItem[] => []);
719
+ const onRender = vi.fn<ProfilerOnRenderCallback>();
720
+ render(
721
+ <Profiler id="typeahead" onRender={onRender}>
722
+ <BaseTypeahead
723
+ searchSource={{search: () => [], bootstrap}}
724
+ value={null}
725
+ onChange={() => {}}
726
+ hasEntriesOnFocus
727
+ />
728
+ </Profiler>,
729
+ );
730
+ const input = screen.getByRole('combobox');
731
+ onRender.mockClear();
732
+
733
+ await act(async () => {
734
+ fireEvent.focus(input);
735
+ await Promise.resolve();
736
+ });
737
+
738
+ expect(bootstrap).toHaveBeenCalledOnce();
739
+ expect(onRender).not.toHaveBeenCalled();
740
+ });
741
+
742
+ it('does not report loading for synchronous bootstrap results', async () => {
743
+ const lane = createBusyIndicatorLane();
744
+ const onLoadingChange = vi.fn(lane.onBusyChange);
745
+ render(
746
+ <BusyIndicatorLaneProvider
747
+ value={{...lane, onBusyChange: onLoadingChange}}>
748
+ <BaseTypeahead
749
+ searchSource={fruitSource}
750
+ value={null}
751
+ onChange={() => {}}
752
+ hasEntriesOnFocus
753
+ />
754
+ </BusyIndicatorLaneProvider>,
755
+ );
756
+ const input = screen.getByRole('combobox');
757
+
758
+ fireEvent.focus(input);
759
+ await waitFor(() => {
760
+ expect(input).toHaveAttribute('aria-expanded', 'true');
761
+ });
762
+
763
+ expect(onLoadingChange).not.toHaveBeenCalled();
764
+ });
765
+
766
+ it('clears a pending search loading state before synchronous bootstrap', async () => {
767
+ let settleSearch: (items: SearchableItem[]) => void = () => {};
768
+ const searchSource: SearchSource = {
769
+ search: async () =>
770
+ new Promise<SearchableItem[]>(resolve => {
771
+ settleSearch = resolve;
772
+ }),
773
+ bootstrap: () => [],
774
+ };
775
+ render(
776
+ <BaseTypeahead
777
+ searchSource={searchSource}
778
+ value={null}
779
+ onChange={() => {}}
780
+ hasEntriesOnFocus
781
+ debounceMs={0}
782
+ />,
783
+ );
784
+ const input = screen.getByRole('combobox');
785
+
786
+ fireEvent.change(input, {target: {value: 'a'}});
787
+ await waitFor(() => {
788
+ expect(screen.getByRole('status', {name: 'Loading'})).toBeInTheDocument();
789
+ });
790
+
791
+ fireEvent.change(input, {target: {value: ''}});
792
+ await waitFor(() => {
793
+ expect(
794
+ screen.queryByRole('status', {name: 'Loading'}),
795
+ ).not.toBeInTheDocument();
796
+ });
797
+
798
+ await act(async () => {
799
+ settleSearch(fruits.slice(0, 1));
800
+ await Promise.resolve();
801
+ });
802
+ expect(input).toHaveAttribute('aria-expanded', 'false');
803
+ });
804
+
805
+ it('keeps the loading state for an asynchronous bootstrap', async () => {
806
+ let settle: (items: SearchableItem[]) => void = () => {};
807
+ const bootstrap = vi.fn(
808
+ async () =>
809
+ new Promise<SearchableItem[]>(resolve => {
810
+ settle = resolve;
811
+ }),
812
+ );
813
+ render(
814
+ <BaseTypeahead
815
+ searchSource={{search: () => [], bootstrap}}
816
+ value={null}
817
+ onChange={() => {}}
818
+ hasEntriesOnFocus
819
+ />,
820
+ );
821
+ const input = screen.getByRole('combobox');
822
+
823
+ fireEvent.focus(input);
824
+ await waitFor(() => {
825
+ expect(screen.getByRole('status', {name: 'Loading'})).toBeInTheDocument();
826
+ });
827
+
828
+ await act(async () => {
829
+ settle([]);
830
+ await Promise.resolve();
831
+ });
832
+ expect(
833
+ screen.queryByRole('status', {name: 'Loading'}),
834
+ ).not.toBeInTheDocument();
835
+ });
836
+
710
837
  it('shows bootstrap results on mouse click', async () => {
711
838
  render(
712
839
  <BaseTypeahead
@@ -918,6 +1045,7 @@ describe('BaseTypeahead minQueryLength', () => {
918
1045
  await waitFor(() => {
919
1046
  expect(screen.getByRole('status', {name: 'Loading'})).toBeInTheDocument();
920
1047
  });
1048
+ expect(input).toHaveAttribute('aria-busy', 'true');
921
1049
 
922
1050
  fireEvent.change(input, {target: {value: 'Ap'}});
923
1051
  await act(async () => {
@@ -928,6 +1056,7 @@ describe('BaseTypeahead minQueryLength', () => {
928
1056
  expect(
929
1057
  screen.queryByRole('status', {name: 'Loading'}),
930
1058
  ).not.toBeInTheDocument();
1059
+ expect(input).not.toHaveAttribute('aria-busy');
931
1060
  expect(input).toHaveAttribute('aria-expanded', 'false');
932
1061
  });
933
1062
  });
@@ -1278,7 +1407,13 @@ describe('Typeahead disabledMessage', () => {
1278
1407
  />,
1279
1408
  );
1280
1409
 
1281
- const container = screen.getByRole('combobox').parentElement as HTMLElement;
1410
+ // The field itself, by its own class: the input no longer sits directly
1411
+ // inside it — it is in the content lane that bounds the value — and
1412
+ // `mouseEnter` does not bubble, so the hover has to land on the element
1413
+ // the tooltip is actually bound to.
1414
+ const container = document.querySelector(
1415
+ '.astryx-typeahead',
1416
+ ) as HTMLElement;
1282
1417
  const tooltip = screen.getByRole('tooltip', h);
1283
1418
  expect(tooltip).toHaveTextContent('You need the Editor role');
1284
1419
 
@@ -1445,3 +1580,369 @@ describe('Typeahead statusVariant forwarding', () => {
1445
1580
  );
1446
1581
  });
1447
1582
  });
1583
+
1584
+ describe('busy indicator ownership', () => {
1585
+ /** A source that stays in flight until the test settles it. */
1586
+ const pendingSource = () => {
1587
+ let settle: (items: SearchableItem[]) => void = () => {};
1588
+ return {
1589
+ source: {
1590
+ search: async () =>
1591
+ new Promise<SearchableItem[]>(resolve => {
1592
+ settle = resolve;
1593
+ }),
1594
+ bootstrap: () => [],
1595
+ },
1596
+ settle: (items: SearchableItem[] = []) => settle(items),
1597
+ };
1598
+ };
1599
+
1600
+ it('renders its own named status for a direct caller', async () => {
1601
+ // BaseTypeaheadProps is re-exported from the package entry point, so the
1602
+ // base has direct callers this repo cannot see. They painted no indicator
1603
+ // of their own — the base did it for them — so it keeps doing it, and the
1604
+ // status stays a named one rather than a bare aria-busy that only reaches
1605
+ // assistive tech.
1606
+ const {source, settle} = pendingSource();
1607
+ render(
1608
+ <BaseTypeahead
1609
+ searchSource={source}
1610
+ value={null}
1611
+ onChange={() => {}}
1612
+ debounceMs={0}
1613
+ />,
1614
+ );
1615
+ const input = screen.getByRole('combobox');
1616
+
1617
+ fireEvent.change(input, {target: {value: 'App'}});
1618
+ await waitFor(() => {
1619
+ expect(screen.getByRole('status', {name: 'Loading'})).toBeInTheDocument();
1620
+ });
1621
+ // A Spinner, not the static clock glyph this used to render: `clock`
1622
+ // means *time* everywhere else in core, and nothing about it moved while
1623
+ // a search was out.
1624
+ expect(
1625
+ screen.getByRole('status', {name: 'Loading'}).querySelector('svg'),
1626
+ ).toBeInTheDocument();
1627
+
1628
+ await act(async () => {
1629
+ settle();
1630
+ await Promise.resolve();
1631
+ });
1632
+ expect(
1633
+ screen.queryByRole('status', {name: 'Loading'}),
1634
+ ).not.toBeInTheDocument();
1635
+ });
1636
+
1637
+ it('hands the indicator over to a field that takes it, and renders none itself', async () => {
1638
+ // Typeahead and Tokenizer paint the spinner in the one inline-end lane
1639
+ // they already own. Two indicators in one field is the defect this PR
1640
+ // exists to fix, so the base must yield rather than add to it.
1641
+ const {source, settle} = pendingSource();
1642
+ // A real lane with its notify spied, rather than a hand-built object: the
1643
+ // lane is a store now, and the base must drive the store the wrappers use.
1644
+ const lane = createBusyIndicatorLane();
1645
+ const onLoadingChange = vi.fn(lane.onBusyChange);
1646
+ render(
1647
+ <BusyIndicatorLaneProvider
1648
+ value={{...lane, onBusyChange: onLoadingChange}}>
1649
+ <BaseTypeahead
1650
+ searchSource={source}
1651
+ value={null}
1652
+ onChange={() => {}}
1653
+ debounceMs={0}
1654
+ />
1655
+ </BusyIndicatorLaneProvider>,
1656
+ );
1657
+ const input = screen.getByRole('combobox');
1658
+
1659
+ fireEvent.change(input, {target: {value: 'App'}});
1660
+ await waitFor(() => {
1661
+ expect(input).toHaveAttribute('aria-busy', 'true');
1662
+ });
1663
+ // Scoped by name: the announcer for result counts is a role="status"
1664
+ // live region too, and it is not the indicator.
1665
+ expect(
1666
+ screen.queryByRole('status', {name: 'Loading'}),
1667
+ ).not.toBeInTheDocument();
1668
+ expect(onLoadingChange).toHaveBeenLastCalledWith(true);
1669
+
1670
+ await act(async () => {
1671
+ settle();
1672
+ await Promise.resolve();
1673
+ });
1674
+ expect(onLoadingChange).toHaveBeenLastCalledWith(false);
1675
+ });
1676
+
1677
+ it('keeps the busy handoff out of the exported prop surface', async () => {
1678
+ // The handoff used to be `__onLoadingChange` on BaseTypeaheadProps, which
1679
+ // the package entry point re-exports — so a builder reading the exported
1680
+ // declaration would find it and could reasonably wire it, pinning a detail
1681
+ // between two wrappers and their base as permanent API. An `@internal` tag
1682
+ // is a note to a reader; a module boundary is the actual seam.
1683
+ const entry = await import('./index');
1684
+ expect(Object.keys(entry)).not.toContain('BusyIndicatorLaneProvider');
1685
+ expect(Object.keys(entry)).not.toContain('useBusyIndicatorLane');
1686
+
1687
+ const source = await readFile(
1688
+ resolve(__dirname, 'BaseTypeahead.tsx'),
1689
+ 'utf8',
1690
+ );
1691
+ expect(source).not.toContain('__onLoadingChange');
1692
+ });
1693
+
1694
+ it('reports each transition once, and reports nothing when nothing changed', async () => {
1695
+ // Edge-triggered on purpose. Every keystroke below the query threshold
1696
+ // clears the flag, and an unconditional report would hand the wrapper a
1697
+ // `false` per character — each one a state write, and on a field that is
1698
+ // re-rendering as the user types.
1699
+ const {source, settle} = pendingSource();
1700
+ // A real lane with its notify spied, rather than a hand-built object: the
1701
+ // lane is a store now, and the base must drive the store the wrappers use.
1702
+ const lane = createBusyIndicatorLane();
1703
+ const onLoadingChange = vi.fn(lane.onBusyChange);
1704
+ render(
1705
+ <BusyIndicatorLaneProvider
1706
+ value={{...lane, onBusyChange: onLoadingChange}}>
1707
+ <BaseTypeahead
1708
+ searchSource={source}
1709
+ value={null}
1710
+ onChange={() => {}}
1711
+ minQueryLength={3}
1712
+ debounceMs={0}
1713
+ />
1714
+ </BusyIndicatorLaneProvider>,
1715
+ );
1716
+ const input = screen.getByRole('combobox');
1717
+
1718
+ // Below the threshold: no search, so nothing to report.
1719
+ fireEvent.change(input, {target: {value: 'A'}});
1720
+ fireEvent.change(input, {target: {value: 'Ap'}});
1721
+ await act(async () => {
1722
+ await Promise.resolve();
1723
+ });
1724
+ expect(onLoadingChange).not.toHaveBeenCalled();
1725
+
1726
+ fireEvent.change(input, {target: {value: 'App'}});
1727
+ await waitFor(() => {
1728
+ expect(onLoadingChange).toHaveBeenCalledTimes(1);
1729
+ });
1730
+ expect(onLoadingChange).toHaveBeenCalledWith(true);
1731
+
1732
+ // Back below the threshold: one report out, not one per keystroke.
1733
+ fireEvent.change(input, {target: {value: 'Ap'}});
1734
+ fireEvent.change(input, {target: {value: 'A'}});
1735
+ fireEvent.change(input, {target: {value: ''}});
1736
+ await act(async () => {
1737
+ settle();
1738
+ await Promise.resolve();
1739
+ });
1740
+ expect(onLoadingChange).toHaveBeenCalledTimes(2);
1741
+ expect(onLoadingChange).toHaveBeenLastCalledWith(false);
1742
+ });
1743
+ });
1744
+
1745
+ describe('end controls stay in flow', () => {
1746
+ // The fix for the transform bug, expressed as a rule rather than a
1747
+ // measurement: these controls are ordinary flex siblings of the input, so
1748
+ // they take up room and nothing has to reserve it for them. jsdom performs
1749
+ // no layout, so what is asserted is the absence of the two things that
1750
+ // stopped that being true — an out-of-flow lane, and a padding reserve fed
1751
+ // by a measured width. The geometry itself is browser-verified in the PR.
1752
+ it('renders the clear button without taking it out of flow', () => {
1753
+ const {container} = render(
1754
+ <Typeahead
1755
+ label="Fruit"
1756
+ searchSource={fruitSource}
1757
+ value={fruits[0]}
1758
+ onChange={() => {}}
1759
+ />,
1760
+ );
1761
+ const field = container.querySelector('.astryx-typeahead');
1762
+ const clear = screen.getByRole('button', {name: /clear/i});
1763
+ expect(field).toContainElement(clear);
1764
+ // The controls sit in a lane that is itself an ordinary in-flow child of
1765
+ // the field — not a box positioned over it, which is what reserved no
1766
+ // room and put the input underneath.
1767
+ const lane = clear.parentElement as HTMLElement;
1768
+ expect(lane.parentElement).toBe(field);
1769
+ expect(getComputedStyle(lane).position).not.toBe('absolute');
1770
+ });
1771
+
1772
+ it('never reserves room with a measured width', () => {
1773
+ // The custom property is Tokenizer's mechanism and must not reappear
1774
+ // here: a width measured in viewport space and spent as local padding is
1775
+ // wrong under any CSS transform (measured on Tokenizer's 123px lane,
1776
+ // scale(.5) covered 14px of the query and scale(2) left a 125.95px gap).
1777
+ const {container} = render(
1778
+ <Typeahead
1779
+ label="Fruit"
1780
+ searchSource={fruitSource}
1781
+ value={fruits[0]}
1782
+ onChange={() => {}}
1783
+ />,
1784
+ );
1785
+ expect(
1786
+ container.querySelector('[style*="--_tokenizer-end-lane-width"]'),
1787
+ ).toBeNull();
1788
+ });
1789
+
1790
+ it('holds the controls at the inline end when a token shows', () => {
1791
+ // `auto` gives free space to the margin rather than to a sibling, so the
1792
+ // controls stay in the corner in the states where the content lane is not
1793
+ // the only flexible item in the row. Without it the clear button sat
1794
+ // against the token in mid-field instead of in the corner (measured:
1795
+ // x=39 in a 300px field, against TextInput's 281).
1796
+ render(
1797
+ <Typeahead
1798
+ label="Fruit"
1799
+ searchSource={fruitSource}
1800
+ value={fruits[0]}
1801
+ onChange={() => {}}
1802
+ />,
1803
+ );
1804
+ const clear = screen.getByRole('button', {name: /clear/i});
1805
+ const lane = clear.parentElement as HTMLElement;
1806
+ expect(getComputedStyle(lane).marginInlineStart).toBe('auto');
1807
+ });
1808
+
1809
+ it('keeps the field on one row so the controls cannot be pushed off it', () => {
1810
+ // `flex-wrap` moves an item to a new line rather than shrinking it, so a
1811
+ // wrapping field put the controls on a row of their own once the token
1812
+ // got long. The shared field base does not wrap; this must not either.
1813
+ const {container} = render(
1814
+ <Typeahead
1815
+ label="Fruit"
1816
+ searchSource={fruitSource}
1817
+ value={fruits[0]}
1818
+ onChange={() => {}}
1819
+ />,
1820
+ );
1821
+ const field = container.querySelector('.astryx-typeahead') as HTMLElement;
1822
+ expect(getComputedStyle(field).flexWrap).not.toBe('wrap');
1823
+ });
1824
+
1825
+ it('keeps the input in flow while a token shows, so the field keeps its width', () => {
1826
+ // A field's width must not depend on its value. Every other field in the
1827
+ // family gets that for free: the `<input>` stays in flow and the field is
1828
+ // as wide as the input's own intrinsic width. This one used to take the
1829
+ // input out of flow and zero its width when a token showed, leaving the
1830
+ // field measuring the token — in a shrink-to-fit parent it snapped to the
1831
+ // value's length (199px to 57px in Chromium, #5560). Block parents hid it,
1832
+ // which is why no story caught it. jsdom resolves no layout, so assert the
1833
+ // mechanism: the input still occupies the row.
1834
+ render(
1835
+ <Typeahead
1836
+ label="Fruit"
1837
+ searchSource={fruitSource}
1838
+ value={fruits[0]}
1839
+ onChange={() => {}}
1840
+ />,
1841
+ );
1842
+ const input = screen.getByRole('combobox');
1843
+ const style = getComputedStyle(input);
1844
+ expect(style.position).not.toBe('absolute');
1845
+ expect(style.width).not.toBe('0px');
1846
+ expect(style.flex).not.toBe('0 0 0');
1847
+ });
1848
+
1849
+ it('paints the token over the input rather than beside it', () => {
1850
+ // In flow the token would add its own width to the row — the same
1851
+ // value-dependent sizing from the other direction, where a long value
1852
+ // grows the field instead of collapsing it.
1853
+ const {container} = render(
1854
+ <Typeahead
1855
+ label="Fruit"
1856
+ searchSource={fruitSource}
1857
+ value={fruits[0]}
1858
+ onChange={() => {}}
1859
+ />,
1860
+ );
1861
+ const token = container.querySelector('.astryx-token') as HTMLElement;
1862
+ expect(getComputedStyle(token).position).toBe('absolute');
1863
+ });
1864
+
1865
+ it('lets the token take the pointer that the hidden input would swallow', () => {
1866
+ // The input still covers that space, so it has to stop intercepting the
1867
+ // clicks that enter edit mode.
1868
+ render(
1869
+ <Typeahead
1870
+ label="Fruit"
1871
+ searchSource={fruitSource}
1872
+ value={fruits[0]}
1873
+ onChange={() => {}}
1874
+ />,
1875
+ );
1876
+ expect(getComputedStyle(screen.getByRole('combobox')).pointerEvents).toBe(
1877
+ 'none',
1878
+ );
1879
+ });
1880
+ });
1881
+
1882
+ describe('the value is bounded by the content lane', () => {
1883
+ // Keeping the input in flow fixed the field's width, but it left the token
1884
+ // positioned against the whole field, which has no idea where the end
1885
+ // controls start. A value longer than the input then ran under the clear
1886
+ // button and out past the field's own border — measured in Chromium at
1887
+ // 28-33px of overlap and up to 4px outside the border, worse than the 12px
1888
+ // of overlap on main. The lane is the box the value may occupy: an ordinary
1889
+ // flex item that ends exactly where the end lane begins.
1890
+ //
1891
+ // jsdom resolves no layout, so what is asserted here is the mechanism. The
1892
+ // geometry is browser-verified in the PR.
1893
+ const renderWithValue = () =>
1894
+ render(
1895
+ <Typeahead
1896
+ label="Fruit"
1897
+ searchSource={fruitSource}
1898
+ value={fruits[0]}
1899
+ onChange={() => {}}
1900
+ hasClear
1901
+ />,
1902
+ );
1903
+
1904
+ it('puts the input and the token in one lane, inside the field', () => {
1905
+ const {container} = renderWithValue();
1906
+ const field = container.querySelector('.astryx-typeahead');
1907
+ const input = screen.getByRole('combobox');
1908
+ const token = container.querySelector('.astryx-token') as HTMLElement;
1909
+
1910
+ const lane = input.parentElement as HTMLElement;
1911
+ expect(lane).not.toBe(field);
1912
+ expect(lane.parentElement).toBe(field);
1913
+ // The token's containing block is the lane, which is what bounds it.
1914
+ expect(token.parentElement).toBe(lane);
1915
+ expect(getComputedStyle(lane).position).toBe('relative');
1916
+ });
1917
+
1918
+ it('lets the lane yield its whole width, so a narrow field cannot overflow', () => {
1919
+ // `min-width: 0` is the half of this that the earlier `200px` floor got
1920
+ // wrong: the field states no width of its own, so it still shrinks to
1921
+ // whatever a narrow parent or an InputGroup gives it.
1922
+ const {container} = renderWithValue();
1923
+ const lane = screen.getByRole('combobox').parentElement as HTMLElement;
1924
+ const style = getComputedStyle(lane);
1925
+ expect(style.minWidth).toBe('0');
1926
+ expect(style.flexGrow).toBe('1');
1927
+ // Nothing states a width: a narrow parent gets all of it back.
1928
+ expect(
1929
+ (container.querySelector('.astryx-typeahead') as HTMLElement).style
1930
+ .minWidth,
1931
+ ).toBe('');
1932
+ });
1933
+
1934
+ it("anchors the token at the lane's end, not just its start", () => {
1935
+ // The bound that stops the value reaching the end controls. Anchored at
1936
+ // one end only, the token is capped by the field's own padding box, which
1937
+ // is past the clear button.
1938
+ const {container} = renderWithValue();
1939
+ const token = container.querySelector('.astryx-token') as HTMLElement;
1940
+ const style = getComputedStyle(token);
1941
+ expect(style.insetInlineEnd).toBe('0');
1942
+ // `fit-content` against that pair of insets is what shrink-wraps the
1943
+ // label yet still caps it at the lane; the `auto` end margin is what
1944
+ // keeps the pair from being over-constrained and dropping the end inset.
1945
+ expect(style.width).toBe('fit-content');
1946
+ expect(style.marginInlineEnd).toBe('auto');
1947
+ });
1948
+ });