fix(ui): addFieldRow set modified (#9324)
Fixes #9264. When externally updating array or block rows through the `addFieldRow` or `replaceFieldRow` methods, nested rich text fields along with any custom components within them are never rendered. This is because unless the form is explicitly set to modified, as the default array and blocks fields currently do, the newly generated form-state will skip the rendering step. Now, the underlying callbacks themselves automatically set the form to modified to trigger rendering.
This commit is contained in:
@@ -52,6 +52,7 @@ export const ArrayFieldComponent: ArrayFieldClientComponent = (props) => {
|
|||||||
schemaPath: schemaPathFromProps,
|
schemaPath: schemaPathFromProps,
|
||||||
validate,
|
validate,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const schemaPath = schemaPathFromProps ?? name
|
const schemaPath = schemaPathFromProps ?? name
|
||||||
|
|
||||||
const minRows = (minRowsProp ?? required) ? 1 : 0
|
const minRows = (minRowsProp ?? required) ? 1 : 0
|
||||||
@@ -129,13 +130,11 @@ export const ArrayFieldComponent: ArrayFieldClientComponent = (props) => {
|
|||||||
schemaPath,
|
schemaPath,
|
||||||
})
|
})
|
||||||
|
|
||||||
setModified(true)
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
scrollToID(`${path}-row-${rowIndex}`)
|
scrollToID(`${path}-row-${rowIndex}`)
|
||||||
}, 0)
|
}, 0)
|
||||||
},
|
},
|
||||||
[addFieldRow, path, schemaPath, setModified],
|
[addFieldRow, path, schemaPath],
|
||||||
)
|
)
|
||||||
|
|
||||||
const duplicateRow = useCallback(
|
const duplicateRow = useCallback(
|
||||||
|
|||||||
@@ -116,13 +116,11 @@ const BlocksFieldComponent: BlocksFieldClientComponent = (props) => {
|
|||||||
schemaPath,
|
schemaPath,
|
||||||
})
|
})
|
||||||
|
|
||||||
setModified(true)
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
scrollToID(`${path}-row-${rowIndex + 1}`)
|
scrollToID(`${path}-row-${rowIndex + 1}`)
|
||||||
}, 0)
|
}, 0)
|
||||||
},
|
},
|
||||||
[addFieldRow, path, schemaPath, setModified],
|
[addFieldRow, path, schemaPath],
|
||||||
)
|
)
|
||||||
|
|
||||||
const duplicateRow = useCallback(
|
const duplicateRow = useCallback(
|
||||||
|
|||||||
@@ -518,6 +518,8 @@ export const Form: React.FC<FormProps> = (props) => {
|
|||||||
rowIndex,
|
rowIndex,
|
||||||
subFieldState,
|
subFieldState,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
setModified(true)
|
||||||
},
|
},
|
||||||
[dispatchFields, getDataByPath],
|
[dispatchFields, getDataByPath],
|
||||||
)
|
)
|
||||||
@@ -541,6 +543,8 @@ export const Form: React.FC<FormProps> = (props) => {
|
|||||||
rowIndex,
|
rowIndex,
|
||||||
subFieldState,
|
subFieldState,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
setModified(true)
|
||||||
},
|
},
|
||||||
[dispatchFields, getDataByPath],
|
[dispatchFields, getDataByPath],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ export interface Config {
|
|||||||
user: User & {
|
user: User & {
|
||||||
collection: 'users';
|
collection: 'users';
|
||||||
};
|
};
|
||||||
jobs?: {
|
jobs: {
|
||||||
tasks: unknown;
|
tasks: unknown;
|
||||||
workflows?: unknown;
|
workflows: unknown;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export interface UserAuthOperations {
|
export interface UserAuthOperations {
|
||||||
|
|||||||
29
test/fields/collections/Array/AddRowButton.tsx
Normal file
29
test/fields/collections/Array/AddRowButton.tsx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useForm } from '@payloadcms/ui'
|
||||||
|
|
||||||
|
const AddRowButton = () => {
|
||||||
|
const { addFieldRow } = useForm()
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
addFieldRow({
|
||||||
|
path: 'externallyUpdatedArray',
|
||||||
|
schemaPath: 'externallyUpdatedArray',
|
||||||
|
subFieldState: {
|
||||||
|
text: {
|
||||||
|
initialValue: 'Hello, world!',
|
||||||
|
valid: true,
|
||||||
|
value: 'Hello, world!',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button id="updateArrayExternally" onClick={handleClick} type="button">
|
||||||
|
Add Row
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AddRowButton
|
||||||
11
test/fields/collections/Array/CustomField.tsx
Normal file
11
test/fields/collections/Array/CustomField.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import type { TextFieldServerComponent } from 'payload'
|
||||||
|
|
||||||
|
import { TextField } from '@payloadcms/ui'
|
||||||
|
|
||||||
|
export const CustomField: TextFieldServerComponent = ({ clientField, path }) => {
|
||||||
|
return (
|
||||||
|
<div id="custom-field">
|
||||||
|
<TextField field={clientField} path={path as string} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -295,4 +295,10 @@ describe('Array', () => {
|
|||||||
'Updated 3 Array Fields successfully.',
|
'Updated 3 Array Fields successfully.',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should externally update array rows and render custom fields', async () => {
|
||||||
|
await page.goto(url.create)
|
||||||
|
await page.locator('#updateArrayExternally').click()
|
||||||
|
await expect(page.locator('#custom-field')).toBeVisible()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -183,6 +183,30 @@ const ArrayFields: CollectionConfig = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'externallyUpdatedArray',
|
||||||
|
type: 'array',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'customField',
|
||||||
|
type: 'ui',
|
||||||
|
admin: {
|
||||||
|
components: {
|
||||||
|
Field: '/collections/Array/CustomField.js#CustomField',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ui',
|
||||||
|
type: 'ui',
|
||||||
|
admin: {
|
||||||
|
components: {
|
||||||
|
Field: '/collections/Array/AddRowButton.js',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
slug: arrayFieldsSlug,
|
slug: arrayFieldsSlug,
|
||||||
versions: true,
|
versions: true,
|
||||||
|
|||||||
@@ -465,6 +465,11 @@ export interface ArrayField {
|
|||||||
id?: string | null;
|
id?: string | null;
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
|
externallyUpdatedArray?:
|
||||||
|
| {
|
||||||
|
id?: string | null;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
@@ -2084,6 +2089,13 @@ export interface ArrayFieldsSelect<T extends boolean = true> {
|
|||||||
};
|
};
|
||||||
id?: T;
|
id?: T;
|
||||||
};
|
};
|
||||||
|
externallyUpdatedArray?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
customField?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
ui?: T;
|
||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
}
|
}
|
||||||
@@ -3400,6 +3412,6 @@ export interface Auth {
|
|||||||
|
|
||||||
|
|
||||||
declare module 'payload' {
|
declare module 'payload' {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
export interface GeneratedTypes extends Config {}
|
export interface GeneratedTypes extends Config {}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user