fix(ui): polish board→rack recall drag and its gesture interplay
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Successful in 52s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m13s

- The rack now animates the opening drop gap for a board-tile recall drag too
  (the reorder transition was gated on a rack-source drag only).
- While a board tile is dragged over the rack (a recall, gap shown), the 
  confirm and its preview caption are hidden so they don't sit over the gap;
  they return if the tile is dragged back out over the board.
- A recall drop lands off the boardwrap, so its pointerup never reached the
  boardwrap handler and left a stale id in the active-pointer set — the next
  board swipe then read as multi-touch and the shuffle / history pull went dead.
  Reconcile the pointer when a drag ends or cancels. Regression e2e added.
This commit is contained in:
Ilia Denisov
2026-06-19 10:12:48 +02:00
parent 4adb608ad1
commit b156d3403c
3 changed files with 36 additions and 3 deletions
+23
View File
@@ -43,6 +43,29 @@ test('a swipe-down on the zoom-out board opens the history', async ({ page }) =>
await expect(page.locator('.boardwrap.slid')).toBeVisible();
});
test('a recall drag onto the rack does not break the next board swipe', async ({ page }) => {
await openGame(page);
// Place a tile, then drag it back onto the rack — the recall's pointerup lands off the
// boardwrap, which once left a stale pointer there so the next swipe read as multi-touch.
await page.locator('.rack .tile').first().click();
await page.locator('[data-cell]:not(.filled)').nth(30).click();
const pending = page.locator('[data-cell].pending');
await expect(pending).toHaveCount(1);
const fb = (await pending.first().boundingBox())!;
const slot = (await page.locator('[data-rack] .tile').nth(2).boundingBox())!;
await page.mouse.move(fb.x + fb.width / 2, fb.y + fb.height / 2);
await page.mouse.down();
await page.mouse.move(slot.x + 2, slot.y + slot.height / 2, { steps: 10 });
await page.mouse.up();
await expect(pending).toHaveCount(0);
// The swipe-down still opens the history (the recall pointer was reconciled).
await page.locator('.stage').evaluate((el) => (el.scrollTop = 0));
const box = (await page.locator('.boardwrap').boundingBox())!;
await touchSwipeDown(page, box.y + 20, box.y + 180);
await expect(page.locator('.history')).toBeVisible();
});
test('the history is a per-seat grid with move scores but no names or running total', async ({ page }) => {
await openGame(page);
await page.locator('.scoreboard').click(); // open the history (gesture is covered separately)