Files
site-builder/craft/src/components/sections/component-tokens.harness.test.tsx
T
shadowdaoandClaude Sonnet 5.5 2877bc06fa feat(site-builder): tokenise colours hard-coded inside components
Component render/toHtml now use var(--wsb-*) instead of hex so Site Design
palette changes reach FeaturesGrid, Testimonials, Accordion, PricingTable,
CTA/CallToAction, forms, Gallery, Tabs and others. Exemption lists removed.

Refs #44

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 12:44:39 -07:00

78 lines
3.6 KiB
TypeScript

import React from 'react';
import { describe, test, expect, afterEach } from 'vitest';
import { renderEditorHarness, EditorHarness } from '../../test-utils/editorHarness';
import { SiteDesignProvider, useSiteDesign, SiteDesignContextValue, DEFAULT_SITE_DESIGN } from '../../state/SiteDesignContext';
import { CanvasTokenStyle } from '../../editor/CanvasTokenStyle';
import { buildNodeTree } from '../../utils/craft-tree';
import { templateComponentToTreeNode } from '../../templates/apply-template';
import { exportBodyHtml, exportToHtml } from '../../utils/html-export';
/**
* #44 component-tokens, real-editor harness: a page of FeaturesGrid + PricingTable +
* Testimonials (default props). Their colours must be token references, the canvas
* token <style> must follow a Site Design change, and the export must reference tokens
* (and define the changed primary once, in :root) rather than carry the old hex.
*/
let h: EditorHarness | null = null;
afterEach(() => {
h?.unmount();
h = null;
});
const DesignProbe: React.FC<{ out: { current: SiteDesignContextValue | null } }> = ({ out }) => {
out.current = useSiteDesign();
return null;
};
const OLD_HEX = ['#f8fafc', '#e2e8f0', '#18181b', '#64748b', '#4b5563', '#374151', '#bbf7d0', '#facc15', '#f59e0b'];
describe('component colours follow Site Design (real editor harness)', () => {
test('FeaturesGrid + PricingTable + Testimonials: canvas vars follow primary; export references tokens', () => {
h = renderEditorHarness();
const ctx: { current: SiteDesignContextValue | null } = { current: null };
h.mountChild(
<SiteDesignProvider>
<CanvasTokenStyle />
<DesignProbe out={ctx} />
</SiteDesignProvider>,
);
h.act(() => {
for (const type of ['FeaturesGrid', 'PricingTable', 'Testimonials']) {
h!.actions.addNodeTree(
buildNodeTree(h!.query, templateComponentToTreeNode({ type, props: {} } as any)),
'ROOT',
);
}
});
// Canvas: the editor-rendered DOM carries var() refs (jsdom keeps them verbatim).
const clone = h.container.cloneNode(true) as HTMLElement;
clone.querySelectorAll('style').forEach((el) => el.remove()); // token definitions legitimately hold hex
const dom = clone.innerHTML;
expect(dom).toContain('var(--wsb-border)');
expect(dom).toContain('var(--wsb-muted)');
expect(dom).toContain('var(--wsb-primary)');
for (const hex of OLD_HEX) expect(dom.toLowerCase(), `canvas ${hex}`).not.toContain(hex);
// Canvas custom properties follow Site Design.
const canvasVars = () => document.querySelector('style[data-wsb-canvas-tokens]')!.textContent ?? '';
expect(canvasVars()).toContain('--wsb-primary:#3b82f6');
h.act(() => ctx.current!.updateDesign({ primaryColor: '#c0ffee', mutedTextColor: '#123456' }));
expect(canvasVars()).toContain('--wsb-primary:#c0ffee');
expect(canvasVars()).toContain('--wsb-muted:#123456');
// Export: tokens, no old design hex in the body.
const ser = h.getSerialized();
const { html } = exportBodyHtml(ser);
expect(html).toContain('var(--wsb-primary)');
expect(html).toContain('var(--wsb-warning)');
for (const hex of OLD_HEX) expect(html.toLowerCase(), `export ${hex}`).not.toContain(hex);
// Restyling Site Design changes only :root, never the body markup.
const a = exportToHtml(ser, { design: { ...DEFAULT_SITE_DESIGN } }).html;
const b = exportToHtml(ser, { design: { ...DEFAULT_SITE_DESIGN, primaryColor: '#c0ffee', mutedTextColor: '#123456' } }).html;
expect(b).toContain('--wsb-muted:#123456');
expect(b.slice(b.indexOf('<body'))).toBe(a.slice(a.indexOf('<body')));
});
});