mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 08:13:50 +00:00
fix: open menus in place with reduced motion instead of flashing in the corner first
This commit is contained in:
+8
-5
@@ -755,9 +755,12 @@ optgroup {
|
||||
animation-iteration-count: 1 !important;
|
||||
/* Every element transitions `all` by default once it has a duration.
|
||||
Visibility is left out: a transition from hidden would keep a newly
|
||||
shown menu or dialog hidden for a frame, and focus() would miss it. */
|
||||
transition-property: all, visibility !important;
|
||||
transition-duration: 0.01ms, 0s !important;
|
||||
shown menu or dialog hidden for a frame, and focus() would miss it.
|
||||
So is the position: Chrome paints a transition's first frame at its
|
||||
start value, and a menu placed from script flashed where it was
|
||||
measured (a submenu in the window's top-left corner). */
|
||||
transition-property: all, visibility, top, right, bottom, left !important;
|
||||
transition-duration: 0.01ms, 0s, 0s, 0s, 0s, 0s !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}
|
||||
@@ -768,8 +771,8 @@ optgroup {
|
||||
[data-motion="reduce"] *::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-property: all, visibility !important;
|
||||
transition-duration: 0.01ms, 0s !important;
|
||||
transition-property: all, visibility, top, right, bottom, left !important;
|
||||
transition-duration: 0.01ms, 0s, 0s, 0s, 0s, 0s !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
|
||||
|
||||
@@ -118,6 +118,46 @@ test("keyboard focus stays visible in Windows contrast themes", async ({ page })
|
||||
expect(outlines.filter((o) => o !== "ok" && o !== "skip")).toEqual([]);
|
||||
});
|
||||
|
||||
test("menus open in place under reduced motion", async ({ page }) => {
|
||||
// Reduce motion shortens transitions to 0.01ms, but Chrome paints a
|
||||
// transition's first frame at its start value: a menu moved from where it
|
||||
// was measured showed there for a frame (the Sort submenu in the corner).
|
||||
await page.emulateMedia({ reducedMotion: "reduce" });
|
||||
await signIn(page);
|
||||
// Each frame, where every visible menu is painted.
|
||||
await page.evaluate(() => {
|
||||
const frames = new Map<Element, string[]>();
|
||||
(window as unknown as { __menuFrames: typeof frames }).__menuFrames = frames;
|
||||
const tick = () => {
|
||||
for (const menu of document.querySelectorAll('[role="menu"]')) {
|
||||
if (getComputedStyle(menu).visibility !== "visible") continue;
|
||||
const r = menu.getBoundingClientRect();
|
||||
frames.set(menu, [...(frames.get(menu) ?? []), `${Math.round(r.left)},${Math.round(r.top)}`]);
|
||||
}
|
||||
requestAnimationFrame(tick);
|
||||
};
|
||||
requestAnimationFrame(tick);
|
||||
});
|
||||
await page.getByTestId("mail-folder-menu").first().click();
|
||||
await page.getByTestId("mail-sort").click();
|
||||
await page.getByTestId("mail-sort-oldest").waitFor();
|
||||
await page.waitForTimeout(500);
|
||||
const result = await page.evaluate(() => {
|
||||
const frames = (window as unknown as { __menuFrames: Map<Element, string[]> }).__menuFrames;
|
||||
const jumps: string[] = [];
|
||||
for (const [menu, seen] of frames) {
|
||||
const r = menu.getBoundingClientRect();
|
||||
const final = `${Math.round(r.left)},${Math.round(r.top)}`;
|
||||
const elsewhere = seen.filter((at) => at !== final);
|
||||
if (elsewhere.length > 0) jumps.push(`${menu.getAttribute("aria-label") ?? "menu"} at ${elsewhere.join(" ")} before ${final}`);
|
||||
}
|
||||
return { jumps, menus: frames.size };
|
||||
});
|
||||
// The ⋮ menu and its Sort submenu.
|
||||
expect(result.menus).toBe(2);
|
||||
expect(result.jumps).toEqual([]);
|
||||
});
|
||||
|
||||
test("single-key shortcuts don't act behind an open dialog", async ({ page }) => {
|
||||
await signIn(page);
|
||||
const rows = page.locator(ROWS);
|
||||
|
||||
Reference in New Issue
Block a user