feat: add media galleries to entities - #6880
Conversation
🦋 Changeset detectedLatest commit: a0416ec The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #6880 +/- ##
==========================================
- Coverage 57.46% 57.40% -0.07%
==========================================
Files 3370 3383 +13
Lines 73200 73400 +200
Branches 19315 19352 +37
==========================================
+ Hits 42068 42133 +65
- Misses 29232 30646 +1414
+ Partials 1900 621 -1279
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
cda3de7 to
e9d08ef
Compare
There was a problem hiding this comment.
Pull request overview
This PR introduces a shared, generic media gallery + media detail experience for additional entity types (categories, collections, and models/pages) using the Saleor 3.24+ mediaCreate/mediaUpdate/mediaDelete/mediaReorder API (staging schema only), while refactoring product media UI to reuse the new shared components.
Changes:
- Add generic entity media gallery/detail flows (upload, URL upload, reorder, bulk delete, metadata/alt editing) for categories/collections/pages gated behind staging schema.
- Refactor product media views to reuse shared media UI dialogs/pages and unify copy in
mediaMessages. - Add staging GraphQL fragments/mutations/queries and regenerate staging hooks/types/policies accordingly.
Reviewed changes
Copilot reviewed 63 out of 66 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/products/views/ProductUpdate/ProductUpdate.tsx | Switches product media delete dialog + media success messages to shared media components/messages. |
| src/products/views/ProductUpdate/handlers/index.ts | Adjusts upload handler to return created media id; switches to shared media messages. |
| src/products/views/ProductImage.tsx | Migrates product media detail to shared MediaDetailPage + shared dialogs/messages. |
| src/products/components/ProductUpdatePage/ProductUpdatePage.tsx | Replaces ProductMedia with shared MediaGallery and swaps URL dialog to shared MediaUrlDialog. |
| src/products/components/ProductUpdatePage/ProductUpdatePage.topNav.test.tsx | Updates gallery mocking and adds a layout regression test for the media container. |
| src/products/components/ProductMediaPage/ProductMediaPage.tsx | Removes legacy product-only media detail page implementation (deleted). |
| src/products/components/ProductMediaPage/index.ts | Removes barrel export for deleted ProductMediaPage (deleted). |
| src/products/components/ProductMediaNavigation/index.ts | Removes barrel export for product media navigation (deleted). |
| src/products/components/ProductMedia/index.ts | Removes barrel export for ProductMedia (deleted). |
| src/modeling/views/ModelMedia.tsx | Adds a page(model) media detail route/view backed by staging query. |
| src/modeling/urls.ts | Adds modeling page media URL helpers. |
| src/modeling/queries.staging.ts | Adds staging-only query for Page.media. |
| src/modeling/index.tsx | Adds staging-gated route for model/page media detail. |
| src/modeling/components/PageDetailsPage/PageDetailsPage.tsx | Embeds model/page media gallery into the page details view. |
| src/modeling/components/PageDetailsPage/PageDetailsPage.topNav.test.tsx | Mocks the new model media gallery in top-nav tests. |
| src/modeling/components/ModelMediaGallery/ModelMediaGallery.tsx | Adds staging-gated media gallery wrapper for models/pages. |
| src/media/useEntityMediaMutations.ts | Adds generic staging mutations + Apollo cache patching for entity media CRUD/reorder. |
| src/media/types.ts | Introduces shared entity media owner/media typing for non-product owners. |
| src/media/mutations.staging.ts | Adds staging GraphQL documents for generic media mutations. |
| src/media/messages.ts | Consolidates media-related success/error messages into mediaMessages. |
| src/media/fragments/media.staging.ts | Adds staging Media fragment including metadata via inline fragment. |
| src/media/components/EntityMediaGallery/EntityMediaGallery.tsx | Adds generic entity media gallery wiring (upload/URL/delete/reorder) using shared gallery UI. |
| src/media/components/EntityMediaDetailPage/EntityMediaDetailPage.tsx | Adds generic entity media detail wiring (alt/metadata/delete) around shared detail UI. |
| src/graphql/typesStaging.generated.ts | Regenerated staging types for new media API + scalar mappings (generated). |
| src/graphql/typePoliciesStaging.generated.ts | Regenerated Apollo type policies for staging media types (generated). |
| src/graphql/hooksStaging.generated.ts | Regenerated staging hooks for media queries/mutations (generated). |
| src/graphql/fragmentTypesStaging.generated.ts | Regenerated fragment type map for staging media unions (generated). |
| src/components/MediaUrlDialog/messages.ts | Adds i18n messages for shared media URL upload dialog. |
| src/components/MediaUrlDialog/MediaUrlDialog.tsx | Introduces shared “upload by URL” modal. |
| src/components/MediaUrlDialog/MediaUrlDialog.test.tsx | Updates tests to use the new shared dialog component. |
| src/components/MediaTile/MediaTile.tsx | Adjusts tile prop typing to accept nullable type/oembedData. |
| src/components/MediaMetadataDialog/MediaMetadataDialog.tsx | Generalizes metadata dialog to accept any media + a refetch document. |
| src/components/MediaMetadataDialog/MediaMetadataDialog.test.tsx | Updates tests for the generalized metadata dialog API. |
| src/components/MediaGallery/validateMediaFiles.ts | Renames/generalizes file validation helper for shared gallery usage. |
| src/components/MediaGallery/validateMediaFiles.test.ts | Updates tests for the renamed validation helper. |
| src/components/MediaGallery/useMediaDrag.ts | Generalizes drag/reorder hook to structural media type. |
| src/components/MediaGallery/useMediaDrag.test.ts | Updates drag hook tests to use structural media type. |
| src/components/MediaGallery/types.ts | Adds structural GalleryMedia type used by the shared gallery. |
| src/components/MediaGallery/messages.ts | Adds shared gallery i18n messages (header/actions/hints). |
| src/components/MediaGallery/MediaGalleryDropzone.tsx | Renames/generalizes dropzone component + switches to shared CSS module. |
| src/components/MediaGallery/MediaGallery.tsx | Refactors gallery UI into shared MediaGallery using DetailSettingsCard. |
| src/components/MediaGallery/MediaGallery.test.tsx | Updates gallery tests for new API + adds aligned-card shell test. |
| src/components/MediaGallery/MediaGallery.module.css | Adds new CSS module for the shared gallery/dropzone styling. |
| src/components/MediaDetailPage/MediaNavigation.tsx | Renames product-specific navigation to shared MediaNavigation and relaxes media field nullability. |
| src/components/MediaDetailPage/MediaDetailPage.tsx | Adds shared media detail page shell (TopNav + preview + alt edit + savebar). |
| src/components/MediaDetailPage/MediaDetailPage.test.tsx | Adds tests for shared detail page navigation behavior on save success/failure. |
| src/components/MediaDeleteDialog/messages.ts | Renames product-specific delete dialog messages to shared media delete messages. |
| src/components/MediaDeleteDialog/MediaDeleteDialog.tsx | Renames product-specific delete dialog component to shared MediaDeleteDialog. |
| src/components/EntityBackgroundImageField/EntityBackgroundImageField.tsx | Switches background image field to the shared media dropzone + shared messages. |
| src/components/AppLayout/TopNav/destinationMessages.ts | Adds TopNav destination messages for category/collection/model. |
| src/collections/views/CollectionMedia.tsx | Adds collection media detail view (staging query + shared entity detail page). |
| src/collections/urls.ts | Adds collection media URL helpers. |
| src/collections/queries.staging.ts | Adds staging-only query for Collection.media. |
| src/collections/index.tsx | Adds staging-gated route for collection media detail. |
| src/collections/components/CollectionMediaGallery/CollectionMediaGallery.tsx | Adds staging-gated collection media gallery wrapper. |
| src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx | Embeds collection media gallery into collection details page. |
| src/categories/views/CategoryMedia.tsx | Adds category media detail view (staging query + shared entity detail page). |
| src/categories/urls.ts | Adds category media URL helpers. |
| src/categories/queries.staging.ts | Adds staging-only query for Category.media. |
| src/categories/index.tsx | Adds staging-gated route for category media detail. |
| src/categories/components/CategoryUpdatePage/CategoryUpdatePage.tsx | Embeds category media gallery into category update page. |
| src/categories/components/CategoryMediaGallery/CategoryMediaGallery.tsx | Adds staging-gated category media gallery wrapper. |
| schema-staging.graphql | Updates staging schema snapshot with generic media API/types. |
| locale/defaultMessages.json | Adds new default messages for TopNav destinations; removes some unrelated entries. |
| codegen-staging.ts | Adds scalar mappings for staging codegen. |
| .changeset/entity-media-gallery.md | Adds changeset describing the new entity media gallery feature and staging gating. |
Suppressed comments (1)
src/components/MediaUrlDialog/MediaUrlDialog.tsx:58
- If
onSubmitreturns errors (e.g. invalid URL / permission / backend validation), the dialog currently just stays open with no feedback. Please surface the failure (ideally inline near the URL input; a short error toast is a fallback).
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| ownerTypename="Collection" | ||
| ownerName={data?.collection?.name} | ||
| ownerUrl={collectionUrl(collectionId)} | ||
| ownerIcon={<TopNavDestinationIcon.categories />} |
| reorderMedia(arrayMove([...media], oldIndex, newIndex)).then( | ||
| errors => | ||
| errors.length === 0 && | ||
| notify({ status: "success", text: intl.formatMessage(mediaMessages.mediaReorderSuccess) }), | ||
| notifyError, | ||
| ); |
| const handleSave = async () => { | ||
| const errors = await submit(); | ||
|
|
||
| if (errors.length === 0) { | ||
| navigate(ownerUrl); |
Two upstream changes landed under this branch during the rebase: - use-react-router was dropped (#6907), so MediaDetailPage.test.tsx now uses useLocation from "react-router" like the rest of the codebase. - knip's unused-file rule is error-level (#6903). ProductUpdate/messages.ts only survived because ProductUpdate.tsx imported its media messages; those moved to @dashboard/media/messages, and the remaining variant messages had no references anywhere, so the file is gone and defaultMessages.json re-extracted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Barrel files were removed upstream (#6909). The rebase auto-merged these files without touching their imports, so they still pointed at deleted index modules. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
e9d08ef to
a0416ec
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 60 out of 63 changed files in this pull request and generated no new comments.
Suppressed comments (4)
src/collections/views/CollectionMedia.tsx:30
ownerIconpoints to the categories icon, but this page is for a collection. This makes the TopNav destination misleading; use the collections destination icon.
ownerUrl={collectionUrl(collectionId)}
ownerIcon={<TopNavDestinationIcon.categories />}
ownerListLabel={intl.formatMessage(topNavDestinationMessages.collection)}
media={data?.collection?.media}
src/media/components/EntityMediaGallery/EntityMediaGallery.tsx:55
- If
mediaReorderreturns GraphQL errors (but not a network error), the current.then()branch does nothing, so the UI may keep the optimistic order without any error feedback. Treat non-emptyerrorsas a failure and surfacenotifyError()(and/or revert).
src/components/MediaUrlDialog/MediaUrlDialog.tsx:24 MediaUrlDialogcurrently typesonSubmiterrors asunknown[], which removes useful structural typing (message/field/code) that the dialog and callers rely on. Use a small structural "error-like" type instead so consumers get type safety without coupling to a specific schema.
This issue also appears on line 54 of the same file.
src/components/MediaUrlDialog/MediaUrlDialog.tsx:55
- After tightening
onSubmittyping,handleSubmitshould return the same error element type instead ofunknown[]so the form submit pipeline stays typed end-to-end.
Scope of the change