diff --git a/craft/src/components/basic/Heading.toHtml.test.ts b/craft/src/components/basic/Heading.toHtml.test.ts index 1236510..298820b 100644 --- a/craft/src/components/basic/Heading.toHtml.test.ts +++ b/craft/src/components/basic/Heading.toHtml.test.ts @@ -154,3 +154,10 @@ describe('Heading.toHtml inline rich text (#46)', () => { test('Heading default colour is the text token', () => { expect((Heading as any).craft.props.style.color).toBe('var(--wsb-text)'); }); + +describe('Heading.toHtml newlines (canvas parity)', () => { + test('plain text newlines become
, like the editor canvas', () => { + const { html } = (Heading as any).toHtml({ text: 'a\nb', level: 'h2' }, ''); + expect(html).toContain('a
b'); + }); +}); diff --git a/craft/src/components/basic/Heading.tsx b/craft/src/components/basic/Heading.tsx index dab636f..8ebe900 100644 --- a/craft/src/components/basic/Heading.tsx +++ b/craft/src/components/basic/Heading.tsx @@ -1,8 +1,7 @@ import React, { CSSProperties } from 'react'; import { useNode, UserComponent } from '@craftjs/core'; import { cssPropsToString } from '../../utils/style-helpers'; -import { escapeHtml } from '../../utils/escape'; -import { resolveInlineHtml } from '../../utils/inline-html'; +import { resolveInlineHtml, textToInlineHtml } from '../../utils/inline-html'; import { tokenRef } from '../../utils/tokens'; import { useInlineEditable } from './useInlineEditable'; @@ -102,7 +101,7 @@ Heading.craft = { (Heading as any).toHtml = (props: HeadingProps, _childrenHtml: string) => { const tag = sanitizeHeadingLevel(props.level); - const safeText = resolveInlineHtml(props.text, props.html) ?? escapeHtml(props.text || ''); + const safeText = resolveInlineHtml(props.text, props.html) ?? textToInlineHtml(props.text || ''); const styleStr = cssPropsToString(props.style); return { html: `<${tag}${styleStr ? ` style="${styleStr}"` : ''}>${safeText}` }; }; diff --git a/craft/src/components/basic/TextBlock.toHtml.test.ts b/craft/src/components/basic/TextBlock.toHtml.test.ts index 564d1e5..be03998 100644 --- a/craft/src/components/basic/TextBlock.toHtml.test.ts +++ b/craft/src/components/basic/TextBlock.toHtml.test.ts @@ -117,3 +117,10 @@ describe('TextBlock.toHtml design tokens (#44)', () => { for (const hex of ["#3f3f46"]) expect(lower, `old ${hex}`).not.toContain(hex); }); }); + +describe('TextBlock.toHtml newlines (canvas parity)', () => { + test('plain text newlines become
, like the editor canvas (textToInlineHtml)', () => { + const { html } = toHtml({ text: 'a\n\nb ' }, ''); + expect(html).toBe('

a

b <i>

'); + }); +}); diff --git a/craft/src/components/basic/TextBlock.tsx b/craft/src/components/basic/TextBlock.tsx index 94ec5fb..16539b3 100644 --- a/craft/src/components/basic/TextBlock.tsx +++ b/craft/src/components/basic/TextBlock.tsx @@ -2,8 +2,7 @@ import React, { CSSProperties } from 'react'; import { tokenRef } from '../../utils/tokens'; import { useNode, UserComponent } from '@craftjs/core'; import { cssPropsToString } from '../../utils/style-helpers'; -import { escapeHtml } from '../../utils/escape'; -import { resolveInlineHtml } from '../../utils/inline-html'; +import { resolveInlineHtml, textToInlineHtml } from '../../utils/inline-html'; import { useInlineEditable } from './useInlineEditable'; interface TextBlockProps { @@ -94,6 +93,6 @@ TextBlock.craft = { (TextBlock as any).toHtml = (props: TextBlockProps, _childrenHtml: string) => { const styleStr = cssPropsToString(props.style); - const escapedText = resolveInlineHtml(props.text, props.html) ?? escapeHtml(props.text || ''); + const escapedText = resolveInlineHtml(props.text, props.html) ?? textToInlineHtml(props.text || ''); return { html: `${escapedText}

` }; }; diff --git a/craft/src/utils/responsive-style.test.ts b/craft/src/utils/responsive-style.test.ts index 6b6d636..e20622f 100644 --- a/craft/src/utils/responsive-style.test.ts +++ b/craft/src/utils/responsive-style.test.ts @@ -205,10 +205,10 @@ describe('responsiveCssForNode', () => { import { dropBlankStyle as _dropBlankStyle } from './responsive-style'; describe('dropBlankStyle (canvas == exporter: blanks are not declarations)', () => { - it('drops "", null, undefined; keeps 0 and real values', () => { + test('drops "", null, undefined; keeps 0 and real values', () => { expect(_dropBlankStyle({ padding: '20px', paddingTop: '', a: null, b: undefined, opacity: 0, zIndex: '0' } as any)).toEqual({ padding: '20px', opacity: 0, zIndex: '0' }); }); - it('returns the same object when nothing is blank, and passes non-objects through', () => { + test('returns the same object when nothing is blank, and passes non-objects through', () => { const s = { color: 'red' }; expect(_dropBlankStyle(s)).toBe(s); expect(_dropBlankStyle(undefined)).toBeUndefined();