Skip to content

Commit

Permalink
fix: do not block navigation to current url (#19730)
Browse files Browse the repository at this point in the history
Lets navigation cause server round-trip, when navigation is triggered by clicking a link.

Fixes #19635
  • Loading branch information
tepi authored Jul 31, 2024
1 parent f81d6bd commit 58b9432
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ function Flow() {
});
const {pathname, search, hash} = useLocation();
const navigated = useRef<boolean>(false);

const fromAnchor = useRef<boolean>(false);
const containerRef = useRef<RouterContainer | undefined>(undefined);

const navigateEventHandler = useCallback((event: MouseEvent) => {
Expand All @@ -184,6 +184,9 @@ function Flow() {
}

navigated.current = false;
// When navigation is triggered by click on a link, fromAnchor is set to true
// in order to get a server round-trip even when navigating to the same URL again
fromAnchor.current = true;
navigate(path);
}, [navigate]);

Expand Down Expand Up @@ -233,10 +236,12 @@ function Flow() {

useEffect(() => {
if (blocker.state === 'blocked') {
if(navigated.current) {
// Do not skip server round-trip if navigation originates from a click on a link
if (navigated.current && !fromAnchor.current) {
blocker.proceed();
return;
}
fromAnchor.current = false;
const {pathname, search} = blocker.location;
const routes = ((window as any)?.Vaadin?.routesConfig || []) as AgnosticRouteObject[];
let matched = matchRoutes(Array.from(routes), window.location.pathname);
Expand Down Expand Up @@ -284,7 +289,7 @@ function Flow() {
}, [blocker.state, blocker.location]);

useEffect(() => {
if(navigated.current) {
if (navigated.current) {
navigated.current = false;
fireNavigated(pathname,search);
return;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright 2000-2024 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package com.vaadin.flow.uitest.ui;

import com.vaadin.flow.component.html.Anchor;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.dom.Element;
import com.vaadin.flow.router.AfterNavigationEvent;
import com.vaadin.flow.router.AfterNavigationObserver;
import com.vaadin.flow.router.BeforeEnterEvent;
import com.vaadin.flow.router.BeforeEnterObserver;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.router.RouterLink;
import com.vaadin.flow.uitest.servlet.ViewTestLayout;

@Route(value = "com.vaadin.flow.uitest.ui.NavigationEventsView", layout = ViewTestLayout.class)
public class NavigationEventsView extends Div
implements BeforeEnterObserver, AfterNavigationObserver {

public static final String BEFORE_ENTER = "BeforeEnterEvent";
public static final String AFTER_NAVIGATION = "AfterNavigationEvent";

private final Element messages = new Element("div");

public NavigationEventsView() {
messages.setAttribute("id", "messages");
getElement().appendChild(messages);

RouterLink routerLink = new RouterLink("RouterLink to self",
NavigationEventsView.class);
routerLink.setId("router-link");
Anchor anchor = new Anchor(
"/view/com.vaadin.flow.uitest.ui.NavigationEventsView",
"Anchor to self");
anchor.setId("anchor");

add(routerLink, anchor);
}

@Override
public void beforeEnter(BeforeEnterEvent event) {
addMessage(BEFORE_ENTER);
}

@Override
public void afterNavigation(AfterNavigationEvent afterNavigationEvent) {
addMessage(AFTER_NAVIGATION);
}

private void addMessage(String message) {
Element element = new Element("div");
element.setText(message);
messages.appendChild(element);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2000-2024 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.flow.uitest.ui;

import java.util.List;
import java.util.stream.Collectors;

import org.junit.Assert;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;

import com.vaadin.flow.component.html.testbench.AnchorElement;
import com.vaadin.flow.testutil.ChromeBrowserTest;

public class NavigationEventsIT extends ChromeBrowserTest {

@Test
public void assertNavigationToSelfProducesNavigationEvents() {
open();

// Initially there should be one round of navigation events
assertMessages(2);

// RouterLink click should cause second set of events
$(AnchorElement.class).id("router-link").click();
assertMessages(4);

// Anchor click should cause third set of events
$(AnchorElement.class).id("anchor").click();
assertMessages(6);
}

private void assertMessages(int expectedSize) {
List<String> messages = getMessages();
Assert.assertEquals("Unexpected amount of navigation events",
expectedSize, messages.size());
Assert.assertEquals("Second to last event should be BeforeEnter",
NavigationEventsView.BEFORE_ENTER,
messages.get(expectedSize - 2));
Assert.assertEquals("Last event should be AfterNavigation",
NavigationEventsView.AFTER_NAVIGATION,
messages.get(expectedSize - 1));

}

private List<String> getMessages() {
return findElement(By.id("messages"))
.findElements(By.cssSelector("div")).stream()
.map(WebElement::getText).collect(Collectors.toList());
}
}

0 comments on commit 58b9432

Please sign in to comment.