fix(ui): align PWA display-mode listeners

This commit is contained in:
Dotta
2026-06-01 21:56:19 +00:00
parent 7ce96e36a0
commit cfcdf2dea9
2 changed files with 104 additions and 7 deletions
@@ -25,8 +25,47 @@ async function flushReact() {
});
}
function installMatchMedia(initialMatches: Record<string, boolean> = {}) {
type Listener = (event: MediaQueryListEvent) => void;
const queries = new Map<string, { matches: boolean; listeners: Set<Listener> }>();
function getQuery(query: string) {
let entry = queries.get(query);
if (!entry) {
entry = { matches: initialMatches[query] ?? false, listeners: new Set<Listener>() };
queries.set(query, entry);
}
return {
get matches() {
return entry.matches;
},
media: query,
addEventListener: (_type: "change", listener: Listener) => entry.listeners.add(listener),
removeEventListener: (_type: "change", listener: Listener) => entry.listeners.delete(listener),
addListener: (listener: Listener) => entry.listeners.add(listener),
removeListener: (listener: Listener) => entry.listeners.delete(listener),
} as MediaQueryList;
}
Object.defineProperty(window, "matchMedia", {
configurable: true,
value: (query: string) => getQuery(query),
});
return {
setMatches(query: string, matches: boolean) {
const entry = queries.get(query) ?? { matches: false, listeners: new Set<Listener>() };
entry.matches = matches;
queries.set(query, entry);
entry.listeners.forEach((listener) => listener({ matches, media: query } as MediaQueryListEvent));
},
};
}
describe("StandaloneBrowserControls", () => {
let container: HTMLDivElement;
const originalMatchMedia = window.matchMedia;
beforeEach(() => {
container = document.createElement("div");
@@ -36,6 +75,11 @@ describe("StandaloneBrowserControls", () => {
afterEach(() => {
delete (navigator as Navigator & { standalone?: boolean }).standalone;
if (originalMatchMedia) {
Object.defineProperty(window, "matchMedia", { configurable: true, value: originalMatchMedia });
} else {
delete (window as Window & { matchMedia?: Window["matchMedia"] }).matchMedia;
}
container.remove();
document.body.innerHTML = "";
});
@@ -62,4 +106,57 @@ describe("StandaloneBrowserControls", () => {
root.unmount();
});
});
it("hides controls in normal mobile browser mode", async () => {
Object.defineProperty(navigator, "standalone", { configurable: true, value: false });
installMatchMedia();
const root = createRoot(container);
await act(async () => {
root.render(
<TooltipProvider>
<ToastProvider>
<StandaloneBrowserControls mobile />
</ToastProvider>
</TooltipProvider>,
);
});
await flushReact();
expect(container.querySelector('[aria-label="Refresh"]')).toBeNull();
await act(async () => {
root.unmount();
});
});
it("responds to all chromeless display-mode media query changes", async () => {
Object.defineProperty(navigator, "standalone", { configurable: true, value: false });
const media = installMatchMedia();
const root = createRoot(container);
await act(async () => {
root.render(
<TooltipProvider>
<ToastProvider>
<StandaloneBrowserControls mobile />
</ToastProvider>
</TooltipProvider>,
);
});
await flushReact();
expect(container.querySelector('[aria-label="Refresh"]')).toBeNull();
await act(() => {
media.setMatches("(display-mode: fullscreen)", true);
});
await flushReact();
expect(container.querySelector('[aria-label="Refresh"]')).not.toBeNull();
await act(async () => {
root.unmount();
});
});
});