diff --git a/src/admin/components/elements/ListDrawer/DrawerContent.tsx b/src/admin/components/elements/ListDrawer/DrawerContent.tsx index 9796e8dae..112fb3f1c 100644 --- a/src/admin/components/elements/ListDrawer/DrawerContent.tsx +++ b/src/admin/components/elements/ListDrawer/DrawerContent.tsx @@ -67,7 +67,8 @@ const DrawerContent: React.FC { const { t, i18n } = useTranslation(['upload', 'general']); const { permissions } = useAuth(); @@ -165,13 +166,20 @@ const DrawerContent: React.FC { const syncColumnsFromPrefs = async () => { @@ -324,7 +332,7 @@ export const ListDrawerContent: React.FC = (props) => { const [enabledCollectionConfigs] = useState(() => collections.filter((coll) => shouldIncludeCollection({ coll, uploads, collectionSlugs }))); - if (enabledCollectionConfigs.length === 0){ + if (enabledCollectionConfigs.length === 0) { return null; } @@ -333,5 +341,5 @@ export const ListDrawerContent: React.FC = (props) => { {...props} enabledCollectionConfigs={enabledCollectionConfigs} /> - ) -} + ); +}; diff --git a/src/admin/components/elements/ListDrawer/index.tsx b/src/admin/components/elements/ListDrawer/index.tsx index b4e585bfc..f5f9870c9 100644 --- a/src/admin/components/elements/ListDrawer/index.tsx +++ b/src/admin/components/elements/ListDrawer/index.tsx @@ -54,7 +54,12 @@ export const ListDrawer: React.FC = (props) => { ); }; -export const useListDrawer: UseListDrawer = ({ collectionSlugs, uploads, selectedCollection }) => { +export const useListDrawer: UseListDrawer = ({ + collectionSlugs, + uploads, + selectedCollection, + filterOptions, +}) => { const drawerDepth = useEditDepth(); const uuid = useId(); const { modalState, toggleModal, closeModal, openModal } = useModal(); @@ -90,9 +95,10 @@ export const useListDrawer: UseListDrawer = ({ collectionSlugs, uploads, selecte closeDrawer={closeDrawer} key={drawerSlug} selectedCollection={selectedCollection} + filterOptions={filterOptions} /> )); - }, [drawerSlug, collectionSlugs, uploads, closeDrawer, selectedCollection]); + }, [drawerSlug, collectionSlugs, uploads, closeDrawer, selectedCollection, filterOptions]); const MemoizedDrawerToggler = useMemo(() => { return ((props) => ( diff --git a/src/admin/components/elements/ListDrawer/types.ts b/src/admin/components/elements/ListDrawer/types.ts index d210e019b..d292b85fd 100644 --- a/src/admin/components/elements/ListDrawer/types.ts +++ b/src/admin/components/elements/ListDrawer/types.ts @@ -1,5 +1,6 @@ import React, { HTMLAttributes } from 'react'; import { SanitizedCollectionConfig } from '../../../../collections/config/types'; +import { FilterOptionsResult } from '../../forms/field-types/Relationship/types'; export type ListDrawerProps = { onSelect?: (args: { @@ -11,6 +12,7 @@ export type ListDrawerProps = { collectionSlugs?: string[] uploads?: boolean selectedCollection?: string + filterOptions?: FilterOptionsResult } export type ListTogglerProps = HTMLAttributes & { @@ -24,6 +26,7 @@ export type UseListDrawer = (args: { collectionSlugs?: string[] selectedCollection?: string uploads?: boolean // finds all collections with upload: true + filterOptions?: FilterOptionsResult }) => [ React.FC>, // drawer React.FC>, // toggler diff --git a/src/admin/components/forms/field-types/Relationship/index.tsx b/src/admin/components/forms/field-types/Relationship/index.tsx index 9d912eab9..05214d728 100644 --- a/src/admin/components/forms/field-types/Relationship/index.tsx +++ b/src/admin/components/forms/field-types/Relationship/index.tsx @@ -22,7 +22,7 @@ import { useDebouncedCallback } from '../../../../hooks/useDebouncedCallback'; import wordBoundariesRegex from '../../../../../utilities/wordBoundariesRegex'; import { AddNewRelation } from './AddNew'; import { findOptionsByValue } from './findOptionsByValue'; -import { GetFilterOptions } from './GetFilterOptions'; +import { GetFilterOptions } from '../../../utilities/GetFilterOptions'; import { SingleValue } from './select-components/SingleValue'; import { MultiValueLabel } from './select-components/MultiValueLabel'; import { DocumentDrawerProps } from '../../../elements/DocumentDrawer/types'; diff --git a/src/admin/components/forms/field-types/Upload/Input.tsx b/src/admin/components/forms/field-types/Upload/Input.tsx index c1a18f2f5..c353051bc 100644 --- a/src/admin/components/forms/field-types/Upload/Input.tsx +++ b/src/admin/components/forms/field-types/Upload/Input.tsx @@ -14,6 +14,8 @@ import { useListDrawer } from '../../../elements/ListDrawer'; import Button from '../../../elements/Button'; import { DocumentDrawerProps } from '../../../elements/DocumentDrawer/types'; import { ListDrawerProps } from '../../../elements/ListDrawer/types'; +import { GetFilterOptions } from '../../../utilities/GetFilterOptions'; +import { FilterOptionsResult } from '../Relationship/types'; import './index.scss'; @@ -57,6 +59,7 @@ const UploadInput: React.FC = (props) => { api = '/api', collection, errorMessage, + filterOptions, } = props; const { t, i18n } = useTranslation('fields'); @@ -64,6 +67,7 @@ const UploadInput: React.FC = (props) => { const [file, setFile] = useState(undefined); const [missingFile, setMissingFile] = useState(false); const [collectionSlugs] = useState([collection?.slug]); + const [filterOptionsResult, setFilterOptionsResult] = useState(); const [ DocumentDrawer, @@ -83,6 +87,7 @@ const UploadInput: React.FC = (props) => { }, ] = useListDrawer({ collectionSlugs, + filterOptions: filterOptionsResult, }); const classes = [ @@ -145,6 +150,15 @@ const UploadInput: React.FC = (props) => { width, }} > + fs.rmSync(`${uploadsDir}/${f}`)); - const filePath = path.resolve(__dirname, './collections/Upload/payload.jpg'); - const file = await getFileByPath(filePath); + const pngPath = path.resolve(__dirname, './uploads/payload.png'); + const pngFile = await getFileByPath(pngPath); + const createdPNGDoc = await payload.create({ collection: 'uploads', data: {}, file: pngFile }); - const createdUploadDoc = await payload.create({ collection: 'uploads', data: uploadsDoc, file }); + const jpgPath = path.resolve(__dirname, './collections/Upload/payload.jpg'); + const jpgFile = await getFileByPath(jpgPath); + const createdJPGDoc = await payload.create({ + collection: 'uploads', + data: { + ...uploadsDoc, + media: createdPNGDoc.id, + }, + file: jpgFile + }); const richTextDocWithRelId = JSON.parse(JSON.stringify(richTextDoc).replace('{{ARRAY_DOC_ID}}', createdArrayDoc.id)); const richTextDocWithRelationship = { ...richTextDocWithRelId }; @@ -101,8 +111,8 @@ export default buildConfig({ richTextDocWithRelationship.richText[richTextRelationshipIndex].value = { id: createdTextDoc.id }; const richTextUploadIndex = richTextDocWithRelationship.richText.findIndex(({ type }) => type === 'upload'); - richTextDocWithRelationship.richText[richTextUploadIndex].value = { id: createdUploadDoc.id }; - richTextDocWithRelationship.richTextReadOnly[richTextUploadIndex].value = { id: createdUploadDoc.id }; + richTextDocWithRelationship.richText[richTextUploadIndex].value = { id: createdJPGDoc.id }; + richTextDocWithRelationship.richTextReadOnly[richTextUploadIndex].value = { id: createdJPGDoc.id }; await payload.create({ collection: 'rich-text-fields', data: richTextBulletsDoc }); await payload.create({ collection: 'rich-text-fields', data: richTextDocWithRelationship }); diff --git a/test/fields/e2e.spec.ts b/test/fields/e2e.spec.ts index 842f51013..c29416f81 100644 --- a/test/fields/e2e.spec.ts +++ b/test/fields/e2e.spec.ts @@ -1,5 +1,6 @@ import type { Page } from '@playwright/test'; import { expect, test } from '@playwright/test'; +import path from 'path'; import { AdminUrlUtil } from '../helpers/adminUrlUtil'; import { initPayloadE2E } from '../helpers/configHelpers'; import { login, saveDocAndAssert } from '../helpers'; @@ -500,4 +501,55 @@ describe('fields', () => { await expect(page.locator('.Toastify')).toContainText('successfully'); }); }); + + describe('upload', () => { + let url: AdminUrlUtil; + beforeAll(() => { + url = new AdminUrlUtil(serverURL, 'uploads'); + }); + + test('should upload files', async () => { + await page.goto(url.create); + + // create a jpg upload + await page.locator('.file-field__upload input[type="file"]').setInputFiles(path.resolve(__dirname, './collections/Upload/payload.jpg')); + await expect(page.locator('.file-field .file-field__filename')).toContainText('payload.jpg'); + await page.locator('#action-save').click(); + await wait(200); + await expect(page.locator('.Toastify')).toContainText('successfully'); + }); + + // test that the image renders + test('should render uploaded image', async () => { + await expect(page.locator('.file-field .file-details img')).toHaveAttribute('src', '/uploads/payload-1.jpg'); + }); + + test('should upload using the document drawer', async () => { + // Open the media drawer and create a png upload + await page.locator('.field-type.upload .upload__toggler.doc-drawer__toggler').click(); + await page.locator('[id^=doc-drawer_uploads_1_] .file-field__upload input[type="file"]').setInputFiles(path.resolve(__dirname, './uploads/payload.png')); + await page.locator('[id^=doc-drawer_uploads_1_] #action-save').click(); + await wait(200); + await expect(page.locator('.Toastify')).toContainText('successfully'); + + // Assert that the media field has the png upload + await expect(page.locator('.field-type.upload .file-details .file-meta__url a')).toHaveAttribute('href', '/uploads/payload-1.png'); + await expect(page.locator('.field-type.upload .file-details .file-meta__url a')).toContainText('payload-1.png'); + await expect(page.locator('.field-type.upload .file-details img')).toHaveAttribute('src', '/uploads/payload-1.png'); + await page.locator('#action-save').click(); + await wait(200); + await expect(page.locator('.Toastify')).toContainText('successfully'); + }); + + test('should clear selected upload', async () => { + await page.locator('.field-type.upload .file-details__remove').click(); + }); + + test('should select using the list drawer and restrict mimetype based on filterOptions', async () => { + await page.locator('.field-type.upload .upload__toggler.list-drawer__toggler').click(); + await wait(200); + const jpgImages = await page.locator('[id^=list-drawer_1_] .upload-gallery img[src$=".jpg"]'); + expect(await jpgImages.count()).toEqual(0); + }); + }); }); diff --git a/test/fields/uploads/payload.png b/test/fields/uploads/payload.png index e69de29bb..39a580da7 100644 Binary files a/test/fields/uploads/payload.png and b/test/fields/uploads/payload.png differ