fix(admin): show placeholder when media picker image is missing#1050
fix(admin): show placeholder when media picker image is missing#1050wojtekpiskorz wants to merge 2 commits into
Conversation
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.
🦋 Changeset detectedLatest commit: 43dd79e The changes in this PR will be included in the next version bump. This PR includes changesets to release 13 packages
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 |
|
All contributors have signed the CLA ✍️ ✅ |
There was a problem hiding this comment.
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
imageBrokenstate +onErrorhandler inBlockKitMediaPickerFieldandContentEditor'sImageFieldRenderer, swapping the<img>for a "Image not found" placeholder withImageBrokenicon. - Add
min-h-20to the<img>to prevent collapse during loading. - Add 3 new tests covering the broken-image state in
BlockKitMediaPickerField.test.tsxand 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); |
|
I have read the CLA Document and I hereby sign the CLA |
|
recheck |
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.
What does this PR do?
When a
media_pickerfield 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 areposition: absolute, they become invisible and unreachable via mouse — the only workaround is keyboard Tab.This PR adds an
onErrorhandler on the<img>tag and renders a broken-image placeholder withmin-heightso the action buttons remain accessible.Type of change
Checklist
pnpm typecheckpassespnpm lintpassespnpm testpasses — 878/878 tests pass (including 3 new)pnpm formathas been runBlockKitMediaPickerField.test.tsx)t\Image not found`via@lingui/react/macro`).changeset/fix-media-picker-broken-image.mdmessages.pochanges includedAI-generated code disclosure
Details
Root cause
Both
BlockKitMediaPickerField.tsx(lines 57-81) andContentEditor.tsxImageFieldRenderer(line 1613) render an<img>with onlymax-h-40/max-h-48(a maximum) — nomin-height, noonErrorhandler. 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
onErrorhandler on<img>that setsimageBrokenstate to truemin-h-20class on the img (prevents collapse during loading)imageBrokenis true: hides the broken img, renders a placeholder div withmin-h-20, broken-image icon, "Image not found" text, and Change/Remove buttons (not absolute-positioned)BlockKitMediaPickerField.tsxandContentEditor.tsxTests added
3 new tests in
BlockKitMediaPickerField.test.tsxunderdescribe("broken image"):Files changed
packages/admin/src/components/BlockKitMediaPickerField.tsximageBrokenstate,onErrorhandler, broken-image placeholderpackages/admin/src/components/ContentEditor.tsxImageFieldRendererpackages/admin/tests/components/BlockKitMediaPickerField.test.tsx.changeset/fix-media-picker-broken-image.md@emdash-cms/admin