From c43f41519e90644f728c5520b5cf7e60d5a71cdd Mon Sep 17 00:00:00 2001
From: Andreas Fernandez <a.fernandez@scripting-base.de>
Date: Mon, 28 Aug 2023 14:53:13 +0200
Subject: [PATCH] [BUGFIX] Listen to correct events in "Extension
 Configuration" filter

Previously, the `keyup` event was intercepted in "Extension
Configuration", which doesn't work with `clearable()`. This patch
changes the code to listen to `input` and `change` events instead.

Resolves: #101772
Releases: 12.4
Change-Id: I1e90ede6ab1693fd76dcb6aafb1dab0a616b5784
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/80714
Reviewed-by: Benjamin Franzke <ben@bnf.dev>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Benjamin Franzke <ben@bnf.dev>
Tested-by: core-ci <typo3@b13.com>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
---
 .../settings/extension-configuration.ts       | 41 +++++++++++--------
 .../settings/extension-configuration.js       |  2 +-
 2 files changed, 25 insertions(+), 18 deletions(-)

diff --git a/Build/Sources/TypeScript/install/module/settings/extension-configuration.ts b/Build/Sources/TypeScript/install/module/settings/extension-configuration.ts
index d952bff3cf10..a38bf274f5b3 100644
--- a/Build/Sources/TypeScript/install/module/settings/extension-configuration.ts
+++ b/Build/Sources/TypeScript/install/module/settings/extension-configuration.ts
@@ -22,6 +22,8 @@ import AjaxRequest from '@typo3/core/ajax/ajax-request';
 import Router from '../../router';
 import { topLevelModuleImport } from '@typo3/backend/utility/top-level-module-import';
 import MessageInterface from '@typo3/install/message-interface';
+import DebounceEvent from '@typo3/core/event/debounce-event';
+import RegularEvent from '@typo3/core/event/regular-event';
 
 /**
  * Module: @typo3/install/module/extension-configuration
@@ -51,23 +53,15 @@ class ExtensionConfiguration extends AbstractInteractableModule {
     });
 
     // Perform expand collapse on search matches
-    currentModal.on('keyup', this.selectorSearchInput, (e: JQueryEventObject): void => {
-      const typedQuery = $(e.target).val();
-      const $searchInput = currentModal.find(this.selectorSearchInput);
-      currentModal.find('.search-item').each((index: number, element: Element): void => {
-        const $item = $(element);
-        if ($(':contains(' + typedQuery + ')', $item).length > 0 || $('input[value*="' + typedQuery + '"]', $item).length > 0) {
-          $item.removeClass('hidden').addClass('searchhit');
-        } else {
-          $item.removeClass('searchhit').addClass('hidden');
-        }
-      });
-      currentModal.find('.searchhit').collapse('show');
-      // Make search field clearable
-      const searchInput = <HTMLInputElement>$searchInput.get(0);
-      searchInput.clearable();
-      searchInput.focus();
-    });
+    new DebounceEvent('input', (event: Event, target: HTMLInputElement): void => {
+      const typedQuery = target.value;
+      this.search(typedQuery);
+    }, 100).delegateTo(currentModal.get(0), this.selectorSearchInput);
+
+    new RegularEvent('change', (event: Event, target: HTMLInputElement): void => {
+      const typedQuery = target.value;
+      this.search(typedQuery);
+    }).delegateTo(currentModal.get(0), this.selectorSearchInput);
 
     currentModal.on('submit', this.selectorFormListener, (e: JQueryEventObject): void => {
       e.preventDefault();
@@ -75,6 +69,18 @@ class ExtensionConfiguration extends AbstractInteractableModule {
     });
   }
 
+  private search(typedQuery: string): void {
+    this.currentModal.find('.search-item').each((index: number, element: Element): void => {
+      const $item = $(element);
+      if ($(':contains(' + typedQuery + ')', $item).length > 0 || $('input[value*="' + typedQuery + '"]', $item).length > 0) {
+        $item.removeClass('hidden').addClass('searchhit');
+      } else {
+        $item.removeClass('searchhit').addClass('hidden');
+      }
+    });
+    this.currentModal.find('.searchhit').collapse('show');
+  }
+
   private getContent(): void {
     const modalContent = this.getModalBody();
     (new AjaxRequest(Router.getUrl('extensionConfigurationGetContent')))
@@ -84,6 +90,7 @@ class ExtensionConfiguration extends AbstractInteractableModule {
           const data = await response.resolve();
           if (data.success === true) {
             modalContent.html(data.html);
+            (modalContent.get(0).querySelector(this.selectorSearchInput) as HTMLInputElement).clearable();
             this.initializeWrap();
             this.initializeColorPicker();
           }
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/settings/extension-configuration.js b/typo3/sysext/install/Resources/Public/JavaScript/module/settings/extension-configuration.js
index f3a501ecaa07..d8a2b9f51d1a 100644
--- a/typo3/sysext/install/Resources/Public/JavaScript/module/settings/extension-configuration.js
+++ b/typo3/sysext/install/Resources/Public/JavaScript/module/settings/extension-configuration.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-import"bootstrap";import $ from"jquery";import"@typo3/install/renderable/clearable.js";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import ModuleMenu from"@typo3/backend/module-menu.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Router from"@typo3/install/router.js";import{topLevelModuleImport}from"@typo3/backend/utility/top-level-module-import.js";class ExtensionConfiguration extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorFormListener=".t3js-extensionConfiguration-form",this.selectorSearchInput=".t3js-extensionConfiguration-search"}initialize(t){this.currentModal=t,this.getContent(),t.on("keydown",(e=>{const a=t.find(this.selectorSearchInput);e.ctrlKey||e.metaKey?"f"===String.fromCharCode(e.which).toLowerCase()&&(e.preventDefault(),a.trigger("focus")):27===e.keyCode&&(e.preventDefault(),a.val("").trigger("focus"))})),t.on("keyup",this.selectorSearchInput,(e=>{const a=$(e.target).val(),o=t.find(this.selectorSearchInput);t.find(".search-item").each(((t,e)=>{const o=$(e);$(":contains("+a+")",o).length>0||$('input[value*="'+a+'"]',o).length>0?o.removeClass("hidden").addClass("searchhit"):o.removeClass("searchhit").addClass("hidden")})),t.find(".searchhit").collapse("show");const r=o.get(0);r.clearable(),r.focus()})),t.on("submit",this.selectorFormListener,(t=>{t.preventDefault(),this.write($(t.currentTarget))}))}getContent(){const t=this.getModalBody();new AjaxRequest(Router.getUrl("extensionConfigurationGetContent")).get({cache:"no-cache"}).then((async e=>{const a=await e.resolve();!0===a.success&&(t.html(a.html),this.initializeWrap(),this.initializeColorPicker())}),(e=>{Router.handleAjaxError(e,t)}))}initializeColorPicker(){window.location!==window.parent.location?topLevelModuleImport("@typo3/backend/color-picker.js").then((({default:t})=>{parent.document.querySelectorAll(".t3js-color-input").forEach((e=>t.initialize(e)))})):import("@typo3/backend/color-picker.js").then((({default:t})=>{document.querySelectorAll(".t3js-color-input").forEach((e=>t.initialize(e)))}))}write(t){const e=this.getModalBody(),a=this.getModuleContent().data("extension-configuration-write-token"),o={};for(const e of t.serializeArray())o[e.name]=e.value;new AjaxRequest(Router.getUrl()).post({install:{token:a,action:"extensionConfigurationWrite",extensionKey:t.attr("data-extensionKey"),extensionConfiguration:o}}).then((async t=>{const e=await t.resolve();!0===e.success&&Array.isArray(e.status)?(e.status.forEach((t=>{Notification.showMessage(t.title,t.message,t.severity)})),"backend"===$("body").data("context")&&ModuleMenu.App.refreshMenu()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}initializeWrap(){this.findInModal(".t3js-emconf-offset").each(((t,e)=>{const a=$(e),o=a.parent(),r=a.attr("id"),i=a.attr("value").split(",");a.attr("data-offsetfield-x","#"+r+"_offset_x").attr("data-offsetfield-y","#"+r+"_offset_y").wrap('<div class="hidden"></div>');const n=$("<div>",{class:"form-multigroup-item"}).append($("<div>",{class:"input-group"}).append($("<div>",{class:"input-group-addon"}).text("x"),$("<input>",{id:r+"_offset_x",class:"form-control t3js-emconf-offsetfield","data-target":"#"+r,value:i[0]?.trim()}))),s=$("<div>",{class:"form-multigroup-item"}).append($("<div>",{class:"input-group"}).append($("<div>",{class:"input-group-addon"}).text("y"),$("<input>",{id:r+"_offset_y",class:"form-control t3js-emconf-offsetfield","data-target":"#"+r,value:i[1]?.trim()}))),l=$("<div>",{class:"form-multigroup-wrap"}).append(n,s);o.append(l),o.find(".t3js-emconf-offsetfield").on("keyup",(t=>{const e=o.find($(t.currentTarget).data("target"));e.val(o.find(e.data("offsetfield-x")).val()+","+o.find(e.data("offsetfield-y")).val())}))})),this.findInModal(".t3js-emconf-wrap").each(((t,e)=>{const a=$(e),o=a.parent(),r=a.attr("id"),i=a.attr("value").split("|");a.attr("data-wrapfield-start","#"+r+"_wrap_start").attr("data-wrapfield-end","#"+r+"_wrap_end").wrap('<div class="hidden"></div>');const n=$("<div>",{class:"form-multigroup-wrap"}).append($("<div>",{class:"form-multigroup-item"}).append($("<input>",{id:r+"_wrap_start",class:"form-control t3js-emconf-wrapfield","data-target":"#"+r,value:i[0]?.trim()})),$("<div>",{class:"form-multigroup-item"}).append($("<input>",{id:r+"_wrap_end",class:"form-control t3js-emconf-wrapfield","data-target":"#"+r,value:i[1]?.trim()})));o.append(n),o.find(".t3js-emconf-wrapfield").on("keyup",(t=>{const e=o.find($(t.currentTarget).data("target"));e.val(o.find(e.data("wrapfield-start")).val()+"|"+o.find(e.data("wrapfield-end")).val())}))}))}}export default new ExtensionConfiguration;
\ No newline at end of file
+import"bootstrap";import $ from"jquery";import"@typo3/install/renderable/clearable.js";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import ModuleMenu from"@typo3/backend/module-menu.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Router from"@typo3/install/router.js";import{topLevelModuleImport}from"@typo3/backend/utility/top-level-module-import.js";import DebounceEvent from"@typo3/core/event/debounce-event.js";import RegularEvent from"@typo3/core/event/regular-event.js";class ExtensionConfiguration extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorFormListener=".t3js-extensionConfiguration-form",this.selectorSearchInput=".t3js-extensionConfiguration-search"}initialize(t){this.currentModal=t,this.getContent(),t.on("keydown",(e=>{const o=t.find(this.selectorSearchInput);e.ctrlKey||e.metaKey?"f"===String.fromCharCode(e.which).toLowerCase()&&(e.preventDefault(),o.trigger("focus")):27===e.keyCode&&(e.preventDefault(),o.val("").trigger("focus"))})),new DebounceEvent("input",((t,e)=>{const o=e.value;this.search(o)}),100).delegateTo(t.get(0),this.selectorSearchInput),new RegularEvent("change",((t,e)=>{const o=e.value;this.search(o)})).delegateTo(t.get(0),this.selectorSearchInput),t.on("submit",this.selectorFormListener,(t=>{t.preventDefault(),this.write($(t.currentTarget))}))}search(t){this.currentModal.find(".search-item").each(((e,o)=>{const a=$(o);$(":contains("+t+")",a).length>0||$('input[value*="'+t+'"]',a).length>0?a.removeClass("hidden").addClass("searchhit"):a.removeClass("searchhit").addClass("hidden")})),this.currentModal.find(".searchhit").collapse("show")}getContent(){const t=this.getModalBody();new AjaxRequest(Router.getUrl("extensionConfigurationGetContent")).get({cache:"no-cache"}).then((async e=>{const o=await e.resolve();!0===o.success&&(t.html(o.html),t.get(0).querySelector(this.selectorSearchInput).clearable(),this.initializeWrap(),this.initializeColorPicker())}),(e=>{Router.handleAjaxError(e,t)}))}initializeColorPicker(){window.location!==window.parent.location?topLevelModuleImport("@typo3/backend/color-picker.js").then((({default:t})=>{parent.document.querySelectorAll(".t3js-color-input").forEach((e=>t.initialize(e)))})):import("@typo3/backend/color-picker.js").then((({default:t})=>{document.querySelectorAll(".t3js-color-input").forEach((e=>t.initialize(e)))}))}write(t){const e=this.getModalBody(),o=this.getModuleContent().data("extension-configuration-write-token"),a={};for(const e of t.serializeArray())a[e.name]=e.value;new AjaxRequest(Router.getUrl()).post({install:{token:o,action:"extensionConfigurationWrite",extensionKey:t.attr("data-extensionKey"),extensionConfiguration:a}}).then((async t=>{const e=await t.resolve();!0===e.success&&Array.isArray(e.status)?(e.status.forEach((t=>{Notification.showMessage(t.title,t.message,t.severity)})),"backend"===$("body").data("context")&&ModuleMenu.App.refreshMenu()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}initializeWrap(){this.findInModal(".t3js-emconf-offset").each(((t,e)=>{const o=$(e),a=o.parent(),r=o.attr("id"),n=o.attr("value").split(",");o.attr("data-offsetfield-x","#"+r+"_offset_x").attr("data-offsetfield-y","#"+r+"_offset_y").wrap('<div class="hidden"></div>');const i=$("<div>",{class:"form-multigroup-item"}).append($("<div>",{class:"input-group"}).append($("<div>",{class:"input-group-addon"}).text("x"),$("<input>",{id:r+"_offset_x",class:"form-control t3js-emconf-offsetfield","data-target":"#"+r,value:n[0]?.trim()}))),s=$("<div>",{class:"form-multigroup-item"}).append($("<div>",{class:"input-group"}).append($("<div>",{class:"input-group-addon"}).text("y"),$("<input>",{id:r+"_offset_y",class:"form-control t3js-emconf-offsetfield","data-target":"#"+r,value:n[1]?.trim()}))),l=$("<div>",{class:"form-multigroup-wrap"}).append(i,s);a.append(l),a.find(".t3js-emconf-offsetfield").on("keyup",(t=>{const e=a.find($(t.currentTarget).data("target"));e.val(a.find(e.data("offsetfield-x")).val()+","+a.find(e.data("offsetfield-y")).val())}))})),this.findInModal(".t3js-emconf-wrap").each(((t,e)=>{const o=$(e),a=o.parent(),r=o.attr("id"),n=o.attr("value").split("|");o.attr("data-wrapfield-start","#"+r+"_wrap_start").attr("data-wrapfield-end","#"+r+"_wrap_end").wrap('<div class="hidden"></div>');const i=$("<div>",{class:"form-multigroup-wrap"}).append($("<div>",{class:"form-multigroup-item"}).append($("<input>",{id:r+"_wrap_start",class:"form-control t3js-emconf-wrapfield","data-target":"#"+r,value:n[0]?.trim()})),$("<div>",{class:"form-multigroup-item"}).append($("<input>",{id:r+"_wrap_end",class:"form-control t3js-emconf-wrapfield","data-target":"#"+r,value:n[1]?.trim()})));a.append(i),a.find(".t3js-emconf-wrapfield").on("keyup",(t=>{const e=a.find($(t.currentTarget).data("target"));e.val(a.find(e.data("wrapfield-start")).val()+"|"+a.find(e.data("wrapfield-end")).val())}))}))}}export default new ExtensionConfiguration;
\ No newline at end of file
-- 
GitLab