diff --git a/apps/web/e2e/unique-ids.spec.ts b/apps/web/e2e/unique-ids.spec.ts new file mode 100644 index 00000000..835298da --- /dev/null +++ b/apps/web/e2e/unique-ids.spec.ts @@ -0,0 +1,20 @@ +import { expect, test } from '@playwright/test'; + +// The navbar renders the locale switcher twice, and the English flag used +// fixed clipPath ids, so every page had duplicate ids (DOS/Rate#1079). +// The English flag is drawn as the current locale's flag on /en pages. +for (const path of ['/en', '/en/smartphones']) { + test(`ids are unique on ${path}`, async ({ page }) => { + const res = await page.goto(path); + expect(res?.status()).toBe(200); + await expect(page.locator('nav').first()).toBeVisible(); + const duplicates = await page.evaluate(() => { + const counts = new Map(); + for (const el of document.querySelectorAll('[id]')) { + counts.set(el.id, (counts.get(el.id) ?? 0) + 1); + } + return [...counts].filter(([, n]) => n > 1).map(([id]) => id); + }); + expect(duplicates).toEqual([]); + }); +} diff --git a/apps/web/src/components/elementary/LocaleSwitcher.tsx b/apps/web/src/components/elementary/LocaleSwitcher.tsx index 106b7c84..ebd97a46 100644 --- a/apps/web/src/components/elementary/LocaleSwitcher.tsx +++ b/apps/web/src/components/elementary/LocaleSwitcher.tsx @@ -1,6 +1,12 @@ 'use client'; -import React, { useState, useRef, useEffect, useTransition } from 'react'; +import React, { + useState, + useRef, + useEffect, + useId, + useTransition, +} from 'react'; import { useLocale, useTranslations } from 'next-intl'; import { FaChevronDown } from 'react-icons/fa6'; @@ -27,38 +33,44 @@ const FlagVN = () => ( ); -const FlagEN = () => ( - - - - - - - - - - - - - - - -); +// The switcher renders twice (phone and desktop), so the clip ids must be +// unique per instance: fixed ids were duplicated on every page and one copy's +// url(#...) resolved into the other (DOS/Rate#1079). +const FlagEN = () => { + const clipId = useId(); + return ( + + + + + + + + + + + + + + + + ); +}; const FlagCS = () => (