fix(ui): use consistent row ids when duplicating array and block rows (#13679)
Fixes #13653. Duplicating array rows causes phantom rows to appear. This is because when duplicate the row locally, we use inconsistent row IDs, e.g. the `array.rows[0].id` does not match its `array.0.id` counterpart. This causes form state to lose the reference to the existing row, which the server interprets as new row as of #13551. Before: https://github.com/user-attachments/assets/9f7efc59-ebd9-4fbb-b643-c22d4d3140a3 After: https://github.com/user-attachments/assets/188db823-4ee5-4757-8b89-751c8d978ad9 --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211210023936585
This commit is contained in:
1
next-env.d.ts
vendored
1
next-env.d.ts
vendored
@@ -1,6 +1,5 @@
|
|||||||
/// <reference types="next" />
|
/// <reference types="next" />
|
||||||
/// <reference types="next/image-types/global" />
|
/// <reference types="next/image-types/global" />
|
||||||
/// <reference path="./.next/types/routes.d.ts" />
|
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
// NOTE: This file should not be edited
|
||||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||||
|
|||||||
@@ -134,35 +134,37 @@ export function fieldReducer(state: FormState, action: FieldAction): FormState {
|
|||||||
case 'DUPLICATE_ROW': {
|
case 'DUPLICATE_ROW': {
|
||||||
const { path, rowIndex } = action
|
const { path, rowIndex } = action
|
||||||
const { remainingFields, rows } = separateRows(path, state)
|
const { remainingFields, rows } = separateRows(path, state)
|
||||||
const rowsMetadata = [...(state[path].rows || [])]
|
const rowsWithDuplicate = [...(state[path].rows || [])]
|
||||||
|
|
||||||
const duplicateRowMetadata = deepCopyObjectSimpleWithoutReactComponents(
|
const newRow = deepCopyObjectSimpleWithoutReactComponents(rowsWithDuplicate[rowIndex])
|
||||||
rowsMetadata[rowIndex],
|
|
||||||
)
|
|
||||||
|
|
||||||
if (duplicateRowMetadata.id) {
|
const newRowID = new ObjectId().toHexString()
|
||||||
duplicateRowMetadata.id = new ObjectId().toHexString()
|
|
||||||
|
if (newRow.id) {
|
||||||
|
newRow.id = newRowID
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rowsMetadata[rowIndex]?.customComponents?.RowLabel) {
|
if (rowsWithDuplicate[rowIndex]?.customComponents?.RowLabel) {
|
||||||
duplicateRowMetadata.customComponents = {
|
newRow.customComponents = {
|
||||||
RowLabel: rowsMetadata[rowIndex].customComponents.RowLabel,
|
RowLabel: rowsWithDuplicate[rowIndex].customComponents.RowLabel,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const duplicateRowState = deepCopyObjectSimpleWithoutReactComponents(rows[rowIndex])
|
const duplicateRowState = deepCopyObjectSimpleWithoutReactComponents(rows[rowIndex])
|
||||||
|
|
||||||
if (duplicateRowState.id) {
|
if (duplicateRowState.id) {
|
||||||
duplicateRowState.id.value = new ObjectId().toHexString()
|
duplicateRowState.id.value = newRowID
|
||||||
duplicateRowState.id.initialValue = new ObjectId().toHexString()
|
duplicateRowState.id.initialValue = newRowID
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const key of Object.keys(duplicateRowState).filter((key) => key.endsWith('.id'))) {
|
for (const key of Object.keys(duplicateRowState).filter((key) => key.endsWith('.id'))) {
|
||||||
const idState = duplicateRowState[key]
|
const idState = duplicateRowState[key]
|
||||||
|
|
||||||
|
const newNestedFieldID = new ObjectId().toHexString()
|
||||||
|
|
||||||
if (idState && typeof idState.value === 'string' && ObjectId.isValid(idState.value)) {
|
if (idState && typeof idState.value === 'string' && ObjectId.isValid(idState.value)) {
|
||||||
duplicateRowState[key].value = new ObjectId().toHexString()
|
duplicateRowState[key].value = newNestedFieldID
|
||||||
duplicateRowState[key].initialValue = new ObjectId().toHexString()
|
duplicateRowState[key].initialValue = newNestedFieldID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,7 +172,7 @@ export function fieldReducer(state: FormState, action: FieldAction): FormState {
|
|||||||
if (Object.keys(duplicateRowState).length > 0) {
|
if (Object.keys(duplicateRowState).length > 0) {
|
||||||
// Add new object containing subfield names to unflattenedRows array
|
// Add new object containing subfield names to unflattenedRows array
|
||||||
rows.splice(rowIndex + 1, 0, duplicateRowState)
|
rows.splice(rowIndex + 1, 0, duplicateRowState)
|
||||||
rowsMetadata.splice(rowIndex + 1, 0, duplicateRowMetadata)
|
rowsWithDuplicate.splice(rowIndex + 1, 0, newRow)
|
||||||
}
|
}
|
||||||
|
|
||||||
const newState = {
|
const newState = {
|
||||||
@@ -179,7 +181,7 @@ export function fieldReducer(state: FormState, action: FieldAction): FormState {
|
|||||||
[path]: {
|
[path]: {
|
||||||
...state[path],
|
...state[path],
|
||||||
disableFormData: true,
|
disableFormData: true,
|
||||||
rows: rowsMetadata,
|
rows: rowsWithDuplicate,
|
||||||
value: rows.length,
|
value: rows.length,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import type { CollectionConfig } from 'payload'
|
import type { CollectionConfig } from 'payload'
|
||||||
|
|
||||||
import { lexicalEditor } from '@payloadcms/richtext-lexical'
|
|
||||||
|
|
||||||
export const postsSlug = 'posts'
|
export const postsSlug = 'posts'
|
||||||
|
|
||||||
export const PostsCollection: CollectionConfig = {
|
export const PostsCollection: CollectionConfig = {
|
||||||
@@ -15,11 +13,14 @@ export const PostsCollection: CollectionConfig = {
|
|||||||
type: 'text',
|
type: 'text',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'content',
|
name: 'array',
|
||||||
type: 'richText',
|
type: 'array',
|
||||||
editor: lexicalEditor({
|
fields: [
|
||||||
features: ({ defaultFeatures }) => [...defaultFeatures],
|
{
|
||||||
}),
|
name: 'title',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,21 +126,12 @@ export interface UserAuthOperations {
|
|||||||
export interface Post {
|
export interface Post {
|
||||||
id: string;
|
id: string;
|
||||||
title?: string | null;
|
title?: string | null;
|
||||||
content?: {
|
array?:
|
||||||
root: {
|
| {
|
||||||
type: string;
|
title?: string | null;
|
||||||
children: {
|
id?: string | null;
|
||||||
type: string;
|
}[]
|
||||||
version: number;
|
| null;
|
||||||
[k: string]: unknown;
|
|
||||||
}[];
|
|
||||||
direction: ('ltr' | 'rtl') | null;
|
|
||||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
|
||||||
indent: number;
|
|
||||||
version: number;
|
|
||||||
};
|
|
||||||
[k: string]: unknown;
|
|
||||||
} | null;
|
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
@@ -279,7 +270,12 @@ export interface PayloadMigration {
|
|||||||
*/
|
*/
|
||||||
export interface PostsSelect<T extends boolean = true> {
|
export interface PostsSelect<T extends boolean = true> {
|
||||||
title?: T;
|
title?: T;
|
||||||
content?: T;
|
array?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
title?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,14 @@ import type { BrowserContext, Page } from '@playwright/test'
|
|||||||
|
|
||||||
import { expect, test } from '@playwright/test'
|
import { expect, test } from '@playwright/test'
|
||||||
import { copyPasteField } from 'helpers/e2e/copyPasteField.js'
|
import { copyPasteField } from 'helpers/e2e/copyPasteField.js'
|
||||||
import { addArrayRowBelow, duplicateArrayRow } from 'helpers/e2e/fields/array/index.js'
|
import { duplicateArrayRow } from 'helpers/e2e/fields/array/index.js'
|
||||||
import { addBlock, openBlocksDrawer, reorderBlocks } from 'helpers/e2e/fields/blocks/index.js'
|
import {
|
||||||
|
addBlock,
|
||||||
|
addBlockBelow,
|
||||||
|
duplicateBlock,
|
||||||
|
openBlocksDrawer,
|
||||||
|
reorderBlocks,
|
||||||
|
} from 'helpers/e2e/fields/blocks/index.js'
|
||||||
import { scrollEntirePage } from 'helpers/e2e/scrollEntirePage.js'
|
import { scrollEntirePage } from 'helpers/e2e/scrollEntirePage.js'
|
||||||
import { toggleBlockOrArrayRow } from 'helpers/e2e/toggleCollapsible.js'
|
import { toggleBlockOrArrayRow } from 'helpers/e2e/toggleCollapsible.js'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
@@ -127,22 +133,13 @@ describe('Block fields', () => {
|
|||||||
test('should open blocks drawer from block row and add below', async () => {
|
test('should open blocks drawer from block row and add below', async () => {
|
||||||
await page.goto(url.create)
|
await page.goto(url.create)
|
||||||
|
|
||||||
await addArrayRowBelow(page, { fieldName: 'blocks' })
|
await addBlockBelow(page, { fieldName: 'blocks', blockToSelect: 'Content' })
|
||||||
|
|
||||||
const blocksDrawer = page.locator('[id^=drawer_1_blocks-drawer-]')
|
|
||||||
await expect(blocksDrawer).toBeVisible()
|
|
||||||
|
|
||||||
// select the first block in the drawer
|
|
||||||
const firstBlockSelector = blocksDrawer
|
|
||||||
.locator('.blocks-drawer__blocks .blocks-drawer__block')
|
|
||||||
.first()
|
|
||||||
|
|
||||||
await expect(firstBlockSelector).toContainText('Content')
|
|
||||||
await firstBlockSelector.click()
|
|
||||||
|
|
||||||
// ensure the block was inserted beneath the first in the rows
|
// ensure the block was inserted beneath the first in the rows
|
||||||
const addedRow = page.locator('#field-blocks #blocks-row-1')
|
const addedRow = page.locator('#field-blocks #blocks-row-1')
|
||||||
|
|
||||||
await expect(addedRow).toBeVisible()
|
await expect(addedRow).toBeVisible()
|
||||||
|
|
||||||
await expect(addedRow.locator('.blocks-field__block-header')).toHaveText(
|
await expect(addedRow.locator('.blocks-field__block-header')).toHaveText(
|
||||||
'Custom Block Label: Content 02',
|
'Custom Block Label: Content 02',
|
||||||
) // went from `Number` to `Content`
|
) // went from `Number` to `Content`
|
||||||
@@ -151,19 +148,17 @@ describe('Block fields', () => {
|
|||||||
test('should duplicate block', async () => {
|
test('should duplicate block', async () => {
|
||||||
await page.goto(url.create)
|
await page.goto(url.create)
|
||||||
|
|
||||||
await duplicateArrayRow(page, { fieldName: 'blocks' })
|
const { rowCount } = await duplicateBlock(page, { fieldName: 'blocks' })
|
||||||
|
|
||||||
const blocks = page.locator('#field-blocks > .blocks-field__rows > div')
|
expect(rowCount).toEqual(5)
|
||||||
expect(await blocks.count()).toEqual(5)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should save when duplicating subblocks', async () => {
|
test('should save when duplicating subblocks', async () => {
|
||||||
await page.goto(url.create)
|
await page.goto(url.create)
|
||||||
|
|
||||||
await duplicateArrayRow(page, { fieldName: 'blocks', rowIndex: 2 })
|
const { rowCount } = await duplicateBlock(page, { fieldName: 'blocks', rowIndex: 2 })
|
||||||
|
|
||||||
const blocks = page.locator('#field-blocks > .blocks-field__rows > div')
|
expect(rowCount).toEqual(5)
|
||||||
expect(await blocks.count()).toEqual(5)
|
|
||||||
|
|
||||||
await page.click('#action-save')
|
await page.click('#action-save')
|
||||||
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
|
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ export interface Config {
|
|||||||
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
||||||
};
|
};
|
||||||
db: {
|
db: {
|
||||||
defaultIDType: number;
|
defaultIDType: string;
|
||||||
};
|
};
|
||||||
globals: {};
|
globals: {};
|
||||||
globalsSelect: {};
|
globalsSelect: {};
|
||||||
@@ -215,7 +215,7 @@ export interface LocalizedTextReference2 {
|
|||||||
* via the `definition` "users".
|
* via the `definition` "users".
|
||||||
*/
|
*/
|
||||||
export interface User {
|
export interface User {
|
||||||
id: number;
|
id: string;
|
||||||
canViewConditionalField?: boolean | null;
|
canViewConditionalField?: boolean | null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
@@ -240,7 +240,7 @@ export interface User {
|
|||||||
* via the `definition` "select-versions-fields".
|
* via the `definition` "select-versions-fields".
|
||||||
*/
|
*/
|
||||||
export interface SelectVersionsField {
|
export interface SelectVersionsField {
|
||||||
id: number;
|
id: string;
|
||||||
hasMany?: ('a' | 'b' | 'c' | 'd')[] | null;
|
hasMany?: ('a' | 'b' | 'c' | 'd')[] | null;
|
||||||
array?:
|
array?:
|
||||||
| {
|
| {
|
||||||
@@ -265,7 +265,7 @@ export interface SelectVersionsField {
|
|||||||
* via the `definition` "array-fields".
|
* via the `definition` "array-fields".
|
||||||
*/
|
*/
|
||||||
export interface ArrayField {
|
export interface ArrayField {
|
||||||
id: number;
|
id: string;
|
||||||
title?: string | null;
|
title?: string | null;
|
||||||
items: {
|
items: {
|
||||||
text: string;
|
text: string;
|
||||||
@@ -369,7 +369,7 @@ export interface ArrayField {
|
|||||||
* via the `definition` "block-fields".
|
* via the `definition` "block-fields".
|
||||||
*/
|
*/
|
||||||
export interface BlockField {
|
export interface BlockField {
|
||||||
id: number;
|
id: string;
|
||||||
blocks: (ContentBlock | NoBlockname | NumberBlock | SubBlocksBlock | TabsBlock)[];
|
blocks: (ContentBlock | NoBlockname | NumberBlock | SubBlocksBlock | TabsBlock)[];
|
||||||
duplicate: (ContentBlock | NoBlockname | NumberBlock | SubBlocksBlock | TabsBlock)[];
|
duplicate: (ContentBlock | NoBlockname | NumberBlock | SubBlocksBlock | TabsBlock)[];
|
||||||
collapsedByDefaultBlocks: (
|
collapsedByDefaultBlocks: (
|
||||||
@@ -500,7 +500,7 @@ export interface BlockField {
|
|||||||
| null;
|
| null;
|
||||||
relationshipBlocks?:
|
relationshipBlocks?:
|
||||||
| {
|
| {
|
||||||
relationship?: (number | null) | TextField;
|
relationship?: (string | null) | TextField;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'relationships';
|
blockType: 'relationships';
|
||||||
@@ -697,7 +697,7 @@ export interface LocalizedTabsBlock {
|
|||||||
* via the `definition` "text-fields".
|
* via the `definition` "text-fields".
|
||||||
*/
|
*/
|
||||||
export interface TextField {
|
export interface TextField {
|
||||||
id: number;
|
id: string;
|
||||||
text: string;
|
text: string;
|
||||||
hiddenTextField?: string | null;
|
hiddenTextField?: string | null;
|
||||||
/**
|
/**
|
||||||
@@ -749,7 +749,7 @@ export interface TextField {
|
|||||||
* via the `definition` "checkbox-fields".
|
* via the `definition` "checkbox-fields".
|
||||||
*/
|
*/
|
||||||
export interface CheckboxField {
|
export interface CheckboxField {
|
||||||
id: number;
|
id: string;
|
||||||
checkbox: boolean;
|
checkbox: boolean;
|
||||||
checkboxNotRequired?: boolean | null;
|
checkboxNotRequired?: boolean | null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
@@ -760,7 +760,7 @@ export interface CheckboxField {
|
|||||||
* via the `definition` "code-fields".
|
* via the `definition` "code-fields".
|
||||||
*/
|
*/
|
||||||
export interface CodeField {
|
export interface CodeField {
|
||||||
id: number;
|
id: string;
|
||||||
javascript?: string | null;
|
javascript?: string | null;
|
||||||
typescript?: string | null;
|
typescript?: string | null;
|
||||||
json?: string | null;
|
json?: string | null;
|
||||||
@@ -775,7 +775,7 @@ export interface CodeField {
|
|||||||
* via the `definition` "collapsible-fields".
|
* via the `definition` "collapsible-fields".
|
||||||
*/
|
*/
|
||||||
export interface CollapsibleField {
|
export interface CollapsibleField {
|
||||||
id: number;
|
id: string;
|
||||||
text: string;
|
text: string;
|
||||||
group: {
|
group: {
|
||||||
textWithinGroup?: string | null;
|
textWithinGroup?: string | null;
|
||||||
@@ -808,7 +808,7 @@ export interface CollapsibleField {
|
|||||||
* via the `definition` "conditional-logic".
|
* via the `definition` "conditional-logic".
|
||||||
*/
|
*/
|
||||||
export interface ConditionalLogic {
|
export interface ConditionalLogic {
|
||||||
id: number;
|
id: string;
|
||||||
text: string;
|
text: string;
|
||||||
toggleField?: boolean | null;
|
toggleField?: boolean | null;
|
||||||
fieldWithDocIDCondition?: string | null;
|
fieldWithDocIDCondition?: string | null;
|
||||||
@@ -922,7 +922,7 @@ export interface CustomRowId {
|
|||||||
* via the `definition` "date-fields".
|
* via the `definition` "date-fields".
|
||||||
*/
|
*/
|
||||||
export interface DateField {
|
export interface DateField {
|
||||||
id: number;
|
id: string;
|
||||||
default: string;
|
default: string;
|
||||||
timeOnly?: string | null;
|
timeOnly?: string | null;
|
||||||
timeOnlyWithMiliseconds?: string | null;
|
timeOnlyWithMiliseconds?: string | null;
|
||||||
@@ -967,7 +967,7 @@ export interface DateField {
|
|||||||
* via the `definition` "email-fields".
|
* via the `definition` "email-fields".
|
||||||
*/
|
*/
|
||||||
export interface EmailField {
|
export interface EmailField {
|
||||||
id: number;
|
id: string;
|
||||||
email: string;
|
email: string;
|
||||||
localizedEmail?: string | null;
|
localizedEmail?: string | null;
|
||||||
emailWithAutocomplete?: string | null;
|
emailWithAutocomplete?: string | null;
|
||||||
@@ -992,7 +992,7 @@ export interface EmailField {
|
|||||||
* via the `definition` "radio-fields".
|
* via the `definition` "radio-fields".
|
||||||
*/
|
*/
|
||||||
export interface RadioField {
|
export interface RadioField {
|
||||||
id: number;
|
id: string;
|
||||||
radio?: ('one' | 'two' | 'three') | null;
|
radio?: ('one' | 'two' | 'three') | null;
|
||||||
radioWithJsxLabelOption?: ('one' | 'two' | 'three') | null;
|
radioWithJsxLabelOption?: ('one' | 'two' | 'three') | null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
@@ -1003,7 +1003,7 @@ export interface RadioField {
|
|||||||
* via the `definition` "group-fields".
|
* via the `definition` "group-fields".
|
||||||
*/
|
*/
|
||||||
export interface GroupField {
|
export interface GroupField {
|
||||||
id: number;
|
id: string;
|
||||||
/**
|
/**
|
||||||
* This is a group.
|
* This is a group.
|
||||||
*/
|
*/
|
||||||
@@ -1085,22 +1085,22 @@ export interface GroupField {
|
|||||||
select?: ('one' | 'two')[] | null;
|
select?: ('one' | 'two')[] | null;
|
||||||
};
|
};
|
||||||
localizedGroupRel?: {
|
localizedGroupRel?: {
|
||||||
email?: (number | null) | EmailField;
|
email?: (string | null) | EmailField;
|
||||||
};
|
};
|
||||||
localizedGroupManyRel?: {
|
localizedGroupManyRel?: {
|
||||||
email?: (number | EmailField)[] | null;
|
email?: (string | EmailField)[] | null;
|
||||||
};
|
};
|
||||||
localizedGroupPolyRel?: {
|
localizedGroupPolyRel?: {
|
||||||
email?: {
|
email?: {
|
||||||
relationTo: 'email-fields';
|
relationTo: 'email-fields';
|
||||||
value: number | EmailField;
|
value: string | EmailField;
|
||||||
} | null;
|
} | null;
|
||||||
};
|
};
|
||||||
localizedGroupPolyHasManyRel?: {
|
localizedGroupPolyHasManyRel?: {
|
||||||
email?:
|
email?:
|
||||||
| {
|
| {
|
||||||
relationTo: 'email-fields';
|
relationTo: 'email-fields';
|
||||||
value: number | EmailField;
|
value: string | EmailField;
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
};
|
};
|
||||||
@@ -1154,30 +1154,30 @@ export interface RowField {
|
|||||||
* via the `definition` "indexed-fields".
|
* via the `definition` "indexed-fields".
|
||||||
*/
|
*/
|
||||||
export interface IndexedField {
|
export interface IndexedField {
|
||||||
id: number;
|
id: string;
|
||||||
text: string;
|
text: string;
|
||||||
uniqueText?: string | null;
|
uniqueText?: string | null;
|
||||||
uniqueRelationship?: (number | null) | TextField;
|
uniqueRelationship?: (string | null) | TextField;
|
||||||
uniqueHasManyRelationship?: (number | TextField)[] | null;
|
uniqueHasManyRelationship?: (string | TextField)[] | null;
|
||||||
uniqueHasManyRelationship_2?: (number | TextField)[] | null;
|
uniqueHasManyRelationship_2?: (string | TextField)[] | null;
|
||||||
uniquePolymorphicRelationship?: {
|
uniquePolymorphicRelationship?: {
|
||||||
relationTo: 'text-fields';
|
relationTo: 'text-fields';
|
||||||
value: number | TextField;
|
value: string | TextField;
|
||||||
} | null;
|
} | null;
|
||||||
uniquePolymorphicRelationship_2?: {
|
uniquePolymorphicRelationship_2?: {
|
||||||
relationTo: 'text-fields';
|
relationTo: 'text-fields';
|
||||||
value: number | TextField;
|
value: string | TextField;
|
||||||
} | null;
|
} | null;
|
||||||
uniqueHasManyPolymorphicRelationship?:
|
uniqueHasManyPolymorphicRelationship?:
|
||||||
| {
|
| {
|
||||||
relationTo: 'text-fields';
|
relationTo: 'text-fields';
|
||||||
value: number | TextField;
|
value: string | TextField;
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
uniqueHasManyPolymorphicRelationship_2?:
|
uniqueHasManyPolymorphicRelationship_2?:
|
||||||
| {
|
| {
|
||||||
relationTo: 'text-fields';
|
relationTo: 'text-fields';
|
||||||
value: number | TextField;
|
value: string | TextField;
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
uniqueRequiredText: string;
|
uniqueRequiredText: string;
|
||||||
@@ -1213,7 +1213,7 @@ export interface IndexedField {
|
|||||||
* via the `definition` "json-fields".
|
* via the `definition` "json-fields".
|
||||||
*/
|
*/
|
||||||
export interface JsonField {
|
export interface JsonField {
|
||||||
id: number;
|
id: string;
|
||||||
json?: {
|
json?: {
|
||||||
array?: {
|
array?: {
|
||||||
object?: {
|
object?: {
|
||||||
@@ -1254,7 +1254,7 @@ export interface JsonField {
|
|||||||
* via the `definition` "number-fields".
|
* via the `definition` "number-fields".
|
||||||
*/
|
*/
|
||||||
export interface NumberField {
|
export interface NumberField {
|
||||||
id: number;
|
id: string;
|
||||||
number?: number | null;
|
number?: number | null;
|
||||||
min?: number | null;
|
min?: number | null;
|
||||||
max?: number | null;
|
max?: number | null;
|
||||||
@@ -1289,7 +1289,7 @@ export interface NumberField {
|
|||||||
* via the `definition` "point-fields".
|
* via the `definition` "point-fields".
|
||||||
*/
|
*/
|
||||||
export interface PointField {
|
export interface PointField {
|
||||||
id: number;
|
id: string;
|
||||||
/**
|
/**
|
||||||
* @minItems 2
|
* @minItems 2
|
||||||
* @maxItems 2
|
* @maxItems 2
|
||||||
@@ -1320,83 +1320,83 @@ export interface PointField {
|
|||||||
* via the `definition` "relationship-fields".
|
* via the `definition` "relationship-fields".
|
||||||
*/
|
*/
|
||||||
export interface RelationshipField {
|
export interface RelationshipField {
|
||||||
id: number;
|
id: string;
|
||||||
text?: string | null;
|
text?: string | null;
|
||||||
relationship:
|
relationship:
|
||||||
| {
|
| {
|
||||||
relationTo: 'text-fields';
|
relationTo: 'text-fields';
|
||||||
value: number | TextField;
|
value: string | TextField;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
relationTo: 'array-fields';
|
relationTo: 'array-fields';
|
||||||
value: number | ArrayField;
|
value: string | ArrayField;
|
||||||
};
|
};
|
||||||
relationHasManyPolymorphic?:
|
relationHasManyPolymorphic?:
|
||||||
| (
|
| (
|
||||||
| {
|
| {
|
||||||
relationTo: 'text-fields';
|
relationTo: 'text-fields';
|
||||||
value: number | TextField;
|
value: string | TextField;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
relationTo: 'array-fields';
|
relationTo: 'array-fields';
|
||||||
value: number | ArrayField;
|
value: string | ArrayField;
|
||||||
}
|
}
|
||||||
)[]
|
)[]
|
||||||
| null;
|
| null;
|
||||||
relationToSelf?: (number | null) | RelationshipField;
|
relationToSelf?: (string | null) | RelationshipField;
|
||||||
relationToSelfSelectOnly?: (number | null) | RelationshipField;
|
relationToSelfSelectOnly?: (string | null) | RelationshipField;
|
||||||
relationWithAllowCreateToFalse?: (number | null) | User;
|
relationWithAllowCreateToFalse?: (string | null) | User;
|
||||||
relationWithAllowEditToFalse?: (number | null) | User;
|
relationWithAllowEditToFalse?: (string | null) | User;
|
||||||
relationWithDynamicDefault?: (number | null) | User;
|
relationWithDynamicDefault?: (string | null) | User;
|
||||||
relationHasManyWithDynamicDefault?: {
|
relationHasManyWithDynamicDefault?: {
|
||||||
relationTo: 'users';
|
relationTo: 'users';
|
||||||
value: number | User;
|
value: string | User;
|
||||||
} | null;
|
} | null;
|
||||||
relationshipWithMin?: (number | TextField)[] | null;
|
relationshipWithMin?: (string | TextField)[] | null;
|
||||||
relationshipWithMax?: (number | TextField)[] | null;
|
relationshipWithMax?: (string | TextField)[] | null;
|
||||||
relationshipHasMany?: (number | TextField)[] | null;
|
relationshipHasMany?: (string | TextField)[] | null;
|
||||||
array?:
|
array?:
|
||||||
| {
|
| {
|
||||||
relationship?: (number | null) | TextField;
|
relationship?: (string | null) | TextField;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
relationshipWithMinRows?:
|
relationshipWithMinRows?:
|
||||||
| {
|
| {
|
||||||
relationTo: 'text-fields';
|
relationTo: 'text-fields';
|
||||||
value: number | TextField;
|
value: string | TextField;
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
relationToRow?: (string | null) | RowField;
|
relationToRow?: (string | null) | RowField;
|
||||||
relationToRowMany?: (string | RowField)[] | null;
|
relationToRowMany?: (string | RowField)[] | null;
|
||||||
relationshipDrawer?: (number | null) | TextField;
|
relationshipDrawer?: (string | null) | TextField;
|
||||||
relationshipDrawerReadOnly?: (number | null) | TextField;
|
relationshipDrawerReadOnly?: (string | null) | TextField;
|
||||||
polymorphicRelationshipDrawer?:
|
polymorphicRelationshipDrawer?:
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'text-fields';
|
relationTo: 'text-fields';
|
||||||
value: number | TextField;
|
value: string | TextField;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'array-fields';
|
relationTo: 'array-fields';
|
||||||
value: number | ArrayField;
|
value: string | ArrayField;
|
||||||
} | null);
|
} | null);
|
||||||
relationshipDrawerHasMany?: (number | TextField)[] | null;
|
relationshipDrawerHasMany?: (string | TextField)[] | null;
|
||||||
relationshipDrawerHasManyPolymorphic?:
|
relationshipDrawerHasManyPolymorphic?:
|
||||||
| (
|
| (
|
||||||
| {
|
| {
|
||||||
relationTo: 'text-fields';
|
relationTo: 'text-fields';
|
||||||
value: number | TextField;
|
value: string | TextField;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
relationTo: 'array-fields';
|
relationTo: 'array-fields';
|
||||||
value: number | ArrayField;
|
value: string | ArrayField;
|
||||||
}
|
}
|
||||||
)[]
|
)[]
|
||||||
| null;
|
| null;
|
||||||
relationshipDrawerWithAllowCreateFalse?: (number | null) | TextField;
|
relationshipDrawerWithAllowCreateFalse?: (string | null) | TextField;
|
||||||
relationshipDrawerWithFilterOptions?: {
|
relationshipDrawerWithFilterOptions?: {
|
||||||
relationTo: 'text-fields';
|
relationTo: 'text-fields';
|
||||||
value: number | TextField;
|
value: string | TextField;
|
||||||
} | null;
|
} | null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
@@ -1406,7 +1406,7 @@ export interface RelationshipField {
|
|||||||
* via the `definition` "select-fields".
|
* via the `definition` "select-fields".
|
||||||
*/
|
*/
|
||||||
export interface SelectField {
|
export interface SelectField {
|
||||||
id: number;
|
id: string;
|
||||||
select?: ('one' | 'two' | 'three') | null;
|
select?: ('one' | 'two' | 'three') | null;
|
||||||
selectReadOnly?: ('one' | 'two' | 'three') | null;
|
selectReadOnly?: ('one' | 'two' | 'three') | null;
|
||||||
selectHasMany?: ('one' | 'two' | 'three' | 'four' | 'five' | 'six')[] | null;
|
selectHasMany?: ('one' | 'two' | 'three' | 'four' | 'five' | 'six')[] | null;
|
||||||
@@ -1436,7 +1436,7 @@ export interface SelectField {
|
|||||||
* via the `definition` "tabs-fields-2".
|
* via the `definition` "tabs-fields-2".
|
||||||
*/
|
*/
|
||||||
export interface TabsFields2 {
|
export interface TabsFields2 {
|
||||||
id: number;
|
id: string;
|
||||||
tabsInArray?:
|
tabsInArray?:
|
||||||
| {
|
| {
|
||||||
text?: string | null;
|
text?: string | null;
|
||||||
@@ -1454,7 +1454,7 @@ export interface TabsFields2 {
|
|||||||
* via the `definition` "tabs-fields".
|
* via the `definition` "tabs-fields".
|
||||||
*/
|
*/
|
||||||
export interface TabsField {
|
export interface TabsField {
|
||||||
id: number;
|
id: string;
|
||||||
/**
|
/**
|
||||||
* This should not collapse despite there being many tabs pushing the main fields open.
|
* This should not collapse despite there being many tabs pushing the main fields open.
|
||||||
*/
|
*/
|
||||||
@@ -1556,9 +1556,9 @@ export interface TabWithName {
|
|||||||
* via the `definition` "uploads".
|
* via the `definition` "uploads".
|
||||||
*/
|
*/
|
||||||
export interface Upload {
|
export interface Upload {
|
||||||
id: number;
|
id: string;
|
||||||
text?: string | null;
|
text?: string | null;
|
||||||
media?: (number | null) | Upload;
|
media?: (string | null) | Upload;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
@@ -1576,9 +1576,9 @@ export interface Upload {
|
|||||||
* via the `definition` "uploads2".
|
* via the `definition` "uploads2".
|
||||||
*/
|
*/
|
||||||
export interface Uploads2 {
|
export interface Uploads2 {
|
||||||
id: number;
|
id: string;
|
||||||
text?: string | null;
|
text?: string | null;
|
||||||
media?: (number | null) | Uploads2;
|
media?: (string | null) | Uploads2;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
@@ -1596,8 +1596,8 @@ export interface Uploads2 {
|
|||||||
* via the `definition` "uploads3".
|
* via the `definition` "uploads3".
|
||||||
*/
|
*/
|
||||||
export interface Uploads3 {
|
export interface Uploads3 {
|
||||||
id: number;
|
id: string;
|
||||||
media?: (number | null) | Uploads3;
|
media?: (string | null) | Uploads3;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
@@ -1615,9 +1615,9 @@ export interface Uploads3 {
|
|||||||
* via the `definition` "uploads-multi".
|
* via the `definition` "uploads-multi".
|
||||||
*/
|
*/
|
||||||
export interface UploadsMulti {
|
export interface UploadsMulti {
|
||||||
id: number;
|
id: string;
|
||||||
text?: string | null;
|
text?: string | null;
|
||||||
media?: (number | Upload)[] | null;
|
media?: (string | Upload)[] | null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
@@ -1626,16 +1626,16 @@ export interface UploadsMulti {
|
|||||||
* via the `definition` "uploads-poly".
|
* via the `definition` "uploads-poly".
|
||||||
*/
|
*/
|
||||||
export interface UploadsPoly {
|
export interface UploadsPoly {
|
||||||
id: number;
|
id: string;
|
||||||
text?: string | null;
|
text?: string | null;
|
||||||
media?:
|
media?:
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'uploads';
|
relationTo: 'uploads';
|
||||||
value: number | Upload;
|
value: string | Upload;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'uploads2';
|
relationTo: 'uploads2';
|
||||||
value: number | Uploads2;
|
value: string | Uploads2;
|
||||||
} | null);
|
} | null);
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
@@ -1645,17 +1645,17 @@ export interface UploadsPoly {
|
|||||||
* via the `definition` "uploads-multi-poly".
|
* via the `definition` "uploads-multi-poly".
|
||||||
*/
|
*/
|
||||||
export interface UploadsMultiPoly {
|
export interface UploadsMultiPoly {
|
||||||
id: number;
|
id: string;
|
||||||
text?: string | null;
|
text?: string | null;
|
||||||
media?:
|
media?:
|
||||||
| (
|
| (
|
||||||
| {
|
| {
|
||||||
relationTo: 'uploads';
|
relationTo: 'uploads';
|
||||||
value: number | Upload;
|
value: string | Upload;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
relationTo: 'uploads2';
|
relationTo: 'uploads2';
|
||||||
value: number | Uploads2;
|
value: string | Uploads2;
|
||||||
}
|
}
|
||||||
)[]
|
)[]
|
||||||
| null;
|
| null;
|
||||||
@@ -1667,11 +1667,11 @@ export interface UploadsMultiPoly {
|
|||||||
* via the `definition` "uploads-restricted".
|
* via the `definition` "uploads-restricted".
|
||||||
*/
|
*/
|
||||||
export interface UploadsRestricted {
|
export interface UploadsRestricted {
|
||||||
id: number;
|
id: string;
|
||||||
text?: string | null;
|
text?: string | null;
|
||||||
uploadWithoutRestriction?: (number | null) | Upload;
|
uploadWithoutRestriction?: (string | null) | Upload;
|
||||||
uploadWithAllowCreateFalse?: (number | null) | Upload;
|
uploadWithAllowCreateFalse?: (string | null) | Upload;
|
||||||
uploadMultipleWithAllowCreateFalse?: (number | Upload)[] | null;
|
uploadMultipleWithAllowCreateFalse?: (string | Upload)[] | null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
@@ -1680,7 +1680,7 @@ export interface UploadsRestricted {
|
|||||||
* via the `definition` "ui-fields".
|
* via the `definition` "ui-fields".
|
||||||
*/
|
*/
|
||||||
export interface UiField {
|
export interface UiField {
|
||||||
id: number;
|
id: string;
|
||||||
text: string;
|
text: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
@@ -1690,39 +1690,39 @@ export interface UiField {
|
|||||||
* via the `definition` "payload-locked-documents".
|
* via the `definition` "payload-locked-documents".
|
||||||
*/
|
*/
|
||||||
export interface PayloadLockedDocument {
|
export interface PayloadLockedDocument {
|
||||||
id: number;
|
id: string;
|
||||||
document?:
|
document?:
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'users';
|
relationTo: 'users';
|
||||||
value: number | User;
|
value: string | User;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'select-versions-fields';
|
relationTo: 'select-versions-fields';
|
||||||
value: number | SelectVersionsField;
|
value: string | SelectVersionsField;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'array-fields';
|
relationTo: 'array-fields';
|
||||||
value: number | ArrayField;
|
value: string | ArrayField;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'block-fields';
|
relationTo: 'block-fields';
|
||||||
value: number | BlockField;
|
value: string | BlockField;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'checkbox-fields';
|
relationTo: 'checkbox-fields';
|
||||||
value: number | CheckboxField;
|
value: string | CheckboxField;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'code-fields';
|
relationTo: 'code-fields';
|
||||||
value: number | CodeField;
|
value: string | CodeField;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'collapsible-fields';
|
relationTo: 'collapsible-fields';
|
||||||
value: number | CollapsibleField;
|
value: string | CollapsibleField;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'conditional-logic';
|
relationTo: 'conditional-logic';
|
||||||
value: number | ConditionalLogic;
|
value: string | ConditionalLogic;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'custom-id';
|
relationTo: 'custom-id';
|
||||||
@@ -1730,27 +1730,27 @@ export interface PayloadLockedDocument {
|
|||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'custom-tab-id';
|
relationTo: 'custom-tab-id';
|
||||||
value: number | CustomTabId;
|
value: string | CustomTabId;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'custom-row-id';
|
relationTo: 'custom-row-id';
|
||||||
value: number | CustomRowId;
|
value: string | CustomRowId;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'date-fields';
|
relationTo: 'date-fields';
|
||||||
value: number | DateField;
|
value: string | DateField;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'email-fields';
|
relationTo: 'email-fields';
|
||||||
value: number | EmailField;
|
value: string | EmailField;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'radio-fields';
|
relationTo: 'radio-fields';
|
||||||
value: number | RadioField;
|
value: string | RadioField;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'group-fields';
|
relationTo: 'group-fields';
|
||||||
value: number | GroupField;
|
value: string | GroupField;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'row-fields';
|
relationTo: 'row-fields';
|
||||||
@@ -1758,76 +1758,76 @@ export interface PayloadLockedDocument {
|
|||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'indexed-fields';
|
relationTo: 'indexed-fields';
|
||||||
value: number | IndexedField;
|
value: string | IndexedField;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'json-fields';
|
relationTo: 'json-fields';
|
||||||
value: number | JsonField;
|
value: string | JsonField;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'number-fields';
|
relationTo: 'number-fields';
|
||||||
value: number | NumberField;
|
value: string | NumberField;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'point-fields';
|
relationTo: 'point-fields';
|
||||||
value: number | PointField;
|
value: string | PointField;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'relationship-fields';
|
relationTo: 'relationship-fields';
|
||||||
value: number | RelationshipField;
|
value: string | RelationshipField;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'select-fields';
|
relationTo: 'select-fields';
|
||||||
value: number | SelectField;
|
value: string | SelectField;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'tabs-fields-2';
|
relationTo: 'tabs-fields-2';
|
||||||
value: number | TabsFields2;
|
value: string | TabsFields2;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'tabs-fields';
|
relationTo: 'tabs-fields';
|
||||||
value: number | TabsField;
|
value: string | TabsField;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'text-fields';
|
relationTo: 'text-fields';
|
||||||
value: number | TextField;
|
value: string | TextField;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'uploads';
|
relationTo: 'uploads';
|
||||||
value: number | Upload;
|
value: string | Upload;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'uploads2';
|
relationTo: 'uploads2';
|
||||||
value: number | Uploads2;
|
value: string | Uploads2;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'uploads3';
|
relationTo: 'uploads3';
|
||||||
value: number | Uploads3;
|
value: string | Uploads3;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'uploads-multi';
|
relationTo: 'uploads-multi';
|
||||||
value: number | UploadsMulti;
|
value: string | UploadsMulti;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'uploads-poly';
|
relationTo: 'uploads-poly';
|
||||||
value: number | UploadsPoly;
|
value: string | UploadsPoly;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'uploads-multi-poly';
|
relationTo: 'uploads-multi-poly';
|
||||||
value: number | UploadsMultiPoly;
|
value: string | UploadsMultiPoly;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'uploads-restricted';
|
relationTo: 'uploads-restricted';
|
||||||
value: number | UploadsRestricted;
|
value: string | UploadsRestricted;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'ui-fields';
|
relationTo: 'ui-fields';
|
||||||
value: number | UiField;
|
value: string | UiField;
|
||||||
} | null);
|
} | null);
|
||||||
globalSlug?: string | null;
|
globalSlug?: string | null;
|
||||||
user: {
|
user: {
|
||||||
relationTo: 'users';
|
relationTo: 'users';
|
||||||
value: number | User;
|
value: string | User;
|
||||||
};
|
};
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
@@ -1837,10 +1837,10 @@ export interface PayloadLockedDocument {
|
|||||||
* via the `definition` "payload-preferences".
|
* via the `definition` "payload-preferences".
|
||||||
*/
|
*/
|
||||||
export interface PayloadPreference {
|
export interface PayloadPreference {
|
||||||
id: number;
|
id: string;
|
||||||
user: {
|
user: {
|
||||||
relationTo: 'users';
|
relationTo: 'users';
|
||||||
value: number | User;
|
value: string | User;
|
||||||
};
|
};
|
||||||
key?: string | null;
|
key?: string | null;
|
||||||
value?:
|
value?:
|
||||||
@@ -1860,7 +1860,7 @@ export interface PayloadPreference {
|
|||||||
* via the `definition` "payload-migrations".
|
* via the `definition` "payload-migrations".
|
||||||
*/
|
*/
|
||||||
export interface PayloadMigration {
|
export interface PayloadMigration {
|
||||||
id: number;
|
id: string;
|
||||||
name?: string | null;
|
name?: string | null;
|
||||||
batch?: number | null;
|
batch?: number | null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
export const ArrayRowLabel = () => {
|
export const ArrayRowLabel = (props) => {
|
||||||
return <p id="custom-array-row-label">This is a custom component</p>
|
return (
|
||||||
|
<p data-id={props.value[props?.rowNumber - 1]?.id} id="custom-array-row-label">
|
||||||
|
This is a custom component
|
||||||
|
</p>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,12 @@ import { expect, test } from '@playwright/test'
|
|||||||
import { assertElementStaysVisible } from 'helpers/e2e/assertElementStaysVisible.js'
|
import { assertElementStaysVisible } from 'helpers/e2e/assertElementStaysVisible.js'
|
||||||
import { assertNetworkRequests } from 'helpers/e2e/assertNetworkRequests.js'
|
import { assertNetworkRequests } from 'helpers/e2e/assertNetworkRequests.js'
|
||||||
import { assertRequestBody } from 'helpers/e2e/assertRequestBody.js'
|
import { assertRequestBody } from 'helpers/e2e/assertRequestBody.js'
|
||||||
import { addArrayRowAsync, removeArrayRow } from 'helpers/e2e/fields/array/index.js'
|
import {
|
||||||
|
addArrayRow,
|
||||||
|
addArrayRowAsync,
|
||||||
|
duplicateArrayRow,
|
||||||
|
removeArrayRow,
|
||||||
|
} from 'helpers/e2e/fields/array/index.js'
|
||||||
import { addBlock } from 'helpers/e2e/fields/blocks/index.js'
|
import { addBlock } from 'helpers/e2e/fields/blocks/index.js'
|
||||||
import { waitForAutoSaveToRunAndComplete } from 'helpers/e2e/waitForAutoSaveToRunAndComplete.js'
|
import { waitForAutoSaveToRunAndComplete } from 'helpers/e2e/waitForAutoSaveToRunAndComplete.js'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
@@ -454,6 +459,34 @@ test.describe('Form State', () => {
|
|||||||
await expect(computedTitleField).toHaveValue('Test Title 2')
|
await expect(computedTitleField).toHaveValue('Test Title 2')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('array and block rows and maintain consistent row IDs across duplication', async () => {
|
||||||
|
await page.goto(postsUrl.create)
|
||||||
|
await addArrayRow(page, { fieldName: 'array' })
|
||||||
|
|
||||||
|
const row0 = page.locator('#field-array #array-row-0')
|
||||||
|
|
||||||
|
await expect(row0.locator('#custom-array-row-label')).toHaveAttribute('data-id')
|
||||||
|
|
||||||
|
await expect(row0.locator('#field-array__0__id')).toHaveValue(
|
||||||
|
(await row0.locator('#custom-array-row-label').getAttribute('data-id'))!,
|
||||||
|
)
|
||||||
|
|
||||||
|
await duplicateArrayRow(page, { fieldName: 'array' })
|
||||||
|
|
||||||
|
const row1 = page.locator('#field-array #array-row-1')
|
||||||
|
|
||||||
|
await expect(row1.locator('#custom-array-row-label')).toHaveAttribute('data-id')
|
||||||
|
|
||||||
|
await expect(row1.locator('#custom-array-row-label')).not.toHaveAttribute(
|
||||||
|
'data-id',
|
||||||
|
(await row0.locator('#custom-array-row-label').getAttribute('data-id'))!,
|
||||||
|
)
|
||||||
|
|
||||||
|
await expect(row1.locator('#field-array__1__id')).toHaveValue(
|
||||||
|
(await row1.locator('#custom-array-row-label').getAttribute('data-id'))!,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
describe('Throttled tests', () => {
|
describe('Throttled tests', () => {
|
||||||
let cdpSession: CDPSession
|
let cdpSession: CDPSession
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { Locator, Page } from 'playwright'
|
import type { Locator, Page } from 'playwright'
|
||||||
|
|
||||||
import { wait } from 'payload/shared'
|
import { wait } from 'payload/shared'
|
||||||
|
import { expect } from 'playwright/test'
|
||||||
|
|
||||||
import { openArrayRowActions } from './openArrayRowActions.js'
|
import { openArrayRowActions } from './openArrayRowActions.js'
|
||||||
|
|
||||||
@@ -18,10 +19,12 @@ export const addArrayRow = async (
|
|||||||
page: Page,
|
page: Page,
|
||||||
{ fieldName }: Omit<Parameters<typeof openArrayRowActions>[1], 'rowIndex'>,
|
{ fieldName }: Omit<Parameters<typeof openArrayRowActions>[1], 'rowIndex'>,
|
||||||
) => {
|
) => {
|
||||||
|
const rowLocator = page.locator(`#field-${fieldName} .array-field__row`)
|
||||||
|
const numberOfPrevRows = await rowLocator.count()
|
||||||
|
|
||||||
await addArrayRowAsync(page, fieldName)
|
await addArrayRowAsync(page, fieldName)
|
||||||
|
|
||||||
// TODO: test the array row has appeared
|
expect(await rowLocator.count()).toBe(numberOfPrevRows + 1)
|
||||||
await wait(300)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -31,16 +34,19 @@ export const addArrayRowBelow = async (
|
|||||||
page: Page,
|
page: Page,
|
||||||
{ fieldName, rowIndex = 0 }: Parameters<typeof openArrayRowActions>[1],
|
{ fieldName, rowIndex = 0 }: Parameters<typeof openArrayRowActions>[1],
|
||||||
): Promise<{ popupContentLocator: Locator; rowActionsButtonLocator: Locator }> => {
|
): Promise<{ popupContentLocator: Locator; rowActionsButtonLocator: Locator }> => {
|
||||||
|
const rowLocator = page.locator(`#field-${fieldName} .array-field__row`)
|
||||||
|
const numberOfPrevRows = await rowLocator.count()
|
||||||
|
|
||||||
const { popupContentLocator, rowActionsButtonLocator } = await openArrayRowActions(page, {
|
const { popupContentLocator, rowActionsButtonLocator } = await openArrayRowActions(page, {
|
||||||
fieldName,
|
fieldName,
|
||||||
rowIndex,
|
rowIndex,
|
||||||
})
|
})
|
||||||
|
|
||||||
const addBelowButton = popupContentLocator.locator('.array-actions__action.array-actions__add')
|
await popupContentLocator.locator('.array-actions__action.array-actions__add').click()
|
||||||
|
|
||||||
await addBelowButton.click()
|
await expect(rowLocator).toHaveCount(numberOfPrevRows + 1)
|
||||||
|
|
||||||
// TODO: test the array row has appeared
|
// TODO: test the array row has appeared in the _correct position_ (immediately below the original row)
|
||||||
await wait(300)
|
await wait(300)
|
||||||
|
|
||||||
return { popupContentLocator, rowActionsButtonLocator }
|
return { popupContentLocator, rowActionsButtonLocator }
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import type { Locator, Page } from 'playwright'
|
import type { Locator, Page } from 'playwright'
|
||||||
|
|
||||||
|
import { expect } from 'playwright/test'
|
||||||
|
|
||||||
import { openArrayRowActions } from './openArrayRowActions.js'
|
import { openArrayRowActions } from './openArrayRowActions.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,6 +14,9 @@ export const duplicateArrayRow = async (
|
|||||||
popupContentLocator: Locator
|
popupContentLocator: Locator
|
||||||
rowActionsButtonLocator: Locator
|
rowActionsButtonLocator: Locator
|
||||||
}> => {
|
}> => {
|
||||||
|
const rowLocator = page.locator(`#field-${fieldName} .array-field__row`)
|
||||||
|
const numberOfPrevRows = await rowLocator.count()
|
||||||
|
|
||||||
const { popupContentLocator, rowActionsButtonLocator } = await openArrayRowActions(page, {
|
const { popupContentLocator, rowActionsButtonLocator } = await openArrayRowActions(page, {
|
||||||
fieldName,
|
fieldName,
|
||||||
rowIndex,
|
rowIndex,
|
||||||
@@ -19,7 +24,9 @@ export const duplicateArrayRow = async (
|
|||||||
|
|
||||||
await popupContentLocator.locator('.array-actions__action.array-actions__duplicate').click()
|
await popupContentLocator.locator('.array-actions__action.array-actions__duplicate').click()
|
||||||
|
|
||||||
// TODO: test the array row has been duplicated
|
expect(await rowLocator.count()).toBe(numberOfPrevRows + 1)
|
||||||
|
|
||||||
|
// TODO: test the array row's field input values have been duplicated as well
|
||||||
|
|
||||||
return { popupContentLocator, rowActionsButtonLocator }
|
return { popupContentLocator, rowActionsButtonLocator }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import type { Locator, Page } from 'playwright'
|
import type { Locator, Page } from 'playwright'
|
||||||
|
|
||||||
|
import { expect } from 'playwright/test'
|
||||||
|
|
||||||
import { openArrayRowActions } from './openArrayRowActions.js'
|
import { openArrayRowActions } from './openArrayRowActions.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,6 +14,9 @@ export const removeArrayRow = async (
|
|||||||
popupContentLocator: Locator
|
popupContentLocator: Locator
|
||||||
rowActionsButtonLocator: Locator
|
rowActionsButtonLocator: Locator
|
||||||
}> => {
|
}> => {
|
||||||
|
const rowLocator = page.locator(`#field-${fieldName} .array-field__row`)
|
||||||
|
const numberOfPrevRows = await rowLocator.count()
|
||||||
|
|
||||||
const { popupContentLocator, rowActionsButtonLocator } = await openArrayRowActions(page, {
|
const { popupContentLocator, rowActionsButtonLocator } = await openArrayRowActions(page, {
|
||||||
fieldName,
|
fieldName,
|
||||||
rowIndex,
|
rowIndex,
|
||||||
@@ -19,8 +24,10 @@ export const removeArrayRow = async (
|
|||||||
|
|
||||||
await popupContentLocator.locator('.array-actions__action.array-actions__remove').click()
|
await popupContentLocator.locator('.array-actions__action.array-actions__remove').click()
|
||||||
|
|
||||||
// TODO: test the array row has been removed
|
expect(await rowLocator.count()).toBe(numberOfPrevRows - 1)
|
||||||
// another row may have been moved into its place, though
|
|
||||||
|
// TODO: test the array row has been removed in the _correct position_ (original row index)
|
||||||
|
// another row may have been moved into its place, need to ensure the test accounts for this fact
|
||||||
|
|
||||||
return { popupContentLocator, rowActionsButtonLocator }
|
return { popupContentLocator, rowActionsButtonLocator }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,30 @@
|
|||||||
import type { Page } from '@playwright/test'
|
import type { Locator, Page } from '@playwright/test'
|
||||||
|
|
||||||
import { expect } from '@playwright/test'
|
import { expect } from '@playwright/test'
|
||||||
import { exactText } from 'helpers.js'
|
import { exactText } from 'helpers.js'
|
||||||
|
|
||||||
|
import { openArrayRowActions } from '../array/openArrayRowActions.js'
|
||||||
import { openBlocksDrawer } from './openBlocksDrawer.js'
|
import { openBlocksDrawer } from './openBlocksDrawer.js'
|
||||||
|
|
||||||
|
const selectBlockFromDrawer = async ({
|
||||||
|
blocksDrawer,
|
||||||
|
blockToSelect,
|
||||||
|
}: {
|
||||||
|
blocksDrawer: Locator
|
||||||
|
blockToSelect: string
|
||||||
|
}) => {
|
||||||
|
const blockCard = blocksDrawer.locator('.blocks-drawer__block .thumbnail-card__label', {
|
||||||
|
hasText: blockToSelect,
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(blockCard).toBeVisible()
|
||||||
|
|
||||||
|
await blocksDrawer.getByRole('button', { name: exactText(blockToSelect) }).click()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a block to the end of the blocks array using the primary "Add Block" button.
|
||||||
|
*/
|
||||||
export const addBlock = async ({
|
export const addBlock = async ({
|
||||||
page,
|
page,
|
||||||
fieldName = 'blocks',
|
fieldName = 'blocks',
|
||||||
@@ -17,15 +37,66 @@ export const addBlock = async ({
|
|||||||
fieldName: string
|
fieldName: string
|
||||||
page: Page
|
page: Page
|
||||||
}) => {
|
}) => {
|
||||||
|
const rowLocator = page.locator(
|
||||||
|
`#field-${fieldName} > .blocks-field__rows > div > .blocks-field__row`,
|
||||||
|
)
|
||||||
|
|
||||||
|
const numberOfPrevRows = await rowLocator.count()
|
||||||
|
|
||||||
const blocksDrawer = await openBlocksDrawer({ page, fieldName })
|
const blocksDrawer = await openBlocksDrawer({ page, fieldName })
|
||||||
|
|
||||||
const blockCard = blocksDrawer.locator('.blocks-drawer__block .thumbnail-card__label', {
|
await selectBlockFromDrawer({
|
||||||
hasText: blockToSelect,
|
blocksDrawer,
|
||||||
|
blockToSelect,
|
||||||
})
|
})
|
||||||
|
|
||||||
await expect(blockCard).toBeVisible()
|
await expect(rowLocator).toHaveCount(numberOfPrevRows + 1)
|
||||||
|
|
||||||
await blocksDrawer.getByRole('button', { name: exactText(blockToSelect) }).click()
|
|
||||||
|
|
||||||
// expect to see the block on the page
|
// expect to see the block on the page
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like `addBlock`, but inserts the block at the specified index using the row actions menu.
|
||||||
|
*/
|
||||||
|
export const addBlockBelow = async (
|
||||||
|
page: Page,
|
||||||
|
{
|
||||||
|
fieldName = 'blocks',
|
||||||
|
blockToSelect = 'Block',
|
||||||
|
rowIndex = 0,
|
||||||
|
}: {
|
||||||
|
/**
|
||||||
|
* The name of the block to select from the blocks drawer.
|
||||||
|
*/
|
||||||
|
blockToSelect: string
|
||||||
|
fieldName: string
|
||||||
|
/**
|
||||||
|
* The index at which to insert the block.
|
||||||
|
*/
|
||||||
|
rowIndex?: number
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const rowLocator = page.locator(
|
||||||
|
`#field-${fieldName} > .blocks-field__rows > div > .blocks-field__row`,
|
||||||
|
)
|
||||||
|
|
||||||
|
const numberOfPrevRows = await rowLocator.count()
|
||||||
|
|
||||||
|
const { popupContentLocator, rowActionsButtonLocator } = await openArrayRowActions(page, {
|
||||||
|
fieldName,
|
||||||
|
rowIndex,
|
||||||
|
})
|
||||||
|
|
||||||
|
await popupContentLocator.locator('.array-actions__action.array-actions__add').click()
|
||||||
|
|
||||||
|
const blocksDrawer = page.locator('[id^=drawer_1_blocks-drawer-]')
|
||||||
|
|
||||||
|
await selectBlockFromDrawer({
|
||||||
|
blocksDrawer,
|
||||||
|
blockToSelect,
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(rowLocator).toHaveCount(numberOfPrevRows + 1)
|
||||||
|
|
||||||
|
return { popupContentLocator, rowActionsButtonLocator }
|
||||||
|
}
|
||||||
|
|||||||
37
test/helpers/e2e/fields/blocks/duplicateBlock.ts
Normal file
37
test/helpers/e2e/fields/blocks/duplicateBlock.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import type { Locator, Page } from 'playwright'
|
||||||
|
|
||||||
|
import { expect } from 'playwright/test'
|
||||||
|
|
||||||
|
import { openArrayRowActions } from '../array/openArrayRowActions.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duplicates the block row at the specified index.
|
||||||
|
*/
|
||||||
|
export const duplicateBlock = async (
|
||||||
|
page: Page,
|
||||||
|
{ fieldName, rowIndex = 0 }: Parameters<typeof openArrayRowActions>[1],
|
||||||
|
): Promise<{
|
||||||
|
popupContentLocator: Locator
|
||||||
|
rowActionsButtonLocator: Locator
|
||||||
|
rowCount: number
|
||||||
|
}> => {
|
||||||
|
const rowLocator = page.locator(
|
||||||
|
`#field-${fieldName} > .blocks-field__rows > div > .blocks-field__row`,
|
||||||
|
)
|
||||||
|
|
||||||
|
const numberOfPrevRows = await rowLocator.count()
|
||||||
|
|
||||||
|
const { popupContentLocator, rowActionsButtonLocator } = await openArrayRowActions(page, {
|
||||||
|
fieldName,
|
||||||
|
rowIndex,
|
||||||
|
})
|
||||||
|
|
||||||
|
await popupContentLocator.locator('.array-actions__action.array-actions__duplicate').click()
|
||||||
|
const numberOfCurrentRows = await rowLocator.count()
|
||||||
|
|
||||||
|
expect(numberOfCurrentRows).toBe(numberOfPrevRows + 1)
|
||||||
|
|
||||||
|
// TODO: test the array row's field input values have been duplicated as well
|
||||||
|
|
||||||
|
return { popupContentLocator, rowActionsButtonLocator, rowCount: numberOfCurrentRows }
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
export { addBlock } from './addBlock.js'
|
export { addBlock, addBlockBelow } from './addBlock.js'
|
||||||
|
export { duplicateBlock } from './duplicateBlock.js'
|
||||||
export { openBlocksDrawer } from './openBlocksDrawer.js'
|
export { openBlocksDrawer } from './openBlocksDrawer.js'
|
||||||
export { removeAllBlocks } from './removeAllBlocks.js'
|
export { removeAllBlocks } from './removeAllBlocks.js'
|
||||||
export { reorderBlocks } from './reorderBlocks.js'
|
export { reorderBlocks } from './reorderBlocks.js'
|
||||||
|
|||||||
Reference in New Issue
Block a user