Skip to content

Commit 7e2a126

Browse files
authored
fix(ui): radio fields do not reliably trigger admin.condition re-evaluation on sibling fields (#16056)
Fixes #15929
1 parent 67b5dc3 commit 7e2a126

4 files changed

Lines changed: 187 additions & 362 deletions

File tree

packages/ui/src/forms/Form/fieldReducer.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,10 +427,13 @@ export function fieldReducer(state: FormState, action: FieldAction): FormState {
427427
}
428428

429429
// reset `isModified` in all other fields
430+
// Use destructuring instead of delete to avoid mutating field objects
431+
// shared with the previous state (shallow copy shares references)
430432
if ('value' in action) {
431433
for (const [path, field] of Object.entries(newState)) {
432434
if (path !== action.path && 'isModified' in field) {
433-
delete newState[path].isModified
435+
const { isModified: _, ...fieldWithoutIsModified } = field
436+
newState[path] = fieldWithoutIsModified as typeof field
434437
}
435438
}
436439
}

test/fields/collections/ConditionalLogic/e2e.spec.ts

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,23 @@ import type { BrowserContext, Page } from '@playwright/test'
22

33
import { expect, test } from '@playwright/test'
44
import { addArrayRow } from '__helpers/e2e/fields/array/index.js'
5+
import { addBlock } from '__helpers/e2e/fields/blocks/index.js'
56
import path from 'path'
67
import { fileURLToPath } from 'url'
78

89
import type { PayloadTestSDK } from '../../../__helpers/shared/sdk/index.js'
910
import type { Config } from '../../payload-types.js'
1011

12+
import { assertNetworkRequests } from '../../../__helpers/e2e/assertNetworkRequests.js'
1113
import {
1214
ensureCompilationIsDone,
1315
initPageConsoleErrorCatch,
1416
saveDocAndAssert,
1517
// throttleTest,
1618
} from '../../../__helpers/e2e/helpers.js'
1719
import { AdminUrlUtil } from '../../../__helpers/shared/adminUrlUtil.js'
18-
import { assertNetworkRequests } from '../../../__helpers/e2e/assertNetworkRequests.js'
19-
import { initPayloadE2ENoConfig } from '../../../__helpers/shared/initPayloadE2ENoConfig.js'
2020
import { reInitializeDB } from '../../../__helpers/shared/clearAndSeed/reInitializeDB.js'
21+
import { initPayloadE2ENoConfig } from '../../../__helpers/shared/initPayloadE2ENoConfig.js'
2122
import { RESTClient } from '../../../__helpers/shared/rest.js'
2223
import { TEST_TIMEOUT_LONG } from '../../../playwright.config.js'
2324
import { conditionalLogicSlug } from '../../slugs.js'
@@ -287,4 +288,36 @@ describe('Conditional Logic', () => {
287288

288289
await expect(fieldWithOperationCondition).toBeHidden()
289290
})
291+
292+
test('should toggle conditional field when radio changes inside a block', async () => {
293+
await page.goto(url.create)
294+
295+
await addBlock({
296+
page,
297+
fieldName: 'blocksWithRadioCondition',
298+
blockToSelect: 'Block With Radio Condition',
299+
})
300+
301+
// Conditional field should be hidden (defaultValue: 'hide')
302+
const conditionalField = page.locator(
303+
'#field-blocksWithRadioCondition__0__conditionalTextField',
304+
)
305+
await expect(conditionalField).toBeHidden()
306+
307+
// Click "Show" radio and wait for form state response
308+
const showRadio = page.locator('label:has(input[id*="radioTrigger-show"])')
309+
await showRadio.click()
310+
311+
await expect(async () => {
312+
await expect(conditionalField).toBeVisible()
313+
}).toPass()
314+
315+
// Click "Hide" radio
316+
const hideRadio = page.locator('label:has(input[id*="radioTrigger-hide"])')
317+
await hideRadio.click()
318+
319+
await expect(async () => {
320+
await expect(conditionalField).toBeHidden()
321+
}).toPass()
322+
})
290323
})

test/fields/collections/ConditionalLogic/index.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,35 @@ const ConditionalLogic: CollectionConfig = {
202202
},
203203
],
204204
},
205+
{
206+
name: 'blocksWithRadioCondition',
207+
type: 'blocks',
208+
blocks: [
209+
{
210+
slug: 'blockWithRadioCondition',
211+
dbName: 'bRadioCon',
212+
fields: [
213+
{
214+
dbName: 'rTrig',
215+
name: 'radioTrigger',
216+
type: 'radio',
217+
defaultValue: 'hide',
218+
options: [
219+
{ label: 'Show', value: 'show' },
220+
{ label: 'Hide', value: 'hide' },
221+
],
222+
},
223+
{
224+
name: 'conditionalTextField',
225+
type: 'text',
226+
admin: {
227+
condition: (_data, siblingData) => siblingData?.radioTrigger === 'show',
228+
},
229+
},
230+
],
231+
},
232+
],
233+
},
205234
{
206235
name: 'arrayOne',
207236
type: 'array',

0 commit comments

Comments
 (0)