mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
fix(ui): say why Expand +1 is blocked while a layer loads
While a layer loads, Expand +1 is blocked (aria-disabled) and still takes the pointer and the focus, but it showed no tooltip: there was no text for that state, and an empty hint shows nothing. It now reads "Layer 3 is loading; "−" cancels it." on hover and on focus. Signed-off-by: Bernhard Jackiewicz <bj@techport.io>
This commit is contained in:
1 parent
6980f6d5d3
commit
ef95339154
3 files changed
+78
-1
No files matched your search
@@ -0,0 +1,73 @@
|
||||
// @vitest-environment jsdom
|
||||
/*
|
||||
* Review zu K31: waehrend eine Ebene laedt, ist Expand +1 gesperrt
|
||||
* (aria-disabled), nimmt Zeiger und Fokus, zeigte aber keinen Tooltip: der
|
||||
* Text fuer diesen Zustand fehlte, und ein leerer Hinweis zeigt nichts. Jetzt
|
||||
* sagt der Knopf, welche Ebene laedt und dass "−" sie abbricht.
|
||||
*/
|
||||
import { act } from 'react';
|
||||
import { createRoot, type Root } from 'react-dom/client';
|
||||
import { afterEach, beforeEach, expect, it, vi } from 'vitest';
|
||||
import GalaxyPanel from './GalaxyPanel';
|
||||
import { galaxyLayerText } from './galaxy-strings';
|
||||
import { scopeFetch, scopeNode } from './test-scope-fetch';
|
||||
import type { GraphEdge } from './types';
|
||||
|
||||
vi.mock('./GraphScene', async importOriginal => ({
|
||||
...await importOriginal<typeof import('./GraphScene')>(),
|
||||
GraphScene: () => <output data-testid="scene" />,
|
||||
}));
|
||||
|
||||
let host: HTMLDivElement, root: Root;
|
||||
beforeEach(() => {
|
||||
(globalThis as unknown as Record<string, unknown>).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
host = document.createElement('div'); document.body.append(host); root = createRoot(host);
|
||||
});
|
||||
afterEach(async () => {
|
||||
await act(async () => root.unmount()); host.remove(); globalThis.__atlasGalaxy = undefined;
|
||||
window.localStorage.clear();
|
||||
});
|
||||
|
||||
const settle = (check: () => void) => vi.waitFor(async () => {
|
||||
await act(async () => { await new Promise(resolve => setTimeout(resolve, 0)); });
|
||||
check();
|
||||
});
|
||||
const status = () => host.querySelector('.atlas-graph-scope-count');
|
||||
const expand = () => [...host.querySelectorAll('button')].find(entry => entry.getAttribute('aria-label') === 'Expand +1')!;
|
||||
const shown = () => document.querySelector<HTMLElement>('[data-testid="atlas-hint"][data-hint-for="galaxy-expand"]');
|
||||
|
||||
it('K31: while a layer loads, the blocked Expand +1 says which layer loads and that "−" cancels it, on hover and on focus', async () => {
|
||||
const nodes = [1, 2, 3, 4, 5, 6].map(id => scopeNode(id));
|
||||
const edges: GraphEdge[] = [{ id: 1, source: 1, target: 2, type: 'CALLS' }, { id: 2, source: 2, target: 3, type: 'CALLS' },
|
||||
{ id: 3, source: 4, target: 2, type: 'CALLS' }, { id: 4, source: 3, target: 5, type: 'CALLS' }, { id: 5, source: 6, target: 1, type: 'TESTS' }];
|
||||
let block = false, release: () => void = () => {}, queries = 0;
|
||||
const { fetch } = scopeFetch({ nodes, edges, gate: async () => {
|
||||
queries += 1;
|
||||
if (block && queries > 1) await new Promise<void>(resolve => { release = resolve; });
|
||||
} });
|
||||
await act(async () => root.render(<GalaxyPanel project="sample" visible workspaceExpanded onOpenNode={vi.fn()} fetch={fetch} />));
|
||||
await settle(() => expect(globalThis.__atlasGalaxy?.nodes).toBe(6));
|
||||
await act(async () => { globalThis.__atlasGalaxy!.clickNode('sample.n1'); });
|
||||
await settle(() => expect(status()?.textContent).toBe('3 nodes · 2 edges'));
|
||||
|
||||
block = true; queries = 0;
|
||||
await act(async () => expand().click());
|
||||
await settle(() => expect(status()?.getAttribute('data-state')).toBe('loading'));
|
||||
expect(expand().getAttribute('aria-disabled')).toBe('true');
|
||||
expect(expand().getAttribute('data-hint')).toBe('Layer 2 is loading; "−" cancels it.');
|
||||
expect(galaxyLayerText.expandLoading(2)).toBe('Layer 2 is loading; "−" cancels it.');
|
||||
|
||||
await act(async () => { expand().dispatchEvent(new MouseEvent('mouseover', { bubbles: true })); });
|
||||
expect(shown()?.textContent).toBe('Layer 2 is loading; "−" cancels it.');
|
||||
await act(async () => { expand().dispatchEvent(new MouseEvent('mouseout', { bubbles: true })); });
|
||||
expect(shown()).toBeNull();
|
||||
await act(async () => { expand().focus(); });
|
||||
expect(shown()?.textContent).toBe('Layer 2 is loading; "−" cancels it.');
|
||||
// A press while it loads still does nothing.
|
||||
await act(async () => expand().click());
|
||||
expect([...host.querySelectorAll('.atlas-graph-exploration span')].some(entry => entry.textContent === '2 layers')).toBe(true);
|
||||
await act(async () => { expand().blur(); release(); });
|
||||
await settle(() => expect(status()?.getAttribute('data-state')).not.toBe('loading'));
|
||||
// Loaded: the tooltip speaks about the next layer again.
|
||||
expect(expand().getAttribute('data-hint') ?? '').not.toContain('is loading');
|
||||
});
|
||||
@@ -2113,7 +2113,9 @@ export default function GalaxyPanel(props: GalaxyPanelProps): JSX.Element {
|
||||
const expandPast = expandOutlook && expandPastLimit(expandOutlook);
|
||||
const expandWarning = Boolean(expandPast);
|
||||
const expandBlocked = Boolean(scope.loading || scope.result?.exhausted || scope.result?.partial);
|
||||
const expandTitle = partial ? galaxyLayerText.expandPartial : scope.result?.exhausted ? galaxyLayerText.expandEnd
|
||||
// Review zu K31: gesperrt heisst immer mit Grund, auch waehrend eine Ebene laedt.
|
||||
const expandTitle = scope.loading ? galaxyLayerText.expandLoading(scope.depth)
|
||||
: partial ? galaxyLayerText.expandPartial : scope.result?.exhausted ? galaxyLayerText.expandEnd
|
||||
: expandOutlook ? galaxyLayerText.expandHint(expandOutlook, expandPast) : undefined;
|
||||
|
||||
/*
|
||||
|
||||
@@ -171,6 +171,8 @@ export const galaxyLayerText = {
|
||||
},
|
||||
expandPartial: 'This layer stopped loading after it passed the render limit, so there is no complete edge to grow from. Raise the limit under Limits first.',
|
||||
expandEnd: 'End of trace: no relationship leads further.',
|
||||
/** Review of K31: Expand +1 is blocked while a layer loads, and says why. */
|
||||
expandLoading: (layer: number) => `Layer ${layer} is loading; "−" cancels it.`,
|
||||
};
|
||||
|
||||
/** Compact toolbar words, so the scoped toolbar keeps to one row at 1600 px. */
|
||||
|
||||
Reference in new issue
Block a user