From 548504294df3d4878b81b67f55bea49e62f0ae5b Mon Sep 17 00:00:00 2001 From: Shawn Chang Date: Sun, 21 Jan 2024 14:47:42 +0800 Subject: [PATCH 1/2] make node not draggable when editing the name --- .../src/reactflow/EditableName.test.tsx | 25 +++++++++++++-- .../src/reactflow/EditableName.tsx | 23 +++++++++---- .../src/reactflow/NodeTitle.test.tsx | 32 ++++++++++++++++++- .../src/reactflow/NodeTitle.tsx | 19 +++++++++-- 4 files changed, 87 insertions(+), 12 deletions(-) diff --git a/interactive-computational-graph/src/reactflow/EditableName.test.tsx b/interactive-computational-graph/src/reactflow/EditableName.test.tsx index 782f202..ea518c8 100644 --- a/interactive-computational-graph/src/reactflow/EditableName.test.tsx +++ b/interactive-computational-graph/src/reactflow/EditableName.test.tsx @@ -6,10 +6,12 @@ jest.mock("../latex/Katex"); test("should render the name only", () => { const handleNameChange = jest.fn(); + const handleEditingChange = jest.fn(); render( , ); @@ -19,11 +21,13 @@ test("should render the name only", () => { test("should render the name and operation text", () => { const operationData = getOperationData(); + const handleEditingChange = jest.fn(); const handleNameChange = jest.fn(); render( , ); @@ -32,57 +36,72 @@ test("should render the name and operation text", () => { expect(screen.getByText("(Operation 1)")).toBeInTheDocument(); }); -test("should trigger event with updated name when blurred", () => { +test("should trigger events when blurred", () => { + const handleEditingChange = jest.fn(); const handleNameChange = jest.fn(); render( , ); clickEditIcon(); + expect(handleEditingChange).toBeCalledWith(true); + changeName("123"); const input = getEditingTextbox(); fireEvent.blur(input); + expect(handleEditingChange).toBeCalledWith(false); expect(handleNameChange).toBeCalledWith("123"); }); -test("should trigger event with updated name when Enter is pressed", () => { +test("should trigger events when Enter is pressed", () => { + const handleEditingChange = jest.fn(); const handleNameChange = jest.fn(); render( , ); clickEditIcon(); + expect(handleEditingChange).toBeCalledWith(true); + changeName("123"); const input = getEditingTextbox(); fireEvent.keyDown(input, { key: "Enter" }); + expect(handleEditingChange).toBeCalledWith(false); expect(handleNameChange).toBeCalledWith("123"); }); -test("should not trigger event with old name when Escape is pressed", () => { +test("should trigger events when Escape is pressed", () => { + const handleEditingChange = jest.fn(); const handleNameChange = jest.fn(); render( , ); clickEditIcon(); + expect(handleEditingChange).toBeCalledWith(true); + changeName("123"); const input = getEditingTextbox(); fireEvent.keyDown(input, { key: "Escape" }); + expect(handleEditingChange).toBeCalledWith(false); expect(handleNameChange).not.toBeCalled(); }); diff --git a/interactive-computational-graph/src/reactflow/EditableName.tsx b/interactive-computational-graph/src/reactflow/EditableName.tsx index 7e1c1ef..1259777 100644 --- a/interactive-computational-graph/src/reactflow/EditableName.tsx +++ b/interactive-computational-graph/src/reactflow/EditableName.tsx @@ -18,17 +18,27 @@ import Katex from "../latex/Katex"; interface EditableNameProps { name: string; operationData: OperationNodeData | null; + onEditingChange: (isEditing: boolean) => void; onNameChange: (name: string) => void; } const EditableName: FunctionComponent = ({ name, operationData, + onEditingChange, onNameChange, }) => { const [isEditing, setIsEditing] = useState(false); const [editingName, setEditingName] = useState(name); + const updateEditing = useCallback( + (isEditing: boolean) => { + setIsEditing(isEditing); + onEditingChange(isEditing); + }, + [onEditingChange], + ); + const handleChange = useCallback((name: string) => { setEditingName(name); }, []); @@ -36,28 +46,29 @@ const EditableName: FunctionComponent = ({ const handleSaveName = useCallback( (name: string) => { onNameChange(name); - setIsEditing(false); + updateEditing(false); }, - [onNameChange], + [onNameChange, updateEditing], ); const handleKeyDown = useCallback( (e: KeyboardEvent) => { if (e.key === "Enter") { onNameChange(editingName); - setIsEditing(false); + updateEditing(false); } else if (e.key === "Escape") { setEditingName(name); - setIsEditing(false); + updateEditing(false); } }, - [editingName, name, onNameChange], + [editingName, name, onNameChange, updateEditing], ); return ( {isEditing ? ( { @@ -93,7 +104,7 @@ const EditableName: FunctionComponent = ({ aria-label="edit" size="small" onClick={() => { - setIsEditing(true); + updateEditing(true); }} > diff --git a/interactive-computational-graph/src/reactflow/NodeTitle.test.tsx b/interactive-computational-graph/src/reactflow/NodeTitle.test.tsx index 8b49a65..9f91901 100644 --- a/interactive-computational-graph/src/reactflow/NodeTitle.test.tsx +++ b/interactive-computational-graph/src/reactflow/NodeTitle.test.tsx @@ -1,4 +1,4 @@ -import { render, screen } from "@testing-library/react"; +import { fireEvent, render, screen } from "@testing-library/react"; import type OperationNodeData from "../features/OperationNodeData"; import NodeTitle from "./NodeTitle"; @@ -22,6 +22,36 @@ test("should render the name", () => { expect(screen.getByText("abc")).toBeInTheDocument(); }); +test("should not have draggable class when editing the name", () => { + const operationData = getOperationData(); + const handleNameChange = jest.fn(); + render( + , + ); + + const nodeTitle = screen.getByTestId("node-title-1"); + const editIcon = screen.getByLabelText("edit"); + + expect(nodeTitle).toHaveClass("drag-handle"); + + fireEvent.click(editIcon); + + expect(nodeTitle).not.toHaveClass("drag-handle"); + + const editingInput = screen.getByLabelText("editingName"); + fireEvent.blur(editingInput); + + expect(nodeTitle).toHaveClass("drag-handle"); +}); + test("should not have striped animation when not highlighted", () => { const operationData = getOperationData(); const handleNameChange = jest.fn(); diff --git a/interactive-computational-graph/src/reactflow/NodeTitle.tsx b/interactive-computational-graph/src/reactflow/NodeTitle.tsx index 009381d..c27b8ee 100644 --- a/interactive-computational-graph/src/reactflow/NodeTitle.tsx +++ b/interactive-computational-graph/src/reactflow/NodeTitle.tsx @@ -1,6 +1,6 @@ import DragIndicatorIcon from "@mui/icons-material/DragIndicator"; import { Box, Stack } from "@mui/material"; -import { useMemo, type FunctionComponent } from "react"; +import { useCallback, useMemo, useState, type FunctionComponent } from "react"; import type OperationNodeData from "../features/OperationNodeData"; import EditableName from "./EditableName"; import "./NodeTitle.css"; @@ -31,11 +31,25 @@ const NodeTitle: FunctionComponent = ({ return isDarkMode ? "striped-animation-dark" : "striped-animation-light"; }, [isDarkMode, isHighlighted]); + const [isEditingName, setIsEditingName] = useState(false); + + const getClassName = useCallback(() => { + if (isEditingName) { + return `${animationClassName}`; + } else { + return `drag-handle ${animationClassName}`; + } + }, [animationClassName, isEditingName]); + + const handleEditingChange = useCallback((isEditing: boolean) => { + setIsEditingName(isEditing); + }, []); + return ( = ({ From aae1fdc3566ed02c114ecdc2561baad7c348d9f3 Mon Sep 17 00:00:00 2001 From: Shawn Chang Date: Sun, 21 Jan 2024 14:53:26 +0800 Subject: [PATCH 2/2] fix label --- .../src/reactflow/EditableName.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interactive-computational-graph/src/reactflow/EditableName.tsx b/interactive-computational-graph/src/reactflow/EditableName.tsx index 1259777..cb89483 100644 --- a/interactive-computational-graph/src/reactflow/EditableName.tsx +++ b/interactive-computational-graph/src/reactflow/EditableName.tsx @@ -68,7 +68,9 @@ const EditableName: FunctionComponent = ({ {isEditing ? ( {