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

Query and Search blocks: support for Instant Search #63147

Conversation

r-chrzan
Copy link

@r-chrzan r-chrzan commented Jul 4, 2024

What?

Initial implementation for Instant Search using the Search and Query Loop blocks. Added as a new experiment on the Gutenberg Experiments page.

Related to #63053

output_62afd8.mp4

How does it work?

  1. Enable the Instant Search and Query Block Gutenberg experiment.
  2. Insert a Query block anywhere on your site.
  3. Make sure to disable Force Page Reload i.e. use "enhanced pagination" in that Query block.
  4. Insert a Search block anywhere inside of that Query block.

When the Search block is inside of the Query block using "enhanced pagination", it automatically gets "updated" to an Instant Search block.

Any search input in the Instant Search block gets passed as the ?instant-search-<queryId>=<search-term> query search param in the URL.

Multiple Query + Search blocks

It's possible to have multiple Instant Search blocks in a page/post/template. In such a case, ensuring their functionality is isolated is essential. For this to work, the queryId is part of the param syntax:

  • instant-search-<queryId>=<search-term>

Search button

The search block can contain a search button (in some configurations). However, when using the Instant Search functionality, the button is redundant because the list of posts updates as you type. For this reason, in the editor, when the Search block is placed inside the Query Loop block with enhanced pagination ON, the Block Controls related to the Search button are removed. The displayed name of the block (via label) is changed to Instant Search (Search).

On the frontend, the Search button is also always removed for each (Instant) Search block.

output_70b3a0.mp4

Pagination

The instant search functionality respects the pagination of the Query block, which uses the query-<queryId>-page syntax.

  1. Every time the search is updated, the pagination of its respective query is reset back to page 1.
  2. The pagination numbers and next/previous, are updated to reflect the number of results in the search.
  3. Clearing the search also resets the pagination back to page 1.

Limitations

Alternatives

Further work

This is an initial prototype and the following features is intentionally not yet implemented:

  • Support for the Default queries, including multiple Default queries on a single page/post/template. Those have been prototyped already in Search and Query blocks: Add support for Default queries via pre_get_posts filter #67289.
  • Explore an alternative mechanism to convert the Search block into a Instant Search. Currently, this is done by nesting the Search inside of Query Loop with enhanced pagination ON, but a different, more explicit mechanism might be appropriate.

@github-actions github-actions bot added the First-time Contributor Pull request opened by a first-time contributor to Gutenberg repository label Jul 4, 2024
Copy link

github-actions bot commented Jul 4, 2024

👋 Thanks for your first Pull Request and for helping build the future of Gutenberg and WordPress, @r-chrzan! In case you missed it, we'd love to have you join us in our Slack community.

If you want to learn more about WordPress development in general, check out the Core Handbook full of helpful information.

@r-chrzan r-chrzan changed the title adding the necessary directives Instant search implementation Jul 4, 2024
@r-chrzan r-chrzan marked this pull request as ready for review July 5, 2024 20:54
@r-chrzan r-chrzan requested a review from ajitbohra as a code owner July 5, 2024 20:54
Copy link

github-actions bot commented Jul 5, 2024

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Unlinked Accounts

The following contributors have not linked their GitHub and WordPress.org accounts: @[email protected], @ktmn.

Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Unlinked contributors: [email protected], ktmn.

Co-authored-by: luisherranz <[email protected]>
Co-authored-by: michalczaplinski <[email protected]>
Co-authored-by: sirreal <[email protected]>
Co-authored-by: cbravobernal <[email protected]>
Co-authored-by: r-chrzan <[email protected]>
Co-authored-by: gziolo <[email protected]>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@luisherranz luisherranz added [Block] Search Affects the Search Block - used to display a search field [Block] Query Loop Affects the Query Loop Block [Feature] Interactivity API API to add frontend interactivity to blocks. labels Jul 8, 2024
@luisherranz luisherranz linked an issue Jul 8, 2024 that may be closed by this pull request
@luisherranz luisherranz added the [Type] Feature New feature to highlight in changelogs. label Jul 8, 2024
@luisherranz luisherranz changed the title Instant search implementation Query and Search blocks: support for Instant Search Jul 8, 2024
@WordPress WordPress deleted a comment from github-actions bot Jul 8, 2024
@luisherranz
Copy link
Member

Great work!! 👏👏

Let's see what's next 🙂

  1. Modifying the query in inherited queries.

    I don't know if you've noticed, but the query_loop_block_query_vars filter only works when the "Inherit query from template" option is disabled.

    Screenshot 2024-07-08 at 16 33 20

    We should check if the query is inherited, and if so, apply the modification to the general query so it works in both cases.

  2. The search parameter in inherited queries.

    It's interesting that we can't use the search parameter ?s= because, in that case, the page that loads is the Search template.

    I'm going to think about it a bit, but it is true that it might not be possible to simply use ?s=. If we can't use it, then we need to think carefully about what the most suitable parameter is. Maybe ?is= for instant search?

  3. The search parameter in non-inherited queries.

    When the "Inherit query from template" option is disabled, the query pagination uses a custom search parameter instead of the regular ?paged=2//page/2 of the inherited queries: ?query-X-page=P where X is the query id ($block->context['queryId']).

    In that case, we should use a similar parameter: ?query-X-search=test.

  4. Pagination when searching.

    You commented in Slack:

    sometimes, when we are on e.g. page 3 and typing something, i see prev link button without results

    That's what happens when the new results don't have as many pages as the page you were on just before you started the search.

    I think we should try resetting the page number every time a new search is performed. In other words, if the user starts a search on page 3, we should remove the search param from the URL before navigating.

    • User is in /page/3 or ?query-X-page=3.
    • User types new characters.
    • We add the search parameter, but remove the pagination: /?is=test.

    This is probably going to present problems, as it might not be that easy to remove the pagination from the URL in the client for certain permalinks, but I think it's worth a try and we'll see what happens. If that's the case, one option to investigate would be to generate the URL of the first page on the server and send it to the client via the state.

  5. Search button closing itself during navigation.

    You commented in Slack:

    it doesn’t work very well when search is just a button. sometimes toggle closes while typing

    That's because the context has a property called isSearchInputVisible which is set to false on the server. So when a navigation occurs, it gets reset to false.

    The way to solve this is to transform that property into an initial property and have a client-only property so that the server property is only checked on the first page load and then only the client-only property is considered.

    I made a commit to implement this pattern by adding this type of getter:

    get isSearchInputVisible() {
      const ctx = getContext();
      if ( typeof ctx.isSearchInputVisible === 'undefined' ) {
        return ctx.isSearchInputInitiallyVisible;
      }
      return ctx.isSearchInputVisible;
    },

@luisherranz
Copy link
Member

I was trying to test the UI, but I'm getting a Maximum update depth exceeded error in the <SearchEdit> component in the Editor.

Copy link
Member

@luisherranz luisherranz left a comment

Choose a reason for hiding this comment

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

I haven't tested the UI yet (due to this problem), but here are a few suggestions/questions 🙂

packages/block-library/src/image/block.json Outdated Show resolved Hide resolved
packages/block-library/src/post-content/block.json Outdated Show resolved Hide resolved
Comment on lines +68 to +76
if ( $enhanced_pagination && $instant_search_enabled && ! empty( $search_query_direct ) ) {
$args = array_merge(
build_query_vars_from_query_block( $block, $page ),
array( 's' => $search_query_direct )
);
$custom_query = new WP_Query( $args );
} else {
$custom_query = new WP_Query( build_query_vars_from_query_block( $block, $page ) );
}
Copy link
Member

Choose a reason for hiding this comment

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

If the Pagination Next block needs this change, I understand that the Pagination Previous block will also need it, right?

Copy link
Contributor

Choose a reason for hiding this comment

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

That's what I had had thought initially as well but actually it won't 🙂

The Query Pagination, Next block has to create a new query to determine if it's on the last page of the results (if it is, then the block shouldn't show anything). We have to pass the search parameter to this new query.

In Query Pagination Previous, no such new query is created, so it just works out of the box!

Copy link
Contributor

@michalczaplinski michalczaplinski Nov 29, 2024

Choose a reason for hiding this comment

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

I just noticed an edge case:

If you navigate to a page with ?instant-search param AND a pagination parameter like http://gutenberg.local/?instant-search-18=qwponbfv&query-18-page=2, then the Pagination Previous block shows up although it should not.

I'll hold on with fixing this for now though until we make a decision on whether we should go ahead with this PR or the approach in #67181 or #67013.

Copy link
Contributor

Choose a reason for hiding this comment

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

This appears to be a general issue with the Query Loop block as discussed with @ntsekouras. I've opened an issue in #67748

$query_args['s'] = $search_query_direct;
}

$query = new WP_Query( $query_args );
}

if ( $query->post_count > 0 ) {
Copy link
Member

Choose a reason for hiding this comment

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

We can include the contents of this block but hide them when there are no posts. That way, the CSS and JS of the inner blocks will be correctly loaded and the blocks will work when the user navigates to a "no results" page in the client.

We can remove this workaround if we finally improve how client-side navigation works before WP 6.8.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, nice. That's a good idea 👍

Can we add this in a follow-up PR though? I feel that it's not essential for the first version of the experimental block.

Copy link
Member

Choose a reason for hiding this comment

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

Sure.

packages/block-library/src/search/view.js Outdated Show resolved Hide resolved
@luisherranz
Copy link
Member

  1. Pagination for Default queries uses the /page/2/ format when using pretty permalinks. Should it be removed from the URL? Here we have two options:

    • Remove it from the URL. Downside is possibly breaking links as we have to parse the URL with a regex and remove the /page/X/ part.
    • Keep it in the URL and use the ?paged URL param. This works fine in my tests and redirects the request to the correct page. E.g. /page/2/?paged=3 will redirect to /page/3/. The downside is that it requires a redirect so is slightly slower.

Regarding this, why don't we pass the root/canonical link from the server (using iAPI's state/config, for example) and use that link instead? I haven't looked at it, but I suppose it won't be difficult to obtain on the server.

@michalczaplinski
Copy link
Contributor

I was trying to test the UI, but I'm getting a Maximum update depth exceeded error in the component in the Editor.

Oh, apologies about this. It was the very last commit that broke it. I've reverted it now (in 3dcf20f).

@michalczaplinski
Copy link
Contributor

Regarding this, why don't we pass the root/canonical link from the server (using iAPI's state/config, for example) and use that link instead? I haven't looked at it, but I suppose it won't be difficult to obtain on the server.

For this we could use wp_get_canonical_url but I have doubts about whether this is a reliable method. We cannot know for sure that the URL returned wp_get_canonical_url is going to the same as the URL the client sees. The WP server can sit behind a proxy like Cloudflare that can modify the hostname, pathname or params.

@luisherranz
Copy link
Member

The WP server can sit behind a proxy like Cloudflare that can modify the hostname, pathname or params.

Are you sure? I don't think a WordPress server can function correctly without being configured with its final hostname. I haven't seen any WordPress installation that works well if the path or parameters are changed either.

It seems like wp_get_canonical_url returns the page, so we might have better luck simply using get_permalink.

Let's try it, and if people report issues when using proxys, we will look for a solution.

@michalczaplinski
Copy link
Contributor

Are you sure? I don't think a WordPress server can function correctly without being configured with its final hostname. I haven't seen any WordPress installation that works well if the path or parameters are changed either.

I'm not 100% sure 🙂 I see that it's possible to have a dynamic WP_SITEURL:

It might still work though. I'll try the approach using get_permalink()

@michalczaplinski
Copy link
Contributor

I believe that the current feedback's been addressed 🙂. Thank you all! I will close this PR in favour of #67181 as agreed in #63053 (comment).

As for the following issues:

  • Use get_permalink() to get the canonical link value and use it when updating the URL for Default queries (comment)
    • I'm working on this in #67289. The approach seems promising.
  • Include contents of the Query No Results block but hide them with CSS (comment)
    • Happy to add this in a follow-up PR 👍
  • There's a bug where the Previous button does not work correctly (comment)
    • I'll fix this in #67181 where work on Instant Search continues.

@michalczaplinski michalczaplinski deleted the feature/query-and-search-blocks-support-for-instant-search branch December 5, 2024 11:38
@luisherranz
Copy link
Member

Thank you, Michał, and thanks also to @r-chrzan for starting this work 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Block] Query Loop Affects the Query Loop Block [Block] Search Affects the Search Block - used to display a search field [Feature] Interactivity API API to add frontend interactivity to blocks. First-time Contributor Pull request opened by a first-time contributor to Gutenberg repository [Type] Feature New feature to highlight in changelogs.
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

Query and Search blocks: support for Instant Search
6 participants