fix(site-builder): add Hero/CTA components to Layers virtual-rows registry
A grep across every component under src/components/ (not just the 12
files the original registry hardcoded) turns up three more leaf
components with the identical array-prop-as-content pattern: Hero
(HeroSimple.tsx), Call to Action (CallToAction.tsx) and CTA Section
(CTASection.tsx) all render a shared `ctas?: CtaButton[]` prop whose
items are `{ text, href, variant?, target? }`. Without this, a Hero's
CTA buttons still wouldn't appear in the Layers tree.
Verified displayName, prop name and label field against each
component's craft.props defaults and the shared CtaButton type in
sections/_cta-helpers.tsx -- all three matched exactly, no corrections
needed. A follow-up sweep for any further array-prop leaf components
found none: the only other array fields in the tree are nested one
level inside already-covered items (ContactFormField.options,
PricingPlan.features), not top-level component props.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -45,6 +45,35 @@ describe('deriveVirtualRows', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Hero, Call to Action and CTA Section derive rows from their shared ctas prop', () => {
|
||||||
|
const ctas = [
|
||||||
|
{ text: 'Get Started', href: '#', variant: 'primary' },
|
||||||
|
{ text: 'Learn More', href: '#learn', variant: 'outline' },
|
||||||
|
];
|
||||||
|
expect(deriveVirtualRows('Hero', { ctas })).toEqual([
|
||||||
|
{ index: 0, label: 'Get Started' },
|
||||||
|
{ index: 1, label: 'Learn More' },
|
||||||
|
]);
|
||||||
|
expect(deriveVirtualRows('Call to Action', { ctas })).toEqual([
|
||||||
|
{ index: 0, label: 'Get Started' },
|
||||||
|
{ index: 1, label: 'Learn More' },
|
||||||
|
]);
|
||||||
|
expect(deriveVirtualRows('CTA Section', { ctas })).toEqual([
|
||||||
|
{ index: 0, label: 'Get Started' },
|
||||||
|
{ index: 1, label: 'Learn More' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a CTA with no text falls back to "Button N"', () => {
|
||||||
|
const rows = deriveVirtualRows('Hero', {
|
||||||
|
ctas: [{ href: '#' }, { text: '', href: '#empty' }],
|
||||||
|
});
|
||||||
|
expect(rows).toEqual([
|
||||||
|
{ index: 0, label: 'Button 1' },
|
||||||
|
{ index: 1, label: 'Button 2' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
test('every registry entry has a non-empty prop, label and fallback', () => {
|
test('every registry entry has a non-empty prop, label and fallback', () => {
|
||||||
for (const [name, spec] of Object.entries(VIRTUAL_CHILD_PROPS)) {
|
for (const [name, spec] of Object.entries(VIRTUAL_CHILD_PROPS)) {
|
||||||
expect(spec.prop, `${name}.prop`).toBeTruthy();
|
expect(spec.prop, `${name}.prop`).toBeTruthy();
|
||||||
|
|||||||
@@ -38,7 +38,18 @@ export interface VirtualChildSpec {
|
|||||||
* - ContentSlider items are `{ heading, text, ... }`, not `{ title }` --
|
* - ContentSlider items are `{ heading, text, ... }`, not `{ title }` --
|
||||||
* registry corrected from `title` to `heading`.
|
* registry corrected from `title` to `heading`.
|
||||||
* Every other entry (prop name and label field) matched the component as
|
* Every other entry (prop name and label field) matched the component as
|
||||||
* originally drafted. */
|
* originally drafted.
|
||||||
|
*
|
||||||
|
* Hero, Call to Action and CTA Section were added after a follow-up sweep
|
||||||
|
* of every component under src/components/ for top-level array props (not
|
||||||
|
* just the 12 files initially listed) turned up three more leaf components
|
||||||
|
* with the identical pattern: all three render a `ctas?: CtaButton[]` prop
|
||||||
|
* (see sections/_cta-helpers.tsx) whose items are `{ text, href, variant?,
|
||||||
|
* target? }`, so `text` is the label field. Verified against each file's
|
||||||
|
* `craft.displayName` and `craft.props.ctas` defaults. That sweep found no
|
||||||
|
* further candidates -- the only other array fields in the tree are nested
|
||||||
|
* one level down inside already-covered items (ContactFormField.options,
|
||||||
|
* PricingPlan.features), not top-level component props. */
|
||||||
export const VIRTUAL_CHILD_PROPS: Record<string, VirtualChildSpec> = {
|
export const VIRTUAL_CHILD_PROPS: Record<string, VirtualChildSpec> = {
|
||||||
'Features Grid': { prop: 'features', label: 'title', fallback: 'Feature' },
|
'Features Grid': { prop: 'features', label: 'title', fallback: 'Feature' },
|
||||||
Tabs: { prop: 'tabs', label: 'label', fallback: 'Tab' },
|
Tabs: { prop: 'tabs', label: 'label', fallback: 'Tab' },
|
||||||
@@ -52,6 +63,9 @@ export const VIRTUAL_CHILD_PROPS: Record<string, VirtualChildSpec> = {
|
|||||||
'Social Links': { prop: 'links', label: 'platform', fallback: 'Link' },
|
'Social Links': { prop: 'links', label: 'platform', fallback: 'Link' },
|
||||||
Navbar: { prop: 'links', label: 'text', fallback: 'Link' },
|
Navbar: { prop: 'links', label: 'text', fallback: 'Link' },
|
||||||
'Contact Form': { prop: 'fields', label: 'label', fallback: 'Field' },
|
'Contact Form': { prop: 'fields', label: 'label', fallback: 'Field' },
|
||||||
|
Hero: { prop: 'ctas', label: 'text', fallback: 'Button' },
|
||||||
|
'Call to Action': { prop: 'ctas', label: 'text', fallback: 'Button' },
|
||||||
|
'CTA Section': { prop: 'ctas', label: 'text', fallback: 'Button' },
|
||||||
};
|
};
|
||||||
|
|
||||||
const MAX_LABEL = 40;
|
const MAX_LABEL = 40;
|
||||||
|
|||||||
Reference in New Issue
Block a user