From d31d48e1c29fe1a4bc1f8f253f4e8f7e406a513b Mon Sep 17 00:00:00 2001 From: Lucio Lelii Date: Fri, 4 Sep 2026 09:11:39 +0200 Subject: [PATCH] Move the execution tree to the bottom left, and hide it without a subtree Anchored to the bottom-left corner instead of the top-left: the top is where the run's own title and toolbar are, so a panel there covered the thing it was meant to sit beside. It grows upward, which keeps its toggle in one place as the tree gets longer. The panel is no longer rendered at all for a run with no container steps. It used to render as a permanently disabled toggle explaining there was no subtree - fine in a rail, but a floating panel that exists only to say it has nothing is worse than no panel. With the section behind that condition, the guards inside it became unreachable: a disabled state, an aria-expanded term and a tooltip that could never appear. Removed, so the markup stops describing a case that cannot happen. The page's test stub grew three methods so the viewer can actually render in it. That is what makes these assertions real rather than vacuous - the first attempt at the absence test passed against a deliberately broken template, because the patch that was supposed to break it never matched. 557 frontend tests green; both assertions fail when the condition is removed. Co-Authored-By: Claude Opus 5 (1M context) --- src/app/layouts/tasks-executor/tasks-executor.css | 5 ++++- src/app/layouts/tasks-executor/tasks-executor.html | 13 ++++++------- .../layouts/tasks-executor/tasks-executor.spec.ts | 14 +++++++++++--- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/app/layouts/tasks-executor/tasks-executor.css b/src/app/layouts/tasks-executor/tasks-executor.css index 3d0995e..301f544 100644 --- a/src/app/layouts/tasks-executor/tasks-executor.css +++ b/src/app/layouts/tasks-executor/tasks-executor.css @@ -40,8 +40,11 @@ */ .tasks-executor-tree-panel-floating { position: fixed; - top: 12px; + /* Anchored to the bottom: the top-left corner is where the run's own title and toolbar sit. */ + bottom: 12px; left: 12px; + /* Growing upward from the toggle keeps that toggle in the same place as the tree gets longer. */ + justify-content: flex-end; z-index: calc(var(--z-modal) + 1); width: 280px; max-height: calc(100vh - 24px); diff --git a/src/app/layouts/tasks-executor/tasks-executor.html b/src/app/layouts/tasks-executor/tasks-executor.html index f550c56..fdf071a 100644 --- a/src/app/layouts/tasks-executor/tasks-executor.html +++ b/src/app/layouts/tasks-executor/tasks-executor.html @@ -11,23 +11,21 @@ @if (selectedExecution(); as run) { + @if (executionTreeAvailable()) { +
- @if (executionTreeAvailable() && executionTreeOpen()) { + @if (executionTreeOpen()) { } + } diff --git a/src/app/layouts/tasks-executor/tasks-executor.spec.ts b/src/app/layouts/tasks-executor/tasks-executor.spec.ts index b92fa67..6f248a2 100644 --- a/src/app/layouts/tasks-executor/tasks-executor.spec.ts +++ b/src/app/layouts/tasks-executor/tasks-executor.spec.ts @@ -69,7 +69,8 @@ describe('TasksExecutor', () => { init: vi.fn(), retrieveExecution: vi.fn().mockReturnValue(of(null)), deleteExecution: vi.fn().mockReturnValue(of(null)), - rerunExecution: vi.fn().mockReturnValue(of(null)) + rerunExecution: vi.fn().mockReturnValue(of(null)), + retrieveExecutionEvents: vi.fn().mockReturnValue(of([])) } }, { @@ -81,13 +82,15 @@ describe('TasksExecutor', () => { { provide: BlocksService, useValue: { - getAllBlocksTypes: vi.fn().mockResolvedValue(signal([])) + getAllBlocksTypes: vi.fn().mockResolvedValue(signal([])), + hasLoadedBlockTypes: vi.fn().mockReturnValue(false) } }, { provide: ContainersService, useValue: { - getAllContainerTypes: vi.fn().mockResolvedValue(signal([])) + getAllContainerTypes: vi.fn().mockResolvedValue(signal([])), + peekContainerType: vi.fn().mockReturnValue(null) } }, { @@ -182,8 +185,10 @@ describe('TasksExecutor', () => { } } } as any]); + fixture.detectChanges(); expect(component.executionTreeAvailable()).toBe(true); expect(component.treeFloating()).toBe(false); + expect(fixture.nativeElement.querySelector('.tasks-executor-tree-panel')).not.toBeNull(); component.viewerFullscreen.set(true); expect(component.treeFloating()).toBe(true); @@ -206,9 +211,12 @@ describe('TasksExecutor', () => { } } as any]); component.viewerFullscreen.set(true); + fixture.detectChanges(); expect(component.executionTreeAvailable()).toBe(false); expect(component.treeFloating()).toBe(false); + // Not merely un-floated: a run with no subflow has no tree to offer, so the panel is absent. + expect(fixture.nativeElement.querySelector('.tasks-executor-tree-panel')).toBeNull(); }); it('follows only container steps waiting for a child subflow', () => {