fix(ui): fixed many bugs in the WhereBuilder relationship select menu (#10553)
Following https://github.com/payloadcms/payload/pull/10551, I found and fixed a handful more bugs: - When writing to the input, the results that were already there were not cleaned, causing incorrect results to appear. - the scroll was causing an infinite loop that showed repeated elements - optimization: only the required field is selected (not required) - refs are set to the initial value to avoid a state where nothing can be searched
This commit is contained in:
@@ -76,18 +76,17 @@ export const RelationshipField: React.FC<Props> = (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> = (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> = (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)
|
||||
}
|
||||
|
||||
21
test/admin/collections/With300Documents.ts
Normal file
21
test/admin/collections/With300Documents.ts
Normal file
@@ -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,
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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<typeof getRoutes>
|
||||
@@ -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)
|
||||
|
||||
@@ -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<false> | GeoSelect<true>;
|
||||
'disable-duplicate': DisableDuplicateSelect<false> | DisableDuplicateSelect<true>;
|
||||
'base-list-filters': BaseListFiltersSelect<false> | BaseListFiltersSelect<true>;
|
||||
with300documents: With300DocumentsSelect<false> | With300DocumentsSelect<true>;
|
||||
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
||||
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
||||
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
||||
@@ -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<T extends boolean = true> {
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "with300documents_select".
|
||||
*/
|
||||
export interface With300DocumentsSelect<T extends boolean = true> {
|
||||
text?: T;
|
||||
selfRelation?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-locked-documents_select".
|
||||
|
||||
@@ -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<unknown>[] = 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) {
|
||||
|
||||
@@ -48,3 +48,4 @@ export const globalSlugs = [
|
||||
hiddenGlobalSlug,
|
||||
noApiViewGlobalSlug,
|
||||
]
|
||||
export const with300DocumentsSlug = 'with300documents'
|
||||
|
||||
Reference in New Issue
Block a user