feat: prevent querying relationship when filterOptions returns false (#4392)
fix: hidden collections showing in lexical and slate relationships feat: prevent querying relationship when filterOptions returns false fix: hidden collections appear in richtext internal link options Co-authored-by: Alessio Gravili <70709113+AlessioGr@users.noreply.github.com>
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
export const slug = 'fields-relationship'
|
||||
|
||||
export const relationOneSlug = 'relation-one'
|
||||
export const relationTrueFilterOptionSlug = 'relation-filter-true'
|
||||
export const relationFalseFilterOptionSlug = 'relation-filter-false'
|
||||
export const relationTwoSlug = 'relation-two'
|
||||
export const relationRestrictedSlug = 'relation-restricted'
|
||||
export const relationWithTitleSlug = 'relation-with-title'
|
||||
|
||||
@@ -8,8 +8,10 @@ import { PrePopulateFieldUI } from './PrePopulateFieldUI'
|
||||
import {
|
||||
collection1Slug,
|
||||
collection2Slug,
|
||||
relationFalseFilterOptionSlug,
|
||||
relationOneSlug,
|
||||
relationRestrictedSlug,
|
||||
relationTrueFilterOptionSlug,
|
||||
relationTwoSlug,
|
||||
relationUpdatedExternallySlug,
|
||||
relationWithTitleSlug,
|
||||
@@ -32,6 +34,7 @@ export interface RelationOne {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
|
||||
export type RelationTwo = RelationOne
|
||||
export type RelationRestricted = RelationOne
|
||||
export type RelationWithTitle = RelationOne
|
||||
@@ -46,7 +49,6 @@ const baseRelationshipFields: CollectionConfig['fields'] = [
|
||||
export default buildConfigWithDefaults({
|
||||
collections: [
|
||||
{
|
||||
slug,
|
||||
admin: {
|
||||
defaultColumns: [
|
||||
'id',
|
||||
@@ -58,41 +60,39 @@ export default buildConfigWithDefaults({
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'relationship',
|
||||
name: 'relationship',
|
||||
relationTo: relationOneSlug,
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
name: 'relationshipHasMany',
|
||||
relationTo: relationOneSlug,
|
||||
hasMany: true,
|
||||
relationTo: relationOneSlug,
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
name: 'relationshipMultiple',
|
||||
relationTo: [relationOneSlug, relationTwoSlug],
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
name: 'relationshipHasManyMultiple',
|
||||
hasMany: true,
|
||||
relationTo: [relationOneSlug, relationTwoSlug],
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
name: 'relationshipRestricted',
|
||||
relationTo: relationRestrictedSlug,
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
name: 'relationshipWithTitle',
|
||||
relationTo: relationWithTitleSlug,
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
name: 'relationshipFiltered',
|
||||
relationTo: relationOneSlug,
|
||||
filterOptions: (args: FilterOptionsProps<FieldsRelationship>) => {
|
||||
return {
|
||||
id: {
|
||||
@@ -100,11 +100,11 @@ export default buildConfigWithDefaults({
|
||||
},
|
||||
}
|
||||
},
|
||||
relationTo: relationOneSlug,
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
name: 'relationshipFilteredAsync',
|
||||
relationTo: relationOneSlug,
|
||||
filterOptions: async (args: FilterOptionsProps<FieldsRelationship>) => {
|
||||
return {
|
||||
id: {
|
||||
@@ -112,57 +112,84 @@ export default buildConfigWithDefaults({
|
||||
},
|
||||
}
|
||||
},
|
||||
relationTo: relationOneSlug,
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
name: 'relationshipManyFiltered',
|
||||
relationTo: [relationWithTitleSlug, relationOneSlug],
|
||||
hasMany: true,
|
||||
filterOptions: ({ relationTo, siblingData }: any) => {
|
||||
if (relationTo === relationOneSlug) {
|
||||
return { name: { equals: 'include' } }
|
||||
}
|
||||
if (relationTo === relationTrueFilterOptionSlug) {
|
||||
return true
|
||||
}
|
||||
if (relationTo === relationFalseFilterOptionSlug) {
|
||||
return false
|
||||
}
|
||||
if (siblingData.filter) {
|
||||
return { name: { contains: siblingData.filter } }
|
||||
}
|
||||
return { and: [] }
|
||||
},
|
||||
hasMany: true,
|
||||
relationTo: [
|
||||
relationWithTitleSlug,
|
||||
relationFalseFilterOptionSlug,
|
||||
relationTrueFilterOptionSlug,
|
||||
relationOneSlug,
|
||||
],
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'filter',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'relationshipReadOnly',
|
||||
type: 'relationship',
|
||||
relationTo: relationOneSlug,
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
relationTo: relationOneSlug,
|
||||
type: 'relationship',
|
||||
},
|
||||
],
|
||||
slug,
|
||||
},
|
||||
{
|
||||
slug: relationOneSlug,
|
||||
fields: baseRelationshipFields,
|
||||
},
|
||||
{
|
||||
slug: relationTwoSlug,
|
||||
fields: baseRelationshipFields,
|
||||
},
|
||||
{
|
||||
slug: relationRestrictedSlug,
|
||||
admin: {
|
||||
useAsTitle: 'name',
|
||||
},
|
||||
fields: baseRelationshipFields,
|
||||
access: {
|
||||
read: () => false,
|
||||
create: () => false,
|
||||
},
|
||||
slug: relationFalseFilterOptionSlug,
|
||||
},
|
||||
{
|
||||
admin: {
|
||||
useAsTitle: 'name',
|
||||
},
|
||||
fields: baseRelationshipFields,
|
||||
slug: relationTrueFilterOptionSlug,
|
||||
},
|
||||
{
|
||||
fields: baseRelationshipFields,
|
||||
slug: relationOneSlug,
|
||||
},
|
||||
{
|
||||
fields: baseRelationshipFields,
|
||||
slug: relationTwoSlug,
|
||||
},
|
||||
{
|
||||
access: {
|
||||
create: () => false,
|
||||
read: () => false,
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: 'name',
|
||||
},
|
||||
fields: baseRelationshipFields,
|
||||
slug: relationRestrictedSlug,
|
||||
},
|
||||
{
|
||||
slug: relationWithTitleSlug,
|
||||
admin: {
|
||||
useAsTitle: 'meta.title',
|
||||
},
|
||||
@@ -170,7 +197,6 @@ export default buildConfigWithDefaults({
|
||||
...baseRelationshipFields,
|
||||
{
|
||||
name: 'meta',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
@@ -178,110 +204,112 @@ export default buildConfigWithDefaults({
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
type: 'group',
|
||||
},
|
||||
],
|
||||
slug: relationWithTitleSlug,
|
||||
},
|
||||
{
|
||||
slug: relationUpdatedExternallySlug,
|
||||
admin: {
|
||||
useAsTitle: 'name',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'relationPrePopulate',
|
||||
type: 'relationship',
|
||||
relationTo: collection1Slug,
|
||||
admin: {
|
||||
width: '75%',
|
||||
},
|
||||
relationTo: collection1Slug,
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
type: 'ui',
|
||||
name: 'prePopulate',
|
||||
admin: {
|
||||
width: '25%',
|
||||
components: {
|
||||
Field: () => PrePopulateFieldUI({ path: 'relationPrePopulate', hasMany: false }),
|
||||
Field: () => PrePopulateFieldUI({ hasMany: false, path: 'relationPrePopulate' }),
|
||||
},
|
||||
width: '25%',
|
||||
},
|
||||
type: 'ui',
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'relationHasMany',
|
||||
type: 'relationship',
|
||||
relationTo: collection1Slug,
|
||||
hasMany: true,
|
||||
admin: {
|
||||
width: '75%',
|
||||
},
|
||||
hasMany: true,
|
||||
relationTo: collection1Slug,
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
type: 'ui',
|
||||
name: 'prePopulateRelationHasMany',
|
||||
admin: {
|
||||
width: '25%',
|
||||
components: {
|
||||
Field: () =>
|
||||
PrePopulateFieldUI({ path: 'relationHasMany', hasMultipleRelations: false }),
|
||||
PrePopulateFieldUI({ hasMultipleRelations: false, path: 'relationHasMany' }),
|
||||
},
|
||||
width: '25%',
|
||||
},
|
||||
type: 'ui',
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'relationToManyHasMany',
|
||||
type: 'relationship',
|
||||
relationTo: [collection1Slug, collection2Slug],
|
||||
hasMany: true,
|
||||
admin: {
|
||||
width: '75%',
|
||||
},
|
||||
hasMany: true,
|
||||
relationTo: [collection1Slug, collection2Slug],
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
type: 'ui',
|
||||
name: 'prePopulateToMany',
|
||||
admin: {
|
||||
width: '25%',
|
||||
components: {
|
||||
Field: () =>
|
||||
PrePopulateFieldUI({
|
||||
path: 'relationToManyHasMany',
|
||||
hasMultipleRelations: true,
|
||||
path: 'relationToManyHasMany',
|
||||
}),
|
||||
},
|
||||
width: '25%',
|
||||
},
|
||||
type: 'ui',
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
],
|
||||
slug: relationUpdatedExternallySlug,
|
||||
},
|
||||
{
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
slug: collection1Slug,
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: collection2Slug,
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
slug: collection2Slug,
|
||||
},
|
||||
],
|
||||
onInit: async (payload) => {
|
||||
@@ -358,11 +386,11 @@ export default buildConfigWithDefaults({
|
||||
collection: slug,
|
||||
data: {
|
||||
relationship: relationOneDocId,
|
||||
relationshipRestricted: restrictedDocId,
|
||||
relationshipHasManyMultiple: relationOneIDs.map((id) => ({
|
||||
relationTo: relationOneSlug,
|
||||
value: id,
|
||||
})),
|
||||
relationshipRestricted: restrictedDocId,
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -374,10 +402,10 @@ export default buildConfigWithDefaults({
|
||||
collection: slug,
|
||||
data: {
|
||||
relationship: relationOneDocId,
|
||||
relationshipRestricted: restrictedDocId,
|
||||
relationshipHasMany: [relationOneID],
|
||||
relationshipHasManyMultiple: [{ relationTo: relationTwoSlug, value: relationTwoID }],
|
||||
relationshipReadOnly: relationOneID,
|
||||
relationshipRestricted: restrictedDocId,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -17,8 +17,10 @@ import { initPageConsoleErrorCatch, openDocControls, saveDocAndAssert } from '..
|
||||
import { AdminUrlUtil } from '../helpers/adminUrlUtil'
|
||||
import { initPayloadE2E } from '../helpers/configHelpers'
|
||||
import {
|
||||
relationFalseFilterOptionSlug,
|
||||
relationOneSlug,
|
||||
relationRestrictedSlug,
|
||||
relationTrueFilterOptionSlug,
|
||||
relationTwoSlug,
|
||||
relationUpdatedExternallySlug,
|
||||
relationWithTitleSlug,
|
||||
@@ -112,9 +114,9 @@ describe('fields - relationship', () => {
|
||||
data: {
|
||||
name: 'with-existing-relations',
|
||||
relationship: relationOneDoc.id,
|
||||
relationshipReadOnly: relationOneDoc.id,
|
||||
relationshipRestricted: restrictedRelation.id,
|
||||
relationshipWithTitle: relationWithTitle.id,
|
||||
relationshipReadOnly: relationOneDoc.id,
|
||||
},
|
||||
})) as any
|
||||
})
|
||||
@@ -322,6 +324,41 @@ describe('fields - relationship', () => {
|
||||
await expect(options).not.toContainText('exclude')
|
||||
})
|
||||
|
||||
test('should not query for a relationship when filterOptions returns false', async () => {
|
||||
await payload.create({
|
||||
collection: relationFalseFilterOptionSlug,
|
||||
data: {
|
||||
name: 'whatever',
|
||||
},
|
||||
})
|
||||
|
||||
await page.goto(url.create)
|
||||
|
||||
// select relationshipMany field that relies on siblingData field above
|
||||
await page.locator('#field-relationshipManyFiltered .rs__control').click()
|
||||
|
||||
const options = page.locator('#field-relationshipManyFiltered .rs__menu')
|
||||
await expect(options).toContainText('Relation With Titles')
|
||||
await expect(options).not.toContainText('whatever')
|
||||
})
|
||||
|
||||
test('should show a relationship when filterOptions returns true', async () => {
|
||||
await payload.create({
|
||||
collection: relationTrueFilterOptionSlug,
|
||||
data: {
|
||||
name: 'truth',
|
||||
},
|
||||
})
|
||||
|
||||
await page.goto(url.create)
|
||||
|
||||
// select relationshipMany field that relies on siblingData field above
|
||||
await page.locator('#field-relationshipManyFiltered .rs__control').click()
|
||||
|
||||
const options = page.locator('#field-relationshipManyFiltered .rs__menu')
|
||||
await expect(options).toContainText('truth')
|
||||
})
|
||||
|
||||
test('should open document drawer from read-only relationships', async () => {
|
||||
await page.goto(url.edit(docWithExistingRelations.id))
|
||||
|
||||
@@ -492,6 +529,6 @@ async function clearCollectionDocs(collectionSlug: string): Promise<void> {
|
||||
(doc) => doc.id,
|
||||
)
|
||||
await mapAsync(ids, async (id) => {
|
||||
await payload.delete({ collection: collectionSlug, id })
|
||||
await payload.delete({ id, collection: collectionSlug })
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user