This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch 
gh-readonly-queue/main/pr-5285-8a4cb29e1045f18cbfaf5f143473e0f05b25d4ea
in repository https://gitbox.apache.org/repos/asf/texera.git

commit 4f73e952555aa7ae4d146c7a08ef0381e873d18b
Author: Kunwoo (Chris) <[email protected]>
AuthorDate: Fri Jun 5 16:09:34 2026 -0700

    fix: clear red change-line brackets when restoring a workflow version 
(#5285)
    
    ### What changes were proposed in this PR?
    
    When restoring a previous workflow version that displays a **red change
    line** (the bracket highlight drawn around the neighbors of deleted
    operators), the red line stayed visible after the version was restored.
    Versions whose diff only contained green/orange operator highlights
    cleared correctly.
    
    Why it's a bug: `highlightOpVersionDiff()` applies three kinds of
    highlight when previewing a version:
    - orange boundary **fill** on modified operators
    - green boundary **fill** on added operators
    - **red bracket strokes** (`path.left-boundary/stroke` /
    `path.right-boundary/stroke`) on the neighbors of *deleted* operators
    
    `unhighlightOpVersionDiff()` only reset the boundary fills — it never
    reset the bracket strokes. `closeParticularVersionDisplay()` masked the
    issue because it calls `reloadWorkflow()`, which rebuilds the JointJS
    paper elements and incidentally wipes the leftover brackets.
    `revertToVersion()` does **not** reload the paper, so the orphaned red
    brackets persisted after restore.
    
    This PR makes `unhighlightOpVersionDiff()` symmetric with
    `highlightOpVersionDiff()`: it now also resets the deleted-operators'
    neighbor brackets back to the default transparent `rgba(0,0,0,0)`, so
    the red change line is cleared on restore.
    
    ### Any related issues, documentation, discussions?
    
    Fixes #3043
    Fixes #3828
    
    ### How was this PR tested?
    
    - Manually verified in the UI that the red change line clears after
    restoring a version.
    
    
    
https://github.com/user-attachments/assets/6417c52c-4f6e-47eb-82d4-0ac95f55ac5f
    
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Code (Claude Opus 4.7)
---
 .../workflow-version.service.spec.ts               | 35 ++++++++++++++++++++++
 .../workflow-version/workflow-version.service.ts   | 14 +++++++++
 2 files changed, 49 insertions(+)

diff --git 
a/frontend/src/app/dashboard/service/user/workflow-version/workflow-version.service.spec.ts
 
b/frontend/src/app/dashboard/service/user/workflow-version/workflow-version.service.spec.ts
index 85c9016500..659af0feb2 100644
--- 
a/frontend/src/app/dashboard/service/user/workflow-version/workflow-version.service.spec.ts
+++ 
b/frontend/src/app/dashboard/service/user/workflow-version/workflow-version.service.spec.ts
@@ -310,6 +310,41 @@ describe("WorkflowVersionService", () => {
     });
   });
 
+  // ─── unhighlightOpVersionDiff ─────────────────────────────────────────────
+
+  describe("unhighlightOpVersionDiff", () => {
+    it("resets the boundary fill of added and modified ops to transparent", () 
=> {
+      service.unhighlightOpVersionDiff({ modified: ["m"], added: ["a"], 
deleted: [] });
+
+      expect(paperGetModelById).toHaveBeenCalledWith("m");
+      expect(paperGetModelById).toHaveBeenCalledWith("a");
+      expect(modelAttr).toHaveBeenCalledWith("rect.boundary/fill", 
"rgba(0,0,0,0)");
+    });
+
+    it("resets the red brackets drawn around the neighbors of deleted ops", () 
=> {
+      const tempWorkflow = buildWorkflow({
+        content: buildContent({
+          operators: [buildOperator({ operatorID: "alive-left" }), 
buildOperator({ operatorID: "alive-right" })],
+          links: [buildLink("dead", "alive-right"), buildLink("alive-left", 
"dead")],
+        }),
+      });
+      actionSpy.getTempWorkflow.mockReturnValue(tempWorkflow);
+
+      service.unhighlightOpVersionDiff({ modified: [], added: [], deleted: 
["dead"] });
+
+      expect(modelAttr).toHaveBeenCalledWith("path.left-boundary/stroke", 
"rgba(0,0,0,0)");
+      expect(modelAttr).toHaveBeenCalledWith("path.right-boundary/stroke", 
"rgba(0,0,0,0)");
+    });
+
+    it("skips bracket clearing when the temp workflow is missing", () => {
+      actionSpy.getTempWorkflow.mockReturnValue(undefined);
+
+      service.unhighlightOpVersionDiff({ modified: [], added: [], deleted: 
["dead"] });
+
+      expect(getMainJointPaper).not.toHaveBeenCalled();
+    });
+  });
+
   // ─── getWorkflowsDifference / getOperatorsDifference ──────────────────────
 
   describe("getWorkflowsDifference", () => {
diff --git 
a/frontend/src/app/dashboard/service/user/workflow-version/workflow-version.service.ts
 
b/frontend/src/app/dashboard/service/user/workflow-version/workflow-version.service.ts
index 0c31b4edf1..1b527e35cb 100644
--- 
a/frontend/src/app/dashboard/service/user/workflow-version/workflow-version.service.ts
+++ 
b/frontend/src/app/dashboard/service/user/workflow-version/workflow-version.service.ts
@@ -271,6 +271,20 @@ export class WorkflowVersionService {
     for (const id of 
differentOpIDsList.added.concat(differentOpIDsList.modified)) {
       this.highlightOpBoundary(id, "0,0,0,0");
     }
+
+    if (differentOpIDsList.deleted.length > 0) {
+      const tempWorkflow = this.workflowActionService.getTempWorkflow();
+      if (tempWorkflow != undefined) {
+        for (const link of tempWorkflow.content.links) {
+          if (differentOpIDsList.deleted.includes(link.source.operatorID) && 
link.target.operatorID != undefined) {
+            this.highlightOpBracket(link.target.operatorID, "0,0,0,0", 
"left-");
+          }
+          if (differentOpIDsList.deleted.includes(link.target.operatorID) && 
link.source.operatorID != undefined) {
+            this.highlightOpBracket(link.source.operatorID, "0,0,0,0", 
"right-");
+          }
+        }
+      }
+    }
     this.operatorPropertyDiff = {};
   }
 

Reply via email to