fix: prevent dialogs from closing on blur (#3314)

This commit is contained in:
David Nguyen
2026-09-01 13:17:48 +10:00
committed by GitHub
parent 5082b475a0
commit eba71d9be4
3 changed files with 137 additions and 0 deletions
@@ -0,0 +1,80 @@
import { prisma } from '@documenso/prisma';
import { seedTeam } from '@documenso/prisma/seed/teams';
import { seedTemplate } from '@documenso/prisma/seed/templates';
import { expect, test } from '@playwright/test';
import { EnvelopeType } from '@prisma/client';
import { apiSignin } from '../fixtures/authentication';
import { openDropdownMenu } from '../fixtures/generic';
test('[TEMPLATES]: bulk send via CSV from the table action dropdown', async ({ page }) => {
const { team, owner } = await seedTeam();
await seedTemplate({
title: 'Bulk send template',
userId: owner.id,
teamId: team.id,
});
const uniqueRecipientEmail = `bulk-send-${Date.now()}@documenso.com`;
await apiSignin({
page,
email: owner.email,
redirectPath: `/t/${team.url}/templates`,
});
const actionBtn = page
.getByRole('row', { name: 'Bulk send template' })
.getByRole('cell', { name: 'Use Template' })
.getByRole('button')
.nth(1);
await openDropdownMenu(page, actionBtn);
await page.getByText('Bulk Send via CSV').click();
const dialog = page.getByRole('dialog').filter({ hasText: 'Bulk Send Template via CSV' });
await expect(dialog).toBeVisible();
// Opening the native file picker blurs the window. Radix dropdown menus close themselves on
// window blur, which used to unmount this dialog when it was nested inside the menu content,
// silently discarding the user's file selection.
await page.evaluate(() => window.dispatchEvent(new Event('blur')));
await expect(dialog).toBeVisible();
const csv = ['recipient_1_email,recipient_1_name', `${uniqueRecipientEmail},Bulk Recipient`].join('\n');
await dialog.locator('input[type="file"]').setInputFiles({
name: 'bulk-send.csv',
mimeType: 'text/csv',
buffer: Buffer.from(csv),
});
await expect(dialog.getByText('bulk-send.csv')).toBeVisible();
await dialog.getByRole('button', { name: 'Upload and Process' }).click();
await expect(page.getByText('Your bulk send has been initiated').first()).toBeVisible();
// The bulk send runs as a background job, so poll for the created document.
await expect
.poll(
async () =>
await prisma.envelope.count({
where: {
type: EnvelopeType.DOCUMENT,
teamId: team.id,
recipients: {
some: {
email: uniqueRecipientEmail,
},
},
},
}),
{ timeout: 30_000 },
)
.toBe(1);
});
+13
View File
@@ -4,6 +4,19 @@ import * as React from 'react';
import { cn } from '../lib/utils';
/**
* Note: The underlying @radix-ui/react-menu package is patched, see
* patches/@radix-ui+react-menu+2.1.24.patch.
*
* Since @radix-ui/react-menu 2.1.18, Radix closes any open menu when the window fires a
* "blur" event. Opening a native file picker blurs the window, which would close the
* menu and unmount anything rendered within its content, such as dialogs containing
* file inputs, silently discarding the user's file selection. The patch removes that
* behaviour since there is currently no native prop to opt out of it, see
* https://github.com/radix-ui/primitives/issues/3618.
*
* Regression test: packages/app-tests/e2e/templates/template-bulk-send.spec.ts
*/
const DropdownMenu = DropdownMenuPrimitive.Root;
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
+44
View File
@@ -0,0 +1,44 @@
diff --git a/node_modules/@radix-ui/react-menu/dist/index.js b/node_modules/@radix-ui/react-menu/dist/index.js
index b3f6113..d8ec2e3 100644
--- a/node_modules/@radix-ui/react-menu/dist/index.js
+++ b/node_modules/@radix-ui/react-menu/dist/index.js
@@ -139,9 +139,14 @@ var Menu = /* @__PURE__ */ __name((props) => {
if (!open) {
return;
}
- const handleBlur = /* @__PURE__ */ __name(() => handleOpenChange(false), "handleBlur");
- window.addEventListener("blur", handleBlur);
- return () => window.removeEventListener("blur", handleBlur);
+ // DOCUMENSO PATCH: @radix-ui/react-menu 2.1.18+ closes open menus when the window
+ // fires a "blur" event. Opening a native file picker blurs the window, which closes
+ // the menu and unmounts anything rendered within its content, such as dialogs that
+ // contain file inputs, silently discarding the user's file selection. This restores
+ // the pre-2.1.18 behaviour. Regression test: e2e/templates/template-bulk-send.spec.ts
+ // const handleBlur = () => handleOpenChange(false);
+ // window.addEventListener("blur", handleBlur);
+ // return () => window.removeEventListener("blur", handleBlur);
}, [open, handleOpenChange]);
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PopperPrimitive.Root, { ...popperScope, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
MenuProvider,
diff --git a/node_modules/@radix-ui/react-menu/dist/index.mjs b/node_modules/@radix-ui/react-menu/dist/index.mjs
index cd4303a..68865e3 100644
--- a/node_modules/@radix-ui/react-menu/dist/index.mjs
+++ b/node_modules/@radix-ui/react-menu/dist/index.mjs
@@ -73,9 +73,14 @@ var Menu = /* @__PURE__ */ __name((props) => {
if (!open) {
return;
}
- const handleBlur = /* @__PURE__ */ __name(() => handleOpenChange(false), "handleBlur");
- window.addEventListener("blur", handleBlur);
- return () => window.removeEventListener("blur", handleBlur);
+ // DOCUMENSO PATCH: @radix-ui/react-menu 2.1.18+ closes open menus when the window
+ // fires a "blur" event. Opening a native file picker blurs the window, which closes
+ // the menu and unmounts anything rendered within its content, such as dialogs that
+ // contain file inputs, silently discarding the user's file selection. This restores
+ // the pre-2.1.18 behaviour. Regression test: e2e/templates/template-bulk-send.spec.ts
+ // const handleBlur = () => handleOpenChange(false);
+ // window.addEventListener("blur", handleBlur);
+ // return () => window.removeEventListener("blur", handleBlur);
}, [open, handleOpenChange]);
return /* @__PURE__ */ jsx(PopperPrimitive.Root, { ...popperScope, children: /* @__PURE__ */ jsx(
MenuProvider,