fix(ui): relationship using list drawer correctly updates when hasMany is true (#12176)

### What?

This fixes an issue raised by @maximseshuk in this PR #11553. Here is
the text of the original comment:

If the field has the property hasMany: true and you select one item, it
shows up in the select field, but any additional selected items won't be
visible in the select field, even though the data is actually there and
can be saved. After refreshing the page, they appear.

In addition I added a fix to an issue where the filterOptions weren't
being passed in to the useListDrawer hook properly in polymorphic
relationships

### How?

Instead of using the push method to update the value state, a new array
is created and directly set using useState. I think the issue was
because using push mutates the original array.
This commit is contained in:
Sam Wheeler
2025-04-28 13:38:50 -07:00
committed by GitHub
parent c85fb808b9
commit 5bd852c9b5
3 changed files with 78 additions and 15 deletions

View File

@@ -136,18 +136,38 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) =>
let newFilterOptions = filterOptions let newFilterOptions = filterOptions
if (value) { if (value) {
;(Array.isArray(value) ? value : [value]).forEach((val) => { const valuesByRelation = (Array.isArray(value) ? value : [value]).reduce((acc, val) => {
;(Array.isArray(relationTo) ? relationTo : [relationTo]).forEach((relationTo) => { if (typeof val === 'object' && val.relationTo) {
newFilterOptions = { if (!acc[val.relationTo]) {
...(filterOptions || {}), acc[val.relationTo] = []
[relationTo]: {
...(typeof filterOptions?.[relationTo] === 'object' ? filterOptions[relationTo] : {}),
id: {
not_in: [typeof val === 'object' ? val.value : val],
},
},
} }
}) acc[val.relationTo].push(val.value)
} else if (val) {
const relation = Array.isArray(relationTo) ? undefined : relationTo
if (relation) {
if (!acc[relation]) {
acc[relation] = []
}
acc[relation].push(val)
}
}
return acc
}, {})
;(Array.isArray(relationTo) ? relationTo : [relationTo]).forEach((relation) => {
newFilterOptions = {
...(newFilterOptions || {}),
[relation]: {
...(typeof filterOptions?.[relation] === 'object' ? filterOptions[relation] : {}),
...(valuesByRelation[relation]
? {
id: {
not_in: valuesByRelation[relation],
},
}
: {}),
},
}
}) })
} }
@@ -174,8 +194,7 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) =>
if (hasMany) { if (hasMany) {
const withSelection = Array.isArray(value) ? value : [] const withSelection = Array.isArray(value) ? value : []
withSelection.push(formattedSelection) setValue([...withSelection, formattedSelection])
setValue(withSelection)
} else { } else {
setValue(formattedSelection) setValue(formattedSelection)
} }

View File

@@ -742,7 +742,15 @@ describe('relationship', () => {
await expect(listDrawerContent).toBeHidden() await expect(listDrawerContent).toBeHidden()
const selectedValue = relationshipField.locator('.relationship--multi-value-label__text') const selectedValue = relationshipField.locator('.relationship--multi-value-label__text')
await expect(selectedValue).toBeVisible() await expect(selectedValue).toHaveCount(1)
await relationshipField.click()
await expect(listDrawerContent).toBeVisible()
await button.click()
await expect(listDrawerContent).toBeHidden()
const selectedValues = relationshipField.locator('.relationship--multi-value-label__text')
await expect(selectedValues).toHaveCount(2)
}) })
test('should handle `hasMany` polymorphic relationship when `appearance: "drawer"`', async () => { test('should handle `hasMany` polymorphic relationship when `appearance: "drawer"`', async () => {
@@ -807,6 +815,42 @@ describe('relationship', () => {
await expect(newRows).toHaveCount(1) await expect(newRows).toHaveCount(1)
await expect(listDrawerContent.getByText('Seeded text document')).toHaveCount(0) await expect(listDrawerContent.getByText('Seeded text document')).toHaveCount(0)
}) })
test('should filter out existing values from polymorphic relationship list drawer', async () => {
await page.goto(url.create)
const relationshipField = page.locator('#field-polymorphicRelationshipDrawer')
await relationshipField.click()
const listDrawerContent = page.locator('.list-drawer').locator('.drawer__content')
await expect(listDrawerContent).toBeVisible()
const relationToSelector = page.locator('.list-header__select-collection')
await expect(relationToSelector).toBeVisible()
await relationToSelector.locator('.rs__control').click()
const option = relationToSelector.locator('.rs__option').nth(1)
await option.click()
const rows = listDrawerContent.locator('table tbody tr')
await expect(rows).toHaveCount(2)
const firstRow = rows.first()
const button = firstRow.locator('button')
await button.click()
await expect(listDrawerContent).toBeHidden()
const selectedValue = relationshipField.locator('.relationship--single-value__text')
await expect(selectedValue).toBeVisible()
await relationshipField.click()
await expect(listDrawerContent).toBeVisible()
await expect(relationToSelector).toBeVisible()
await relationToSelector.locator('.rs__control').click()
await option.click()
const newRows = listDrawerContent.locator('table tbody tr')
await expect(newRows).toHaveCount(1)
const newFirstRow = newRows.first()
const newButton = newFirstRow.locator('button')
await newButton.click()
await expect(listDrawerContent).toBeHidden()
})
}) })
async function createTextFieldDoc(overrides?: Partial<TextField>): Promise<TextField> { async function createTextFieldDoc(overrides?: Partial<TextField>): Promise<TextField> {

View File

@@ -158,7 +158,7 @@ const RelationshipFields: CollectionConfig = {
}, },
{ {
name: 'relationshipDrawerHasManyPolymorphic', name: 'relationshipDrawerHasManyPolymorphic',
relationTo: ['text-fields'], relationTo: ['text-fields', 'array-fields'],
admin: { admin: {
appearance: 'drawer', appearance: 'drawer',
}, },