feat(stammbaum): keyboard pan/zoom on the canvas (#692)
+/- zoom by the fixed step and arrow keys pan by a tenth of the visible extent, emitted via onPanZoom. Provides the keyboard-only alternative path required by NFR-A11Y-002. Nodes keep their own Enter/Space selection. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -509,6 +509,45 @@ describe('StammbaumTree node rendering branches', () => {
|
||||
node.dispatchEvent(new KeyboardEvent('keydown', { key: ' ', bubbles: true }));
|
||||
expect(onSelect).toHaveBeenCalledWith(ID_A);
|
||||
});
|
||||
});
|
||||
|
||||
describe('StammbaumTree keyboard pan/zoom (#692)', () => {
|
||||
const renderTree = (onPanZoom: ReturnType<typeof vi.fn>) =>
|
||||
render(StammbaumTree, {
|
||||
nodes: [{ id: ID_A, displayName: 'Anna', familyMember: true }],
|
||||
edges: [],
|
||||
selectedId: null,
|
||||
panZoom: { x: 0, y: 0, z: 1 },
|
||||
onPanZoom,
|
||||
onSelect: () => {}
|
||||
});
|
||||
|
||||
it('zooms in on "+" and out on "-" by the keyboard step (OQ-002)', async () => {
|
||||
const onPanZoom = vi.fn();
|
||||
renderTree(onPanZoom);
|
||||
const svg = document.querySelector('svg')!;
|
||||
|
||||
svg.dispatchEvent(new KeyboardEvent('keydown', { key: '+', bubbles: true }));
|
||||
expect(onPanZoom).toHaveBeenCalledTimes(1);
|
||||
expect(onPanZoom.mock.calls[0][0].z).toBeCloseTo(1.1, 6);
|
||||
|
||||
onPanZoom.mockClear();
|
||||
svg.dispatchEvent(new KeyboardEvent('keydown', { key: '-', bubbles: true }));
|
||||
expect(onPanZoom.mock.calls[0][0].z).toBeCloseTo(0.9, 6);
|
||||
});
|
||||
|
||||
it('pans right/down on arrow keys (REQ-PAN-004)', async () => {
|
||||
const onPanZoom = vi.fn();
|
||||
renderTree(onPanZoom);
|
||||
const svg = document.querySelector('svg')!;
|
||||
|
||||
svg.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true }));
|
||||
expect(onPanZoom.mock.calls[0][0].x).toBeGreaterThan(0);
|
||||
|
||||
onPanZoom.mockClear();
|
||||
svg.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true }));
|
||||
expect(onPanZoom.mock.calls[0][0].y).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('does not call onSelect for other keys', async () => {
|
||||
const onSelect = vi.fn();
|
||||
|
||||
Reference in New Issue
Block a user