feat: Update node name and description UX editing (#5920)

* ✨ (NodeName/index.tsx): add cursor-grab class to improve user experience when dragging the node
✨ (NodeName/index.tsx): add nodrag class to prevent text selection when dragging the node
✨ (NodeStatus/index.tsx): add nodrag class to prevent text selection when dragging the node
✨ (GenericNode/index.tsx): add nopan, nodelete, nodrag, noflow classes to improve node dragging behavior

* 📝 (NodeDescription/index.tsx): Update cursor style to 'auto' for better user experience
📝 (NodeName/index.tsx): Update cursor style to 'auto' for better user experience
📝 (NodeOutputfield/index.tsx): Add cursor style 'pointer' to improve interactivity

* ✨ (NodeDescription/index.tsx): Add support for editing node description when selected and editNameDescription is true
✨ (NodeName/index.tsx): Add support for editing node name when selected and editNameDescription is true
🔧 (GenericNode/index.tsx): Introduce useAlternate hook to handle toggling editNameDescription state
📝 (use-alternate.tsx): Add custom hook useAlternate to handle toggling boolean state
🔧 (style/index.css): Add new CSS variable --zinc-foreground for styling purposes
🔧 (tailwind.config.mjs): Add new tailwind color variable "zinc-foreground" for styling purposes

* 📝 (NodeDescription/index.tsx): Remove unnecessary setInputDescription call and update useEffect dependencies for better performance
📝 (NodeDescription/index.tsx): Update className for Textarea component to improve styling and readability
📝 (NodeDescription/index.tsx): Update className for generic-node-desc-text to improve styling and cursor behavior
📝 (NodeName/index.tsx): Remove unnecessary setInputName call and update useEffect dependencies for better performance
📝 (NodeName/index.tsx): Update className for span element to improve cursor behavior and styling
📝 (GenericNode/index.tsx): Add useRef for node element and implement useChangeOnUnfocus hook for better handling of focus events
📝 (GenericNode/index.tsx): Update className for pencil icon based on editNameDescription state for better visual feedback
📝 (GenericNode/index.tsx): Add editNameDescription to dependencies of useCallback to prevent unnecessary re-renders
📝 (GenericNode/index.tsx): Add editNameDescription to dependencies of useEffect to handle changes in editNameDescription state
📝 (use-change-on-unfocus.tsx): Implement custom hook useChangeOnUnfocus for handling focus events and state changes

* Refactor NodeDescription to remove old logic and variables

* Refactor NodeName component to remove unnecessary logic and variables

* [autofix.ci] apply automated fixes

* ✨ (NodeDescription/index.tsx): Add functionality to edit node description and handle events like blur, key down, and double click for sticky notes
📝 (NoteNode/index.tsx): Introduce useAlternate hook to toggle edit mode for node description in NoteNode component

* ♻️ (NoteNode/index.tsx): refactor useAlternate hook usage to simplify code and improve readability

* 🔧 (GenericNode/index.tsx): refactor className to conditionally apply translate-x styles based on showNode state for improved UI responsiveness

* 📝 (NodeDescription/index.tsx): Refactor handleBlurFn and handleKeyDownFn to improve code readability and maintainability
📝 (NodeName/index.tsx): Refactor handleBlur and handleKeyDown functions for better code organization and readability
📝 (GenericNode/index.tsx): Update toggleEditNameDescription prop to setEditNameDescription for consistency and clarity
📝 (use-change-on-unfocus.tsx): Remove unnecessary handleEscape function and handleBlur event listener for better code simplicity and performance

* 📝 (NodeDescription/index.tsx): Update CSS class name to use 'focus-border-primary' instead of 'focus-border-black' for consistency and clarity
📝 (GenericNode/index.tsx): Add data-testid attribute to save and edit name description buttons for testing purposes
📝 (edit-name-description-node.spec.ts): Add test to verify user can edit name and description of a node in the UI

* ✨ (GenericNode/index.tsx): Add functionality to show and hide toolbar with animation based on node selection status
📝 (get-class-toolbar-transform.ts): Create helper function to determine transform classes for toolbar animation based on showToolbar and showNode status

* ✨ (NodeDescription/index.tsx): add setHasChangedNodeDescription prop to update parent component when node description changes
✨ (NodeName/index.tsx): add setHasChangedNodeDescription prop to update parent component when node name changes
✨ (GenericNode/index.tsx): add hasChangedNodeDescription state and setHasChangedNodeDescription function to track changes in node description and update parent component
📝 (edit-name-description-node.spec.ts): add wait for sidebar custom component button and timeout to improve test reliability

* ✨ (group.spec.ts): Update click event on "title-Group" element to improve user interaction
🐛 (group.spec.ts): Fix click event on "save-name-description-button" element to properly save changes
🐛 (general-bugs-save-changes-on-node.spec.ts): Increase timeout for selectors to prevent test failures due to slow loading
🐛 (general-bugs-save-changes-on-node.spec.ts): Fix random value generation to ensure consistent length
🐛 (general-bugs-save-changes-on-node.spec.ts): Fix click event on "add-component-button-text-output" element to add component correctly
🐛 (general-bugs-save-changes-on-node.spec.ts): Fix timeout for selector to prevent test failures due to slow loading
🐛 (general-bugs-save-changes-on-node.spec.ts): Fix verifyTextareaValue function to properly verify textarea values

---------

Co-authored-by: anovazzi1 <otavio2204@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Cristhian Zanforlin Lousa 2025-02-05 11:02:57 -03:00 • committed by GitHub
commit 8531e1b58d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 465 additions and 127 deletions

View file

@ -22,9 +22,10 @@ test.describe("group node test", () => {
await page.getByTestId("title-OpenAI").click({ modifiers: ["Control"] });
await page.getByRole("button", { name: "Group" }).click();
await page.getByTestId("title-Group").dblclick();
await page.getByTestId("title-Group").click();
await page.getByTestId("edit-name-description-button").click();
await page.getByTestId("input-title-Group").first().fill("test");
await page.getByTestId("icon-Ungroup").first().click();
await page.getByTestId("save-name-description-button").first().click();
await page.keyboard.press("Control+g");
await page.getByTestId("title-OpenAI").isVisible();
await page.getByTestId("title-Prompt").isVisible();

View file

@ -0,0 +1,113 @@
import { expect, test } from "@playwright/test";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
test(
"user should be able to edit name and description of a node",
{ tag: ["@release", "@workspace"] },
async ({ page }) => {
const randomName = Math.random().toString(36).substring(2, 15);
const randomDescription = Math.random().toString(36).substring(2, 15);
const randomName_2 = Math.random().toString(36).substring(2, 15);
const randomDescription_2 = Math.random().toString(36).substring(2, 15);
const randomName_3 = Math.random().toString(36).substring(2, 15);
const randomDescription_3 = Math.random().toString(36).substring(2, 15);
const randomName_4 = Math.random().toString(36).substring(2, 15);
const randomDescription_4 = Math.random().toString(36).substring(2, 15);
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 30000,
});
await page.getByTestId("blank-flow").click();
await page.waitForSelector(
'[data-testid="sidebar-custom-component-button"]',
{
timeout: 30000,
},
);
await page.waitForTimeout(500);
await page.getByTestId("sidebar-custom-component-button").click();
await page.getByTestId("div-generic-node").click();
await page.getByTestId("edit-name-description-button").click();
await page.getByTestId("input-title-Custom Component").fill(randomName);
await page.getByTestId("textarea").fill(randomDescription);
await page.getByTestId("api_button_modal").click();
await page.getByText("Close").last().click();
expect(await page.getByText(randomName).count()).toBe(1);
expect(await page.getByText(randomDescription).count()).toBe(1);
await page.getByTestId("div-generic-node").click();
await page.getByTestId("edit-name-description-button").click();
await page.getByTestId(`input-title-${randomName}`).fill(randomName_2);
await page.getByTestId("textarea").fill(randomDescription_2);
await page.getByTestId("save-name-description-button").click();
expect(await page.getByText(randomName_2).count()).toBe(1);
expect(await page.getByText(randomDescription_2).count()).toBe(1);
await page.getByTestId("div-generic-node").click();
await page.getByTestId("edit-name-description-button").click();
await page.getByTestId(`input-title-${randomName_2}`).fill(randomName_3);
await page.keyboard.press("Enter");
expect(await page.getByText(randomName_3).count()).toBe(1);
await page.getByTestId("div-generic-node").click();
await page.getByTestId("edit-name-description-button").click();
await page.getByTestId(`input-title-${randomName_3}`).fill(randomName_4);
await page.getByTestId("textarea").fill(randomDescription_4);
await page.keyboard.press("Escape");
expect(await page.getByText(randomName_4).count()).toBe(1);
expect(await page.getByText(randomDescription_2).count()).toBe(1);
expect(await page.getByText(randomDescription_4).count()).toBe(0);
expect(await page.getByText(randomName_3).count()).toBe(0);
await page.getByTestId("div-generic-node").click();
await page.getByTestId("edit-name-description-button").click();
await page.getByTestId("textarea").fill(randomDescription_3);
await page.getByTestId(`input-title-${randomName_4}`).fill(randomName_3);
await page.keyboard.press("Escape");
expect(await page.getByText(randomDescription_3).count()).toBe(1);
expect(await page.getByText(randomName_4).count()).toBe(1);
expect(await page.getByText(randomName_3).count()).toBe(0);
expect(await page.getByText(randomDescription_4).count()).toBe(0);
},
);

View file

@ -2,17 +2,25 @@ import { expect, Page, test } from "@playwright/test";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
async function verifyTextareaValue(page: Page, value: string) {
await page
.getByTestId("textarea_str_input_value")
.waitFor({ state: "visible" });
await page.getByTestId("textarea_str_input_value").fill(value);
await expect(page.getByTestId("textarea_str_input_value")).toHaveValue(value);
await page.getByTestId("icon-ChevronLeft").first().click();
await page.waitForSelector('[data-testid="list-card"]', {
timeout: 3000,
timeout: 5000,
state: "visible",
});
await page.getByTestId("list-card").first().click();
await page.waitForSelector('[data-testid="textarea_str_input_value"]', {
timeout: 3000,
timeout: 5000,
state: "visible",
});
const inputValue = await page
@ -26,32 +34,35 @@ test(
{ tag: ["@release", "@components"] },
async ({ page }) => {
const randomValues = Array.from({ length: 4 }, () =>
Math.random().toString(36).substring(2, 15),
Math.random().toString(36).substring(2, 8),
);
await awaitBootstrapTest(page);
await page.getByTestId("blank-flow").click();
await page.waitForSelector('[data-testid="fit_view"]', {
timeout: 100000,
timeout: 10000,
state: "visible",
});
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("text output");
await page
.getByTestId("outputsText Output")
.hover()
.then(async () => {
await page.getByTestId("add-component-button-text-output").click();
});
await page.getByTestId("outputsText Output").waitFor({ state: "visible" });
await page.getByTestId("add-component-button-text-output").click();
await page.waitForSelector('[data-testid="title-Text Output"]', {
timeout: 3000,
timeout: 5000,
state: "visible",
});
// Verify each random value
for (const value of randomValues) {
await verifyTextareaValue(page, value);
try {
await verifyTextareaValue(page, value);
} catch (error) {
console.error(`Failed to verify value: ${value}`, error);
throw error;
}
}
},
);