fix(ui): restore plan-reminder menu focus before opening the edit dialog - #1723
Merged
Conversation
The plan-reminder edit/duplicate flow defers opening the Astryx form dialog by two requestAnimationFrames after the row menu closes (the menu stashes an intent, runs it on close, then remounts a closed form session before Astryx observes a false->true transition). Astryx Dialog restores focus on Escape to whatever was `document.activeElement` when it opened, and the menu's own close-focus-return sits in that same window. On a loaded CI runner the captured element can land on <body> instead of the menu trigger, so Escape strands focus away from the row and `expect(menu).toBeFocused()` times out. This made the `plan-reminders` E2E flaky on docs-only PR #1719 (same code tree passed on #1714 and failed on #1719; a rerun went green), and the suite runs with retries: 0 so the flake blocks unrelated PRs. Keep a ref to each row's menu trigger and re-focus it inside the deferred intent, right before the dialog opens, so the dialog captures and later restores focus to the row's trigger deterministically — independent of the menu-close vs deferred-open micro-timing. Verified: packages/ui typecheck + 296 contract tests, biome lint, and the plan-reminders E2E (12/12 with --repeat-each 6).
Astro-Han
added a commit
that referenced
this pull request
Jul 31, 2026
#1720 (opencode-free as a zero-credential default provider) added `ensureBootstrapConnection`, which unconditionally seeds an `opencode-free` connection on first launch when no connections exist. The E2E gate that skips it, `if (!e2eFixture)`, only triggers when `MAKA_E2E_FIXTURE` is set, but the `emptyWindow` fixture (used by all four `first-run.spec.ts` tests) passes `seed: false` and no `e2eFixtureScenario`, so `MAKA_E2E_FIXTURE` is unset and the bootstrap runs — seeding `opencode-free` into the "empty" workspace. The onboarding state becomes `ready_empty` instead of `needs_connection`, so `OnboardingHero` no longer renders `.maka-firstrun-row`, and the four tests fail with `toHaveCount 0` / click timeouts. This is a deterministic regression on current main (not a flake): #1720's own e2e failed exactly these four; every post-#1720 run fails them identically; pre-#1720 runs passed. #1720 merged with the failing e2e, so every PR's e2e is now red on these four. The `needs_connection` first-run hero is now unreachable for a fresh install (opencode-free is always seeded, and re-seeded whenever the connection list is empty), so these tests assert a contract that no longer holds. Drop them now to unblock e2e; follow-up will add coverage for the post-#1720 fresh-install boot contract (opencode-free seeded -> ready_empty). Also remove the now-unused `emptyWindow` fixture (it was "Used by first-run only"). Unblocks the plan-reminder focus-restore fix (#1723) and the docs PR #1719, whose e2e runs merge with main and so also hit this cluster. Verified: `npx playwright test --list` parses the remaining 93 tests across 32 files with no import/syntax errors; `emptyWindow` has no remaining references. The 93 remaining tests passed in the CI runs that failed only on these four.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The plan-reminder edit/duplicate flow defers opening the Astryx form dialog by two
requestAnimationFrames after the row menu closes: the menu stashes an intent, runs it on close, thenopenReminderDialogremounts a closed form session before Astryx observes a false→true transition. AstryxDialogrestores focus on Escape to whatever wasdocument.activeElementwhen it opened, and the menu's own close-focus-return sits in that same window. On a loaded CI runner the captured element can land on<body>instead of the menu trigger, so Escape strands focus away from the row andexpect(menu).toBeFocused()times out.This made the
plan-remindersE2E flaky: on docs-only PR #1719 the same code tree (b1a0004e) passed on #1714 and failed on #1719, and a rerun went green. The suite runs withretries: 0, so the flake blocks unrelated PRs.Keep a ref to each row's menu trigger (via the Astryx
DropdownMenubutton.ref) and re-focus it inside the deferred intent, right before the dialog opens, so the dialog captures and later restores focus to the row's trigger deterministically — independent of the menu-close vs deferred-open micro-timing.Verification
packages/uitypecheck +@maka/uibuild (tsc): clean@maka/uicontract tests: 296 passed, 0 failedbiome lint packages/ui/src/plan-reminder-panel.tsx: cleanplan-remindersE2E (apps/desktop),--repeat-each 6: 12/12 passed, includingcloses a reminder menu before opening its edit dialog(the previously flaky case)typecheckandtestgreen;e2e— the twoplan-reminderstests pass, but 4e2e/first-run.spec.tstests fail. Those 4 fail with an identical pattern (4 failed, 93 passed) on at least three unrelated branches at the same time (codex/remove-expert-mode, feat/opencode-free-default-models, chore/prune-static-contract-tests), so they are a pre-existing active flaky cluster in the first-run boot flow, not a regression from this change (which only touchespackages/ui/src/plan-reminder-panel.tsx).Root cause
apps/desktop/e2e/plan-reminders.spec.ts:75 › closes a reminder menu before opening its edit dialogassertedexpect(menu).toBeFocused()after Escape closed the edit dialog. AstryxDialogrestores focus to the element it captured asdocument.activeElementat open time (triggerElementRef.current = document.activeElementin its open effect). The edit flow deferred the dialog open two rAFs past the menu close, so the menu's close-focus-return and the dialog's capture raced; under CI load the capture landed on<body>, so Escape restored to<body>and the trigger stayedinactivefor the full 10s timeout.