feat(ui): hide modifier tooltip on pieces with no active modifiers
The hover tooltip previously rendered on every piece regardless of whether it had any modifier facts, showing just a piece-type header and 'No active modifiers' — noise with zero information the user can't already see on the board. Now returns null when there are no modifier rows. The pinned panel (click-to-pin) keeps its empty-state copy because an explicit pin is a deliberate inspect action where confirming 'nothing here' is valid. Tests: - Inverted the two T24 hover tests to assert the tooltip does NOT render on unmodified pieces (b1 knight, e2 pawn on a vanilla solo game). - Added a positive test: hover a modified pawn (HP +1 from a seeded profile) and assert the tooltip + at least one row are visible.
This commit is contained in:
parent
ce6b2c1816
commit
7bee3cbaa9
2 changed files with 79 additions and 22 deletions
|
|
@ -165,29 +165,85 @@ test.describe('Modifier Profiles — hover tooltip (T24)', () => {
|
||||||
await page.waitForURL('**/game');
|
await page.waitForURL('**/game');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('hover piece shows modifier tooltip', async ({ page }) => {
|
test('hover unmodified piece does NOT show tooltip', async ({ page }) => {
|
||||||
// Hover white knight at b1 (standard opening position).
|
// Standard solo game — no modifier profile active, so b1 knight
|
||||||
|
// has no modifier facts. The tooltip only renders when the piece
|
||||||
|
// actually has modifiers; showing an empty card on every hover was
|
||||||
|
// noisy and unhelpful.
|
||||||
await page.hover('[data-square="b1"]');
|
await page.hover('[data-square="b1"]');
|
||||||
await page.waitForTimeout(200);
|
await page.waitForTimeout(200);
|
||||||
|
|
||||||
// Tooltip must appear and identify the piece.
|
await expect(
|
||||||
|
page.locator('[data-testid="modifier-tooltip"]'),
|
||||||
|
).toHaveCount(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('hover unmodified pawn does NOT show tooltip', async ({ page }) => {
|
||||||
|
// Same invariant, second fixture piece. Guards the "no noise on
|
||||||
|
// unmodified pieces" behaviour in case a future change accidentally
|
||||||
|
// re-enables the always-render path.
|
||||||
|
await page.hover('[data-square="e2"]'); // white pawn
|
||||||
|
await page.waitForTimeout(200);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.locator('[data-testid="modifier-tooltip-row"]'),
|
||||||
|
).toHaveCount(0);
|
||||||
|
await expect(
|
||||||
|
page.locator('[data-testid="modifier-tooltip"]'),
|
||||||
|
).toHaveCount(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('hover modified piece DOES show tooltip with rows', async ({ page }) => {
|
||||||
|
// Seed a profile that puts HP +1 on every white pawn, apply it to
|
||||||
|
// a fresh solo game via the lobby picker, then hover e2. The
|
||||||
|
// tooltip should render and surface the HP Bonus row.
|
||||||
|
await page.goto('/');
|
||||||
|
const LIBRARY_KEY = 'houserules:modifier-profiles:v1';
|
||||||
|
const entry = {
|
||||||
|
id: 't24-hover-positive',
|
||||||
|
name: 'Hover Positive',
|
||||||
|
profile: {
|
||||||
|
id: 't24-hover-positive',
|
||||||
|
name: 'Hover Positive',
|
||||||
|
description: '',
|
||||||
|
layoutId: 'classic',
|
||||||
|
perType: [
|
||||||
|
{ kind: 'hp-bonus', pieceType: 'pawn', color: 'white', value: 1 },
|
||||||
|
],
|
||||||
|
perInstance: [],
|
||||||
|
version: 1,
|
||||||
|
source: 'custom',
|
||||||
|
},
|
||||||
|
starred: false,
|
||||||
|
updatedAt: Date.now(),
|
||||||
|
};
|
||||||
|
await page.evaluate(
|
||||||
|
({ key, e }) => localStorage.setItem(key, JSON.stringify([e])),
|
||||||
|
{ key: LIBRARY_KEY, e: entry },
|
||||||
|
);
|
||||||
|
await page.reload();
|
||||||
|
const picker = page.getByTestId('profile-picker');
|
||||||
|
if (!(await picker.isVisible())) {
|
||||||
|
await page.locator('[data-action="open-rules-drawer"]').click();
|
||||||
|
}
|
||||||
|
await expect(picker).toBeVisible();
|
||||||
|
await picker.selectOption('t24-hover-positive');
|
||||||
|
await page.locator('[data-action="play-solo"]').click();
|
||||||
|
await page.waitForURL('**/game');
|
||||||
|
|
||||||
|
// Hover e2 — white pawn with HpBonus +1 from the profile.
|
||||||
|
await page.hover('[data-square="e2"]');
|
||||||
|
await page.waitForTimeout(200);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page.locator('[data-testid="modifier-tooltip"]'),
|
page.locator('[data-testid="modifier-tooltip"]'),
|
||||||
).toBeVisible({ timeout: 1000 });
|
).toBeVisible({ timeout: 1000 });
|
||||||
await expect(
|
await expect(
|
||||||
page.locator('[data-testid="modifier-tooltip"]'),
|
page.locator('[data-testid="modifier-tooltip"]'),
|
||||||
).toContainText(/knight/i);
|
).toContainText(/pawn/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(
|
await expect(
|
||||||
page.locator('[data-testid="modifier-tooltip-row"]'),
|
page.locator('[data-testid="modifier-tooltip-row"]').first(),
|
||||||
).toHaveCount(0);
|
).toBeVisible();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,10 @@
|
||||||
* the engine session. Renders as an absolute-positioned card; the parent is
|
* the engine session. Renders as an absolute-positioned card; the parent is
|
||||||
* responsible for placement (positioned within a `relative` container).
|
* responsible for placement (positioned within a `relative` container).
|
||||||
*
|
*
|
||||||
* Source attribution is simplified for T24: any fact that matches a
|
* Renders nothing when the piece has no modifier facts — the tooltip exists
|
||||||
* MODIFIER_REGISTRY attribute is shown without distinguishing per-type /
|
* to surface modifier state, and showing an empty card on every unmodified
|
||||||
* per-instance / preset origin (T26 can refine this).
|
* piece is noisy. The pinned panel (click-to-pin) carries the "no modifiers"
|
||||||
|
* empty state instead, since an explicit pin is a deliberate inspect action.
|
||||||
*/
|
*/
|
||||||
import { asEntityId } from '@paratype/rete';
|
import { asEntityId } from '@paratype/rete';
|
||||||
import { MODIFIER_REGISTRY } from '../modifiers/index.js';
|
import { MODIFIER_REGISTRY } from '../modifiers/index.js';
|
||||||
|
|
@ -40,6 +41,11 @@ export function ModifierTooltip({ pieceId, engine }: Props) {
|
||||||
return [{ id: descriptor.id, label: descriptor.label, description }];
|
return [{ id: descriptor.id, label: descriptor.label, description }];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// No modifiers on this piece → don't render the tooltip at all. A card
|
||||||
|
// that just says "White Knight" with an empty-state line adds noise
|
||||||
|
// without carrying information the user can't already see on the board.
|
||||||
|
if (modifierRows.length === 0) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-testid="modifier-tooltip"
|
data-testid="modifier-tooltip"
|
||||||
|
|
@ -63,11 +69,6 @@ export function ModifierTooltip({ pieceId, engine }: Props) {
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{/* Empty state */}
|
|
||||||
{modifierRows.length === 0 && (
|
|
||||||
<p className="text-[11px] text-neutral-400 italic">No active modifiers</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue