From ee0d4fc47031ba6fa84d29d35450beaafd14dc6f Mon Sep 17 00:00:00 2001 From: lucaronin Date: Fri, 8 May 2026 13:44:19 +0200 Subject: [PATCH] fix: allow delete menu clicks in drag regions --- src/hooks/useDragRegion.test.tsx | 12 ++++++++++ src/hooks/useDragRegion.ts | 12 +++++++++- tests/smoke/delete-note-nonblocking.spec.ts | 26 ++++++++++++++++++++- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/hooks/useDragRegion.test.tsx b/src/hooks/useDragRegion.test.tsx index c3818226..3ded3fec 100644 --- a/src/hooks/useDragRegion.test.tsx +++ b/src/hooks/useDragRegion.test.tsx @@ -23,6 +23,9 @@ function DragRegionHarness() {
+
+
Delete this note
+
) } @@ -68,6 +71,15 @@ describe('useDragRegion', () => { expect(invoke).not.toHaveBeenCalled() }) + it('does not start dragging from menu items rendered inside drag surfaces', () => { + render() + + fireEvent.mouseDown(screen.getByRole('menuitem', { name: 'Delete this note' }), { button: 0 }) + + expect(startDragging).not.toHaveBeenCalled() + expect(invoke).not.toHaveBeenCalled() + }) + it('ignores non-primary mouse buttons', () => { render() diff --git a/src/hooks/useDragRegion.ts b/src/hooks/useDragRegion.ts index abfa70d3..7b767a6b 100644 --- a/src/hooks/useDragRegion.ts +++ b/src/hooks/useDragRegion.ts @@ -2,7 +2,17 @@ import { invoke } from '@tauri-apps/api/core' import { getCurrentWindow } from '@tauri-apps/api/window' import { useCallback } from 'react' -const NO_DRAG_SELECTOR = 'button, input, select, a, [data-no-drag]' +const NO_DRAG_SELECTOR = [ + 'button', + 'input', + 'select', + 'a', + '[role="menu"]', + '[role="menuitem"]', + '[role="menuitemcheckbox"]', + '[role="menuitemradio"]', + '[data-no-drag]', +].join(', ') function isDragDisabledTarget(target: EventTarget | null): boolean { return target instanceof Element && target.closest(NO_DRAG_SELECTOR) !== null diff --git a/tests/smoke/delete-note-nonblocking.spec.ts b/tests/smoke/delete-note-nonblocking.spec.ts index d71d58a7..f63fe934 100644 --- a/tests/smoke/delete-note-nonblocking.spec.ts +++ b/tests/smoke/delete-note-nonblocking.spec.ts @@ -27,7 +27,10 @@ test.describe('responsive note deletion', () => { const handlers = window.__mockHandlers if (!handlers) throw new Error('Mock handlers unavailable for delete override') const delayedDelete = (args?: { paths?: string[] }) => - new Promise((resolve) => window.setTimeout(() => resolve(args?.paths ?? []), 3_000)) + new Promise((resolve) => { + const testWindow = window as typeof window & { __resolveDelete?: () => void } + testWindow.__resolveDelete = () => resolve(args?.paths ?? []) + }) handlers.batch_delete_notes = delayedDelete handlers.batch_delete_notes_async = delayedDelete }) @@ -49,7 +52,28 @@ test.describe('responsive note deletion', () => { await expect(page.getByRole('heading', { name: 'Note B', level: 1 })).toBeVisible({ timeout: 5_000 }) await expect(progressNotice).toBeVisible() + await page.evaluate(() => { + const { __resolveDelete: resolveDelete } = window as typeof window & { __resolveDelete?: () => void } + if (!resolveDelete) throw new Error('Delete resolver was not registered') + resolveDelete() + }) await expect(progressNotice).toHaveCount(0, { timeout: 5_000 }) await expect(page.getByText('Note permanently deleted')).toBeVisible({ timeout: 5_000 }) }) + + test('breadcrumb delete menu responds to a single pointer click @smoke', async ({ page }) => { + await openFixtureVaultDesktopHarness(page, tempVaultDir) + await page.getByText('Alpha Project', { exact: true }).first().click() + await expect(page.getByRole('heading', { name: 'Alpha Project', level: 1 })).toBeVisible({ timeout: 5_000 }) + + await page.getByRole('button', { name: 'More note actions' }).click() + const menu = page.getByRole('menu') + const deleteItem = menu.getByRole('menuitem', { name: 'Delete this note' }) + await expect(deleteItem).toBeVisible({ timeout: 5_000 }) + + await deleteItem.click() + + await expect(page.getByTestId('confirm-delete-dialog')).toBeVisible({ timeout: 5_000 }) + await expect(page.getByTestId('confirm-delete-btn')).toBeFocused() + }) })