From a0eb5687a0bc021c159790bb77ca6aa68c9437d9 Mon Sep 17 00:00:00 2001 From: Yang Zhang Date: Fri, 18 Sep 2026 19:59:02 -0700 Subject: [PATCH] fix(frontend): give the workflow editor its own container, not the document's first Found in the browser: switching to the Form View and back left the canvas blank -- nothing to pan, nothing to click -- while the graph itself was fine, as the Form View's preview went on showing it running. `WorkflowEditorComponent` found its container with `document.getElementById("workflow-editor")`, and both views render that same hardcoded id: the canvas's editor and the Form View's preview are the same component. While the switch was a full page load the two could never coexist, so the lookup was always right. Routing between the views overlaps them for a tick -- the arriving view runs `ngAfterViewInit` while the departing one is still in the DOM -- and a document-wide lookup then returns the departing view's container, first in document order. The paper was built into a div about to be removed, and the arriving canvas kept an empty one. Measured in a real browser, at the moment the canvas comes back: [GETBYID] workflow-editor: 2 in document, returned index 0 before: #workflow-editor 1399x1000, svg=false, cells=0 after: #workflow-editor 1399x1000, svg=true, cells=1 It resolves both elements from its own host now. No run is needed to reproduce; expanding the Form View's preview once is enough, which is why watching a run there made it certain. Deletion-checked: restoring the document-wide lookup turns exactly the new named test red. Full frontend suite: 224 files, 6150 passed, 1 skipped, 0 failed. AOT build, eslint and prettier clean. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01FVvP3ttj22f9LB4p9u2anY --- .../workflow-editor.component.spec.ts | 24 +++++++++++++++++++ .../workflow-editor.component.ts | 10 ++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.spec.ts b/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.spec.ts index d397704d101..49d8f790f23 100644 --- a/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.spec.ts +++ b/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.spec.ts @@ -136,6 +136,30 @@ describe("WorkflowEditorComponent", () => { expect(wrapper.getHeatmapView()).toBeNull(); }); + // Two of these editors are in the page at once for one tick when the two views of a workflow + // hand over: the arriving one initialises while the departing one is still being removed, and + // both templates carry id="workflow-editor". Searching the document found the departing view's + // container, so the paper was built into a div about to disappear and the arriving canvas came + // up blank -- nothing to pan, nothing to click, while the graph itself was untouched. + it("builds its paper in its own container, not whichever the document holds first", () => { + const decoy = document.createElement("div"); + decoy.id = "workflow-editor"; + // Earlier in document order than the fixture, as the departing view's container is. + document.body.insertBefore(decoy, document.body.firstChild); + try { + const other = TestBed.createComponent(WorkflowEditorComponent); + other.detectChanges(); + + const host = other.nativeElement as HTMLElement; + expect(host.contains((other.componentInstance as any).editor)).toBe(true); + expect((other.componentInstance as any).editor).not.toBe(decoy); + expect(decoy.querySelector("svg")).toBeNull(); + other.destroy(); + } finally { + decoy.remove(); + } + }); + it("should hide operator status on the canvas by default", () => { // keeps the Status toggle off until the user enables it const editor = (component as any).editor as HTMLElement; diff --git a/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.ts b/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.ts index 92264762563..d087aeb5820 100644 --- a/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.ts +++ b/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.ts @@ -205,8 +205,14 @@ export class WorkflowEditorComponent implements OnInit, AfterViewInit, OnDestroy } ngAfterViewInit() { - this.editor = document.getElementById("workflow-editor")!; - this.editorWrapper = document.getElementById("workflow-editor-wrapper")!; + // This component's own elements, not whichever the document happens to hold first. Two of + // these editors are briefly in the page at once when the two views of a workflow hand over: + // the arriving one initialises while the departing one is still being removed. Searching the + // document returned the departing view's container, so the paper was built into a div about + // to disappear and the arriving canvas stayed blank, with nothing to pan and nothing to click. + const host = this.elementRef.nativeElement as HTMLElement; + this.editor = host.querySelector("#workflow-editor")!; + this.editorWrapper = host.querySelector("#workflow-editor-wrapper")!; document.addEventListener("keydown", this._handleKeyboardAction.bind(this)); this.initializeJointPaper(); this.handleDisableJointPaperInteractiveness();