fix(ui): opening relationship field with appearance: "drawer" inside rich text inline block closes drawer (#13830)

Previously, clicking on a relationship with `appearance: 'drawer'`
within a lexical inline block drawer would close both drawers, instead
of opening a new drawer to select the relationship document.

Fixes https://github.com/payloadcms/payload/issues/13778

## Before


https://github.com/user-attachments/assets/371619d2-c64b-4e12-b8f3-72ad599db5a9


## After


https://github.com/user-attachments/assets/a05b9338-3b1d-4b0c-b78c-8e6b3b57014c

## Technical Notes

The issue happened due to the [`ModalContainer`'s
onClick](https://github.com/faceless-ui/modal/blob/main/src/ModalContainer/index.tsx#L43)
function being triggered when mouseUp on the relationship field is
triggered.

**Causes issue**: MouseDown => drawer opens => mouseUp
**Does not cause issue**: MouseDown => MouseUp => drawer opens

This is why the previous `setTimeout()` fix worked: it delayed the
drawer opening until the mouseUp event occured. If you click very slow,
the issue could still happen though.

I was not able to figure out _why_ the `onClick` of the ModalContainer
is triggered.

## The Fix

This is the ModalProvider `onClick` handler:

```ts
(e: MouseEvent<HTMLElement>) => {
      if (closeOnBlur) closeAllModals();
      if (typeof onClick === 'function') onClick(e);
    },
```

The fix is to simply set `closeOnBlur` to `false`, so that
`closeAllModals` is no longer called.

I was not able to manually trigger the onClick event of the
`ModalProvider`. I figured that it is more often called by strange React
Components triggering events like maniacs (react-select) rather than
some genuine user action.

In case some piece of functionality somehow relied on this event being
triggered and then closing the modal, that piece of functionality could
manually call `closeModal()` or attach a custom `onClick` function to
the modal. That way, this mechanism will be run in a more deliberate,
expected way.


---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211375615406672
This commit is contained in:
Alessio Gravili
2025-09-24 14:45:36 -07:00
committed by GitHub
parent dea91f3d8d
commit 7bbd07c4a5
9 changed files with 58 additions and 8 deletions

View File

@@ -45,7 +45,7 @@ describe('Lexical Fully Featured', () => {
test('prevent extra paragraph when inserting decorator blocks like blocks or upload node', async () => {
await lexical.slashCommand('block')
await expect(lexical.editor.locator('.lexical-block')).toBeVisible()
await lexical.slashCommand('relationship')
await lexical.slashCommand('relationship', true, 'Relationship')
await lexical.drawer.locator('.list-drawer__header').getByText('Create New').click()
await lexical.save('drawer')
await expect(lexical.decorator).toHaveCount(2)
@@ -129,4 +129,26 @@ describe('Lexical Fully Featured', () => {
const someButton = dropdownItems!.locator(`[data-item-key="bg-red"]`)
await expect(someButton).toHaveAttribute('aria-disabled', 'false')
})
test('ensure opening relationship field with appearance: "drawer" inside rich text inline block does not close drawer', async ({
page,
}) => {
// https://github.com/payloadcms/payload/pull/13830
await lexical.slashCommand('inlineblockwithrelationship')
await expect(lexical.drawer).toBeVisible()
await lexical.drawer.locator('.react-select').click()
// At this point, the drawer would close if the issue is not fixed
await page.getByText('Seeded text document').click()
await expect(
lexical.drawer.locator('.react-select .relationship--single-value__text'),
).toHaveText('Seeded text document')
await lexical.drawer.getByText('Save changes').click()
await expect(lexical.drawer).toBeHidden()
})
})

View File

@@ -57,6 +57,20 @@ export const LexicalFullyFeatured: CollectionConfig = {
},
],
},
{
slug: 'inlineBlockWithRelationship',
fields: [
{
name: 'relationship',
type: 'relationship',
relationTo: 'text-fields',
admin: {
// Required to reproduce issue: https://github.com/payloadcms/payload/issues/13778
appearance: 'drawer',
},
},
],
},
],
}),
],

View File

@@ -191,6 +191,7 @@ export class LexicalHelpers {
command: ('block' | 'check' | 'code' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' |'h6' | 'inline'
| 'link' | 'ordered' | 'paragraph' | 'quote' | 'relationship' | 'table' | 'unordered'|'upload') | ({} & string),
expectMenuToClose = true,
labelToMatch?: string,
) {
await this.page.keyboard.press(`/`)
@@ -198,7 +199,11 @@ export class LexicalHelpers {
await expect(slashMenuPopover).toBeVisible()
await this.page.keyboard.type(command)
await wait(200)
await this.page.keyboard.press(`Enter`)
if (labelToMatch) {
await slashMenuPopover.getByText(labelToMatch).click()
} else {
await this.page.keyboard.press(`Enter`)
}
if (expectMenuToClose) {
await expect(slashMenuPopover).toBeHidden()
}