From 46d45da0efaebcbd65257090aa1e1e25ef8bb734 Mon Sep 17 00:00:00 2001
From: Andreas Fernandez <a.fernandez@scripting-base.de>
Date: Mon, 6 Mar 2023 17:51:25 +0100
Subject: [PATCH] [TASK] Remove jQuery from shortcut handling

This patch removes jQuery from the shortcut handling. Also, some minor
refactorings were done:

* use proper events for submit / cancel handling
* submit the form as-is instead of building a custom payload

Resolves: #100099
Releases: main
Change-Id: I25fe414ba7bd8f513f6864a915fec8c75b61a16a
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/78039
Tested-by: Georg Ringer <georg.ringer@gmail.com>
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Reviewed-by: Georg Ringer <georg.ringer@gmail.com>
Tested-by: core-ci <typo3@b13.com>
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Tested-by: Markus Klein <markus.klein@typo3.org>
---
 .../backend/toolbar/shortcut-menu.ts          | 93 +++++++++----------
 .../ShortcutToolbarItemEditForm.html          |  9 +-
 .../JavaScript/toolbar/shortcut-menu.js       |  2 +-
 .../Application/Topbar/BookmarkCest.php       |  4 +-
 4 files changed, 53 insertions(+), 55 deletions(-)

diff --git a/Build/Sources/TypeScript/backend/toolbar/shortcut-menu.ts b/Build/Sources/TypeScript/backend/toolbar/shortcut-menu.ts
index 0488d4fda8cc..8c780e54cad1 100644
--- a/Build/Sources/TypeScript/backend/toolbar/shortcut-menu.ts
+++ b/Build/Sources/TypeScript/backend/toolbar/shortcut-menu.ts
@@ -11,7 +11,6 @@
  * The TYPO3 project - inspiring people to share!
  */
 
-import $ from 'jquery';
 import {AjaxResponse} from '@typo3/core/ajax/ajax-response';
 import AjaxRequest from '@typo3/core/ajax/ajax-request';
 import Icons from '../icons';
@@ -22,6 +21,7 @@ import SecurityUtility from '@typo3/core/security-utility';
 import {ModuleStateStorage} from '@typo3/backend/storage/module-state-storage';
 import '@typo3/backend/element/spinner-element';
 import {Sizes} from '../enum/icon-types';
+import RegularEvent from '@typo3/core/event/regular-event';
 
 enum Identifiers {
   containerSelector = '#typo3-cms-backend-backend-toolbaritems-shortcuttoolbaritem',
@@ -33,10 +33,6 @@ enum Identifiers {
   shortcutDeleteSelector = '.t3js-shortcut-delete',
   shortcutEditSelector = '.t3js-shortcut-edit',
 
-  shortcutFormTitleSelector = 'input[name="shortcut-title"]',
-  shortcutFormGroupSelector = 'select[name="shortcut-group"]',
-  shortcutFormSaveSelector = '.t3js-shortcut-form-save',
-  shortcutFormCancelSelector = '.t3js-shortcut-form-cancel',
   shortcutFormSelector = '.t3js-shortcut-form',
 }
 
@@ -88,55 +84,64 @@ class ShortcutMenu {
         return;
       }
 
-      const isDropdownItem = $(shortcutButton).hasClass('dropdown-item');
+      const isDropdownItem = shortcutButton.classList.contains('dropdown-item');
       const securityUtility = new SecurityUtility();
       Icons.getIcon('actions-system-shortcut-active', Icons.sizes.small).then((icon: string): void => {
-        $(shortcutButton).html(icon + (isDropdownItem ? ' ' + securityUtility.encodeHtml(TYPO3.lang['labels.alreadyBookmarked']) : ''));
+        shortcutButton.innerHTML = icon + (isDropdownItem ? ' ' + securityUtility.encodeHtml(TYPO3.lang['labels.alreadyBookmarked']) : '');
       });
-      $(shortcutButton).addClass(isDropdownItem ? 'disabled' : 'active');
+      shortcutButton.classList.add(isDropdownItem ? 'disabled' : 'active');
       // @todo using plain `disabled` HTML attr would have been better, since it disables events, mouse cursor, etc.
       //       (however, it might make things more complicated in Bootstrap's `button-variant` mixin)
-      $(shortcutButton).attr('data-dispatch-disabled', 'disabled');
-      $(shortcutButton).attr('title', TYPO3.lang['labels.alreadyBookmarked']);
+      shortcutButton.dataset.dispatchDisabled = 'disabled';
+      shortcutButton.title = securityUtility.encodeHtml(TYPO3.lang['labels.alreadyBookmarked']);
     }).catch((): void => {
       Notification.error(TYPO3.lang['bookmark.failedTitle'], TYPO3.lang['bookmark.failedMessage']);
     });
   }
 
   private initializeEvents = (): void => {
-    $(Identifiers.containerSelector).on('click', Identifiers.shortcutDeleteSelector, (evt: JQueryEventObject): void => {
+    const containerSelector = document.querySelector(Identifiers.containerSelector);
+    new RegularEvent('click', (evt: Event, target: HTMLElement): void => {
       evt.preventDefault();
       evt.stopImmediatePropagation();
-      this.deleteShortcut($(evt.currentTarget).closest(Identifiers.shortcutItemSelector).data('shortcutid'));
-    }).on('click', Identifiers.shortcutFormGroupSelector, (evt: JQueryEventObject): void => {
-      evt.preventDefault();
-      evt.stopImmediatePropagation();
-    }).on('click', Identifiers.shortcutEditSelector, (evt: JQueryEventObject): void => {
-      evt.preventDefault();
-      evt.stopImmediatePropagation();
-      this.editShortcut($(evt.currentTarget).closest(Identifiers.shortcutItemSelector));
-    }).on('click', Identifiers.shortcutFormSaveSelector, (evt: JQueryEventObject): void => {
+
+      const shortcutItem = target.closest(Identifiers.shortcutItemSelector) as HTMLElement;
+      this.deleteShortcut(parseInt(shortcutItem.dataset.shortcutid, 10));
+    }).delegateTo(containerSelector, Identifiers.shortcutDeleteSelector);
+
+    new RegularEvent('click', (evt: Event, target: HTMLElement): void => {
       evt.preventDefault();
       evt.stopImmediatePropagation();
-      this.saveShortcutForm($(evt.currentTarget).closest(Identifiers.shortcutFormSelector));
-    }).on('submit', Identifiers.shortcutFormSelector, (evt: JQueryEventObject): void => {
+
+      const shortcutItem = target.closest(Identifiers.shortcutItemSelector) as HTMLElement;
+      this.editShortcut(parseInt(shortcutItem.dataset.shortcutid, 10), shortcutItem.dataset.shortcutgroup);
+    }).delegateTo(containerSelector, Identifiers.shortcutEditSelector);
+
+    new RegularEvent('submit', (evt: Event, target: HTMLFormElement): void => {
       evt.preventDefault();
       evt.stopImmediatePropagation();
-      this.saveShortcutForm($(evt.currentTarget).closest(Identifiers.shortcutFormSelector));
-    }).on('click', Identifiers.shortcutFormCancelSelector, (evt: JQueryEventObject): void => {
+
+      this.saveShortcutForm(target);
+    }).delegateTo(containerSelector, Identifiers.shortcutFormSelector);
+
+    new RegularEvent('reset', (evt: Event): void => {
       evt.preventDefault();
       evt.stopImmediatePropagation();
+
       this.refreshMenu();
-    }).on('click', Identifiers.shortcutJumpSelector, (evt: JQueryEventObject): void => {
+    }).delegateTo(containerSelector, Identifiers.shortcutFormSelector);
+
+    new RegularEvent('click', (evt: Event, target: HTMLAnchorElement): void => {
       evt.preventDefault();
-      const pageId = $(evt.currentTarget).data('pageid');
+
+      const pageId = target.dataset.pageId;
       if (pageId) {
         ModuleStateStorage.updateWithCurrentMount('web', pageId, true);
       }
       const router = document.querySelector('typo3-backend-module-router');
-      router.setAttribute('endpoint', $(evt.currentTarget).attr('href'))
-      router.setAttribute('module', $(evt.currentTarget).data('module'));
-    });
+      router.setAttribute('endpoint', target.href)
+      router.setAttribute('module', target.dataset.module);
+    }).delegateTo(containerSelector, Identifiers.shortcutJumpSelector);
   }
 
   /**
@@ -164,30 +169,22 @@ class ShortcutMenu {
 
   /**
    * Build the in-place-editor for a shortcut
-   *
-   * @param {JQuery} $shortcutRecord
    */
-  private editShortcut($shortcutRecord: JQuery): void {
+  private editShortcut(shortcutId: number, shortcutGroup: string): void {
     // load the form
     (new AjaxRequest(TYPO3.settings.ajaxUrls.shortcut_editform)).withQueryArguments({
-      shortcutId: $shortcutRecord.data('shortcutid'),
-      shortcutGroup: $shortcutRecord.data('shortcutgroup'),
+      shortcutId,
+      shortcutGroup,
     }).get({cache: 'no-cache'}).then(async (response: AjaxResponse): Promise<any> => {
-      $(Identifiers.containerSelector).find(Identifiers.toolbarMenuSelector).html(await response.resolve());
+      document.querySelector(Identifiers.containerSelector + ' ' + Identifiers.toolbarMenuSelector).innerHTML = await response.resolve();
     });
   }
 
   /**
    * Save the data from the in-place-editor for a shortcut
-   *
-   * @param {JQuery} $shortcutForm
    */
-  private saveShortcutForm($shortcutForm: JQuery): void {
-    (new AjaxRequest(TYPO3.settings.ajaxUrls.shortcut_saveform)).post({
-      shortcutId: $shortcutForm.data('shortcutid'),
-      shortcutTitle: $shortcutForm.find(Identifiers.shortcutFormTitleSelector).val(),
-      shortcutGroup: $shortcutForm.find(Identifiers.shortcutFormGroupSelector).val(),
-    }).then((): void => {
+  private saveShortcutForm(shortcutForm: HTMLFormElement): void {
+    (new AjaxRequest(TYPO3.settings.ajaxUrls.shortcut_saveform)).post(new FormData(shortcutForm)).then((): void => {
       Notification.success(TYPO3.lang['bookmark.savedTitle'], TYPO3.lang['bookmark.savedMessage']);
       this.refreshMenu();
     });
@@ -197,17 +194,17 @@ class ShortcutMenu {
    * Reloads the menu after an update
    */
   private refreshMenu(): void {
-    const $toolbarItemIcon = $(Identifiers.toolbarIconSelector, Identifiers.containerSelector);
-    const $existingIcon = $toolbarItemIcon.clone();
+    const toolbarItemIcon = document.querySelector(Identifiers.containerSelector + ' ' + Identifiers.toolbarIconSelector);
+    const existingIcon = toolbarItemIcon.cloneNode(true);
 
     const spinner = document.createElement('typo3-backend-spinner');
     spinner.setAttribute('size', Sizes.small);
-    $toolbarItemIcon.replaceWith(spinner);
+    toolbarItemIcon.replaceWith(spinner);
 
     (new AjaxRequest(TYPO3.settings.ajaxUrls.shortcut_list)).get({cache: 'no-cache'}).then(async (response: AjaxResponse): Promise<any> => {
-      $(Identifiers.toolbarMenuSelector, Identifiers.containerSelector).html(await response.resolve());
+      document.querySelector(Identifiers.containerSelector + ' ' + Identifiers.toolbarMenuSelector).innerHTML = await response.resolve();
     }).finally((): void => {
-      $('typo3-backend-spinner', Identifiers.containerSelector).replaceWith($existingIcon);
+      document.querySelector(Identifiers.containerSelector + ' typo3-backend-spinner').replaceWith(existingIcon);
     });
   }
 }
diff --git a/typo3/sysext/backend/Resources/Private/Templates/ToolbarItems/ShortcutToolbarItemEditForm.html b/typo3/sysext/backend/Resources/Private/Templates/ToolbarItems/ShortcutToolbarItemEditForm.html
index 8a03cc7f5ab6..89b1488b8039 100644
--- a/typo3/sysext/backend/Resources/Private/Templates/ToolbarItems/ShortcutToolbarItemEditForm.html
+++ b/typo3/sysext/backend/Resources/Private/Templates/ToolbarItems/ShortcutToolbarItemEditForm.html
@@ -5,16 +5,17 @@
     <hr class="dropdown-divider" aria-hidden="true">
     <div class="dropdown-item-text">
         <div class="form-group">
-            <input type="text" class="form-control" name="shortcut-title" value="{selectedShortcut.label}"/>
+            <input type="text" class="form-control" name="shortcutTitle" value="{selectedShortcut.label}"/>
         </div>
         <div class="form-group">
-            <select class="form-select" name="shortcut-group">
+            <select class="form-select" name="shortcutGroup">
                 <f:for each="{shortcutGroups}" key="shortcutGroupId" as="shortcutGroupTitle">
                     <option value="{shortcutGroupId}" {f:if(condition: '{selectedShortcutGroupId} == {shortcutGroupId}', then: 'selected="selected"')}>{shortcutGroupTitle}</option>
                 </f:for>
             </select>
         </div>
-        <input type="button" class="btn btn-default t3js-shortcut-form-cancel" value="Cancel"/>
-        <input type="button" class="btn btn-success t3js-shortcut-form-save" value="Save"/>
+        <input type="hidden" name="shortcutId" value="{selectedShortcutId}" />
+        <input type="reset" class="btn btn-default" value="Cancel"/>
+        <input type="submit" class="btn btn-success" value="Save"/>
     </div>
 </form>
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/toolbar/shortcut-menu.js b/typo3/sysext/backend/Resources/Public/JavaScript/toolbar/shortcut-menu.js
index 25a1f2035a42..e664341ed06f 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/toolbar/shortcut-menu.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/toolbar/shortcut-menu.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-import $ from"jquery";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Icons from"@typo3/backend/icons.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import Viewport from"@typo3/backend/viewport.js";import SecurityUtility from"@typo3/core/security-utility.js";import{ModuleStateStorage}from"@typo3/backend/storage/module-state-storage.js";import"@typo3/backend/element/spinner-element.js";import{Sizes}from"@typo3/backend/enum/icon-types.js";var Identifiers;!function(t){t.containerSelector="#typo3-cms-backend-backend-toolbaritems-shortcuttoolbaritem",t.toolbarIconSelector=".dropdown-toggle span.icon",t.toolbarMenuSelector=".dropdown-menu",t.shortcutItemSelector=".t3js-topbar-shortcut",t.shortcutJumpSelector=".t3js-shortcut-jump",t.shortcutDeleteSelector=".t3js-shortcut-delete",t.shortcutEditSelector=".t3js-shortcut-edit",t.shortcutFormTitleSelector='input[name="shortcut-title"]',t.shortcutFormGroupSelector='select[name="shortcut-group"]',t.shortcutFormSaveSelector=".t3js-shortcut-form-save",t.shortcutFormCancelSelector=".t3js-shortcut-form-cancel",t.shortcutFormSelector=".t3js-shortcut-form"}(Identifiers||(Identifiers={}));class ShortcutMenu{constructor(){this.initializeEvents=()=>{$(Identifiers.containerSelector).on("click",Identifiers.shortcutDeleteSelector,(t=>{t.preventDefault(),t.stopImmediatePropagation(),this.deleteShortcut($(t.currentTarget).closest(Identifiers.shortcutItemSelector).data("shortcutid"))})).on("click",Identifiers.shortcutFormGroupSelector,(t=>{t.preventDefault(),t.stopImmediatePropagation()})).on("click",Identifiers.shortcutEditSelector,(t=>{t.preventDefault(),t.stopImmediatePropagation(),this.editShortcut($(t.currentTarget).closest(Identifiers.shortcutItemSelector))})).on("click",Identifiers.shortcutFormSaveSelector,(t=>{t.preventDefault(),t.stopImmediatePropagation(),this.saveShortcutForm($(t.currentTarget).closest(Identifiers.shortcutFormSelector))})).on("submit",Identifiers.shortcutFormSelector,(t=>{t.preventDefault(),t.stopImmediatePropagation(),this.saveShortcutForm($(t.currentTarget).closest(Identifiers.shortcutFormSelector))})).on("click",Identifiers.shortcutFormCancelSelector,(t=>{t.preventDefault(),t.stopImmediatePropagation(),this.refreshMenu()})).on("click",Identifiers.shortcutJumpSelector,(t=>{t.preventDefault();const e=$(t.currentTarget).data("pageid");e&&ModuleStateStorage.updateWithCurrentMount("web",e,!0);const o=document.querySelector("typo3-backend-module-router");o.setAttribute("endpoint",$(t.currentTarget).attr("href")),o.setAttribute("module",$(t.currentTarget).data("module"))}))},Viewport.Topbar.Toolbar.registerEvent(this.initializeEvents)}createShortcut(t,e,o,r,s){new AjaxRequest(TYPO3.settings.ajaxUrls.shortcut_create).post({routeIdentifier:t,arguments:e,displayName:o}).then((async t=>{const e=await t.resolve();if("success"===e.result?Notification.success(TYPO3.lang["bookmark.savedTitle"],TYPO3.lang["bookmark.savedMessage"]):"alreadyExists"===e.result&&Notification.info(TYPO3.lang["bookmark.alreadyExistsTitle"],TYPO3.lang["bookmark.alreadyExistsMessage"]),this.refreshMenu(),"object"!=typeof s)return void console.warn("Expected argument shortcutButton to be an object, got "+typeof s);const o=$(s).hasClass("dropdown-item"),r=new SecurityUtility;Icons.getIcon("actions-system-shortcut-active",Icons.sizes.small).then((t=>{$(s).html(t+(o?" "+r.encodeHtml(TYPO3.lang["labels.alreadyBookmarked"]):""))})),$(s).addClass(o?"disabled":"active"),$(s).attr("data-dispatch-disabled","disabled"),$(s).attr("title",TYPO3.lang["labels.alreadyBookmarked"])})).catch((()=>{Notification.error(TYPO3.lang["bookmark.failedTitle"],TYPO3.lang["bookmark.failedMessage"])}))}deleteShortcut(t){const e=Modal.confirm(TYPO3.lang["bookmark.delete"],TYPO3.lang["bookmark.confirmDelete"]);e.addEventListener("confirm.button.ok",(()=>{new AjaxRequest(TYPO3.settings.ajaxUrls.shortcut_remove).post({shortcutId:t}).then((()=>{this.refreshMenu()})),e.hideModal()})),e.addEventListener("confirm.button.cancel",(()=>{e.hideModal()}))}editShortcut(t){new AjaxRequest(TYPO3.settings.ajaxUrls.shortcut_editform).withQueryArguments({shortcutId:t.data("shortcutid"),shortcutGroup:t.data("shortcutgroup")}).get({cache:"no-cache"}).then((async t=>{$(Identifiers.containerSelector).find(Identifiers.toolbarMenuSelector).html(await t.resolve())}))}saveShortcutForm(t){new AjaxRequest(TYPO3.settings.ajaxUrls.shortcut_saveform).post({shortcutId:t.data("shortcutid"),shortcutTitle:t.find(Identifiers.shortcutFormTitleSelector).val(),shortcutGroup:t.find(Identifiers.shortcutFormGroupSelector).val()}).then((()=>{Notification.success(TYPO3.lang["bookmark.savedTitle"],TYPO3.lang["bookmark.savedMessage"]),this.refreshMenu()}))}refreshMenu(){const t=$(Identifiers.toolbarIconSelector,Identifiers.containerSelector),e=t.clone(),o=document.createElement("typo3-backend-spinner");o.setAttribute("size",Sizes.small),t.replaceWith(o),new AjaxRequest(TYPO3.settings.ajaxUrls.shortcut_list).get({cache:"no-cache"}).then((async t=>{$(Identifiers.toolbarMenuSelector,Identifiers.containerSelector).html(await t.resolve())})).finally((()=>{$("typo3-backend-spinner",Identifiers.containerSelector).replaceWith(e)}))}}top.TYPO3.ShortcutMenu&&"object"==typeof top.TYPO3.ShortcutMenu||(top.TYPO3.ShortcutMenu=new ShortcutMenu);const shortcutMenuObject=top.TYPO3.ShortcutMenu;export default shortcutMenuObject;
\ No newline at end of file
+import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Icons from"@typo3/backend/icons.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import Viewport from"@typo3/backend/viewport.js";import SecurityUtility from"@typo3/core/security-utility.js";import{ModuleStateStorage}from"@typo3/backend/storage/module-state-storage.js";import"@typo3/backend/element/spinner-element.js";import{Sizes}from"@typo3/backend/enum/icon-types.js";import RegularEvent from"@typo3/core/event/regular-event.js";var Identifiers;!function(e){e.containerSelector="#typo3-cms-backend-backend-toolbaritems-shortcuttoolbaritem",e.toolbarIconSelector=".dropdown-toggle span.icon",e.toolbarMenuSelector=".dropdown-menu",e.shortcutItemSelector=".t3js-topbar-shortcut",e.shortcutJumpSelector=".t3js-shortcut-jump",e.shortcutDeleteSelector=".t3js-shortcut-delete",e.shortcutEditSelector=".t3js-shortcut-edit",e.shortcutFormSelector=".t3js-shortcut-form"}(Identifiers||(Identifiers={}));class ShortcutMenu{constructor(){this.initializeEvents=()=>{const e=document.querySelector(Identifiers.containerSelector);new RegularEvent("click",((e,t)=>{e.preventDefault(),e.stopImmediatePropagation();const o=t.closest(Identifiers.shortcutItemSelector);this.deleteShortcut(parseInt(o.dataset.shortcutid,10))})).delegateTo(e,Identifiers.shortcutDeleteSelector),new RegularEvent("click",((e,t)=>{e.preventDefault(),e.stopImmediatePropagation();const o=t.closest(Identifiers.shortcutItemSelector);this.editShortcut(parseInt(o.dataset.shortcutid,10),o.dataset.shortcutgroup)})).delegateTo(e,Identifiers.shortcutEditSelector),new RegularEvent("submit",((e,t)=>{e.preventDefault(),e.stopImmediatePropagation(),this.saveShortcutForm(t)})).delegateTo(e,Identifiers.shortcutFormSelector),new RegularEvent("reset",(e=>{e.preventDefault(),e.stopImmediatePropagation(),this.refreshMenu()})).delegateTo(e,Identifiers.shortcutFormSelector),new RegularEvent("click",((e,t)=>{e.preventDefault();const o=t.dataset.pageId;o&&ModuleStateStorage.updateWithCurrentMount("web",o,!0);const r=document.querySelector("typo3-backend-module-router");r.setAttribute("endpoint",t.href),r.setAttribute("module",t.dataset.module)})).delegateTo(e,Identifiers.shortcutJumpSelector)},Viewport.Topbar.Toolbar.registerEvent(this.initializeEvents)}createShortcut(e,t,o,r,s){new AjaxRequest(TYPO3.settings.ajaxUrls.shortcut_create).post({routeIdentifier:e,arguments:t,displayName:o}).then((async e=>{const t=await e.resolve();if("success"===t.result?Notification.success(TYPO3.lang["bookmark.savedTitle"],TYPO3.lang["bookmark.savedMessage"]):"alreadyExists"===t.result&&Notification.info(TYPO3.lang["bookmark.alreadyExistsTitle"],TYPO3.lang["bookmark.alreadyExistsMessage"]),this.refreshMenu(),"object"!=typeof s)return void console.warn("Expected argument shortcutButton to be an object, got "+typeof s);const o=s.classList.contains("dropdown-item"),r=new SecurityUtility;Icons.getIcon("actions-system-shortcut-active",Icons.sizes.small).then((e=>{s.innerHTML=e+(o?" "+r.encodeHtml(TYPO3.lang["labels.alreadyBookmarked"]):"")})),s.classList.add(o?"disabled":"active"),s.dataset.dispatchDisabled="disabled",s.title=r.encodeHtml(TYPO3.lang["labels.alreadyBookmarked"])})).catch((()=>{Notification.error(TYPO3.lang["bookmark.failedTitle"],TYPO3.lang["bookmark.failedMessage"])}))}deleteShortcut(e){const t=Modal.confirm(TYPO3.lang["bookmark.delete"],TYPO3.lang["bookmark.confirmDelete"]);t.addEventListener("confirm.button.ok",(()=>{new AjaxRequest(TYPO3.settings.ajaxUrls.shortcut_remove).post({shortcutId:e}).then((()=>{this.refreshMenu()})),t.hideModal()})),t.addEventListener("confirm.button.cancel",(()=>{t.hideModal()}))}editShortcut(e,t){new AjaxRequest(TYPO3.settings.ajaxUrls.shortcut_editform).withQueryArguments({shortcutId:e,shortcutGroup:t}).get({cache:"no-cache"}).then((async e=>{document.querySelector(Identifiers.containerSelector+" "+Identifiers.toolbarMenuSelector).innerHTML=await e.resolve()}))}saveShortcutForm(e){new AjaxRequest(TYPO3.settings.ajaxUrls.shortcut_saveform).post(new FormData(e)).then((()=>{Notification.success(TYPO3.lang["bookmark.savedTitle"],TYPO3.lang["bookmark.savedMessage"]),this.refreshMenu()}))}refreshMenu(){const e=document.querySelector(Identifiers.containerSelector+" "+Identifiers.toolbarIconSelector),t=e.cloneNode(!0),o=document.createElement("typo3-backend-spinner");o.setAttribute("size",Sizes.small),e.replaceWith(o),new AjaxRequest(TYPO3.settings.ajaxUrls.shortcut_list).get({cache:"no-cache"}).then((async e=>{document.querySelector(Identifiers.containerSelector+" "+Identifiers.toolbarMenuSelector).innerHTML=await e.resolve()})).finally((()=>{document.querySelector(Identifiers.containerSelector+" typo3-backend-spinner").replaceWith(t)}))}}top.TYPO3.ShortcutMenu&&"object"==typeof top.TYPO3.ShortcutMenu||(top.TYPO3.ShortcutMenu=new ShortcutMenu);const shortcutMenuObject=top.TYPO3.ShortcutMenu;export default shortcutMenuObject;
\ No newline at end of file
diff --git a/typo3/sysext/core/Tests/Acceptance/Application/Topbar/BookmarkCest.php b/typo3/sysext/core/Tests/Acceptance/Application/Topbar/BookmarkCest.php
index 88a410c7d519..05b44b727cc1 100644
--- a/typo3/sysext/core/Tests/Acceptance/Application/Topbar/BookmarkCest.php
+++ b/typo3/sysext/core/Tests/Acceptance/Application/Topbar/BookmarkCest.php
@@ -92,8 +92,8 @@ final class BookmarkCest
         $firstShortcutSelector = self::$topBarModuleSelector . ' .t3js-topbar-shortcut';
         $I->click('.t3js-shortcut-edit', $firstShortcutSelector);
         $secondShortcutSelector = self::$topBarModuleSelector . ' form.t3js-shortcut-form';
-        $I->fillField($secondShortcutSelector . ' input[name="shortcut-title"]', 'Scheduled tasks renamed');
-        $I->click('.t3js-shortcut-form-save', $secondShortcutSelector);
+        $I->fillField($secondShortcutSelector . ' input[name="shortcutTitle"]', 'Scheduled tasks renamed');
+        $I->click('input[type="submit"]', $secondShortcutSelector);
 
         // searching in a specific context fails with an "Stale Element Reference Exception"
         // see http://docs.seleniumhq.org/exceptions/stale_element_reference.jsp
-- 
GitLab