feat: Clear handles from advanced fields on code update (#8754)

*  (frontend): add new function clearHandlesFromAdvancedFields to handle clearing handles from advanced fields when code is updated
📝 (frontend): update documentation and add tests for deleting handles from advanced fields when code is updated
🔧 (frontend): remove unused style for Outlook in styleUtils to clean up code
🔧 (frontend): add test for deleting handles from advanced fields when code is updated in general-bugs-delete-handle-advanced-input.spec

*  (frontend): add support for managing edges and nodes in the flowStore to improve data handling and flow visualization
♻️ (frontend): refactor cleanEdges function to accept an object with nodes, edges, componentId, and data parameters for better modularity and reusability

*  (codeAreaModal/index.tsx): Refactor import statements and function names for better clarity and organization
♻️ (flowStore.ts): Refactor cleanEdges function to accept nodes and edges as separate arguments instead of an object
♻️ (flowStore.ts): Update all calls to cleanEdges function to pass nodes and edges separately
♻️ (reactflowUtils.ts): Refactor clearHandlesFromAdvancedFields function to accept only necessary arguments and remove unnecessary logic
This commit is contained in:
Cristhian Zanforlin Lousa 2025-06-26 17:41:09 -03:00 committed by GitHub
commit 65b71bec5b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 132 additions and 3 deletions

View file

@ -1,6 +1,6 @@
import { usePostValidateCode } from "@/controllers/API/queries/nodes/use-post-validate-code";
import { usePostValidateComponentCode } from "@/controllers/API/queries/nodes/use-post-validate-component-code";
import useFlowStore from "@/stores/flowStore";
import { clearHandlesFromAdvancedFields } from "@/utils/reactflowUtils";
import "ace-builds/src-noconflict/ace";
import "ace-builds/src-noconflict/ext-language_tools";
import "ace-builds/src-noconflict/ext-searchbox";
@ -83,7 +83,6 @@ export default function CodeAreaModal({
});
setOpen(false);
setValue(code);
// setValue(code);
} else {
if (funcErrors.length !== 0) {
setErrorData({
@ -121,6 +120,8 @@ export default function CodeAreaModal({
onSuccess: ({ data, type }) => {
if (data && type) {
setValue(code);
clearHandlesFromAdvancedFields(componentId!, data);
setNodeClass(data, type);
setError({ detail: { error: undefined, traceback: undefined } });
setOpen(false);
@ -255,6 +256,7 @@ export default function CodeAreaModal({
type="submit"
id="checkAndSaveBtn"
disabled={readonly}
data-testid="checkAndSaveBtn"
>
Check & Save
</Button>

View file

@ -176,6 +176,45 @@ export function cleanEdges(nodes: AllNodeType[], edges: EdgeType[]) {
return newEdges;
}
export function clearHandlesFromAdvancedFields(
componentId: string,
data: APIClassType,
): void {
if (!componentId || !data?.template) {
return;
}
try {
const flowStore = useFlowStore.getState();
const { edges, deleteEdge } = flowStore;
const connectedEdges = edges.filter((edge) => edge.target === componentId);
if (connectedEdges.length === 0) {
return;
}
const edgeIdsToDelete: string[] = [];
for (const edge of connectedEdges) {
const fieldName = edge.data?.targetHandle?.fieldName;
if (fieldName && isAdvancedField(data, fieldName)) {
edgeIdsToDelete.push(edge.id);
}
}
edgeIdsToDelete.forEach(deleteEdge);
} catch (error) {
console.error("Error clearing handles from advanced fields:", error);
}
}
const isAdvancedField = (data: APIClassType, fieldName: string): boolean => {
const field = data.template[fieldName];
return field && "advanced" in field && field.advanced === true;
};
export function filterHiddenFieldsEdges(
edge: EdgeType,
newEdges: EdgeType[],

View file

@ -229,7 +229,6 @@ export const SIDEBAR_CATEGORIES = [
];
export const SIDEBAR_BUNDLES = [
{ display_name: "Outlook", name: "outlook", icon: "Outlook" },
{
display_name: "Language Models",
name: "languagemodels",

View file

@ -0,0 +1,89 @@
import { expect, test } from "@playwright/test";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { extractAndCleanCode } from "../../utils/extract-and-clean-code";
import { zoomOut } from "../../utils/zoom-out";
test(
"the system must delete the handles from advanced fields when the code is updated",
{ tag: ["@release"] },
async ({ page }) => {
await awaitBootstrapTest(page);
await page.getByTestId("blank-flow").click();
await page.waitForSelector('[data-testid="fit_view"]', {
timeout: 100000,
});
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("if else");
await page
.getByTestId("logicIf-Else")
.hover()
.then(async () => {
await page.getByTestId("add-component-button-if-else").click();
});
await page.getByTestId("fit_view").click();
await zoomOut(page, 3);
await page.getByTestId("edit-button-modal").click();
await page.getByTestId("showmessage").click();
await page.getByText("Close").last().click();
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("text input");
await page.waitForSelector('[data-testid="input_outputText Input"]', {
timeout: 2000,
});
await page
.getByTestId("input_outputText Input")
.dragTo(page.locator('//*[@id="react-flow-id"]'), {
targetPosition: { x: 200, y: 100 },
});
await page
.getByTestId("handle-textinput-shownode-output text-right")
.click();
await page
.getByTestId("handle-conditionalrouter-shownode-alternative output-left")
.click();
await page.getByTestId("title-If-Else").click();
await page.getByTestId("edit-button-modal").click();
const numberOfDisabledInputs = await page
.getByPlaceholder("Receiving input")
.count();
expect(numberOfDisabledInputs).toBe(2);
const numberOfLockIcons = await page.getByTestId("icon-lock").count();
expect(numberOfLockIcons).toBe(2);
await page.getByText("Close").last().click();
await page.getByTestId("title-If-Else").click();
await page.getByTestId("code-button-modal").click();
await page.getByTestId("checkAndSaveBtn").last().click();
await page.getByTestId("edit-button-modal").click();
const numberOfDisabledInputsAfter = await page
.getByPlaceholder("Receiving input")
.count();
expect(numberOfDisabledInputsAfter).toBe(0);
const numberOfLockIconsAfter = await page.getByTestId("icon-lock").count();
expect(numberOfLockIconsAfter).toBe(0);
},
);