Skip to content

Keep query-bound search on the current page - #74

Merged
goldenapples merged 3 commits into
mainfrom
fix/search-action-ownership
Oct 28, 2026
Merged

goldenapples merged 3 commits into
mainfrom
fix/search-action-ownership

Conversation

@goldenapples

@goldenapples goldenapples commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #73.

Changes

  1. JS: updateURL() in src/taxonomy/view.js builds the next URL from the core/router store's state.url, falling back to window.location.href, instead of form.action. A new search drops the query's page parameter (context.pageParam for a loop with its own query; paged and a trailing /page/N/ for an inherited loop), matching the server.
  2. PHP: render_block_search runs at priority 20, so it sets the action of query-bound search blocks after Polylang-style filters at 10.
  3. No-JS: the current request's scalar $_GET parameters, except this query's search and page parameters, are rendered as hidden inputs (sanitize_text_field() + esc_attr()), so a GET submission keeps applied filters.

get_query_param() (added in 0.6.0) names the search and page parameters on both sides, so they match what the pagination block writes.

Testing

New Playwright specs in tests/e2e/search-action.spec.js fail on main and pass with the fix:

  • an action rewritten to the home page at priority 10, registered later (Polylang-style, via a test mu-plugin), is replaced by the plugin's own;
  • the search navigates within the page even when form.action is changed in the DOM;
  • filter params are rendered as hidden inputs, without the search or page param;
  • a no-JS submission keeps query-3-post_type.

npx playwright test: 48 passed. PHPCS clean. wp-scripts lint-js reports pre-existing errors only (including view.js:86, unchanged from main).

Also checked against a real Polylang Pro install (Solace, pretty permalinks): the search action stays on /blog/ and /fr/blog/ instead of being rewritten to the site/language root, and on a page with two loops the hidden inputs carry the other loop's page parameter.

Compatibility notes

  • Priority 20 changes the order relative to third-party render_block_core/search filters at 10–19, but only for search blocks inside a Query Loop. Code that needs to change those blocks after this plugin can hook later than 20.
  • The JS no longer reads form.action, so changing the action on the client has no effect on a query-bound search. The server-rendered action is still used for no-JS submissions.
  • Hidden inputs carry every scalar query parameter, not only this loop's, matching what the action URL already kept (other loops' filters, campaign params, and so on). Array-shaped values are skipped, as pre_get_posts_transpose_query_vars() already ignores them.
  • With Polylang and plain permalinks, Polylang adds its own lang hidden input and lang may also be in $_GET, which may produce a duplicate lang field with the same value. Not tested; harmless for GET, but worth excluding if confirmed.
  • Not covered: the inherited-loop /page/N/ path in JS has no dedicated test; only Chromium was run.

🤖 Generated with Claude Code

Open WordPress Playground Preview

Build the search URL from the current location rather than the form
action, run the search render filter after plugins such as Polylang
that rewrite search actions, and carry current parameters as hidden
inputs for no-JS submissions.

Fixes #73.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Playwright — PHP 8.2 / WP latest

passed  50 passed

Details

stats  50 tests across 10 suites
duration  58.6 seconds
commit  d595417

@kadamwhite kadamwhite left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@goldenapples looks good! One optional change, and a docs wording preference. Nothing blocking.

Comment thread inc/namespace.php Outdated
$block_content->set_attribute( 'data-wp-on--input', 'actions.search' );

return (string) $block_content;
return add_filter_hidden_inputs( (string) $block_content, [ $query_var, $page_var ] );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For an inherited loop, I believe that $page_var will be literal 'page', but that $paged will also be set. We deleted 'paged' in JS here, but in a non-JS environment with pretty permalinks disabled, this would leave a hidden input with paged set to whatever the on-load value was, and that would be passed along with a successive request.

This is an edge case, for our needs; pretty permalinks are ubiquitous. But I think this would make it more correct:

Suggested change
return add_filter_hidden_inputs( (string) $block_content, [ $query_var, $page_var ] );
return add_filter_hidden_inputs( (string) $block_content, [ $query_var, $page_var, 'paged' ] );

Please feel free to pit your agent against mine, and this isn't blocking if you disagree 😁

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I refined it just a little in d595417, so that we only strip the "paged" variable if this is the main/inherited query on a page.

Otherwise, if there's two queries on a page each with pagination, it might be possible for a navigation action in the custom query to lose the current page variable of the inherited query.

Comment thread inc/namespace.php Resolved
Comment thread inc/namespace.php

// Only echoed back, escaped, as the current URL already is in the form action.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
foreach ( wp_unslash( $_GET ) as $key => $value ) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL that unslash() takes an array. Nice!

Co-authored-by: K Adam White <kadamwhite@users.noreply.github.com>
github-actions Bot added a commit that referenced this pull request Oct 28, 2026
github-actions Bot added a commit that referenced this pull request Oct 28, 2026
@goldenapples
goldenapples merged commit 12978e4 into main Oct 28, 2026
6 checks passed
@goldenapples
goldenapples deleted the fix/search-action-ownership branch October 28, 2026 22:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Search inside a Query Loop navigates to the wrong page when another plugin filters search form actions (e.g. Polylang)

2 participants