From 74db17e348509b0df31193d4bbc7fc3553a31ebe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D1=83=D1=80=D0=BD=D0=B0=D1=82=20=D0=90=D0=BD=D0=B4?= =?UTF-8?q?=D1=80=D0=B5=D0=B9?= Date: Thu, 4 Jun 2026 21:06:02 +0300 Subject: [PATCH] Sync app window title with active file --- src/{App.test.ts => App.test.tsx} | 36 +++++++++++++++++++++++++++++-- src/App.tsx | 6 +++++- 2 files changed, 39 insertions(+), 3 deletions(-) rename src/{App.test.ts => App.test.tsx} (58%) diff --git a/src/App.test.ts b/src/App.test.tsx similarity index 58% rename from src/App.test.ts rename to src/App.test.tsx index 0285702..fd4d244 100644 --- a/src/App.test.ts +++ b/src/App.test.tsx @@ -1,7 +1,15 @@ -import { describe, expect, it } from "vitest"; +import { cleanup, render, waitFor } from "@testing-library/react"; +import { afterEach, describe, expect, it } from "vitest"; import { initialState } from "./data/sampleData"; import type { QOfficeState } from "./types"; -import { qofficeWindowTitle } from "./App"; +import { App, qofficeWindowTitle } from "./App"; + +afterEach(() => { + cleanup(); + window.localStorage.clear(); + window.history.pushState({}, "", "/"); + window.document.title = ""; +}); describe("qofficeWindowTitle", () => { it("uses the current artifact name for standalone application title bars", () => { @@ -32,4 +40,28 @@ describe("qofficeWindowTitle", () => { expect(qofficeWindowTitle(state, "slides")).toBe("Презентация1 - QPowerPoint"); expect(qofficeWindowTitle(state, "mail")).toBe("Почта - QOutlook"); }); + + it("syncs the browser document title with the active standalone application", async () => { + const originalResizeObserver = globalThis.ResizeObserver; + const originalRequestAnimationFrame = window.requestAnimationFrame; + + globalThis.ResizeObserver = class { + observe() {} + disconnect() {} + } as unknown as typeof ResizeObserver; + window.requestAnimationFrame = ((callback: FrameRequestCallback) => { + callback(0); + return 1; + }) as typeof window.requestAnimationFrame; + window.history.pushState({}, "", "/qword"); + + try { + render(); + + await waitFor(() => expect(window.document.title).toBe(qofficeWindowTitle(initialState, "word"))); + } finally { + globalThis.ResizeObserver = originalResizeObserver; + window.requestAnimationFrame = originalRequestAnimationFrame; + } + }); }); diff --git a/src/App.tsx b/src/App.tsx index 9894863..1fccdbb 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import { useCallback, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import { BookOpen, Columns2, FileText, Minus, Plus, Rows3 } from "lucide-react"; import { AppShell } from "./components/AppShell"; import { QOutlook } from "./features/qoutlook/QOutlook"; @@ -125,6 +125,10 @@ export function App() { const activeApp = activeAppFromLocation(window.location); const windowTitle = qofficeWindowTitle(state, activeApp); + useEffect(() => { + window.document.title = windowTitle; + }, [windowTitle]); + const updateWordStatus = useCallback((nextStatus: QWordStatus) => { setWordStatus((current) => current.currentPage === nextStatus.currentPage && current.pageCount === nextStatus.pageCount && current.wordCount === nextStatus.wordCount ? current : nextStatus