mirror of
https://github.com/block/buzz.git
synced 2026-10-29 05:21:31 +00:00
fix(desktop): keep closed message actions from taking edit focus
Backport the independently reviewed 646f30febd436d8a10a2f531f38a4ac9d2912064 fix and its regression to published PR1 db566eb5. Keep the closed-menu production owner and browser assertions unchanged; register the test next to mention-recipients on this base.
Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>
This commit is contained in:
@@ -77,6 +77,7 @@ export default defineConfig({
|
||||
"**/remote-owned-mentions.spec.ts",
|
||||
"**/mention-spacing.spec.ts",
|
||||
"**/mention-recipients.spec.ts",
|
||||
"**/message-edit-focus.spec.ts",
|
||||
"**/team-mentions.spec.ts",
|
||||
"**/persistent-agent-audience.spec.ts",
|
||||
"**/relay-reconnect.spec.ts",
|
||||
|
||||
@@ -157,6 +157,9 @@ function MoreActionsMenu({
|
||||
</Tooltip>
|
||||
<DropdownMenuContent
|
||||
align="end"
|
||||
// Exit animation keeps Radix pointer-leave handlers mounted after
|
||||
// editing takes focus. A closed menu must no longer accept focus.
|
||||
inert={!open}
|
||||
side="top"
|
||||
sideOffset={6}
|
||||
onCloseAutoFocus={(event) => {
|
||||
|
||||
86
desktop/tests/e2e/message-edit-focus.spec.ts
Normal file
86
desktop/tests/e2e/message-edit-focus.spec.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { installMockBridge } from "../helpers/bridge";
|
||||
|
||||
// Hold exit presence, not composer readiness: a closed menu can still receive a
|
||||
// queued pointer-leave while its CSS exit animation is mounted.
|
||||
const holdExit = `
|
||||
[role="menu"][data-state="closed"] { animation-play-state: paused !important; }
|
||||
`;
|
||||
|
||||
test("closing message menu cannot reclaim editor focus on pointer leave", async ({
|
||||
page,
|
||||
}) => {
|
||||
await installMockBridge(page);
|
||||
await page.goto("/");
|
||||
await page.getByTestId("channel-general").click();
|
||||
await page.addStyleTag({ content: holdExit });
|
||||
const input = page.getByTestId("message-input");
|
||||
await input.fill("focus ownership");
|
||||
await page.getByTestId("send-message").click();
|
||||
const row = page
|
||||
.getByTestId("message-timeline")
|
||||
.getByTestId("message-row")
|
||||
.last();
|
||||
await expect(row).toContainText("focus ownership");
|
||||
await row.hover();
|
||||
await row.getByRole("button", { name: "More actions" }).click();
|
||||
const edit = page.getByRole("menuitem", { name: "Edit message" });
|
||||
const editElement = await edit.elementHandle();
|
||||
await edit.click();
|
||||
await expect(input).toHaveText("focus ownership");
|
||||
// A deliberate user click establishes focus independently of RAF timing.
|
||||
await input.click();
|
||||
await input.press("ControlOrMeta+End");
|
||||
await expect(input).toBeFocused();
|
||||
await page.keyboard.type(" edit");
|
||||
await expect(input).toHaveText("focus ownership edit");
|
||||
const closing = await editElement?.evaluate((element) => {
|
||||
const menu = element.closest('[role="menu"]');
|
||||
const state = menu?.getAttribute("data-state");
|
||||
const connected = element.isConnected;
|
||||
element.dispatchEvent(
|
||||
new PointerEvent("pointerout", {
|
||||
bubbles: true,
|
||||
pointerType: "mouse",
|
||||
relatedTarget: document.body,
|
||||
}),
|
||||
);
|
||||
return { state, connected };
|
||||
});
|
||||
expect(closing).toEqual({ state: "closed", connected: true });
|
||||
await expect(input).toBeFocused();
|
||||
await page.keyboard.type("ed");
|
||||
await expect(input).toHaveText("focus ownership edited");
|
||||
await page.getByTestId("send-message").click();
|
||||
await expect
|
||||
.poll(() =>
|
||||
page.evaluate(() => {
|
||||
const call = window.__BUZZ_E2E_COMMAND_LOG__
|
||||
?.filter((entry) => entry.command === "edit_message")
|
||||
.at(-1);
|
||||
return (call?.payload as { input?: { content: string } })?.input
|
||||
?.content;
|
||||
}),
|
||||
)
|
||||
.toBe("focus ownership edited");
|
||||
});
|
||||
|
||||
test("message menu Escape still restores its keyboard trigger", async ({
|
||||
page,
|
||||
}) => {
|
||||
await installMockBridge(page);
|
||||
await page.goto("/");
|
||||
await page.getByTestId("channel-general").click();
|
||||
const row = page
|
||||
.getByTestId("message-timeline")
|
||||
.getByTestId("message-row")
|
||||
.last();
|
||||
await row.hover();
|
||||
const trigger = row.getByRole("button", { name: "More actions" });
|
||||
await trigger.focus();
|
||||
await trigger.press("Enter");
|
||||
await expect(page.getByRole("menu")).toBeVisible();
|
||||
await page.keyboard.press("Escape");
|
||||
await expect(page.getByRole("menu")).toHaveCount(0);
|
||||
await expect(trigger).toBeFocused();
|
||||
});
|
||||
Reference in New Issue
Block a user