Skip to content

Commit

Permalink
feat: push and replace without navigation callback (#19736)
Browse files Browse the repository at this point in the history
* feat: push and replace without navigation callback

Add feature to support replace and
push without getting a callback
to the server for the change.

fixes #19613

* change default callback for replace also to false

* history gets same events as previously with vaadin-router.

* Forward should callback to update url.

* Fix java doc link
  • Loading branch information
caalador authored Aug 9, 2024
1 parent ed02ec3 commit 432256d
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,29 @@ public void pushState(JsonValue state, String location) {

/**
* Invokes <code>history.pushState</code> in the browser with the given
* parameters.
* parameters. This is a shorthand method for
* {@link History#pushState(JsonValue, Location, boolean)}, creating
* {@link Location} from the string provided.
*
* @param state
* the JSON state to push to the history stack, or
* <code>null</code> to only change the location
* @param location
* the new location to set in the browser, or <code>null</code>
* to only change the JSON state
* @param callback
* {@code true} if the change should make a return call to the
* server
*/
public void pushState(JsonValue state, String location, boolean callback) {
pushState(state,
Optional.ofNullable(location).map(Location::new).orElse(null),
callback);
}

/**
* Invokes <code>history.pushState</code> in the browser with the given
* parameters. Does not make a callback to the server by default.
*
* @param state
* the JSON state to push to the history stack, or
Expand All @@ -182,14 +204,33 @@ public void pushState(JsonValue state, String location) {
* to only change the JSON state
*/
public void pushState(JsonValue state, Location location) {
pushState(state, location, false);
}

/**
* Invokes <code>history.pushState</code> in the browser with the given
* parameters.
*
* @param state
* the JSON state to push to the history stack, or
* <code>null</code> to only change the location
* @param location
* the new location to set in the browser, or <code>null</code>
* to only change the JSON state
* @param callback
* {@code true} if the change should make a return call to the
* server
*/
public void pushState(JsonValue state, Location location,
boolean callback) {
final String pathWithQueryParameters = getPathWithQueryParameters(
location);
// Second parameter is title which is currently ignored according to
// https://developer.mozilla.org/en-US/docs/Web/API/History_API
if (ui.getSession().getConfiguration().isReactEnabled()) {
ui.getPage().executeJs(
"window.dispatchEvent(new CustomEvent('vaadin-navigate', { detail: { state: $0, url: $1, replace: false } }));",
state, pathWithQueryParameters);
"window.dispatchEvent(new CustomEvent('vaadin-navigate', { detail: { state: $0, url: $1, replace: false, callback: $2 } }));",
state, pathWithQueryParameters, callback);
} else {
ui.getPage().executeJs(
"setTimeout(() => { window.history.pushState($0, '', $1); window.dispatchEvent(new CustomEvent('vaadin-navigated')); })",
Expand Down Expand Up @@ -217,7 +258,30 @@ public void replaceState(JsonValue state, String location) {

/**
* Invokes <code>history.replaceState</code> in the browser with the given
* parameters.
* parameters. This is a shorthand method for
* {@link History#replaceState(JsonValue, Location, boolean)}, creating
* {@link Location} from the string provided.
*
* @param state
* the JSON state to push to the history stack, or
* <code>null</code> to only change the location
* @param location
* the new location to set in the browser, or <code>null</code>
* to only change the JSON state
* @param callback
* {@code true} if the change should make a return call to the
* server
*/
public void replaceState(JsonValue state, String location,
boolean callback) {
replaceState(state,
Optional.ofNullable(location).map(Location::new).orElse(null),
callback);
}

/**
* Invokes <code>history.replaceState</code> in the browser with the given
* parameters. Does not make a callback to the server by default.
*
* @param state
* the JSON state to push to the history stack, or
Expand All @@ -227,14 +291,33 @@ public void replaceState(JsonValue state, String location) {
* to only change the JSON state
*/
public void replaceState(JsonValue state, Location location) {
replaceState(state, location, false);
}

/**
* Invokes <code>history.replaceState</code> in the browser with the given
* parameters.
*
* @param state
* the JSON state to push to the history stack, or
* <code>null</code> to only change the location
* @param location
* the new location to set in the browser, or <code>null</code>
* to only change the JSON state
* @param callback
* {@code true} if the change should make a return call to the
* server
*/
public void replaceState(JsonValue state, Location location,
boolean callback) {
final String pathWithQueryParameters = getPathWithQueryParameters(
location);
// Second parameter is title which is currently ignored according to
// https://developer.mozilla.org/en-US/docs/Web/API/History_API
if (ui.getSession().getConfiguration().isReactEnabled()) {
ui.getPage().executeJs(
"window.dispatchEvent(new CustomEvent('vaadin-navigate', { detail: { state: $0, url: $1, replace: true } }));",
state, pathWithQueryParameters);
"window.dispatchEvent(new CustomEvent('vaadin-navigate', { detail: { state: $0, url: $1, replace: true, callback: $2 } }));",
state, pathWithQueryParameters, callback);
} else {
ui.getPage().executeJs(
"setTimeout(() => { window.history.replaceState($0, '', $1); window.dispatchEvent(new CustomEvent('vaadin-navigated')); })",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -727,7 +727,7 @@ private int forward(NavigationEvent event, BeforeEvent beforeNavigation) {
NavigationEvent newNavigationEvent = getNavigationEvent(event,
beforeNavigation);
newNavigationEvent.getUI().getPage().getHistory().replaceState(null,
newNavigationEvent.getLocation());
newNavigationEvent.getLocation(), true);

return handler.handle(newNavigationEvent);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,9 @@ function Flow() {
navigate(path);
}, [navigate]);

const vaadinNavigateEventHandler = useCallback((event: CustomEvent<{state: unknown, url: string, replace?: boolean}>) => {
const vaadinNavigateEventHandler = useCallback((event: CustomEvent<{state: unknown, url: string, replace?: boolean, callback: boolean}>) => {
const path = '/' + event.detail.url;
navigated.current = !event.detail.replace;
navigated.current = !event.detail.callback;
navigate(path, { state: event.detail.state, replace: event.detail.replace});
}, [navigate]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
public class JavaScriptBootstrapUITest {

private static final String CLIENT_PUSHSTATE_TO = "setTimeout(() => { window.history.pushState($0, '', $1); window.dispatchEvent(new CustomEvent('vaadin-navigated')); })";
private static final String REACT_PUSHSTATE_TO = "window.dispatchEvent(new CustomEvent('vaadin-navigate', { detail: { state: $0, url: $1, replace: false } }));";
private static final String REACT_PUSHSTATE_TO = "window.dispatchEvent(new CustomEvent('vaadin-navigate', { detail: { state: $0, url: $1, replace: false, callback: $2 } }));";

private MockServletServiceSessionSetup mocks;
private UI ui;
Expand Down Expand Up @@ -501,7 +501,7 @@ public void server_should_not_doClientRoute_when_navigatingToServer() {
} else {
assertEquals(CLIENT_PUSHSTATE_TO, execJs.getValue());
}
assertEquals(2, execValues.length);
assertEquals(3, execValues.length);
assertNull(execValues[0]);
assertEquals("dirty", execValues[1]);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ public PendingJavaScriptResult executeJs(String expression,
private static final String PUSH_STATE_JS = "setTimeout(() => { window.history.pushState($0, '', $1); window.dispatchEvent(new CustomEvent('vaadin-navigated')); })";
private static final String REPLACE_STATE_JS = "setTimeout(() => { window.history.replaceState($0, '', $1); window.dispatchEvent(new CustomEvent('vaadin-navigated')); })";

private static final String PUSH_STATE_REACT = "window.dispatchEvent(new CustomEvent('vaadin-navigate', { detail: { state: $0, url: $1, replace: false } }));";
private static final String REPLACE_STATE_REACT = "window.dispatchEvent(new CustomEvent('vaadin-navigate', { detail: { state: $0, url: $1, replace: true } }));";
private static final String PUSH_STATE_REACT = "window.dispatchEvent(new CustomEvent('vaadin-navigate', { detail: { state: $0, url: $1, replace: false, callback: $2 } }));";
private static final String REPLACE_STATE_REACT = "window.dispatchEvent(new CustomEvent('vaadin-navigate', { detail: { state: $0, url: $1, replace: true, callback: $2 } }));";

@Before
public void setup() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,8 @@ public void testHistory() throws URISyntaxException {
// Forward to originally pushed state
forwardButton.click();
Assert.assertEquals(baseUrl.resolve("asdf"), getCurrentUrl());
Assert.assertEquals(Arrays.asList("New location: qwerty",
"New location: asdf", "New state: {\"foo\":true}"),
getStatusMessages());
Assert.assertEquals(Arrays.asList("New location: asdf",
"New state: {\"foo\":true}"), getStatusMessages());
clearButton.click();

// Back to the replaced state
Expand Down

0 comments on commit 432256d

Please sign in to comment.