chore(ui): simplifies adminThumbnail functionality (#5615)

This commit is contained in:
Jarrod Flesch
2024-04-03 08:49:31 -04:00
committed by GitHub
parent 4ee4ad25b0
commit a330fe6017
31 changed files with 273 additions and 237 deletions

View File

@@ -0,0 +1,16 @@
import type { CollectionConfig } from 'payload/types'
import { uploadCollectionSlug } from '../slugs.js'
export const UploadCollection: CollectionConfig = {
slug: uploadCollectionSlug,
upload: {
adminThumbnail: () => 'https://payloadcms.com/images/universal-truth.jpg',
},
fields: [
{
name: 'title',
type: 'text',
},
],
}

View File

@@ -11,6 +11,7 @@ import { CollectionGroup2B } from './collections/Group2B.js'
import { CollectionHidden } from './collections/Hidden.js'
import { CollectionNoApiView } from './collections/NoApiView.js'
import { Posts } from './collections/Posts.js'
import { UploadCollection } from './collections/Upload.js'
import { Users } from './collections/Users.js'
import { AdminButton } from './components/AdminButton/index.js'
import { AfterDashboard } from './components/AfterDashboard/index.js'
@@ -74,6 +75,7 @@ export default buildConfigWithDefaults({
},
},
collections: [
UploadCollection,
Posts,
Users,
CollectionHidden,

View File

@@ -1,14 +1,15 @@
export const usersCollectionSlug = 'users' as const
export const customViews1CollectionSlug = 'custom-views-one' as const
export const customViews2CollectionSlug = 'custom-views-two' as const
export const geoCollectionSlug = 'geo' as const
export const postsCollectionSlug = 'posts' as const
export const group1Collection1Slug = 'group-one-collection-ones' as const
export const group1Collection2Slug = 'group-one-collection-twos' as const
export const group2Collection1Slug = 'group-two-collection-ones' as const
export const group2Collection2Slug = 'group-two-collection-twos' as const
export const hiddenCollectionSlug = 'hidden-collection' as const
export const noApiViewCollectionSlug = 'collection-no-api-view' as const
export const usersCollectionSlug = 'users'
export const customViews1CollectionSlug = 'custom-views-one'
export const customViews2CollectionSlug = 'custom-views-two'
export const geoCollectionSlug = 'geo'
export const postsCollectionSlug = 'posts'
export const group1Collection1Slug = 'group-one-collection-ones'
export const group1Collection2Slug = 'group-one-collection-twos'
export const group2Collection1Slug = 'group-two-collection-ones'
export const group2Collection2Slug = 'group-two-collection-twos'
export const hiddenCollectionSlug = 'hidden-collection'
export const noApiViewCollectionSlug = 'collection-no-api-view'
export const uploadCollectionSlug = 'uploads'
export const collectionSlugs = [
usersCollectionSlug,
customViews1CollectionSlug,
@@ -23,12 +24,12 @@ export const collectionSlugs = [
noApiViewCollectionSlug,
]
export const customGlobalViews1GlobalSlug = 'custom-global-views-one' as const
export const customGlobalViews2GlobalSlug = 'custom-global-views-two' as const
export const globalSlug = 'global' as const
export const group1GlobalSlug = 'group-globals-one' as const
export const group2GlobalSlug = 'group-globals-two' as const
export const hiddenGlobalSlug = 'hidden-global' as const
export const customGlobalViews1GlobalSlug = 'custom-global-views-one'
export const customGlobalViews2GlobalSlug = 'custom-global-views-two'
export const globalSlug = 'global'
export const group1GlobalSlug = 'group-globals-one'
export const group2GlobalSlug = 'group-globals-two'
export const hiddenGlobalSlug = 'hidden-global'
export const noApiViewGlobalSlug = 'global-no-api-view'
export const globalSlugs = [
customGlobalViews1GlobalSlug,

View File

@@ -2,7 +2,7 @@ import { getPayloadHMR } from '@payloadcms/next/utilities'
import { createServer } from 'http'
import nextImport from 'next'
import path from 'path'
import { type Payload } from 'payload'
import { type Payload } from 'payload/types'
import { wait } from 'payload/utilities'
import { parse } from 'url'

View File

@@ -37,7 +37,8 @@
"@payloadcms/ui/templates/*": ["./packages/ui/src/templates/*/index.tsx"],
"@payloadcms/ui/utilities/*": ["./packages/ui/src/utilities/*.ts"],
"@payloadcms/ui/scss": ["./packages/ui/src/scss.scss"],
"@payloadcms/ui/scss/app.scss": ["./packages/ui/src/scss/app.scss"]
"@payloadcms/ui/scss/app.scss": ["./packages/ui/src/scss/app.scss"],
"payload/types": ["./packages/payload/src/exports/types/index.ts"],
}
},
"exclude": ["dist", "build", "node_modules", ".eslintrc.js", "dist/**/*.js", "**/dist/**/*.js"],

View File

@@ -0,0 +1,17 @@
import type { CollectionConfig } from 'payload/types'
import path from 'path'
import { fileURLToPath } from 'url'
import { adminThumbnailFunctionSlug } from '../../shared.js'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
export const AdminThumbnailFunction: CollectionConfig = {
slug: adminThumbnailFunctionSlug,
upload: {
staticDir: path.resolve(dirname, 'test/uploads/media'),
adminThumbnail: () => 'https://payloadcms.com/images/universal-truth.jpg',
},
fields: [],
}

View File

@@ -0,0 +1,29 @@
import type { CollectionConfig } from 'payload/types'
import path from 'path'
import { fileURLToPath } from 'url'
import { adminThumbnailSizeSlug } from '../../shared.js'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
export const AdminThumbnailSize: CollectionConfig = {
slug: adminThumbnailSizeSlug,
upload: {
staticDir: path.resolve(dirname, 'test/uploads/media'),
adminThumbnail: 'small',
imageSizes: [
{
name: 'small',
width: 100,
height: 100,
},
{
name: 'medium',
width: 200,
height: 200,
},
],
},
fields: [],
}

View File

@@ -1,35 +0,0 @@
'use client'
import type React from 'react'
import { useAdminThumbnail } from '@payloadcms/ui/hooks/useAdminThumbnail'
type TypeWithFile = {
filename: string
filesize: number
mimeType: string
} & Record<string, unknown>
function docHasFilename(doc: Record<string, unknown>): doc is TypeWithFile {
if (typeof doc === 'object' && 'filename' in doc) {
return true
}
return false
}
export const adminThumbnailSrc = '/media/image-640x480.png'
function getThumbnailSrc({ doc }) {
if (docHasFilename(doc)) {
if (doc.mimeType.startsWith('image/')) {
return null // Fallback to default admin thumbnail if image
}
return adminThumbnailSrc // Use custom thumbnail if not image
}
return null
}
export const RegisterAdminThumbnailFn: React.FC = () => {
void useAdminThumbnail(getThumbnailSrc)
return null
}

View File

@@ -1,17 +0,0 @@
import type { CollectionConfig } from 'payload/types'
import { fileURLToPath } from 'node:url'
import path from 'path'
import { RegisterAdminThumbnailFn } from './RegisterThumbnailFn.js'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
export const AdminThumbnailCol: CollectionConfig = {
slug: 'admin-thumbnail',
upload: {
staticDir: path.resolve(dirname, '../../media'),
adminThumbnail: RegisterAdminThumbnailFn,
},
fields: [],
}

View File

@@ -5,9 +5,10 @@ import { fileURLToPath } from 'url'
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
import { devUser } from '../credentials.js'
import removeFiles from '../helpers/removeFiles.js'
import { AdminThumbnailFunction } from './collections/AdminThumbnailFunction/index.js'
import { AdminThumbnailSize } from './collections/AdminThumbnailSize/index.js'
import { Uploads1 } from './collections/Upload1/index.js'
import { Uploads2 } from './collections/Upload2/index.js'
import { AdminThumbnailCol } from './collections/admin-thumbnail/index.js'
import {
audioSlug,
enlargeSlug,
@@ -416,7 +417,8 @@ export default buildConfigWithDefaults({
},
Uploads1,
Uploads2,
AdminThumbnailCol,
AdminThumbnailFunction,
AdminThumbnailSize,
{
slug: 'optional-file',
fields: [],
@@ -508,7 +510,7 @@ export default buildConfigWithDefaults({
// Create admin thumbnail media
await payload.create({
collection: AdminThumbnailCol.slug,
collection: AdminThumbnailSize.slug,
data: {},
file: {
...audioFile,
@@ -517,12 +519,21 @@ export default buildConfigWithDefaults({
})
await payload.create({
collection: AdminThumbnailCol.slug,
collection: AdminThumbnailSize.slug,
data: {},
file: {
...imageFile,
name: `thumb-${imageFile.name}`,
},
})
await payload.create({
collection: AdminThumbnailFunction.slug,
data: {},
file: {
...imageFile,
name: `function-image-${imageFile.name}`,
},
})
},
})

View File

@@ -12,7 +12,13 @@ import { initPageConsoleErrorCatch, saveDocAndAssert } from '../helpers.js'
import { AdminUrlUtil } from '../helpers/adminUrlUtil.js'
import { initPayloadE2E } from '../helpers/initPayloadE2E.js'
import { RESTClient } from '../helpers/rest.js'
import { adminThumbnailSlug, audioSlug, mediaSlug, relationSlug } from './shared.js'
import {
adminThumbnailFunctionSlug,
adminThumbnailSizeSlug,
audioSlug,
mediaSlug,
relationSlug,
} from './shared.js'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
@@ -24,7 +30,8 @@ let serverURL: string
let mediaURL: AdminUrlUtil
let audioURL: AdminUrlUtil
let relationURL: AdminUrlUtil
let adminThumbnailURL: AdminUrlUtil
let adminThumbnailSizeURL: AdminUrlUtil
let adminThumbnailFunctionURL: AdminUrlUtil
describe('uploads', () => {
let page: Page
@@ -39,7 +46,8 @@ describe('uploads', () => {
mediaURL = new AdminUrlUtil(serverURL, mediaSlug)
audioURL = new AdminUrlUtil(serverURL, audioSlug)
relationURL = new AdminUrlUtil(serverURL, relationSlug)
adminThumbnailURL = new AdminUrlUtil(serverURL, adminThumbnailSlug)
adminThumbnailSizeURL = new AdminUrlUtil(serverURL, adminThumbnailSizeSlug)
adminThumbnailFunctionURL = new AdminUrlUtil(serverURL, adminThumbnailFunctionSlug)
const context = await browser.newContext()
page = await context.newPage()
@@ -196,6 +204,7 @@ describe('uploads', () => {
test('should restrict mimetype based on filterOptions', async () => {
await page.goto(audioURL.edit(audioDoc.id))
await page.waitForURL(audioURL.edit(audioDoc.id))
// remove the selection and open the list drawer
await page.locator('.file-details__remove').click()
@@ -210,16 +219,31 @@ describe('uploads', () => {
.locator('[id^=doc-drawer_media_2_] .file-field__upload input[type="file"]')
.setInputFiles(path.resolve(dirname, './image.png'))
await page.locator('[id^=doc-drawer_media_2_] button#action-save').click()
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.Toastify .Toastify__toast--success')).toContainText('successfully')
await page.locator('.Toastify .Toastify__toast--success .Toastify__close-button').click()
// save the document and expect an error
await page.locator('button#action-save').click()
await expect(page.locator('.Toastify')).toContainText('Please correct invalid fields.')
await expect(page.locator('.Toastify .Toastify__toast--error')).toContainText(
'Please correct invalid fields.',
)
})
test('Should execute adminThumbnail and provide thumbnail when set', async () => {
await page.goto(adminThumbnailURL.list)
await page.waitForURL(adminThumbnailURL.list)
test('Should render adminThumbnail when using a function', async () => {
await page.goto(adminThumbnailFunctionURL.list)
await page.waitForURL(adminThumbnailFunctionURL.list)
// Ensure sure false or null shows generic file svg
const genericUploadImage = page.locator('tr.row-1 .thumbnail img')
await expect(genericUploadImage).toHaveAttribute(
'src',
'https://payloadcms.com/images/universal-truth.jpg',
)
})
test('Should render adminThumbnail when using a specific size', async () => {
await page.goto(adminThumbnailSizeURL.list)
await page.waitForURL(adminThumbnailSizeURL.list)
// Ensure sure false or null shows generic file svg
const genericUploadImage = page.locator('tr.row-1 .thumbnail img')

View File

@@ -10,7 +10,9 @@ export const enlargeSlug = 'enlarge'
export const reduceSlug = 'reduce'
export const adminThumbnailSlug = 'admin-thumbnail'
export const adminThumbnailFunctionSlug = 'admin-thumbnail-function'
export const adminThumbnailSizeSlug = 'admin-thumbnail-size'
export const unstoredMediaSlug = 'unstored-media'