fix: filterOptions for upload fields (#7347)
## Description Fixes uploads `filterOptions` not being respected in the Payload admin UI. Needs a test written, fixes to types in build, as well as any tests that fail due to this change in CI. - [x] I have read and understand the [CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md) document in this repository. ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## Checklist: - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] Existing test suite passes locally with my changes - [ ] I have made corresponding changes to the documentation
This commit is contained in:
@@ -21,7 +21,7 @@ import type { SanitizedCollectionConfig, TypeWithID } from '../../collections/co
|
||||
import type { CustomComponent, LabelFunction } from '../../config/types.js'
|
||||
import type { DBIdentifierName } from '../../database/types.js'
|
||||
import type { SanitizedGlobalConfig } from '../../globals/config/types.js'
|
||||
import type { CollectionSlug, GeneratedTypes } from '../../index.js'
|
||||
import type { CollectionSlug } from '../../index.js'
|
||||
import type { DocumentPreferences } from '../../preferences/types.js'
|
||||
import type { Operation, PayloadRequest, RequestContext, Where } from '../../types/index.js'
|
||||
import type { ClientFieldConfig } from './client.js'
|
||||
@@ -123,6 +123,10 @@ export type FilterOptionsProps<TData = any> = {
|
||||
user: Partial<PayloadRequest['user']>
|
||||
}
|
||||
|
||||
export type FilterOptionsFunc<TData = any> = (
|
||||
options: FilterOptionsProps<TData>,
|
||||
) => Promise<Where | boolean> | Where | boolean
|
||||
|
||||
export type FilterOptions<TData = any> =
|
||||
| ((options: FilterOptionsProps<TData>) => Promise<Where | boolean> | Where | boolean)
|
||||
| Where
|
||||
|
||||
@@ -2,6 +2,7 @@ import type {
|
||||
Data,
|
||||
DocumentPreferences,
|
||||
Field,
|
||||
FilterOptionsResult,
|
||||
FormField,
|
||||
FormState,
|
||||
PayloadRequest,
|
||||
@@ -379,16 +380,31 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
|
||||
}
|
||||
|
||||
case 'relationship': {
|
||||
if (typeof field.filterOptions === 'function') {
|
||||
const query = await getFilterOptionsQuery(field.filterOptions, {
|
||||
id,
|
||||
data: fullData,
|
||||
relationTo: field.relationTo,
|
||||
siblingData: data,
|
||||
user: req.user,
|
||||
})
|
||||
if (field.filterOptions) {
|
||||
if (typeof field.filterOptions === 'object') {
|
||||
if (typeof field.relationTo === 'string') {
|
||||
fieldState.filterOptions = {
|
||||
[field.relationTo]: field.filterOptions,
|
||||
}
|
||||
} else {
|
||||
fieldState.filterOptions = field.relationTo.reduce((acc, relation) => {
|
||||
acc[relation] = field.filterOptions
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
}
|
||||
|
||||
fieldState.filterOptions = query
|
||||
if (typeof field.filterOptions === 'function') {
|
||||
const query = await getFilterOptionsQuery(field.filterOptions, {
|
||||
id,
|
||||
data: fullData,
|
||||
relationTo: field.relationTo,
|
||||
siblingData: data,
|
||||
user: req.user,
|
||||
})
|
||||
|
||||
fieldState.filterOptions = query
|
||||
}
|
||||
}
|
||||
|
||||
if (field.hasMany) {
|
||||
@@ -449,16 +465,24 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
|
||||
}
|
||||
|
||||
case 'upload': {
|
||||
if (typeof field.filterOptions === 'function') {
|
||||
const query = await getFilterOptionsQuery(field.filterOptions, {
|
||||
id,
|
||||
data: fullData,
|
||||
relationTo: field.relationTo,
|
||||
siblingData: data,
|
||||
user: req.user,
|
||||
})
|
||||
if (field.filterOptions) {
|
||||
if (typeof field.filterOptions === 'object') {
|
||||
fieldState.filterOptions = {
|
||||
[field.relationTo]: field.filterOptions,
|
||||
}
|
||||
}
|
||||
|
||||
fieldState.filterOptions = query
|
||||
if (typeof field.filterOptions === 'function') {
|
||||
const query = await getFilterOptionsQuery(field.filterOptions, {
|
||||
id,
|
||||
data: fullData,
|
||||
relationTo: field.relationTo,
|
||||
siblingData: data,
|
||||
user: req.user,
|
||||
})
|
||||
|
||||
fieldState.filterOptions = query
|
||||
}
|
||||
}
|
||||
|
||||
const relationshipValue =
|
||||
|
||||
@@ -300,6 +300,22 @@ describe('uploads', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('should restrict uploads in drawer 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 wait(500) // flake workaround
|
||||
await page.locator('.file-details__remove').click()
|
||||
|
||||
await openDocDrawer(page, '.upload__toggler.list-drawer__toggler')
|
||||
|
||||
const listDrawer = page.locator('[id^=list-drawer_1_]')
|
||||
await expect(listDrawer).toBeVisible()
|
||||
|
||||
await expect(listDrawer.locator('tbody tr')).toHaveCount(1)
|
||||
})
|
||||
|
||||
test('should throw error when file is larger than the limit and abortOnLimit is true', async () => {
|
||||
await page.goto(mediaURL.create)
|
||||
await page.setInputFiles('input[type="file"]', path.resolve(dirname, './2mb.jpg'))
|
||||
|
||||
@@ -16,6 +16,9 @@ export interface Config {
|
||||
'gif-resize': GifResize;
|
||||
'no-image-sizes': NoImageSize;
|
||||
'object-fit': ObjectFit;
|
||||
'with-meta-data': WithMetaDatum;
|
||||
'without-meta-data': WithoutMetaDatum;
|
||||
'with-only-jpeg-meta-data': WithOnlyJpegMetaDatum;
|
||||
'crop-only': CropOnly;
|
||||
'focal-only': FocalOnly;
|
||||
'focal-no-sizes': FocalNoSize;
|
||||
@@ -38,6 +41,9 @@ export interface Config {
|
||||
'payload-preferences': PayloadPreference;
|
||||
'payload-migrations': PayloadMigration;
|
||||
};
|
||||
db: {
|
||||
defaultIDType: string;
|
||||
};
|
||||
globals: {};
|
||||
locale: null;
|
||||
user: User & {
|
||||
@@ -49,13 +55,16 @@ export interface UserAuthOperations {
|
||||
email: string;
|
||||
};
|
||||
login: {
|
||||
password: string;
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
registerFirstUser: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
unlock: {
|
||||
email: string;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
@@ -344,6 +353,90 @@ export interface ObjectFit {
|
||||
};
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "with-meta-data".
|
||||
*/
|
||||
export interface WithMetaDatum {
|
||||
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;
|
||||
sizes?: {
|
||||
sizeOne?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "without-meta-data".
|
||||
*/
|
||||
export interface WithoutMetaDatum {
|
||||
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;
|
||||
sizes?: {
|
||||
sizeTwo?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "with-only-jpeg-meta-data".
|
||||
*/
|
||||
export interface WithOnlyJpegMetaDatum {
|
||||
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;
|
||||
sizes?: {
|
||||
sizeThree?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "crop-only".
|
||||
|
||||
Reference in New Issue
Block a user