From 0c903bc9a5bcd07b434189429fbfa2a8690588b3 Mon Sep 17 00:00:00 2001 From: Lukas Date: Wed, 11 Mar 2026 10:04:27 +0100 Subject: [PATCH] Fix ConfirmButton Enter/Space keydown bubbling to parent row handler The button's onClick stopped mouse event propagation, but keyboard Enter/Space fired a separate keydown event that bubbled to the combatant row's onKeyDown, opening the stat block side panel instead of arming/confirming the button. Co-Authored-By: Claude Opus 4.6 --- .../web/src/__tests__/confirm-button.test.tsx | 20 +++++++++++++++++++ apps/web/src/components/ui/confirm-button.tsx | 7 +++++++ 2 files changed, 27 insertions(+) diff --git a/apps/web/src/__tests__/confirm-button.test.tsx b/apps/web/src/__tests__/confirm-button.test.tsx index 3f23d44..32d03ba 100644 --- a/apps/web/src/__tests__/confirm-button.test.tsx +++ b/apps/web/src/__tests__/confirm-button.test.tsx @@ -195,4 +195,24 @@ describe("ConfirmButton", () => { fireEvent.blur(button); expect(screen.getByTestId("x-icon")).toBeTruthy(); }); + + it("Enter/Space keydown stops propagation to prevent parent handlers", () => { + const parentHandler = vi.fn(); + render( + // biome-ignore lint/a11y/noStaticElementInteractions: test wrapper +
+ } + label="Remove combatant" + onConfirm={vi.fn()} + /> +
, + ); + const button = screen.getByRole("button"); + + fireEvent.keyDown(button, { key: "Enter" }); + fireEvent.keyDown(button, { key: " " }); + + expect(parentHandler).not.toHaveBeenCalled(); + }); }); diff --git a/apps/web/src/components/ui/confirm-button.tsx b/apps/web/src/components/ui/confirm-button.tsx index 9763da6..0bb47b5 100644 --- a/apps/web/src/components/ui/confirm-button.tsx +++ b/apps/web/src/components/ui/confirm-button.tsx @@ -67,6 +67,12 @@ export function ConfirmButton({ }; }, [isConfirming, revert]); + const handleKeyDown = useCallback((e: React.KeyboardEvent) => { + if (e.key === "Enter" || e.key === " ") { + e.stopPropagation(); + } + }, []); + const handleClick = useCallback( (e: React.MouseEvent) => { e.stopPropagation(); @@ -95,6 +101,7 @@ export function ConfirmButton({ "bg-destructive text-primary-foreground rounded-md animate-confirm-pulse hover:bg-destructive hover:text-primary-foreground", )} onClick={handleClick} + onKeyDown={handleKeyDown} onBlur={revert} disabled={disabled} aria-label={isConfirming ? `Confirm ${label.toLowerCase()}` : label}