From e7124f61764879c76d7a82cabbd74543c42d03e3 Mon Sep 17 00:00:00 2001 From: Patrik <35232443+PatrikKozak@users.noreply.github.com> Date: Tue, 29 Jul 2025 17:29:04 -0400 Subject: [PATCH] fix(next): cannot filter trash (#13320) ### What? - Updated `TrashView` to pass `trash: true` as a dedicated prop instead of embedding it in the `query` object. - Modified `renderListView` to correctly merge `trash` and `where` queries by using both `queryFromArgs` and `queryFromReq`. - Ensured filtering (via `where`) works correctly in the trash view. ### Why? Previously, the `trash: true` flag was injected into the `query` object, and `renderListView` only used `queryFromArgs`. This caused the `where` clause from filters (added by the `WhereBuilder`) to be overridden, breaking filtering in the trash view. ### How? - Introduced an explicit `trash` prop in `renderListView` arguments. - Updated `TrashView` to pass `trash: true` separately. - Updated `renderListView` to apply the `trash` filter in addition to any `where` conditions. --- docs/integrations/vercel-content-link.mdx | 8 +- docs/live-preview/overview.mdx | 2 +- .../next/src/views/CollectionTrash/index.tsx | 11 +- packages/next/src/views/List/index.tsx | 11 +- test/trash/e2e.spec.ts | 42 +++++++ tsconfig.base.json | 114 +++++------------- 6 files changed, 86 insertions(+), 102 deletions(-) diff --git a/docs/integrations/vercel-content-link.mdx b/docs/integrations/vercel-content-link.mdx index e66cff385..29356b4ec 100644 --- a/docs/integrations/vercel-content-link.mdx +++ b/docs/integrations/vercel-content-link.mdx @@ -34,13 +34,13 @@ npm i @payloadcms/plugin-csm Then in the `plugins` array of your Payload Config, call the plugin and enable any collections that require Content Source Maps. ```ts -import { buildConfig } from "payload/config" -import contentSourceMaps from "@payloadcms/plugin-csm" +import { buildConfig } from 'payload/config' +import contentSourceMaps from '@payloadcms/plugin-csm' const config = buildConfig({ collections: [ { - slug: "pages", + slug: 'pages', fields: [ { name: 'slug', @@ -55,7 +55,7 @@ const config = buildConfig({ ], plugins: [ contentSourceMaps({ - collections: ["pages"], + collections: ['pages'], }), ], }) diff --git a/docs/live-preview/overview.mdx b/docs/live-preview/overview.mdx index 5635b1180..9a86c5588 100644 --- a/docs/live-preview/overview.mdx +++ b/docs/live-preview/overview.mdx @@ -45,7 +45,7 @@ The following options are available: | Path | Description | | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | -| **`url`** | String, or function that returns a string, pointing to your front-end application. This value is used as the iframe `src`. [More details](#url). | +| **`url`** | String, or function that returns a string, pointing to your front-end application. This value is used as the iframe `src`. [More details](#url). | | **`breakpoints`** | Array of breakpoints to be used as “device sizes” in the preview window. Each item appears as an option in the toolbar. [More details](#breakpoints). | | **`collections`** | Array of collection slugs to enable Live Preview on. | | **`globals`** | Array of global slugs to enable Live Preview on. | diff --git a/packages/next/src/views/CollectionTrash/index.tsx b/packages/next/src/views/CollectionTrash/index.tsx index 7fed4d7b7..3501fbf83 100644 --- a/packages/next/src/views/CollectionTrash/index.tsx +++ b/packages/next/src/views/CollectionTrash/index.tsx @@ -19,17 +19,14 @@ type RenderTrashViewArgs = { redirectAfterRestore?: boolean } & AdminViewServerProps -export const TrashView: React.FC< - { query?: any } & Omit -> = async (args) => { +export const TrashView: React.FC> = async ( + args, +) => { try { const { List: TrashList } = await renderListView({ ...args, enableRowSelections: true, - query: { - ...(args.query || {}), - trash: true, // force trash view - }, + trash: true, viewType: 'trash', }) diff --git a/packages/next/src/views/List/index.tsx b/packages/next/src/views/List/index.tsx index a1f90beac..0f90a9b8f 100644 --- a/packages/next/src/views/List/index.tsx +++ b/packages/next/src/views/List/index.tsx @@ -40,6 +40,10 @@ type RenderListViewArgs = { query: ListQuery redirectAfterDelete?: boolean redirectAfterDuplicate?: boolean + /** + * @experimental This prop is subject to change in future releases. + */ + trash?: boolean } & AdminViewServerProps /** @@ -66,6 +70,7 @@ export const renderListView = async ( params, query: queryFromArgs, searchParams, + trash, viewType, } = args @@ -153,7 +158,7 @@ export const renderListView = async ( where: combineWhereConstraints([query?.where, baseListFilter]), }) - if (query?.trash === true) { + if (trash === true) { whereWithMergedSearch = { and: [ whereWithMergedSearch, @@ -217,7 +222,7 @@ export const renderListView = async ( enableRowSelections, query, req, - trash: query?.trash === true, + trash, user, where: whereWithMergedSearch, })) @@ -234,7 +239,7 @@ export const renderListView = async ( page: query?.page ? Number(query.page) : undefined, req, sort: query?.sort, - trash: query?.trash === true, + trash, user, where: whereWithMergedSearch, }) diff --git a/test/trash/e2e.spec.ts b/test/trash/e2e.spec.ts index 0e078686b..e9171d62f 100644 --- a/test/trash/e2e.spec.ts +++ b/test/trash/e2e.spec.ts @@ -1,6 +1,7 @@ import type { Page } from '@playwright/test' import { expect, test } from '@playwright/test' +import { addListFilter } from 'helpers/e2e/addListFilter.js' import * as path from 'path' import { mapAsync } from 'payload' import { fileURLToPath } from 'url' @@ -537,6 +538,47 @@ describe('Trash', () => { }) .toBe(0) }) + + test('Should properly filter trashed docs through where query builder', async () => { + const createdDocs: Post[] = [] + + // Create 2 "Test Post" docs + await mapAsync([...Array(2)], async (item, index) => { + const doc = await createTrashedPostDoc({ + title: `Test Post ${index + 1}`, + }) + createdDocs.push(doc) + }) + + // Create 2 "Some Post" docs + await mapAsync([...Array(2)], async (item, index) => { + const doc = await createTrashedPostDoc({ + title: `Some Post ${index + 1}`, + }) + createdDocs.push(doc) + }) + + await page.goto(postsUrl.trash) + + await addListFilter({ + page, + fieldLabel: 'Title', + operatorLabel: 'is like', + value: 'Test', + }) + + await expect(page.locator('.cell-title', { hasText: 'Test Post' })).toHaveCount(2) + await expect(page.locator('.cell-title', { hasText: 'Some Post' })).toHaveCount(0) + + // Cleanup: permanently delete the created docs + await mapAsync(createdDocs, async (doc) => { + await payload.delete({ + collection: postsSlug, + id: doc.id, + trash: true, // Force permanent delete + }) + }) + }) }) describe('Edit view', () => { diff --git a/tsconfig.base.json b/tsconfig.base.json index ab3910098..0898ad390 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -21,15 +21,8 @@ "skipLibCheck": true, "emitDeclarationOnly": true, "sourceMap": true, - "lib": [ - "DOM", - "DOM.Iterable", - "ES2022" - ], - "types": [ - "node", - "jest" - ], + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "types": ["node", "jest"], "incremental": true, "isolatedModules": true, "plugins": [ @@ -38,72 +31,36 @@ } ], "paths": { - "@payload-config": [ - "./test/joins/config.ts" - ], - "@payloadcms/admin-bar": [ - "./packages/admin-bar/src" - ], - "@payloadcms/live-preview": [ - "./packages/live-preview/src" - ], - "@payloadcms/live-preview-react": [ - "./packages/live-preview-react/src/index.ts" - ], - "@payloadcms/live-preview-vue": [ - "./packages/live-preview-vue/src/index.ts" - ], - "@payloadcms/ui": [ - "./packages/ui/src/exports/client/index.ts" - ], - "@payloadcms/ui/shared": [ - "./packages/ui/src/exports/shared/index.ts" - ], - "@payloadcms/ui/rsc": [ - "./packages/ui/src/exports/rsc/index.ts" - ], - "@payloadcms/ui/scss": [ - "./packages/ui/src/scss.scss" - ], - "@payloadcms/ui/scss/app.scss": [ - "./packages/ui/src/scss/app.scss" - ], - "@payloadcms/next/*": [ - "./packages/next/src/exports/*.ts" - ], + "@payload-config": ["./test/_community/config.ts"], + "@payloadcms/admin-bar": ["./packages/admin-bar/src"], + "@payloadcms/live-preview": ["./packages/live-preview/src"], + "@payloadcms/live-preview-react": ["./packages/live-preview-react/src/index.ts"], + "@payloadcms/live-preview-vue": ["./packages/live-preview-vue/src/index.ts"], + "@payloadcms/ui": ["./packages/ui/src/exports/client/index.ts"], + "@payloadcms/ui/shared": ["./packages/ui/src/exports/shared/index.ts"], + "@payloadcms/ui/rsc": ["./packages/ui/src/exports/rsc/index.ts"], + "@payloadcms/ui/scss": ["./packages/ui/src/scss.scss"], + "@payloadcms/ui/scss/app.scss": ["./packages/ui/src/scss/app.scss"], + "@payloadcms/next/*": ["./packages/next/src/exports/*.ts"], "@payloadcms/richtext-lexical/client": [ "./packages/richtext-lexical/src/exports/client/index.ts" ], - "@payloadcms/richtext-lexical/rsc": [ - "./packages/richtext-lexical/src/exports/server/rsc.ts" - ], - "@payloadcms/richtext-slate/rsc": [ - "./packages/richtext-slate/src/exports/server/rsc.ts" - ], + "@payloadcms/richtext-lexical/rsc": ["./packages/richtext-lexical/src/exports/server/rsc.ts"], + "@payloadcms/richtext-slate/rsc": ["./packages/richtext-slate/src/exports/server/rsc.ts"], "@payloadcms/richtext-slate/client": [ "./packages/richtext-slate/src/exports/client/index.ts" ], - "@payloadcms/plugin-seo/client": [ - "./packages/plugin-seo/src/exports/client.ts" - ], - "@payloadcms/plugin-sentry/client": [ - "./packages/plugin-sentry/src/exports/client.ts" - ], - "@payloadcms/plugin-stripe/client": [ - "./packages/plugin-stripe/src/exports/client.ts" - ], - "@payloadcms/plugin-search/client": [ - "./packages/plugin-search/src/exports/client.ts" - ], + "@payloadcms/plugin-seo/client": ["./packages/plugin-seo/src/exports/client.ts"], + "@payloadcms/plugin-sentry/client": ["./packages/plugin-sentry/src/exports/client.ts"], + "@payloadcms/plugin-stripe/client": ["./packages/plugin-stripe/src/exports/client.ts"], + "@payloadcms/plugin-search/client": ["./packages/plugin-search/src/exports/client.ts"], "@payloadcms/plugin-form-builder/client": [ "./packages/plugin-form-builder/src/exports/client.ts" ], "@payloadcms/plugin-import-export/rsc": [ "./packages/plugin-import-export/src/exports/rsc.ts" ], - "@payloadcms/plugin-multi-tenant/rsc": [ - "./packages/plugin-multi-tenant/src/exports/rsc.ts" - ], + "@payloadcms/plugin-multi-tenant/rsc": ["./packages/plugin-multi-tenant/src/exports/rsc.ts"], "@payloadcms/plugin-multi-tenant/utilities": [ "./packages/plugin-multi-tenant/src/exports/utilities.ts" ], @@ -113,42 +70,25 @@ "@payloadcms/plugin-multi-tenant/client": [ "./packages/plugin-multi-tenant/src/exports/client.ts" ], - "@payloadcms/plugin-multi-tenant": [ - "./packages/plugin-multi-tenant/src/index.ts" - ], + "@payloadcms/plugin-multi-tenant": ["./packages/plugin-multi-tenant/src/index.ts"], "@payloadcms/plugin-multi-tenant/translations/languages/all": [ "./packages/plugin-multi-tenant/src/translations/index.ts" ], "@payloadcms/plugin-multi-tenant/translations/languages/*": [ "./packages/plugin-multi-tenant/src/translations/languages/*.ts" ], - "@payloadcms/next": [ - "./packages/next/src/exports/*" - ], - "@payloadcms/storage-azure/client": [ - "./packages/storage-azure/src/exports/client.ts" - ], - "@payloadcms/storage-s3/client": [ - "./packages/storage-s3/src/exports/client.ts" - ], + "@payloadcms/next": ["./packages/next/src/exports/*"], + "@payloadcms/storage-azure/client": ["./packages/storage-azure/src/exports/client.ts"], + "@payloadcms/storage-s3/client": ["./packages/storage-s3/src/exports/client.ts"], "@payloadcms/storage-vercel-blob/client": [ "./packages/storage-vercel-blob/src/exports/client.ts" ], - "@payloadcms/storage-gcs/client": [ - "./packages/storage-gcs/src/exports/client.ts" - ], + "@payloadcms/storage-gcs/client": ["./packages/storage-gcs/src/exports/client.ts"], "@payloadcms/storage-uploadthing/client": [ "./packages/storage-uploadthing/src/exports/client.ts" ] } }, - "include": [ - "${configDir}/src" - ], - "exclude": [ - "${configDir}/dist", - "${configDir}/build", - "${configDir}/temp", - "**/*.spec.ts" - ] + "include": ["${configDir}/src"], + "exclude": ["${configDir}/dist", "${configDir}/build", "${configDir}/temp", "**/*.spec.ts"] }