From 99540c6abed82f1dfd794167ba505bc6ae6ebe39 Mon Sep 17 00:00:00 2001 From: victorwon2001 <192616110+victorwon2001@users.noreply.github.com> Date: Thu, 3 Sep 2026 21:30:51 +0900 Subject: [PATCH] =?UTF-8?q?fix(notes):=20=E4=BF=AE=E5=A4=8D=E5=8F=B3?= =?UTF-8?q?=E9=94=AE=E9=87=8D=E5=91=BD=E5=90=8D=E7=84=A6=E7=82=B9=E4=B8=A2?= =?UTF-8?q?=E5=A4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../panel/notes/NoteTreeContextMenu.test.tsx | 104 ++++++++++++++++++ .../panel/notes/NoteTreeContextMenu.tsx | 18 ++- 2 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 src/components/panel/notes/NoteTreeContextMenu.test.tsx diff --git a/src/components/panel/notes/NoteTreeContextMenu.test.tsx b/src/components/panel/notes/NoteTreeContextMenu.test.tsx new file mode 100644 index 00000000..b0b52a77 --- /dev/null +++ b/src/components/panel/notes/NoteTreeContextMenu.test.tsx @@ -0,0 +1,104 @@ +import { fireEvent, render, screen, waitFor } from "@testing-library/react"; +import { useState } from "react"; +import { describe, expect, it, vi } from "vitest"; +import { ContextMenu, ContextMenuTrigger } from "@/components/ui/context-menu"; +import type { NoteTreeNode } from "@/types/notes"; +import NoteTreeContextMenu, { + type NoteTreeMenuLabels, +} from "./NoteTreeContextMenu"; +import NoteTreeItem from "./NoteTreeItem"; + +const labels: NoteTreeMenuLabels = { + open: "Open", + newNote: "New note", + newFolder: "New folder", + rename: "Rename", + moveTo: "Move to", + delete: "Delete", + refresh: "Refresh", + root: "Root", + expandAll: "Expand all", + collapseAll: "Collapse all", +}; + +const baseNode = { + parentId: null, + sortOrder: 0, + updatedAtMs: 1, + children: [], +}; + +describe("NoteTreeContextMenu rename focus", () => { + it.each([ + [ + "note", + { ...baseNode, id: "note-1", kind: "note", name: "Note 1", revision: 1 }, + ], + [ + "folder", + { ...baseNode, id: "folder-1", kind: "folder", name: "Folder 1" }, + ], + ] as const)( + "keeps the %s inline editor focused after choosing Rename", + async (_kind, node) => { + render(); + + screen.getByRole("treeitem").focus(); + fireEvent.contextMenu(screen.getByTestId("notes-tree-trigger")); + + const renameItem = await screen.findByText(labels.rename); + fireEvent.click(renameItem); + + const input = await screen.findByDisplayValue(node.name); + await waitFor(() => { + expect( + document.querySelector('[data-slot="context-menu-content"]'), + ).toBeNull(); + expect(document.activeElement).toBe(input); + }); + }, + ); +}); + +function RenameHarness({ node }: { node: NoteTreeNode }) { + const [editing, setEditing] = useState(false); + + return ( + + +
+ setEditing(false)} + onDragStartNode={vi.fn()} + onDragOverNode={vi.fn()} + onDropNode={vi.fn()} + onDragEnd={vi.fn()} + /> +
+
+ setEditing(true)} + onMove={vi.fn()} + onDelete={vi.fn()} + onRefresh={vi.fn()} + /> +
+ ); +} diff --git a/src/components/panel/notes/NoteTreeContextMenu.tsx b/src/components/panel/notes/NoteTreeContextMenu.tsx index ddb0bcfc..ab9fdb7f 100644 --- a/src/components/panel/notes/NoteTreeContextMenu.tsx +++ b/src/components/panel/notes/NoteTreeContextMenu.tsx @@ -1,3 +1,4 @@ +import { useRef } from "react"; import { MdAdd, MdCreateNewFolder, @@ -60,6 +61,7 @@ export default function NoteTreeContextMenu({ onDelete, onRefresh, }: NoteTreeContextMenuProps) { + const preventNextContextMenuAutoFocusRef = useRef(false); const parentId = node?.kind === "folder" ? node.id : (node?.parentId ?? null); const moveTargets = folderTargets.filter( (item) => @@ -67,7 +69,14 @@ export default function NoteTreeContextMenu({ ); return ( - + { + if (!preventNextContextMenuAutoFocusRef.current) return; + preventNextContextMenuAutoFocusRef.current = false; + event.preventDefault(); + }} + > {node?.kind === "note" ? ( onOpen(node)}> @@ -88,7 +97,12 @@ export default function NoteTreeContextMenu({ ) : null} - onRename(node)}> + { + preventNextContextMenuAutoFocusRef.current = true; + onRename(node); + }} + > {labels.rename}