From 5bd852c9b59e053629d49f5cbae9fdc38987e5e6 Mon Sep 17 00:00:00 2001 From: Sam Wheeler <104921112+swheeler7@users.noreply.github.com> Date: Mon, 28 Apr 2025 13:38:50 -0700 Subject: [PATCH] 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. --- packages/ui/src/fields/Relationship/index.tsx | 45 ++++++++++++------ .../collections/Relationship/e2e.spec.ts | 46 ++++++++++++++++++- test/fields/collections/Relationship/index.ts | 2 +- 3 files changed, 78 insertions(+), 15 deletions(-) diff --git a/packages/ui/src/fields/Relationship/index.tsx b/packages/ui/src/fields/Relationship/index.tsx index 449c1e1374..a572b6d393 100644 --- a/packages/ui/src/fields/Relationship/index.tsx +++ b/packages/ui/src/fields/Relationship/index.tsx @@ -136,18 +136,38 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) => let newFilterOptions = filterOptions if (value) { - ;(Array.isArray(value) ? value : [value]).forEach((val) => { - ;(Array.isArray(relationTo) ? relationTo : [relationTo]).forEach((relationTo) => { - newFilterOptions = { - ...(filterOptions || {}), - [relationTo]: { - ...(typeof filterOptions?.[relationTo] === 'object' ? filterOptions[relationTo] : {}), - id: { - not_in: [typeof val === 'object' ? val.value : val], - }, - }, + const valuesByRelation = (Array.isArray(value) ? value : [value]).reduce((acc, val) => { + if (typeof val === 'object' && val.relationTo) { + if (!acc[val.relationTo]) { + acc[val.relationTo] = [] } - }) + 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) { const withSelection = Array.isArray(value) ? value : [] - withSelection.push(formattedSelection) - setValue(withSelection) + setValue([...withSelection, formattedSelection]) } else { setValue(formattedSelection) } diff --git a/test/fields/collections/Relationship/e2e.spec.ts b/test/fields/collections/Relationship/e2e.spec.ts index 5fd1c5a110..492a40b686 100644 --- a/test/fields/collections/Relationship/e2e.spec.ts +++ b/test/fields/collections/Relationship/e2e.spec.ts @@ -742,7 +742,15 @@ describe('relationship', () => { await expect(listDrawerContent).toBeHidden() 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 () => { @@ -807,6 +815,42 @@ describe('relationship', () => { await expect(newRows).toHaveCount(1) 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): Promise { diff --git a/test/fields/collections/Relationship/index.ts b/test/fields/collections/Relationship/index.ts index 1a8f6ff924..ec09f8cbcd 100644 --- a/test/fields/collections/Relationship/index.ts +++ b/test/fields/collections/Relationship/index.ts @@ -158,7 +158,7 @@ const RelationshipFields: CollectionConfig = { }, { name: 'relationshipDrawerHasManyPolymorphic', - relationTo: ['text-fields'], + relationTo: ['text-fields', 'array-fields'], admin: { appearance: 'drawer', },