From 98cf62547fec946ff0e7af9bafa1da3df388344a Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Sun, 27 Sep 2026 13:38:55 +0800 Subject: [PATCH] fix(file-manager): accept OS file drops over existing rows (#1484) * fix(file-manager): accept OS file drops over existing rows * test(file-manager): target the file label in drop tests * test(css): exercise the configured PostCSS plugin chain * test(identity): preserve full Ed25519 public keys in fixtures * test(remote-desktop): always mutate ciphertext in tamper test --- .../src/frontend/FileManagerGrid.tsx | 3 ++ .../tests/frontend/FileManagerGrid.test.tsx | 52 ++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/plugins/file-manager/src/frontend/FileManagerGrid.tsx b/plugins/file-manager/src/frontend/FileManagerGrid.tsx index e1202aa4a..c1f67bb61 100644 --- a/plugins/file-manager/src/frontend/FileManagerGrid.tsx +++ b/plugins/file-manager/src/frontend/FileManagerGrid.tsx @@ -458,6 +458,9 @@ export function FileManagerGrid({ }; const handleFileDrop = (e: React.DragEvent, targetFile: FileItem) => { + // OS drops belong to the pane's upload handler, even over an existing row. + if (dragState.type !== "internal" && !isLocalFilesDrag(e.dataTransfer)) + return; e.preventDefault(); e.stopPropagation(); diff --git a/plugins/file-manager/tests/frontend/FileManagerGrid.test.tsx b/plugins/file-manager/tests/frontend/FileManagerGrid.test.tsx index fafd1636b..198a04da8 100644 --- a/plugins/file-manager/tests/frontend/FileManagerGrid.test.tsx +++ b/plugins/file-manager/tests/frontend/FileManagerGrid.test.tsx @@ -1,4 +1,4 @@ -import { afterEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { cleanup, fireEvent, render, screen } from "@testing-library/react"; import "@testing-library/jest-dom/vitest"; import { FileManagerGrid } from "../../src/frontend/FileManagerGrid"; @@ -44,6 +44,7 @@ const props = { afterEach(() => { cleanup(); vi.restoreAllMocks(); + vi.unstubAllGlobals(); }); describe("remote file rename", () => { @@ -89,3 +90,52 @@ describe("remote file rename", () => { expect(screen.getByText(file.modified!)).toBeInTheDocument(); }); }); + +describe("external file drops", () => { + beforeEach(() => { + vi.stubGlobal( + "ResizeObserver", + class { + observe() {} + disconnect() {} + }, + ); + }); + it.each(["list", "grid"] as const)( + "uploads dropped folders over a file in %s view", + (viewMode) => { + const onUploadItems = vi.fn(); + render( + , + ); + const folder = { + name: "logs", + isDirectory: true, + isFile: false, + } as FileSystemEntry; + fireEvent.drop(screen.getByText(file.name), { + dataTransfer: { + types: ["Files"], + files: [], + getData: () => "", + items: [{ webkitGetAsEntry: () => folder }], + }, + }); + expect(onUploadItems).toHaveBeenCalledExactlyOnceWith([folder]); + }, + ); + + it("uploads a plain OS file dropped over a row", () => { + const onUpload = vi.fn(); + render(); + const files = [new File(["log"], "app.log")]; + fireEvent.drop(screen.getByText(file.name), { + dataTransfer: { types: ["Files"], files, items: [], getData: () => "" }, + }); + expect(onUpload).toHaveBeenCalledExactlyOnceWith(files); + }); +});