ImageViewer default export downloads cross-origin images via blob (DES-133)
Version: 0.4.1 · Type: 🐛 Bug Fix
Problem
When no onExport prop is provided, ImageViewer's default export uses <a download> + the original src URL. However, the download attribute only works for same-origin, blob:, or data: URLs. In practice most images are served from cross-origin CDN hosts; browsers silently ignore the download attribute for cross-origin resources (security restriction) and navigate to the URL instead — resulting in "export opens a new tab" rather than triggering a download. The design-site's use of picsum.photos (cross-origin) reliably reproduced the issue.
Changed Files
src/components/ImageViewer/ImageViewer.tsxsrc/components/ImageViewer/__tests__/ImageViewer.test.tsxsrc/components/ImageViewer/ImageViewerDesignSpec.mdpackages/design-site/docs/components/patterns/image-viewer.mdx
Changes
Blob-based download in ImageViewer.tsx
Default export changed to: fetch(src) → blob() → URL.createObjectURL to produce a same-origin objectURL, then trigger <a download>. Cross-origin CDN images can now be genuinely downloaded (requires the CDN to return appropriate CORS headers).
- Falls back to the original URL when fetch fails (CORS denied / network error) or HTTP non-2xx (
response.okis false) — ensures silent failure never occurs and avoids saving an error-page body as a corrupted image file. - objectURL revocation deferred by 1000 ms (
OBJECT_URL_REVOKE_DELAY) after the download is triggered, preventing a synchronousrevokeObjectURLfrom releasing the blob before the browser reads it. - Download filename inferred from the final path segment of
src; falls back to browser-determined filename if no valid name is found. - New module-level helpers:
getFileNameFromSrc/triggerAnchorDownload/downloadImageBySrc. - Component JSDoc and
onExportprop comment updated to reflect the blob-download behavior.
Tests in ImageViewer.test.tsx
- Original "fallback anchor download" test case converted to blob-download assertions (mocks
fetch/URL.createObjectURL/revokeObjectURL; verifies fetch arguments, anchor click, and delayed objectURL revocation). - New test: fetch failure falls back to direct original-URL download.
- New test: HTTP non-2xx (404) fallback — verifies no objectURL is created and the download falls back to the original URL directly.
Documentation
ImageViewerDesignSpec.md and image-viewer.mdx export descriptions updated to reflect the blob-download approach.