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:
@@ -69,6 +69,8 @@ export function ConfirmationModal(props: ConfirmationModalProps) {
|
||||
return (
|
||||
<Modal
|
||||
className={[baseClass, className].filter(Boolean).join(' ')}
|
||||
// Fixes https://github.com/payloadcms/payload/issues/13778
|
||||
closeOnBlur={false}
|
||||
slug={modalSlug}
|
||||
style={{
|
||||
zIndex: drawerZBase + editDepth,
|
||||
|
||||
@@ -53,6 +53,8 @@ export const DocumentLocked: React.FC<{
|
||||
return (
|
||||
<Modal
|
||||
className={baseClass}
|
||||
// Fixes https://github.com/payloadcms/payload/issues/13778
|
||||
closeOnBlur={false}
|
||||
onClose={() => {
|
||||
startRouteTransition(() => handleGoBack())
|
||||
}}
|
||||
|
||||
@@ -31,7 +31,12 @@ export const DocumentTakeOver: React.FC<{
|
||||
}, [isActive, openModal, closeModal])
|
||||
|
||||
return (
|
||||
<Modal className={baseClass} slug={modalSlug}>
|
||||
<Modal
|
||||
className={baseClass}
|
||||
// // Fixes https://github.com/payloadcms/payload/issues/13778
|
||||
closeOnBlur={false}
|
||||
slug={modalSlug}
|
||||
>
|
||||
<div className={`${baseClass}__wrapper`}>
|
||||
<div className={`${baseClass}__content`}>
|
||||
<h1>{t('general:editingTakenOver')}</h1>
|
||||
|
||||
@@ -79,6 +79,8 @@ export const Drawer: React.FC<Props> = ({
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
// Fixes https://github.com/payloadcms/payload/issues/13778
|
||||
closeOnBlur={false}
|
||||
slug={slug}
|
||||
style={{
|
||||
zIndex: drawerZBase + drawerDepth,
|
||||
|
||||
@@ -11,6 +11,8 @@ export function FullscreenModal(props: Parameters<typeof ModalType>[0]) {
|
||||
|
||||
return (
|
||||
<Modal
|
||||
// Fixes https://github.com/payloadcms/payload/issues/13778
|
||||
closeOnBlur={false}
|
||||
{...props}
|
||||
style={{
|
||||
...(props.style || {}),
|
||||
|
||||
@@ -788,11 +788,7 @@ export const RelationshipInput: React.FC<RelationshipInputProps> = (props) => {
|
||||
}}
|
||||
onMenuOpen={() => {
|
||||
if (appearance === 'drawer') {
|
||||
// TODO: This timeout is only necessary for inline blocks in the lexical editor
|
||||
// and when the devtools are closed. Temporary solution, we can probably do better.
|
||||
setTimeout(() => {
|
||||
openListDrawer()
|
||||
}, 100)
|
||||
openListDrawer()
|
||||
} else if (appearance === 'select') {
|
||||
setMenuIsOpen(true)
|
||||
if (!hasLoadedFirstPageRef.current) {
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user