Keep menu edit links inside the current window - #444
Conversation
Clicking a link inside a chromeless iframe is routed by the parent shell: same page navigates the iframe in place, a different page opens a fresh window. "Same page" was decided by comparing the target slug against the window's `baseId` alone — the page the window was OPENED on, which never moves. That breaks as soon as the submenu tab strip re-points the iframe. The Appearance window opened on `themes.php` keeps `baseId: themes-php` while displaying `nav-menus.php`, so every link on the Menus screen — including its own per-menu tab row (`nav-menus.php?action=edit&menu=N`) — read as cross-page and spawned another window per click. A window now counts as being "on" two slugs: its `baseId` and the slug its iframe currently shows (`getCurrentUrl()`). Matching either one only ever widens the same-page set, so it can turn a would-be new window into an in-place navigation but never the reverse — a window that navigated away still treats a link back to its landing page as in-page. Fixes #442 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015sKW1mu1SGg5k9oSHgxv5s
A submenu tab points at one landing URL, but the screen behind it has more states than that: nav-menus.php also renders as `?action=locations` and `?action=edit&menu=2`, list tables paginate into `?paged=2`, settings screens redirect back with `?settings-updated=true`. Matching tabs by exact URL blanked the strip on all of them — clicking "Manage Locations" inside the Menus screen dropped the Menus tab's highlight. The active tab is now resolved in two passes: an exact URL match wins outright, and failing that the shell lights the tab whose page the URL belongs to. Ownership needs both halves to hold — the page-identity params agree (`post_type`, `taxonomy`, `page`, `path`, …, via the new `pageIdentityKey` helper, which reuses `deriveWindowId`'s identity set) AND every param the tab's own URL declares is present with the same value. So Categories can't claim Tags, and `admin.php?page=x&tab=test` can't claim `?tab=logs` — that falls back to the `?page=x` entry. Ties go to the more specific entry. Exactly one tab is active now; the old loop could light several if two entries shared a URL. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015sKW1mu1SGg5k9oSHgxv5s
|
Follow-up pushed in 561d94d — the tab highlight.
The active tab is now resolved in two passes:
The second condition is what keeps this from over-matching. Categories can't claim Tags. And a plugin registering both One incidental tightening: exactly one tab is active now. The old loop could light several if two entries resolved to the same URL. New
|
Every Analytics submenu entry shares `page=wc-admin` and differs only by `path`. `path` is an identity param, so they're distinct pages — a sub-view of one must never light another's tab. Verified against the real menu tree; pinning it so a future tweak to the identity set can't quietly collapse them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015sKW1mu1SGg5k9oSHgxv5s
Regression check against the real menu treeI didn't want to answer "no regressions" from reasoning alone, so I dumped the live admin menu off the dev site ( Probe: every tab under every parent (including the synthetic back-to-parent tab, deduped the same way
The scratch harness isn't committed — it depends on a fixture dumped from one specific site. The shapes it exercised are covered by the 11 cases in On the routing change (commit 1), the guarantee is structural rather than empirical: the check went from |
Fixes #442.
The bug
Editing a nav menu spawned a new window on every click. Picking a menu from the Menus screen's tab row, or hitting Edit Menus / Manage Locations, opened yet another window instead of updating the one already showing the Menus screen.
Why
The chromeless bridge
preventDefaults every admin-internal link click inside an iframe and lets the parent shell route it (handleCrossPageAdminLinkinsrc/window/iframe-bridge.ts). Same page →location.assign()in place; different page →windowManager.open()."Same page" was decided by comparing the target slug against the window's
baseId— the page the window was opened on, which never moves.That's stale the moment the submenu tab strip re-points the iframe. Appearance → Menus navigates the existing window's iframe to
nav-menus.phpwhile the window keepsbaseId: themes-php. So every link on the Menus screen — including WP's own per-menu tab row (nav-menus.php?action=edit&menu=N) — derived tonav-menus-php, didn't matchthemes-php, and was classified cross-page.Not specific to Menus: the same stale comparison hits any screen reached through the tab strip (Appearance → Widgets, Tools → Import, Settings → Writing, …). Menus is where it's most visible because its UI is a row of links back to itself.
The fix
A window now counts as being "on" two slugs: its
baseIdand the slug its iframe currently displays (getCurrentUrl()). A click matching either one navigates in place.Matching both rather than swapping
baseIdout for the live slug is deliberate — it only ever widens the same-page set, so it can convert a would-be new-window open into an in-place navigation but never the reverse. A window that navigated fromadmin.php?page=footoadmin.php?page=foo&path=/barstill treats a link back to the landing page as in-page.Tests
Three cases added to
src/window/iframe-bridge.test.ts, covering the fix and both of its guardrails:baseIdstill navigates in place after the iframe has moved (no narrowing);npm run lint,npm run typecheck,npm run test:js(2459 tests), andnpm run buildall green.Docs:
docs/bridge-protocol.md— the "Admin link routing inside chromeless iframes" dispatch list now states the two-slug rule and why the live slug can only widen the match.How to verify
Before: a new window per click. After: the same window's iframe updates.
🤖 Generated with Claude Code