Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interactivity API: Add timeout option to navigate() #54474

Merged
merged 6 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@
data-wp-text="state.router.status"
>undefined</output>

<button
data-wp-on--click="actions.router.toggleTimeout"
data-testid="toggle timeout"
>
Timeout <span data-wp-text="state.router.timeout">NaN</span>
</button>

<?php
if ( isset( $attributes['links'] ) ) {
foreach ( $attributes['links'] as $key => $link ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
router: {
status: 'idle',
navigations: 0,
timeout: 10000,
}
},
actions: {
Expand All @@ -20,15 +21,20 @@
state.router.status = 'busy';

const force = e.target.dataset.forceNavigation === 'true';
const { timeout } = state.router;

await navigate( e.target.href, { force } );
await navigate( e.target.href, { force, timeout } );

state.router.navigations -= 1;

if ( state.router.navigations === 0) {
state.router.status = 'idle';
}
},
toggleTimeout: ( { state }) => {
state.router.timeout =
state.router.timeout === 10000 ? 0 : 10000;
}
},
},
} );
Expand Down
2 changes: 2 additions & 0 deletions packages/interactivity/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

- Improve `navigate()` to render only the result of the last call when multiple happen simultaneously. ([#54201](https://github.com/WordPress/gutenberg/pull/54201))

- Add `timeout` option to `navigate()`, with a default value of `10000` milliseconds. ([#54474](https://github.com/WordPress/gutenberg/pull/54474))

## 2.2.0 (2023-08-31)

### Enhancements
Expand Down
10 changes: 9 additions & 1 deletion packages/interactivity/src/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,14 @@ export const navigate = async ( href, options = {} ) => {
const url = cleanUrl( href );
navigatingTo = href;
prefetch( url, options );
const page = await pages.get( url );

// Create a promise that resolves when the specified timeout ends. The
// timeout value is 10 seconds by default.
const timeoutPromise = new Promise( ( resolve ) =>
setTimeout( resolve, options.timeout ?? 10000 )
);

const page = await Promise.race( [ pages.get( url ), timeoutPromise ] );

// Once the page is fetched, the destination URL could have changed (e.g.,
// by clicking another link in the meantime). If so, bail out, and let the
Expand All @@ -102,6 +109,7 @@ export const navigate = async ( href, options = {} ) => {
);
} else {
window.location.assign( href );
await new Promise( () => {} );
}
};

Expand Down
50 changes: 42 additions & 8 deletions test/e2e/specs/interactivity/router-navigate.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,24 +106,58 @@ test.describe( 'Router navigate', () => {
await expect( status ).toHaveText( 'busy' );
await expect( title ).toHaveText( 'Main' );

{
const resolver = resolvers.pop();
if ( resolver ) resolver();
}
resolvers.pop()!();

await expect( navigations ).toHaveText( '1' );
await expect( status ).toHaveText( 'busy' );
await expect( title ).toHaveText( 'Link 1' );
await expect( page ).toHaveURL( href );

{
const resolver = resolvers.pop();
if ( resolver ) resolver();
}
resolvers.pop()!();

await expect( navigations ).toHaveText( '0' );
await expect( status ).toHaveText( 'idle' );
await expect( title ).toHaveText( 'Link 1' );
await expect( page ).toHaveURL( href );
} );

test( 'should reload the next page when the timeout ends', async ( {
page,
interactivityUtils: utils,
} ) => {
const link1 = utils.getLink( 'router navigate - link 1' );

const title = page.getByTestId( 'title' );
const toggleTimeout = page.getByTestId( 'toggle timeout' );

let resolver: Function;

await page.route( link1, async ( route ) => {
// Only capture the first request.
if ( ! resolver ) {
await new Promise( ( r ) => ( resolver = r ) );
await route.abort();
} else {
await route.continue();
}
} );

await expect( toggleTimeout ).toHaveText( 'Timeout 10000' );

// Set timeout to 0.
await toggleTimeout.click();
await expect( toggleTimeout ).toHaveText( 'Timeout 0' );

// Navigation should timeout almost instantly.
await page.getByTestId( 'link 1' ).click();

await expect( page ).toHaveURL( link1 );
await expect( title ).toHaveText( 'Link 1' );

// If timeout is 10000, that means the page has been reloaded.
await expect( toggleTimeout ).toHaveText( 'Timeout 10000' );

// Make the fetch abort, just in case.
resolver!();
} );
} );
Loading