From ca0ff97ed5019662f12a0245140a8f6e39966deb Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 10:32:24 +0000 Subject: [PATCH] Drop the loop's page parameter when a search runs The search block's form action is built from the current URL, so the pagination parameter the pagination block wrote is carried into the search. Searching from page 2 asks for page 2 of the matches, and a term with only one page of results renders an empty loop over results that do exist. Remove the loop's page parameter from the action, named as core names it so the parameter the pagination block wrote is the one that gets dropped. The taxonomy filter already resets pagination this way. Reported and first fixed by @samikeijonen in #22, which resets the same parameter on the JS side. Props samikeijonen. Fixes #21 Co-authored-by: Sami Keijonen <1820415+samikeijonen@users.noreply.github.com> Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Lj871x28S1DXjoVm7AB4k5 --- inc/namespace.php | 22 +++++++++++++++++----- tests/e2e/README.md | 1 + tests/e2e/search-filter.spec.js | 17 +++++++++++++++++ tests/seed.php | 21 +++++++++++++++++++++ 4 files changed, 56 insertions(+), 5 deletions(-) diff --git a/inc/namespace.php b/inc/namespace.php index 8f53a69..bd505e1 100644 --- a/inc/namespace.php +++ b/inc/namespace.php @@ -302,11 +302,23 @@ function render_block_search( string $block_content, array $block, \WP_Block $in wp_enqueue_script_module( 'query-filter-taxonomy-view-script-module' ); - $query_var = empty( $instance->context['query']['inherit'] ) - ? sprintf( 'query-%d-s', $instance->context['queryId'] ?? 0 ) - : 'query-s'; - - $action = str_replace( '/page/' . get_query_var( 'paged', 1 ), '', add_query_arg( [ $query_var => '' ] ) ); + $inherit = ! empty( $instance->context['query']['inherit'] ); + + $query_var = $inherit + ? 'query-s' + : sprintf( 'query-%d-s', $instance->context['queryId'] ?? 0 ); + + // A search is a new set of results, so it belongs on the first page. Left + // in place, the page the visitor happened to be on is carried into the + // search and a term with fewer pages of matches than that renders empty. + // Named as core names it, so the parameter the pagination block wrote is + // the one that gets dropped. + $page_var = $inherit + ? 'page' + : ( isset( $instance->context['queryId'] ) ? 'query-' . $instance->context['queryId'] . '-page' : 'query-page' ); + + $action = remove_query_arg( $page_var, add_query_arg( [ $query_var => '' ] ) ); + $action = str_replace( '/page/' . get_query_var( 'paged', 1 ), '', $action ); $search_value = sanitize_search_query_var( $query_var ); diff --git a/tests/e2e/README.md b/tests/e2e/README.md index 0a8ad6f..985371b 100644 --- a/tests/e2e/README.md +++ b/tests/e2e/README.md @@ -44,6 +44,7 @@ Seeding is idempotent, guarded by the `query_filter_e2e_seeded` option. | `/taxonomy-filter/` | 1 | Taxonomy filter (select) | | `/taxonomy-checkboxes/` | 2 | Taxonomy filter (checkboxes) | | `/post-type-filter/` | 3 | Post type filter and core search block | +| `/search-pagination/` | 5 | Core search block and pagination, two posts to a page | Posts: `Alpha One` and `Alpha Two` in the `alpha` category, `Beta One` in `beta`, and `Unfiled Post` in neither — so an active filter is always diff --git a/tests/e2e/search-filter.spec.js b/tests/e2e/search-filter.spec.js index 503c63f..7b6becd 100644 --- a/tests/e2e/search-filter.spec.js +++ b/tests/e2e/search-filter.spec.js @@ -32,4 +32,21 @@ test.describe( 'Search filter', () => { 'Beta' ); } ); + + test( 'searching from a later page starts the results over', async ( { + page, + loop, + } ) => { + // Two posts to a page, so page 2 holds the posts a search for "Alpha" + // does not match. Carrying the page parameter into the search renders + // an empty loop over results that do exist. + await page.goto( '/search-pagination/?query-5-page=2' ); + await loop.expectTitles( [ 'Beta One', 'Unfiled Post' ] ); + + await page.locator( '.wp-block-search__input' ).fill( 'Alpha' ); + + await page.waitForURL( /query-5-s=Alpha/ ); + expect( page.url() ).not.toContain( 'query-5-page' ); + await loop.expectTitles( POSTS.alpha ); + } ); } ); diff --git a/tests/seed.php b/tests/seed.php index db7f31f..8252022 100644 --- a/tests/seed.php +++ b/tests/seed.php @@ -146,6 +146,27 @@ function query_loop_markup( int $query_id, string $filters ) : string { ), ] ); +// Page 5: a paginated loop with a search block, two posts to a page, so a +// search can be started from a page the results do not reach. +seed_post( 'Search Pagination', 'page', [ + 'post_name' => 'search-pagination', + 'post_content' => << +
+ + + + + + + + + +
+ +HTML, +] ); + update_option( 'query_filter_e2e_seeded', 1 ); echo "Seeded query filter e2e fixtures.\n";