diff --git a/src/apps/translatewp/__tests__/switcherSurfaces.test.tsx b/src/apps/translatewp/__tests__/switcherSurfaces.test.tsx
index 618cece..8fae5e3 100644
--- a/src/apps/translatewp/__tests__/switcherSurfaces.test.tsx
+++ b/src/apps/translatewp/__tests__/switcherSurfaces.test.tsx
@@ -379,6 +379,36 @@ describe('TranslateWP switcher surfaces', () =>
}
});
+ it('lets the visitor choose a language when the page holds a hidden copy of the switcher', async () =>
+ {
+ // Menus and page builder headers print a desktop copy and a hidden mobile copy.
+ installContext(true, 'dropdown');
+ window.TranslateWP.context.home_url = window.location.origin;
+ document.body.innerHTML = `
+
+
+ `;
+ window.history.replaceState(null, '', '/about/');
+ pendingBatch();
+
+ await renderApp();
+
+ const desktop = document.querySelector('.desktop') as HTMLElement;
+ const mobile = document.querySelector('.mobile') as HTMLElement;
+ await act(async () => { (desktop.querySelector('.twp-trigger') as HTMLButtonElement).click(); });
+ expect(mobile.querySelector('.twp-dropdown-item')).toBeNull();
+
+ const option = Array.from(desktop.querySelectorAll('.twp-dropdown-item'))
+ .find((item) => item.textContent?.includes('EspaƱol')) as HTMLButtonElement;
+ expect(option).toBeDefined();
+ // A real click presses the mouse first; every other copy sees that press as outside itself.
+ await act(async () => { option.dispatchEvent(new MouseEvent('mousedown', { bubbles: true })); });
+ await act(async () => { option.click(); });
+
+ expect(window.location.pathname).toBe('/es/about/');
+ expect(document.documentElement.lang).toBe('es');
+ });
+
it('translates an address in a language folder into that language and leaves the address alone', async () =>
{
installContext(true, 'dropdown');
diff --git a/src/apps/translatewp/index.tsx b/src/apps/translatewp/index.tsx
index c5041cc..deba7ad 100644
--- a/src/apps/translatewp/index.tsx
+++ b/src/apps/translatewp/index.tsx
@@ -61,7 +61,10 @@ export const TranslateWP = () =>
[languagesList]
);
const defaultLocale = languages.find((l) => l.default)?.locale ?? languages[0]?.locale ?? '';
- const [isOpen, setIsOpen] = useState(false);
+ // Which switcher on the page is open. A page can hold several copies,
+ // often one hidden for mobile, and only the one the visitor opened may
+ // close its own list.
+ const [openSwitcher, setOpenSwitcher] = useState(null);
const [selectedLocale, setSelectedLocale] = useState(defaultLocale);
const selectedLocaleRef = useRef(defaultLocale);
selectedLocaleRef.current = selectedLocale;
@@ -200,7 +203,7 @@ export const TranslateWP = () =>
function beginLanguageSelection(locale: string, history: 'push' | 'replace'): void
{
setActiveLocale(locale);
- setIsOpen(false);
+ setOpenSwitcher(null);
setLanguageAddress(locale, history);
applyDocumentLanguage(locale);
persistLanguageSelection(locale);
@@ -295,7 +298,7 @@ export const TranslateWP = () =>
if (selectedLocale !== defaultLocale)
{
resetToDefaultLanguage();
- setIsOpen(false);
+ setOpenSwitcher(null);
restorePageTitle();
setIsTranslating(false);
@@ -600,11 +603,17 @@ export const TranslateWP = () =>
const FrontendEditor = managerCan().edit ? resolveFrontendSlot('frontendEditor') : null;
const viewProps: SwitcherViewProps = {
- languages, switcher: switcher ?? {}, selectedLocale, isOpen, notice, isTranslating,
- onOpenChange: setIsOpen,
+ languages, switcher: switcher ?? {}, selectedLocale, notice, isTranslating,
+ isOpen: openSwitcher === 'floating',
+ onOpenChange: open => setOpenSwitcher(current => (open ? 'floating' : (current === 'floating' ? null : current))),
onLanguageSelect: language => translate(language),
onDismissNotice: hidePendingNotice,
};
+ const inlineProps = (placeholder: HTMLElement): SwitcherViewProps => ({
+ ...viewProps,
+ isOpen: openSwitcher === placeholder,
+ onOpenChange: open => setOpenSwitcher(current => (open ? placeholder : (current === placeholder ? null : current))),
+ });
return (
<>
@@ -622,7 +631,7 @@ export const TranslateWP = () =>
)}
{placeholders.map((placeholder) => (
createPortal(
- ,
+ ,
placeholder
)
))}