Markdown editor fix (#19732)

* fix block editor escaping html elements

* add unit tests
This commit is contained in:
Karan Hotchandani 2025-02-11 12:34:57 +05:30 committed by GitHub
parent fcbe9241bc
commit 442b58b0e7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 87 additions and 2 deletions

View File

@ -14,6 +14,7 @@ import {
formatValueBasedOnContent, formatValueBasedOnContent,
getHtmlStringFromMarkdownString, getHtmlStringFromMarkdownString,
getTextFromHtmlString, getTextFromHtmlString,
isHTMLString,
} from './BlockEditorUtils'; } from './BlockEditorUtils';
describe('getTextFromHtmlString', () => { describe('getTextFromHtmlString', () => {
@ -132,3 +133,59 @@ describe('formatValueBasedOnContent', () => {
expect(formatValueBasedOnContent(input)).toBe(''); expect(formatValueBasedOnContent(input)).toBe('');
}); });
}); });
describe('isHTMLString', () => {
it('should return true for simple HTML content', () => {
const htmlContent = '<p>This is a paragraph</p>';
expect(isHTMLString(htmlContent)).toBe(true);
});
it('should return true for complex HTML content', () => {
const htmlContent = `
<div class="container">
<h1>Title</h1>
<p>This is a <strong>bold</strong> paragraph with <a href="#">link</a></p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</div>
`;
expect(isHTMLString(htmlContent)).toBe(true);
});
it('should return false for markdown content', () => {
const markdownContent = `
***
### Data Sharing Policies
If there is any question or concern regarding the data sharing policies,
please contact the support team <test@test.com>.
***
`;
expect(isHTMLString(markdownContent)).toBe(false);
});
it('should return false for plain text', () => {
const plainText = 'This is just plain text without any formatting';
expect(isHTMLString(plainText)).toBe(false);
});
it('should return false for empty string', () => {
expect(isHTMLString('')).toBe(false);
});
it('should return false when content has both HTML and markdown', () => {
const mixedContent = `
<div>
# Markdown Header
* List item
</div>
`;
expect(isHTMLString(mixedContent)).toBe(true);
});
});

View File

@ -122,12 +122,40 @@ export const formatValueBasedOnContent = (value: string) =>
value === '<p></p>' ? '' : value; value === '<p></p>' ? '' : value;
export const isHTMLString = (content: string) => { export const isHTMLString = (content: string) => {
// Quick check for common HTML tags
const commonHtmlTags =
/<(p|div|span|a|ul|ol|li|h[1-6]|br|strong|em|code|pre)[>\s]/i;
// If content doesn't have any HTML-like structure, return false early
if (!commonHtmlTags.test(content)) {
return false;
}
try { try {
const parser = new DOMParser(); const parser = new DOMParser();
const parsedDocument = parser.parseFromString(content, 'text/html'); const parsedDocument = parser.parseFromString(content, 'text/html');
// since text can be also counted as child node so we will check if length is greater than 1 // Check if there are any actual HTML elements (not just text nodes)
return parsedDocument.body.childNodes.length > 1; const hasHtmlElements = Array.from(parsedDocument.body.childNodes).some(
(node) => node.nodeType === Node.ELEMENT_NODE
);
// Check if the content has markdown-specific patterns
const markdownPatterns = [
/^#{1,6}\s/, // Headers
/^\s*[-*+]\s/, // Lists
/^\s*\d+\.\s/, // Numbered lists
/^\s*>{1,}\s/, // Blockquotes
/^---|\*\*\*|___/, // Horizontal rules
/`{1,3}[^`]+`{1,3}/, // Code blocks
];
const hasMarkdownSyntax = markdownPatterns.some((pattern) =>
pattern.test(content)
);
// If it has markdown syntax but also parsed as HTML, prefer markdown interpretation
return hasHtmlElements && !hasMarkdownSyntax;
} catch (e) { } catch (e) {
return false; return false;
} }