From 5002b62d0082b0aa5a962046b640bb42f7bf9b8d Mon Sep 17 00:00:00 2001 From: Christopher Allford <6451942+ObliviousHarmony@users.noreply.github.com> Date: Thu, 21 Mar 2024 14:03:13 -0700 Subject: [PATCH] Reset Product Collection Pagination When Filtering (#45693) Since we can't be sure that a page exists once the filters have been changed we should reset the page when they do. --- .../js/blocks/product-filter/frontend.ts | 23 ++++++++++ .../assets/js/utils/filters.ts | 43 ++++++++----------- .../changelog/fix-45644-filter-page-reset | 4 ++ 3 files changed, 45 insertions(+), 25 deletions(-) create mode 100644 plugins/woocommerce/changelog/fix-45644-filter-page-reset diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/frontend.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filter/frontend.ts index 5caba95ce815f..13a489f89c0ea 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/frontend.ts +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filter/frontend.ts @@ -12,6 +12,29 @@ const needsRefresh = getSetting< boolean >( ); export function navigate( href: string, options = {} ) { + /** + * We may need to reset the current page when changing filters. + * This is because the current page may not exist for this set + * of filters and will 404 when the user navigates to it. + * + * There are different pagination formats to consider, as documented here: + * https://github.com/WordPress/gutenberg/blob/317eb8f14c8e1b81bf56972cca2694be250580e3/packages/block-library/src/query-pagination-numbers/index.php#L22-L85 + */ + const url = new URL( href ); + // When pretty permalinks are enabled, the page number may be in the path name. + url.pathname = url.pathname.replace( /\/page\/[0-9]+/i, '' ); + // When plain permalinks are enabled, the page number may be in the "paged" query parameter. + url.searchParams.delete( 'paged' ); + // On posts and pages the page number will be in a query parameter that + // identifies which block we are paginating. + url.searchParams.forEach( ( _, key ) => { + if ( key.match( /^query(?:-[0-9]+)?-page$/ ) ) { + url.searchParams.delete( key ); + } + } ); + // Make sure to update the href with the changes. + href = url.href; + if ( needsRefresh || ( ! isBlockTheme && isProductArchive ) ) { return ( window.location.href = href ); } diff --git a/plugins/woocommerce-blocks/assets/js/utils/filters.ts b/plugins/woocommerce-blocks/assets/js/utils/filters.ts index 059d7da5d7720..c2b887ef749b6 100644 --- a/plugins/woocommerce-blocks/assets/js/utils/filters.ts +++ b/plugins/woocommerce-blocks/assets/js/utils/filters.ts @@ -35,34 +35,27 @@ export function getUrlParameter( name: string ) { export function changeUrl( newUrl: string ) { if ( filteringForPhpTemplate ) { /** - * We want to remove page number from URL whenever filters are changed. - * This will move the user to the first page of results. + * We may need to reset the current page when changing filters. + * This is because the current page may not exist for this set + * of filters and will 404 when the user navigates to it. * - * There are following page number formats: - * 1. query-{number}-page={number} (ex. query-1-page=2) - * - ref: https://github.com/WordPress/gutenberg/blob/5693a62214b6c76d3dc5f3f69d8aad187748af79/packages/block-library/src/query-pagination-numbers/index.php#L18 - * 2. query-page={number} (ex. query-page=2) - * - ref: same as above - * 3. page/{number} (ex. page/2) (Default WordPress pagination format) + * There are different pagination formats to consider, as documented here: + * https://github.com/WordPress/gutenberg/blob/317eb8f14c8e1b81bf56972cca2694be250580e3/packages/block-library/src/query-pagination-numbers/index.php#L22-L85 */ - newUrl = newUrl.replace( - /(?:query-(?:\d+-)?page=(\d+))|(?:page\/(\d+))/g, - '' - ); + const url = new URL( newUrl ); + // When pretty permalinks are enabled, the page number may be in the path name. + url.pathname = url.pathname.replace( /\/page\/[0-9]+/i, '' ); + // When plain permalinks are enabled, the page number may be in the "paged" query parameter. + url.searchParams.delete( 'paged' ); + // On posts and pages the page number will be in a query parameter that + // identifies which block we are paginating. + url.searchParams.forEach( ( _, key ) => { + if ( key.match( /^query(?:-[0-9]+)?-page$/ ) ) { + url.searchParams.delete( key ); + } + } ); - /** - * If the URL ends with '?', we remove the trailing '?' from the URL. - * The trailing '?' in a URL is unnecessary and can cause the page to - * reload, which can negatively affect performance. By removing the '?', - * we prevent this unnecessary reload. This is safe to do even if there - * are query parameters, as they will not be affected by the removal - * of a trailing '?'. - */ - if ( newUrl.endsWith( '?' ) ) { - newUrl = newUrl.slice( 0, -1 ); - } - - window.location.href = newUrl; + window.location.href = url.href; } else { window.history.replaceState( {}, '', newUrl ); } diff --git a/plugins/woocommerce/changelog/fix-45644-filter-page-reset b/plugins/woocommerce/changelog/fix-45644-filter-page-reset new file mode 100644 index 0000000000000..9c43619e36107 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-45644-filter-page-reset @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Reset Product Collection block pagination when filters change.