fix(nav): the dead-page rewrite does not restore a spent focus stamp
It spread the event's snapshot, which still carried the sfNav stamp lib/history's own listener had just taken off the entry, so a later landing there (an overlay closing) refocused a stale item. It now spreads the live entry (test red on the snapshot). Also: format.test's comment named a test image that never runs vitest; it is the gate host's locale.
This commit is contained in:
@@ -191,6 +191,19 @@ describe("NavigationProvider · Back", () => {
|
|||||||
expect(window.history.state).toMatchObject({ sfPage: "feed" });
|
expect(window.history.state).toMatchObject({ sfPage: "feed" });
|
||||||
expect(window.location.search).toBe("?keep=1");
|
expect(window.location.search).toBe("?keep=1");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// lib/history's own listener takes a spent focus stamp off the entry it lands on; the rewrite must
|
||||||
|
// not put it back, or the next landing there (an overlay closing) would refocus a stale item.
|
||||||
|
it("does not put back the focus stamp the landing spent", () => {
|
||||||
|
render();
|
||||||
|
const st = { sfPage: "messages", sfNav: { list: 0, lists: 1 } };
|
||||||
|
act(() => {
|
||||||
|
window.history.replaceState(st, "", "/?page=messages");
|
||||||
|
window.dispatchEvent(new PopStateEvent("popstate", { state: st }));
|
||||||
|
});
|
||||||
|
expect(window.history.state).toMatchObject({ sfPage: "feed" });
|
||||||
|
expect(window.history.state).not.toHaveProperty("sfNav");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("NavigationProvider · never under an open overlay", () => {
|
describe("NavigationProvider · never under an open overlay", () => {
|
||||||
|
|||||||
@@ -200,11 +200,16 @@ export function NavigationProvider({ children }: { children: ReactNode }) {
|
|||||||
// An entry can name a page this build no longer has (one pushed before a module was removed,
|
// An entry can name a page this build no longer has (one pushed before a module was removed,
|
||||||
// reached by Back after a reload onto the new build); that lands on the feed, not on a crash.
|
// reached by Back after a reload onto the new build); that lands on the feed, not on a crash.
|
||||||
// The entry is rewritten to say so: left alone, the address bar would keep naming the dead page
|
// The entry is rewritten to say so: left alone, the address bar would keep naming the dead page
|
||||||
// and a click on Feed would be a no-op (setPage's same-page guard).
|
// and a click on Feed would be a no-op (setPage's same-page guard). It spreads the LIVE entry,
|
||||||
|
// not `e.state`: lib/history's listener ran first and already took the spent `sfNav` off it.
|
||||||
const sf: unknown = e.state?.sfPage;
|
const sf: unknown = e.state?.sfPage;
|
||||||
const p: Page = typeof sf === "string" && isPage(sf) ? sf : "feed";
|
const p: Page = typeof sf === "string" && isPage(sf) ? sf : "feed";
|
||||||
if (typeof sf === "string" && sf !== p)
|
if (typeof sf === "string" && sf !== p)
|
||||||
window.history.replaceState({ ...e.state, sfPage: p }, "", urlWithParams({ page: null }));
|
window.history.replaceState(
|
||||||
|
{ ...window.history.state, sfPage: p },
|
||||||
|
"",
|
||||||
|
urlWithParams({ page: null }),
|
||||||
|
);
|
||||||
setPageState(p);
|
setPageState(p);
|
||||||
setAccountRaw(PAGE_KEY, p);
|
setAccountRaw(PAGE_KEY, p);
|
||||||
// The YouTube-search sub-view and the channel page ride in history.state, so Back/Forward
|
// The YouTube-search sub-view and the channel page ride in history.state, so Back/Forward
|
||||||
|
|||||||
@@ -132,8 +132,8 @@ describe("relativeFromMs", () => {
|
|||||||
// A bare `n.toLocaleString()` groups in the BROWSER's locale, so an English UI in a Hungarian browser
|
// A bare `n.toLocaleString()` groups in the BROWSER's locale, so an English UI in a Hungarian browser
|
||||||
// printed "1 234 567" (and a Hungarian UI in an English browser "1,234,567"). The case below asserts
|
// printed "1 234 567" (and a Hungarian UI in an English browser "1,234,567"). The case below asserts
|
||||||
// BOTH languages and that the two differ, rather than comparing against the runtime's own default:
|
// BOTH languages and that the two differ, rather than comparing against the runtime's own default:
|
||||||
// the default is whatever locale the machine running the suite has (Hungarian on the dev box, English
|
// the default is whatever locale the host running the gate has (Hungarian on the dev box, English or
|
||||||
// in the test image), so a one-language assertion passes under the old bug on half the machines.
|
// C on a Linux host), so a one-language assertion passes under the old bug on half the machines.
|
||||||
describe("formatNumber", () => {
|
describe("formatNumber", () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
i18nStub.language = "en";
|
i18nStub.language = "en";
|
||||||
|
|||||||
Reference in New Issue
Block a user