feat(stammbaum): highlight the selected person's bloodline (#703) #704

Merged
marcel merged 13 commits from feat/issue-703-stammbaum-lineage-highlight into main 2026-05-31 20:11:07 +02:00
Showing only changes of commit 4ebebe1e07 - Show all commits

View File

@@ -157,6 +157,15 @@ describe('stammbaum page', () => {
// desktop side panel is a flex sibling that never overlaps the canvas, so no
// centring should fire there. These tests prove the matchMedia gate around
// selectPerson, not just the recentreAbove geometry (covered in panZoom.test).
// We assert on the rendered viewBox — a pure function of the view state — so a
// recentre is observed as a shifted origin, with no dependence on the noisy
// mount-time URL-mirror timing.
function svgViewBox(): string {
const svg = document.querySelector('svg[aria-label="Stammbaum"]');
if (!svg) throw new Error('No Stammbaum svg rendered');
return svg.getAttribute('viewBox') ?? '';
}
it('recentres the tapped person when matchMedia reports mobile (#703 AC8)', async () => {
mockMatchMedia(true);
mockPage.url = new URL('http://localhost/stammbaum');
@@ -164,15 +173,17 @@ describe('stammbaum page', () => {
render(Stammbaum, {
props: { data: { nodes: familyNodes, edges: [], initialView: DEFAULT_VIEW } }
});
// Let the mount-time URL mirror settle, then isolate the tap's effect.
await vi.waitFor(() => expect(replaceState).toHaveBeenCalled());
replaceState.mockClear();
const before = await vi.waitFor(() => {
const vb = svgViewBox();
expect(vb).toBeTruthy();
return vb;
});
await page.getByRole('button', { name: 'Anna Schmidt' }).click();
// The mobile tap recentres the canvas → the view changes → the ?cx&cy&z
// mirror effect re-fires. (Desktop, below, leaves the view untouched.)
await vi.waitFor(() => expect(replaceState).toHaveBeenCalled());
// The mobile tap recentres the canvas, lifting the anchor above the sheet,
// so the viewBox origin shifts.
await vi.waitFor(() => expect(svgViewBox()).not.toBe(before));
});
it('does not recentre on tap when matchMedia reports desktop (#703 AC8)', async () => {
@@ -182,14 +193,17 @@ describe('stammbaum page', () => {
render(Stammbaum, {
props: { data: { nodes: familyNodes, edges: [], initialView: DEFAULT_VIEW } }
});
await vi.waitFor(() => expect(replaceState).toHaveBeenCalled());
replaceState.mockClear();
const before = await vi.waitFor(() => {
const vb = svgViewBox();
expect(vb).toBeTruthy();
return vb;
});
await page.getByRole('button', { name: 'Anna Schmidt' }).click();
// The tap registers — the desktop side panel opens — but no recentre fires,
// so the view never changes and the mirror effect stays silent.
// so the viewBox is unchanged.
await expect.element(page.getByRole('complementary')).toBeVisible();
expect(replaceState).not.toHaveBeenCalled();
expect(svgViewBox()).toBe(before);
});
});