Files
feuerwerk/tests/ui/popup-placement.test.ts
T
adminandClaude Opus 5 3b039384c4
game-maker / suite (push) Failing after 10m23s
A search that ignores what a thing is called is no search, so the name wins (#43)
Typing "variable" into the node palette landed on `Run behavior`, because the
filter weighed label, type and description alike and then sorted by category.
`searchRank` in `src/ui/search.ts` now ranks a match: exact label or type, then
prefix, then label, then type, and only last a hit that lives in the
description alone. Category and alphabet order within a rank. The searchable
`Menu` uses the same function, so searching means the same thing everywhere.

The `+` menu grew to fifteen entries in declaration order. It is built in two
alphabetical groups now -- the types that go into a scene, then the ones that
stay in the library -- with a separator between them, the split `isPlaceableType`
already draws. `ASSET_TYPES` itself stays untouched: it is also the
serialisation and check order. A search flattens the groups and drops the lines.

Popups were capped at fixed heights (320 menu, 260 palette, 340 pickers) while
the canvas had room to spare. `Popup.roomFor()` measures its own chrome and
takes the window height minus a tenth at each end. Placement clamps
asymmetrically: the bottom margin is enforced, the top is only a budget, so a
menu anchored near the top stays at its button instead of being pushed off it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 10:21:20 +02:00

37 lines
1.3 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { clampPopupY, screenMargin } from '../../src/ui/widgets/Popup';
describe('screenMargin', () => {
it('is a tenth of the canvas, so it grows with the screen', () => {
expect(screenMargin(800)).toBe(80);
expect(screenMargin(1440)).toBe(144);
});
});
describe('clampPopupY', () => {
it('leaves a popup that already sits inside the band where it is', () => {
expect(clampPopupY(300, 200, 1000)).toBe(300);
});
it('keeps the same gap below as above when the anchor sits low', () => {
// 1000 tall canvas, 100 margin: a 700 tall popup asked for y=500 ends at 200, not at 300.
const y = clampPopupY(500, 700, 1000);
expect(y).toBe(200);
expect(1000 - (y + 700)).toBe(100); // the gap below matches the 100 above
});
it('leaves a popup anchored high where it is, the gap above is no minimum', () => {
// The + menu hangs off a button near the top; pushing it down to the margin would tear it away.
expect(clampPopupY(10, 200, 1000)).toBe(10);
expect(clampPopupY(0, 200, 1000)).toBe(0);
});
it('still keeps the gap below when a tall popup has to be shifted up', () => {
expect(clampPopupY(400, 900, 1000)).toBe(0);
});
it('gives up rather than shifting a popup taller than the canvas off screen', () => {
expect(clampPopupY(400, 1200, 1000)).toBe(0);
});
});