@astryxdesign/core 0.5.1 → 0.5.2-canary.00477a5

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 (270) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/BottomSheet/BottomSheet.d.ts +3 -1
  3. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  4. package/dist/BottomSheet/BottomSheet.js +4 -3
  5. package/dist/BottomSheet/BottomSheetEdgeTint.d.ts +2 -2
  6. package/dist/BottomSheet/BottomSheetEdgeTint.d.ts.map +1 -1
  7. package/dist/BottomSheet/BottomSheetEdgeTint.js +7 -10
  8. package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
  9. package/dist/BottomSheet/BottomSheetSwitcher.js +1 -1
  10. package/dist/Button/Button.d.ts +2 -1
  11. package/dist/Button/Button.d.ts.map +1 -1
  12. package/dist/Button/Button.js +11 -4
  13. package/dist/Carousel/Carousel.d.ts.map +1 -1
  14. package/dist/Carousel/Carousel.js +2 -2
  15. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  16. package/dist/Chat/ChatToolCalls.js +34 -17
  17. package/dist/ContextMenu/ContextMenu.d.ts +14 -3
  18. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  19. package/dist/ContextMenu/ContextMenu.js +149 -26
  20. package/dist/ContextMenu/index.d.ts +1 -0
  21. package/dist/ContextMenu/index.d.ts.map +1 -1
  22. package/dist/DropdownMenu/DropdownMenu.d.ts +24 -5
  23. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  24. package/dist/DropdownMenu/DropdownMenu.js +340 -26
  25. package/dist/DropdownMenu/DropdownMenuContext.d.ts +1 -1
  26. package/dist/DropdownMenu/DropdownMenuContext.d.ts.map +1 -1
  27. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  28. package/dist/DropdownMenu/DropdownMenuItem.js +1 -1
  29. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +6 -2
  30. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  31. package/dist/DropdownMenu/DropdownMenuSubMenu.js +43 -14
  32. package/dist/DropdownMenu/MenuBottomSheet.d.ts +20 -0
  33. package/dist/DropdownMenu/MenuBottomSheet.d.ts.map +1 -0
  34. package/dist/DropdownMenu/MenuBottomSheet.js +36 -0
  35. package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts +10 -0
  36. package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts.map +1 -0
  37. package/dist/DropdownMenu/MenuBottomSheetActionList.js +111 -0
  38. package/dist/DropdownMenu/index.d.ts +2 -1
  39. package/dist/DropdownMenu/index.d.ts.map +1 -1
  40. package/dist/DropdownMenu/menuWidth.d.ts +14 -0
  41. package/dist/DropdownMenu/menuWidth.d.ts.map +1 -0
  42. package/dist/DropdownMenu/menuWidth.js +35 -0
  43. package/dist/DropdownMenu/useMenuOverflow.d.ts +10 -0
  44. package/dist/DropdownMenu/useMenuOverflow.d.ts.map +1 -0
  45. package/dist/DropdownMenu/useMenuOverflow.js +60 -0
  46. package/dist/MoreMenu/MoreMenu.d.ts +8 -2
  47. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  48. package/dist/MoreMenu/MoreMenu.js +2 -0
  49. package/dist/MultiSelector/MultiSelector.d.ts +11 -1
  50. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  51. package/dist/MultiSelector/MultiSelector.js +97 -68
  52. package/dist/MultiSelector/index.d.ts +1 -1
  53. package/dist/MultiSelector/index.d.ts.map +1 -1
  54. package/dist/Popover/Popover.d.ts +10 -6
  55. package/dist/Popover/Popover.d.ts.map +1 -1
  56. package/dist/Popover/Popover.js +141 -16
  57. package/dist/Popover/usePopover.d.ts +4 -0
  58. package/dist/Popover/usePopover.d.ts.map +1 -1
  59. package/dist/Popover/usePopover.js +52 -5
  60. package/dist/Selector/Selector.d.ts +10 -0
  61. package/dist/Selector/Selector.d.ts.map +1 -1
  62. package/dist/Selector/Selector.js +106 -80
  63. package/dist/Selector/SelectorBottomSheet.d.ts +21 -0
  64. package/dist/Selector/SelectorBottomSheet.d.ts.map +1 -0
  65. package/dist/Selector/SelectorBottomSheet.js +83 -0
  66. package/dist/Selector/index.d.ts +1 -1
  67. package/dist/Selector/index.d.ts.map +1 -1
  68. package/dist/Selector/selectorPresentation.stylex.d.ts +16 -0
  69. package/dist/Selector/selectorPresentation.stylex.d.ts.map +1 -0
  70. package/dist/Selector/selectorPresentation.stylex.js +21 -0
  71. package/dist/Selector/useSelectorPresentation.d.ts +32 -0
  72. package/dist/Selector/useSelectorPresentation.d.ts.map +1 -0
  73. package/dist/Selector/useSelectorPresentation.js +90 -0
  74. package/dist/Toast/useToastGesture.d.ts.map +1 -1
  75. package/dist/Toast/useToastGesture.js +46 -9
  76. package/dist/astryx.css +19 -1
  77. package/dist/hooks/useAdaptivePresentation.d.ts +5 -0
  78. package/dist/hooks/useAdaptivePresentation.d.ts.map +1 -0
  79. package/dist/hooks/useAdaptivePresentation.js +19 -0
  80. package/dist/hooks/useFocusReturnVisibility.d.ts +7 -0
  81. package/dist/hooks/useFocusReturnVisibility.d.ts.map +1 -0
  82. package/dist/hooks/useFocusReturnVisibility.js +35 -0
  83. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  84. package/dist/hooks/useFocusTrap.js +12 -0
  85. package/dist/i18n/InternationalizationContext.d.ts +2 -0
  86. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  87. package/dist/i18n/InternationalizationContext.js +3 -1
  88. package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
  89. package/dist/i18n/InternationalizationProvider.js +3 -1
  90. package/dist/i18n/resolve.d.ts +1 -1
  91. package/dist/i18n/resolve.d.ts.map +1 -1
  92. package/dist/i18n/resolve.js +37 -38
  93. package/dist/i18n/useTranslator.d.ts.map +1 -1
  94. package/dist/i18n/useTranslator.js +2 -3
  95. package/locales/af-ZA.json +0 -4
  96. package/locales/ar-SA.json +0 -4
  97. package/locales/ca-ES.json +0 -4
  98. package/locales/cs-CZ.json +0 -4
  99. package/locales/da-DK.json +0 -4
  100. package/locales/de-DE.json +0 -4
  101. package/locales/el-GR.json +0 -4
  102. package/locales/en.json +20 -0
  103. package/locales/es-ES.json +0 -4
  104. package/locales/fi-FI.json +0 -4
  105. package/locales/fr-FR.json +0 -4
  106. package/locales/he-IL.json +0 -4
  107. package/locales/hu-HU.json +0 -4
  108. package/locales/it-IT.json +0 -4
  109. package/locales/ja-JP.json +0 -4
  110. package/locales/ko-KR.json +0 -4
  111. package/locales/nl-NL.json +0 -4
  112. package/locales/no-NO.json +0 -4
  113. package/locales/pl-PL.json +0 -4
  114. package/locales/pseudo.json +15 -0
  115. package/locales/pt-BR.json +0 -4
  116. package/locales/pt-PT.json +0 -4
  117. package/locales/ro-RO.json +0 -4
  118. package/locales/ru-RU.json +0 -4
  119. package/locales/sr-SP.json +0 -4
  120. package/locales/sv-SE.json +0 -4
  121. package/locales/tr-TR.json +0 -4
  122. package/locales/uk-UA.json +0 -4
  123. package/locales/vi-VN.json +0 -4
  124. package/locales/zh-CN.json +0 -4
  125. package/locales/zh-TW.json +0 -4
  126. package/package.json +3 -3
  127. package/src/BottomSheet/BottomSheet.doc.mjs +37 -0
  128. package/src/BottomSheet/BottomSheet.spec.md +177 -0
  129. package/src/BottomSheet/BottomSheet.test.tsx +29 -0
  130. package/src/BottomSheet/BottomSheet.tsx +7 -4
  131. package/src/BottomSheet/BottomSheetEdgeTint.test.tsx +4 -6
  132. package/src/BottomSheet/BottomSheetEdgeTint.tsx +7 -10
  133. package/src/BottomSheet/BottomSheetSwitcher.tsx +1 -2
  134. package/src/Button/Button.doc.mjs +56 -0
  135. package/src/Button/Button.test.tsx +10 -0
  136. package/src/Button/Button.tsx +13 -5
  137. package/src/ButtonGroup/ButtonGroup.doc.mjs +47 -0
  138. package/src/Calendar/Calendar.doc.mjs +1 -1
  139. package/src/Carousel/Carousel.test.tsx +83 -0
  140. package/src/Carousel/Carousel.tsx +8 -2
  141. package/src/Chat/ChatToolCalls.test.tsx +44 -0
  142. package/src/Chat/ChatToolCalls.tsx +36 -15
  143. package/src/CheckboxInput/CheckboxInput.doc.mjs +1 -1
  144. package/src/CheckboxList/CheckboxList.doc.mjs +173 -32
  145. package/src/CheckboxList/CheckboxList.spec.md +214 -0
  146. package/src/CommandPalette/CommandPalette.doc.mjs +72 -0
  147. package/src/CommandPalette/CommandPalette.spec.md +223 -0
  148. package/src/ComplexSelector/ComplexSelector.doc.mjs +44 -2
  149. package/src/ComplexSelector/ComplexSelector.spec.md +189 -0
  150. package/src/ContextMenu/ContextMenu.doc.mjs +13 -1
  151. package/src/ContextMenu/ContextMenu.test.tsx +131 -1
  152. package/src/ContextMenu/ContextMenu.tsx +199 -32
  153. package/src/ContextMenu/index.ts +1 -0
  154. package/src/DateInput/DateInput.doc.mjs +1 -1
  155. package/src/DateRangeInput/DateRangeInput.doc.mjs +1 -1
  156. package/src/DateTimeInput/DateTimeInput.doc.mjs +1 -1
  157. package/src/Divider/Divider.doc.mjs +24 -0
  158. package/src/Divider/Divider.spec.md +150 -0
  159. package/src/DropdownMenu/DropdownMenu.doc.mjs +201 -40
  160. package/src/DropdownMenu/DropdownMenu.test.tsx +518 -7
  161. package/src/DropdownMenu/DropdownMenu.tsx +459 -28
  162. package/src/DropdownMenu/DropdownMenuContext.tsx +1 -1
  163. package/src/DropdownMenu/DropdownMenuItem.tsx +2 -0
  164. package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +4 -2
  165. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +124 -0
  166. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +72 -17
  167. package/src/DropdownMenu/MenuBottomSheet.tsx +46 -0
  168. package/src/DropdownMenu/MenuBottomSheetActionList.tsx +141 -0
  169. package/src/DropdownMenu/index.ts +2 -0
  170. package/src/DropdownMenu/menuWidth.ts +51 -0
  171. package/src/DropdownMenu/useMenuOverflow.ts +77 -0
  172. package/src/Field/Field.doc.mjs +1 -1
  173. package/src/FieldStatus/FieldStatus.doc.mjs +22 -1
  174. package/src/FieldStatus/FieldStatus.spec.md +212 -0
  175. package/src/FileInput/FileInput.doc.mjs +1 -1
  176. package/src/Grid/Grid.doc.mjs +19 -0
  177. package/src/Grid/Grid.spec.md +161 -0
  178. package/src/Icon/Icon.doc.mjs +196 -48
  179. package/src/Icon/Icon.spec.md +191 -0
  180. package/src/IconButton/IconButton.doc.mjs +38 -0
  181. package/src/Indicator/Indicator.doc.mjs +1 -1
  182. package/src/InputGroup/InputGroup.doc.mjs +1 -1
  183. package/src/Kbd/Kbd.doc.mjs +18 -0
  184. package/src/Kbd/Kbd.spec.md +141 -0
  185. package/src/Layout/Layout.doc.mjs +32 -0
  186. package/src/Layout/Layout.spec.md +199 -0
  187. package/src/Lightbox/Lightbox.doc.mjs +149 -33
  188. package/src/Lightbox/Lightbox.spec.md +197 -0
  189. package/src/Markdown/Markdown.doc.mjs +60 -0
  190. package/src/Markdown/Markdown.spec.md +200 -0
  191. package/src/MobileNav/MobileNav.doc.mjs +140 -32
  192. package/src/MobileNav/MobileNav.spec.md +208 -0
  193. package/src/MoreMenu/MoreMenu.doc.mjs +17 -0
  194. package/src/MoreMenu/MoreMenu.test.tsx +47 -0
  195. package/src/MoreMenu/MoreMenu.tsx +13 -1
  196. package/src/MultiSelector/MultiSelector.doc.mjs +129 -1
  197. package/src/MultiSelector/MultiSelector.spec.md +266 -0
  198. package/src/MultiSelector/MultiSelector.test.tsx +127 -0
  199. package/src/MultiSelector/MultiSelector.tsx +121 -73
  200. package/src/MultiSelector/index.ts +1 -0
  201. package/src/NavIcon/NavIcon.doc.mjs +17 -0
  202. package/src/NavIcon/NavIcon.spec.md +133 -0
  203. package/src/NavMenu/NavHeadingMenu.spec.md +205 -0
  204. package/src/NavMenu/NavMenu.doc.mjs +37 -0
  205. package/src/NumberInput/NumberInput.doc.mjs +1 -1
  206. package/src/Outline/Outline.doc.mjs +35 -0
  207. package/src/Outline/Outline.spec.md +161 -0
  208. package/src/OverflowList/OverflowList.doc.mjs +24 -0
  209. package/src/OverflowList/OverflowList.spec.md +156 -0
  210. package/src/Pagination/Pagination.doc.mjs +74 -0
  211. package/src/Pagination/Pagination.spec.md +225 -0
  212. package/src/Popover/Popover.doc.mjs +11 -11
  213. package/src/Popover/Popover.test.tsx +397 -2
  214. package/src/Popover/Popover.tsx +228 -21
  215. package/src/Popover/usePopover.tsx +92 -15
  216. package/src/PowerSearch/PowerSearch.doc.mjs +1 -1
  217. package/src/ProgressBar/ProgressBar.doc.mjs +198 -38
  218. package/src/ProgressBar/ProgressBar.spec.md +260 -0
  219. package/src/RadioList/RadioList.doc.mjs +1 -1
  220. package/src/Resizable/Resizable.doc.mjs +59 -16
  221. package/src/Resizable/Resizable.spec.md +194 -0
  222. package/src/Section/Section.doc.mjs +17 -0
  223. package/src/Section/Section.spec.md +138 -0
  224. package/src/SegmentedControl/SegmentedControl.doc.mjs +83 -0
  225. package/src/SegmentedControl/SegmentedControl.spec.md +151 -0
  226. package/src/Selector/Selector.doc.mjs +13 -1
  227. package/src/Selector/Selector.test.tsx +135 -0
  228. package/src/Selector/Selector.tsx +141 -82
  229. package/src/Selector/SelectorBottomSheet.tsx +97 -0
  230. package/src/Selector/index.ts +1 -0
  231. package/src/Selector/selectorPresentation.stylex.ts +20 -0
  232. package/src/Selector/useSelectorPresentation.ts +147 -0
  233. package/src/Skeleton/Skeleton.doc.mjs +12 -0
  234. package/src/Skeleton/Skeleton.spec.md +126 -0
  235. package/src/Slider/Slider.doc.mjs +186 -32
  236. package/src/Slider/Slider.spec.md +223 -0
  237. package/src/Stack/Stack.doc.mjs +22 -0
  238. package/src/Stack/Stack.spec.md +154 -0
  239. package/src/StatusDot/StatusDot.doc.mjs +17 -0
  240. package/src/StatusDot/StatusDot.spec.md +137 -0
  241. package/src/Switch/Switch.doc.mjs +41 -1
  242. package/src/Switch/Switch.spec.md +174 -0
  243. package/src/Text/Text.doc.mjs +190 -43
  244. package/src/Text/Text.spec.md +205 -0
  245. package/src/TextArea/TextArea.doc.mjs +256 -46
  246. package/src/TextArea/TextArea.spec.md +204 -0
  247. package/src/TextInput/TextInput.doc.mjs +1 -1
  248. package/src/TimeInput/TimeInput.doc.mjs +1 -1
  249. package/src/Toast/ToastViewport.test.tsx +118 -4
  250. package/src/Toast/useToastGesture.ts +61 -8
  251. package/src/ToggleButton/ToggleButton.doc.mjs +56 -0
  252. package/src/Tokenizer/Tokenizer.doc.mjs +1 -1
  253. package/src/Toolbar/Toolbar.doc.mjs +19 -0
  254. package/src/Toolbar/Toolbar.spec.md +173 -0
  255. package/src/Tooltip/Tooltip.doc.mjs +17 -0
  256. package/src/Tooltip/Tooltip.spec.md +141 -0
  257. package/src/TreeList/TreeList.doc.mjs +58 -0
  258. package/src/TreeList/TreeList.spec.md +220 -0
  259. package/src/Typeahead/Typeahead.doc.mjs +84 -2
  260. package/src/Typeahead/Typeahead.spec.md +250 -0
  261. package/src/hooks/useAdaptivePresentation.ts +35 -0
  262. package/src/hooks/useFocusReturnVisibility.ts +45 -0
  263. package/src/hooks/useFocusTrap.test.tsx +40 -0
  264. package/src/hooks/useFocusTrap.ts +13 -0
  265. package/src/i18n/InternationalizationContext.ts +3 -0
  266. package/src/i18n/InternationalizationProvider.tsx +8 -1
  267. package/src/i18n/__tests__/resolve.test.ts +23 -1
  268. package/src/i18n/resolve.ts +46 -49
  269. package/src/i18n/useTranslator.ts +2 -7
  270. package/src/theme/themingTargets.test.ts +91 -25
@@ -0,0 +1,151 @@
1
+ ---
2
+ schema_version: 1
3
+ template_version: 3
4
+ kind: component
5
+ id: component:SegmentedControl
6
+ authority: draft
7
+ archive_reason: null
8
+ superseded_by: null
9
+ approved_by: null
10
+ approved_at: null
11
+ owners: [cixzhang]
12
+ review_triggers: [theming, accessibility]
13
+ verified_by:
14
+ [
15
+ packages/core/src/SegmentedControl/SegmentedControl.test.tsx,
16
+ packages/core/src/theme/themingTargets.test.ts,
17
+ scripts/check-knowledge.mjs,
18
+ ]
19
+ families: []
20
+ design_specs: []
21
+ architecture: [architecture:component-theming-surface]
22
+ contributing: []
23
+ system_specs: []
24
+ ---
25
+
26
+ # SegmentedControl component contract
27
+
28
+ ## Intent
29
+
30
+ SegmentedControl presents a group of mutually exclusive choices through
31
+ SegmentedControlItem children. This draft records the current consumer anatomy
32
+ and theming ownership without changing selection behavior, targets, or public
33
+ API.
34
+
35
+ ## Compatibility and migration
36
+
37
+ - Released default preserved: `yes`
38
+ - Compatibility class: additive documentation only; runtime, DOM, styling,
39
+ targets, and public API remain unchanged
40
+ - Controlled/uncontrolled behavior: unchanged; selection remains controlled by
41
+ `value` and `onChange`
42
+ - Migration decision: none
43
+
44
+ Consumer migration instructions belong in consumer docs and release notes.
45
+
46
+ ## Ownership boundary
47
+
48
+ **Owns**
49
+
50
+ - The radiogroup control and its current `segmented-control` theming target.
51
+ - SegmentedControlItem's segment button and its current
52
+ `segmented-control-item` theming target.
53
+ - Rendering optional icon content and a visible label inside a segment.
54
+
55
+ **Does not own / non-goals**
56
+
57
+ - The artwork supplied through an item's `icon` prop — owned by the caller.
58
+ - A separate anatomy part or target for selected state; selection is state on the
59
+ segment target.
60
+ - A visible control-level label; the control's required `label` is its accessible
61
+ name and is not rendered as visual anatomy.
62
+
63
+ ## Public concepts
64
+
65
+ No new public concept is introduced. Consumer props and usage remain documented
66
+ in `SegmentedControl.doc.mjs` and `SegmentedControlItem.doc.mjs`.
67
+
68
+ ## Behavioral and layout contract
69
+
70
+ | ID | Candidate invariant | Basis | Draft review state |
71
+ | --- | ------------------------------------------------------------------------------------------------------------- | ------------------------------- | -------------------------------------------------- |
72
+ | FR1 | The radiogroup container carries the current `segmented-control` target and renders its supplied children. | Current source, docs, and tests | Verified current behavior; no new behavior decided |
73
+ | FR2 | Each SegmentedControlItem renders one radio button carrying the current `segmented-control-item` target. | Current source, docs, and tests | Verified current behavior; no new behavior decided |
74
+ | FR3 | A visible item label and optional icon render inside the item without separate public targets. | Current source and tests | Verified current behavior; no target change |
75
+ | FR4 | Selected and disabled remain reflected states on `segmented-control-item`, not standalone anatomy or targets. | Current source, docs, and tests | Verified current behavior; no target change |
76
+
77
+ ### Allowed variation
78
+
79
+ - A segment may render its visible label, an icon and label, or an icon while the
80
+ required label supplies the accessible name.
81
+
82
+ ### Representative states
83
+
84
+ - Selected, unselected, disabled, and enabled items retain the same anatomy.
85
+ Selection and disabled styling vary on the segment target.
86
+
87
+ ### Transformation and precedence order
88
+
89
+ - No new selection, focus, layout, or styling precedence rule is introduced.
90
+
91
+ ### Performance and resources
92
+
93
+ - No new performance or resource rule is introduced.
94
+
95
+ ## Accessibility contract
96
+
97
+ This draft does not change or extend the existing radiogroup, radio, accessible
98
+ name, roving focus, disabled-message, or selection behavior.
99
+
100
+ ## Design relationships
101
+
102
+ | Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
103
+ | ---------------- | ----------------------------------------------------------- | ------------------------------ | -------------- | ------------------ |
104
+ | Control | Presents the current grouped choice container. | Current source and public docs | Supporting | FR1 |
105
+ | Segment | Presents one mutually exclusive choice and its item states. | Current source and public docs | Prominent | FR2, FR4 |
106
+ | Label | Identifies a segment visually when not hidden. | Current source and public docs | Prominent | FR3 |
107
+ | Icon | Presents optional caller-supplied artwork within a segment. | Caller-supplied content | Supporting | FR3 |
108
+
109
+ Selection is a state of Segment, not a separate anatomy part.
110
+
111
+ ### Theming anatomy
112
+
113
+ <!-- anatomy-theming:v1 -->
114
+
115
+ ```json
116
+ {
117
+ "Control": {"target": "segmented-control"},
118
+ "Segment": {"target": "segmented-control-item"},
119
+ "Label": {"inherits": "segmented-control-item"},
120
+ "Icon": {"inherits": "segmented-control-item"}
121
+ }
122
+ ```
123
+
124
+ ## Family and system relationships
125
+
126
+ - `architecture:component-theming-surface` owns anatomy qualification, inherited
127
+ parts, and state-on-target rules.
128
+
129
+ ## Verification map
130
+
131
+ | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
132
+ | ------------------- | ----------------------------------------------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------- | -------------------------------- |
133
+ | FR1, FR2 | `SegmentedControl.test.tsx` radiogroup and rendering suites | Control with multiple segment items | Removing the group or item semantics fails existing role and content assertions. | `audit:SegmentedControl/anatomy` |
134
+ | FR3 | `SegmentedControl.test.tsx` label and icon suites | Visible label, icon with label, icon-only item | Removing or changing current label/icon rendering fails existing DOM assertions. | `audit:SegmentedControl/anatomy` |
135
+ | FR4 | `SegmentedControl.test.tsx` selection and disabled suites | Selected, unselected, and disabled items | Breaking reflected item state fails current ARIA, class, or data-attribute assertions. | `audit:SegmentedControl/theming` |
136
+ | Target inventory | `packages/core/src/theme/themingTargets.test.ts` | Control and item targets | Runtime and documented target metadata drift fails target validation. | `audit:SegmentedControl/theming` |
137
+ | Theming anatomy map | `scripts/check-knowledge.mjs` | Canonical anatomy and both current targets | Missing, extra, prefixed, or stale mappings fail repository validation. | `audit:SegmentedControl/theming` |
138
+
139
+ ## Decision log
140
+
141
+ None. This draft records current facts and introduces no component-local design,
142
+ selection, accessibility, or API decision.
143
+
144
+ ## Open questions
145
+
146
+ None.
147
+
148
+ ## Content boundary
149
+
150
+ This file does not duplicate consumer prop tables, examples, selection or focus
151
+ mechanics, implementation steps, or system rules. It links to their owners.
@@ -6,7 +6,7 @@ export const docs = {
6
6
  name: 'Selector',
7
7
  displayName: 'Selector',
8
8
  group: 'Selector',
9
- category: 'Data Input',
9
+ category: 'Form Controls',
10
10
  keywords: [
11
11
  'selector',
12
12
  'select',
@@ -194,6 +194,13 @@ export const docs = {
194
194
  'Which edge of the option row carries the selected mark. start reserves a mark column ahead of every label so they stay aligned, the way a native menu does; end is the house convention shared with Typeahead and CommandPalette.',
195
195
  default: "'end'",
196
196
  },
197
+ {
198
+ name: 'presentation',
199
+ type: "'popover' | 'bottom-sheet' | 'adaptive'",
200
+ description:
201
+ 'How the option list is presented. adaptive uses a bottom sheet on compact touch screens and an anchored popover otherwise.',
202
+ default: "'popover'",
203
+ },
197
204
  {
198
205
  name: 'width',
199
206
  type: 'SizeValue',
@@ -253,6 +260,11 @@ export const docs = {
253
260
  description:
254
261
  'Use variant="ghost" when a selector sits in a toolbar with ghost buttons. If validation status is needed there, prefer statusVariant="tooltip" so the toolbar height stays compact.',
255
262
  },
263
+ {
264
+ guidance: true,
265
+ description:
266
+ 'Use presentation="adaptive" when the selector should become a bottom sheet on compact touch screens.',
267
+ },
256
268
  {
257
269
  guidance: false,
258
270
  description:
@@ -10,6 +10,7 @@
10
10
  */
11
11
 
12
12
  import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
13
+ import {readFileSync} from 'node:fs';
13
14
  import {
14
15
  act,
15
16
  render,
@@ -19,6 +20,7 @@ import {
19
20
  within,
20
21
  } from '@testing-library/react';
21
22
  import userEvent from '@testing-library/user-event';
23
+ import * as stylex from '@stylexjs/stylex';
22
24
  import {useState} from 'react';
23
25
  import type {ReactNode} from 'react';
24
26
  import {Selector} from './Selector';
@@ -35,6 +37,7 @@ import {defineTheme} from '../theme/defineTheme';
35
37
  import {Theme} from '../theme/Theme';
36
38
  import {generateThemeCSS} from '../theme/generateThemeRules';
37
39
  import {spacingVars} from '../theme/tokens.stylex';
40
+ import {selectorPresentationStyles} from './selectorPresentation.stylex';
38
41
 
39
42
  function generateThemeTestCSS(theme: Parameters<typeof generateThemeCSS>[0]) {
40
43
  const {prose, component} = generateThemeCSS(theme);
@@ -45,6 +48,28 @@ function generateThemeTestCSS(theme: Parameters<typeof generateThemeCSS>[0]) {
45
48
  beforeEach(() => {
46
49
  // The live regions are a document-level singleton; start each test clean.
47
50
  __resetLiveRegionsForTest();
51
+ __resetInteractionModalityForTest();
52
+ HTMLDialogElement.prototype.showModal = vi.fn(function (
53
+ this: HTMLDialogElement,
54
+ ) {
55
+ this.setAttribute('open', '');
56
+ });
57
+ HTMLDialogElement.prototype.close = vi.fn(function (this: HTMLDialogElement) {
58
+ this.removeAttribute('open');
59
+ });
60
+ vi.stubGlobal(
61
+ 'matchMedia',
62
+ vi.fn().mockImplementation((query: string) => ({
63
+ matches: false,
64
+ media: query,
65
+ onchange: null,
66
+ addEventListener: vi.fn(),
67
+ removeEventListener: vi.fn(),
68
+ addListener: vi.fn(),
69
+ removeListener: vi.fn(),
70
+ dispatchEvent: vi.fn(),
71
+ })),
72
+ );
48
73
  HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) {
49
74
  this.setAttribute('popover-open', '');
50
75
  const event = new Event('toggle', {bubbles: false});
@@ -228,6 +253,116 @@ function mockSelectorRects({
228
253
  }
229
254
 
230
255
  describe('Selector', () => {
256
+ it('uses a bottom sheet and closes after a selection when requested', async () => {
257
+ const user = userEvent.setup();
258
+ const onChange = vi.fn();
259
+ render(
260
+ <Selector
261
+ label="Fruit"
262
+ options={OPTIONS}
263
+ onChange={onChange}
264
+ presentation="bottom-sheet"
265
+ />,
266
+ );
267
+
268
+ const trigger = screen.getByRole('combobox');
269
+ await user.click(trigger);
270
+
271
+ expect(
272
+ await screen.findByRole('dialog', {name: 'Fruit'}),
273
+ ).toBeInTheDocument();
274
+ expect(HTMLElement.prototype.showPopover).not.toHaveBeenCalled();
275
+
276
+ await user.click(screen.getByRole('option', {name: /Banana/}));
277
+ expect(onChange).toHaveBeenCalledWith('Banana');
278
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
279
+ });
280
+
281
+ it('restores touch focus without painting a trigger focus ring', async () => {
282
+ render(
283
+ <Selector
284
+ label="Fruit"
285
+ options={OPTIONS}
286
+ onChange={() => {}}
287
+ presentation="bottom-sheet"
288
+ />,
289
+ );
290
+
291
+ const trigger = screen.getByRole('combobox');
292
+ fireEvent.pointerDown(trigger, {pointerType: 'touch'});
293
+ fireEvent.click(trigger, {detail: 1});
294
+ const option = await screen.findByRole('option', {name: /Banana/});
295
+ fireEvent.pointerDown(option, {pointerType: 'touch'});
296
+ fireEvent.click(option, {detail: 1});
297
+
298
+ trigger.focus();
299
+ expect(trigger).toHaveFocus();
300
+ expect(trigger.parentElement).toHaveClass(
301
+ stylex.props(selectorPresentationStyles.pointerRestoredFocus).className!,
302
+ );
303
+ });
304
+
305
+ it('uses content-hugging height without extra bottom content padding', () => {
306
+ const source = readFileSync(
307
+ 'packages/core/src/Selector/SelectorBottomSheet.tsx',
308
+ 'utf8',
309
+ );
310
+
311
+ expect(source).toContain('height="hug"');
312
+ expect(source).toContain('paddingBlockStart={4}');
313
+ expect(source).toContain('paddingBlockEnd={0}');
314
+ });
315
+
316
+ it('moves keyboard focus into a bottom-sheet listbox', async () => {
317
+ const user = userEvent.setup();
318
+ render(
319
+ <Selector label="Fruit" options={OPTIONS} presentation="bottom-sheet" />,
320
+ );
321
+
322
+ const trigger = screen.getByRole('combobox');
323
+ trigger.focus();
324
+ await user.keyboard('{Enter}');
325
+
326
+ await waitFor(() => expect(screen.getByRole('listbox')).toHaveFocus());
327
+ });
328
+
329
+ it('uses a bottom sheet for adaptive presentation on compact touch', async () => {
330
+ vi.stubGlobal(
331
+ 'matchMedia',
332
+ vi.fn().mockImplementation((query: string) => ({
333
+ matches: query === '(max-width: 768px) and (pointer: coarse)',
334
+ media: query,
335
+ onchange: null,
336
+ addEventListener: vi.fn(),
337
+ removeEventListener: vi.fn(),
338
+ addListener: vi.fn(),
339
+ removeListener: vi.fn(),
340
+ dispatchEvent: vi.fn(),
341
+ })),
342
+ );
343
+ const user = userEvent.setup();
344
+ render(
345
+ <Selector label="Fruit" options={OPTIONS} presentation="adaptive" />,
346
+ );
347
+
348
+ await user.click(screen.getByRole('combobox'));
349
+ expect(
350
+ await screen.findByRole('dialog', {name: 'Fruit'}),
351
+ ).toBeInTheDocument();
352
+ expect(HTMLElement.prototype.showPopover).not.toHaveBeenCalled();
353
+ });
354
+
355
+ it('keeps adaptive presentation anchored without compact touch', async () => {
356
+ const user = userEvent.setup();
357
+ render(
358
+ <Selector label="Fruit" options={OPTIONS} presentation="adaptive" />,
359
+ );
360
+
361
+ await user.click(screen.getByRole('combobox'));
362
+ expect(HTMLElement.prototype.showPopover).toHaveBeenCalledOnce();
363
+ expect(HTMLDialogElement.prototype.showModal).not.toHaveBeenCalled();
364
+ });
365
+
231
366
  it('renders with placeholder when no value', () => {
232
367
  render(<Selector label="Fruit" options={OPTIONS} placeholder="Pick one" />);
233
368
  expect(screen.getByRole('combobox')).toHaveTextContent('Pick one');