diff --git a/flow-server/src/main/java/com/vaadin/flow/component/internal/UIInternals.java b/flow-server/src/main/java/com/vaadin/flow/component/internal/UIInternals.java index c5cc329da71..f33939089ea 100644 --- a/flow-server/src/main/java/com/vaadin/flow/component/internal/UIInternals.java +++ b/flow-server/src/main/java/com/vaadin/flow/component/internal/UIInternals.java @@ -699,12 +699,8 @@ public boolean containsPendingJavascript(String containsFilter) { */ public void setTitle(String title) { assert title != null; - JavaScriptInvocation invocation = new JavaScriptInvocation(""" - document.title = $0; - if(window?.Vaadin?.documentTitleSignal) { - window.Vaadin.documentTitleSignal.value = $0; - } - """.stripIndent(), title); + JavaScriptInvocation invocation = new JavaScriptInvocation( + generateTitleScript().stripIndent(), title); pendingTitleUpdateCanceler = new PendingJavaScriptInvocation( getStateTree().getRootNode(), invocation); @@ -713,6 +709,23 @@ public void setTitle(String title) { this.title = title; } + private String generateTitleScript() { + String setTitleScript = """ + document.title = $0; + if(window?.Vaadin?.documentTitleSignal) { + window.Vaadin.documentTitleSignal.value = $0; + } + """; + if (getSession().getConfiguration().isReactEnabled()) { + // For react-router we should wait for navigation to finish + // before updating the title. + setTitleScript = String.format( + "if(window.Vaadin.Flow.navigation) { window.addEventListener('vaadin-navigated', function(event) {%s}, {once:true}); } else { %1$s }", + setTitleScript); + } + return setTitleScript; + } + /** * Records the text content of the title tag in the application shell. *

diff --git a/flow-server/src/main/resources/com/vaadin/flow/server/frontend/Flow.tsx b/flow-server/src/main/resources/com/vaadin/flow/server/frontend/Flow.tsx index 8cf8e18d7ab..1883c734aac 100644 --- a/flow-server/src/main/resources/com/vaadin/flow/server/frontend/Flow.tsx +++ b/flow-server/src/main/resources/com/vaadin/flow/server/frontend/Flow.tsx @@ -151,13 +151,16 @@ function extractPath(event: MouseEvent): void | string { * @param search search of navigation */ function fireNavigated(pathname:string, search: string) { - setTimeout(() => - window.dispatchEvent(new CustomEvent('vaadin-navigated', { - detail: { - pathname, - search - } - })) + setTimeout(() => { + window.dispatchEvent(new CustomEvent('vaadin-navigated', { + detail: { + pathname, + search + } + })); + // @ts-ignore + delete window.Vaadin.Flow.navigation; + } ) } @@ -255,6 +258,8 @@ function Flow() { }, [navigate]); const vaadinNavigateEventHandler = useCallback((event: CustomEvent<{state: unknown, url: string, replace?: boolean, callback: boolean}>) => { + // @ts-ignore + window.Vaadin.Flow.navigation = true; const path = '/' + event.detail.url; navigated.current = !event.detail.callback; fromAnchor.current = false; diff --git a/flow-tests/test-root-context/src/test/java/com/vaadin/flow/uitest/ui/PageIT.java b/flow-tests/test-root-context/src/test/java/com/vaadin/flow/uitest/ui/PageIT.java index d430936a813..adf95eeaf07 100644 --- a/flow-tests/test-root-context/src/test/java/com/vaadin/flow/uitest/ui/PageIT.java +++ b/flow-tests/test-root-context/src/test/java/com/vaadin/flow/uitest/ui/PageIT.java @@ -9,6 +9,7 @@ import org.openqa.selenium.By; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.Keys; +import org.openqa.selenium.TimeoutException; import com.vaadin.flow.component.html.testbench.DivElement; import com.vaadin.flow.component.html.testbench.InputTextElement; @@ -52,8 +53,12 @@ public void testPageTitleClears() { } private void verifyTitle(String title) { - Assert.assertEquals("Page title does not match", title, - getDriver().getTitle()); + try { + waitUntil(driver -> driver.getTitle().equals(title)); + } catch (TimeoutException te) { + Assert.fail("Page title does not match. Expected: " + title + + ", Actual: " + driver.getTitle()); + } } private void updateTitle(String title) {