Compare commits

...

10 Commits

Author SHA1 Message Date
Jacob Fletcher
f9bb2202b1 fix test 2025-09-05 10:43:30 -04:00
Jacob Fletcher
4d89a2b747 fix join field relationship table 2025-09-05 10:27:47 -04:00
Jacob Fletcher
e62c8a5661 cleanup 2025-09-05 10:10:45 -04:00
Jacob Fletcher
19fea26447 Merge branch 'main' into feat/list-view-select 2025-09-05 09:51:34 -04:00
Jacob Fletcher
38c346b4c7 reset _community and base tsconfig 2025-09-05 09:45:16 -04:00
Jacob Fletcher
142228e70b add test and rename property again 2025-09-05 09:42:47 -04:00
Jacob Fletcher
ad892a1da4 renames to listSelectActiveColumns 2025-09-04 17:01:51 -04:00
Jacob Fletcher
414e5d9363 add docs 2025-09-04 16:59:50 -04:00
Jacob Fletcher
195accdc00 fix column accessor 2025-09-04 16:30:17 -04:00
Jacob Fletcher
7bb5321a95 perf(ui): opt-in to the select api in the list view 2025-09-04 14:35:37 -04:00
25 changed files with 323 additions and 65 deletions

View File

@@ -85,7 +85,7 @@ The following options are available:
| `versions` | Set to true to enable default options, or configure with object properties. [More details](../versions/overview#collection-config). |
| `defaultPopulate` | Specify which fields to select when this Collection is populated from another document. [More Details](../queries/select#defaultpopulate-collection-config-property). |
| `indexes` | Define compound indexes for this collection. This can be used to either speed up querying/sorting by 2 or more fields at the same time or to ensure uniqueness between several fields. |
| `forceSelect` | Specify which fields should be selected always, regardless of the `select` query which can be useful that the field exists for access control / hooks |
| `forceSelect` | Specify which fields should be selected always, regardless of the `select` query which can be useful that the field exists for access control / hooks. [More details](../queries/select). |
| `disableBulkEdit` | Disable the bulk edit operation for the collection in the admin panel and the REST API |
_\* An asterisk denotes that a property is required._
@@ -141,6 +141,7 @@ The following options are available:
| `livePreview` | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |
| `components` | Swap in your own React components to be used within this Collection. [More details](#custom-components). |
| `listSearchableFields` | Specify which fields should be searched in the List search view. [More details](#list-searchable-fields). |
| `enableListViewSelectAPI` | Performance opt-in. When `true`, uses the Select API in the List View to query only the active columns as opposed to entire documents. [More details](#enable-list-view-select-api). |
| `pagination` | Set pagination-specific options for this Collection in the List View. [More details](#pagination). |
| `baseFilter` | Defines a default base filter which will be applied to the List View (along with any other filters applied by the user) and internal links in Lexical Editor, |
@@ -272,6 +273,66 @@ export const Posts: CollectionConfig = {
these fields so your admin queries can remain performant.
</Banner>
## Enable List View Select API
When `true`, the List View will use the [Select API](../queries/select) to query only the _active_ columns as opposed to entire documents. This can greatly improve performance, especially for collections with large documents or many fields.
To enable this, set `enableListViewSelectAPI: true` in your Collection Config:
```ts
import type { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
// ...
admin: {
// ...
// highlight-start
enableListViewSelectAPI: true,
// highlight-end
},
}
```
<Banner type="info">
**Note:** The `enableListViewSelectAPI` property is labeled as experimental,
as it will likely become the default behavior in v4 and be deprecated.
</Banner>
Enabling this feature may cause unexpected behavior in some cases, however, such as when using hooks that rely on the full document data.
For example, if your component relies on a "title" field, this field will no longer be populated if the column is inactive:
```ts
import type { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
hooks: {
afterRead: [
({ doc }) => doc.title, // The `title` field will no longer be populated by default, unless the column is active
],
},
},
],
}
```
To ensure title is always present, you will need to add that field to the [`forceSelect`](../queries/select) property in your Collection Config:
```ts
export const Posts: CollectionConfig = {
// ...
forceSelect: {
title: true,
},
}
```
## GraphQL
You can completely disable GraphQL for this collection by passing `graphQL: false` to your collection config. This will completely disable all queries, mutations, and types from appearing in your GraphQL schema.

View File

@@ -84,7 +84,7 @@ The following options are available:
| `slug` \* | Unique, URL-friendly string that will act as an identifier for this Global. |
| `typescript` | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
| `versions` | Set to true to enable default options, or configure with object properties. [More details](../versions/overview#global-config). |
| `forceSelect` | Specify which fields should be selected always, regardless of the `select` query which can be useful that the field exists for access control / hooks |
| `forceSelect` | Specify which fields should be selected always, regardless of the `select` query which can be useful that the field exists for access control / hooks. [More details](../queries/select). |
_\* An asterisk denotes that a property is required._

View File

@@ -69,6 +69,7 @@ export type Args = {
relationshipsSuffix?: string
/**
* The schema name to use for the database
*
* @experimental This only works when there are not other tables or enums of the same name in the database under a different schema. Awaiting fix from Drizzle.
*/
schemaName?: string

View File

@@ -72,6 +72,7 @@ export type Args = {
relationshipsSuffix?: string
/**
* The schema name to use for the database
*
* @experimental This only works when there are not other tables or enums of the same name in the database under a different schema. Awaiting fix from Drizzle.
*/
schemaName?: string

View File

@@ -1,10 +1,12 @@
import type {
ClientCollectionConfig,
ClientConfig,
Column,
ListQuery,
PaginatedDocs,
PayloadRequest,
SanitizedCollectionConfig,
SelectType,
ViewTypes,
Where,
} from 'payload'
@@ -14,6 +16,7 @@ import { formatDate } from '@payloadcms/ui/shared'
import { flattenAllFields } from 'payload'
export const handleGroupBy = async ({
clientCollectionConfig,
clientConfig,
collectionConfig,
collectionSlug,
@@ -23,11 +26,13 @@ export const handleGroupBy = async ({
enableRowSelections,
query,
req,
select,
trash = false,
user,
viewType,
where: whereWithMergedSearch,
}: {
clientCollectionConfig: ClientCollectionConfig
clientConfig: ClientConfig
collectionConfig: SanitizedCollectionConfig
collectionSlug: string
@@ -37,6 +42,7 @@ export const handleGroupBy = async ({
enableRowSelections?: boolean
query?: ListQuery
req: PayloadRequest
select?: SelectType
trash?: boolean
user: any
viewType?: ViewTypes
@@ -50,7 +56,6 @@ export const handleGroupBy = async ({
let columnState: Column[]
const dataByGroup: Record<string, PaginatedDocs> = {}
const clientCollectionConfig = clientConfig.collections.find((c) => c.slug === collectionSlug)
// NOTE: is there a faster/better way to do this?
const flattenedFields = flattenAllFields({ fields: collectionConfig.fields })
@@ -132,6 +137,7 @@ export const handleGroupBy = async ({
req,
// Note: if we wanted to enable table-by-table sorting, we could use this:
// sort: query?.queryByGroup?.[valueOrRelationshipID]?.sort,
select,
sort: query?.sort,
trash,
user,

View File

@@ -1,6 +1,6 @@
import { DefaultListView, HydrateAuthProvider, ListQueryProvider } from '@payloadcms/ui'
import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent'
import { renderFilters, renderTable, upsertPreferences } from '@payloadcms/ui/rsc'
import { getColumns, renderFilters, renderTable, upsertPreferences } from '@payloadcms/ui/rsc'
import { notFound } from 'next/navigation.js'
import {
type AdminViewServerProps,
@@ -28,6 +28,7 @@ import { getDocumentPermissions } from '../Document/getDocumentPermissions.js'
import { handleGroupBy } from './handleGroupBy.js'
import { renderListViewSlots } from './renderListViewSlots.js'
import { resolveAllFilterOptions } from './resolveAllFilterOptions.js'
import { transformColumnsToSelect } from './transformColumnsToSelect.js'
type RenderListViewArgs = {
customCellProps?: Record<string, any>
@@ -208,18 +209,32 @@ export const renderListView = async (
totalPages: 0,
}
const clientCollectionConfig = clientConfig.collections.find((c) => c.slug === collectionSlug)
const columns = getColumns({
collectionConfig: clientCollectionConfig,
columns: collectionPreferences?.columns,
i18n,
})
const select = collectionConfig.admin.enableListViewSelectAPI
? transformColumnsToSelect(columns)
: undefined
try {
if (collectionConfig.admin.groupBy && query.groupBy) {
;({ columnState, data, Table } = await handleGroupBy({
clientCollectionConfig,
clientConfig,
collectionConfig,
collectionSlug,
columns: collectionPreferences?.columns,
columns,
customCellProps,
drawerSlug,
enableRowSelections,
query,
req,
select,
trash,
user,
viewType,
@@ -237,15 +252,16 @@ export const renderListView = async (
overrideAccess: false,
page: query?.page ? Number(query.page) : undefined,
req,
select,
sort: query?.sort,
trash,
user,
where: whereWithMergedSearch,
})
;({ columnState, Table } = renderTable({
clientCollectionConfig: clientConfig.collections.find((c) => c.slug === collectionSlug),
clientCollectionConfig,
collectionConfig,
columns: collectionPreferences?.columns,
columns,
customCellProps,
data,
drawerSlug,

View File

@@ -0,0 +1,9 @@
import type { ColumnPreference, SelectType } from 'payload'
export const transformColumnsToSelect = (columns: ColumnPreference[]): SelectType =>
columns.reduce((acc, column) => {
if (column.active) {
acc[column.accessor] = true
}
return acc
}, {} as SelectType)

View File

@@ -58,9 +58,10 @@ export type FieldState = {
filterOptions?: FilterOptionsResult
initialValue?: unknown
/**
* @experimental - Note: this property is experimental and may change in the future. Use at your own discretion.
* Every time a field is changed locally, this flag is set to true. Prevents form state from server from overwriting local changes.
* After merging server form state, this flag is reset.
*
* @experimental This property is experimental and may change in the future. Use at your own discretion.
*/
isModified?: boolean
/**

View File

@@ -383,6 +383,15 @@ export type CollectionAdminOptions = {
* @default false
*/
disableCopyToLocale?: boolean
/**
* Performance opt-in. If true, will use the [Select API](https://payloadcms.com/docs/queries/select) when
* loading the list view to query only the active columns, as opposed to the entire documents.
* If your cells require specific fields that may be unselected, such as within hooks, etc.,
* use `forceSelect` in conjunction with this property.
*
* @experimental This is an experimental feature and may change in the future. Use at your own discretion.
*/
enableListViewSelectAPI?: boolean
enableRichTextLink?: boolean
enableRichTextRelationship?: boolean
/**
@@ -393,10 +402,10 @@ export type CollectionAdminOptions = {
*/
group?: false | Record<string, string> | string
/**
* @experimental This option is currently in beta and may change in future releases and/or contain bugs.
* Use at your own risk.
* @description Enable grouping by a field in the list view.
* Uses `payload.findDistinct` under the hood to populate the group-by options.
*
* @experimental This option is currently in beta and may change in future releases. Use at your own discretion.
*/
groupBy?: boolean
/**

View File

@@ -948,9 +948,10 @@ export type Config = {
*/
timezones?: TimezonesConfig
/**
* @experimental
* Configure toast message behavior and appearance in the admin panel.
* Currently using [Sonner](https://sonner.emilkowal.ski) for toast notifications.
*
* @experimental This property is experimental and may change in future releases. Use at your own discretion.
*/
toast?: {
/**
@@ -1057,7 +1058,8 @@ export type Config = {
experimental?: ExperimentalConfig
/**
* Options for folder view within the admin panel
* @experimental this feature may change in minor versions until it is fully stable
*
* @experimental This feature may change in minor versions until it is fully stable
*/
folders?: false | RootFoldersConfiguration
/**

View File

@@ -69,7 +69,7 @@ type FlattenFieldsOptions = {
* @param options - Options to control the flattening behavior
*/
export function flattenTopLevelFields<TField extends ClientField | Field>(
fields: TField[],
fields: TField[] = [],
options?: boolean | FlattenFieldsOptions,
): FlattenedField<TField>[] {
const normalizedOptions: FlattenFieldsOptions =

View File

@@ -21,8 +21,9 @@ export type DocumentDrawerContextProps = {
readonly onSave?: (args: {
collectionConfig?: ClientCollectionConfig
/**
* @experimental - Note: this property is experimental and may change in the future. Use at your own discretion.
* If you want to pass additional data to the onSuccess callback, you can use this context object.
*
* @experimental This property is experimental and may change in the future. Use at your own discretion.
*/
context?: Record<string, unknown>
doc: TypeWithID

View File

@@ -6,6 +6,7 @@ export { getHTMLDiffComponents } from '../../elements/HTMLDiff/index.js'
export { File } from '../../graphics/File/index.js'
export { CheckIcon } from '../../icons/Check/index.js'
export { copyDataFromLocaleHandler } from '../../utilities/copyDataFromLocale.js'
export { getColumns } from '../../utilities/getColumns.js'
export { getFolderResultsComponentAndData } from '../../utilities/getFolderResultsComponentAndData.js'
export { renderFilters, renderTable } from '../../utilities/renderTable.js'
export { resolveFilterOptions } from '../../utilities/resolveFilterOptions.js'

View File

@@ -88,8 +88,9 @@ export type SubmitOptions<C = Record<string, unknown>> = {
acceptValues?: AcceptValues
action?: string
/**
* @experimental - Note: this property is experimental and may change in the future. Use at your own discretion.
* If you want to pass additional data to the onSuccess callback, you can use this context object.
*
* @experimental This property is experimental and may change in the future.
*/
context?: C
/**
@@ -117,8 +118,9 @@ export type Submit = <T extends Response, C extends Record<string, unknown>>(
options?: SubmitOptions<C>,
e?: React.FormEvent<HTMLFormElement>,
) => Promise</**
* @experimental - Note: the `{ res: ... }` return type is experimental and may change in the future. Use at your own discretion.
* Returns the form state and the response from the server.
*
* @experimental - Note: the `{ res: ... }` return type is experimental and may change in the future. Use at your own discretion.
*/
{ formState?: FormState; res: T } | void>

View File

@@ -15,6 +15,7 @@ import { APIError, formatErrors } from 'payload'
import { isNumber } from 'payload/shared'
import { getClientConfig } from './getClientConfig.js'
import { getColumns } from './getColumns.js'
import { renderFilters, renderTable } from './renderTable.js'
import { upsertPreferences } from './upsertPreferences.js'
@@ -73,7 +74,7 @@ const buildTableState = async (
): Promise<BuildTableStateSuccessResult> => {
const {
collectionSlug,
columns,
columns: columnsFromArgs,
data: dataFromArgs,
enableRowSelections,
orderableFieldName,
@@ -148,7 +149,7 @@ const buildTableState = async (
: `collection-${collectionSlug}`,
req,
value: {
columns,
columns: columnsFromArgs,
limit: isNumber(query?.limit) ? Number(query.limit) : undefined,
sort: query?.sort as string,
},
@@ -229,7 +230,11 @@ const buildTableState = async (
clientConfig,
collectionConfig,
collections: Array.isArray(collectionSlug) ? collectionSlug : undefined,
columns,
columns: getColumns({
collectionConfig: clientCollectionConfig,
columns: columnsFromArgs,
i18n: req.i18n,
}),
data,
enableRowSelections,
i18n: req.i18n,

View File

@@ -0,0 +1,36 @@
import type { I18nClient } from '@payloadcms/translations'
import type { ClientCollectionConfig, ColumnPreference } from 'payload'
import { flattenTopLevelFields } from 'payload'
import { filterFields } from '../providers/TableColumns/buildColumnState/filterFields.js'
import { getInitialColumns } from '../providers/TableColumns/getInitialColumns.js'
export const getColumns = ({
collectionConfig,
columns,
i18n,
isPolymorphic,
}: {
collectionConfig?: ClientCollectionConfig
columns: ColumnPreference[]
i18n: I18nClient
isPolymorphic?: boolean
}) =>
columns
? columns?.filter((column) =>
flattenTopLevelFields(collectionConfig?.fields, {
i18n,
keepPresentationalFields: true,
moveSubFieldsToTop: true,
})?.some((field) => {
const accessor =
'accessor' in field ? field.accessor : 'name' in field ? field.name : undefined
return accessor === column.accessor
}),
)
: getInitialColumns(
isPolymorphic ? collectionConfig?.fields : filterFields(collectionConfig?.fields),
collectionConfig.admin?.useAsTitle,
isPolymorphic ? [] : collectionConfig?.admin?.defaultColumns,
)

View File

@@ -3,7 +3,6 @@ import type {
ClientConfig,
ClientField,
CollectionConfig,
CollectionPreferences,
Column,
ColumnPreference,
Field,
@@ -16,7 +15,7 @@ import type {
} from 'payload'
import { getTranslation, type I18nClient } from '@payloadcms/translations'
import { fieldAffectsData, fieldIsHiddenOrDisabled, flattenTopLevelFields } from 'payload/shared'
import { fieldAffectsData, fieldIsHiddenOrDisabled } from 'payload/shared'
import React from 'react'
import type { BuildColumnStateArgs } from '../providers/TableColumns/buildColumnState/index.js'
@@ -37,7 +36,6 @@ import {
} from '../exports/client/index.js'
import { filterFields } from '../providers/TableColumns/buildColumnState/filterFields.js'
import { buildColumnState } from '../providers/TableColumns/buildColumnState/index.js'
import { getInitialColumns } from '../providers/TableColumns/getInitialColumns.js'
export const renderFilters = (
fields: Field[],
@@ -69,7 +67,7 @@ export const renderTable = ({
clientConfig,
collectionConfig,
collections,
columns: columnsFromArgs,
columns,
customCellProps,
data,
enableRowSelections,
@@ -90,7 +88,7 @@ export const renderTable = ({
clientConfig?: ClientConfig
collectionConfig?: SanitizedCollectionConfig
collections?: string[]
columns?: CollectionPreferences['columns']
columns: ColumnPreference[]
customCellProps?: Record<string, unknown>
data?: PaginatedDocs | undefined
drawerSlug?: string
@@ -150,24 +148,6 @@ export const renderTable = ({
}
}
const columns: ColumnPreference[] = columnsFromArgs
? columnsFromArgs?.filter((column) =>
flattenTopLevelFields(clientFields, {
i18n,
keepPresentationalFields: true,
moveSubFieldsToTop: true,
})?.some((field) => {
const accessor =
'accessor' in field ? field.accessor : 'name' in field ? field.name : undefined
return accessor === column.accessor
}),
)
: getInitialColumns(
isPolymorphic ? clientFields : filterFields(clientFields),
useAsTitle,
isPolymorphic ? [] : clientCollectionConfig?.admin?.defaultColumns,
)
const sharedArgs: Pick<
BuildColumnStateArgs,
| 'clientFields'

View File

@@ -1,5 +1,7 @@
import type { CollectionConfig } from 'payload'
import { lexicalEditor } from '@payloadcms/richtext-lexical'
export const postsSlug = 'posts'
export const PostsCollection: CollectionConfig = {
@@ -13,14 +15,11 @@ export const PostsCollection: CollectionConfig = {
type: 'text',
},
{
name: 'array',
type: 'array',
fields: [
{
name: 'title',
type: 'text',
},
],
name: 'content',
type: 'richText',
editor: lexicalEditor({
features: ({ defaultFeatures }) => [...defaultFeatures],
}),
},
],
}

View File

@@ -126,12 +126,21 @@ export interface UserAuthOperations {
export interface Post {
id: string;
title?: string | null;
array?:
| {
title?: string | null;
id?: string | null;
}[]
| null;
content?: {
root: {
type: string;
children: {
type: string;
version: number;
[k: string]: unknown;
}[];
direction: ('ltr' | 'rtl') | null;
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
indent: number;
version: number;
};
[k: string]: unknown;
} | null;
updatedAt: string;
createdAt: string;
}
@@ -270,12 +279,7 @@ export interface PayloadMigration {
*/
export interface PostsSelect<T extends boolean = true> {
title?: T;
array?:
| T
| {
title?: T;
id?: T;
};
content?: T;
updatedAt?: T;
createdAt?: T;
}

View File

@@ -0,0 +1,9 @@
'use client'
import { useListQuery } from '@payloadcms/ui'
export const BeforeListTable = () => {
const { data } = useListQuery()
return <p id="table-state">{JSON.stringify(data?.docs || [])}</p>
}

View File

@@ -0,0 +1,25 @@
import type { CollectionConfig } from 'payload'
export const listViewSelectAPISlug = 'list-view-select-api'
export const ListViewSelectAPI: CollectionConfig = {
slug: listViewSelectAPISlug,
admin: {
enableListViewSelectAPI: true,
components: {
beforeListTable: [
'./collections/ListViewSelectAPI/BeforeListTable/index.tsx#BeforeListTable',
],
},
},
fields: [
{
name: 'title',
type: 'text',
},
{
name: 'description',
type: 'text',
},
],
}

View File

@@ -19,6 +19,7 @@ import { CollectionGroup2A } from './collections/Group2A.js'
import { CollectionGroup2B } from './collections/Group2B.js'
import { CollectionHidden } from './collections/Hidden.js'
import { ListDrawer } from './collections/ListDrawer.js'
import { ListViewSelectAPI } from './collections/ListViewSelectAPI/index.js'
import { CollectionNoApiView } from './collections/NoApiView.js'
import { CollectionNotInView } from './collections/NotInView.js'
import { Placeholder } from './collections/Placeholder.js'
@@ -188,6 +189,7 @@ export default buildConfigWithDefaults({
UseAsTitleGroupField,
DisableBulkEdit,
CustomListDrawer,
ListViewSelectAPI,
Virtuals,
],
globals: [

View File

@@ -34,8 +34,10 @@ const description = 'Description'
let payload: PayloadTestSDK<Config>
import { listViewSelectAPISlug } from 'admin/collections/ListViewSelectAPI/index.js'
import { devUser } from 'credentials.js'
import { addListFilter } from 'helpers/e2e/addListFilter.js'
import { assertNetworkRequests } from 'helpers/e2e/assertNetworkRequests.js'
import { goToNextPage, goToPreviousPage } from 'helpers/e2e/goToNextPage.js'
import { goToFirstCell } from 'helpers/e2e/navigateToDoc.js'
import { openListColumns } from 'helpers/e2e/openListColumns.js'
@@ -968,7 +970,7 @@ describe('List View', () => {
).toBeHidden()
})
test('should toggle columns and effect table', async () => {
test('should toggle columns and affect table', async () => {
const tableHeaders = 'table > thead > tr > th'
await openListColumns(page, {})
@@ -992,6 +994,60 @@ describe('List View', () => {
await toggleColumn(page, { columnLabel: 'ID', columnName: 'id', targetState: 'off' })
})
test('should use select API in the list view when `enableListViewSelectAPI` is true', async () => {
const doc = await payload.create({
collection: listViewSelectAPISlug,
data: {
title: 'This is a test title',
description: 'This is a test description',
},
})
const selectAPIUrl = new AdminUrlUtil(serverURL, listViewSelectAPISlug)
await page.goto(selectAPIUrl.list)
const printedResults = page.locator('#table-state')
await expect
.poll(
async () => {
const resultText = await printedResults.innerText()
const parsedResult = JSON.parse(resultText)
return Boolean(parsedResult[0].id && parsedResult[0].description)
},
{
timeout: 3000,
intervals: [100, 250, 500, 1000],
},
)
.toBeTruthy()
await toggleColumn(page, { columnLabel: 'ID', columnName: 'id', targetState: 'off' })
await toggleColumn(page, {
columnLabel: 'Description',
columnName: 'description',
targetState: 'off',
})
// Poll until the "description" field is removed from the response BUT `id` is still present
// The `id` field will remain selected despite it being inactive
await expect
.poll(
async () => {
const resultText = await printedResults.innerText()
const parsedResult = JSON.parse(resultText)
return Boolean(parsedResult[0].description === undefined && parsedResult[0].id)
},
{
timeout: 3000,
intervals: [100, 250, 500, 1000],
},
)
.toBeTruthy()
})
test('should toggle columns and save to preferences', async () => {
const tableHeaders = 'table > thead > tr > th'
const numberOfColumns = await page.locator(tableHeaders).count()

View File

@@ -94,6 +94,7 @@ export interface Config {
'use-as-title-group-field': UseAsTitleGroupField;
'disable-bulk-edit': DisableBulkEdit;
'custom-list-drawer': CustomListDrawer;
'list-view-select-api': ListViewSelectApi;
virtuals: Virtual;
'payload-locked-documents': PayloadLockedDocument;
'payload-preferences': PayloadPreference;
@@ -128,6 +129,7 @@ export interface Config {
'use-as-title-group-field': UseAsTitleGroupFieldSelect<false> | UseAsTitleGroupFieldSelect<true>;
'disable-bulk-edit': DisableBulkEditSelect<false> | DisableBulkEditSelect<true>;
'custom-list-drawer': CustomListDrawerSelect<false> | CustomListDrawerSelect<true>;
'list-view-select-api': ListViewSelectApiSelect<false> | ListViewSelectApiSelect<true>;
virtuals: VirtualsSelect<false> | VirtualsSelect<true>;
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
@@ -584,6 +586,17 @@ export interface CustomListDrawer {
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "list-view-select-api".
*/
export interface ListViewSelectApi {
id: string;
title?: string | null;
description?: string | null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "virtuals".
@@ -711,6 +724,10 @@ export interface PayloadLockedDocument {
relationTo: 'custom-list-drawer';
value: string | CustomListDrawer;
} | null)
| ({
relationTo: 'list-view-select-api';
value: string | ListViewSelectApi;
} | null)
| ({
relationTo: 'virtuals';
value: string | Virtual;
@@ -1129,6 +1146,16 @@ export interface CustomListDrawerSelect<T extends boolean = true> {
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "list-view-select-api_select".
*/
export interface ListViewSelectApiSelect<T extends boolean = true> {
title?: T;
description?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "virtuals_select".

View File

@@ -22,7 +22,12 @@ export const assertRequestBody = async <T>(
page: Page,
options: {
action: () => Promise<void> | void
expect?: (requestBody: T) => boolean | Promise<boolean>
expect?: (
requestBody: T,
requestHeaders: {
[key: string]: string
},
) => boolean | Promise<boolean>
requestMethod?: string
url: string
},
@@ -43,7 +48,7 @@ export const assertRequestBody = async <T>(
const parsedBody = JSON.parse(requestBody) as T
if (typeof options.expect === 'function') {
expect(await options.expect(parsedBody)).toBeTruthy()
expect(await options.expect(parsedBody, request.headers())).toBeTruthy()
}
return parsedBody