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>
85 lines
3.0 KiB
TypeScript
85 lines
3.0 KiB
TypeScript
import { describe, test, expect } from 'vitest';
|
|
import { deriveVirtualRows, VIRTUAL_CHILD_PROPS } from './layers-virtual-rows';
|
|
|
|
describe('deriveVirtualRows', () => {
|
|
test('returns one row per item, labelled by the registered field', () => {
|
|
const rows = deriveVirtualRows('Features Grid', {
|
|
features: [{ title: 'Fast' }, { title: 'Secure' }],
|
|
});
|
|
expect(rows).toEqual([
|
|
{ index: 0, label: 'Fast' },
|
|
{ index: 1, label: 'Secure' },
|
|
]);
|
|
});
|
|
|
|
test('falls back to "<Fallback> N" when the label field is missing or blank', () => {
|
|
const rows = deriveVirtualRows('Features Grid', {
|
|
features: [{ title: '' }, { description: 'no title key' }],
|
|
});
|
|
expect(rows).toEqual([
|
|
{ index: 0, label: 'Feature 1' },
|
|
{ index: 1, label: 'Feature 2' },
|
|
]);
|
|
});
|
|
|
|
test('trims and truncates a long label to 40 characters with an ellipsis', () => {
|
|
const long = 'x'.repeat(60);
|
|
const rows = deriveVirtualRows('Features Grid', { features: [{ title: ` ${long} ` }] });
|
|
expect(rows[0].label).toHaveLength(41);
|
|
expect(rows[0].label.endsWith('…')).toBe(true);
|
|
});
|
|
|
|
test('an unregistered component yields no rows', () => {
|
|
expect(deriveVirtualRows('Heading', { text: 'hi' })).toEqual([]);
|
|
});
|
|
|
|
test('a missing or non-array prop yields no rows instead of throwing', () => {
|
|
expect(deriveVirtualRows('Tabs', {})).toEqual([]);
|
|
expect(deriveVirtualRows('Tabs', { tabs: 'not an array' })).toEqual([]);
|
|
expect(deriveVirtualRows('Tabs', { tabs: null })).toEqual([]);
|
|
});
|
|
|
|
test('a non-object item still gets a fallback label', () => {
|
|
expect(deriveVirtualRows('Menu', { links: ['raw string'] })).toEqual([
|
|
{ index: 0, label: 'Link 1' },
|
|
]);
|
|
});
|
|
|
|
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', () => {
|
|
for (const [name, spec] of Object.entries(VIRTUAL_CHILD_PROPS)) {
|
|
expect(spec.prop, `${name}.prop`).toBeTruthy();
|
|
expect(spec.label, `${name}.label`).toBeTruthy();
|
|
expect(spec.fallback, `${name}.fallback`).toBeTruthy();
|
|
}
|
|
});
|
|
});
|