- Add tabindex="0" so the SVG can receive DOM focus - Auto-focus the SVG on mount so arrow keys work immediately after clicking an annotation to select it - Show preview rect during keyboard nudging (not just pointer drag) by checking hasLiveChanges instead of only checking dragState - Suppress default browser focus outline (outline: none) on the SVG Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
72 lines
2.4 KiB
TypeScript
72 lines
2.4 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { render } from 'vitest-browser-svelte';
|
|
import AnnotationEditOverlay from './AnnotationEditOverlay.svelte';
|
|
import type { Annotation } from '$lib/types';
|
|
|
|
const annotation: Annotation = {
|
|
id: 'ann-1',
|
|
documentId: 'doc-1',
|
|
pageNumber: 1,
|
|
x: 0.1,
|
|
y: 0.2,
|
|
width: 0.3,
|
|
height: 0.4,
|
|
color: '#00c7b1',
|
|
createdAt: '2026-01-01T00:00:00Z'
|
|
};
|
|
|
|
describe('AnnotationEditOverlay', () => {
|
|
it('renders 8 handle elements', async () => {
|
|
render(AnnotationEditOverlay, { annotation });
|
|
|
|
const handles = document.querySelectorAll('[data-handle]');
|
|
expect(handles).toHaveLength(8);
|
|
});
|
|
|
|
it('renders handles for all four corners and four edge midpoints', async () => {
|
|
render(AnnotationEditOverlay, { annotation });
|
|
|
|
expect(document.querySelector('[data-handle="nw"]')).not.toBeNull();
|
|
expect(document.querySelector('[data-handle="ne"]')).not.toBeNull();
|
|
expect(document.querySelector('[data-handle="sw"]')).not.toBeNull();
|
|
expect(document.querySelector('[data-handle="se"]')).not.toBeNull();
|
|
expect(document.querySelector('[data-handle="n"]')).not.toBeNull();
|
|
expect(document.querySelector('[data-handle="s"]')).not.toBeNull();
|
|
expect(document.querySelector('[data-handle="e"]')).not.toBeNull();
|
|
expect(document.querySelector('[data-handle="w"]')).not.toBeNull();
|
|
});
|
|
|
|
it('each handle has a 44x44 hit area', async () => {
|
|
render(AnnotationEditOverlay, { annotation });
|
|
|
|
const hitAreas = document.querySelectorAll('[data-handle-hit]');
|
|
expect(hitAreas).toHaveLength(8);
|
|
hitAreas.forEach((el) => {
|
|
expect(el.getAttribute('width')).toBe('44');
|
|
expect(el.getAttribute('height')).toBe('44');
|
|
});
|
|
});
|
|
|
|
it('renders a move area covering the full box', async () => {
|
|
render(AnnotationEditOverlay, { annotation });
|
|
|
|
const moveArea = document.querySelector('[data-move-area]');
|
|
expect(moveArea).not.toBeNull();
|
|
});
|
|
|
|
it('renders an aria-live region for screen reader announcement', async () => {
|
|
render(AnnotationEditOverlay, { annotation });
|
|
|
|
const liveRegion = document.querySelector('[aria-live="polite"]');
|
|
expect(liveRegion).not.toBeNull();
|
|
});
|
|
|
|
it('SVG root has tabindex="0" so it can receive keyboard focus', async () => {
|
|
render(AnnotationEditOverlay, { annotation });
|
|
|
|
const svg = document.querySelector('svg[role="application"]');
|
|
expect(svg).not.toBeNull();
|
|
expect(svg!.getAttribute('tabindex')).toBe('0');
|
|
});
|
|
});
|