mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-07-02 06:31:39 +00:00
fix(frontend): reliably restore row focus on panel close (#1602)
fix for the focus-restore@800 E2E test that's currently failing on master (see runs 26990436988, 26986419081) Chromium headless is notorious for dropping synchronous or rAF-based focus restores when elements are hidden. By manually blurring the active element before hiding the panel, and staggering the focus restore with a setTimeout macrotask after the rAF, we ensure the focus call lands after the browser has completed all implicit focus resets and event handlers. Furthermore, dynamically evaluating the focus resolver directly inside the deferred focus attempt prevents the target element from becoming stale if a live WebSocket packet triggers a background table re-render in the intervening milliseconds.
This commit is contained in:
+26
-5
@@ -426,16 +426,37 @@
|
||||
// Defer to next microtask + rAF so the focus call lands AFTER any
|
||||
// event-handler bookkeeping (e.g. an Escape keydown chain that would
|
||||
// otherwise see focus snap back to <body> as the key event unwinds).
|
||||
const target = toFocus;
|
||||
const tryFocus = function () {
|
||||
// Munger #1: bail if a newer open() has happened since close-time.
|
||||
if (openSeq !== seqAtClose) return;
|
||||
if (document.body.contains(target)) {
|
||||
try { target.focus(); } catch {}
|
||||
let t = toFocus;
|
||||
if (resolver) {
|
||||
try {
|
||||
const fresh = resolver();
|
||||
if (fresh) t = fresh;
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
// MINOR fix: don't steal focus if the user already focused another input
|
||||
if (document.activeElement &&
|
||||
document.activeElement !== document.body &&
|
||||
document.activeElement !== t &&
|
||||
!panel.contains(document.activeElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (t && document.body.contains(t)) {
|
||||
try { t.focus({ preventScroll: true }); } catch (_) {}
|
||||
}
|
||||
};
|
||||
tryFocus();
|
||||
requestAnimationFrame(tryFocus);
|
||||
|
||||
requestAnimationFrame(function () {
|
||||
if (document.activeElement && panel.contains(document.activeElement)) {
|
||||
try { document.activeElement.blur(); } catch (_) {}
|
||||
}
|
||||
tryFocus();
|
||||
setTimeout(tryFocus, 10);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -428,6 +428,36 @@ const PAGES = [
|
||||
assert(r.isActive, 'focus did NOT restore to originating row after Escape: ' + JSON.stringify(r));
|
||||
});
|
||||
|
||||
await step('focus-restore@800: re-renders while open still restore to new row instance', async () => {
|
||||
const rowKey = await openPanelFromRow();
|
||||
|
||||
// Force a re-render of the table so the original DOM node is detached
|
||||
await page.evaluate(() => {
|
||||
if (typeof window.renderRows === 'function') {
|
||||
window.renderRows();
|
||||
}
|
||||
});
|
||||
|
||||
await page.keyboard.press('Escape');
|
||||
// Wait for renderRows() + post-rAF focus restore to settle.
|
||||
await page.waitForFunction((key) => {
|
||||
const esc = (window.CSS && CSS.escape) ? CSS.escape(key) : key;
|
||||
const row = document.querySelector('#nodesTable tbody tr[data-value="' + esc + '"]');
|
||||
return !!row && document.activeElement === row;
|
||||
}, rowKey, { timeout: 2000 }).catch(() => {});
|
||||
|
||||
const r = await page.evaluate((key) => {
|
||||
const esc = (window.CSS && CSS.escape) ? CSS.escape(key) : key;
|
||||
const row = document.querySelector('#nodesTable tbody tr[data-value="' + esc + '"]');
|
||||
return {
|
||||
rowExists: !!row,
|
||||
isActive: !!row && document.activeElement === row,
|
||||
};
|
||||
}, rowKey);
|
||||
assert(r.rowExists, 'originating row vanished from DOM after manual re-render');
|
||||
assert(r.isActive, 'focus did NOT restore to NEW row instance after Escape: ' + JSON.stringify(r));
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// SKIP: tracked in #1172 — flaky in CI Chromium, see issue for repro.
|
||||
// X-click focus-restore is real and works locally; head-to-head with
|
||||
|
||||
Reference in New Issue
Block a user