UX: Revamp quick search (PR #14499)

This PR changes the behaviour of the quick search dropdown considerably.

Default state Before / After

The “options” link to full page search is now an icon, visible at all times (previously, this was not shown once context search was enabled). A random hint is now included. (In a subsequent PR, the random hint will be extendable via the plugin API.)

Inline results Before / After

This is one of the biggest changes. The search dropdown no longer includes topic/post search results as you type. Instead, only category, tag (if enabled) and user results are displayed as the user types. This change is a performance improvement, full search on every keystroke is costly and we see a lot of noise in search request logs (for example, requests for incomplete words as the user types).

In the UI, this also allows us to always show results in one list, which is cleaner than the columns we previously used.

To trigger a topic/post search, users have to hit Enter or click the first result item (hitting Arrow Down and Enter is also equivalent). This is what the list of topic results looks like once invoked:

Note also that a new button to clear the search terms has been added next to the advanced search icon.

Contextual search Before / After

This PR also removes the contextual search checkbox and instead offers a “{keyword} in {context}” row that users can invoke to search in the current context (topic, category, tag, PMs/group).

API addition In the backend, this PR also adds a new query parameter to the search/query endpoint that lets us search for everything excluding topics with one request to the backend.

Example: /search/query?term=keyword&type_filter=exclude_topics


      expect(result.tags[0].id).to eq(tag.id)

Quick note to self: I need to review and make sure this doesn’t cause any issues with a few themes/components that extend or reuse the search panel.

Is this method called through some dynamic method lookup? Quick grep here makes it seem like the method isn’t being used.

  context 'exclude_topics filter' do

I think this is much clearer for what we’re testing for.

      expect(result.users.map(&:id)).to contain_exactly(user.id)

      expect(result.categories.map(&:id)).to eq(category.id)

      expect(result.groups.map(&:id)).to contain_exactly(group.id)

      expect(result.tags.map(&:id)).to eq(tag.id)

Minor but we can assert for both count and accuracy in a single assertion.

I’m wondering if we should move this to a constant like DEFAULT_TYPE_FILTER so that we don’t duplicate this string across multiple places making it hard to change in the future.

When excluding topics, are the posts and topics attributes included in the payload?

A few minor comments but I tested the feature locally and everything seems to be working really well :clap: