Skip to content

🐛 Fixed header cards losing their layout when imported from HTML - #29706

Merged
9larsons merged 5 commits into
TryGhost:mainfrom
msdianprince-7:fix/29268-header-card-layout-parsing
Aug 3, 2026
Merged

🐛 Fixed header cards losing their layout when imported from HTML#29706
9larsons merged 5 commits into
TryGhost:mainfrom
msdianprince-7:fix/29268-header-card-layout-parsing

Conversation

@msdianprince-7

Copy link
Copy Markdown
Contributor

Fixes #29268.

Why

The v2 header card parser decided the card's layout from a single heuristic:

const layout = backgroundImageSrc ? 'split' : '';

Any header card that had a background image was imported as split, regardless of what the HTML actually said. So when rendered Ghost HTML is sent back to Ghost — the documented Admin API flow of POST /posts/?source=html — every regular, wide and full header card came back as split, and the image that was meant to be a full-bleed background ended up side-by-side with the text. Authors had to fix each card by hand in the editor.

What

The layout is not something we have to guess at: the renderer already encodes it in the card's classes in getCardClasses. The parser now reverses that mapping instead:

classes layout
kg-layout-split split
kg-width-full full
kg-width-wide wide
kg-width-regular regular

kg-layout-split is checked first because split cards also carry kg-width-full.

For hand-authored HTML that omits the width classes there is still a structural signal to fall back on — the renderer only nests the image inside .kg-header-card-content for split layouts, so an image in the content wrapper means split, and anything else falls back to the node's full default.

Note on the changed test

parses a header card V2 asserted layout === 'split' for markup with no width classes and the image as a direct child of the card — which is the exact shape of a full-width card, and the bug being reported here. That expectation now reads full.

Tests

Added to test/nodes/header.test.ts:

  • parses the layout from the card classes — round-trips all four layouts
  • does not force split layout when a full-width card has a background image — the reproduction from the issue verbatim
  • falls back to the image position when the layout classes are missing — covers the structural fallback

All three fail on main and pass with this change. Full package suite: 63 files / 851 tests passing, tsc --noEmit clean, eslint clean on both touched files.


  • I've read and followed the Contributor Guide
  • I've explained my change
  • I've written an automated test to prove my change works

fixes TryGhost#29268

- the v2 header parser inferred the layout purely from the presence of a background image, so every card with an image was imported as `split`
- this flattened regular, wide and full cards into split whenever rendered HTML was sent back to Ghost, e.g. via the Admin API with `?source=html`
- the layout is already encoded in the card's classes by the renderer's `getCardClasses`, so the parser now reverses that mapping rather than guessing from the image
- for hand-authored HTML that omits the width classes, it falls back to the image's position in the DOM, which is the only structural difference between split and the other layouts
- the existing importDOM test asserted the buggy behaviour (a card with no width classes and the image as a direct child was expected to be `split`), so it now expects `full`

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c3f5ea34-4449-47cf-b12c-b7fb3c5940b5

📥 Commits

Reviewing files that changed from the base of the PR and between 1872b95 and b0a2a4d.

📒 Files selected for processing (1)
  • .changeset/floppy-bobcats-spend.md

Walkthrough

The v2 header parser now infers layouts from card classes and image placement. It supports split, full, wide, and regular layouts, with full as the default. Tests cover class-based layouts, round-trip preservation, full layouts with background images, and structural fallback to split.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main fix: preserving header card layouts during HTML import.
Description check ✅ Passed The description explains the parser bug, the implemented fix, affected layouts, and test coverage.
Linked Issues check ✅ Passed The changes infer layouts from explicit classes and DOM structure, preserving full, wide, regular, and split layouts as required by issue #29268.
Out of Scope Changes check ✅ Passed The parser fix, regression tests, and patch changeset are directly related to the linked issue objectives.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@9larsons
9larsons enabled auto-merge (squash) August 3, 2026 17:24
@nx-cloud

nx-cloud Bot commented Aug 3, 2026

Copy link
Copy Markdown

🤖 Nx Cloud AI Fix

Ensure the fix-ci command is configured to always run in your CI pipeline to get automatic fixes in future runs. For more information, please see https://nx.dev/ci/features/self-healing-ci


View your CI Pipeline Execution ↗ for commit 56e37d2

Command Status Duration Result
nx run ghost:test:ci:integration ✅ Succeeded 3m 19s View ↗
nx run @tryghost/admin:test:acceptance ✅ Succeeded 6m 16s View ↗
nx run ghost:test:integration ✅ Succeeded 2m 31s View ↗
nx run @tryghost/koenig-lexical:test:acceptance ✅ Succeeded 2m 18s View ↗
nx run ghost:test:legacy ✅ Succeeded 2m 53s View ↗
nx run ghost:test:e2e ✅ Succeeded 2m 9s View ↗
nx run-many -t test:unit -p @tryghost/kg-defaul... ✅ Succeeded 42s View ↗
nx run @tryghost/activitypub:test:acceptance ✅ Succeeded 43s View ↗
Additional runs (5) ✅ Succeeded ... View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-08-03 17:43:57 UTC

ref TryGhost#29268

This ensures the parser fix is included in the next kg-default-nodes patch release.
@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 75.46%. Comparing base (c34a487) to head (b0a2a4d).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #29706      +/-   ##
==========================================
+ Coverage   75.42%   75.46%   +0.03%     
==========================================
  Files        1611     1611              
  Lines      141912   141912              
  Branches    17549    17563      +14     
==========================================
+ Hits       107037   107090      +53     
+ Misses      33803    33752      -51     
+ Partials     1072     1070       -2     
Flag Coverage Δ
e2e-tests 77.59% <ø> (+0.04%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@9larsons
9larsons merged commit a402f9f into TryGhost:main Aug 3, 2026
48 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Header card HTML parser incorrectly defaults to split layout when image is present

2 participants