fix(canvas/Toolbar): help button always opens — no double-click close bug
Some checks failed
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 16s
CI / Detect changes (pull_request) Successful in 35s
E2E API Smoke Test / detect-changes (pull_request) Successful in 47s
Harness Replays / detect-changes (pull_request) Successful in 26s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 1m9s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 1m14s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 27s
gate-check-v3 / gate-check (pull_request) Successful in 40s
qa-review / approved (pull_request) Failing after 20s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 1m5s
security-review / approved (pull_request) Failing after 19s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m37s
sop-checklist-gate / gate (pull_request) Successful in 28s
sop-tier-check / tier-check (pull_request) Successful in 24s
sop-checklist / all-items-acked (pull_request) [info tier:low] acked: 0/7 — missing: comprehensive-testing, local-postgres-e2e, staging-smoke, +4 — body-unfilled: comprehensive-testing, l
CI / Platform (Go) (pull_request) Successful in 15s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 9s
CI / Python Lint & Test (pull_request) Successful in 10s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 17s
Harness Replays / Harness Replays (pull_request) Successful in 12s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 12s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 10s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 8m26s
CI / Canvas (Next.js) (pull_request) Successful in 13m48s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
CI / all-required (pull_request) Successful in 8s
Some checks failed
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 16s
CI / Detect changes (pull_request) Successful in 35s
E2E API Smoke Test / detect-changes (pull_request) Successful in 47s
Harness Replays / detect-changes (pull_request) Successful in 26s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 1m9s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 1m14s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 27s
gate-check-v3 / gate-check (pull_request) Successful in 40s
qa-review / approved (pull_request) Failing after 20s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 1m5s
security-review / approved (pull_request) Failing after 19s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m37s
sop-checklist-gate / gate (pull_request) Successful in 28s
sop-tier-check / tier-check (pull_request) Successful in 24s
sop-checklist / all-items-acked (pull_request) [info tier:low] acked: 0/7 — missing: comprehensive-testing, local-postgres-e2e, staging-smoke, +4 — body-unfilled: comprehensive-testing, l
CI / Platform (Go) (pull_request) Successful in 15s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 9s
CI / Python Lint & Test (pull_request) Successful in 10s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 17s
Harness Replays / Harness Replays (pull_request) Successful in 12s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 12s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 10s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 8m26s
CI / Canvas (Next.js) (pull_request) Successful in 13m48s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
CI / all-required (pull_request) Successful in 8s
The help button's onClick used setHelpOpen((open) => !open) (toggle). Combined with the window.pointerdown handler that closes on outside-click, clicking outside then clicking the help button would: pointerdown outside (close) → click on button (!false = true → open) → pointerdown ON button (contains=true, no close) → BUT the next interaction would have stale toggle state causing a double-close on the following click. Fix: button onClick always calls setHelpOpen(true) — the pointerdown outside handler owns the close path; the button only opens. Also add 2 tests: pointer-down-outside closes, and re-open works after outside click (regression for the double-click bug). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
a783c60a39
commit
e7ed06e2f4
@ -314,7 +314,7 @@ export function Toolbar() {
|
||||
<div ref={helpRef} className="relative">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setHelpOpen((open) => !open)}
|
||||
onClick={() => setHelpOpen(true)}
|
||||
className="flex items-center justify-center w-7 h-7 bg-surface-card hover:bg-surface-card/70 border border-line rounded-lg transition-colors text-ink-mid hover:text-ink focus:outline-none focus-visible:ring-2 focus-visible:ring-accent/40"
|
||||
aria-expanded={helpOpen}
|
||||
aria-label="Open shortcuts and tips"
|
||||
|
||||
@ -255,6 +255,32 @@ describe("Toolbar — Help popover", () => {
|
||||
fireEvent.click(closeBtn);
|
||||
expect(screen.queryByRole("dialog")).toBeNull();
|
||||
});
|
||||
|
||||
it("closes when pointer is pressed outside the help popover", () => {
|
||||
render(<Toolbar />);
|
||||
const helpBtn = screen.getByRole("button", { name: /open shortcuts and tips/i });
|
||||
fireEvent.click(helpBtn);
|
||||
expect(screen.getByRole("dialog")).toBeTruthy();
|
||||
// Simulate pointerdown outside the help popover (not on the help button)
|
||||
fireEvent.pointerDown(document.body);
|
||||
expect(screen.queryByRole("dialog")).toBeNull();
|
||||
});
|
||||
|
||||
it("opens on click even after a previous pointer-outside close", () => {
|
||||
// Regression: clicking outside closed the popover AND toggled the button
|
||||
// state, so the next click on the button would close it again.
|
||||
// The fix makes the button always open (never toggle) so re-opening works.
|
||||
render(<Toolbar />);
|
||||
const helpBtn = screen.getByRole("button", { name: /open shortcuts and tips/i });
|
||||
fireEvent.click(helpBtn);
|
||||
expect(screen.getByRole("dialog")).toBeTruthy();
|
||||
// Click outside (pointerdown on body, not on help button)
|
||||
fireEvent.pointerDown(document.body);
|
||||
expect(screen.queryByRole("dialog")).toBeNull();
|
||||
// Click the help button again — must re-open, not double-close
|
||||
fireEvent.click(helpBtn);
|
||||
expect(screen.getByRole("dialog")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Toolbar — A2A edges toggle", () => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user