mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-07 04:12:04 +00:00
fix(day): keep Escape in the date calendar from closing the whole day
On a phone the date's calendar is a sheet over the day's own sheet. In the moment after it opens, before Radix has re-rendered both layers, the day's sheet still holds the Escape listener, so an Escape aimed at the calendar closed the day as well; the day-view spec caught it as a flake on the phone viewport (4 of 10 runs locally). The day's sheet now ignores an Escape that comes from inside another dialog, and the spec waits for the layer handover instead of racing it. The coach open-at-end spec read the thread's height as soon as an element with the thread's slot was visible, but the empty state carries the same slot. It now waits for the loaded thread's frame and all of its answers.
This commit is contained in:
1 parent
118494b91d
commit
7d8ff0f2df
3 files changed
+50
-5
No files matched your search
@@ -185,12 +185,26 @@ test.describe("Coach: a saved conversation opens at its end", () => {
|
||||
waitUntil: "domcontentloaded",
|
||||
});
|
||||
|
||||
const thread = page.locator('[data-slot="coach-message-thread"]');
|
||||
await expect(thread).toBeVisible({ timeout: 15_000 });
|
||||
// The empty state carries the same `coach-message-thread` slot, so the
|
||||
// thread being visible says nothing about the conversation having
|
||||
// arrived; the height used to be read from the empty state on a slow
|
||||
// run. Wait for the frame only a loaded thread renders, and for every
|
||||
// stored answer in it.
|
||||
await expect(
|
||||
page.locator('[data-slot="coach-message-thread-frame"]'),
|
||||
).toBeVisible({ timeout: 15_000 });
|
||||
await expect(
|
||||
page.locator('[data-slot="coach-assistant-turn"]'),
|
||||
).toHaveCount(8, { timeout: 15_000 });
|
||||
const thread = page.locator(
|
||||
'[data-slot="coach-message-thread-frame"] [data-slot="coach-message-thread"]',
|
||||
);
|
||||
// The thread scrolls at all: otherwise "at the end" proves nothing.
|
||||
expect(
|
||||
await thread.evaluate((el) => el.scrollHeight > el.clientHeight + 200),
|
||||
).toBe(true);
|
||||
await expect
|
||||
.poll(() =>
|
||||
thread.evaluate((el) => el.scrollHeight > el.clientHeight + 200),
|
||||
)
|
||||
.toBe(true);
|
||||
|
||||
const chart = page.locator(
|
||||
'[data-slot="coach-result-chart"][data-ref="r1"]',
|
||||
|
||||
@@ -275,6 +275,25 @@ test.describe("the day view", () => {
|
||||
),
|
||||
)
|
||||
.toBe(true);
|
||||
// On a phone the calendar is a sheet over the day's own sheet. Focus
|
||||
// arrives before Radix has handed it the Escape key: in between, the day
|
||||
// underneath still holds it. The handover shows as the day's layer
|
||||
// turning inert (`pointer-events: none`) once the calendar is the top
|
||||
// layer; wait for that, not for a fixed time. The docked column is no
|
||||
// dialog and has no such handover.
|
||||
if (!desktop)
|
||||
await expect
|
||||
.poll(() =>
|
||||
panel(page).evaluate(
|
||||
(el) =>
|
||||
new Promise<string>((resolve) =>
|
||||
requestAnimationFrame(() =>
|
||||
setTimeout(() => resolve(getComputedStyle(el).pointerEvents)),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toBe("none");
|
||||
await page.keyboard.press("Escape");
|
||||
await expect(picker).toHaveCount(0);
|
||||
await expect(panel(page)).toBeVisible();
|
||||
|
||||
@@ -717,6 +717,18 @@ function DaySheet({
|
||||
event.preventDefault();
|
||||
returnFocus();
|
||||
}}
|
||||
onEscapeKeyDown={(event) => {
|
||||
// A dialog opened from inside the day (the date's calendar) is
|
||||
// portaled out of the panel. For a moment after it opens, until
|
||||
// Radix has re-rendered both layers, the panel still holds the
|
||||
// Escape listener: an Escape aimed at the calendar then closed the
|
||||
// whole day. Escape from inside another dialog is never the day's.
|
||||
const owner =
|
||||
event.target instanceof Element
|
||||
? event.target.closest('[role="dialog"]')
|
||||
: null;
|
||||
if (owner && owner !== panelRef.current) event.preventDefault();
|
||||
}}
|
||||
className={cn(
|
||||
// Quicker than the primitive's 500 ms, which read as lag on a
|
||||
// layer opened from a tap; no slide at all under reduced motion.
|
||||
|
||||
Reference in new issue
Block a user