FEATURE: Add assistant to quick search widget (#13650)

FEATURE: Add assistant to quick search widget (#13650)

Replaces the autocomplete overlay for categories and usernames on the search input and adds suggestions as items in the search results instead. Also adds the same behaviour for @mentions as well as special in: status: order: keywords. See PR for more details.

diff --git a/app/assets/javascripts/discourse/app/components/site-header.js b/app/assets/javascripts/discourse/app/components/site-header.js
index d989580..9ac49c9 100644
--- a/app/assets/javascripts/discourse/app/components/site-header.js
+++ b/app/assets/javascripts/discourse/app/components/site-header.js
@@ -218,7 +218,6 @@ const SiteHeaderComponent = MountWidget.extend(
 
       this.dispatch("notifications:changed", "user-notifications");
       this.dispatch("header:keyboard-trigger", "header");
-      this.dispatch("search-autocomplete:after-complete", "search-term");
       this.dispatch("user-menu:navigation", "user-menu");
 
       this.appEvents.on("dom:clean", this, "_cleanDom");
diff --git a/app/assets/javascripts/discourse/app/lib/plugin-api.js b/app/assets/javascripts/discourse/app/lib/plugin-api.js
index 994d45d..cbf157d 100644
--- a/app/assets/javascripts/discourse/app/lib/plugin-api.js
+++ b/app/assets/javascripts/discourse/app/lib/plugin-api.js
@@ -73,9 +73,10 @@ import { replaceFormatter } from "discourse/lib/utilities";
 import { replaceTagRenderer } from "discourse/lib/render-tag";
 import { setNewCategoryDefaultColors } from "discourse/routes/new-category";
 import { addSearchResultsCallback } from "discourse/lib/search";
+import { addInSearchShortcut } from "discourse/widgets/search-menu-results";
 
 // If you add any methods to the API ensure you bump up this number
-const PLUGIN_API_VERSION = "0.11.5";
+const PLUGIN_API_VERSION = "0.11.6";
 
 class PluginApi {
   constructor(version, container) {
@@ -1295,6 +1296,18 @@ class PluginApi {
   addSearchResultsCallback(callback) {
     addSearchResultsCallback(callback);
   }
+
+  /**
+   * Add a in: shortcut to search menu panel.
+   *
+   * `‍``
+   * addInSearchShortcut("in:assigned");
+   * `‍``
+   *
+   */
+  addInSearchShortcut(value) {
+    addInSearchShortcut(value);
+  }
 }
 
 // from http://stackoverflow.com/questions/6832596/how-to-compare-software-version-number-using-js-only-number
diff --git a/app/assets/javascripts/discourse/app/lib/search.js b/app/assets/javascripts/discourse/app/lib/search.js
index b2de359..a53769b 100644
--- a/app/assets/javascripts/discourse/app/lib/search.js
+++ b/app/assets/javascripts/discourse/app/lib/search.js
@@ -206,53 +206,30 @@ export function isValidSearchTerm(searchTerm, siteSettings) {
   }
 }
 
-export function applySearchAutocomplete(
-  $input,
-  siteSettings,
-  appEvents,
-  options
-) {
-  const afterComplete = function () {
-    if (appEvents) {
-      appEvents.trigger("search-autocomplete:after-complete");
-    }
-  };
-
+export function applySearchAutocomplete($input, siteSettings) {
   $input.autocomplete(
-    deepMerge(
-      {
-        template: findRawTemplate("category-tag-autocomplete"),
-        key: "#",
-        width: "100%",
-        treatAsTextarea: true,
-        autoSelectFirstSuggestion: false,
-        transformComplete(obj) {
-          return obj.text;
-        },
-        dataSource(term) {
-          return searchCategoryTag(term, siteSettings);
-        },
-        afterComplete,
-      },
-      options
-    )
+    deepMerge({
+      template: findRawTemplate("category-tag-autocomplete"),
+      key: "#",
+      width: "100%",
+      treatAsTextarea: true,
+      autoSelectFirstSuggestion: false,
+      transformComplete: (obj) => obj.text,
+      dataSource: (term) => searchCategoryTag(term, siteSettings),
+    })
   );
 
   if (siteSettings.enable_mentions) {
     $input.autocomplete(
-      deepMerge(
-        {
-          template: findRawTemplate("user-selector-autocomplete"),
-          key: "@",
-          width: "100%",
-          treatAsTextarea: true,
-          autoSelectFirstSuggestion: false,
-          transformComplete: (v) => v.username || v.name,
-          dataSource: (term) => userSearch({ term, includeGroups: true }),
-          afterComplete,
-        },
-        options
-      )
+      deepMerge({
+        template: findRawTemplate("user-selector-autocomplete"),
+        key: "@",
+        width: "100%",
+        treatAsTextarea: true,
+        autoSelectFirstSuggestion: false,
+        transformComplete: (v) => v.username || v.name,
+        dataSource: (term) => userSearch({ term, includeGroups: true }),
+      })
     );
   }
 }
diff --git a/app/assets/javascripts/discourse/app/widgets/header.js b/app/assets/javascripts/discourse/app/widgets/header.js
index 784d526..0c292de 100644
--- a/app/assets/javascripts/discourse/app/widgets/header.js
+++ b/app/assets/javascripts/discourse/app/widgets/header.js
@@ -2,7 +2,6 @@ import DiscourseURL, { userPath } from "discourse/lib/url";
 import I18n from "I18n";
 import { addExtraUserClasses } from "discourse/helpers/user-avatar";
 import { ajax } from "discourse/lib/ajax";
-import { applySearchAutocomplete } from "discourse/lib/search";
 import { avatarImg } from "discourse/widgets/post";
 import { createWidget } from "discourse/widgets/widget";
 import { get } from "@ember/object";
@@ -484,17 +483,9 @@ export default createWidget("header", {
 
     if (this.state.searchVisible) {
       schedule("afterRender", () => {
-        const $searchInput = $("#search-term");
-        $searchInput.focus().select();
-
-        applySearchAutocomplete(
-          $searchInput,
-          this.siteSettings,
-          this.appEvents,
-          {
-            appendSelector: ".menu-panel",
-          }
-        );
+        const searchInput = document.querySelector("#search-term");
+        searchInput.focus();
+        searchInput.select();
       });
     }
   },
diff --git a/app/assets/javascripts/discourse/app/widgets/search-menu-controls.js b/app/assets/javascripts/discourse/app/widgets/search-menu-controls.js
index dd2f485..dc329df 100644
--- a/app/assets/javascripts/discourse/app/widgets/search-menu-controls.js
+++ b/app/assets/javascripts/discourse/app/widgets/search-menu-controls.js
@@ -13,10 +13,6 @@ createWidget("search-term", {
     return { afterAutocomplete: false };
   },
 
-  searchAutocompleteAfterComplete() {
-    this.state.afterAutocomplete = true;
-  },
-
   buildAttributes(attrs) {
     return {
       type: "text",
@@ -28,12 +24,8 @@ createWidget("search-term", {
   },
 
   keyUp(e) {
-    if (e.which === 13) {
-      if (this.state.afterAutocomplete) {
-        this.state.afterAutocomplete = false;
-      } else {
-        return this.sendWidgetAction("fullSearch");
-      }
+    if (e.which === 13 && !this.state.afterAutocomplete) {
+      return this.sendWidgetAction("fullSearch");
     }
 
     const val = this.attrs.value;
diff --git a/app/assets/javascripts/discourse/app/widgets/search-menu-results.js b/app/assets/javascripts/discourse/app/widgets/search-menu-results.js
index 3a5885d..5ef9d05 100644
--- a/app/assets/javascripts/discourse/app/widgets/search-menu-results.js
+++ b/app/assets/javascripts/discourse/app/widgets/search-menu-results.js
@@ -10,6 +10,33 @@ import highlightSearch from "discourse/lib/highlight-search";
 import { iconNode } from "discourse-common/lib/icon-library";
 import renderTag from "discourse/lib/render-tag";
 
+const inSearchShortcuts = [
+  "in:title",
+  "in:personal",
+  "in:seen",
+  "in:likes",
+  "in:bookmarks",
+  "in:created",
+];
+const statusSearchShortcuts = [
+  "status:open",
+  "status:closed",
+  "status:public",
+  "status:noreplies",
+];
+const orderSearchShortcuts = [
+  "order:latest",
+  "order:views",
+  "order:likes",
+  "order:latest_topic",
+];
+
+export function addInSearchShortcut(value) {
+  if (inSearchShortcuts.indexOf(value) === -1) {
+    inSearchShortcuts.push(value);
+  }
+}
+
 class Highlighted extends RawHtml {
   constructor(html, term) {
     super({ html: `<span>${html}</span>` });
@@ -207,6 +234,14 @@ createWidget("search-menu-results", {
   tagName: "div.results",
 
   html(attrs) {
+    if (attrs.suggestionKeyword) {
+      return this.attach("search-menu-assistant", {
+        fullTerm: attrs.term,

[... diff too long, it was truncated ...]

GitHub sha: 438a7629561397830fa9622f517ecec46e003e82

This commit appears in #13650 which was approved by eviltrout. It was merged by pmusaraj.