UX: launch full page search on second `Enter` hit (#14978)

UX: launch full page search on second Enter hit (#14978)

diff --git a/app/assets/javascripts/discourse/app/controllers/keyboard-shortcuts-help.js b/app/assets/javascripts/discourse/app/controllers/keyboard-shortcuts-help.js
index 65eacc5..0386451 100644
--- a/app/assets/javascripts/discourse/app/controllers/keyboard-shortcuts-help.js
+++ b/app/assets/javascripts/discourse/app/controllers/keyboard-shortcuts-help.js
@@ -232,6 +232,10 @@ export default Controller.extend(ModalFunctionality, {
         insert_url: buildShortcut("search_menu.insert_url", {
           keys1: ["a"],
         }),
+        full_page_search: buildShortcut("search_menu.full_page_search", {
+          keys1: [translateModKey("Meta"), "Enter"],
+          keysDelimiter: PLUS,
+        }),
       },
     });
   },
diff --git a/app/assets/javascripts/discourse/app/templates/modal/keyboard-shortcuts-help.hbs b/app/assets/javascripts/discourse/app/templates/modal/keyboard-shortcuts-help.hbs
index 60edf99..9488750 100644
--- a/app/assets/javascripts/discourse/app/templates/modal/keyboard-shortcuts-help.hbs
+++ b/app/assets/javascripts/discourse/app/templates/modal/keyboard-shortcuts-help.hbs
@@ -100,6 +100,7 @@
       <ul>
         <li>{{html-safe shortcuts.search_menu.prev_next}}</li>
         <li>{{html-safe shortcuts.search_menu.insert_url}}</li>
+        <li>{{html-safe shortcuts.search_menu.full_page_search}}</li>
       </ul>
     </section>
   </div>
diff --git a/app/assets/javascripts/discourse/app/widgets/search-menu.js b/app/assets/javascripts/discourse/app/widgets/search-menu.js
index ad03c11..e5366f5 100644
--- a/app/assets/javascripts/discourse/app/widgets/search-menu.js
+++ b/app/assets/javascripts/discourse/app/widgets/search-menu.js
@@ -16,6 +16,7 @@ import { cancel } from "@ember/runloop";
 const CATEGORY_SLUG_REGEXP = /(\#[a-zA-Z0-9\-:]*)$/gi;
 const USERNAME_REGEXP = /(\@[a-zA-Z0-9\-\_]*)$/gi;
 const SUGGESTIONS_REGEXP = /(in:|status:|order:|:)([a-zA-Z]*)$/gi;
+const SECOND_ENTER_MAX_DELAY = 15000;
 export const MODIFIER_REGEXP = /.*(\#|\@|:).*$/gi;
 export const DEFAULT_TYPE_FILTER = "exclude_topics";
 
@@ -111,14 +112,14 @@ const SearchHelper = {
 
     if (!term) {
       searchData.noResults = false;
-      searchData.results = [];
+      searchData.results = {};
       searchData.loading = false;
       searchData.invalidTerm = false;
 
       widget.scheduleRerender();
     } else if (!isValidSearchTerm(term, widget.siteSettings)) {
       searchData.noResults = true;
-      searchData.results = [];
+      searchData.results = {};
       searchData.loading = false;
       searchData.invalidTerm = true;
 
@@ -191,6 +192,7 @@ export default createWidget("search-menu", {
   defaultState(attrs) {
     return {
       inTopicContext: attrs.inTopicContext,
+      _lastEnterTimestamp: null,
       _debouncer: null,
     };
   },
@@ -418,13 +420,23 @@ export default createWidget("search-menu", {
 
     const searchInput = document.querySelector("#search-term");
     if (e.which === 13 && e.target === searchInput) {
+      const recentEnterHit =
+        this.state._lastEnterTimestamp &&
+        Date.now() - this.state._lastEnterTimestamp < SECOND_ENTER_MAX_DELAY;
+
       // same combination as key-enter-escape mixin
-      if (e.ctrlKey || e.metaKey || (isiPad() && e.altKey)) {
+      if (
+        e.ctrlKey ||
+        e.metaKey ||
+        (isiPad() && e.altKey) ||
+        (searchData.typeFilter !== DEFAULT_TYPE_FILTER && recentEnterHit)
+      ) {
         this.fullSearch();
       } else {
         searchData.typeFilter = null;
         this.triggerSearch();
       }
+      this.state._lastEnterTimestamp = Date.now();
     }
 
     if (e.target === searchInput && e.which === 8 /* backspace */) {
@@ -477,7 +489,6 @@ export default createWidget("search-menu", {
   },
 
   fullSearch() {
-    searchData.results = [];
     searchData.loading = false;
     SearchHelper.cancel();
     const url = this.fullSearchUrl();
diff --git a/app/assets/javascripts/discourse/tests/acceptance/search-test.js b/app/assets/javascripts/discourse/tests/acceptance/search-test.js
index d3f39ae..6be3204 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/search-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/search-test.js
@@ -480,6 +480,31 @@ acceptance("Search - Authenticated", function (needs) {
       `${window.location.origin}${firstLink}`,
       "hitting A when focused on a search result copies link to composer"
     );
+
+    await click("#search-button");
+    await triggerKeyEvent("#search-term", "keydown", keyEnter);
+
+    assert.ok(
+      exists(query(`${container} .search-result-topic`)),
+      "has topic results"
+    );
+
+    await triggerKeyEvent("#search-term", "keydown", keyEnter);
+
+    assert.ok(
+      exists(query(`.search-container`)),
+      "second Enter hit goes to full page search"
+    );
+    assert.ok(
+      !exists(query(`.search-menu`)),
+      "search dropdown is collapsed after second Enter hit"
+    );
+
+    // new search launched, Enter key should be reset
+    await click("#search-button");
+    assert.ok(exists(query(`${container} ul li`)), "has a list of items");
+    await triggerKeyEvent("#search-term", "keydown", keyEnter);
+    assert.ok(exists(query(`.search-menu`)), "search dropdown is visible");
   });
 });
 
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 2a35962..bb90661 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -3720,6 +3720,7 @@ en:
         title: "Search Menu"
         prev_next: "%{shortcut} Move selection up and down"
         insert_url: "%{shortcut} Insert selection into open composer"
+        full_page_search: "%{shortcut} Launches full page search"
 
     badges:
       earned_n_times:

GitHub sha: 2ff7f105d95ba085657527551aeaaedf0142ef1a

This commit appears in #14978 which was approved by eviltrout. It was merged by SamSaffron.