fix: disallow duplicate fieldNames to be used on the same level in the config (#4381)

This commit is contained in:
Jarrod Flesch
2023-12-11 16:52:24 -05:00
committed by GitHub
parent 548e78c598
commit a1d66b83e0
14 changed files with 758 additions and 279 deletions

View File

@@ -0,0 +1,11 @@
import APIError from './APIError'
class DuplicateFieldName extends APIError {
constructor(fieldName: string) {
super(
`A field with the name '${fieldName}' was found multiple times on the same level. Field names must be unique.`,
)
}
}
export default DuplicateFieldName

View File

@@ -2,6 +2,7 @@ export { default as errorHandler } from '../express/middleware/errorHandler'
export { default as APIError } from './APIError' export { default as APIError } from './APIError'
export { default as AuthenticationError } from './AuthenticationError' export { default as AuthenticationError } from './AuthenticationError'
export { default as DuplicateCollection } from './DuplicateCollection' export { default as DuplicateCollection } from './DuplicateCollection'
export { default as DuplicateFieldName } from './DuplicateFieldName'
export { default as DuplicateGlobal } from './DuplicateGlobal' export { default as DuplicateGlobal } from './DuplicateGlobal'
export { default as ErrorDeletingFile } from './ErrorDeletingFile' export { default as ErrorDeletingFile } from './ErrorDeletingFile'
export { default as FileUploadError } from './FileUploadError' export { default as FileUploadError } from './FileUploadError'

View File

@@ -2,7 +2,12 @@ import type { Config } from '../../config/types'
import type { Field } from './types' import type { Field } from './types'
import withCondition from '../../admin/components/forms/withCondition' import withCondition from '../../admin/components/forms/withCondition'
import { InvalidFieldName, InvalidFieldRelationship, MissingFieldType } from '../../errors' import {
DuplicateFieldName,
InvalidFieldName,
InvalidFieldRelationship,
MissingFieldType,
} from '../../errors'
import { formatLabels, toWords } from '../../utilities/formatLabels' import { formatLabels, toWords } from '../../utilities/formatLabels'
import { baseBlockFields } from '../baseFields/baseBlockFields' import { baseBlockFields } from '../baseFields/baseBlockFields'
import { baseIDField } from '../baseFields/baseIDField' import { baseIDField } from '../baseFields/baseIDField'
@@ -11,6 +16,7 @@ import { fieldAffectsData, tabHasName } from './types'
type Args = { type Args = {
config: Config config: Config
existingFieldNames?: Set<string>
fields: Field[] fields: Field[]
/** /**
* If not null, will validate that upload and relationship fields do not relate to a collection that is not in this array. * If not null, will validate that upload and relationship fields do not relate to a collection that is not in this array.
@@ -19,7 +25,12 @@ type Args = {
validRelationships: null | string[] validRelationships: null | string[]
} }
export const sanitizeFields = ({ config, fields, validRelationships }: Args): Field[] => { export const sanitizeFields = ({
config,
existingFieldNames = new Set(),
fields,
validRelationships,
}: Args): Field[] => {
if (!fields) return [] if (!fields) return []
return fields.map((unsanitizedField) => { return fields.map((unsanitizedField) => {
@@ -100,6 +111,12 @@ export const sanitizeFields = ({ config, fields, validRelationships }: Args): Fi
} }
if (fieldAffectsData(field)) { if (fieldAffectsData(field)) {
if (existingFieldNames.has(field.name)) {
throw new DuplicateFieldName(field.name)
} else if (!['id', 'blockName'].includes(field.name)) {
existingFieldNames.add(field.name)
}
if (field.localized && !config.localization) delete field.localized if (field.localized && !config.localization) delete field.localized
if (typeof field.validate === 'undefined') { if (typeof field.validate === 'undefined') {
@@ -126,6 +143,7 @@ export const sanitizeFields = ({ config, fields, validRelationships }: Args): Fi
if ('fields' in field && field.fields) { if ('fields' in field && field.fields) {
field.fields = sanitizeFields({ field.fields = sanitizeFields({
config, config,
existingFieldNames: fieldAffectsData(field) ? new Set() : existingFieldNames,
fields: field.fields, fields: field.fields,
validRelationships, validRelationships,
}) })
@@ -140,6 +158,7 @@ export const sanitizeFields = ({ config, fields, validRelationships }: Args): Fi
unsanitizedTab.fields = sanitizeFields({ unsanitizedTab.fields = sanitizeFields({
config, config,
existingFieldNames: tabHasName(tab) ? new Set() : existingFieldNames,
fields: tab.fields, fields: tab.fields,
validRelationships, validRelationships,
}) })
@@ -159,6 +178,7 @@ export const sanitizeFields = ({ config, fields, validRelationships }: Args): Fi
config, config,
fields: block.fields, fields: block.fields,
validRelationships, validRelationships,
existingFieldNames: new Set(),
}) })
return unsanitizedBlock return unsanitizedBlock

View File

@@ -1,33 +1,164 @@
/* tslint:disable */ /* tslint:disable */
/* eslint-disable */
/** /**
* This file was automatically generated by Payload CMS. * This file was automatically generated by Payload.
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config, * DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
* and re-run `payload generate:types` to regenerate this file. * and re-run `payload generate:types` to regenerate this file.
*/ */
export interface Config { export interface Config {
collections: { collections: {
posts: Post 'error-fields': ErrorField
uploads: Upload
users: User users: User
'payload-preferences': PayloadPreference
'payload-migrations': PayloadMigration
} }
globals: {} globals: {}
} }
export interface Post { export interface ErrorField {
id: string id: string
arrayField?: { parentArray?:
group23field: { | {
arrayField: { childArray: {
group23field: { childArrayText: string
arrayField: { id?: string | null
textField: string }[]
id?: string id?: string | null
}[]
}
id?: string
}[] }[]
} | null
id?: string home: {
}[] tabText: string
text: string
array?:
| {
requiredArrayText: string
arrayText?: string | null
group: {
text: string
number: number
date: string
checkbox: boolean
}
code: string
json:
| {
[k: string]: unknown
}
| unknown[]
| string
| number
| boolean
| null
email: string
/**
* @minItems 2
* @maxItems 2
*/
point: [number, number]
radio: 'mint' | 'dark_gray'
relationship: string | User
richtext: {
[k: string]: unknown
}[]
select: 'mint' | 'dark_gray'
upload: string | Upload
text: string
textarea: string
id?: string | null
}[]
| null
}
tabText: string
text: string
array?:
| {
requiredArrayText: string
arrayText?: string | null
group: {
text: string
number: number
date: string
checkbox: boolean
}
code: string
json:
| {
[k: string]: unknown
}
| unknown[]
| string
| number
| boolean
| null
email: string
/**
* @minItems 2
* @maxItems 2
*/
point: [number, number]
radio: 'mint' | 'dark_gray'
relationship: string | User
richtext: {
[k: string]: unknown
}[]
select: 'mint' | 'dark_gray'
upload: string | Upload
text: string
textarea: string
id?: string | null
}[]
| null
layout?:
| {
tabText: string
text: string
array?:
| {
requiredArrayText: string
arrayText?: string | null
group: {
text: string
number: number
date: string
checkbox: boolean
}
code: string
json:
| {
[k: string]: unknown
}
| unknown[]
| string
| number
| boolean
| null
email: string
/**
* @minItems 2
* @maxItems 2
*/
point: [number, number]
radio: 'mint' | 'dark_gray'
relationship: string | User
richtext: {
[k: string]: unknown
}[]
select: 'mint' | 'dark_gray'
upload: string | Upload
text: string
textarea: string
id?: string | null
}[]
| null
id?: string | null
blockName?: string | null
blockType: 'block1'
}[]
| null
group: {
text: string
}
updatedAt: string updatedAt: string
createdAt: string createdAt: string
} }
@@ -35,12 +166,60 @@ export interface User {
id: string id: string
updatedAt: string updatedAt: string
createdAt: string createdAt: string
email?: string email: string
resetPasswordToken?: string resetPasswordToken?: string | null
resetPasswordExpiration?: string resetPasswordExpiration?: string | null
salt?: string salt?: string | null
hash?: string hash?: string | null
loginAttempts?: number loginAttempts?: number | null
lockUntil?: string lockUntil?: string | null
password?: string password: string | null
}
export interface Upload {
id: string
text?: string | null
media?: string | Upload | null
richText?:
| {
[k: string]: unknown
}[]
| null
updatedAt: string
createdAt: string
url?: string | null
filename?: string | null
mimeType?: string | null
filesize?: number | null
width?: number | null
height?: number | null
}
export interface PayloadPreference {
id: string
user: {
relationTo: 'users'
value: string | User
}
key?: string | null
value?:
| {
[k: string]: unknown
}
| unknown[]
| string
| number
| boolean
| null
updatedAt: string
createdAt: string
}
export interface PayloadMigration {
id: string
name?: string | null
batch?: number | null
updatedAt: string
createdAt: string
}
declare module 'payload' {
export interface GeneratedTypes extends Config {}
} }

View File

@@ -8,7 +8,7 @@ import type {
RelationRestricted, RelationRestricted,
RelationTwo, RelationTwo,
RelationWithTitle, RelationWithTitle,
} from './config' } from './payload-types'
import payload from '../../packages/payload/src' import payload from '../../packages/payload/src'
import { mapAsync } from '../../packages/payload/src/utilities/mapAsync' import { mapAsync } from '../../packages/payload/src/utilities/mapAsync'
@@ -55,37 +55,37 @@ describe('fields - relationship', () => {
await clearAllDocs() await clearAllDocs()
// Create docs to relate to // Create docs to relate to
relationOneDoc = await payload.create({ relationOneDoc = (await payload.create({
collection: relationOneSlug, collection: relationOneSlug,
data: { data: {
name: 'relation', name: 'relation',
}, },
}) })) as any
anotherRelationOneDoc = await payload.create({ anotherRelationOneDoc = (await payload.create({
collection: relationOneSlug, collection: relationOneSlug,
data: { data: {
name: 'relation', name: 'relation',
}, },
}) })) as any
relationTwoDoc = await payload.create({ relationTwoDoc = (await payload.create({
collection: relationTwoSlug, collection: relationTwoSlug,
data: { data: {
name: 'second-relation', name: 'second-relation',
}, },
}) })) as any
// Create restricted doc // Create restricted doc
restrictedRelation = await payload.create({ restrictedRelation = (await payload.create({
collection: relationRestrictedSlug, collection: relationRestrictedSlug,
data: { data: {
name: 'restricted', name: 'restricted',
}, },
}) })) as any
// Doc with useAsTitle // Doc with useAsTitle
relationWithTitle = await payload.create({ relationWithTitle = (await payload.create({
collection: relationWithTitleSlug, collection: relationWithTitleSlug,
data: { data: {
name: 'relation-title', name: 'relation-title',
@@ -93,7 +93,7 @@ describe('fields - relationship', () => {
title: 'relation-title', title: 'relation-title',
}, },
}, },
}) })) as any
// Doc with useAsTitle for word boundary test // Doc with useAsTitle for word boundary test
await payload.create({ await payload.create({
@@ -107,7 +107,7 @@ describe('fields - relationship', () => {
}) })
// Add restricted doc as relation // Add restricted doc as relation
docWithExistingRelations = await payload.create({ docWithExistingRelations = (await payload.create({
collection: slug, collection: slug,
data: { data: {
name: 'with-existing-relations', name: 'with-existing-relations',
@@ -116,7 +116,7 @@ describe('fields - relationship', () => {
relationshipWithTitle: relationWithTitle.id, relationshipWithTitle: relationWithTitle.id,
relationshipReadOnly: relationOneDoc.id, relationshipReadOnly: relationOneDoc.id,
}, },
}) })) as any
}) })
test('should create relationship', async () => { test('should create relationship', async () => {
@@ -279,18 +279,18 @@ describe('fields - relationship', () => {
}) })
test('should allow usage of relationTo in filterOptions', async () => { test('should allow usage of relationTo in filterOptions', async () => {
const { id: include } = await payload.create({ const { id: include } = (await payload.create({
collection: relationOneSlug, collection: relationOneSlug,
data: { data: {
name: 'include', name: 'include',
}, },
}) })) as any
const { id: exclude } = await payload.create({ const { id: exclude } = (await payload.create({
collection: relationOneSlug, collection: relationOneSlug,
data: { data: {
name: 'exclude', name: 'exclude',
}, },
}) })) as any
await page.goto(url.create) await page.goto(url.create)

View File

@@ -1,180 +1,171 @@
/* tslint:disable */ /* tslint:disable */
/* eslint-disable */
/** /**
* This file was automatically generated by Payload. * This file was automatically generated by Payload.
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config, * DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
* and re-run `payload generate:types` to regenerate this file. * and re-run `payload generate:types` to regenerate this file.
*/ */
export interface Config {} export interface Config {
/** collections: {
* This interface was referenced by `Config`'s JSON-Schema 'fields-relationship': FieldsRelationship
* via the `definition` "fields-relationship". 'relation-one': RelationOne
*/ 'relation-two': RelationTwo
'relation-restricted': RelationRestricted
'relation-with-title': RelationWithTitle
'relation-updated-externally': RelationUpdatedExternally
'collection-1': Collection1
'collection-2': Collection2
users: User
'payload-preferences': PayloadPreference
'payload-migrations': PayloadMigration
}
globals: {}
}
export interface FieldsRelationship { export interface FieldsRelationship {
id: string id: string
relationship?: string | RelationOne relationship?: (string | null) | RelationOne
relationshipHasMany?: string[] | RelationOne[] relationshipHasMany?: (string | RelationOne)[] | null
relationshipMultiple?: relationshipMultiple?:
| { | ({
value: string | RelationOne
relationTo: 'relation-one' relationTo: 'relation-one'
} value: string | RelationOne
| { } | null)
value: string | RelationTwo | ({
relationTo: 'relation-two' relationTo: 'relation-two'
} value: string | RelationTwo
} | null)
relationshipHasManyMultiple?: relationshipHasManyMultiple?:
| ( | (
| { | {
value: string
relationTo: 'relation-one' relationTo: 'relation-one'
value: string | RelationOne
} }
| { | {
value: string
relationTo: 'relation-two' relationTo: 'relation-two'
value: string | RelationTwo
} }
)[] )[]
| ( | null
| { relationshipRestricted?: (string | null) | RelationRestricted
value: RelationOne relationshipWithTitle?: (string | null) | RelationWithTitle
relationTo: 'relation-one' relationshipFiltered?: (string | null) | RelationOne
} relationshipFilteredAsync?: (string | null) | RelationOne
| {
value: RelationTwo
relationTo: 'relation-two'
}
)[]
relationshipRestricted?: string | RelationRestricted
relationshipWithTitle?: string | RelationWithTitle
relationshipFiltered?: string | RelationOne
relationshipManyFiltered?: relationshipManyFiltered?:
| ( | (
| { | {
value: string
relationTo: 'relation-with-title' relationTo: 'relation-with-title'
value: string | RelationWithTitle
} }
| { | {
value: string
relationTo: 'relation-one' relationTo: 'relation-one'
value: string | RelationOne
} }
)[] )[]
| ( | null
| { filter?: string | null
value: RelationWithTitle relationshipReadOnly?: (string | null) | RelationOne
relationTo: 'relation-with-title'
}
| {
value: RelationOne
relationTo: 'relation-one'
}
)[]
filter?: string
createdAt: string
updatedAt: string updatedAt: string
createdAt: string
} }
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "relation-one".
*/
export interface RelationOne { export interface RelationOne {
id: string id: string
name?: string name?: string | null
createdAt: string
updatedAt: string updatedAt: string
createdAt: string
} }
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "relation-two".
*/
export interface RelationTwo { export interface RelationTwo {
id: string id: string
name?: string name?: string | null
createdAt: string
updatedAt: string updatedAt: string
createdAt: string
} }
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "relation-restricted".
*/
export interface RelationRestricted { export interface RelationRestricted {
id: string id: string
name?: string name?: string | null
createdAt: string
updatedAt: string updatedAt: string
createdAt: string
} }
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "relation-with-title".
*/
export interface RelationWithTitle { export interface RelationWithTitle {
id: string id: string
name?: string name?: string | null
createdAt: string meta?: {
title?: string | null
}
updatedAt: string updatedAt: string
createdAt: string
} }
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "relation-updated-externally".
*/
export interface RelationUpdatedExternally { export interface RelationUpdatedExternally {
id: string id: string
relationPrePopulate?: string | Collection1 relationPrePopulate?: (string | null) | Collection1
relationHasMany?: string[] | Collection1[] relationHasMany?: (string | Collection1)[] | null
relationToManyHasMany?: relationToManyHasMany?:
| ( | (
| { | {
value: string
relationTo: 'collection-1' relationTo: 'collection-1'
value: string | Collection1
} }
| { | {
value: string
relationTo: 'collection-2' relationTo: 'collection-2'
value: string | Collection2
} }
)[] )[]
| ( | null
| {
value: Collection1
relationTo: 'collection-1'
}
| {
value: Collection2
relationTo: 'collection-2'
}
)[]
createdAt: string
updatedAt: string updatedAt: string
createdAt: string
} }
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "collection-1".
*/
export interface Collection1 { export interface Collection1 {
id: string id: string
name?: string name?: string | null
createdAt: string
updatedAt: string updatedAt: string
createdAt: string
} }
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "collection-2".
*/
export interface Collection2 { export interface Collection2 {
id: string id: string
name?: string name?: string | null
createdAt: string
updatedAt: string updatedAt: string
createdAt: string
} }
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users".
*/
export interface User { export interface User {
id: string id: string
email?: string
resetPasswordToken?: string
resetPasswordExpiration?: string
loginAttempts?: number
lockUntil?: string
createdAt: string
updatedAt: string updatedAt: string
createdAt: string
email: string
resetPasswordToken?: string | null
resetPasswordExpiration?: string | null
salt?: string | null
hash?: string | null
loginAttempts?: number | null
lockUntil?: string | null
password: string | null
}
export interface PayloadPreference {
id: string
user: {
relationTo: 'users'
value: string | User
}
key?: string | null
value?:
| {
[k: string]: unknown
}
| unknown[]
| string
| number
| boolean
| null
updatedAt: string
createdAt: string
}
export interface PayloadMigration {
id: string
name?: string | null
batch?: number | null
updatedAt: string
createdAt: string
}
declare module 'payload' {
export interface GeneratedTypes extends Config {}
} }

View File

@@ -55,8 +55,7 @@ const CollapsibleFields: CollectionConfig = {
type: 'text', type: 'text',
}, },
{ {
// TODO: change group name, to not be a duplicate of the above collapsible name: 'group2',
name: 'group',
type: 'group', type: 'group',
fields: [ fields: [
{ {

View File

@@ -1,11 +1,11 @@
import type { Block } from '../../../../packages/payload/src/fields/config/types' import type { ArrayField, Block } from '../../../../packages/payload/src/fields/config/types'
import { lexicalEditor } from '../../../../packages/richtext-lexical/src' import { lexicalEditor } from '../../../../packages/richtext-lexical/src'
import { textFieldsSlug } from '../Text/shared' import { textFieldsSlug } from '../Text/shared'
export const BlockColumns: any = { export const BlockColumns = ({ name }: { name: string }): ArrayField => ({
type: 'array', type: 'array',
name: 'columns', name,
interfaceName: 'BlockColumns', interfaceName: 'BlockColumns',
admin: { admin: {
initCollapsed: true, initCollapsed: true,
@@ -16,7 +16,7 @@ export const BlockColumns: any = {
type: 'text', type: 'text',
}, },
], ],
} })
export const ConditionalLayoutBlock: Block = { export const ConditionalLayoutBlock: Block = {
fields: [ fields: [
{ {
@@ -28,7 +28,7 @@ export const ConditionalLayoutBlock: Block = {
required: true, required: true,
}, },
{ {
...BlockColumns, ...BlockColumns({ name: 'columns' }),
admin: { admin: {
condition: (data, siblingData) => { condition: (data, siblingData) => {
return ['1'].includes(siblingData.layout) return ['1'].includes(siblingData.layout)
@@ -38,7 +38,7 @@ export const ConditionalLayoutBlock: Block = {
maxRows: 1, maxRows: 1,
}, },
{ {
...BlockColumns, ...BlockColumns({ name: 'columns2' }),
admin: { admin: {
condition: (data, siblingData) => { condition: (data, siblingData) => {
return ['2'].includes(siblingData.layout) return ['2'].includes(siblingData.layout)
@@ -48,7 +48,7 @@ export const ConditionalLayoutBlock: Block = {
maxRows: 2, maxRows: 2,
}, },
{ {
...BlockColumns, ...BlockColumns({ name: 'columns3' }),
admin: { admin: {
condition: (data, siblingData) => { condition: (data, siblingData) => {
return ['3'].includes(siblingData.layout) return ['3'].includes(siblingData.layout)

View File

@@ -270,6 +270,8 @@ export function generateLexicalRichText() {
text: 'text in conditionalLayout block', text: 'text in conditionalLayout block',
}, },
], ],
columns2: null,
columns3: null,
}, },
}, // Do not remove this blocks node. It ensures that validation passes when it's created }, // Do not remove this blocks node. It ensures that validation passes when it's created
{ {

View File

@@ -29,6 +29,7 @@ import {
import { tabsDoc } from './collections/Tabs/shared' import { tabsDoc } from './collections/Tabs/shared'
import { defaultText } from './collections/Text/shared' import { defaultText } from './collections/Text/shared'
import { clearAndSeedEverything } from './seed' import { clearAndSeedEverything } from './seed'
import { GroupField } from './payload-types'
import { import {
arrayFieldsSlug, arrayFieldsSlug,
blockFieldsSlug, blockFieldsSlug,
@@ -587,10 +588,10 @@ describe('Fields', () => {
}) })
it('should create with ids and nested ids', async () => { it('should create with ids and nested ids', async () => {
const docWithIDs = await payload.create({ const docWithIDs = (await payload.create({
collection: groupFieldsSlug, collection: groupFieldsSlug,
data: groupDoc, data: groupDoc,
}) })) as Partial<GroupField>
expect(docWithIDs.group.subGroup.arrayWithinGroup[0].id).toBeDefined() expect(docWithIDs.group.subGroup.arrayWithinGroup[0].id).toBeDefined()
}) })

View File

@@ -658,45 +658,15 @@ describe('lexical', () => {
const selectFieldMenu = selectField.locator('.rs__menu').first() const selectFieldMenu = selectField.locator('.rs__menu').first()
await selectFieldMenu.locator('.rs__option').nth(1).click() // Select "2" (2 columns / array fields) await selectFieldMenu.locator('.rs__option').nth(1).click() // Select "2" (2 columns / array fields)
await conditionalArrayBlock.getByText('Add Column').click() await conditionalArrayBlock.locator('.btn__label:has-text("Add Columns2")').first().click()
await conditionalArrayBlock.locator('.btn__label:has-text("Add Columns2")').first().click()
await conditionalArrayBlock await conditionalArrayBlock
.locator('.array-field__draggable-rows') .locator('.array-field__draggable-rows > div:nth-child(2) .input-wrapper input')
.first() .fill('second input')
.locator('.array-field__row')
.nth(1)
.locator('.input-wrapper input')
.first()
.fill('second text')
await saveDocAndAssert(page) await saveDocAndAssert(page)
await selectField.click()
await selectFieldMenu.locator('.rs__option').nth(0).click() // Select "1" (1 columns / array fields)
// Remove 2nd column
await conditionalArrayBlock
.locator('.array-field__draggable-rows')
.first()
.locator('.array-field__row')
.nth(1)
.locator('.array-actions__button')
.first()
.click()
await conditionalArrayBlock
.locator('.array-field__draggable-rows')
.first()
.locator('.array-field__row')
.nth(1)
.locator('.popup__content')
.first()
.locator('Button')
.nth(1)
.click()
await saveDocAndAssert(page)
// This can be triggered if the 2nd row's data is not actually deleted (<= this is the bug), as the validation expects just one row
await expect(page.locator('.Toastify')).not.toContainText('Please correct invalid fields.') await expect(page.locator('.Toastify')).not.toContainText('Please correct invalid fields.')
}) })
}) })

View File

@@ -36,7 +36,9 @@ export interface Config {
'payload-preferences': PayloadPreference 'payload-preferences': PayloadPreference
'payload-migrations': PayloadMigration 'payload-migrations': PayloadMigration
} }
globals: {} globals: {
tabsWithRichText: TabsWithRichText
}
} }
export interface LexicalField { export interface LexicalField {
id: string id: string
@@ -169,6 +171,12 @@ export interface ArrayField {
title?: string | null title?: string | null
items: { items: {
text: string text: string
subArray?:
| {
text?: string | null
id?: string | null
}[]
| null
id?: string | null id?: string | null
}[] }[]
collapsedArray?: collapsedArray?:
@@ -447,6 +455,12 @@ export interface CollapsibleField {
} }
} }
someText?: string | null someText?: string | null
group2?: {
textWithinGroup?: string | null
subGroup?: {
textWithinSubGroup?: string | null
}
}
functionTitleField?: string | null functionTitleField?: string | null
componentTitleField?: string | null componentTitleField?: string | null
nestedTitle?: string | null nestedTitle?: string | null
@@ -838,6 +852,15 @@ export interface TabsField {
} }
textInRow: string textInRow: string
numberInRow: number numberInRow: number
json?:
| {
[k: string]: unknown
}
| unknown[]
| string
| number
| boolean
| null
tab: { tab: {
array: { array: {
text: string text: string
@@ -943,6 +966,45 @@ export interface PayloadMigration {
updatedAt: string updatedAt: string
createdAt: string createdAt: string
} }
export interface TabsWithRichText {
id: string
tab1: {
rt1?: {
root: {
children: {
type: string
version: number
[k: string]: unknown
}[]
direction: ('ltr' | 'rtl') | null
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''
indent: number
type: string
version: number
}
[k: string]: unknown
} | null
}
tab2: {
rt2?: {
root: {
children: {
type: string
version: number
[k: string]: unknown
}[]
direction: ('ltr' | 'rtl') | null
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''
indent: number
type: string
version: number
}
[k: string]: unknown
} | null
}
updatedAt?: string | null
createdAt?: string | null
}
declare module 'payload' { declare module 'payload' {
export interface GeneratedTypes extends Config {} export interface GeneratedTypes extends Config {}

View File

@@ -91,7 +91,7 @@ export default buildConfigWithDefaults({
fields: [ fields: [
{ {
type: 'array', type: 'array',
name: 'meta', name: 'metaArray',
interfaceName: 'SharedMetaArray', interfaceName: 'SharedMetaArray',
fields: [ fields: [
{ {
@@ -106,7 +106,7 @@ export default buildConfigWithDefaults({
}, },
{ {
type: 'group', type: 'group',
name: 'meta', name: 'metaGroup',
interfaceName: 'SharedMeta', interfaceName: 'SharedMeta',
fields: [ fields: [
{ {

View File

@@ -452,7 +452,8 @@ type Collection1DeleteDocAccess {
type Collection2 { type Collection2 {
id: String id: String
meta: SharedMeta metaArray: [SharedMetaArray!]
metaGroup: SharedMeta
nestedGroup: Collection2_NestedGroup nestedGroup: Collection2_NestedGroup
updatedAt: DateTime updatedAt: DateTime
createdAt: DateTime createdAt: DateTime
@@ -482,9 +483,11 @@ type Collection2s {
} }
input Collection2_where { input Collection2_where {
meta__title: Collection2_meta__title_operator metaArray__title: Collection2_metaArray__title_operator
meta__description: Collection2_meta__description_operator metaArray__description: Collection2_metaArray__description_operator
meta__id: Collection2_meta__id_operator metaArray__id: Collection2_metaArray__id_operator
metaGroup__title: Collection2_metaGroup__title_operator
metaGroup__description: Collection2_metaGroup__description_operator
nestedGroup__meta__title: Collection2_nestedGroup__meta__title_operator nestedGroup__meta__title: Collection2_nestedGroup__meta__title_operator
nestedGroup__meta__description: Collection2_nestedGroup__meta__description_operator nestedGroup__meta__description: Collection2_nestedGroup__meta__description_operator
updatedAt: Collection2_updatedAt_operator updatedAt: Collection2_updatedAt_operator
@@ -494,7 +497,7 @@ input Collection2_where {
OR: [Collection2_where_or] OR: [Collection2_where_or]
} }
input Collection2_meta__title_operator { input Collection2_metaArray__title_operator {
equals: String equals: String
not_equals: String not_equals: String
like: String like: String
@@ -505,7 +508,7 @@ input Collection2_meta__title_operator {
exists: Boolean exists: Boolean
} }
input Collection2_meta__description_operator { input Collection2_metaArray__description_operator {
equals: String equals: String
not_equals: String not_equals: String
like: String like: String
@@ -516,7 +519,29 @@ input Collection2_meta__description_operator {
exists: Boolean exists: Boolean
} }
input Collection2_meta__id_operator { input Collection2_metaArray__id_operator {
equals: String
not_equals: String
like: String
contains: String
in: [String]
not_in: [String]
all: [String]
exists: Boolean
}
input Collection2_metaGroup__title_operator {
equals: String
not_equals: String
like: String
contains: String
in: [String]
not_in: [String]
all: [String]
exists: Boolean
}
input Collection2_metaGroup__description_operator {
equals: String equals: String
not_equals: String not_equals: String
like: String like: String
@@ -583,9 +608,11 @@ input Collection2_id_operator {
} }
input Collection2_where_and { input Collection2_where_and {
meta__title: Collection2_meta__title_operator metaArray__title: Collection2_metaArray__title_operator
meta__description: Collection2_meta__description_operator metaArray__description: Collection2_metaArray__description_operator
meta__id: Collection2_meta__id_operator metaArray__id: Collection2_metaArray__id_operator
metaGroup__title: Collection2_metaGroup__title_operator
metaGroup__description: Collection2_metaGroup__description_operator
nestedGroup__meta__title: Collection2_nestedGroup__meta__title_operator nestedGroup__meta__title: Collection2_nestedGroup__meta__title_operator
nestedGroup__meta__description: Collection2_nestedGroup__meta__description_operator nestedGroup__meta__description: Collection2_nestedGroup__meta__description_operator
updatedAt: Collection2_updatedAt_operator updatedAt: Collection2_updatedAt_operator
@@ -596,9 +623,11 @@ input Collection2_where_and {
} }
input Collection2_where_or { input Collection2_where_or {
meta__title: Collection2_meta__title_operator metaArray__title: Collection2_metaArray__title_operator
meta__description: Collection2_meta__description_operator metaArray__description: Collection2_metaArray__description_operator
meta__id: Collection2_meta__id_operator metaArray__id: Collection2_metaArray__id_operator
metaGroup__title: Collection2_metaGroup__title_operator
metaGroup__description: Collection2_metaGroup__description_operator
nestedGroup__meta__title: Collection2_nestedGroup__meta__title_operator nestedGroup__meta__title: Collection2_nestedGroup__meta__title_operator
nestedGroup__meta__description: Collection2_nestedGroup__meta__description_operator nestedGroup__meta__description: Collection2_nestedGroup__meta__description_operator
updatedAt: Collection2_updatedAt_operator updatedAt: Collection2_updatedAt_operator
@@ -617,84 +646,184 @@ type collection2DocAccess {
} }
type Collection2DocAccessFields { type Collection2DocAccessFields {
meta: Collection2DocAccessFields_meta metaArray: Collection2DocAccessFields_metaArray
metaGroup: Collection2DocAccessFields_metaGroup
nestedGroup: Collection2DocAccessFields_nestedGroup nestedGroup: Collection2DocAccessFields_nestedGroup
updatedAt: Collection2DocAccessFields_updatedAt updatedAt: Collection2DocAccessFields_updatedAt
createdAt: Collection2DocAccessFields_createdAt createdAt: Collection2DocAccessFields_createdAt
} }
type Collection2DocAccessFields_meta { type Collection2DocAccessFields_metaArray {
create: Collection2DocAccessFields_meta_Create create: Collection2DocAccessFields_metaArray_Create
read: Collection2DocAccessFields_meta_Read read: Collection2DocAccessFields_metaArray_Read
update: Collection2DocAccessFields_meta_Update update: Collection2DocAccessFields_metaArray_Update
delete: Collection2DocAccessFields_meta_Delete delete: Collection2DocAccessFields_metaArray_Delete
fields: Collection2DocAccessFields_meta_Fields fields: Collection2DocAccessFields_metaArray_Fields
} }
type Collection2DocAccessFields_meta_Create { type Collection2DocAccessFields_metaArray_Create {
permission: Boolean! permission: Boolean!
} }
type Collection2DocAccessFields_meta_Read { type Collection2DocAccessFields_metaArray_Read {
permission: Boolean! permission: Boolean!
} }
type Collection2DocAccessFields_meta_Update { type Collection2DocAccessFields_metaArray_Update {
permission: Boolean! permission: Boolean!
} }
type Collection2DocAccessFields_meta_Delete { type Collection2DocAccessFields_metaArray_Delete {
permission: Boolean! permission: Boolean!
} }
type Collection2DocAccessFields_meta_Fields { type Collection2DocAccessFields_metaArray_Fields {
title: Collection2DocAccessFields_meta_title title: Collection2DocAccessFields_metaArray_title
description: Collection2DocAccessFields_meta_description description: Collection2DocAccessFields_metaArray_description
id: Collection2DocAccessFields_metaArray_id
} }
type Collection2DocAccessFields_meta_title { type Collection2DocAccessFields_metaArray_title {
create: Collection2DocAccessFields_meta_title_Create create: Collection2DocAccessFields_metaArray_title_Create
read: Collection2DocAccessFields_meta_title_Read read: Collection2DocAccessFields_metaArray_title_Read
update: Collection2DocAccessFields_meta_title_Update update: Collection2DocAccessFields_metaArray_title_Update
delete: Collection2DocAccessFields_meta_title_Delete delete: Collection2DocAccessFields_metaArray_title_Delete
} }
type Collection2DocAccessFields_meta_title_Create { type Collection2DocAccessFields_metaArray_title_Create {
permission: Boolean! permission: Boolean!
} }
type Collection2DocAccessFields_meta_title_Read { type Collection2DocAccessFields_metaArray_title_Read {
permission: Boolean! permission: Boolean!
} }
type Collection2DocAccessFields_meta_title_Update { type Collection2DocAccessFields_metaArray_title_Update {
permission: Boolean! permission: Boolean!
} }
type Collection2DocAccessFields_meta_title_Delete { type Collection2DocAccessFields_metaArray_title_Delete {
permission: Boolean! permission: Boolean!
} }
type Collection2DocAccessFields_meta_description { type Collection2DocAccessFields_metaArray_description {
create: Collection2DocAccessFields_meta_description_Create create: Collection2DocAccessFields_metaArray_description_Create
read: Collection2DocAccessFields_meta_description_Read read: Collection2DocAccessFields_metaArray_description_Read
update: Collection2DocAccessFields_meta_description_Update update: Collection2DocAccessFields_metaArray_description_Update
delete: Collection2DocAccessFields_meta_description_Delete delete: Collection2DocAccessFields_metaArray_description_Delete
} }
type Collection2DocAccessFields_meta_description_Create { type Collection2DocAccessFields_metaArray_description_Create {
permission: Boolean! permission: Boolean!
} }
type Collection2DocAccessFields_meta_description_Read { type Collection2DocAccessFields_metaArray_description_Read {
permission: Boolean! permission: Boolean!
} }
type Collection2DocAccessFields_meta_description_Update { type Collection2DocAccessFields_metaArray_description_Update {
permission: Boolean! permission: Boolean!
} }
type Collection2DocAccessFields_meta_description_Delete { type Collection2DocAccessFields_metaArray_description_Delete {
permission: Boolean!
}
type Collection2DocAccessFields_metaArray_id {
create: Collection2DocAccessFields_metaArray_id_Create
read: Collection2DocAccessFields_metaArray_id_Read
update: Collection2DocAccessFields_metaArray_id_Update
delete: Collection2DocAccessFields_metaArray_id_Delete
}
type Collection2DocAccessFields_metaArray_id_Create {
permission: Boolean!
}
type Collection2DocAccessFields_metaArray_id_Read {
permission: Boolean!
}
type Collection2DocAccessFields_metaArray_id_Update {
permission: Boolean!
}
type Collection2DocAccessFields_metaArray_id_Delete {
permission: Boolean!
}
type Collection2DocAccessFields_metaGroup {
create: Collection2DocAccessFields_metaGroup_Create
read: Collection2DocAccessFields_metaGroup_Read
update: Collection2DocAccessFields_metaGroup_Update
delete: Collection2DocAccessFields_metaGroup_Delete
fields: Collection2DocAccessFields_metaGroup_Fields
}
type Collection2DocAccessFields_metaGroup_Create {
permission: Boolean!
}
type Collection2DocAccessFields_metaGroup_Read {
permission: Boolean!
}
type Collection2DocAccessFields_metaGroup_Update {
permission: Boolean!
}
type Collection2DocAccessFields_metaGroup_Delete {
permission: Boolean!
}
type Collection2DocAccessFields_metaGroup_Fields {
title: Collection2DocAccessFields_metaGroup_title
description: Collection2DocAccessFields_metaGroup_description
}
type Collection2DocAccessFields_metaGroup_title {
create: Collection2DocAccessFields_metaGroup_title_Create
read: Collection2DocAccessFields_metaGroup_title_Read
update: Collection2DocAccessFields_metaGroup_title_Update
delete: Collection2DocAccessFields_metaGroup_title_Delete
}
type Collection2DocAccessFields_metaGroup_title_Create {
permission: Boolean!
}
type Collection2DocAccessFields_metaGroup_title_Read {
permission: Boolean!
}
type Collection2DocAccessFields_metaGroup_title_Update {
permission: Boolean!
}
type Collection2DocAccessFields_metaGroup_title_Delete {
permission: Boolean!
}
type Collection2DocAccessFields_metaGroup_description {
create: Collection2DocAccessFields_metaGroup_description_Create
read: Collection2DocAccessFields_metaGroup_description_Read
update: Collection2DocAccessFields_metaGroup_description_Update
delete: Collection2DocAccessFields_metaGroup_description_Delete
}
type Collection2DocAccessFields_metaGroup_description_Create {
permission: Boolean!
}
type Collection2DocAccessFields_metaGroup_description_Read {
permission: Boolean!
}
type Collection2DocAccessFields_metaGroup_description_Update {
permission: Boolean!
}
type Collection2DocAccessFields_metaGroup_description_Delete {
permission: Boolean! permission: Boolean!
} }
@@ -1664,84 +1793,184 @@ type collection2Access {
} }
type Collection2Fields { type Collection2Fields {
meta: Collection2Fields_meta metaArray: Collection2Fields_metaArray
metaGroup: Collection2Fields_metaGroup
nestedGroup: Collection2Fields_nestedGroup nestedGroup: Collection2Fields_nestedGroup
updatedAt: Collection2Fields_updatedAt updatedAt: Collection2Fields_updatedAt
createdAt: Collection2Fields_createdAt createdAt: Collection2Fields_createdAt
} }
type Collection2Fields_meta { type Collection2Fields_metaArray {
create: Collection2Fields_meta_Create create: Collection2Fields_metaArray_Create
read: Collection2Fields_meta_Read read: Collection2Fields_metaArray_Read
update: Collection2Fields_meta_Update update: Collection2Fields_metaArray_Update
delete: Collection2Fields_meta_Delete delete: Collection2Fields_metaArray_Delete
fields: Collection2Fields_meta_Fields fields: Collection2Fields_metaArray_Fields
} }
type Collection2Fields_meta_Create { type Collection2Fields_metaArray_Create {
permission: Boolean! permission: Boolean!
} }
type Collection2Fields_meta_Read { type Collection2Fields_metaArray_Read {
permission: Boolean! permission: Boolean!
} }
type Collection2Fields_meta_Update { type Collection2Fields_metaArray_Update {
permission: Boolean! permission: Boolean!
} }
type Collection2Fields_meta_Delete { type Collection2Fields_metaArray_Delete {
permission: Boolean! permission: Boolean!
} }
type Collection2Fields_meta_Fields { type Collection2Fields_metaArray_Fields {
title: Collection2Fields_meta_title title: Collection2Fields_metaArray_title
description: Collection2Fields_meta_description description: Collection2Fields_metaArray_description
id: Collection2Fields_metaArray_id
} }
type Collection2Fields_meta_title { type Collection2Fields_metaArray_title {
create: Collection2Fields_meta_title_Create create: Collection2Fields_metaArray_title_Create
read: Collection2Fields_meta_title_Read read: Collection2Fields_metaArray_title_Read
update: Collection2Fields_meta_title_Update update: Collection2Fields_metaArray_title_Update
delete: Collection2Fields_meta_title_Delete delete: Collection2Fields_metaArray_title_Delete
} }
type Collection2Fields_meta_title_Create { type Collection2Fields_metaArray_title_Create {
permission: Boolean! permission: Boolean!
} }
type Collection2Fields_meta_title_Read { type Collection2Fields_metaArray_title_Read {
permission: Boolean! permission: Boolean!
} }
type Collection2Fields_meta_title_Update { type Collection2Fields_metaArray_title_Update {
permission: Boolean! permission: Boolean!
} }
type Collection2Fields_meta_title_Delete { type Collection2Fields_metaArray_title_Delete {
permission: Boolean! permission: Boolean!
} }
type Collection2Fields_meta_description { type Collection2Fields_metaArray_description {
create: Collection2Fields_meta_description_Create create: Collection2Fields_metaArray_description_Create
read: Collection2Fields_meta_description_Read read: Collection2Fields_metaArray_description_Read
update: Collection2Fields_meta_description_Update update: Collection2Fields_metaArray_description_Update
delete: Collection2Fields_meta_description_Delete delete: Collection2Fields_metaArray_description_Delete
} }
type Collection2Fields_meta_description_Create { type Collection2Fields_metaArray_description_Create {
permission: Boolean! permission: Boolean!
} }
type Collection2Fields_meta_description_Read { type Collection2Fields_metaArray_description_Read {
permission: Boolean! permission: Boolean!
} }
type Collection2Fields_meta_description_Update { type Collection2Fields_metaArray_description_Update {
permission: Boolean! permission: Boolean!
} }
type Collection2Fields_meta_description_Delete { type Collection2Fields_metaArray_description_Delete {
permission: Boolean!
}
type Collection2Fields_metaArray_id {
create: Collection2Fields_metaArray_id_Create
read: Collection2Fields_metaArray_id_Read
update: Collection2Fields_metaArray_id_Update
delete: Collection2Fields_metaArray_id_Delete
}
type Collection2Fields_metaArray_id_Create {
permission: Boolean!
}
type Collection2Fields_metaArray_id_Read {
permission: Boolean!
}
type Collection2Fields_metaArray_id_Update {
permission: Boolean!
}
type Collection2Fields_metaArray_id_Delete {
permission: Boolean!
}
type Collection2Fields_metaGroup {
create: Collection2Fields_metaGroup_Create
read: Collection2Fields_metaGroup_Read
update: Collection2Fields_metaGroup_Update
delete: Collection2Fields_metaGroup_Delete
fields: Collection2Fields_metaGroup_Fields
}
type Collection2Fields_metaGroup_Create {
permission: Boolean!
}
type Collection2Fields_metaGroup_Read {
permission: Boolean!
}
type Collection2Fields_metaGroup_Update {
permission: Boolean!
}
type Collection2Fields_metaGroup_Delete {
permission: Boolean!
}
type Collection2Fields_metaGroup_Fields {
title: Collection2Fields_metaGroup_title
description: Collection2Fields_metaGroup_description
}
type Collection2Fields_metaGroup_title {
create: Collection2Fields_metaGroup_title_Create
read: Collection2Fields_metaGroup_title_Read
update: Collection2Fields_metaGroup_title_Update
delete: Collection2Fields_metaGroup_title_Delete
}
type Collection2Fields_metaGroup_title_Create {
permission: Boolean!
}
type Collection2Fields_metaGroup_title_Read {
permission: Boolean!
}
type Collection2Fields_metaGroup_title_Update {
permission: Boolean!
}
type Collection2Fields_metaGroup_title_Delete {
permission: Boolean!
}
type Collection2Fields_metaGroup_description {
create: Collection2Fields_metaGroup_description_Create
read: Collection2Fields_metaGroup_description_Read
update: Collection2Fields_metaGroup_description_Update
delete: Collection2Fields_metaGroup_description_Delete
}
type Collection2Fields_metaGroup_description_Create {
permission: Boolean!
}
type Collection2Fields_metaGroup_description_Read {
permission: Boolean!
}
type Collection2Fields_metaGroup_description_Update {
permission: Boolean!
}
type Collection2Fields_metaGroup_description_Delete {
permission: Boolean! permission: Boolean!
} }
@@ -2251,13 +2480,20 @@ input mutationCollection1Update_MetaInput {
} }
input mutationCollection2Input { input mutationCollection2Input {
meta: mutationCollection2_MetaInput metaArray: [mutationCollection2_MetaArrayInput]
metaGroup: mutationCollection2_MetaGroupInput
nestedGroup: mutationCollection2_NestedGroupInput nestedGroup: mutationCollection2_NestedGroupInput
updatedAt: String updatedAt: String
createdAt: String createdAt: String
} }
input mutationCollection2_MetaInput { input mutationCollection2_MetaArrayInput {
title: String
description: String
id: String
}
input mutationCollection2_MetaGroupInput {
title: String title: String
description: String description: String
} }
@@ -2272,13 +2508,20 @@ input mutationCollection2_NestedGroup_MetaInput {
} }
input mutationCollection2UpdateInput { input mutationCollection2UpdateInput {
meta: mutationCollection2Update_MetaInput metaArray: [mutationCollection2Update_MetaArrayInput]
metaGroup: mutationCollection2Update_MetaGroupInput
nestedGroup: mutationCollection2Update_NestedGroupInput nestedGroup: mutationCollection2Update_NestedGroupInput
updatedAt: String updatedAt: String
createdAt: String createdAt: String
} }
input mutationCollection2Update_MetaInput { input mutationCollection2Update_MetaArrayInput {
title: String
description: String
id: String
}
input mutationCollection2Update_MetaGroupInput {
title: String title: String
description: String description: String
} }