From 5f5602b71e008543dee6e8d9b56f40779ea6ed60 Mon Sep 17 00:00:00 2001 From: saschabuehrle Date: Mon, 16 Mar 2026 12:20:49 +0100 Subject: [PATCH 1/2] fix: correct header card layout parsing based on DOM structure and classes (fixes #1656) The header card HTML parser was incorrectly defaulting to split layout whenever an image was present, ignoring the actual HTML structure and classes that indicate the intended layout. This fix implements proper layout detection by: - Checking for explicit layout classes (kg-layout-split, kg-content-wide) - Analyzing DOM structure (picture as direct child = full, picture inside content = split) - Only falling back to split layout when structure is ambiguous Test cases added for: - Full/wide layout with kg-content-wide class - Split layout with kg-layout-split class - Full layout when picture is direct child of card Resolves the issue where programmatically created header cards via Admin API were incorrectly parsed as split layout despite having correct full/wide layout HTML structure. --- .../lib/nodes/header/parsers/header-parser.js | 24 +++++++- .../test/nodes/header.test.js | 57 +++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/packages/kg-default-nodes/lib/nodes/header/parsers/header-parser.js b/packages/kg-default-nodes/lib/nodes/header/parsers/header-parser.js index 78e83c9050..be444f09bf 100644 --- a/packages/kg-default-nodes/lib/nodes/header/parsers/header-parser.js +++ b/packages/kg-default-nodes/lib/nodes/header/parsers/header-parser.js @@ -51,7 +51,29 @@ export function parseHeaderNode(HeaderNode) { const buttonElement = div.querySelector('.kg-header-card-button'); const alignment = div.classList.contains('kg-align-center') ? 'center' : ''; const backgroundImageSrc = div.querySelector('.kg-header-card-image')?.getAttribute('src'); - const layout = backgroundImageSrc ? 'split' : ''; + + // Determine layout based on classes and DOM structure, not just image presence + let layout = ''; + if (backgroundImageSrc) { + // Check for explicit layout class first + const hasSplitClass = div.classList.contains('kg-layout-split'); + const hasContentWideClass = div.classList.contains('kg-content-wide'); + + // Check DOM structure: picture as direct child = full, picture inside content = split + const picture = div.querySelector('picture'); + const content = div.querySelector('.kg-header-card-content'); + const isPictureDirectChild = picture && picture.parentElement === div; + const isPictureInContent = picture && content && content.contains(picture); + + if (hasSplitClass || isPictureInContent) { + layout = 'split'; + } else if (hasContentWideClass || isPictureDirectChild) { + layout = ''; // full/wide layout (empty string) + } else { + // Fallback to old behavior if structure is ambiguous + layout = 'split'; + } + } const backgroundColor = div.classList.contains('kg-style-accent') ? 'accent' : div.getAttribute('data-background-color'); const buttonColor = buttonElement?.getAttribute('data-button-color') || ''; const textColor = headerElement?.getAttribute('data-text-color') || ''; diff --git a/packages/kg-default-nodes/test/nodes/header.test.js b/packages/kg-default-nodes/test/nodes/header.test.js index b37f191114..9b205e5ae7 100644 --- a/packages/kg-default-nodes/test/nodes/header.test.js +++ b/packages/kg-default-nodes/test/nodes/header.test.js @@ -400,6 +400,63 @@ describe('HeaderNode', function () { const node = nodes[0]; node.version.should.equal(1); })); + + it('parses full/wide layout when kg-content-wide class is present', editorTest(function () { + const htmlstring = ` +
+ +
+
+

Title

+

Subtitle

+
+
+
`; + const document = createDocument(htmlstring); + const nodes = $generateNodesFromDOM(editor, document); + nodes.length.should.equal(1); + const node = nodes[0]; + node.layout.should.equal(''); // full/wide layout is empty string + node.backgroundImageSrc.should.equal('https://example.com/image.jpg'); + })); + + it('parses split layout when kg-layout-split class is present', editorTest(function () { + const htmlstring = ` +
+
+ +
+

Title

+

Subtitle

+
+
+
`; + const document = createDocument(htmlstring); + const nodes = $generateNodesFromDOM(editor, document); + nodes.length.should.equal(1); + const node = nodes[0]; + node.layout.should.equal('split'); + node.backgroundImageSrc.should.equal('https://example.com/image.jpg'); + })); + + it('parses full layout when picture is direct child of card', editorTest(function () { + const htmlstring = ` +
+ +
+
+

Title

+

Subtitle

+
+
+
`; + const document = createDocument(htmlstring); + const nodes = $generateNodesFromDOM(editor, document); + nodes.length.should.equal(1); + const node = nodes[0]; + node.layout.should.equal(''); // full layout is empty string + node.backgroundImageSrc.should.equal('https://example.com/image.jpg'); + })); }); describe('getType', function () { From c45afb6391b5fe0bc593c6eb52d5aacb76d8f3fd Mon Sep 17 00:00:00 2001 From: saschabuehrle Date: Mon, 16 Mar 2026 13:40:28 +0100 Subject: [PATCH 2/2] fix: move picture element inside content div in test to match split layout structure The test was expecting layout='split' but had the picture element as a direct child of the card instead of inside the content container. Per the parser logic, when isPictureInContent is false but isPictureDirectChild is true, the layout defaults to empty string rather than 'split'. Moving the picture inside .kg-header-card-content makes isPictureInContent true, resulting in the expected 'split' layout. --- packages/kg-default-nodes/test/nodes/header.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kg-default-nodes/test/nodes/header.test.js b/packages/kg-default-nodes/test/nodes/header.test.js index 9b205e5ae7..05323ec700 100644 --- a/packages/kg-default-nodes/test/nodes/header.test.js +++ b/packages/kg-default-nodes/test/nodes/header.test.js @@ -359,8 +359,8 @@ describe('HeaderNode', function () { it('parses a header card V2', editorTest(function () { const htmlstring = `
-
+

Header

Subheader