fix(ui): prevents unwanted data overrides when bulk editing (#9842)

### What?

It became possible for fields to reset to a defined `defaultValue` when
bulk editing from the `edit-many` drawer.

### Why?

The form-state of all fields were being considered during a bulk edit -
this also meant using their initial states - this meant any fields with
default values or nested fields (`arrays`) would be overwritten with
their initial states

I.e. empty values or default values.

### How?

Now - we only send through the form data of the fields specifically
being edited in the edit-many drawer and ignore all other fields.

Leaving all other fields stay their current values.

Fixes #9590

---------

Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
This commit is contained in:
Patrik
2024-12-10 11:39:15 -05:00
committed by GitHub
parent fee17448e7
commit 563694d930
7 changed files with 236 additions and 31 deletions

View File

@@ -103,16 +103,64 @@ export const Posts: CollectionConfig = {
},
],
},
{
name: 'arrayOfFields',
type: 'array',
admin: {
initCollapsed: true,
},
fields: [
{
name: 'optional',
type: 'text',
},
{
name: 'innerArrayOfFields',
type: 'array',
fields: [
{
name: 'innerOptional',
type: 'text',
},
],
},
],
},
{
name: 'group',
type: 'group',
fields: [
{
name: 'defaultValueField',
type: 'text',
defaultValue: 'testing',
},
{
name: 'title',
type: 'text',
},
],
},
{
name: 'someBlock',
type: 'blocks',
blocks: [
{
slug: 'textBlock',
fields: [
{
name: 'textFieldForBlock',
type: 'text',
},
],
},
],
},
{
name: 'defaultValueField',
type: 'text',
defaultValue: 'testing',
},
{
name: 'relationship',
type: 'relationship',

View File

@@ -516,6 +516,68 @@ describe('admin3', () => {
await expect(page.locator('.row-3 .cell-title')).toContainText(updatedPostTitle)
})
test('should not override un-edited values in bulk edit if it has a defaultValue', async () => {
await deleteAllPosts()
const post1Title = 'Post'
const postData = {
title: 'Post',
arrayOfFields: [
{
optional: 'some optional array field',
innerArrayOfFields: [
{
innerOptional: 'some inner optional array field',
},
],
},
],
group: {
defaultValueField: 'not the group default value',
title: 'some title',
},
someBlock: [
{
textFieldForBlock: 'some text for block text',
blockType: 'textBlock',
},
],
defaultValueField: 'not the default value',
}
const updatedPostTitle = `${post1Title} (Updated)`
await Promise.all([createPost(postData)])
await page.goto(postsUrl.list)
await page.locator('input#select-all').check()
await page.locator('.edit-many__toggle').click()
await page.locator('.field-select .rs__control').click()
const titleOption = page.locator('.field-select .rs__option', {
hasText: exactText('Title'),
})
await expect(titleOption).toBeVisible()
await titleOption.click()
const titleInput = page.locator('#field-title')
await expect(titleInput).toBeVisible()
await titleInput.fill(updatedPostTitle)
await page.locator('.form-submit button[type="submit"].edit-many__publish').click()
await expect(page.locator('.payload-toast-container .toast-success')).toContainText(
'Updated 1 Post successfully.',
)
const updatedPost = await payload.find({
collection: 'posts',
limit: 1,
})
expect(updatedPost.docs[0].title).toBe(updatedPostTitle)
expect(updatedPost.docs[0].arrayOfFields.length).toBe(1)
expect(updatedPost.docs[0].arrayOfFields[0].optional).toBe('some optional array field')
expect(updatedPost.docs[0].arrayOfFields[0].innerArrayOfFields.length).toBe(1)
expect(updatedPost.docs[0].someBlock[0].textFieldForBlock).toBe('some text for block text')
expect(updatedPost.docs[0].defaultValueField).toBe('not the default value')
})
test('should bulk update with filters and across pages', async () => {
// First, delete all posts created by the seed
await deleteAllPosts()

View File

@@ -138,9 +138,31 @@ export interface Post {
[k: string]: unknown;
}[]
| null;
arrayOfFields?:
| {
optional?: string | null;
innerArrayOfFields?:
| {
innerOptional?: string | null;
id?: string | null;
}[]
| null;
id?: string | null;
}[]
| null;
group?: {
defaultValueField?: string | null;
title?: string | null;
};
someBlock?:
| {
textFieldForBlock?: string | null;
id?: string | null;
blockName?: string | null;
blockType: 'textBlock';
}[]
| null;
defaultValueField?: string | null;
relationship?: (string | null) | Post;
customCell?: string | null;
sidebarField?: string | null;
@@ -484,11 +506,36 @@ export interface PostsSelect<T extends boolean = true> {
description?: T;
number?: T;
richText?: T;
arrayOfFields?:
| T
| {
optional?: T;
innerArrayOfFields?:
| T
| {
innerOptional?: T;
id?: T;
};
id?: T;
};
group?:
| T
| {
defaultValueField?: T;
title?: T;
};
someBlock?:
| T
| {
textBlock?:
| T
| {
textFieldForBlock?: T;
id?: T;
blockName?: T;
};
};
defaultValueField?: T;
relationship?: T;
customCell?: T;
sidebarField?: T;