feat(ui): hover modifier tooltip

Adds ModifierTooltip component that reads MODIFIER_REGISTRY attrs from
engine.session for the hovered piece and renders them as labelled rows.
The tooltip always appears on piece hover (piece type + color header) and
shows modifier rows only when modifier facts are set on the entity.

Board.tsx gains an optional onPieceHover callback; GameView.tsx tracks
hoveredPieceId and renders the tooltip absolutely in the board wrapper.
A 120ms hide-delay prevents flicker when cursor briefly leaves a piece.

Two Playwright tests added: hover shows tooltip with piece name; hover
over unmodified piece shows zero modifier-tooltip-row elements.
This commit is contained in:
Joey Yakimowich-Payne 2026-04-18 23:21:05 -06:00
commit 5f252e2dff
No known key found for this signature in database
4 changed files with 161 additions and 3 deletions

View file

@ -142,6 +142,55 @@ test.describe('Modifier Profiles', () => {
});
});
// ── T24: Hover modifier tooltip ──────────────────────────────────────────
test.describe('Modifier Profiles — hover tooltip (T24)', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
// Clear autosave so Play Solo starts a fresh game.
await page.evaluate(() => {
for (let i = localStorage.length - 1; i >= 0; i--) {
const key = localStorage.key(i);
if (key !== null && key.startsWith('paratype-chess:v2:autosave:')) {
localStorage.removeItem(key);
}
}
localStorage.removeItem('paratype-chess:v1:autosave');
});
// Navigate into the game view WITHOUT opening the rules drawer,
// so the board is fully interactive (no backdrop overlay).
await page.locator('[data-action="play-solo"]').click();
await page.waitForURL('**/game');
});
test('hover piece shows modifier tooltip', async ({ page }) => {
// Hover white knight at b1 (standard opening position).
await page.hover('[data-square="b1"]');
await page.waitForTimeout(200);
// Tooltip must appear and identify the piece.
await expect(
page.locator('[data-testid="modifier-tooltip"]'),
).toBeVisible({ timeout: 1000 });
await expect(
page.locator('[data-testid="modifier-tooltip"]'),
).toContainText(/knight/i);
});
test('hover unmodified piece shows no modifier rows', async ({ page }) => {
// Standard solo game — no modifier profile active.
await page.hover('[data-square="e2"]'); // white pawn
await page.waitForTimeout(200);
// Tooltip appears (header always shows) but no modifier rows.
await expect(
page.locator('[data-testid="modifier-tooltip-row"]'),
).toHaveCount(0);
});
});
// ── T26: Lobby profile picker + GameView badge ─────────────────────────
test.describe('Modifier Profiles — Lobby integration (T26)', () => {