@wix/agent-skills 1.31.2 → 1.33.0
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/README.md +4 -12
- package/package.json +1 -1
- package/skills/wix-app/SKILL.md +39 -15
- package/skills/wix-app/references/BACKEND_API.md +1 -1
- package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +47 -45
- package/skills/wix-app/references/SDK_MODULE_MAP.md +1 -1
- package/skills/wix-app/references/STORES_VERSIONING.md +24 -2
- package/skills/wix-app/references/dashboard-page/DATA_SOURCES.md +9 -13
- package/skills/wix-app/references/dashboard-page/IMAGE_RENDERING.md +61 -0
- package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +54 -64
- package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +25 -27
- package/skills/wix-app/references/editor-react-component/COMPONENT-CONTRACT.md +37 -30
- package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +43 -29
- package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +42 -42
- package/skills/wix-app/references/editor-react-component/DISABLED-INVALID.md +31 -0
- package/skills/wix-app/references/editor-react-component/MANIFEST-ERRORS.md +14 -1
- package/skills/wix-app/references/editor-react-component/PARTS.md +40 -9
- package/skills/wix-app/references/editor-react-component/PROPS-VS-CSS.md +22 -2
- package/skills/wix-app/references/editor-react-component/SSR.md +6 -3
- package/skills/wix-app/scripts/scan-a11y-eslint.cjs +39 -16
- package/skills/wix-app/scripts/sdk-curated.json +45 -0
- package/skills/wix-app/scripts/sdk-lookup.cjs +1180 -0
- package/skills/wix-base44-connector/SKILL.md +4 -2
- package/skills/wix-base44-connector/scripts/utils.cjs +5 -2
- package/skills/wix-headless-kit/SKILL.md +4 -1
- package/skills/wix-headless-kit/guides/api-run.md +14 -20
- package/skills/wix-headless-kit/install/pins.json +1 -1
- package/skills/wix-manage/SKILL.md +30 -8
- package/skills/wix-manage/references/automations/actions.md +134 -0
- package/skills/wix-manage/references/automations/activation-status.md +64 -48
- package/skills/wix-manage/references/automations/api-catalog.md +213 -77
- package/skills/wix-manage/references/automations/automation-model.md +199 -0
- package/skills/wix-manage/references/automations/bracket-expressions.md +132 -0
- package/skills/wix-manage/references/automations/build-and-manage-automations.md +305 -26
- package/skills/wix-manage/references/automations/triggers.md +190 -0
- package/skills/wix-manage/references/collaborators/manage-site-collaborators.md +128 -0
- package/skills/wix-manage/references/forms/forms-dashboard-navigation.md +3 -1
- package/skills/wix-manage/references/forms/read-forms-and-submissions.md +277 -0
- package/skills/wix-manage/references/forms/update-form.md +5 -0
- package/skills/wix-manage/references/seo/manage-url-redirects.md +33 -4
- package/skills/wix-manage/references/sites/create-headless-site.md +14 -5
- package/skills/wix-manage/references/sites/upload-static-site.md +373 -302
- package/skills/wix-vibe-headless/references/shared/app/components/WixManageBanner.jsx +3 -3
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Use this reference when a named part changes appearance through interaction or
|
|
4
4
|
a selectable value. A design state styles an element differently for an
|
|
5
5
|
eligible interaction (`hover`, `focus`, `disabled`, `invalid`) or per a
|
|
6
|
-
selectable value in its data (`selected`, `active`, `
|
|
6
|
+
selectable value in its data (`selected`, `active`, `checked`, …). Author states
|
|
7
7
|
in the component's `.tsx` and `.module.css`.
|
|
8
8
|
|
|
9
9
|
## Contents
|
|
@@ -23,10 +23,11 @@ in the component's `.tsx` and `.module.css`.
|
|
|
23
23
|
| Interactive — `button`/`a`/`input`/`select`/`textarea`/`summary`, an interactive `role`, or an interactive handler (`onClick`/`onMouseEnter`/`onFocus`/…) | `hover` |
|
|
24
24
|
| Input field — `select`/`textarea`; `input` except `hidden`/`button`/`submit`/`reset`/`image`; or an input-widget `role` (`checkbox`/`radio`/`switch`/`slider`/`spinbutton`/`textbox`/`searchbox`/`combobox`/`listbox`) | `focus` |
|
|
25
25
|
| Other interactive element whose editable focus appearance the user explicitly requests | `focus` |
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
|
26
|
+
| `button`/`input`/`select`/`textarea`/`fieldset` when asked or its action can be unavailable | `disabled` |
|
|
27
|
+
| `input`/`select`/`textarea` with a constraint or `setCustomValidity`; a `form`/`fieldset` with one | `invalid` |
|
|
28
|
+
| Has a selectable/variant value in its data — `selected`/`active`/`current`/`checked`/`featured` | that custom state |
|
|
29
|
+
| Whole component opens/collapses or reveals content (expandable panel, disclosure, dropdown, drawer, popup, tooltip) on click, hover or focus | root `isOpen` state, no part `open` state (§6) |
|
|
30
|
+
| None of the above, incl. a non-interactive root or part | no states, and no `:hover`/`:active`/`:focus` rules, even decorative; resting style only |
|
|
30
31
|
|
|
31
32
|
An explicit `role` overrides the tag's implicit semantics: `<input role="button">`
|
|
32
33
|
is a non-input control, while `<div role="checkbox">` is an input field.
|
|
@@ -37,8 +38,7 @@ unless the user explicitly requests editable focus styling. A control that
|
|
|
37
38
|
restyles its own outline, background, or border must still carry a standalone
|
|
38
39
|
`:focus-visible` rule as its keyboard affordance.
|
|
39
40
|
|
|
40
|
-
A
|
|
41
|
-
`isFeatured`) instead of by markup or item data — see §5. For an active-item
|
|
41
|
+
A root-level boolean prop can also drive a custom state (§5). For an active-item
|
|
42
42
|
component, drive each item's `--active` class by comparing its index with the
|
|
43
43
|
active-index prop rather than storing a per-item boolean.
|
|
44
44
|
|
|
@@ -47,7 +47,7 @@ active-index prop rather than storing a per-item boolean.
|
|
|
47
47
|
Flat: the element's own global class + `--<state>`. Because inner-part
|
|
48
48
|
global classes are prefixed with the component name, the state class is prefixed too —
|
|
49
49
|
e.g. `pricing-card-cta--hover`, `pricing-card-plan-row--selected`. Never bare
|
|
50
|
-
(`cta--hover`
|
|
50
|
+
(`cta--hover` collides with other components) and never
|
|
51
51
|
nested (`card__row--selected`).
|
|
52
52
|
|
|
53
53
|
## 3. Author CSS
|
|
@@ -56,7 +56,7 @@ Put the resting value in the bare class; put only the state override in the
|
|
|
56
56
|
state selector. When both set the same property, write the state selector
|
|
57
57
|
**before** the bare class. Manifest generation can otherwise record the state
|
|
58
58
|
value as the part's resting `defaultValue`, even though runtime CSS renders
|
|
59
|
-
correctly.
|
|
59
|
+
correctly.
|
|
60
60
|
|
|
61
61
|
- **Native design state** — pair the pseudo-class with the `:global` modifier.
|
|
62
62
|
Every exposed native state needs both selectors in the same rule; do not
|
|
@@ -101,34 +101,32 @@ The local static accessibility scan cannot measure rendered color contrast.
|
|
|
101
101
|
- **Native** — render the correct interactive element (`<button>`, an
|
|
102
102
|
interactive `role`, or a handler). No custom state class is needed, but the
|
|
103
103
|
element must still satisfy the accessibility contract for its semantics.
|
|
104
|
-
- **Custom** — toggle the global state class from the element's data
|
|
105
|
-
state class toggled in TSX needs a
|
|
106
|
-
one the manifest generator silently
|
|
104
|
+
- **Custom** — toggle the global state class from the element's data, except
|
|
105
|
+
for open/collapsed content (§6). Every state class toggled in TSX needs a
|
|
106
|
+
matching `:global()` rule in §3; without one the manifest generator silently
|
|
107
|
+
drops the state.
|
|
107
108
|
- **Inner elements** — every named inner element gets an `elementProps` entry;
|
|
108
109
|
spread it so editor-driven states reach it. On a raw HTML element also merge
|
|
109
110
|
`elementProps?.<key>.className` inline; on a skill-built sub-component the
|
|
110
|
-
spread alone suffices (it merges `className` itself). The
|
|
111
|
-
|
|
112
|
-
is prefixed (`pricing-card-cta`) — don't rename the key to match the class.
|
|
113
|
-
|
|
114
|
-
Wire native and custom states as follows:
|
|
111
|
+
spread alone suffices (it merges `className` itself). The key is the global
|
|
112
|
+
class in camelCase (`COMPONENT-CONTRACT.md`).
|
|
115
113
|
|
|
116
114
|
```tsx
|
|
117
115
|
<button
|
|
118
116
|
type="button"
|
|
119
|
-
{...elementProps?.
|
|
120
|
-
className={classNames('pricing-card-cta', styles.cta, elementProps?.
|
|
117
|
+
{...elementProps?.pricingCardCta}
|
|
118
|
+
className={classNames('pricing-card-cta', styles.cta, elementProps?.pricingCardCta?.className)}
|
|
121
119
|
>
|
|
122
120
|
{label}
|
|
123
121
|
</button>
|
|
124
122
|
|
|
125
123
|
<li
|
|
126
|
-
{...elementProps?.
|
|
124
|
+
{...elementProps?.pricingCardPlanRow}
|
|
127
125
|
className={classNames(
|
|
128
126
|
'pricing-card-plan-row',
|
|
129
127
|
styles.planRow,
|
|
130
128
|
row.selected && 'pricing-card-plan-row--selected',
|
|
131
|
-
elementProps?.
|
|
129
|
+
elementProps?.pricingCardPlanRow?.className,
|
|
132
130
|
)}
|
|
133
131
|
>
|
|
134
132
|
{row.label}
|
|
@@ -143,10 +141,9 @@ A **prop-triggered** state is a custom state switched by a single boolean
|
|
|
143
141
|
whole component's appearance — e.g. `isLoading`, `isFeatured`.
|
|
144
142
|
|
|
145
143
|
Mark the prop with the `ElementState<boolean>` type from
|
|
146
|
-
`@wix/react-component-utils`. It is an identity alias
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
on the **root element**.
|
|
144
|
+
`@wix/react-component-utils`. It is an identity alias (a plain `boolean` at
|
|
145
|
+
runtime and in data); the manifest generator detects the marker and emits a
|
|
146
|
+
custom state on the **root element**.
|
|
150
147
|
|
|
151
148
|
```tsx
|
|
152
149
|
import type { ElementState } from '@wix/react-component-utils';
|
|
@@ -178,39 +175,41 @@ Rules:
|
|
|
178
175
|
```
|
|
179
176
|
|
|
180
177
|
Prefer a native state (interactive markup) or a class trigger (per-item data
|
|
181
|
-
such as `row.selected`) whenever one fits
|
|
182
|
-
|
|
183
|
-
or an open/collapsed toggle (§6).
|
|
178
|
+
such as `row.selected`) whenever one fits; both work at any depth. Reach for a
|
|
179
|
+
prop trigger only for a root-level boolean switch.
|
|
184
180
|
|
|
185
181
|
## 6. Expose Open/Collapsed Toggles
|
|
186
182
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
For one-of-many item bodies, use the
|
|
183
|
+
Expose the open state of a component that opens, collapses or reveals content
|
|
184
|
+
on click, hover or focus. `useState` alone, or a CSS `:hover`/`:focus-within`
|
|
185
|
+
reveal, gives the editor no state picker. For one-of-many item bodies, use the
|
|
186
|
+
active-item contract instead.
|
|
190
187
|
|
|
191
188
|
- Add `isOpen?: ElementState<boolean>` and set `isOpen: false` in
|
|
192
|
-
`defaultProps`.
|
|
193
|
-
- Seed and resync internal state from the prop; clicks
|
|
189
|
+
`defaultProps`. Keep it props-only (§5).
|
|
190
|
+
- Seed and resync internal state from the prop; clicks, pointer enter/leave and
|
|
191
|
+
focus/blur update it.
|
|
194
192
|
- Always render the collapsible content; hide it with CSS plus `aria-hidden`
|
|
195
193
|
and `inert` when closed. Never mount it with `open && …`.
|
|
196
|
-
- Style the open
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
194
|
+
- Style the open look with a module class (`open && styles.contentOpen`), not
|
|
195
|
+
only inline styles or render-time refs.
|
|
196
|
+
- Never toggle a `:global()` class for it (`--open`, `--visible`, …) on the
|
|
197
|
+
root or any part: each adds an "Open" design-panel state beside the on-stage
|
|
198
|
+
picker.
|
|
200
199
|
|
|
201
200
|
```tsx
|
|
202
201
|
const [open, setOpen] = React.useState(Boolean(isOpen));
|
|
203
202
|
React.useEffect(() => setOpen(Boolean(isOpen)), [isOpen]);
|
|
204
203
|
|
|
205
204
|
<div
|
|
206
|
-
{...elementProps?.
|
|
205
|
+
{...elementProps?.infoPanelContent}
|
|
207
206
|
aria-hidden={!open}
|
|
208
207
|
{...(!open && { inert: '' })}
|
|
209
208
|
className={classNames(
|
|
210
209
|
'info-panel-content',
|
|
211
210
|
styles.content,
|
|
212
|
-
open &&
|
|
213
|
-
elementProps?.
|
|
211
|
+
open && styles.contentOpen,
|
|
212
|
+
elementProps?.infoPanelContent?.className,
|
|
214
213
|
)}
|
|
215
214
|
>
|
|
216
215
|
<p>{text}</p>
|
|
@@ -219,7 +218,7 @@ React.useEffect(() => setOpen(Boolean(isOpen)), [isOpen]);
|
|
|
219
218
|
|
|
220
219
|
```css
|
|
221
220
|
.content { display: none; }
|
|
222
|
-
.
|
|
221
|
+
.contentOpen { display: block; }
|
|
223
222
|
```
|
|
224
223
|
|
|
225
224
|
## Checklist
|
|
@@ -233,4 +232,5 @@ React.useEffect(() => setOpen(Boolean(isOpen)), [isOpen]);
|
|
|
233
232
|
- [ ] Every state class toggled in TSX has a matching `:global()` CSS rule.
|
|
234
233
|
- [ ] `ElementState<boolean>` is used only for a component-level root state.
|
|
235
234
|
- [ ] An open/collapsed component exposes a props-only `isOpen` root state,
|
|
236
|
-
syncs internal state from it,
|
|
235
|
+
syncs internal state from it, always renders its content, and adds no
|
|
236
|
+
`--open` class anywhere.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Disabled and Invalid Wiring
|
|
2
|
+
|
|
3
|
+
Use after DESIGN-STATES.md §1 decides a part exposes `disabled` or `invalid`. A
|
|
4
|
+
native element's `:disabled` or `:invalid`, paired with the `:global` modifier in
|
|
5
|
+
the §3 rule, is what generates the state.
|
|
6
|
+
|
|
7
|
+
## Disabled
|
|
8
|
+
|
|
9
|
+
Author it when asked, or when the control stays visible but its action can be
|
|
10
|
+
unavailable (unmet precondition, request in progress, sold out). Otherwise author
|
|
11
|
+
no `disabled` binding and no `--disabled` rule.
|
|
12
|
+
|
|
13
|
+
## Invalid
|
|
14
|
+
|
|
15
|
+
Author it only for a native field with a constraint (`required`, `pattern`,
|
|
16
|
+
`type="email"`, …) or a `setCustomValidity` message. An unconstrained field is
|
|
17
|
+
never invalid.
|
|
18
|
+
|
|
19
|
+
For validity from outside the field (a server check, a password confirmation),
|
|
20
|
+
call `setCustomValidity` in the component file, on the field itself: through a
|
|
21
|
+
ref bound with `ref={confirmRef}`, a callback ref, or `event.currentTarget` in an
|
|
22
|
+
`on*` handler (inline, or a function of the component passed by name). The field may be in a component of the same file
|
|
23
|
+
that renders it. Not from a custom hook, a function outside the component, or
|
|
24
|
+
another file, and not through a ref given to a custom component. A field in a loop
|
|
25
|
+
or rendered conditionally is detected when every field with that `ref` or handler
|
|
26
|
+
prop calls it. If only some do, the ones that do may not be detected: give them a
|
|
27
|
+
constraint prop instead, otherwise leave `invalid` off them.
|
|
28
|
+
|
|
29
|
+
A `form` or `fieldset` is invalid when a field inside it is
|
|
30
|
+
(`.form:global(.signup-form--invalid), .form:invalid`), with no constraint of its
|
|
31
|
+
own; with no constrained field it is never invalid.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Manifest Build Errors
|
|
2
2
|
|
|
3
3
|
Use this reference only when `npx wix build` or `npx wix generate manifest`
|
|
4
|
-
exits with an error.
|
|
4
|
+
exits with an error or prints a warning. For an error, read the error name from the first token before the colon
|
|
5
5
|
(`IoError:`, `ParseError:`, or `NotFoundError:`), then apply the matching fix.
|
|
6
6
|
|
|
7
7
|
| Error | Fix |
|
|
@@ -20,6 +20,19 @@ exits with an error. Read the error name from the first token before the colon
|
|
|
20
20
|
|
|
21
21
|
After each fix, re-run `npx wix build && npx wix generate manifest` to confirm.
|
|
22
22
|
|
|
23
|
+
## Warnings
|
|
24
|
+
|
|
25
|
+
The build can also print non-fatal warnings such as
|
|
26
|
+
`[css] … styles selectors that matched no element in the extraction render`.
|
|
27
|
+
Read every warning and fix it; this is mandatory, with one exception below.
|
|
28
|
+
|
|
29
|
+
- **A part's own class** (`.errorMessage`, `.item`) means the part is not
|
|
30
|
+
mounted in the extraction render. Keep it mounted per `PARTS.md`, then
|
|
31
|
+
re-run build and manifest generation.
|
|
32
|
+
- **A class applied only by state** (`.contentOpen` from `open && styles.contentOpen`)
|
|
33
|
+
is expected: the part is mounted and only the state look is absent. Leave the
|
|
34
|
+
markup and CSS unchanged; do not force the state on or remove the styling.
|
|
35
|
+
|
|
23
36
|
## Checklist
|
|
24
37
|
|
|
25
38
|
- [ ] The fix matches the reported error name and message.
|
|
@@ -24,13 +24,14 @@ would plausibly control independently in the editor. It receives:
|
|
|
24
24
|
|
|
25
25
|
- one prefixed global class: `'<component-name>-<part-name>'`
|
|
26
26
|
- one CSS Module class
|
|
27
|
-
- one matching `elementProps` entry,
|
|
28
|
-
|
|
29
|
-
spreading (see
|
|
27
|
+
- one matching `elementProps` entry, keyed by the camelCase of its global class
|
|
28
|
+
and spread onto the element. Declare `a11y` on the entry only when the part
|
|
29
|
+
reads `ariaLabel`, and destructure it out before spreading (see
|
|
30
|
+
`ACCESSIBILITY.md`)
|
|
30
31
|
|
|
31
32
|
Apply this filter to every candidate:
|
|
32
33
|
|
|
33
|
-
- A state or variant (`selected`, `active`, `
|
|
34
|
+
- A state or variant (`selected`, `active`, `checked`, `disabled`) is not a part.
|
|
34
35
|
Implement it as a prefixed design-state modifier on the affected part.
|
|
35
36
|
- A hidden and shown version of the same element is one part, not two.
|
|
36
37
|
- A grouping or layout-only wrapper is not a part. A wrapper that isn't
|
|
@@ -45,13 +46,43 @@ Apply this filter to every candidate:
|
|
|
45
46
|
share the same semantic editor surface. Distinguish position with data or a
|
|
46
47
|
module-only helper class, not separate part names.
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
## Keep Every Part Mounted
|
|
50
|
+
|
|
51
|
+
The manifest is generated from a single server-side render of the component
|
|
52
|
+
with mock props. Effects do not run and `useState` keeps its initial value, so
|
|
53
|
+
markup that is not mounted in that render, and the CSS that styles it, never
|
|
54
|
+
reaches the manifest. The source is not changed afterwards: write it so every
|
|
55
|
+
part is mounted regardless of state.
|
|
56
|
+
|
|
57
|
+
- Mount every phase (loading, error, empty, question, result) and hide the
|
|
58
|
+
inactive ones with the `hidden` attribute. Never write
|
|
59
|
+
`phase === 'x' && <Part />`. Give a `hidden` element no author `display`
|
|
60
|
+
rule: it would override `hidden`.
|
|
61
|
+
- While data is missing, render one static placeholder item inside a hidden
|
|
62
|
+
wrapper so the item parts exist. It is never visible.
|
|
63
|
+
- Mutually exclusive messages (loading, error, empty) are one part whose text
|
|
64
|
+
switches by phase, not several parts with the same name.
|
|
65
|
+
- A modifier toggled by state (`correct`, `incorrect`) is a design state; see
|
|
66
|
+
`DESIGN-STATES.md`.
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
const item = current ?? placeholderItem; // placeholder shows only while hidden
|
|
70
|
+
|
|
71
|
+
<div hidden={phase !== 'question'}>
|
|
72
|
+
<h3
|
|
73
|
+
{...elementProps?.quizQuestion}
|
|
74
|
+
className={classNames('quiz-question', styles.question, elementProps?.quizQuestion?.className)}
|
|
75
|
+
>
|
|
76
|
+
{item.question}
|
|
77
|
+
</h3>
|
|
78
|
+
</div>
|
|
79
|
+
```
|
|
80
|
+
|
|
51
81
|
After generating the manifest, confirm that its `elements` include every
|
|
52
|
-
intended named part
|
|
82
|
+
intended named part and that each `elementProps.<key>` read in code equals the
|
|
83
|
+
camelCase of that part's global class.
|
|
53
84
|
|
|
54
|
-
##
|
|
85
|
+
## Sanity Check
|
|
55
86
|
|
|
56
87
|
For each candidate, ask:
|
|
57
88
|
|
|
@@ -31,11 +31,11 @@ Use this pattern:
|
|
|
31
31
|
|
|
32
32
|
```tsx
|
|
33
33
|
<div
|
|
34
|
-
{...elementProps?.
|
|
34
|
+
{...elementProps?.audioPlayerProgressBar}
|
|
35
35
|
className={classNames(
|
|
36
36
|
'audio-player-progress-bar',
|
|
37
37
|
styles.progressBar,
|
|
38
|
-
elementProps?.
|
|
38
|
+
elementProps?.audioPlayerProgressBar?.className,
|
|
39
39
|
)}
|
|
40
40
|
>
|
|
41
41
|
{/* progress UI */}
|
|
@@ -51,6 +51,26 @@ the interaction contract. `pauseButtonVisibility` in an autoplaying component
|
|
|
51
51
|
is the canonical exception: the editor preview must be able to force the
|
|
52
52
|
play/pause control visible.
|
|
53
53
|
|
|
54
|
+
## Lists of Groups
|
|
55
|
+
|
|
56
|
+
When each item of a list holds its own list, the Manage panel can't edit the inner list. Use fixed
|
|
57
|
+
groups instead and map them internally:
|
|
58
|
+
|
|
59
|
+
- Give each group flat top-level fields named after the group, so Manage
|
|
60
|
+
Items shows "Starters Dishes", not "Dishes 1".
|
|
61
|
+
- For a requested range ("2 to 5 sections"), create the maximum and add a
|
|
62
|
+
count prop that shows the first N.
|
|
63
|
+
- Group order is fixed. If the user asks to reorder groups, don't add an
|
|
64
|
+
order, slot, key or label field or a separate array to emulate it; say in
|
|
65
|
+
your reply that groups can't be reordered.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
// startersTitle: string; startersDishes: Array<Dish>
|
|
69
|
+
// /** @min 2 @max 5 */ sectionCount: number
|
|
70
|
+
// Not: sections: Array<{ dishes: Array<Dish> }>, starters: Section,
|
|
71
|
+
// or sections: Array<{ slot: number }> with dishes1…dishes5
|
|
72
|
+
```
|
|
73
|
+
|
|
54
74
|
## Examples
|
|
55
75
|
|
|
56
76
|
### Content and Behavior Props
|
|
@@ -29,9 +29,11 @@ const items = buildItems(props.items);
|
|
|
29
29
|
return <ul>{items.map(renderItem)}</ul>;
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
This does not require every data branch to render simultaneously
|
|
33
|
-
|
|
34
|
-
and the same on server and client.
|
|
32
|
+
This does not require every data branch to render simultaneously: loading,
|
|
33
|
+
empty, and error states may be conditional when their initial value is stable
|
|
34
|
+
and the same on server and client. A branch that contains a named part must
|
|
35
|
+
still be mounted in the first render, or the manifest omits that part (see
|
|
36
|
+
`PARTS.md`).
|
|
35
37
|
|
|
36
38
|
## Deterministic Output
|
|
37
39
|
|
|
@@ -47,4 +49,5 @@ format, or a client-only enhancement that does not replace the initial markup.
|
|
|
47
49
|
|
|
48
50
|
- [ ] No browser globals are accessed at module scope or during render.
|
|
49
51
|
- [ ] Initial server/client markup is complete and identical.
|
|
52
|
+
- [ ] Every named part is mounted in the first render.
|
|
50
53
|
- [ ] Render output does not depend on random, clock, locale, or timezone state.
|
|
@@ -61,19 +61,23 @@ const SDK_HANDLER_PROPS = {
|
|
|
61
61
|
onDoubleClick: 'onDblClick',
|
|
62
62
|
onMouseEnter: 'onMouseIn',
|
|
63
63
|
onMouseLeave: 'onMouseOut',
|
|
64
|
+
onFocus: 'onFocus',
|
|
65
|
+
onBlur: 'onBlur',
|
|
64
66
|
};
|
|
65
67
|
const isSdkRoot = (tag) => /\bid=\{\s*(?:props\.)?id\s*\}/.test(tag);
|
|
66
68
|
|
|
67
|
-
|
|
69
|
+
const getHandlerAttributes = (tag) => [...tag.matchAll(/\b(on[A-Z]\w*)=\{([^}]*)\}/g)];
|
|
70
|
+
|
|
71
|
+
/** `onClick={onClick}` or `onMouseEnter={props.onMouseIn}`. */
|
|
72
|
+
function isSdkForward(name, value) {
|
|
73
|
+
const sdkProp = SDK_HANDLER_PROPS[name];
|
|
74
|
+
return Boolean(sdkProp) && value.trim().replace(/^props\./, '') === sdkProp;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Every handler on the tag forwards its SDK prop; anything else makes the root a control. */
|
|
68
78
|
function forwardsOnlySdkHandlers(tag) {
|
|
69
|
-
const handlers =
|
|
70
|
-
return (
|
|
71
|
-
handlers.length > 0 &&
|
|
72
|
-
handlers.every(([, name, value]) => {
|
|
73
|
-
const sdkProp = SDK_HANDLER_PROPS[name];
|
|
74
|
-
return Boolean(sdkProp) && value.trim().replace(/^props\./, '') === sdkProp;
|
|
75
|
-
})
|
|
76
|
-
);
|
|
79
|
+
const handlers = getHandlerAttributes(tag);
|
|
80
|
+
return handlers.length > 0 && handlers.every(([, name, value]) => isSdkForward(name, value));
|
|
77
81
|
}
|
|
78
82
|
|
|
79
83
|
const PASSIVE_AUTOPLAY_HANDLERS = new Set([
|
|
@@ -85,11 +89,26 @@ const PASSIVE_AUTOPLAY_HANDLERS = new Set([
|
|
|
85
89
|
'onBlurCapture',
|
|
86
90
|
]);
|
|
87
91
|
|
|
88
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* Carousel roots observe hover and focus to pause autoplay; these handlers do not activate
|
|
94
|
+
* the region. A root needs both, so hover-only roots stay flagged. SDK forwards may sit
|
|
95
|
+
* beside them; any other handler makes the root a control.
|
|
96
|
+
*/
|
|
89
97
|
function pausesAutoplayOnly(tag) {
|
|
90
|
-
if (!isSdkRoot(tag)
|
|
91
|
-
const handlers =
|
|
92
|
-
|
|
98
|
+
if (!isSdkRoot(tag)) return false;
|
|
99
|
+
const handlers = getHandlerAttributes(tag);
|
|
100
|
+
// `onMouseEnter={onMouseIn}`, `onMouseEnter={onMouseEnter}` or `onFocus={onFocus}` forwards a prop; it is not a pause observer.
|
|
101
|
+
const isPauseObserver = (name, value) =>
|
|
102
|
+
PASSIVE_AUTOPLAY_HANDLERS.has(name) &&
|
|
103
|
+
!isSdkForward(name, value) &&
|
|
104
|
+
value.trim().replace(/^props\./, '') !== name;
|
|
105
|
+
const hasPauseObserver = (pattern) =>
|
|
106
|
+
handlers.some(([, name, value]) => pattern.test(name) && isPauseObserver(name, value));
|
|
107
|
+
return (
|
|
108
|
+
hasPauseObserver(/^onMouse/) &&
|
|
109
|
+
hasPauseObserver(/^on(?:Focus|Blur)/) &&
|
|
110
|
+
handlers.every(([, name, value]) => isPauseObserver(name, value) || isSdkForward(name, value))
|
|
111
|
+
);
|
|
93
112
|
}
|
|
94
113
|
|
|
95
114
|
/** Roles whose native element cannot express a styled component. */
|
|
@@ -112,9 +131,13 @@ function roleHasNoNativeTag(tag) {
|
|
|
112
131
|
*/
|
|
113
132
|
const EXEMPTIONS = [
|
|
114
133
|
{
|
|
115
|
-
// ANIMATED-COMPONENTS.md requires the
|
|
116
|
-
//
|
|
117
|
-
rules: new Set([
|
|
134
|
+
// ANIMATED-COMPONENTS.md requires the carousel root to observe hover and
|
|
135
|
+
// focus so autoplay pauses while a visitor is using it.
|
|
136
|
+
rules: new Set([
|
|
137
|
+
'jsx-a11y/click-events-have-key-events',
|
|
138
|
+
'jsx-a11y/no-noninteractive-element-interactions',
|
|
139
|
+
'jsx-a11y/no-static-element-interactions',
|
|
140
|
+
]),
|
|
118
141
|
tag: pausesAutoplayOnly,
|
|
119
142
|
},
|
|
120
143
|
{
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"about": "Choices no SDK index can make. sdk-lookup.cjs prints a rule when one of its entities matches. `choice: site` means the namespace depends on the site, not on the install.",
|
|
4
|
+
"rules": [
|
|
5
|
+
{
|
|
6
|
+
"entities": ["product", "products", "catalog"],
|
|
7
|
+
"namespaces": ["products", "productsV3"],
|
|
8
|
+
"choice": "site",
|
|
9
|
+
"note": "Call catalogVersioning.getCatalogVersion() first: V3 sites use productsV3, V1 sites use products. Support both paths, and request both scope sets plus SCOPE.STORES.CATALOG_READ_LIMITED. To search by name, V3 has productsV3.searchProducts() (queryProducts cannot filter on name); V1 has no search method, and its builder takes name only with startsWith.",
|
|
10
|
+
"source": "SKILL.md (Stores), references/STORES_VERSIONING.md, references/stores/QUERY.md"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"entities": ["inventory"],
|
|
14
|
+
"namespaces": ["inventory", "inventoryItemsV3"],
|
|
15
|
+
"choice": "site",
|
|
16
|
+
"note": "Call catalogVersioning.getCatalogVersion() first: V3 sites use inventoryItemsV3, V1 sites use inventory. Request both scope sets.",
|
|
17
|
+
"source": "references/STORES_VERSIONING.md"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"entities": ["refund", "refunds"],
|
|
21
|
+
"namespaces": ["orders", "orderTransactions", "refunds"],
|
|
22
|
+
"choice": { "prefer": "orders", "intents": ["search", "read"] },
|
|
23
|
+
"note": "Refunded orders: orders.searchOrders with a paymentStatus filter, then orderTransactions.listTransactionsForMultipleOrders(ids) once for the visible page. Refund events are the exception: they live in @wix/payments (namespace refunds), not in @wix/ecom.",
|
|
24
|
+
"source": "references/dashboard-page/DATA_SOURCES.md, references/backend-event/COMMON-EVENTS.md"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"entities": ["location", "locations"],
|
|
28
|
+
"namespaces": ["locations"],
|
|
29
|
+
"note": "queryLocations is admin-only although its scope is SCOPE.DC-MULTILOCATION.READ-LOCATIONS: route it to a backend endpoint and elevate it there.",
|
|
30
|
+
"source": "moved from SKILL.md (Identity and Elevation Requirement), which links here"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"entities": ["member", "members"],
|
|
34
|
+
"namespaces": ["members"],
|
|
35
|
+
"note": "getMember and queryMembers withhold PRIVATE members from visitor and member callers: never elevate them to fill a sparse result.",
|
|
36
|
+
"source": "SKILL.md (Identity and Elevation Requirement)"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"entities": ["cart", "currentcart"],
|
|
40
|
+
"namespaces": ["currentCartV2", "cartV2"],
|
|
41
|
+
"note": "currentCartV2.addLineItemsToCurrentCart is visitor-callable although its scope is SCOPE.ECOM.MANAGE-ADMIN. Never elevate it.",
|
|
42
|
+
"source": "moved from SKILL.md (Identity and Elevation Requirement), which links here"
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|