@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.
- package/CHANGELOG.md +19 -0
- package/dist/BottomSheet/BottomSheet.d.ts +3 -1
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +4 -3
- package/dist/BottomSheet/BottomSheetEdgeTint.d.ts +2 -2
- package/dist/BottomSheet/BottomSheetEdgeTint.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetEdgeTint.js +7 -10
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetSwitcher.js +1 -1
- package/dist/Button/Button.d.ts +2 -1
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +11 -4
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +2 -2
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +34 -17
- package/dist/ContextMenu/ContextMenu.d.ts +14 -3
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +149 -26
- package/dist/ContextMenu/index.d.ts +1 -0
- package/dist/ContextMenu/index.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.d.ts +24 -5
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +340 -26
- package/dist/DropdownMenu/DropdownMenuContext.d.ts +1 -1
- package/dist/DropdownMenu/DropdownMenuContext.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuItem.js +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +6 -2
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.js +43 -14
- package/dist/DropdownMenu/MenuBottomSheet.d.ts +20 -0
- package/dist/DropdownMenu/MenuBottomSheet.d.ts.map +1 -0
- package/dist/DropdownMenu/MenuBottomSheet.js +36 -0
- package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts +10 -0
- package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts.map +1 -0
- package/dist/DropdownMenu/MenuBottomSheetActionList.js +111 -0
- package/dist/DropdownMenu/index.d.ts +2 -1
- package/dist/DropdownMenu/index.d.ts.map +1 -1
- package/dist/DropdownMenu/menuWidth.d.ts +14 -0
- package/dist/DropdownMenu/menuWidth.d.ts.map +1 -0
- package/dist/DropdownMenu/menuWidth.js +35 -0
- package/dist/DropdownMenu/useMenuOverflow.d.ts +10 -0
- package/dist/DropdownMenu/useMenuOverflow.d.ts.map +1 -0
- package/dist/DropdownMenu/useMenuOverflow.js +60 -0
- package/dist/MoreMenu/MoreMenu.d.ts +8 -2
- package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
- package/dist/MoreMenu/MoreMenu.js +2 -0
- package/dist/MultiSelector/MultiSelector.d.ts +11 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +97 -68
- package/dist/MultiSelector/index.d.ts +1 -1
- package/dist/MultiSelector/index.d.ts.map +1 -1
- package/dist/Popover/Popover.d.ts +10 -6
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +141 -16
- package/dist/Popover/usePopover.d.ts +4 -0
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +52 -5
- package/dist/Selector/Selector.d.ts +10 -0
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +106 -80
- package/dist/Selector/SelectorBottomSheet.d.ts +21 -0
- package/dist/Selector/SelectorBottomSheet.d.ts.map +1 -0
- package/dist/Selector/SelectorBottomSheet.js +83 -0
- package/dist/Selector/index.d.ts +1 -1
- package/dist/Selector/index.d.ts.map +1 -1
- package/dist/Selector/selectorPresentation.stylex.d.ts +16 -0
- package/dist/Selector/selectorPresentation.stylex.d.ts.map +1 -0
- package/dist/Selector/selectorPresentation.stylex.js +21 -0
- package/dist/Selector/useSelectorPresentation.d.ts +32 -0
- package/dist/Selector/useSelectorPresentation.d.ts.map +1 -0
- package/dist/Selector/useSelectorPresentation.js +90 -0
- package/dist/Toast/useToastGesture.d.ts.map +1 -1
- package/dist/Toast/useToastGesture.js +46 -9
- package/dist/astryx.css +19 -1
- package/dist/hooks/useAdaptivePresentation.d.ts +5 -0
- package/dist/hooks/useAdaptivePresentation.d.ts.map +1 -0
- package/dist/hooks/useAdaptivePresentation.js +19 -0
- package/dist/hooks/useFocusReturnVisibility.d.ts +7 -0
- package/dist/hooks/useFocusReturnVisibility.d.ts.map +1 -0
- package/dist/hooks/useFocusReturnVisibility.js +35 -0
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +12 -0
- package/dist/i18n/InternationalizationContext.d.ts +2 -0
- package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
- package/dist/i18n/InternationalizationContext.js +3 -1
- package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
- package/dist/i18n/InternationalizationProvider.js +3 -1
- package/dist/i18n/resolve.d.ts +1 -1
- package/dist/i18n/resolve.d.ts.map +1 -1
- package/dist/i18n/resolve.js +37 -38
- package/dist/i18n/useTranslator.d.ts.map +1 -1
- package/dist/i18n/useTranslator.js +2 -3
- package/locales/af-ZA.json +0 -4
- package/locales/ar-SA.json +0 -4
- package/locales/ca-ES.json +0 -4
- package/locales/cs-CZ.json +0 -4
- package/locales/da-DK.json +0 -4
- package/locales/de-DE.json +0 -4
- package/locales/el-GR.json +0 -4
- package/locales/en.json +20 -0
- package/locales/es-ES.json +0 -4
- package/locales/fi-FI.json +0 -4
- package/locales/fr-FR.json +0 -4
- package/locales/he-IL.json +0 -4
- package/locales/hu-HU.json +0 -4
- package/locales/it-IT.json +0 -4
- package/locales/ja-JP.json +0 -4
- package/locales/ko-KR.json +0 -4
- package/locales/nl-NL.json +0 -4
- package/locales/no-NO.json +0 -4
- package/locales/pl-PL.json +0 -4
- package/locales/pseudo.json +15 -0
- package/locales/pt-BR.json +0 -4
- package/locales/pt-PT.json +0 -4
- package/locales/ro-RO.json +0 -4
- package/locales/ru-RU.json +0 -4
- package/locales/sr-SP.json +0 -4
- package/locales/sv-SE.json +0 -4
- package/locales/tr-TR.json +0 -4
- package/locales/uk-UA.json +0 -4
- package/locales/vi-VN.json +0 -4
- package/locales/zh-CN.json +0 -4
- package/locales/zh-TW.json +0 -4
- package/package.json +3 -3
- package/src/BottomSheet/BottomSheet.doc.mjs +37 -0
- package/src/BottomSheet/BottomSheet.spec.md +177 -0
- package/src/BottomSheet/BottomSheet.test.tsx +29 -0
- package/src/BottomSheet/BottomSheet.tsx +7 -4
- package/src/BottomSheet/BottomSheetEdgeTint.test.tsx +4 -6
- package/src/BottomSheet/BottomSheetEdgeTint.tsx +7 -10
- package/src/BottomSheet/BottomSheetSwitcher.tsx +1 -2
- package/src/Button/Button.doc.mjs +56 -0
- package/src/Button/Button.test.tsx +10 -0
- package/src/Button/Button.tsx +13 -5
- package/src/ButtonGroup/ButtonGroup.doc.mjs +47 -0
- package/src/Calendar/Calendar.doc.mjs +1 -1
- package/src/Carousel/Carousel.test.tsx +83 -0
- package/src/Carousel/Carousel.tsx +8 -2
- package/src/Chat/ChatToolCalls.test.tsx +44 -0
- package/src/Chat/ChatToolCalls.tsx +36 -15
- package/src/CheckboxInput/CheckboxInput.doc.mjs +1 -1
- package/src/CheckboxList/CheckboxList.doc.mjs +173 -32
- package/src/CheckboxList/CheckboxList.spec.md +214 -0
- package/src/CommandPalette/CommandPalette.doc.mjs +72 -0
- package/src/CommandPalette/CommandPalette.spec.md +223 -0
- package/src/ComplexSelector/ComplexSelector.doc.mjs +44 -2
- package/src/ComplexSelector/ComplexSelector.spec.md +189 -0
- package/src/ContextMenu/ContextMenu.doc.mjs +13 -1
- package/src/ContextMenu/ContextMenu.test.tsx +131 -1
- package/src/ContextMenu/ContextMenu.tsx +199 -32
- package/src/ContextMenu/index.ts +1 -0
- package/src/DateInput/DateInput.doc.mjs +1 -1
- package/src/DateRangeInput/DateRangeInput.doc.mjs +1 -1
- package/src/DateTimeInput/DateTimeInput.doc.mjs +1 -1
- package/src/Divider/Divider.doc.mjs +24 -0
- package/src/Divider/Divider.spec.md +150 -0
- package/src/DropdownMenu/DropdownMenu.doc.mjs +201 -40
- package/src/DropdownMenu/DropdownMenu.test.tsx +518 -7
- package/src/DropdownMenu/DropdownMenu.tsx +459 -28
- package/src/DropdownMenu/DropdownMenuContext.tsx +1 -1
- package/src/DropdownMenu/DropdownMenuItem.tsx +2 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +4 -2
- package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +124 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.tsx +72 -17
- package/src/DropdownMenu/MenuBottomSheet.tsx +46 -0
- package/src/DropdownMenu/MenuBottomSheetActionList.tsx +141 -0
- package/src/DropdownMenu/index.ts +2 -0
- package/src/DropdownMenu/menuWidth.ts +51 -0
- package/src/DropdownMenu/useMenuOverflow.ts +77 -0
- package/src/Field/Field.doc.mjs +1 -1
- package/src/FieldStatus/FieldStatus.doc.mjs +22 -1
- package/src/FieldStatus/FieldStatus.spec.md +212 -0
- package/src/FileInput/FileInput.doc.mjs +1 -1
- package/src/Grid/Grid.doc.mjs +19 -0
- package/src/Grid/Grid.spec.md +161 -0
- package/src/Icon/Icon.doc.mjs +196 -48
- package/src/Icon/Icon.spec.md +191 -0
- package/src/IconButton/IconButton.doc.mjs +38 -0
- package/src/Indicator/Indicator.doc.mjs +1 -1
- package/src/InputGroup/InputGroup.doc.mjs +1 -1
- package/src/Kbd/Kbd.doc.mjs +18 -0
- package/src/Kbd/Kbd.spec.md +141 -0
- package/src/Layout/Layout.doc.mjs +32 -0
- package/src/Layout/Layout.spec.md +199 -0
- package/src/Lightbox/Lightbox.doc.mjs +149 -33
- package/src/Lightbox/Lightbox.spec.md +197 -0
- package/src/Markdown/Markdown.doc.mjs +60 -0
- package/src/Markdown/Markdown.spec.md +200 -0
- package/src/MobileNav/MobileNav.doc.mjs +140 -32
- package/src/MobileNav/MobileNav.spec.md +208 -0
- package/src/MoreMenu/MoreMenu.doc.mjs +17 -0
- package/src/MoreMenu/MoreMenu.test.tsx +47 -0
- package/src/MoreMenu/MoreMenu.tsx +13 -1
- package/src/MultiSelector/MultiSelector.doc.mjs +129 -1
- package/src/MultiSelector/MultiSelector.spec.md +266 -0
- package/src/MultiSelector/MultiSelector.test.tsx +127 -0
- package/src/MultiSelector/MultiSelector.tsx +121 -73
- package/src/MultiSelector/index.ts +1 -0
- package/src/NavIcon/NavIcon.doc.mjs +17 -0
- package/src/NavIcon/NavIcon.spec.md +133 -0
- package/src/NavMenu/NavHeadingMenu.spec.md +205 -0
- package/src/NavMenu/NavMenu.doc.mjs +37 -0
- package/src/NumberInput/NumberInput.doc.mjs +1 -1
- package/src/Outline/Outline.doc.mjs +35 -0
- package/src/Outline/Outline.spec.md +161 -0
- package/src/OverflowList/OverflowList.doc.mjs +24 -0
- package/src/OverflowList/OverflowList.spec.md +156 -0
- package/src/Pagination/Pagination.doc.mjs +74 -0
- package/src/Pagination/Pagination.spec.md +225 -0
- package/src/Popover/Popover.doc.mjs +11 -11
- package/src/Popover/Popover.test.tsx +397 -2
- package/src/Popover/Popover.tsx +228 -21
- package/src/Popover/usePopover.tsx +92 -15
- package/src/PowerSearch/PowerSearch.doc.mjs +1 -1
- package/src/ProgressBar/ProgressBar.doc.mjs +198 -38
- package/src/ProgressBar/ProgressBar.spec.md +260 -0
- package/src/RadioList/RadioList.doc.mjs +1 -1
- package/src/Resizable/Resizable.doc.mjs +59 -16
- package/src/Resizable/Resizable.spec.md +194 -0
- package/src/Section/Section.doc.mjs +17 -0
- package/src/Section/Section.spec.md +138 -0
- package/src/SegmentedControl/SegmentedControl.doc.mjs +83 -0
- package/src/SegmentedControl/SegmentedControl.spec.md +151 -0
- package/src/Selector/Selector.doc.mjs +13 -1
- package/src/Selector/Selector.test.tsx +135 -0
- package/src/Selector/Selector.tsx +141 -82
- package/src/Selector/SelectorBottomSheet.tsx +97 -0
- package/src/Selector/index.ts +1 -0
- package/src/Selector/selectorPresentation.stylex.ts +20 -0
- package/src/Selector/useSelectorPresentation.ts +147 -0
- package/src/Skeleton/Skeleton.doc.mjs +12 -0
- package/src/Skeleton/Skeleton.spec.md +126 -0
- package/src/Slider/Slider.doc.mjs +186 -32
- package/src/Slider/Slider.spec.md +223 -0
- package/src/Stack/Stack.doc.mjs +22 -0
- package/src/Stack/Stack.spec.md +154 -0
- package/src/StatusDot/StatusDot.doc.mjs +17 -0
- package/src/StatusDot/StatusDot.spec.md +137 -0
- package/src/Switch/Switch.doc.mjs +41 -1
- package/src/Switch/Switch.spec.md +174 -0
- package/src/Text/Text.doc.mjs +190 -43
- package/src/Text/Text.spec.md +205 -0
- package/src/TextArea/TextArea.doc.mjs +256 -46
- package/src/TextArea/TextArea.spec.md +204 -0
- package/src/TextInput/TextInput.doc.mjs +1 -1
- package/src/TimeInput/TimeInput.doc.mjs +1 -1
- package/src/Toast/ToastViewport.test.tsx +118 -4
- package/src/Toast/useToastGesture.ts +61 -8
- package/src/ToggleButton/ToggleButton.doc.mjs +56 -0
- package/src/Tokenizer/Tokenizer.doc.mjs +1 -1
- package/src/Toolbar/Toolbar.doc.mjs +19 -0
- package/src/Toolbar/Toolbar.spec.md +173 -0
- package/src/Tooltip/Tooltip.doc.mjs +17 -0
- package/src/Tooltip/Tooltip.spec.md +141 -0
- package/src/TreeList/TreeList.doc.mjs +58 -0
- package/src/TreeList/TreeList.spec.md +220 -0
- package/src/Typeahead/Typeahead.doc.mjs +84 -2
- package/src/Typeahead/Typeahead.spec.md +250 -0
- package/src/hooks/useAdaptivePresentation.ts +35 -0
- package/src/hooks/useFocusReturnVisibility.ts +45 -0
- package/src/hooks/useFocusTrap.test.tsx +40 -0
- package/src/hooks/useFocusTrap.ts +13 -0
- package/src/i18n/InternationalizationContext.ts +3 -0
- package/src/i18n/InternationalizationProvider.tsx +8 -1
- package/src/i18n/__tests__/resolve.test.ts +23 -1
- package/src/i18n/resolve.ts +46 -49
- package/src/i18n/useTranslator.ts +2 -7
- 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: '
|
|
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');
|