fix(site-builder): HtmlBlock render stops applying style so editor matches published output
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import { describe, test, expect, vi } from 'vitest';
|
||||
import React from 'react';
|
||||
import { createRoot, Root } from 'react-dom/client';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
|
||||
vi.mock('@craftjs/core', () => ({
|
||||
useNode: (collect?: (node: any) => any) => {
|
||||
const node = { events: { selected: false } };
|
||||
return {
|
||||
connectors: { connect: (el: any) => el, drag: (el: any) => el },
|
||||
actions: { setProp: vi.fn() },
|
||||
...(collect ? collect(node) : {}),
|
||||
};
|
||||
},
|
||||
}));
|
||||
|
||||
import { HtmlBlock } from './HtmlBlock';
|
||||
|
||||
let container: HTMLDivElement;
|
||||
let root: Root;
|
||||
|
||||
function render(ui: React.ReactElement) {
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
act(() => {
|
||||
root = createRoot(container);
|
||||
root.render(ui);
|
||||
});
|
||||
}
|
||||
|
||||
describe('HtmlBlock render ignores the style prop (matches toHtml)', () => {
|
||||
test('a stored backgroundColor/color/padding is NOT applied to the wrapper', () => {
|
||||
render(
|
||||
React.createElement(HtmlBlock as any, {
|
||||
code: '<p>hello</p>',
|
||||
style: { backgroundColor: 'rgb(255, 0, 0)', color: 'rgb(0, 0, 255)', padding: '40px' },
|
||||
}),
|
||||
);
|
||||
const wrapper = container.firstElementChild as HTMLElement;
|
||||
expect(wrapper.style.backgroundColor).toBe('');
|
||||
expect(wrapper.style.color).toBe('');
|
||||
expect(wrapper.style.padding).toBe('');
|
||||
});
|
||||
|
||||
test('the editor affordances survive: minHeight is kept, content still renders', () => {
|
||||
render(React.createElement(HtmlBlock as any, { code: '<p>hello</p>', style: {} }));
|
||||
const wrapper = container.firstElementChild as HTMLElement;
|
||||
expect(wrapper.style.minHeight).toBe('40px');
|
||||
expect(wrapper.innerHTML).toContain('hello');
|
||||
});
|
||||
});
|
||||
@@ -23,3 +23,13 @@ describe('HtmlBlock.toHtml sanitizes raw code (A4.1)', () => {
|
||||
expect(html).toBe('<p>hi</p>');
|
||||
});
|
||||
});
|
||||
|
||||
test('toHtml never emits the style prop (the other half of the render/export contract)', () => {
|
||||
const out = (HtmlBlock as any).toHtml(
|
||||
{ code: '<p>hi</p>', style: { backgroundColor: '#ff0000', padding: '40px' } },
|
||||
'',
|
||||
);
|
||||
expect(out.html).toBe('<p>hi</p>');
|
||||
expect(out.html).not.toContain('background');
|
||||
expect(out.html).not.toContain('40px');
|
||||
});
|
||||
|
||||
@@ -62,16 +62,21 @@ export function purifyHtml(input: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
export const HtmlBlock: UserComponent<HtmlBlockProps> = ({ code = '', style = {} }) => {
|
||||
export const HtmlBlock: UserComponent<HtmlBlockProps> = ({ code = '' }) => {
|
||||
const { connectors: { connect, drag }, selected } = useNode((node) => ({ selected: node.events.selected }));
|
||||
const clean = useMemo(() => purifyHtml(code), [code]);
|
||||
const setRef = (ref: HTMLElement | null): void => { if (ref) connect(drag(ref)); };
|
||||
// The `style` prop is deliberately NOT applied. `toHtml()` emits only the
|
||||
// purified `code`, so anything styled here would show in the editor and
|
||||
// vanish on the live page -- the exact bug this block was reported for.
|
||||
// Wrapper styling belongs in the user's own markup (see the Edit HTML
|
||||
// toolbar's colour control). `style` stays on the props interface so
|
||||
// already-saved sites keep deserializing cleanly.
|
||||
return React.createElement('div', {
|
||||
ref: setRef,
|
||||
style: {
|
||||
minHeight: '40px',
|
||||
outline: selected ? '2px solid #3b82f6' : 'none',
|
||||
...style,
|
||||
},
|
||||
dangerouslySetInnerHTML: { __html: clean },
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user