diff --git a/apps/web/e2e/admin-settings.spec.ts b/apps/web/e2e/admin-settings.spec.ts index 2b0e2b1..8ced0c3 100644 --- a/apps/web/e2e/admin-settings.spec.ts +++ b/apps/web/e2e/admin-settings.spec.ts @@ -33,9 +33,10 @@ test('instance name changed in the general settings form persists', async ({ bro // Scoped to the card: the page has several forms with status regions. await expect(generalCard.getByRole('status')).toHaveText(/^(Saved\.|Gespeichert\.)$/); - // The TopBar shows the new name without a reload — the save invalidates - // the branding query the TopBar reads from. + // The TopBar and the document title show the new name without a reload — + // the save invalidates the branding query both read from (issue #323). await expect(page.locator('.topbar__brand')).toHaveText(newName); + await expect(page).toHaveTitle(new RegExp(`${newName}$`)); // The proof the form really persisted: the value survives a reload and // the api returns it. diff --git a/apps/web/src/lib/use-document-title.ts b/apps/web/src/lib/use-document-title.ts index a3ec0a7..4d02608 100644 --- a/apps/web/src/lib/use-document-title.ts +++ b/apps/web/src/lib/use-document-title.ts @@ -1,22 +1,33 @@ import { useEffect } from 'react'; +import { useBranding } from '../branding/use-branding'; + const APP_NAME = 'Dorfteich'; /** * Route-specific document title (issue #163, WCAG 2.4.2): joins the given - * parts with the app name ("Page — Pond — Dorfteich"). Empty/undefined + * parts with the instance name ("Page — Pond — My Wiki"). Empty/undefined * parts are skipped, so callers can pass still-loading data directly. - * Falls back to the bare app name on unmount. + * Falls back to the bare instance name on unmount. + * + * The trailing name is the OPERATOR'S instance name, not the product name + * (issue #323) — same reasoning as the TopBar brand (issue #306). Until + * the branding query resolves (or when it cannot, e.g. maintenance mode) + * the shipped default keeps the title stable, so an untouched instance + * reads exactly as before. */ export function useDocumentTitle(...parts: (string | null | undefined)[]): void { - const joined = [...parts.filter(Boolean), APP_NAME].join(' — '); + const appName = useBranding()?.instanceName.trim() || APP_NAME; + const joined = [...parts.filter(Boolean), appName].join(' — '); useEffect(() => { document.title = joined; }, [joined]); useEffect( + // On unmount only in effect: `joined` always changes with `appName`, + // so the title effect above re-runs right after this cleanup. () => () => { - document.title = APP_NAME; + document.title = appName; }, - [], + [appName], ); }