fix(ui): make cancelling a Galaxy layer a step back in the history

Hand test K8 with K2. Cancelling a loading layer with the minus button
pushed the previous depth as a new history entry, so Back led to the
cancelled layer again. A cancel now moves the history cursor back when
the previous entry is that depth; Forward retries the cancelled layer.
Back to an entry with the same root keeps the selection instead of
selecting the root again.

Signed-off-by: Bernhard Jackiewicz <bj@techport.io>
This commit is contained in:
Bernhard Jackiewicz
2026-10-03 19:48:48 +02:00
parent 86af069f76
commit 96fbec6782
2 changed files with 20 additions and 4 deletions
@@ -70,6 +70,10 @@ it('K8: shows loaded nodes and edges while a layer loads, and "−" cancels back
await settle(() => expect(status()?.textContent).toBe('3 nodes · 2 edges'));
await act(async () => { release(); });
await settle(() => expect(status()?.textContent).toBe('3 nodes · 2 edges'));
// A cancelled layer is a step back, not a new step: Forward retries it, and the history holds no duplicate.
expect(seam().history.entries).toEqual(['All graph', 'n1 · 1 layer', 'n1 · 2 layers']);
expect(seam().history.index).toBe(1);
expect(host.querySelector<HTMLButtonElement>('button[aria-label="Forward"]')?.title).toBe('Forward to n1 · 2 layers (Alt+Right)');
});
it('K8: a layer past the render limit stops there, says it is partial and cannot be expanded further', async () => {
+16 -4
View File
@@ -1946,10 +1946,11 @@ export default function GalaxyPanel(props: GalaxyPanelProps): JSX.Element {
setTrailStep(0);
setBackgroundCleared(false);
setNote('');
// A symbol scope reports its root itself once it is complete (see `notifiedScopedSymbol`).
if (entry.scope) selectOnComplete.current = entry.scope.kind === 'symbol' ? undefined : scopeIdentity(entry.scope);
// A symbol scope reports its root itself once it is complete (see `notifiedScopedSymbol`); the same root stays selected.
const sameRoot = entry.scope && scope.scope && scopeIdentity(entry.scope) === scopeIdentity(scope.scope);
if (entry.scope && !sameRoot) selectOnComplete.current = entry.scope.kind === 'symbol' ? undefined : scopeIdentity(entry.scope);
else { setHighlighted(null); props.onClearSelection?.(); }
}, [scope.restore, changeTraceTypes, project, props.onClearSelection]);
}, [scope.restore, scope.scope, changeTraceTypes, project, props.onClearSelection]);
const historyBack = peekNavigation(history, -1);
const historyForward = peekNavigation(history, 1);
const goHistory = useCallback((step: -1 | 1) => {
@@ -2029,6 +2030,17 @@ export default function GalaxyPanel(props: GalaxyPanelProps): JSX.Element {
const outsideLimits = props.workspaceExpanded && data && shown && (shown.nodes.length < data.nodes.length || shown.edges.length < data.edges.length)
? galaxyToolbarText.outsideLimits(data.nodes.length - shown.nodes.length, data.edges.length - shown.edges.length) : undefined;
/*
* "−" waehrend des Ladens ist ein Abbruch (K8), und ein Abbruch ist ein
* Schritt zurueck und kein neuer: steht die vorige Ebene direkt davor im
* Verlauf, geht es dorthin, und Vor laedt die abgebrochene Ebene wieder.
*/
const cancelOrRemoveLayer = () => {
const previous = historyEntry && { ...historyEntry, depth: scope.depth - 1 };
if (scope.loading && previous && historyBack && galaxyHistoryOptions.key(historyBack) === galaxyHistoryOptions.key(previous)) goHistory(-1);
else scope.setDepth(scope.depth - 1);
};
/* Was die Leiste ueber das Laden der Ebenen sagt (Handtest K8). */
const partial = scope.complete ? scope.result?.partial : undefined;
const scopeState = scope.validating ? 'checking' : scope.loading ? 'loading' : organicTask.loading ? 'arranging'
@@ -2556,7 +2568,7 @@ export default function GalaxyPanel(props: GalaxyPanelProps): JSX.Element {
*/}
<button type="button" disabled={scope.depth <= scope.minDepth}
title={scope.loading ? galaxyLayerText.cancelLoading(scope.depth) : galaxyLayerText.removeLayer}
onClick={() => scope.setDepth(scope.depth - 1)} aria-label="Remove graph layer">−</button>
onClick={cancelOrRemoveLayer} aria-label="Remove graph layer">−</button>
<span>{scope.depth} {scope.depth === 1 ? 'layer' : 'layers'}</span>
<button type="button" disabled={scope.loading || scope.result?.exhausted || Boolean(scope.result?.partial)}
data-warning={expandWarning || undefined} title={expandTitle}