From a11586811e4a7b2ba4860fca6a9f57f4e60b5e8d Mon Sep 17 00:00:00 2001 From: Alessio Gravili Date: Tue, 2 Sep 2025 18:14:07 -0700 Subject: [PATCH] fix(ui): field.admin.condition data attribute missing document ID when document is being edited (#13676) Fixes https://github.com/payloadcms/payload/issues/10379 During form state requests, the passed `data` did not have access to the document ID. This was because the data we use came from the client, passed as an argument. The client did not pass data that included the document ID. --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211203844178567 --- packages/ui/src/utilities/buildFormState.ts | 6 + .../collections/ConditionalLogic/e2e.spec.ts | 48 ++++ .../collections/ConditionalLogic/index.ts | 7 + test/fields/payload-types.ts | 228 +++++++++--------- 4 files changed, 176 insertions(+), 113 deletions(-) diff --git a/packages/ui/src/utilities/buildFormState.ts b/packages/ui/src/utilities/buildFormState.ts index 149a50a2ea..0b4a341280 100644 --- a/packages/ui/src/utilities/buildFormState.ts +++ b/packages/ui/src/utilities/buildFormState.ts @@ -204,6 +204,12 @@ export const buildFormState = async ( ? fieldOrEntityConfig.fields : [fieldOrEntityConfig] + // Ensure data.id is present during form state requests, where the data + // is passed from the client as an argument, without the ID + if (!data.id && id) { + data.id = id + } + const formStateResult = await fieldSchemasToFormState({ id, clientFieldSchemaMap: clientSchemaMap, diff --git a/test/fields/collections/ConditionalLogic/e2e.spec.ts b/test/fields/collections/ConditionalLogic/e2e.spec.ts index b2d94a9690..d2added145 100644 --- a/test/fields/collections/ConditionalLogic/e2e.spec.ts +++ b/test/fields/collections/ConditionalLogic/e2e.spec.ts @@ -15,6 +15,7 @@ import { // throttleTest, } from '../../../helpers.js' import { AdminUrlUtil } from '../../../helpers/adminUrlUtil.js' +import { assertNetworkRequests } from '../../../helpers/e2e/assertNetworkRequests.js' import { initPayloadE2ENoConfig } from '../../../helpers/initPayloadE2ENoConfig.js' import { reInitializeDB } from '../../../helpers/reInitializeDB.js' import { RESTClient } from '../../../helpers/rest.js' @@ -99,6 +100,53 @@ describe('Conditional Logic', () => { expect(true).toBe(true) }) + test('ensure conditions receive document ID during form state request', async () => { + await page.goto(url.create) + + const fieldOnlyVisibleIfNoID = page.locator('#field-fieldWithDocIDCondition') + + await expect(fieldOnlyVisibleIfNoID).toBeVisible() + + const textField = page.locator('#field-text') + await assertNetworkRequests( + page, + '/admin/collections/conditional-logic', + async () => { + await textField.fill('some text') + }, + { + minimumNumberOfRequests: 1, + }, + ) + + await assertNetworkRequests( + page, + '/api/conditional-logic', + async () => { + await saveDocAndAssert(page) + }, + { + minimumNumberOfRequests: 1, + }, + ) + + await expect(fieldOnlyVisibleIfNoID).toBeHidden() + + // Fill text and wait for form state request to come back + await assertNetworkRequests( + page, + '/admin/collections/conditional-logic', + async () => { + await textField.fill('updated text') + }, + { + minimumNumberOfRequests: 1, + }, + ) + + await expect(fieldOnlyVisibleIfNoID).toBeHidden() + }) + test('should conditionally render custom field that renders a Payload field', async () => { await page.goto(url.create) diff --git a/test/fields/collections/ConditionalLogic/index.ts b/test/fields/collections/ConditionalLogic/index.ts index 96af0df38c..d33aa547b4 100644 --- a/test/fields/collections/ConditionalLogic/index.ts +++ b/test/fields/collections/ConditionalLogic/index.ts @@ -17,6 +17,13 @@ const ConditionalLogic: CollectionConfig = { name: 'toggleField', type: 'checkbox', }, + { + name: 'fieldWithDocIDCondition', + type: 'text', + admin: { + condition: ({ id }) => !id, + }, + }, { name: 'fieldWithCondition', type: 'text', diff --git a/test/fields/payload-types.ts b/test/fields/payload-types.ts index ea9f013b6e..9a2b3ff50e 100644 --- a/test/fields/payload-types.ts +++ b/test/fields/payload-types.ts @@ -149,7 +149,7 @@ export interface Config { 'payload-migrations': PayloadMigrationsSelect | PayloadMigrationsSelect; }; db: { - defaultIDType: string; + defaultIDType: number; }; globals: {}; globalsSelect: {}; @@ -215,7 +215,7 @@ export interface LocalizedTextReference2 { * via the `definition` "users". */ export interface User { - id: string; + id: number; canViewConditionalField?: boolean | null; updatedAt: string; createdAt: string; @@ -240,7 +240,7 @@ export interface User { * via the `definition` "select-versions-fields". */ export interface SelectVersionsField { - id: string; + id: number; hasMany?: ('a' | 'b' | 'c' | 'd')[] | null; array?: | { @@ -265,7 +265,7 @@ export interface SelectVersionsField { * via the `definition` "array-fields". */ export interface ArrayField { - id: string; + id: number; title?: string | null; items: { text: string; @@ -369,7 +369,7 @@ export interface ArrayField { * via the `definition` "block-fields". */ export interface BlockField { - id: string; + id: number; blocks: (ContentBlock | NoBlockname | NumberBlock | SubBlocksBlock | TabsBlock)[]; duplicate: (ContentBlock | NoBlockname | NumberBlock | SubBlocksBlock | TabsBlock)[]; collapsedByDefaultBlocks: ( @@ -500,7 +500,7 @@ export interface BlockField { | null; relationshipBlocks?: | { - relationship?: (string | null) | TextField; + relationship?: (number | null) | TextField; id?: string | null; blockName?: string | null; blockType: 'relationships'; @@ -697,7 +697,7 @@ export interface LocalizedTabsBlock { * via the `definition` "text-fields". */ export interface TextField { - id: string; + id: number; text: string; hiddenTextField?: string | null; /** @@ -749,7 +749,7 @@ export interface TextField { * via the `definition` "checkbox-fields". */ export interface CheckboxField { - id: string; + id: number; checkbox: boolean; checkboxNotRequired?: boolean | null; updatedAt: string; @@ -760,7 +760,7 @@ export interface CheckboxField { * via the `definition` "code-fields". */ export interface CodeField { - id: string; + id: number; javascript?: string | null; typescript?: string | null; json?: string | null; @@ -775,7 +775,7 @@ export interface CodeField { * via the `definition` "collapsible-fields". */ export interface CollapsibleField { - id: string; + id: number; text: string; group: { textWithinGroup?: string | null; @@ -808,9 +808,10 @@ export interface CollapsibleField { * via the `definition` "conditional-logic". */ export interface ConditionalLogic { - id: string; + id: number; text: string; toggleField?: boolean | null; + fieldWithDocIDCondition?: string | null; fieldWithCondition?: string | null; fieldWithOperationCondition?: string | null; customFieldWithField?: string | null; @@ -921,7 +922,7 @@ export interface CustomRowId { * via the `definition` "date-fields". */ export interface DateField { - id: string; + id: number; default: string; timeOnly?: string | null; timeOnlyWithMiliseconds?: string | null; @@ -966,7 +967,7 @@ export interface DateField { * via the `definition` "email-fields". */ export interface EmailField { - id: string; + id: number; email: string; localizedEmail?: string | null; emailWithAutocomplete?: string | null; @@ -991,7 +992,7 @@ export interface EmailField { * via the `definition` "radio-fields". */ export interface RadioField { - id: string; + id: number; radio?: ('one' | 'two' | 'three') | null; radioWithJsxLabelOption?: ('one' | 'two' | 'three') | null; updatedAt: string; @@ -1002,7 +1003,7 @@ export interface RadioField { * via the `definition` "group-fields". */ export interface GroupField { - id: string; + id: number; /** * This is a group. */ @@ -1084,22 +1085,22 @@ export interface GroupField { select?: ('one' | 'two')[] | null; }; localizedGroupRel?: { - email?: (string | null) | EmailField; + email?: (number | null) | EmailField; }; localizedGroupManyRel?: { - email?: (string | EmailField)[] | null; + email?: (number | EmailField)[] | null; }; localizedGroupPolyRel?: { email?: { relationTo: 'email-fields'; - value: string | EmailField; + value: number | EmailField; } | null; }; localizedGroupPolyHasManyRel?: { email?: | { relationTo: 'email-fields'; - value: string | EmailField; + value: number | EmailField; }[] | null; }; @@ -1153,30 +1154,30 @@ export interface RowField { * via the `definition` "indexed-fields". */ export interface IndexedField { - id: string; + id: number; text: string; uniqueText?: string | null; - uniqueRelationship?: (string | null) | TextField; - uniqueHasManyRelationship?: (string | TextField)[] | null; - uniqueHasManyRelationship_2?: (string | TextField)[] | null; + uniqueRelationship?: (number | null) | TextField; + uniqueHasManyRelationship?: (number | TextField)[] | null; + uniqueHasManyRelationship_2?: (number | TextField)[] | null; uniquePolymorphicRelationship?: { relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; } | null; uniquePolymorphicRelationship_2?: { relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; } | null; uniqueHasManyPolymorphicRelationship?: | { relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; }[] | null; uniqueHasManyPolymorphicRelationship_2?: | { relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; }[] | null; uniqueRequiredText: string; @@ -1212,7 +1213,7 @@ export interface IndexedField { * via the `definition` "json-fields". */ export interface JsonField { - id: string; + id: number; json?: { array?: { object?: { @@ -1253,7 +1254,7 @@ export interface JsonField { * via the `definition` "number-fields". */ export interface NumberField { - id: string; + id: number; number?: number | null; min?: number | null; max?: number | null; @@ -1288,7 +1289,7 @@ export interface NumberField { * via the `definition` "point-fields". */ export interface PointField { - id: string; + id: number; /** * @minItems 2 * @maxItems 2 @@ -1319,83 +1320,83 @@ export interface PointField { * via the `definition` "relationship-fields". */ export interface RelationshipField { - id: string; + id: number; text?: string | null; relationship: | { relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; } | { relationTo: 'array-fields'; - value: string | ArrayField; + value: number | ArrayField; }; relationHasManyPolymorphic?: | ( | { relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; } | { relationTo: 'array-fields'; - value: string | ArrayField; + value: number | ArrayField; } )[] | null; - relationToSelf?: (string | null) | RelationshipField; - relationToSelfSelectOnly?: (string | null) | RelationshipField; - relationWithAllowCreateToFalse?: (string | null) | User; - relationWithAllowEditToFalse?: (string | null) | User; - relationWithDynamicDefault?: (string | null) | User; + relationToSelf?: (number | null) | RelationshipField; + relationToSelfSelectOnly?: (number | null) | RelationshipField; + relationWithAllowCreateToFalse?: (number | null) | User; + relationWithAllowEditToFalse?: (number | null) | User; + relationWithDynamicDefault?: (number | null) | User; relationHasManyWithDynamicDefault?: { relationTo: 'users'; - value: string | User; + value: number | User; } | null; - relationshipWithMin?: (string | TextField)[] | null; - relationshipWithMax?: (string | TextField)[] | null; - relationshipHasMany?: (string | TextField)[] | null; + relationshipWithMin?: (number | TextField)[] | null; + relationshipWithMax?: (number | TextField)[] | null; + relationshipHasMany?: (number | TextField)[] | null; array?: | { - relationship?: (string | null) | TextField; + relationship?: (number | null) | TextField; id?: string | null; }[] | null; relationshipWithMinRows?: | { relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; }[] | null; relationToRow?: (string | null) | RowField; relationToRowMany?: (string | RowField)[] | null; - relationshipDrawer?: (string | null) | TextField; - relationshipDrawerReadOnly?: (string | null) | TextField; + relationshipDrawer?: (number | null) | TextField; + relationshipDrawerReadOnly?: (number | null) | TextField; polymorphicRelationshipDrawer?: | ({ relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; } | null) | ({ relationTo: 'array-fields'; - value: string | ArrayField; + value: number | ArrayField; } | null); - relationshipDrawerHasMany?: (string | TextField)[] | null; + relationshipDrawerHasMany?: (number | TextField)[] | null; relationshipDrawerHasManyPolymorphic?: | ( | { relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; } | { relationTo: 'array-fields'; - value: string | ArrayField; + value: number | ArrayField; } )[] | null; - relationshipDrawerWithAllowCreateFalse?: (string | null) | TextField; + relationshipDrawerWithAllowCreateFalse?: (number | null) | TextField; relationshipDrawerWithFilterOptions?: { relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; } | null; updatedAt: string; createdAt: string; @@ -1405,7 +1406,7 @@ export interface RelationshipField { * via the `definition` "select-fields". */ export interface SelectField { - id: string; + id: number; select?: ('one' | 'two' | 'three') | null; selectReadOnly?: ('one' | 'two' | 'three') | null; selectHasMany?: ('one' | 'two' | 'three' | 'four' | 'five' | 'six')[] | null; @@ -1435,7 +1436,7 @@ export interface SelectField { * via the `definition` "tabs-fields-2". */ export interface TabsFields2 { - id: string; + id: number; tabsInArray?: | { text?: string | null; @@ -1453,7 +1454,7 @@ export interface TabsFields2 { * via the `definition` "tabs-fields". */ export interface TabsField { - id: string; + id: number; /** * This should not collapse despite there being many tabs pushing the main fields open. */ @@ -1555,9 +1556,9 @@ export interface TabWithName { * via the `definition` "uploads". */ export interface Upload { - id: string; + id: number; text?: string | null; - media?: (string | null) | Upload; + media?: (number | null) | Upload; updatedAt: string; createdAt: string; url?: string | null; @@ -1575,9 +1576,9 @@ export interface Upload { * via the `definition` "uploads2". */ export interface Uploads2 { - id: string; + id: number; text?: string | null; - media?: (string | null) | Uploads2; + media?: (number | null) | Uploads2; updatedAt: string; createdAt: string; url?: string | null; @@ -1595,8 +1596,8 @@ export interface Uploads2 { * via the `definition` "uploads3". */ export interface Uploads3 { - id: string; - media?: (string | null) | Uploads3; + id: number; + media?: (number | null) | Uploads3; updatedAt: string; createdAt: string; url?: string | null; @@ -1614,9 +1615,9 @@ export interface Uploads3 { * via the `definition` "uploads-multi". */ export interface UploadsMulti { - id: string; + id: number; text?: string | null; - media?: (string | Upload)[] | null; + media?: (number | Upload)[] | null; updatedAt: string; createdAt: string; } @@ -1625,16 +1626,16 @@ export interface UploadsMulti { * via the `definition` "uploads-poly". */ export interface UploadsPoly { - id: string; + id: number; text?: string | null; media?: | ({ relationTo: 'uploads'; - value: string | Upload; + value: number | Upload; } | null) | ({ relationTo: 'uploads2'; - value: string | Uploads2; + value: number | Uploads2; } | null); updatedAt: string; createdAt: string; @@ -1644,17 +1645,17 @@ export interface UploadsPoly { * via the `definition` "uploads-multi-poly". */ export interface UploadsMultiPoly { - id: string; + id: number; text?: string | null; media?: | ( | { relationTo: 'uploads'; - value: string | Upload; + value: number | Upload; } | { relationTo: 'uploads2'; - value: string | Uploads2; + value: number | Uploads2; } )[] | null; @@ -1666,11 +1667,11 @@ export interface UploadsMultiPoly { * via the `definition` "uploads-restricted". */ export interface UploadsRestricted { - id: string; + id: number; text?: string | null; - uploadWithoutRestriction?: (string | null) | Upload; - uploadWithAllowCreateFalse?: (string | null) | Upload; - uploadMultipleWithAllowCreateFalse?: (string | Upload)[] | null; + uploadWithoutRestriction?: (number | null) | Upload; + uploadWithAllowCreateFalse?: (number | null) | Upload; + uploadMultipleWithAllowCreateFalse?: (number | Upload)[] | null; updatedAt: string; createdAt: string; } @@ -1679,7 +1680,7 @@ export interface UploadsRestricted { * via the `definition` "ui-fields". */ export interface UiField { - id: string; + id: number; text: string; updatedAt: string; createdAt: string; @@ -1689,39 +1690,39 @@ export interface UiField { * via the `definition` "payload-locked-documents". */ export interface PayloadLockedDocument { - id: string; + id: number; document?: | ({ relationTo: 'users'; - value: string | User; + value: number | User; } | null) | ({ relationTo: 'select-versions-fields'; - value: string | SelectVersionsField; + value: number | SelectVersionsField; } | null) | ({ relationTo: 'array-fields'; - value: string | ArrayField; + value: number | ArrayField; } | null) | ({ relationTo: 'block-fields'; - value: string | BlockField; + value: number | BlockField; } | null) | ({ relationTo: 'checkbox-fields'; - value: string | CheckboxField; + value: number | CheckboxField; } | null) | ({ relationTo: 'code-fields'; - value: string | CodeField; + value: number | CodeField; } | null) | ({ relationTo: 'collapsible-fields'; - value: string | CollapsibleField; + value: number | CollapsibleField; } | null) | ({ relationTo: 'conditional-logic'; - value: string | ConditionalLogic; + value: number | ConditionalLogic; } | null) | ({ relationTo: 'custom-id'; @@ -1729,27 +1730,27 @@ export interface PayloadLockedDocument { } | null) | ({ relationTo: 'custom-tab-id'; - value: string | CustomTabId; + value: number | CustomTabId; } | null) | ({ relationTo: 'custom-row-id'; - value: string | CustomRowId; + value: number | CustomRowId; } | null) | ({ relationTo: 'date-fields'; - value: string | DateField; + value: number | DateField; } | null) | ({ relationTo: 'email-fields'; - value: string | EmailField; + value: number | EmailField; } | null) | ({ relationTo: 'radio-fields'; - value: string | RadioField; + value: number | RadioField; } | null) | ({ relationTo: 'group-fields'; - value: string | GroupField; + value: number | GroupField; } | null) | ({ relationTo: 'row-fields'; @@ -1757,76 +1758,76 @@ export interface PayloadLockedDocument { } | null) | ({ relationTo: 'indexed-fields'; - value: string | IndexedField; + value: number | IndexedField; } | null) | ({ relationTo: 'json-fields'; - value: string | JsonField; + value: number | JsonField; } | null) | ({ relationTo: 'number-fields'; - value: string | NumberField; + value: number | NumberField; } | null) | ({ relationTo: 'point-fields'; - value: string | PointField; + value: number | PointField; } | null) | ({ relationTo: 'relationship-fields'; - value: string | RelationshipField; + value: number | RelationshipField; } | null) | ({ relationTo: 'select-fields'; - value: string | SelectField; + value: number | SelectField; } | null) | ({ relationTo: 'tabs-fields-2'; - value: string | TabsFields2; + value: number | TabsFields2; } | null) | ({ relationTo: 'tabs-fields'; - value: string | TabsField; + value: number | TabsField; } | null) | ({ relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; } | null) | ({ relationTo: 'uploads'; - value: string | Upload; + value: number | Upload; } | null) | ({ relationTo: 'uploads2'; - value: string | Uploads2; + value: number | Uploads2; } | null) | ({ relationTo: 'uploads3'; - value: string | Uploads3; + value: number | Uploads3; } | null) | ({ relationTo: 'uploads-multi'; - value: string | UploadsMulti; + value: number | UploadsMulti; } | null) | ({ relationTo: 'uploads-poly'; - value: string | UploadsPoly; + value: number | UploadsPoly; } | null) | ({ relationTo: 'uploads-multi-poly'; - value: string | UploadsMultiPoly; + value: number | UploadsMultiPoly; } | null) | ({ relationTo: 'uploads-restricted'; - value: string | UploadsRestricted; + value: number | UploadsRestricted; } | null) | ({ relationTo: 'ui-fields'; - value: string | UiField; + value: number | UiField; } | null); globalSlug?: string | null; user: { relationTo: 'users'; - value: string | User; + value: number | User; }; updatedAt: string; createdAt: string; @@ -1836,10 +1837,10 @@ export interface PayloadLockedDocument { * via the `definition` "payload-preferences". */ export interface PayloadPreference { - id: string; + id: number; user: { relationTo: 'users'; - value: string | User; + value: number | User; }; key?: string | null; value?: @@ -1859,7 +1860,7 @@ export interface PayloadPreference { * via the `definition` "payload-migrations". */ export interface PayloadMigration { - id: string; + id: number; name?: string | null; batch?: number | null; updatedAt: string; @@ -2452,6 +2453,7 @@ export interface CollapsibleFieldsSelect { export interface ConditionalLogicSelect { text?: T; toggleField?: T; + fieldWithDocIDCondition?: T; fieldWithCondition?: T; fieldWithOperationCondition?: T; customFieldWithField?: T;