@astryxdesign/core 0.5.2-canary.1e29ea2 → 0.5.2-canary.1f005bf
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/package.json +2 -2
- package/src/BottomSheet/BottomSheet.doc.mjs +31 -0
- package/src/BottomSheet/BottomSheet.spec.md +177 -0
- package/src/CheckboxList/CheckboxList.doc.mjs +172 -31
- package/src/CheckboxList/CheckboxList.spec.md +214 -0
- package/src/CommandPalette/CommandPalette.spec.md +2 -2
- package/src/ComplexSelector/ComplexSelector.doc.mjs +42 -0
- package/src/ComplexSelector/ComplexSelector.spec.md +189 -0
- package/src/ContextMenu/ContextMenu.doc.mjs +48 -0
- package/src/ContextMenu/ContextMenu.spec.md +206 -0
- package/src/Divider/Divider.doc.mjs +24 -0
- package/src/Divider/Divider.spec.md +150 -0
- package/src/DropdownMenu/DropdownMenu.doc.mjs +104 -0
- package/src/DropdownMenu/DropdownMenu.spec.md +245 -0
- 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/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 +42 -0
- package/src/MoreMenu/MoreMenu.spec.md +221 -0
- package/src/MultiSelector/MultiSelector.doc.mjs +112 -0
- package/src/MultiSelector/MultiSelector.spec.md +266 -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/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/ProgressBar/ProgressBar.doc.mjs +198 -38
- package/src/ProgressBar/ProgressBar.spec.md +77 -25
- 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 +27 -0
- package/src/SegmentedControl/SegmentedControl.spec.md +151 -0
- package/src/Selector/Selector.spec.md +202 -0
- package/src/Skeleton/Skeleton.doc.mjs +12 -0
- package/src/Skeleton/Skeleton.spec.md +126 -0
- package/src/Slider/Slider.doc.mjs +184 -30
- 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 +40 -0
- 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 +255 -45
- package/src/TextArea/TextArea.spec.md +204 -0
- 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.spec.md +5 -5
- package/src/Typeahead/Typeahead.doc.mjs +83 -1
- package/src/Typeahead/Typeahead.spec.md +250 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema_version: 1
|
|
3
|
+
template_version: 3
|
|
4
|
+
kind: component
|
|
5
|
+
id: component:CheckboxList
|
|
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]
|
|
13
|
+
verified_by:
|
|
14
|
+
[
|
|
15
|
+
packages/core/src/CheckboxList/CheckboxList.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
|
+
# CheckboxList component contract
|
|
27
|
+
|
|
28
|
+
## Intent
|
|
29
|
+
|
|
30
|
+
CheckboxList presents a labeled group of checkbox options. This draft records
|
|
31
|
+
its current consumer anatomy and the theming ownership of parts rendered by
|
|
32
|
+
CheckboxList, List, CheckboxInput, Field, FieldStatus, and Spinner. It changes no
|
|
33
|
+
runtime behavior or public 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
|
|
41
|
+
- Migration decision: none
|
|
42
|
+
|
|
43
|
+
Consumer migration instructions belong in consumer docs and release notes.
|
|
44
|
+
|
|
45
|
+
## Ownership boundary
|
|
46
|
+
|
|
47
|
+
**Owns**
|
|
48
|
+
|
|
49
|
+
- The checkbox-group composition and its existing `checkbox-list` target.
|
|
50
|
+
- Collection state shared with CheckboxListItem.
|
|
51
|
+
|
|
52
|
+
**Does not own / non-goals**
|
|
53
|
+
|
|
54
|
+
- List and option-row presentation — owned by `component:List`; its
|
|
55
|
+
`list-item` target reaches only the row root.
|
|
56
|
+
- Standard option-label and option-description wrappers — rendered as separate,
|
|
57
|
+
untargeted spans by Item rather than Text; rich label content and end content
|
|
58
|
+
remain caller-owned.
|
|
59
|
+
- The checkbox indicator — owned by `component:CheckboxInput`.
|
|
60
|
+
- Group-label and validation-message presentation — owned by `component:Field`
|
|
61
|
+
and `component:FieldStatus`.
|
|
62
|
+
- Loading-indicator presentation — owned by `component:Spinner`.
|
|
63
|
+
- New targets for untargeted descriptions or caller-provided item content.
|
|
64
|
+
|
|
65
|
+
## Public concepts
|
|
66
|
+
|
|
67
|
+
No new public concept is introduced. Consumer props, collection and standalone
|
|
68
|
+
item modes, and usage remain documented in `CheckboxList.doc.mjs` and
|
|
69
|
+
`CheckboxListItem.doc.mjs`.
|
|
70
|
+
|
|
71
|
+
## Behavioral and layout contract
|
|
72
|
+
|
|
73
|
+
| ID | Candidate invariant | Basis | Draft review state |
|
|
74
|
+
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------- | -------------------------------------------------- |
|
|
75
|
+
| FR1 | The current render places CheckboxListItem children inside a List and a group named by the group label. | Current source, docs, and focused tests | Verified current behavior; no new behavior decided |
|
|
76
|
+
| FR2 | The CheckboxList root carries the current `checkbox-list` target. | Current source and public docs | Verified current behavior; no target change |
|
|
77
|
+
| FR3 | The option-row root and checkbox indicator continue to be rendered by List and CheckboxInput rather than reimplemented. | Current source | Verified composition boundary |
|
|
78
|
+
| FR5 | Item renders option label and description as separate untargeted children; end content remains caller-provided inside its own untargeted slot wrapper. | Current source and focused tests | Verified child ownership; no target change |
|
|
79
|
+
| FR4 | A pending item renders Spinner inside that item's checkbox; a status message renders through detached FieldStatus. | Current source, docs, and focused tests | Verified conditional composition |
|
|
80
|
+
|
|
81
|
+
### Allowed variation
|
|
82
|
+
|
|
83
|
+
- Option labels, descriptions, and end content may vary with caller-provided
|
|
84
|
+
CheckboxListItem content without becoming new CheckboxList targets.
|
|
85
|
+
- Density, dividers, checked state, and disabled state remain capabilities of
|
|
86
|
+
their current owning components rather than separate anatomy parts.
|
|
87
|
+
|
|
88
|
+
### Representative states
|
|
89
|
+
|
|
90
|
+
| State | Required invariant | Allowed variation |
|
|
91
|
+
| ------------------------- | --------------------------------------------------------------- | ---------------------------------- |
|
|
92
|
+
| Default collection | Named group, options list, rows, and checkbox indicators render | Option content and selected values |
|
|
93
|
+
| Item pending | Only the pending checkbox includes a Spinner | Which option is pending |
|
|
94
|
+
| Group status with message | Detached FieldStatus follows the group | Error, warning, or success status |
|
|
95
|
+
|
|
96
|
+
### Transformation and precedence order
|
|
97
|
+
|
|
98
|
+
- No new state, event, layout, or styling precedence rule is introduced.
|
|
99
|
+
|
|
100
|
+
### Performance and resources
|
|
101
|
+
|
|
102
|
+
- No new performance or resource rule is introduced.
|
|
103
|
+
|
|
104
|
+
## Accessibility contract
|
|
105
|
+
|
|
106
|
+
This draft does not change or extend CheckboxList's existing group naming,
|
|
107
|
+
checkbox semantics, focus order, busy state, or description/status association.
|
|
108
|
+
|
|
109
|
+
## Design relationships
|
|
110
|
+
|
|
111
|
+
| Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
|
|
112
|
+
| ------------------ | ---------------------------------------------------------------------------- | ------------------------------- | -------------- | ------------------ |
|
|
113
|
+
| Group | Contains the current checkbox-group composition. | Current source and public docs | Supporting | FR1, FR2 |
|
|
114
|
+
| Options and rows | Present the List-owned list and row root plus Item-rendered child structure. | Current source | Supporting | FR1, FR3 |
|
|
115
|
+
| Checkbox | Presents each option's current selection indicator. | Current CheckboxInput source | Prominent | FR3 |
|
|
116
|
+
| Spinner and status | Present the current conditional feedback components. | Current shared-component source | Supporting | FR4 |
|
|
117
|
+
|
|
118
|
+
### Theming anatomy
|
|
119
|
+
|
|
120
|
+
<!-- anatomy-theming:v1 -->
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"Group": {"target": "checkbox-list"},
|
|
125
|
+
"Group label": {
|
|
126
|
+
"delegatesTo": {"owner": "component:Field", "target": "field-label"}
|
|
127
|
+
},
|
|
128
|
+
"Description": {
|
|
129
|
+
"none": {
|
|
130
|
+
"reason": "unsettled: No current public target reaches the stable Description; future exposure still needs an owner decision"
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
"Options list": {
|
|
134
|
+
"delegatesTo": {"owner": "component:List", "target": "list"}
|
|
135
|
+
},
|
|
136
|
+
"Option row": {
|
|
137
|
+
"delegatesTo": {"owner": "component:List", "target": "list-item"}
|
|
138
|
+
},
|
|
139
|
+
"Checkbox": {
|
|
140
|
+
"delegatesTo": {
|
|
141
|
+
"owner": "component:CheckboxInput",
|
|
142
|
+
"target": "checkbox-indicator"
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
"Option label": {
|
|
146
|
+
"none": {
|
|
147
|
+
"reason": "unsettled: Item rather than Text renders the stable Option label in a separate untargeted span; future exposure still needs an owner decision"
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
"Option description": {
|
|
151
|
+
"none": {
|
|
152
|
+
"reason": "unsettled: Item rather than Text renders the stable Option description in a separate untargeted span; future exposure still needs an owner decision"
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
"End content": {
|
|
156
|
+
"none": {
|
|
157
|
+
"reason": "intentional: End content is caller-provided content outside CheckboxList's public theming ownership"
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
"Spinner": {
|
|
161
|
+
"delegatesTo": {"owner": "component:Spinner", "target": "spinner"}
|
|
162
|
+
},
|
|
163
|
+
"Status message": {
|
|
164
|
+
"delegatesTo": {
|
|
165
|
+
"owner": "component:FieldStatus",
|
|
166
|
+
"target": "field-status"
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
`Description` remains stable consumer anatomy, but no current public target
|
|
173
|
+
reaches it. Item, not Text, renders option labels and descriptions in separate
|
|
174
|
+
untargeted spans, so neither delegates to Text nor inherits the row-root
|
|
175
|
+
`list-item` target; future exposure remains unsettled. Rich label content and end
|
|
176
|
+
content are caller-provided, and end content intentionally stays outside
|
|
177
|
+
CheckboxList's public theming ownership.
|
|
178
|
+
|
|
179
|
+
## Family and system relationships
|
|
180
|
+
|
|
181
|
+
- `architecture:component-theming-surface` owns anatomy qualification, factual
|
|
182
|
+
`none` dispositions, and composition-preserving target ownership.
|
|
183
|
+
- List, CheckboxInput, Field, FieldStatus, and Spinner retain their existing
|
|
184
|
+
public target contracts when composed by CheckboxList.
|
|
185
|
+
|
|
186
|
+
## Verification map
|
|
187
|
+
|
|
188
|
+
| Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
|
|
189
|
+
| ------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- |
|
|
190
|
+
| FR1, FR3, FR5 | `CheckboxList.test.tsx` structure, content, and interaction suites plus `Item.tsx` source inspection | Collection mode; string/rich label; description; end content | Removing the group, row, checkbox, or separate content children breaks existing DOM and interaction assertions. | `audit:CheckboxList/anatomy` |
|
|
191
|
+
| FR4 | `CheckboxList.test.tsx` loading and status suites | Pending item and detached status | Removing composed feedback breaks existing spinner, busy-state, or message assertions. | `audit:CheckboxList/anatomy` |
|
|
192
|
+
| Local target source | `themingTargets.test.ts` | `checkbox-list` | Source/docs target drift fails the repository target guard. | `audit:CheckboxList/theming` |
|
|
193
|
+
| Theming anatomy map | `scripts/check-knowledge.mjs` | Canonical anatomy and current local target | Canonical-key drift, invalid dispositions or target spelling, or an unclaimed current local target fails repository validation. | `audit:CheckboxList/theming` |
|
|
194
|
+
|
|
195
|
+
The focused CheckboxList suite does not pin the exact delegated target classes
|
|
196
|
+
or the local `checkbox-list` class in rendered DOM. The source/metadata target
|
|
197
|
+
guard independently verifies that each owner's emitted targets remain
|
|
198
|
+
documented. No current repository check resolves `delegatesTo` owner/target
|
|
199
|
+
pairs; the pairs in this draft were verified manually, so semantic delegation
|
|
200
|
+
drift remains a validation gap.
|
|
201
|
+
|
|
202
|
+
## Decision log
|
|
203
|
+
|
|
204
|
+
None. This draft records current facts and introduces no component-local design
|
|
205
|
+
or API decision.
|
|
206
|
+
|
|
207
|
+
## Open questions
|
|
208
|
+
|
|
209
|
+
None.
|
|
210
|
+
|
|
211
|
+
## Content boundary
|
|
212
|
+
|
|
213
|
+
This file does not duplicate consumer prop tables, examples, implementation
|
|
214
|
+
steps, or shared-component contracts. It links to their owners.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
schema_version: 1
|
|
3
|
-
template_version:
|
|
3
|
+
template_version: 3
|
|
4
4
|
kind: component
|
|
5
5
|
id: component:CommandPalette
|
|
6
6
|
authority: draft
|
|
@@ -153,7 +153,7 @@ reachability, not a decision that it must remain unthemeable.
|
|
|
153
153
|
},
|
|
154
154
|
"Query field": {
|
|
155
155
|
"none": {
|
|
156
|
-
"reason": "No current public target is applied directly to the native query field"
|
|
156
|
+
"reason": "unsettled: No current public target is applied directly to the native query field; future exposure still needs an owner decision"
|
|
157
157
|
}
|
|
158
158
|
},
|
|
159
159
|
"Loading spinner": {
|
|
@@ -9,6 +9,46 @@
|
|
|
9
9
|
* SYNC: When modified, update ComplexSelector.tsx, tests, and stories.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
/** @type {import('@astryxdesign/cli/authoring').ComponentAnatomyElement[]} */
|
|
13
|
+
const anatomy = [
|
|
14
|
+
{
|
|
15
|
+
name: 'Field',
|
|
16
|
+
required: true,
|
|
17
|
+
description:
|
|
18
|
+
'Field shell that provides the label and optional supporting field content.',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: 'Trigger',
|
|
22
|
+
required: true,
|
|
23
|
+
description:
|
|
24
|
+
'Control that displays the current value or placeholder and opens the popup.',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'Icon-rendered start icon',
|
|
28
|
+
required: false,
|
|
29
|
+
description:
|
|
30
|
+
'Optional leading semantic icon or icon component rendered through Icon.',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'Caller-rendered start content',
|
|
34
|
+
required: false,
|
|
35
|
+
description:
|
|
36
|
+
'Optional arbitrary React content rendered directly at the start of the trigger.',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'Indicator icon',
|
|
40
|
+
required: true,
|
|
41
|
+
description:
|
|
42
|
+
'Trailing chevron that rotates to reflect whether the popup is open.',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'Popup',
|
|
46
|
+
required: true,
|
|
47
|
+
description:
|
|
48
|
+
'Mounted dialog surface that is painted and shown while open and hidden while closed.',
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
|
|
12
52
|
/** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */
|
|
13
53
|
|
|
14
54
|
export const docs = {
|
|
@@ -158,6 +198,7 @@ export const docs = {
|
|
|
158
198
|
},
|
|
159
199
|
],
|
|
160
200
|
usage: {
|
|
201
|
+
anatomy,
|
|
161
202
|
description:
|
|
162
203
|
'Use ComplexSelector when a selection needs richer custom content than a Selector option row. It is intentionally one component: ComplexSelector owns the field, trigger, popover, focus restore, and changeAction flow, while the content render prop owns the selector-specific accessible structure.',
|
|
163
204
|
bestPractices: [
|
|
@@ -218,6 +259,7 @@ export const docsDense = {
|
|
|
218
259
|
description:
|
|
219
260
|
'Input/ghost trigger + dialog-popover shell for rich custom selectors. Content gets value/onChange/close/state; content owns semantics. Use focus hooks and evaluate custom content against WCAG 2.2.',
|
|
220
261
|
usage: {
|
|
262
|
+
anatomy,
|
|
221
263
|
description:
|
|
222
264
|
'Use when a selection needs richer custom content than a Selector row. One component: it owns field, trigger, popover, focus restore, and changeAction; the render prop owns the selector-specific accessible structure.',
|
|
223
265
|
bestPractices: [
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema_version: 1
|
|
3
|
+
template_version: 3
|
|
4
|
+
kind: component
|
|
5
|
+
id: component:ComplexSelector
|
|
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]
|
|
13
|
+
verified_by:
|
|
14
|
+
[
|
|
15
|
+
packages/core/src/ComplexSelector/ComplexSelector.test.tsx,
|
|
16
|
+
packages/core/src/Field/Field.test.tsx,
|
|
17
|
+
packages/core/src/Icon/Icon.test.tsx,
|
|
18
|
+
scripts/check-knowledge.mjs,
|
|
19
|
+
]
|
|
20
|
+
families: [family:overlay-dismissal]
|
|
21
|
+
design_specs: []
|
|
22
|
+
architecture:
|
|
23
|
+
[architecture:component-theming-surface, architecture:layer-runtime]
|
|
24
|
+
contributing: []
|
|
25
|
+
system_specs: []
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
# ComplexSelector component contract
|
|
29
|
+
|
|
30
|
+
## Intent
|
|
31
|
+
|
|
32
|
+
ComplexSelector renders a field-owned shell, an owned trigger, and a dialog
|
|
33
|
+
popup for caller-provided rich selection content. This draft records current
|
|
34
|
+
consumer anatomy and theming ownership without changing runtime behavior,
|
|
35
|
+
styling, targets, or public API.
|
|
36
|
+
|
|
37
|
+
## Compatibility and migration
|
|
38
|
+
|
|
39
|
+
- Released default preserved: `yes`
|
|
40
|
+
- Compatibility class: additive documentation only; runtime, DOM, styling,
|
|
41
|
+
targets, and public API remain unchanged
|
|
42
|
+
- Controlled/uncontrolled behavior: unchanged
|
|
43
|
+
- Migration decision: none
|
|
44
|
+
|
|
45
|
+
Consumer migration instructions belong in consumer docs and release notes.
|
|
46
|
+
|
|
47
|
+
## Ownership boundary
|
|
48
|
+
|
|
49
|
+
**Owns**
|
|
50
|
+
|
|
51
|
+
- The trigger and its current `complex-selector` target.
|
|
52
|
+
- The trailing indicator icon and its current
|
|
53
|
+
`complex-selector-indicator-icon` target.
|
|
54
|
+
- The painted popup surface and its current `complex-selector-popup` target.
|
|
55
|
+
|
|
56
|
+
**Does not own / non-goals**
|
|
57
|
+
|
|
58
|
+
- Field-shell presentation — rendered by Field and themed through Field's
|
|
59
|
+
current `field` target.
|
|
60
|
+
- General Icon presentation — rendered by Icon for semantic icon names and icon
|
|
61
|
+
component types and themed through Icon's current `icon` target.
|
|
62
|
+
- Arbitrary ReactNode content supplied through `startIcon` — rendered directly
|
|
63
|
+
and owned by the product callsite.
|
|
64
|
+
- The selector-specific structure supplied through `children` — owned by the
|
|
65
|
+
product callsite.
|
|
66
|
+
- Shared layer lifecycle, positioning, and dismissal behavior.
|
|
67
|
+
|
|
68
|
+
## Public concepts
|
|
69
|
+
|
|
70
|
+
No new public concept is introduced. Consumer props and usage remain documented
|
|
71
|
+
in `ComplexSelector.doc.mjs`.
|
|
72
|
+
|
|
73
|
+
## Behavioral and layout contract
|
|
74
|
+
|
|
75
|
+
| ID | Candidate invariant | Basis | Draft review state |
|
|
76
|
+
| --- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | -------------------------------------------------- |
|
|
77
|
+
| FR1 | The current render contains a Field, Trigger, Indicator icon, and mounted Popup; one optional start slot may contain either an Icon-rendered start icon or caller-rendered start content. | Current source, docs, and tests | Verified current behavior; no new behavior decided |
|
|
78
|
+
| FR2 | Trigger, Indicator icon, and Popup carry `complex-selector`, `complex-selector-indicator-icon`, and `complex-selector-popup`, respectively. | Current source, docs, and tests | Verified current behavior; no target change |
|
|
79
|
+
| FR3 | Field delegates to Field's `field` target, and a semantic name or icon component in the start slot delegates to Icon's `icon` target. | Current source and owner tests | Verified current behavior; no target change |
|
|
80
|
+
| FR4 | Arbitrary ReactNode start content renders directly and carries no ComplexSelector-owned target. | Current source | Verified current behavior; no target change |
|
|
81
|
+
|
|
82
|
+
### Allowed variation
|
|
83
|
+
|
|
84
|
+
- Caller-provided popup content may vary without becoming a new
|
|
85
|
+
ComplexSelector-owned anatomy part or target.
|
|
86
|
+
- The optional start slot has two factual branches: semantic names and icon
|
|
87
|
+
component types render through Icon, while arbitrary ReactNode content renders
|
|
88
|
+
directly under caller ownership.
|
|
89
|
+
|
|
90
|
+
### Representative states
|
|
91
|
+
|
|
92
|
+
- Input and ghost variants use the same six-part anatomy and current targets.
|
|
93
|
+
- The start slot is absent when `startIcon` is omitted. When present, exactly
|
|
94
|
+
one of Icon-rendered start icon or Caller-rendered start content applies.
|
|
95
|
+
- The Popup remains mounted in both states: it is hidden while closed and shown
|
|
96
|
+
while open. The Indicator icon remains present and reflects collapsed or
|
|
97
|
+
expanded state on its current target.
|
|
98
|
+
|
|
99
|
+
### Transformation and precedence order
|
|
100
|
+
|
|
101
|
+
- No new value, open-state, positioning, or style precedence rule is introduced.
|
|
102
|
+
|
|
103
|
+
### Performance and resources
|
|
104
|
+
|
|
105
|
+
- No new performance or resource rule is introduced.
|
|
106
|
+
|
|
107
|
+
## Accessibility contract
|
|
108
|
+
|
|
109
|
+
This draft does not change or extend ComplexSelector's existing field, trigger,
|
|
110
|
+
dialog, focus, or keyboard behavior.
|
|
111
|
+
|
|
112
|
+
## Design relationships
|
|
113
|
+
|
|
114
|
+
| Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
|
|
115
|
+
| ----------------------------- | -------------------------------------------------------------------------------- | ------------------------------ | ----------------- | ------------------ |
|
|
116
|
+
| Field | Provides the field shell around the owned trigger. | Field component | Supporting | FR1, FR3 |
|
|
117
|
+
| Trigger | Displays the current value or placeholder and opens the popup. | Current source and public docs | Prominent | FR1, FR2 |
|
|
118
|
+
| Icon-rendered start icon | Optionally presents a semantic or component icon through Icon. | Icon component | Supporting | FR1, FR3 |
|
|
119
|
+
| Caller-rendered start content | Optionally presents arbitrary React content directly in the start slot. | Caller-supplied content | Context-dependent | FR1, FR4 |
|
|
120
|
+
| Indicator icon | Presents the trailing disclosure glyph and reflects collapsed or expanded state. | Current source and public docs | Supporting | FR1, FR2 |
|
|
121
|
+
| Popup | Keeps the dialog surface mounted, hidden while closed and painted while open. | Current source and public docs | Prominent | FR1, FR2 |
|
|
122
|
+
|
|
123
|
+
The Field and Icon delegations preserve their existing owners. Arbitrary
|
|
124
|
+
ReactNode start content remains caller-owned and receives no local target. The
|
|
125
|
+
local Indicator icon target remains distinct because it carries the selector's
|
|
126
|
+
expanded/collapsed state and rotation on the glyph itself. Caller-provided popup
|
|
127
|
+
content remains outside the owned anatomy inventory.
|
|
128
|
+
|
|
129
|
+
### Theming anatomy
|
|
130
|
+
|
|
131
|
+
<!-- anatomy-theming:v1 -->
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"Field": {"delegatesTo": {"owner": "component:Field", "target": "field"}},
|
|
136
|
+
"Trigger": {"target": "complex-selector"},
|
|
137
|
+
"Icon-rendered start icon": {
|
|
138
|
+
"delegatesTo": {"owner": "component:Icon", "target": "icon"}
|
|
139
|
+
},
|
|
140
|
+
"Caller-rendered start content": {
|
|
141
|
+
"none": {
|
|
142
|
+
"reason": "intentional: Arbitrary ReactNode content is caller-owned and receives no ComplexSelector target."
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
"Indicator icon": {"target": "complex-selector-indicator-icon"},
|
|
146
|
+
"Popup": {"target": "complex-selector-popup"}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Family and system relationships
|
|
151
|
+
|
|
152
|
+
- `architecture:component-theming-surface` owns anatomy qualification, local
|
|
153
|
+
target mapping, and delegation rules.
|
|
154
|
+
- `architecture:layer-runtime` owns the current `usePopover` host, positioning,
|
|
155
|
+
native light-dismiss, and visibility reconciliation used by the Popup.
|
|
156
|
+
- `family:overlay-dismissal` owns shared Escape and platform-close ordering;
|
|
157
|
+
ComplexSelector participates through its composed Popover owner.
|
|
158
|
+
- Field and Icon retain ownership of their delegated targets and presentation.
|
|
159
|
+
|
|
160
|
+
## Verification map
|
|
161
|
+
|
|
162
|
+
| Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
|
|
163
|
+
| ------------------- | -------------------------------------------------------------------------- | ------------------------------------------------ | --------------------------------------------------------------------------------------------- | ------------------------------- |
|
|
164
|
+
| FR1 | `ComplexSelector.test.tsx` render, ghost-trigger, and popup suites | Default closed, ghost start branches, open Popup | Removing a documented part fails existing role, content, or structure assertions. | `audit:ComplexSelector/anatomy` |
|
|
165
|
+
| FR2 | Component target suites, source inspection, and theming target inventories | Closed/open Popup; expanded Indicator icon | Removing or renaming a current local target fails component assertions or target inventories. | `audit:ComplexSelector/theming` |
|
|
166
|
+
| FR3, FR4 | Icon owner tests and `renderIconSlot` source inspection | Semantic/component icon; arbitrary ReactNode | A branch gains the wrong owner, loses its target, or receives an invented local target. | `audit:ComplexSelector/theming` |
|
|
167
|
+
| Theming anatomy map | `scripts/check-knowledge.mjs` | Canonical anatomy and current local targets | Missing, extra, prefixed, stale, or multiply assigned mappings fail repository validation. | `audit:ComplexSelector/theming` |
|
|
168
|
+
|
|
169
|
+
Current component tests directly assert the Trigger and Popup targets. Source
|
|
170
|
+
inspection confirms that non-lazy `useLayer` keeps the Popup mounted while
|
|
171
|
+
closed. The Indicator icon target is covered by source inspection and shared
|
|
172
|
+
target inventories rather than a dedicated component assertion. `renderIconSlot`
|
|
173
|
+
and Icon owner tests distinguish the Icon-rendered branch from arbitrary
|
|
174
|
+
caller-owned ReactNode content.
|
|
175
|
+
|
|
176
|
+
## Decision log
|
|
177
|
+
|
|
178
|
+
None. This draft records current facts and introduces no component-local design,
|
|
179
|
+
API, theming, or layer-system decision.
|
|
180
|
+
|
|
181
|
+
## Open questions
|
|
182
|
+
|
|
183
|
+
None.
|
|
184
|
+
|
|
185
|
+
## Content boundary
|
|
186
|
+
|
|
187
|
+
This file does not duplicate consumer prop tables, examples, caller-provided
|
|
188
|
+
popup structure, implementation steps, or shared layer and theming rules. It
|
|
189
|
+
links to their owners.
|
|
@@ -1,4 +1,50 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').ComponentAnatomyElement[]} */
|
|
4
|
+
const anatomy = [
|
|
5
|
+
{
|
|
6
|
+
name: 'Trigger area',
|
|
7
|
+
required: true,
|
|
8
|
+
description:
|
|
9
|
+
'Caller-provided region that accepts right-click, keyboard context-menu, and long-press input.',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
name: 'Pointer menu surface',
|
|
13
|
+
required: false,
|
|
14
|
+
description:
|
|
15
|
+
'Cursor-positioned menu panel used by the pointer presentation.',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: 'Pointer action row',
|
|
19
|
+
required: false,
|
|
20
|
+
description:
|
|
21
|
+
'DropdownMenu-owned action, selectable option, or submenu row in the pointer presentation.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'Touch sheet frame',
|
|
25
|
+
required: false,
|
|
26
|
+
description:
|
|
27
|
+
'BottomSheet panel, content area, handle, and optional scrim that host touch actions.',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'Touch menu surface',
|
|
31
|
+
required: false,
|
|
32
|
+
description:
|
|
33
|
+
'ContextMenu-owned content panel rendered inside the touch sheet frame.',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'Touch action list',
|
|
37
|
+
required: false,
|
|
38
|
+
description: 'Spacious List that groups data-driven touch actions.',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'Touch action row',
|
|
42
|
+
required: false,
|
|
43
|
+
description:
|
|
44
|
+
'ListItem button used for a data-driven action or drill-in entry in the touch presentation.',
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
|
|
2
48
|
/** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */
|
|
3
49
|
|
|
4
50
|
export const docs = {
|
|
@@ -80,6 +126,7 @@ export const docs = {
|
|
|
80
126
|
{name: 'ContextMenuItem'},
|
|
81
127
|
],
|
|
82
128
|
usage: {
|
|
129
|
+
anatomy,
|
|
83
130
|
description: 'A right-click context menu that appears at the cursor position. Use to provide contextual actions for specific elements or regions without cluttering the UI with visible buttons.',
|
|
84
131
|
bestPractices: [
|
|
85
132
|
{ guidance: true, description: 'Keep menu items concise and action-oriented; users expect quick access to contextual actions.' },
|
|
@@ -128,6 +175,7 @@ export const docsZh = {
|
|
|
128
175
|
export const docsDense = {
|
|
129
176
|
description: 'right-click context menu at cursor position',
|
|
130
177
|
usage: {
|
|
178
|
+
anatomy,
|
|
131
179
|
description: 'A right-click context menu that appears at the cursor position. Use to provide contextual actions for specific elements or regions.',
|
|
132
180
|
bestPractices: [
|
|
133
181
|
{ guidance: true, description: 'Keep items concise and action-oriented.' },
|