# Component API Rules and patterns for props structure, elementProps, data types, file structure, containers, data-driven components, and array props. --- ## Should This Use elementProps or CSS? ``` Is this element a named part (carries a global class string)? │ ├─ YES │ └─ ✅ It gets an elementProps entry — spread it + merge its className. │ Add config fields inside the entry only as needed │ (data, direction, event handlers). │ └─ NO → purely visual/decorative non-part (icons, separators, decorations, layout wrappers, elements only hidden/shown via CSS) └─ ❌ CSS class only — no elementProps entry ``` --- ## Rules ### Component Props Structure ```typescript interface ComponentProps { // Identity id?: string; className?: string; // Mandatory features (from @wix/editor-react-types) direction?: Direction; a11y?: A11y; // ALL ARIA attributes come through this (ariaLabel, role, etc.) // Component-specific props (NO children unless container-type - see container rules below) label?: string; items?: Array; // ... from specification // NEVER add: ariaLabel, ariaDescribedBy, role, etc. - use a11y prop instead // Sub-component configuration (only if needed - see elementProps rules) elementProps?: { [partName]?: { [configProp]?: string | boolean | number; // Only include if element needs config beyond className direction?: Direction; }; }; } ``` ### Propagate `elementProps` to inner elements `elementProps` is a map keyed by the manifest's inner-element keys; each entry carries that element's `className` (the editor sets it — this is how design states reach the element) plus any data. **Every named inner element — every element that carries a global class string — gets an `elementProps` entry, and you spread that entry onto the element.** This is mandatory, even when the element needs no other config: without it the editor cannot style that element or drive its states. How `className` gets merged depends on what the element is: - **Raw HTML element** (` // Sub-component built with this skill — spread is enough (it merges className itself): ``` **Non-parts** (module-class-only elements: icons, decorations, separators, layout wrappers — no global class) get no entry. Style them with CSS. **Inside each entry**, add only the config the element needs (data, direction, event handlers). Never add a bare `className?` as the *only* field to decide whether an element qualifies — `className` always flows through; the entry exists because the element is a named part. ### What Qualifies as a Part See [`PARTS.md`](PARTS.md) for root election (Step 0), the mandatory filter, and full rules. The root gets no `elementProps` entry — its `className`, `a11y`, and `direction` arrive as top-level props. ### Derived values are not props If a displayed value can be computed with a simple pure function from other props and/or internal state, compute it internally — don't expose it as a prop. Expose only the source inputs (use numeric types when arithmetic is needed: `price: number`, not `price: string`). **Example:** subtotal = `price × quantity` → computed inside the component, not a prop. ### Data-Driven Components (NO children in exported props) - Component's **exported interface** must NOT accept `children` prop - ALL content MUST come through explicit named props (see "Derived values" above for what stays internal): - Text (labels, placeholders, messages) → `label`, `text`, `placeholder`, etc. - Media (images, videos, icons) → `imageSrc`, `videoUrl`, `iconName`, etc. - Links (URLs, hrefs) → `link`, `href`, `url`, etc. - Collections (list items, options, menu items) → `items`, `options`, `menuItems`, etc. - **Internal implementation** can use children for composition between sub-components - Hardcoded values are ONLY for fallback defaults when props are undefined **Exception — Container-type components:** Components whose purpose is to wrap arbitrary child elements (e.g., BoxContainer) MAY accept `children: React.ReactNode`. This applies only to structural containers — data-driven leaf components (Button, Tabs, Accordion, etc.) must NOT use `children`. ### Array Props: Data on Parent Only When the parent component defines an array prop (e.g., `items`), child/item components receive a single item directly. They do NOT redeclare the data structure in their own props. ### Array Element Types: Always Objects with Named Keys Array elements MUST be objects with named keys. This enables stable item identity (each item can carry its own `id`/`key`), non-breaking extension (new fields can be added later without changing the prop signature), and semantic naming (each value has meaning instead of being an opaque scalar). **Allowed forms:** - Inline object literal: `Array<{ key: ValueType, ... }>` - Named interface where the interface itself is an object with named keys (e.g. `Array` is OK because `AccordionItem` is `{ name, content }`) **Never allowed as the array element:** - Primitives: `Array`, `Array`, `Array` - Leaf data types from `@wix/editor-react-types`: `Array`, `Array`, `Array