diff --git a/packages/ui/src/elements/WhereBuilder/Condition/Relationship/index.tsx b/packages/ui/src/elements/WhereBuilder/Condition/Relationship/index.tsx index 575a665f3..0cb2c21a0 100644 --- a/packages/ui/src/elements/WhereBuilder/Condition/Relationship/index.tsx +++ b/packages/ui/src/elements/WhereBuilder/Condition/Relationship/index.tsx @@ -76,18 +76,17 @@ export const RelationshipField: React.FC = (props) => { const fieldToSearch = collection?.admin?.useAsTitle || 'id' const pageIndex = nextPageByRelationshipRef.current.get(relationSlug) - const query: { - depth?: number - limit?: number - page?: number - where: Where - } = { + const where: Where = { + and: [], + } + const query = { depth: 0, limit: maxResultsPerRequest, page: pageIndex, - where: { - and: [], + select: { + [fieldToSearch]: true, }, + where, } if (debouncedSearch) { @@ -115,15 +114,13 @@ export const RelationshipField: React.FC = (props) => { if (data.docs.length > 0) { addOptions(data, relationSlug) - if (!debouncedSearch) { - if (data.nextPage) { - nextPageByRelationshipRef.current.set(relationSlug, data.nextPage) - } else { - partiallyLoadedRelationshipSlugs.current = - partiallyLoadedRelationshipSlugs.current.filter( - (partiallyLoadedRelation) => partiallyLoadedRelation !== relationSlug, - ) - } + if (data.nextPage) { + nextPageByRelationshipRef.current.set(relationSlug, data.nextPage) + } else { + partiallyLoadedRelationshipSlugs.current = + partiallyLoadedRelationshipSlugs.current.filter( + (partiallyLoadedRelation) => partiallyLoadedRelation !== relationSlug, + ) } } } else { @@ -209,7 +206,9 @@ export const RelationshipField: React.FC = (props) => { }, [hasMany, hasMultipleRelations, value, options]) const handleInputChange = (input: string) => { + dispatchOptions({ type: 'CLEAR', i18n, required: false }) const relationSlug = partiallyLoadedRelationshipSlugs.current[0] + partiallyLoadedRelationshipSlugs.current = relationSlugs nextPageByRelationshipRef.current.set(relationSlug, 1) setSearch(input) } diff --git a/test/admin/collections/With300Documents.ts b/test/admin/collections/With300Documents.ts new file mode 100644 index 000000000..57e14d7a6 --- /dev/null +++ b/test/admin/collections/With300Documents.ts @@ -0,0 +1,21 @@ +import type { CollectionConfig } from 'payload' + +import { with300DocumentsSlug } from '../slugs.js' + +export const with300Documents: CollectionConfig = { + slug: with300DocumentsSlug, + admin: { + useAsTitle: 'text', + }, + fields: [ + { + name: 'text', + type: 'text', + }, + { + name: 'selfRelation', + type: 'relationship', + relationTo: with300DocumentsSlug, + }, + ], +} diff --git a/test/admin/config.ts b/test/admin/config.ts index b08a1060f..4c9ba3a32 100644 --- a/test/admin/config.ts +++ b/test/admin/config.ts @@ -19,6 +19,7 @@ import { CollectionNotInView } from './collections/NotInView.js' import { Posts } from './collections/Posts.js' import { UploadCollection } from './collections/Upload.js' import { Users } from './collections/Users.js' +import { with300Documents } from './collections/With300Documents.js' import { CustomGlobalViews1 } from './globals/CustomViews1.js' import { CustomGlobalViews2 } from './globals/CustomViews2.js' import { Global } from './globals/Global.js' @@ -155,6 +156,7 @@ export default buildConfigWithDefaults({ Geo, DisableDuplicate, BaseListFilter, + with300Documents, ], globals: [ GlobalHidden, diff --git a/test/admin/e2e/list-view/e2e.spec.ts b/test/admin/e2e/list-view/e2e.spec.ts index d7c5ed362..30bd22906 100644 --- a/test/admin/e2e/list-view/e2e.spec.ts +++ b/test/admin/e2e/list-view/e2e.spec.ts @@ -16,7 +16,12 @@ import { import { AdminUrlUtil } from '../../../helpers/adminUrlUtil.js' import { initPayloadE2ENoConfig } from '../../../helpers/initPayloadE2ENoConfig.js' import { customAdminRoutes } from '../../shared.js' -import { customViews1CollectionSlug, geoCollectionSlug, postsCollectionSlug } from '../../slugs.js' +import { + customViews1CollectionSlug, + geoCollectionSlug, + postsCollectionSlug, + with300DocumentsSlug, +} from '../../slugs.js' const { beforeAll, beforeEach, describe } = test @@ -48,6 +53,7 @@ describe('List View', () => { let postsUrl: AdminUrlUtil let baseListFiltersUrl: AdminUrlUtil let customViewsUrl: AdminUrlUtil + let with300DocumentsUrl: AdminUrlUtil let serverURL: string let adminRoutes: ReturnType @@ -65,6 +71,7 @@ describe('List View', () => { geoUrl = new AdminUrlUtil(serverURL, geoCollectionSlug) postsUrl = new AdminUrlUtil(serverURL, postsCollectionSlug) + with300DocumentsUrl = new AdminUrlUtil(serverURL, with300DocumentsSlug) baseListFiltersUrl = new AdminUrlUtil(serverURL, 'base-list-filters') customViewsUrl = new AdminUrlUtil(serverURL, customViews1CollectionSlug) @@ -608,6 +615,35 @@ describe('List View', () => { }) }) + describe('WhereBuilder', () => { + test('should render where builder', async () => { + await page.goto( + `${with300DocumentsUrl.list}?limit=10&page=1&where%5Bor%5D%5B0%5D%5Band%5D%5B0%5D%5BselfRelation%5D%5Bequals%5D=null`, + ) + const valueField = page.locator('.condition__value') + await valueField.click() + await page.keyboard.type('4') + const options = page.getByRole('option') + expect(options).toHaveCount(10) + for (const option of await options.all()) { + expect(option).toHaveText('4') + } + await page.keyboard.press('Backspace') + await page.keyboard.type('5') + expect(options).toHaveCount(10) + for (const option of await options.all()) { + expect(option).toHaveText('5') + } + // await options.last().scrollIntoViewIfNeeded() + await options.first().hover() + // three times because react-select is not very reliable + await page.mouse.wheel(0, 50) + await page.mouse.wheel(0, 50) + await page.mouse.wheel(0, 50) + expect(options).toHaveCount(20) + }) + }) + describe('table columns', () => { test('should hide field column when field.hidden is true', async () => { await page.goto(postsUrl.list) diff --git a/test/admin/payload-types.ts b/test/admin/payload-types.ts index 4c322d9c8..448fcf550 100644 --- a/test/admin/payload-types.ts +++ b/test/admin/payload-types.ts @@ -27,6 +27,7 @@ export interface Config { geo: Geo; 'disable-duplicate': DisableDuplicate; 'base-list-filters': BaseListFilter; + with300documents: With300Document; 'payload-locked-documents': PayloadLockedDocument; 'payload-preferences': PayloadPreference; 'payload-migrations': PayloadMigration; @@ -49,6 +50,7 @@ export interface Config { geo: GeoSelect | GeoSelect; 'disable-duplicate': DisableDuplicateSelect | DisableDuplicateSelect; 'base-list-filters': BaseListFiltersSelect | BaseListFiltersSelect; + with300documents: With300DocumentsSelect | With300DocumentsSelect; 'payload-locked-documents': PayloadLockedDocumentsSelect | PayloadLockedDocumentsSelect; 'payload-preferences': PayloadPreferencesSelect | PayloadPreferencesSelect; 'payload-migrations': PayloadMigrationsSelect | PayloadMigrationsSelect; @@ -374,6 +376,17 @@ export interface BaseListFilter { updatedAt: string; createdAt: string; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "with300documents". + */ +export interface With300Document { + id: string; + text?: string | null; + selfRelation?: (string | null) | With300Document; + updatedAt: string; + createdAt: string; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-locked-documents". @@ -444,6 +457,10 @@ export interface PayloadLockedDocument { | ({ relationTo: 'base-list-filters'; value: string | BaseListFilter; + } | null) + | ({ + relationTo: 'with300documents'; + value: string | With300Document; } | null); globalSlug?: string | null; user: { @@ -734,6 +751,16 @@ export interface BaseListFiltersSelect { updatedAt?: T; createdAt?: T; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "with300documents_select". + */ +export interface With300DocumentsSelect { + text?: T; + selfRelation?: T; + updatedAt?: T; + createdAt?: T; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-locked-documents_select". diff --git a/test/admin/seed.ts b/test/admin/seed.ts index 3dbe06262..2bbe5f17c 100644 --- a/test/admin/seed.ts +++ b/test/admin/seed.ts @@ -11,6 +11,7 @@ import { noApiViewCollectionSlug, postsCollectionSlug, usersCollectionSlug, + with300DocumentsSlug, } from './slugs.js' export const seed = async (_payload) => { @@ -117,6 +118,26 @@ export const seed = async (_payload) => { ], false, ) + + // delete all with300Documents + await _payload.delete({ + collection: with300DocumentsSlug, + where: {}, + }) + + // Create 300 documents of with300Documents + const manyDocumentsPromises: Promise[] = Array.from({ length: 300 }, (_, i) => { + const index = (i + 1).toString().padStart(3, '0') + return _payload.create({ + collection: with300DocumentsSlug, + data: { + id: index, + text: `document ${index}`, + }, + }) + }) + + await Promise.all([...manyDocumentsPromises]) } export async function clearAndSeedEverything(_payload: Payload) { diff --git a/test/admin/slugs.ts b/test/admin/slugs.ts index d25fa1ca8..2370222b2 100644 --- a/test/admin/slugs.ts +++ b/test/admin/slugs.ts @@ -48,3 +48,4 @@ export const globalSlugs = [ hiddenGlobalSlug, noApiViewGlobalSlug, ] +export const with300DocumentsSlug = 'with300documents'