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);
+ });
+});