Skip to content

fix(admin): show placeholder when media picker image is missing#1050

Open
wojtekpiskorz wants to merge 2 commits into
emdash-cms:mainfrom
wojtekpiskorz:fix/media-picker-broken-image-collapse
Open

fix(admin): show placeholder when media picker image is missing#1050
wojtekpiskorz wants to merge 2 commits into
emdash-cms:mainfrom
wojtekpiskorz:fix/media-picker-broken-image-collapse

Conversation

@wojtekpiskorz
Copy link
Copy Markdown

What does this PR do?

When a media_picker field references an image that no longer exists in storage (e.g. after syncing a database between environments without syncing uploads), the <img> tag 404s and the preview container collapses to 0px height. Since the "Change" and "Remove" buttons are position: absolute, they become invisible and unreachable via mouse — the only workaround is keyboard Tab.

This PR adds an onError handler on the <img> tag and renders a broken-image placeholder with min-height so the action buttons remain accessible.

Type of change

  • Bug fix

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes — 878/878 tests pass (including 3 new)
  • pnpm format has been run
  • I have added/updated tests for my changes (3 new tests in BlockKitMediaPickerField.test.tsx)
  • User-visible strings wrapped for translation (t\Image not found`via@lingui/react/macro`)
  • I have added a changeset.changeset/fix-media-picker-broken-image.md
  • No messages.po changes included

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: DeepSeek V4 Pro (via Hermes Agent delegation), GLM 5.1 (orchestration)

Details

Root cause

Both BlockKitMediaPickerField.tsx (lines 57-81) and ContentEditor.tsx ImageFieldRenderer (line 1613) render an <img> with only max-h-40 / max-h-48 (a maximum) — no min-height, no onError handler. When the image src 404s, the browser renders the broken-image indicator at ~0px height. The <div className="relative group"> container collapses, and the absolutely-positioned Change/Remove buttons become invisible.

Fix

  1. Added onError handler on <img> that sets imageBroken state to true
  2. Added min-h-20 class on the img (prevents collapse during loading)
  3. When imageBroken is true: hides the broken img, renders a placeholder div with min-h-20, broken-image icon, "Image not found" text, and Change/Remove buttons (not absolute-positioned)
  4. Applied to both files: BlockKitMediaPickerField.tsx and ContentEditor.tsx

Tests added

3 new tests in BlockKitMediaPickerField.test.tsx under describe("broken image"):

  • shows a broken-image placeholder when the img fails to load
  • keeps Change and Remove buttons accessible when the image is broken
  • maintains a minimum height so the container does not collapse

Files changed

File Change
packages/admin/src/components/BlockKitMediaPickerField.tsx Added imageBroken state, onError handler, broken-image placeholder
packages/admin/src/components/ContentEditor.tsx Same fix for ImageFieldRenderer
packages/admin/tests/components/BlockKitMediaPickerField.test.tsx 3 new tests for broken image behavior
.changeset/fix-media-picker-broken-image.md Patch changeset for @emdash-cms/admin

When a media_picker field references an image that no longer exists
(e.g. after syncing DB between environments without syncing uploads),
the img tag 404s and the preview container collapses to 0px height.
Since Change/Remove buttons are position: absolute, they become
invisible and unreachable via mouse.

Fix: add onError handler + min-height on img + broken-image placeholder
with accessible Change/Remove buttons. Applied to both:
- BlockKitMediaPickerField.tsx (Block Kit plugin field)
- ContentEditor.tsx (standalone image field)

Tests: 3 new tests for broken image behavior, all 878 tests pass.
Copilot AI review requested due to automatic review settings May 15, 2026 09:56
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 15, 2026

🦋 Changeset detected

Latest commit: 43dd79e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@emdash-cms/admin Patch
emdash Patch
@emdash-cms/cloudflare Patch
@emdash-cms/fixture-perf-site Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/auth Patch
@emdash-cms/blocks Patch
@emdash-cms/gutenberg-to-portable-text Patch
@emdash-cms/x402 Patch
create-emdash Patch
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 15, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a UX bug where a media_picker field referencing a missing image collapses the preview container to 0px (because only max-h-* is set), making the absolutely-positioned Change/Remove buttons unreachable. The fix adds an onError handler and renders a placeholder with min-h-20 so the actions stay accessible.

Changes:

  • Add imageBroken state + onError handler in BlockKitMediaPickerField and ContentEditor's ImageFieldRenderer, swapping the <img> for a "Image not found" placeholder with ImageBroken icon.
  • Add min-h-20 to the <img> to prevent collapse during loading.
  • Add 3 new tests covering the broken-image state in BlockKitMediaPickerField.test.tsx and a patch changeset for @emdash-cms/admin.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
packages/admin/src/components/BlockKitMediaPickerField.tsx Adds broken-image state + placeholder branch and onError handler on <img>.
packages/admin/src/components/ContentEditor.tsx Same fix applied to ImageFieldRenderer; imports ImageBroken.
packages/admin/tests/components/BlockKitMediaPickerField.test.tsx New "broken image" describe block with 3 tests.
.changeset/fix-media-picker-broken-image.md Patch changeset entry for @emdash-cms/admin.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}: BlockKitMediaPickerFieldProps) {
const { t } = useLingui();
const [pickerOpen, setPickerOpen] = React.useState(false);
const [imageBroken, setImageBroken] = React.useState(false);
}: ImageFieldRendererProps) {
const { t } = useLingui();
const [pickerOpen, setPickerOpen] = React.useState(false);
const [imageBroken, setImageBroken] = React.useState(false);
@wojtekpiskorz
Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

@wojtekpiskorz
Copy link
Copy Markdown
Author

recheck

github-actions Bot added a commit that referenced this pull request May 15, 2026
Addresses Copilot review: imageBroken was never reset when the user
selected a new image via Change, so the broken placeholder persisted.
Now useEffect resets imageBroken when url/displayUrl changes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants