Skip to content

Search: auto-switch Results List to Product view for product-scoped searches#48943

Merged
kangzj merged 2 commits into
trunkfrom
raven/search-201-auto-product-view
May 19, 2026
Merged

Search: auto-switch Results List to Product view for product-scoped searches#48943
kangzj merged 2 commits into
trunkfrom
raven/search-201-auto-product-view

Conversation

@kangzj

@kangzj kangzj commented May 18, 2026

Copy link
Copy Markdown
Contributor

Fixes SEARCH-201

Why

When a shopper follows a product-scoped search link (?post_types[]=product), the Results List previously rendered in the saved editorial format — title, breadcrumb path, excerpt, author/date — which looks wrong for a store. It now automatically switches to the Product grid (image cards with price and rating) for product-only searches, so a shop search looks like a shop without the author having to hand-pick the layout. Authors who don't want this can turn it off per block.

Proposed changes

  • New autoProductView attribute on jetpack-search/results-list (boolean, default on).
  • render.php auto-switches the resolved layout to product when the request is scoped to exactly the product post type via the URL — gated by autoProductView and the existing Search_Blocks::woocommerce_blocks_enabled() check, with the non-Woo collapse still the final gate (never forces WC-shaped markup on a non-Woo site).
  • New Search_Blocks::request_is_product_only() helper — true only when the combined post-type request resolves to exactly product (mixed requests keep the saved layout so non-product results never render as product cards).
  • WooCommerce-only inspector toggle ("Auto-switch to Product view for product searches") to opt out per block.
  • PHP + JS test matrices; changelog entry.

Scope note: the trigger is the Jetpack Search array shape ?post_types[]=product — the shape store/url-state.js writes and round-trips. The scalar ?post_type=product is a WordPress core query var that reroutes the request to the WooCommerce shop archive before any Jetpack Search block renders, so it never reaches this block and is intentionally a non-goal (handling it would require overriding standard shop-archive routing site-wide).

Screenshots

Same ?s=hat&post_types[]=product URL, captured at 1440×900 via local docker.

Before (trunk) After (this PR)
before after

Related product discussion/links

Does this pull request change what data or activity we track or use?

No.

Testing instructions

Requires a WooCommerce site with the Search 3.0 blocks experience and the WC search blocks enabled (add_filter( 'jetpack_search_woocommerce_blocks_enabled', '__return_true', 20 )), with a Search Results template containing a results-list block whose saved format is not Product. Acceptance criteria:

  • ?s=<term>&post_types[]=product renders the Results List as a product grid (image cards, price, rating) with autoProductView on (default).
  • Without the param, the same search keeps the saved (e.g. Expanded) format.
  • ?post_types[]=product&post_types[]=post (mixed) keeps the saved format.
  • Turning off the editor toggle "Auto-switch to Product view" pins the saved format even with ?post_types[]=product.
  • On a non-Woo site, ?post_types[]=product never produces the product layout (collapses to Expanded).
  • The inspector toggle appears only when WooCommerce is enabled and defaults to on.
  • jp test php packages/search and jp test js packages/search are green.

@kangzj kangzj added the [Status] Needs Review This PR is ready for review. label May 18, 2026
@kangzj kangzj self-assigned this May 18, 2026
@github-actions

github-actions Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.

  • To test on WoA, go to the Plugins menu on a WoA dev site. Click on the "Upload" button and follow the upgrade flow to be able to upload, install, and activate the Jetpack Beta plugin. Once the plugin is active, go to Jetpack > Jetpack Beta, select your plugin (Jetpack), and enable the raven/search-201-auto-product-view branch.
  • To test on Simple, run the following command on your sandbox:
bin/jetpack-downloader test jetpack raven/search-201-auto-product-view

Interested in more tips and information?

  • In your local development environment, use the jetpack rsync command to sync your changes to a WoA dev blog.
  • Read more about our development workflow here: PCYsg-eg0-p2
  • Figure out when your changes will be shipped to customers here: PCYsg-eg5-p2

@kangzj

This comment has been minimized.

@github-actions github-actions Bot added [Package] Search Contains core Search functionality for Jetpack and Search plugins [Tests] Includes Tests labels May 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thank you for your PR!

When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:

  • ✅ Include a description of your PR changes.
  • ✅ Add a "[Status]" label (In Progress, Needs Review, ...).
  • ✅ Add testing instructions.
  • ✅ Specify whether this PR includes any changes to data or privacy.
  • ✅ Add changelog entries to affected projects

This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖


Follow this PR Review Process:

  1. Ensure all required checks appearing at the bottom of this PR are passing.
  2. Make sure to test your changes on all platforms that it applies to. You're responsible for the quality of the code you ship.
  3. You can use GitHub's Reviewers functionality to request a review.
  4. When it's reviewed and merged, you will be pinged in Slack to deploy the changes to WordPress.com simple once the build is done.

If you have questions about anything, reach out in #jetpack-developers for guidance!

@claude

claude Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kangzj's task in 2m 46s —— View job


PR Review

  • Gather context and understand the request
  • Read changed files
  • Analyze code for bugs, security, and quality issues
  • Post comprehensive review

Overall this is a well-crafted feature. The WC-gating pattern is followed correctly, the test matrix is thorough, and the layered safeguards (auto-switch → non-Woo collapse) make the render logic robust. A few items worth addressing:


Issues

1. Help text uses the wrong URL shape (user-visible bug)

edit.js:153

help={ __(
    'When the search is scoped to products (a ?post_type=product link), show results as a product grid …',
    'jetpack-search-pkg'
) }

The help text tells authors the trigger URL is ?post_type=product, but the PR description (and request_is_product_only()'s own docblock) are explicit that ?post_type=product is a WordPress core query var that reroutes the request to the WC shop archive before any Jetpack Search block renders — it never fires the auto-switch in a normal Woo setup. The real trigger is ?post_types[]=product (Jetpack Search's array shape). An author reading this help text and crafting a "product search" link with ?post_type=product will get the WC shop archive instead, not the expected grid.

Suggested fix: change the example to ?post_types[]=product:

'When the search is scoped to products (a ?post_types[]=product link), show results as a product grid regardless of the format above. Turn off to always use the format above.'

Fix this →


2. render.php inline comment repeats the same misleading URL

render.php:79–84

// Auto-switch to the product layout when the request is scoped to exactly
// the `product` post type via the URL (`?post_type=product` or
// `?post_types[]=product`),

The request_is_product_only() docblock already has the nuanced explanation ("scalar form is accepted for completeness, but normally reroutes before reaching this block"). The shorter inline comment at the render call site just says ?post_type=product without that context and may confuse a reader into thinking both shapes are equally expected. This is a documentation-only issue but consistent with point 1.


3. request_is_product_only() is not memoized

Unlike is_initial_loading(), woocommerce_blocks_enabled(), etc., the new helper re-parses $_GET on every call. Today there's one results-list block per page so it's called once — no problem. But the inconsistency is worth noting as a pattern divergence, especially since the existing helpers carry explicit memo comments explaining why they're cached. A one-line static cache would keep the class consistent:

public static function request_is_product_only(): bool {
    static $cache = null;
    if ( null !== $cache ) {
        return $cache;
    }
    // … existing logic …
    $cache = array( 'product' ) === array_keys( $requested );
    return $cache;
}

(And a matching reset_request_is_product_only_cache() for tests, or $_GET-based isolation like the existing render_with_request() helper in tests — which already resets is_initial_loading_cache but would also need to reset this one if it were memoized.)


Minor observations (non-blocking)

edit.js:268renderProductPreview uses key={ product.title } on list items. Hardcoded sample titles happen to be unique so no React warning fires, but an index key is the conventional choice for static sample arrays:

products.map( ( product, i ) => (
    <li key={ i } >

Default-on is a behavioral change for existing blocks — blocks saved without autoProductView in the markup will get default: true from block.json on the next render, meaning Woo sites start auto-switching immediately after the update without any author action. This is the explicit intent of the feature, but it's worth surfacing in the release notes / changelog beyond the current one-liner, so site owners aren't surprised.


What looks good

  • WC gating is correct: The toggle is hidden from non-Woo editors (isWooCommerceBlocksEnabled() gate in edit.js:143), the auto-switch is gated in PHP (woocommerce_blocks_enabled()), and the non-Woo collapse at render.php:95 is the final safety net — three independent layers, all consistent with the AGENTS.md WC-gating contract.
  • request_is_product_only() sanitization: sanitize_key() per-value, is_scalar() guard, and wp_unslash() on the raw $_GET — the pattern matches the rest of the class.
  • Test matrix is thorough: Both the PHP and JS tests hit every acceptance criterion from the PR description (scalar shape, array shape, mixed types, auto-switch override, autoProductView=false, non-Woo collapse).
  • $resolve_layout fallback: The unknown-layout → expanded fallback ensures the auto-switch can never produce an unrecognized modifier class even if something upstream sets an unexpected layout string.

@jp-launch-control

jp-launch-control Bot commented May 18, 2026

Copy link
Copy Markdown

Code Coverage Summary

Coverage changed in 3 files.

File Coverage Δ% Δ Uncovered
projects/packages/search/src/search-blocks/class-search-blocks.php 377/516 (73.06%) 0.41% 2 ❤️‍🩹
projects/packages/search/src/search-blocks/blocks/results-list/edit.js 29/29 (100.00%) 0.00% 0 💚
projects/packages/search/src/search-blocks/blocks/results-list/render.php 273/273 (100.00%) 0.00% 0 💚

Full summary · PHP report · JS report

@kangzj kangzj force-pushed the raven/search-201-auto-product-view branch from 8553b34 to adc909b Compare May 18, 2026 23:31
@kangzj

This comment has been minimized.

@kangzj

This comment has been minimized.

@kangzj

This comment has been minimized.

@claude

claude Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kangzj's task in 2m 13s —— View job


PR Re-Review

  • Gather context and understand the request
  • Read changed files (current branch state after fixes)
  • Verify previous issues were properly addressed
  • Check for any new issues
  • Post comprehensive re-review

All three issues from the previous review have been correctly resolved. Here's the verification:

Issues resolved ✅

Issue 1 — Help text URL shape (edit.js:152–155)

The toggle's help prop now reads:

'When the search is scoped to products (a ?post_types[]=product link), show results as a product grid regardless of the format above. Turn off to always use the format above.'

Correct URL shape, no ambiguity. ✓

Issue 2 — render.php inline comment (render.php:78–87)

The comment now names ?post_types[]=product as the canonical trigger and explicitly notes that the scalar ?post_type=product is a WP core query var that reroutes before this block renders — pointing readers to request_is_product_only()'s docblock for the full nuance. ✓

Issue 3 — Memoization of request_is_product_only() (class-search-blocks.php:1222–1247)

The no-memo decision is now fully documented in the docblock:

  • Single call site; a page carries one results-list block → at most one call per request
  • Mirrors parse_url_filters(), which is likewise uncached
  • Skipping the static-cache + test-reset plumbing preserves the no-shared-state contract the tests rely on

Reasonable tradeoff, well documented. ✓

The default-on behavioral change is now surfaced in the changelog entry, giving site owners advance notice that existing results-list blocks on Woo sites will auto-switch immediately after the update. ✓


Fresh pass — nothing new to flag

request_is_product_only() logic: The deduplication via $requested[$slug] = true is correct — duplicate slugs across the two params (e.g. ?post_type=product&post_types[]=product) collapse to a single key, and array('product') === array_keys($requested) evaluates correctly. The edge case where post_types is a scalar string (?post_types=product) is handled and tested.

Three-layer WC gating in render.php is intact:

  1. $auto_product_view && Search_Blocks::woocommerce_blocks_enabled() && Search_Blocks::request_is_product_only() → auto-switch
  2. 'product' === $layout && ! Search_Blocks::woocommerce_blocks_enabled() → collapse to expanded (final gate)

The second gate can never be short-circuited by the auto-switch because the auto-switch itself requires woocommerce_blocks_enabled() to be true. ✓

Test isolation: render_with_request() clears $_GET and resets is_initial_loading_cache around each case. Since request_is_product_only() is deliberately unmemoized, there's no cache to reset there — the test harness stays clean with no hidden shared state. ✓

JS tests cover all acceptance criteria: toggle visibility gated on WC, default-on, false pinning, attribute updates, and the non-Woo describe block. ✓


What looks good overall

The layered safeguards, sanitization, WC gating, test matrix, changelog surfacing, and documentation all meet the bar. This PR looks ready to merge.

This comment has been minimized.

Copilot finished work on behalf of kangzj May 18, 2026 23:34
kangzj added 2 commits May 19, 2026 11:45
…earches

When a search is scoped to exactly the product post type via the
?post_types[]=product URL parameter, the Results List block now renders
in the Product layout automatically instead of the saved format. A new
per-block autoProductView attribute (default on, WooCommerce-only
inspector toggle) lets authors opt out. Gated behind the existing
woocommerce_blocks_enabled() check so it never forces WC-shaped markup
on a non-Woo site.
…ment no-memo choice, expand changelog (comment #4483062776)
@kangzj kangzj force-pushed the raven/search-201-auto-product-view branch from adc909b to 95f6fdf Compare May 18, 2026 23:45
@kangzj kangzj merged commit 7504da2 into trunk May 19, 2026
72 of 74 checks passed
@kangzj kangzj deleted the raven/search-201-auto-product-view branch May 19, 2026 00:03
@kangzj

kangzj commented May 19, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Review-cycle summary — 507004e90f95f6fdf335

4 rounds; all required CI green (38 pass / 8 skip); 2 AI reviewers addressed and approved; branch rebased on fresh trunk.

What changed during the cycle

Commits added during the cycle:

  • 95f6fdf335 — Address review: correct trigger-URL shape in help text/comments, document no-memo choice, expand changelog (comment #4483062776)

Diff vs. cycle base: 4 files changed, 17 insertions(+), 8 deletions(-)

Review feedback addressed:

Source Item Resolution
claude[bot] (#4483062776) Help text used ?post_type=product (reroutes to WC shop archive) Corrected to ?post_types[]=productadc909b708
claude[bot] (#4483062776) render.php inline comment repeated the misleading scalar shape Rewrote to name the array trigger + point to the helper docblock — adc909b708
claude[bot] (#4483062776) request_is_product_only() memoization inconsistency Documented the deliberate no-memo choice (single call site; mirrors uncached parse_url_filters()) — adc909b708
claude[bot] (#4483062776) default-on behavioral change not surfaced Expanded the changelog entry — adc909b708
claude[bot] (#4483134126) Re-review All issues verified resolved — "ready to merge"
Copilot (#4483140667) Review "follow-up fixes look good — no additional issues found in scope"

The pre-existing key={ product.title } nit in renderProductPreview was left untouched (outside this feature's scope, keeping the diff minimal).

Unaddressed (flagged for owner): None.

CI: all required checks passing on 95f6fdf335. The non-required external "Test plugin upgrades" check at developer.jetpack.com is independent of this change.

@github-actions github-actions Bot added [Status] UI Changes Add this to PRs that change the UI so documentation can be updated. and removed [Status] Needs Review This PR is ready for review. labels May 19, 2026
@kangzj kangzj added the [Status] Needs Team Review Obsolete. Use Needs Review instead. label May 19, 2026
robertsreberski added a commit that referenced this pull request May 19, 2026
Drop redundant package-name prefixes inside each package's own
changelog (per the skill style guide § Prefixes and scope), tighten
the search package's Added entries the reviewer flagged as
non-imperative or too technical, split compound bullets, and
regenerate the Jetpack readme:

- charts, components, activity-log, forms, search (Fixed),
  jetpack plugin (Improved compatibility): drop the prefix that
  repeats the package name.
- search Added: rephrase the REST entry to start with `Add` and
  drop the `Module_Control` aside; de-internalize the rebuild-mode
  entry (no `Custom_Taxonomy_Slot_Mapping::backfill()`); split the
  Results List auto-switch entry into the behavior bullet and the
  opt-out toggle bullet (#48943); capitalize Whitelist; drop
  the `Search:` prefix on the WP-CLI backfill entry; split the
  `Search: AI Answers` entry into two `AI Answers:` bullets — one
  for the streaming panel, one for the customization dashboard
  (#48592).
- Regenerated `projects/plugins/jetpack/readme.txt` via
  `tools/plugin-changelog-to-readme.sh jetpack`.
Copilot AI pushed a commit that referenced this pull request May 19, 2026
…earches (#48943)

* Search: auto-switch Results List to Product view for product-scoped searches

When a search is scoped to exactly the product post type via the
?post_types[]=product URL parameter, the Results List block now renders
in the Product layout automatically instead of the saved format. A new
per-block autoProductView attribute (default on, WooCommerce-only
inspector toggle) lets authors opt out. Gated behind the existing
woocommerce_blocks_enabled() check so it never forces WC-shaped markup
on a non-Woo site.

* Address review: correct trigger-URL shape in help text/comments, document no-memo choice, expand changelog (comment #4483062776)
yuliyan pushed a commit that referenced this pull request May 20, 2026
…earches (#48943)

* Search: auto-switch Results List to Product view for product-scoped searches

When a search is scoped to exactly the product post type via the
?post_types[]=product URL parameter, the Results List block now renders
in the Product layout automatically instead of the saved format. A new
per-block autoProductView attribute (default on, WooCommerce-only
inspector toggle) lets authors opt out. Gated behind the existing
woocommerce_blocks_enabled() check so it never forces WC-shaped markup
on a non-Woo site.

* Address review: correct trigger-URL shape in help text/comments, document no-memo choice, expand changelog (comment #4483062776)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Package] Search Contains core Search functionality for Jetpack and Search plugins [Status] Needs Team Review Obsolete. Use Needs Review instead. [Status] UI Changes Add this to PRs that change the UI so documentation can be updated. [Tests] Includes Tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants