Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 69 additions & 2 deletions packages/x-markdown/src/XMarkdown/__tests__/Parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,72 @@ describe('Parser', () => {
});

describe('protectCustomTagNewlines', () => {
it('should keep markdown syntax inside custom tag children as plain text', () => {
const parser = new Parser({
components: { custom: 'div' },
});
const content = '<custom>{"sales":[{"name":"电子产品[202](红)","value":52000}]}</custom>';
const result = parser.parse(content);
expect(result).toContain('电子产品[202](红)');
expect(result).not.toContain('<a href=');
});

it('should keep markdown syntax inside unclosed custom tag children as plain text', () => {
const parser = new Parser({
components: { custom: 'div' },
});
const content = '<custom>{"sales":[{"name":"电子产品[202](红)","value":52000';
const result = parser.parse(content);
expect(result).toContain('电子产品[202](红)');
expect(result).not.toContain('<a href=');
});

it('should not protect native HTML tags configured as components', () => {
const parser = new Parser({
components: { p: 'p' },
});
const result = parser.parse('<p>[Google](https://google.com)</p>');
expect(result).toContain('<p>[Google](https://google.com)</p>');
});

it('should keep CommonMark behavior for native HTML tag components by default', () => {
const parser = new Parser({
components: { span: 'span' },
});
const result = parser.parse('<span>*hello*</span>');
expect(result).toContain('<span><em>hello</em></span>');
});

it('should keep native HTML tag component children as plain text when enabled', () => {
const parser = new Parser({
components: { span: 'span' },
rawCustomComponents: true,
});
const result = parser.parse('<span>*hello* <i>world</i></span>');
expect(result).toContain('<span>*hello* &lt;i&gt;world&lt;&#x2F;i&gt;</span>');
expect(result).not.toContain('<em>hello</em>');
});

it('should only keep configured component tag children as plain text when enabled', () => {
const parser = new Parser({
components: { span: 'span' },
rawCustomComponents: true,
});
const result = parser.parse('<span>*hello*</span> <em>*world*</em>');
expect(result).toContain('<span>*hello*</span>');
expect(result).toContain('<em><em>world</em></em>');
});

it('should keep unclosed native HTML tag component children as plain text when enabled', () => {
const parser = new Parser({
components: { span: 'span' },
rawCustomComponents: true,
});
const result = parser.parse('<span>*hello* <i>world</i>');
expect(result).toContain('<span>*hello* &lt;i&gt;world&lt;&#x2F;i&gt;');
expect(result).not.toContain('<em>hello</em>');
});

it('should protect newlines inside custom tags when protectCustomTagNewlines is true', () => {
const parser = new Parser({
protectCustomTagNewlines: true,
Expand All @@ -92,14 +158,15 @@ describe('Parser', () => {
expect(result).not.toMatch(/<CustomComponent>First line<\/p>\s*<p>Second line/);
});

it('should not protect newlines when protectCustomTagNewlines is false', () => {
it('should protect custom tag content when protectCustomTagNewlines is false', () => {
const parser = new Parser({
protectCustomTagNewlines: false,
components: { CustomComponent: 'div' },
});
const content = '<CustomComponent>First line\n\nSecond line</CustomComponent>';
const result = parser.parse(content);
expect(result).toContain('<p>');
expect(result).toContain('<CustomComponent>First line\n\nSecond line</CustomComponent>');
expect(result).not.toMatch(/<CustomComponent>First line<\/p>\s*<p>Second line/);
});
Comment thread
meet-student marked this conversation as resolved.

it('should work normally when protectCustomTagNewlines is true but no custom components', () => {
Expand Down
69 changes: 69 additions & 0 deletions packages/x-markdown/src/XMarkdown/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,75 @@ describe('XMarkdown', () => {
expect((container.firstChild as HTMLElement)?.innerHTML).toBe(html);
});

it('passes custom component children as plain text without parsing markdown links', () => {
let receivedChildren: React.ReactNode;
const markdown =
'<custom>{"sales":[{"name":"电子产品[202](红)","value":52000}],"majorGroupName":"华南师范大学[202](汕尾校区)"}</custom>';

const { container } = render(
<XMarkdown
content={markdown}
components={{
custom: (props) => {
receivedChildren = props.children;
return <span data-testid="custom">{props.children}</span>;
},
}}
/>,
);

expect(receivedChildren).toBe(
'{"sales":[{"name":"电子产品[202](红)","value":52000}],"majorGroupName":"华南师范大学[202](汕尾校区)"}',
);
expect(container.querySelector('a')).not.toBeInTheDocument();
});

it('passes streaming custom component children as plain text before the tag is closed', () => {
let receivedChildren: React.ReactNode;
const markdown = '<custom>{"sales":[{"name":"电子产品[202](红)","value":52000';

const { container } = render(
<XMarkdown
content={markdown}
streaming={{ hasNextChunk: true }}
components={{
custom: (props) => {
receivedChildren = props.children;
return <span data-testid="custom">{props.children}</span>;
},
}}
/>,
);

expect(receivedChildren).toBe('{"sales":[{"name":"电子产品[202](红)","value":52000');
expect(container.querySelector('a')).not.toBeInTheDocument();
});

it('keeps native component children as plain text when rawCustomComponents is true', () => {
let receivedChildren: React.ReactNode;
const markdown = '<span>*hello* <i>world</i></span>';

const { container } = render(
<XMarkdown
content={markdown}
rawCustomComponents
components={{
span: (props) => {
receivedChildren = props.children;
return <span data-testid="plain">{props.children}</span>;
},
}}
/>,
);

expect(receivedChildren).toBe('*hello* <i>world</i>');
expect(container.querySelector('em')).not.toBeInTheDocument();
expect(container.querySelector('i')).not.toBeInTheDocument();
expect(container.querySelector('[data-testid="plain"]')).toHaveTextContent(
'*hello* <i>world</i>',
);
});

it('walkToken', () => {
const walkTokens = (token: Token) => {
if (token.type === 'heading') {
Expand Down
Loading
Loading