SectionTypePanel (Accordion/Tabs/Testimonials/Countdown/NumberCounter/ CTASection/CallToAction/FeaturesGrid), PricingStylePanel, and SocialStylePanel all gain a Spacing & Border section (margin/padding per-side, border, box-shadow, opacity), an Animation section, and a Visibility section, wired to the shared SpacingControl/BorderControl/ AnimationControl/VisibilityControl. PricingTable's per-card colors (cardBg/textColor/subColor/featColor/ checkColor/btnBg/btnColor) were previously hard-coded literals computed from featuredBg inside toHtml -- promoted to real optional props (each falling back to the exact prior literal when unset) and exposed via ColorPickerField in PricingStylePanel. SocialStylePanel now exposes SocialLinks' iconShape/gap (already-built props with no control), plus Icon's bgColor/bgShape/bgSize/link and StarRating's filledColor/emptyColor, which the panel's generic iconBgColor/starColor checks never matched since those aren't Icon's or StarRating's real prop names. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
35 lines
1.5 KiB
TypeScript
35 lines
1.5 KiB
TypeScript
import { describe, test, expect } from 'vitest';
|
|
import { FeaturesGrid } from './FeaturesGrid';
|
|
|
|
const toHtml = (FeaturesGrid as any).toHtml;
|
|
|
|
describe('FeaturesGrid.toHtml image sink uses safeImageUrl (data:image/svg+xml allowed)', () => {
|
|
test('feat.image as a data:image/svg+xml value emits a non-empty <img src>', () => {
|
|
const svgDataUri = 'data:image/svg+xml,%3Csvg%2F%3E';
|
|
const features = [
|
|
{ title: 'Feature', description: 'Desc', icon: '⚡', image: svgDataUri, imageAlt: 'alt' },
|
|
];
|
|
const { html } = toHtml({ features }, '');
|
|
expect(html).toContain(`<img src="${svgDataUri}"`);
|
|
});
|
|
|
|
test('feat.buttonUrl stays on safeUrl (data:image/svg+xml blocked as a navigation target)', () => {
|
|
const features = [
|
|
{ title: 'Feature', description: 'Desc', icon: '⚡', buttonText: 'Go', buttonUrl: 'data:image/svg+xml,<svg onload=alert(1)>' },
|
|
];
|
|
const { html } = toHtml({ features }, '');
|
|
expect(html).toMatch(/<a href=""/);
|
|
});
|
|
});
|
|
|
|
describe('FeaturesGrid.craft.props includes the box-model/animation/visibility rollout props', () => {
|
|
test('animation, animationDelay, and all 3 hideOn* flags are declared (blank/false defaults)', () => {
|
|
const props = (FeaturesGrid as any).craft.props;
|
|
expect(props).toHaveProperty('animation', '');
|
|
expect(props).toHaveProperty('animationDelay', '');
|
|
expect(props).toHaveProperty('hideOnDesktop', false);
|
|
expect(props).toHaveProperty('hideOnTablet', false);
|
|
expect(props).toHaveProperty('hideOnMobile', false);
|
|
});
|
|
});
|