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) <noreply@anthropic.com>
This commit is contained in:
parent
a8101bee63
commit
d31d48e1c2
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -11,23 +11,21 @@
|
|||
</app-tasks-executions-list>
|
||||
|
||||
@if (selectedExecution(); as run) {
|
||||
@if (executionTreeAvailable()) {
|
||||
<!-- Only rendered for a run that has a subtree, so nothing inside needs to handle its absence. -->
|
||||
<section class="tasks-executor-tree-panel"
|
||||
[class.tasks-executor-tree-panel-collapsed]="!executionTreeAvailable() || !executionTreeOpen()"
|
||||
[class.tasks-executor-tree-panel-collapsed]="!executionTreeOpen()"
|
||||
[class.tasks-executor-tree-panel-floating]="treeFloating()">
|
||||
<button
|
||||
type="button"
|
||||
class="tasks-executor-tree-toggle"
|
||||
[disabled]="!executionTreeAvailable()"
|
||||
[attr.aria-expanded]="executionTreeAvailable() && executionTreeOpen()"
|
||||
[matTooltip]="executionTreeAvailable() ? '' : 'This run has no container steps, so there is no subtree to show.'"
|
||||
[attr.aria-expanded]="executionTreeOpen()"
|
||||
(click)="toggleExecutionTree()">
|
||||
<span>Execution tree</span>
|
||||
@if (executionTreeAvailable()) {
|
||||
<span class="tasks-executor-tree-toggle-icon" [class.tasks-executor-tree-toggle-icon-open]="executionTreeOpen()">⌃</span>
|
||||
}
|
||||
</button>
|
||||
|
||||
@if (executionTreeAvailable() && executionTreeOpen()) {
|
||||
@if (executionTreeOpen()) {
|
||||
<app-execution-tree
|
||||
class="tasks-executor-rail-tree"
|
||||
[rootExecution]="run"
|
||||
|
|
@ -37,6 +35,7 @@
|
|||
}
|
||||
</section>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
<mat-card class="tasks-executor-viewer flex w-full flex-col overflow-hidden !rounded-md !bg-gray-100">
|
||||
|
|
|
|||
|
|
@ -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', () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue