From bd3579812342ca6acdab1ccb051467b17a1216b3 Mon Sep 17 00:00:00 2001 From: handreyrc Date: Thu, 25 Jun 2026 18:54:56 -0400 Subject: [PATCH 1/4] Fix edge labels rendered behind nodes and selected edges Signed-off-by: handreyrc --- .../src/react-flow/diagram/Diagram.css | 7 ++ .../src/react-flow/diagram/Diagram.tsx | 14 ++- .../src/react-flow/edges/Edges.tsx | 4 +- .../tests/react-flow/diagram/Diagram.test.tsx | 87 ++++++++++++++---- .../tests/react-flow/edges/Edges.test.tsx | 88 +++++++++++++++++++ 5 files changed, 180 insertions(+), 20 deletions(-) diff --git a/packages/serverless-workflow-diagram-editor/src/react-flow/diagram/Diagram.css b/packages/serverless-workflow-diagram-editor/src/react-flow/diagram/Diagram.css index 42d7c67..743e4f7 100644 --- a/packages/serverless-workflow-diagram-editor/src/react-flow/diagram/Diagram.css +++ b/packages/serverless-workflow-diagram-editor/src/react-flow/diagram/Diagram.css @@ -18,6 +18,13 @@ /* react flow overrides */ @layer react-flow-overrides { + /* Elevate the edge-label renderer container above nodes. + React Flow renders .react-flow__edgelabel-renderer before NodeRenderer + in the DOM, so without an explicit z-index it is painted behind nodes. */ + .dec-root .react-flow__edgelabel-renderer { + z-index: 1000; + } + .dec-root .diagram-background { --xy-background-pattern-color: #ccc; background-color: #e5e4e2; diff --git a/packages/serverless-workflow-diagram-editor/src/react-flow/diagram/Diagram.tsx b/packages/serverless-workflow-diagram-editor/src/react-flow/diagram/Diagram.tsx index 861331f..5d59542 100644 --- a/packages/serverless-workflow-diagram-editor/src/react-flow/diagram/Diagram.tsx +++ b/packages/serverless-workflow-diagram-editor/src/react-flow/diagram/Diagram.tsx @@ -72,10 +72,11 @@ export const Diagram = ({ divRef, ref, colorMode = "light" }: DiagramProps) => { setEdges((edgesSnapshot) => { const updatedEdges = RF.applyEdgeChanges(changes, edgesSnapshot); - // Update zIndex for selected edges to bring them to front + // Elevate selected edges above regular edges (z-index: 100) + // but keep them below edge labels (z-index: 1000+) return updatedEdges.map((edge) => ({ ...edge, - zIndex: edge.selected ? 1000 : 0, + zIndex: edge.selected ? 100 : 0, })); }); }, @@ -105,7 +106,13 @@ export const Diagram = ({ divRef, ref, colorMode = "light" }: DiagramProps) => { // Only update if this effect is still active (not cancelled by cleanup) if (isActive && !abortController?.signal.aborted) { setNodes(nodes); - setEdges(edges); + // Apply z-index to edges: selected edges (100) above regular edges (0) + // but below edge labels (1000+) + const edgesWithZIndex = edges.map((edge) => ({ + ...edge, + zIndex: edge.selected ? 100 : 0, + })); + setEdges(edgesWithZIndex); // Queue fitView to run after React updates the DOM fitViewTimeoutId = setTimeout(() => reactFlowInstance.fitView(), 0); @@ -173,6 +180,7 @@ export const Diagram = ({ divRef, ref, colorMode = "light" }: DiagramProps) => { }, }} data-testid={"react-flow-canvas"} + elevateEdgesOnSelect={false} nodesDraggable={!isReadOnly} nodesConnectable={!isReadOnly} > diff --git a/packages/serverless-workflow-diagram-editor/src/react-flow/edges/Edges.tsx b/packages/serverless-workflow-diagram-editor/src/react-flow/edges/Edges.tsx index 5a031e7..a26c868 100644 --- a/packages/serverless-workflow-diagram-editor/src/react-flow/edges/Edges.tsx +++ b/packages/serverless-workflow-diagram-editor/src/react-flow/edges/Edges.tsx @@ -43,6 +43,7 @@ export type EdgeLabelProps = { targetPosition?: RF.Position; type?: EdgeTypes; data?: BaseEdgeData | undefined; + selected?: boolean; }; export function createPathFromWayPoints( @@ -127,7 +128,7 @@ function getEdgeLabelPosition({ } export function EdgeLabel(props: EdgeLabelProps) { - const { type, data } = props; + const { type, data, selected } = props; const { x, y } = getEdgeLabelPosition(props); return ( @@ -137,6 +138,7 @@ export function EdgeLabel(props: EdgeLabelProps) {
diff --git a/packages/serverless-workflow-diagram-editor/tests/react-flow/diagram/Diagram.test.tsx b/packages/serverless-workflow-diagram-editor/tests/react-flow/diagram/Diagram.test.tsx index fbcc9b5..b289915 100644 --- a/packages/serverless-workflow-diagram-editor/tests/react-flow/diagram/Diagram.test.tsx +++ b/packages/serverless-workflow-diagram-editor/tests/react-flow/diagram/Diagram.test.tsx @@ -183,27 +183,51 @@ describe("Diagram Component", () => { }); }); - describe("onEdgesChange with zIndex updates", () => { - it("should provide onEdgesChange callback to ReactFlow", async () => { + it("should disable automatic edge elevation on select", async () => { + renderDiagram({ isReadOnly: false }); + + // Wait for ReactFlow to be called + await waitFor(() => { + expect(ReactFlow).toHaveBeenCalled(); + }); + + // Verify that ReactFlow was called with elevateEdgesOnSelect={false} + const mockReactFlow = vi.mocked(ReactFlow); + const lastCall = mockReactFlow.mock.calls.at(-1); + expect(lastCall).toBeDefined(); + const reactFlowProps = lastCall![0]; + expect(reactFlowProps.elevateEdgesOnSelect).toBe(false); + + await waitFor(() => { + expect(applyAutoLayoutSpy).toHaveBeenCalled(); + }); + }); + + describe("edge z-index management", () => { + it("should apply correct z-index to edges based on selection state", async () => { + applyAutoLayoutSpy.mockResolvedValueOnce({ + nodes: [], + edges: [ + { id: "edge1", source: "n1", target: "n2", selected: false }, + { id: "edge2", source: "n2", target: "n3", selected: false }, + ], + }); + renderDiagram({ isReadOnly: false }); - // Wait for initial render await waitFor(() => { - expect(applyAutoLayoutSpy).toHaveBeenCalled(); + const lastCall = vi.mocked(ReactFlow).mock.calls.at(-1); + expect(lastCall).toBeDefined(); + expect(lastCall![0].edges).toHaveLength(2); }); - // Get the onEdgesChange callback from ReactFlow mock - const mockReactFlow = vi.mocked(ReactFlow); - const lastCall = mockReactFlow.mock.calls.at(-1); - expect(lastCall).toBeDefined(); - const reactFlowProps = lastCall![0]; - const onEdgesChange = reactFlowProps.onEdgesChange; - - expect(onEdgesChange).toBeDefined(); - expect(typeof onEdgesChange).toBe("function"); + // All unselected edges should have zIndex: 0 + const edges = vi.mocked(ReactFlow).mock.calls.at(-1)![0].edges!; + expect(edges.find((e: RF.Edge) => e.id === "edge1")?.zIndex).toBe(0); + expect(edges.find((e: RF.Edge) => e.id === "edge2")?.zIndex).toBe(0); }); - it("should apply zIndex correctly when edges are updated", async () => { + it("should elevate selected edge above regular edges but below labels", async () => { applyAutoLayoutSpy.mockResolvedValueOnce({ nodes: [], edges: [ @@ -232,8 +256,39 @@ describe("Diagram Component", () => { await waitFor(() => { const edges = vi.mocked(ReactFlow).mock.calls.at(-1)![0].edges!; - expect(edges.find((e: RF.Edge) => e.id === "edge1")?.zIndex).toBe(1000); - expect(edges.find((e: RF.Edge) => e.id === "edge2")?.zIndex).toBe(1000); + // Selected edge should have zIndex: 100 (above regular edges, below labels at 1000+) + expect(edges.find((e: RF.Edge) => e.id === "edge1")?.zIndex).toBe(100); + expect(edges.find((e: RF.Edge) => e.id === "edge2")?.zIndex).toBe(100); + }); + }); + + it("should maintain z-index hierarchy: regular edges (0) < selected edges (100) < labels (1000+)", async () => { + applyAutoLayoutSpy.mockResolvedValueOnce({ + nodes: [], + edges: [ + { id: "edge1", source: "n1", target: "n2", selected: true }, + { id: "edge2", source: "n2", target: "n3", selected: false }, + ], + }); + + renderDiagram({ isReadOnly: false }); + + await waitFor(() => { + const lastCall = vi.mocked(ReactFlow).mock.calls.at(-1); + expect(lastCall).toBeDefined(); + const edges = lastCall![0].edges!; + + // Verify z-index hierarchy + const selectedEdge = edges.find((e: RF.Edge) => e.id === "edge1"); + const regularEdge = edges.find((e: RF.Edge) => e.id === "edge2"); + + expect(selectedEdge?.zIndex).toBe(100); + expect(regularEdge?.zIndex).toBe(0); + + // Hierarchy in the test + // Regular edges: 0 + // Selected edges: 100 + // Edge labels: 1000+ (tested in Edges.test.tsx) }); }); }); diff --git a/packages/serverless-workflow-diagram-editor/tests/react-flow/edges/Edges.test.tsx b/packages/serverless-workflow-diagram-editor/tests/react-flow/edges/Edges.test.tsx index 7f48f0c..a0e097e 100644 --- a/packages/serverless-workflow-diagram-editor/tests/react-flow/edges/Edges.test.tsx +++ b/packages/serverless-workflow-diagram-editor/tests/react-flow/edges/Edges.test.tsx @@ -498,3 +498,91 @@ describe("EdgeLabel positioning", () => { expect(JSON.stringify(result)).toContain(`translate(${labelX}px,${labelY}px)`); }); }); + +describe("EdgeLabel z-index behavior", () => { + it.each([ + { + selected: false, + expectedZIndex: 1000, + description: "applies z-index 1000 when selected=false", + }, + { + selected: true, + expectedZIndex: 1001, + description: "applies z-index 1001 when selected=true", + }, + { + selected: undefined, + expectedZIndex: 1000, + description: "applies default z-index 1000 when selected=undefined", + }, + ])("$description", ({ selected, expectedZIndex }) => { + const result = EdgeLabel({ + sourceX: 0, + sourceY: 0, + targetX: 100, + targetY: 100, + data: { label: "Test Label" }, + selected, + }); + + const resultString = JSON.stringify(result); + expect(resultString).toContain(`"zIndex":${expectedZIndex}`); + }); + + it("applies default z-index 1000 when selected prop is not provided", () => { + const result = EdgeLabel({ + sourceX: 0, + sourceY: 0, + targetX: 100, + targetY: 100, + data: { label: "Test Label" }, + }); + + const resultString = JSON.stringify(result); + expect(resultString).toContain('"zIndex":1000'); + }); + + describe("integration with edge components", () => { + it.each([ + { component: DefaultEdge, edgeType: "default", edgeClass: "", selected: true }, + { component: ErrorEdge, edgeType: "error", edgeClass: "error", selected: true }, + { component: ConditionEdge, edgeType: "condition", edgeClass: "condition", selected: true }, + { component: DefaultEdge, edgeType: "default", edgeClass: "", selected: false }, + { component: ErrorEdge, edgeType: "error", edgeClass: "error", selected: false }, + { component: ConditionEdge, edgeType: "condition", edgeClass: "condition", selected: false }, + ])( + "$edgeType edge passes selected=$selected to EdgeLabel", + ({ component: Component, edgeClass, selected }) => { + const { container } = render( + + + , + ); + + // Verify the edge path is rendered with correct selected class + const pathSelector = edgeClass ? `path.edge-line.${edgeClass}` : "path.edge-line"; + const path = container.querySelector(pathSelector); + expect(path).toBeInTheDocument(); + + if (selected) { + expect(path).toHaveClass("selected"); + } else { + expect(path).not.toHaveClass("selected"); + } + }, + ); + }); +}); From 93d2ca9c286520447889cbaec5ebd69e2b0d712f Mon Sep 17 00:00:00 2001 From: handreyrc Date: Thu, 25 Jun 2026 19:47:30 -0400 Subject: [PATCH 2/4] fix copilot complaints Signed-off-by: handreyrc --- .../src/react-flow/diagram/Diagram.css | 2 +- .../src/react-flow/diagram/Diagram.tsx | 13 +++--- .../src/react-flow/edges/Edges.tsx | 3 +- .../src/react-flow/zIndexConstants.ts | 43 +++++++++++++++++++ .../src/styles.css | 3 ++ .../tests/react-flow/diagram/Diagram.test.tsx | 7 +-- .../tests/react-flow/edges/Edges.test.tsx | 19 ++++---- 7 files changed, 70 insertions(+), 20 deletions(-) create mode 100644 packages/serverless-workflow-diagram-editor/src/react-flow/zIndexConstants.ts diff --git a/packages/serverless-workflow-diagram-editor/src/react-flow/diagram/Diagram.css b/packages/serverless-workflow-diagram-editor/src/react-flow/diagram/Diagram.css index 743e4f7..47afe0a 100644 --- a/packages/serverless-workflow-diagram-editor/src/react-flow/diagram/Diagram.css +++ b/packages/serverless-workflow-diagram-editor/src/react-flow/diagram/Diagram.css @@ -22,7 +22,7 @@ React Flow renders .react-flow__edgelabel-renderer before NodeRenderer in the DOM, so without an explicit z-index it is painted behind nodes. */ .dec-root .react-flow__edgelabel-renderer { - z-index: 1000; + z-index: var(--dec-zindex-edge-label-regular); } .dec-root .diagram-background { diff --git a/packages/serverless-workflow-diagram-editor/src/react-flow/diagram/Diagram.tsx b/packages/serverless-workflow-diagram-editor/src/react-flow/diagram/Diagram.tsx index 5d59542..80854be 100644 --- a/packages/serverless-workflow-diagram-editor/src/react-flow/diagram/Diagram.tsx +++ b/packages/serverless-workflow-diagram-editor/src/react-flow/diagram/Diagram.tsx @@ -25,6 +25,7 @@ import { useDiagramEditorContext } from "../../store/DiagramEditorContext"; import { buildDiagramElements } from "./diagramBuilder"; import { applyAutoLayout } from "./autoLayout"; import { SidePanelTrigger } from "@/side-panel/SidePanelTrigger"; +import { ZINDEX } from "../zIndexConstants"; const FIT_VIEW_OPTIONS: RF.FitViewOptions = { maxZoom: 1, @@ -72,11 +73,11 @@ export const Diagram = ({ divRef, ref, colorMode = "light" }: DiagramProps) => { setEdges((edgesSnapshot) => { const updatedEdges = RF.applyEdgeChanges(changes, edgesSnapshot); - // Elevate selected edges above regular edges (z-index: 100) - // but keep them below edge labels (z-index: 1000+) + // Elevate selected edges above regular edges + // but keep them below edge labels return updatedEdges.map((edge) => ({ ...edge, - zIndex: edge.selected ? 100 : 0, + zIndex: edge.selected ? ZINDEX.EDGE_SELECTED : ZINDEX.EDGE_REGULAR, })); }); }, @@ -106,11 +107,11 @@ export const Diagram = ({ divRef, ref, colorMode = "light" }: DiagramProps) => { // Only update if this effect is still active (not cancelled by cleanup) if (isActive && !abortController?.signal.aborted) { setNodes(nodes); - // Apply z-index to edges: selected edges (100) above regular edges (0) - // but below edge labels (1000+) + // Apply z-index to edges: selected edges above regular edges + // but below edge labels const edgesWithZIndex = edges.map((edge) => ({ ...edge, - zIndex: edge.selected ? 100 : 0, + zIndex: edge.selected ? ZINDEX.EDGE_SELECTED : ZINDEX.EDGE_REGULAR, })); setEdges(edgesWithZIndex); diff --git a/packages/serverless-workflow-diagram-editor/src/react-flow/edges/Edges.tsx b/packages/serverless-workflow-diagram-editor/src/react-flow/edges/Edges.tsx index a26c868..40fdb2a 100644 --- a/packages/serverless-workflow-diagram-editor/src/react-flow/edges/Edges.tsx +++ b/packages/serverless-workflow-diagram-editor/src/react-flow/edges/Edges.tsx @@ -16,6 +16,7 @@ import * as RF from "@xyflow/react"; import type { Point, WayPoints } from "../diagram/autoLayout"; +import { ZINDEX } from "../zIndexConstants"; export enum EdgeTypes { Default = "default", @@ -138,7 +139,7 @@ export function EdgeLabel(props: EdgeLabelProps) {
diff --git a/packages/serverless-workflow-diagram-editor/src/react-flow/zIndexConstants.ts b/packages/serverless-workflow-diagram-editor/src/react-flow/zIndexConstants.ts new file mode 100644 index 0000000..b4cda4f --- /dev/null +++ b/packages/serverless-workflow-diagram-editor/src/react-flow/zIndexConstants.ts @@ -0,0 +1,43 @@ +/* + * Copyright 2021-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Z-index layering constants for diagram edges and labels. + * + * Layering hierarchy (bottom to top): + * 1. Regular edges (0) + * 2. Selected edges (100) + * 3. Edge labels - regular (1000) + * 4. Edge labels - selected (1001) + * + * This ensures: + * - Selected edges appear above regular edges + * - All labels appear above all edges (preventing overlap) + * - Selected edge labels appear above regular labels + */ +export const ZINDEX = { + /** Regular (unselected) edges */ + EDGE_REGULAR: 0, + + /** Selected edges - appear above regular edges but below all labels */ + EDGE_SELECTED: 100, + + /** Regular (unselected) edge labels - appear above all edges */ + EDGE_LABEL_REGULAR: 1000, + + /** Selected edge labels - appear above everything */ + EDGE_LABEL_SELECTED: 1001, +} as const; diff --git a/packages/serverless-workflow-diagram-editor/src/styles.css b/packages/serverless-workflow-diagram-editor/src/styles.css index 486f72d..ca40758 100644 --- a/packages/serverless-workflow-diagram-editor/src/styles.css +++ b/packages/serverless-workflow-diagram-editor/src/styles.css @@ -45,6 +45,9 @@ .dec-root { @apply dec:h-full; + /* Z-index layering for edges and labels */ + --dec-zindex-edge-label-regular: 1000; + --dec-error-accent: #ef4444; --dec-error-glow: rgba(239, 68, 68, 0.35); --dec-edge-selected: #aea6a6; diff --git a/packages/serverless-workflow-diagram-editor/tests/react-flow/diagram/Diagram.test.tsx b/packages/serverless-workflow-diagram-editor/tests/react-flow/diagram/Diagram.test.tsx index b289915..a6f35b3 100644 --- a/packages/serverless-workflow-diagram-editor/tests/react-flow/diagram/Diagram.test.tsx +++ b/packages/serverless-workflow-diagram-editor/tests/react-flow/diagram/Diagram.test.tsx @@ -24,6 +24,7 @@ import { en } from "../../../src/i18n/locales/en"; import { ReactFlowProvider, ReactFlow } from "@xyflow/react"; import * as RF from "@xyflow/react"; import * as autoLayoutModule from "../../../src/react-flow/diagram/autoLayout"; +import { ZINDEX } from "../../../src/react-flow/zIndexConstants"; // Mock ReactFlow to capture props vi.mock("@xyflow/react", async () => { @@ -256,9 +257,9 @@ describe("Diagram Component", () => { await waitFor(() => { const edges = vi.mocked(ReactFlow).mock.calls.at(-1)![0].edges!; - // Selected edge should have zIndex: 100 (above regular edges, below labels at 1000+) - expect(edges.find((e: RF.Edge) => e.id === "edge1")?.zIndex).toBe(100); - expect(edges.find((e: RF.Edge) => e.id === "edge2")?.zIndex).toBe(100); + // Selected edges should have elevated z-index (above regular edges, below labels) + expect(edges.find((e: RF.Edge) => e.id === "edge1")?.zIndex).toBe(ZINDEX.EDGE_SELECTED); + expect(edges.find((e: RF.Edge) => e.id === "edge2")?.zIndex).toBe(ZINDEX.EDGE_SELECTED); }); }); diff --git a/packages/serverless-workflow-diagram-editor/tests/react-flow/edges/Edges.test.tsx b/packages/serverless-workflow-diagram-editor/tests/react-flow/edges/Edges.test.tsx index a0e097e..734376f 100644 --- a/packages/serverless-workflow-diagram-editor/tests/react-flow/edges/Edges.test.tsx +++ b/packages/serverless-workflow-diagram-editor/tests/react-flow/edges/Edges.test.tsx @@ -27,6 +27,7 @@ import { createPathFromWayPoints, getWayPointsMidpoint, } from "../../../src/react-flow/edges/Edges"; +import { ZINDEX } from "../../../src/react-flow/zIndexConstants"; import * as RF from "@xyflow/react"; describe("React Flow custom edge types", () => { @@ -503,18 +504,18 @@ describe("EdgeLabel z-index behavior", () => { it.each([ { selected: false, - expectedZIndex: 1000, - description: "applies z-index 1000 when selected=false", + expectedZIndex: ZINDEX.EDGE_LABEL_REGULAR, + description: "applies regular label z-index when selected=false", }, { selected: true, - expectedZIndex: 1001, - description: "applies z-index 1001 when selected=true", + expectedZIndex: ZINDEX.EDGE_LABEL_SELECTED, + description: "applies selected label z-index when selected=true", }, { selected: undefined, - expectedZIndex: 1000, - description: "applies default z-index 1000 when selected=undefined", + expectedZIndex: ZINDEX.EDGE_LABEL_REGULAR, + description: "applies default regular label z-index when selected=undefined", }, ])("$description", ({ selected, expectedZIndex }) => { const result = EdgeLabel({ @@ -530,7 +531,7 @@ describe("EdgeLabel z-index behavior", () => { expect(resultString).toContain(`"zIndex":${expectedZIndex}`); }); - it("applies default z-index 1000 when selected prop is not provided", () => { + it("applies default regular label z-index when selected prop is not provided", () => { const result = EdgeLabel({ sourceX: 0, sourceY: 0, @@ -540,7 +541,7 @@ describe("EdgeLabel z-index behavior", () => { }); const resultString = JSON.stringify(result); - expect(resultString).toContain('"zIndex":1000'); + expect(resultString).toContain(`"zIndex":${ZINDEX.EDGE_LABEL_REGULAR}`); }); describe("integration with edge components", () => { @@ -552,7 +553,7 @@ describe("EdgeLabel z-index behavior", () => { { component: ErrorEdge, edgeType: "error", edgeClass: "error", selected: false }, { component: ConditionEdge, edgeType: "condition", edgeClass: "condition", selected: false }, ])( - "$edgeType edge passes selected=$selected to EdgeLabel", + "$edgeType edge applies selected class when selected=$selected", ({ component: Component, edgeClass, selected }) => { const { container } = render( From 99f8a18508306d294791f9449d5cddf9e41f3cfc Mon Sep 17 00:00:00 2001 From: handreyrc Date: Fri, 26 Jun 2026 09:16:21 -0400 Subject: [PATCH 3/4] fix pnpm-lock.yaml Signed-off-by: handreyrc --- pnpm-lock.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3f9083f..da58998 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7443,4 +7443,4 @@ snapshots: use-sync-external-store: 1.6.0(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 - react: 19.2.7 + react: 19.2.7 \ No newline at end of file From 5d5c9e0cdd187ffe43efddadf936a2850af2ca1e Mon Sep 17 00:00:00 2001 From: handreyrc Date: Fri, 26 Jun 2026 09:46:50 -0400 Subject: [PATCH 4/4] fix copilot complaints Signed-off-by: handreyrc --- .../src/react-flow/diagram/Diagram.tsx | 22 +++++++------------ .../tests/react-flow/diagram/Diagram.test.tsx | 8 +++---- pnpm-lock.yaml | 2 +- 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/packages/serverless-workflow-diagram-editor/src/react-flow/diagram/Diagram.tsx b/packages/serverless-workflow-diagram-editor/src/react-flow/diagram/Diagram.tsx index 80854be..ac542f1 100644 --- a/packages/serverless-workflow-diagram-editor/src/react-flow/diagram/Diagram.tsx +++ b/packages/serverless-workflow-diagram-editor/src/react-flow/diagram/Diagram.tsx @@ -33,6 +33,12 @@ const FIT_VIEW_OPTIONS: RF.FitViewOptions = { duration: 400, }; +const applyEdgeZIndex = (edges: T[]): T[] => + edges.map((edge) => ({ + ...edge, + zIndex: edge.selected ? ZINDEX.EDGE_SELECTED : ZINDEX.EDGE_REGULAR, + })); + /** * Diagram component API */ @@ -72,13 +78,7 @@ export const Diagram = ({ divRef, ref, colorMode = "light" }: DiagramProps) => { (changes) => { setEdges((edgesSnapshot) => { const updatedEdges = RF.applyEdgeChanges(changes, edgesSnapshot); - - // Elevate selected edges above regular edges - // but keep them below edge labels - return updatedEdges.map((edge) => ({ - ...edge, - zIndex: edge.selected ? ZINDEX.EDGE_SELECTED : ZINDEX.EDGE_REGULAR, - })); + return applyEdgeZIndex(updatedEdges); }); }, [setEdges], @@ -107,13 +107,7 @@ export const Diagram = ({ divRef, ref, colorMode = "light" }: DiagramProps) => { // Only update if this effect is still active (not cancelled by cleanup) if (isActive && !abortController?.signal.aborted) { setNodes(nodes); - // Apply z-index to edges: selected edges above regular edges - // but below edge labels - const edgesWithZIndex = edges.map((edge) => ({ - ...edge, - zIndex: edge.selected ? ZINDEX.EDGE_SELECTED : ZINDEX.EDGE_REGULAR, - })); - setEdges(edgesWithZIndex); + setEdges(applyEdgeZIndex(edges)); // Queue fitView to run after React updates the DOM fitViewTimeoutId = setTimeout(() => reactFlowInstance.fitView(), 0); diff --git a/packages/serverless-workflow-diagram-editor/tests/react-flow/diagram/Diagram.test.tsx b/packages/serverless-workflow-diagram-editor/tests/react-flow/diagram/Diagram.test.tsx index a6f35b3..4628d21 100644 --- a/packages/serverless-workflow-diagram-editor/tests/react-flow/diagram/Diagram.test.tsx +++ b/packages/serverless-workflow-diagram-editor/tests/react-flow/diagram/Diagram.test.tsx @@ -224,8 +224,8 @@ describe("Diagram Component", () => { // All unselected edges should have zIndex: 0 const edges = vi.mocked(ReactFlow).mock.calls.at(-1)![0].edges!; - expect(edges.find((e: RF.Edge) => e.id === "edge1")?.zIndex).toBe(0); - expect(edges.find((e: RF.Edge) => e.id === "edge2")?.zIndex).toBe(0); + expect(edges.find((e: RF.Edge) => e.id === "edge1")?.zIndex).toBe(ZINDEX.EDGE_REGULAR); + expect(edges.find((e: RF.Edge) => e.id === "edge2")?.zIndex).toBe(ZINDEX.EDGE_REGULAR); }); it("should elevate selected edge above regular edges but below labels", async () => { @@ -283,8 +283,8 @@ describe("Diagram Component", () => { const selectedEdge = edges.find((e: RF.Edge) => e.id === "edge1"); const regularEdge = edges.find((e: RF.Edge) => e.id === "edge2"); - expect(selectedEdge?.zIndex).toBe(100); - expect(regularEdge?.zIndex).toBe(0); + expect(selectedEdge?.zIndex).toBe(ZINDEX.EDGE_SELECTED); + expect(regularEdge?.zIndex).toBe(ZINDEX.EDGE_REGULAR); // Hierarchy in the test // Regular edges: 0 diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index da58998..3f9083f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7443,4 +7443,4 @@ snapshots: use-sync-external-store: 1.6.0(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 - react: 19.2.7 \ No newline at end of file + react: 19.2.7