From 96d1d90e78894dc53a23b6999216b2d7292731cb Mon Sep 17 00:00:00 2001 From: Patrik Date: Fri, 28 Feb 2025 12:47:02 -0500 Subject: [PATCH] fix(ui): use full image url for upload previews instead of thumbnail url (#11435) --- .../ui/src/fields/Upload/HasMany/index.tsx | 17 ++++- .../ui/src/fields/Upload/HasOne/index.tsx | 21 +++-- .../Upload/RelationshipContent/index.tsx | 4 +- test/uploads/collections/Upload1/index.ts | 11 +++ test/uploads/e2e.spec.ts | 66 ++++++++++++++++ test/uploads/payload-types.ts | 76 ++++++++++--------- 6 files changed, 149 insertions(+), 46 deletions(-) diff --git a/packages/ui/src/fields/Upload/HasMany/index.tsx b/packages/ui/src/fields/Upload/HasMany/index.tsx index bd2aacbd4f..ef747f5d3c 100644 --- a/packages/ui/src/fields/Upload/HasMany/index.tsx +++ b/packages/ui/src/fields/Upload/HasMany/index.tsx @@ -67,14 +67,22 @@ export function UploadComponentHasMany(props: Props) { > {fileDocs.map(({ relationTo, value }, index) => { const id = String(value.id) - const url: string = value.thumbnailURL || value.url let src: string + let thumbnailSrc: string - if (url) { + if (value.url) { try { - src = new URL(url, serverURL).toString() + src = new URL(value.url, serverURL).toString() } catch { - src = `${serverURL}${url}` + src = `${serverURL}${value.url}` + } + } + + if (value.thumbnailURL) { + try { + thumbnailSrc = new URL(value.thumbnailURL, serverURL).toString() + } catch { + thumbnailSrc = `${serverURL}${value.thumbnailURL}` } } @@ -118,6 +126,7 @@ export function UploadComponentHasMany(props: Props) { onRemove={() => removeItem(index)} reloadDoc={reloadDoc} src={src} + thumbnailSrc={thumbnailSrc || src} withMeta={false} x={value?.width as number} y={value?.height as number} diff --git a/packages/ui/src/fields/Upload/HasOne/index.tsx b/packages/ui/src/fields/Upload/HasOne/index.tsx index a5d805c360..a808656f1c 100644 --- a/packages/ui/src/fields/Upload/HasOne/index.tsx +++ b/packages/ui/src/fields/Upload/HasOne/index.tsx @@ -29,13 +29,23 @@ export function UploadComponentHasOne(props: Props) { const { relationTo, value } = fileDoc const id = String(value?.id) - const url: string = value.thumbnailURL || value.url let src: string + let thumbnailSrc: string - try { - src = new URL(url, serverURL).toString() - } catch { - src = `${serverURL}${url}` + if (value.url) { + try { + src = new URL(value.url, serverURL).toString() + } catch { + src = `${serverURL}${value.url}` + } + } + + if (value.thumbnailURL) { + try { + thumbnailSrc = new URL(value.thumbnailURL, serverURL).toString() + } catch { + thumbnailSrc = `${serverURL}${value.thumbnailURL}` + } } return ( @@ -52,6 +62,7 @@ export function UploadComponentHasOne(props: Props) { onRemove={onRemove} reloadDoc={reloadDoc} src={src} + thumbnailSrc={thumbnailSrc || src} x={value?.width as number} y={value?.height as number} /> diff --git a/packages/ui/src/fields/Upload/RelationshipContent/index.tsx b/packages/ui/src/fields/Upload/RelationshipContent/index.tsx index ec6bbf64f4..6a2a805076 100644 --- a/packages/ui/src/fields/Upload/RelationshipContent/index.tsx +++ b/packages/ui/src/fields/Upload/RelationshipContent/index.tsx @@ -27,6 +27,7 @@ type Props = { readonly onRemove: () => void readonly reloadDoc: ReloadDoc readonly src: string + readonly thumbnailSrc: string readonly withMeta?: boolean readonly x?: number readonly y?: number @@ -45,6 +46,7 @@ export function RelationshipContent(props: Props) { onRemove, reloadDoc, src, + thumbnailSrc, withMeta = true, x, y, @@ -86,7 +88,7 @@ export function RelationshipContent(props: Props) { alt={alt} className={`${baseClass}__thumbnail`} filename={filename} - fileSrc={src} + fileSrc={thumbnailSrc} size="small" />
diff --git a/test/uploads/collections/Upload1/index.ts b/test/uploads/collections/Upload1/index.ts index bc6580611e..c6d327fde9 100644 --- a/test/uploads/collections/Upload1/index.ts +++ b/test/uploads/collections/Upload1/index.ts @@ -41,6 +41,17 @@ export const Uploads1: CollectionConfig = { }, }, }, + { + type: 'upload', + name: 'hasManyThumbnailUpload', + relationTo: 'admin-thumbnail-size', + hasMany: true, + }, + { + type: 'upload', + name: 'singleThumbnailUpload', + relationTo: 'admin-thumbnail-size', + }, { type: 'richText', name: 'richText', diff --git a/test/uploads/e2e.spec.ts b/test/uploads/e2e.spec.ts index c8b7c4d1cd..6d1032b9cc 100644 --- a/test/uploads/e2e.spec.ts +++ b/test/uploads/e2e.spec.ts @@ -696,6 +696,72 @@ describe('Uploads', () => { await expect(clientText).toHaveText('This text was rendered on the client') }) + test('should show original image url on a single upload card for an upload with adminThumbnail defined', async () => { + await page.goto(uploadsOne.create) + + const singleThumbnailButton = page.locator('#field-singleThumbnailUpload button', { + hasText: exactText('Create New'), + }) + + await singleThumbnailButton.click() + + const singleThumbnailModal = page.locator('[id^="doc-drawer_admin-thumbnail-size"]') + await expect(singleThumbnailModal).toBeVisible() + + await page.setInputFiles( + '[id^="doc-drawer_admin-thumbnail-size"] input[type="file"]', + path.resolve(dirname, './test-image.png'), + ) + const filename = page.locator('[id^="doc-drawer_admin-thumbnail-size"] .file-field__filename') + await expect(filename).toHaveValue('test-image.png') + + await page.waitForSelector('[id^="doc-drawer_admin-thumbnail-size"] #action-save') + await page.locator('[id^="doc-drawer_admin-thumbnail-size"] #action-save').click() + + await expect(page.locator('.payload-toast-container')).toContainText('successfully') + + const href = await page.locator('#field-singleThumbnailUpload a').getAttribute('href') + + // Ensure the URL starts correctly + expect(href).toMatch(/^\/api\/admin-thumbnail-size\/file\/test-image(-\d+)?\.png$/i) + + // Ensure no "-100x100" or any similar suffix + expect(href).not.toMatch(/-\d+x\d+\.png$/) + }) + + test('should show original image url on a hasMany upload card for an upload with adminThumbnail defined', async () => { + await page.goto(uploadsOne.create) + + const hasManyThumbnailButton = page.locator('#field-hasManyThumbnailUpload button', { + hasText: exactText('Create New'), + }) + await hasManyThumbnailButton.click() + + const hasManyThumbnailModal = page.locator('#bulk-upload-drawer-slug-1') + await expect(hasManyThumbnailModal).toBeVisible() + + await page.setInputFiles('#bulk-upload-drawer-slug-1 .dropzone input[type="file"]', [ + path.resolve(dirname, './test-image.png'), + ]) + + const saveButton = page.locator('.bulk-upload--actions-bar__saveButtons button') + await saveButton.click() + + await page.waitForSelector('#field-hasManyThumbnailUpload .upload--has-many__dragItem') + const itemCount = await page + .locator('#field-hasManyThumbnailUpload .upload--has-many__dragItem') + .count() + expect(itemCount).toEqual(1) + + await page.waitForSelector('#field-hasManyThumbnailUpload .upload--has-many__dragItem a') + const href = await page + .locator('#field-hasManyThumbnailUpload .upload--has-many__dragItem a') + .getAttribute('href') + + expect(href).toMatch(/^\/api\/admin-thumbnail-size\/file\/test-image(-\d+)?\.png$/i) + expect(href).not.toMatch(/-\d+x\d+\.png$/) + }) + describe('bulk uploads', () => { test('should bulk upload multiple files', async () => { // Navigate to the upload creation page diff --git a/test/uploads/payload-types.ts b/test/uploads/payload-types.ts index 038b568d30..1956fbcdba 100644 --- a/test/uploads/payload-types.ts +++ b/test/uploads/payload-types.ts @@ -1002,6 +1002,8 @@ export interface Uploads1 { id: string; hasManyUpload?: (string | Uploads2)[] | null; singleUpload?: (string | null) | Uploads2; + hasManyThumbnailUpload?: (string | AdminThumbnailSize)[] | null; + singleThumbnailUpload?: (string | null) | AdminThumbnailSize; richText?: { root: { type: string; @@ -1049,42 +1051,6 @@ export interface Uploads2 { focalX?: number | null; focalY?: number | null; } -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "admin-thumbnail-function". - */ -export interface AdminThumbnailFunction { - id: string; - updatedAt: string; - createdAt: string; - url?: string | null; - thumbnailURL?: string | null; - filename?: string | null; - mimeType?: string | null; - filesize?: number | null; - width?: number | null; - height?: number | null; - focalX?: number | null; - focalY?: number | null; -} -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "admin-thumbnail-with-search-queries". - */ -export interface AdminThumbnailWithSearchQuery { - id: string; - updatedAt: string; - createdAt: string; - url?: string | null; - thumbnailURL?: string | null; - filename?: string | null; - mimeType?: string | null; - filesize?: number | null; - width?: number | null; - height?: number | null; - focalX?: number | null; - focalY?: number | null; -} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "admin-thumbnail-size". @@ -1121,6 +1087,42 @@ export interface AdminThumbnailSize { }; }; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "admin-thumbnail-function". + */ +export interface AdminThumbnailFunction { + id: string; + updatedAt: string; + createdAt: string; + url?: string | null; + thumbnailURL?: string | null; + filename?: string | null; + mimeType?: string | null; + filesize?: number | null; + width?: number | null; + height?: number | null; + focalX?: number | null; + focalY?: number | null; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "admin-thumbnail-with-search-queries". + */ +export interface AdminThumbnailWithSearchQuery { + id: string; + updatedAt: string; + createdAt: string; + url?: string | null; + thumbnailURL?: string | null; + filename?: string | null; + mimeType?: string | null; + filesize?: number | null; + width?: number | null; + height?: number | null; + focalX?: number | null; + focalY?: number | null; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "optional-file". @@ -2344,6 +2346,8 @@ export interface ExternallyServedMediaSelect { export interface Uploads1Select { hasManyUpload?: T; singleUpload?: T; + hasManyThumbnailUpload?: T; + singleThumbnailUpload?: T; richText?: T; updatedAt?: T; createdAt?: T;