From 30c3000e87ab25986bcce45d7bf7af0dd6370dcc Mon Sep 17 00:00:00 2001
From: nathan <nathangray.bsc+github@gmail.com>
Date: Fri, 16 Sep 2022 10:20:47 -0600
Subject: [PATCH] Et2Select fixes - Search with no results blocked any futher
 searches from displaying results - Only show "no suggestions" after searching

---
 api/js/etemplate/Et2Link/Et2LinkSearch.ts |  4 +--
 api/js/etemplate/Et2Select/SearchMixin.ts | 43 +++++++++++++++++------
 2 files changed, 35 insertions(+), 12 deletions(-)

diff --git a/api/js/etemplate/Et2Link/Et2LinkSearch.ts b/api/js/etemplate/Et2Link/Et2LinkSearch.ts
index e0dcd1aa5b..6c65ecfb63 100644
--- a/api/js/etemplate/Et2Link/Et2LinkSearch.ts
+++ b/api/js/etemplate/Et2Link/Et2LinkSearch.ts
@@ -69,10 +69,10 @@ export class Et2LinkSearch extends Et2Select
 		{
 			if(!this.query(request, this))
 			{
-				return;
+				return Promise.resolve();
 			}
 		}
-		request.then((result) =>
+		return request.then((result) =>
 		{
 			this.processRemoteResults(result);
 		});
diff --git a/api/js/etemplate/Et2Select/SearchMixin.ts b/api/js/etemplate/Et2Select/SearchMixin.ts
index 585e6b8a02..8e5b094996 100644
--- a/api/js/etemplate/Et2Select/SearchMixin.ts
+++ b/api/js/etemplate/Et2Select/SearchMixin.ts
@@ -359,8 +359,6 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
 
 			super.updateComplete.then(() =>
 			{
-				this.menu.querySelector("slot").textContent = this.egw().lang("No suggestions");
-
 				let control = this.shadowRoot.querySelector(".form-control-input");
 				control.append(div);
 			});
@@ -760,8 +758,10 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
 		 * If we have local options, we'll search & display any matches.
 		 * If serverUrl is set, we'll ask the server for results as well.
 		 */
-		public startSearch()
+		public async startSearch()
 		{
+			this.menu.querySelector("slot").textContent = "";
+
 			// Stop timeout timer
 			clearTimeout(this._searchTimeout);
 
@@ -777,18 +777,31 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
 				clear_button.style.display = "none";
 			}
 
+			// Clear previous results
+			this._clearResults();
+			await this.updateComplete;
+
 			// Start the searches
-			Promise.all([
+			return Promise.all([
 				this.localSearch(this._searchInputNode.value, this.searchOptions),
 				this.remoteSearch(this._searchInputNode.value, this.searchOptions)
 			]).then(() =>
 			{
+				// Show / hide no results indicator
+				this.menu.querySelector("slot").textContent = this.menuItems.length == 0 ? this.egw().lang("No suggestions") : "";
+
+				// Remove spinner
 				spinner.remove();
+
 				// Restore clear button
 				if(clear_button)
 				{
 					clear_button.style.display = "";
 				}
+			}).then(() =>
+			{
+				// Not sure why this stays hidden if there's no results but it sticks and hides all results afterward
+				this.dropdown.shadowRoot.querySelector(".dropdown__panel").removeAttribute("hidden");
 			});
 		}
 
@@ -802,6 +815,17 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
 			// Stop timeout timer
 			clearTimeout(this._searchTimeout);
 
+			this._clearResults();
+
+			// Clear search term
+			if(this._searchInputNode)
+			{
+				this._searchInputNode.value = "";
+			}
+		}
+
+		protected _clearResults()
+		{
 			// Remove remote options that aren't used
 			let target = this._optionTargetNode || this;
 			let keepers = this._selected_remote.reduce((prev, current) =>
@@ -824,12 +848,6 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
 				item.classList.remove("match");
 				item.classList.remove("no-match");
 			});
-
-			// Clear search term
-			if(this._searchInputNode)
-			{
-				this._searchInputNode.value = "";
-			}
 		}
 
 		/**
@@ -904,8 +922,13 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
 		 */
 		protected processRemoteResults(results)
 		{
+
 			let entries = cleanSelectOptions(results);
 
+			if(entries.length == 0)
+			{
+				return Promise.resolve();
+			}
 			// Add a "remote" class so we can tell these apart from any local results
 			entries.forEach((entry) => entry.class = (entry.class || "") + " remote");