Replace KeyboardShortcutMixin by addShortcut() util in 3 dialogs

Google-Bug-Id: b/199305453
Change-Id: Id5e1ece4e5eb8cbaa6bc627c05d2bd61c39b6d6d
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.ts b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.ts
index 07da53f..df537e0 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.ts
+++ b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.ts
@@ -19,9 +19,9 @@
 import '../../../styles/shared-styles';
 import {PolymerElement} from '@polymer/polymer/polymer-element';
 import {htmlTemplate} from './gr-confirm-abandon-dialog_html';
-import {KeyboardShortcutMixin} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
 import {customElement, property} from '@polymer/decorators';
 import {IronAutogrowTextareaElement} from '@polymer/iron-autogrow-textarea/iron-autogrow-textarea';
+import {addShortcut, Key, Modifier} from '../../../utils/dom-util';
 
 export interface GrConfirmAbandonDialog {
   $: {
@@ -35,11 +35,8 @@
   }
 }
 
-// This avoids JSC_DYNAMIC_EXTENDS_WITHOUT_JSDOC closure compiler error.
-const base = KeyboardShortcutMixin(PolymerElement);
-
 @customElement('gr-confirm-abandon-dialog')
-export class GrConfirmAbandonDialog extends base {
+export class GrConfirmAbandonDialog extends PolymerElement {
   static get template() {
     return htmlTemplate;
   }
@@ -59,20 +56,33 @@
   @property({type: String})
   message = '';
 
-  get keyBindings() {
-    return {
-      'ctrl+enter meta+enter': '_handleEnterKey',
-    };
+  /** Called in disconnectedCallback. */
+  private cleanups: (() => void)[] = [];
+
+  override disconnectedCallback() {
+    super.disconnectedCallback();
+    for (const cleanup of this.cleanups) cleanup();
+    this.cleanups = [];
+  }
+
+  override connectedCallback() {
+    super.connectedCallback();
+    this.cleanups.push(
+      addShortcut(this, {key: Key.ENTER, modifiers: [Modifier.CTRL_KEY]}, _ =>
+        this._confirm()
+      )
+    );
+    this.cleanups.push(
+      addShortcut(this, {key: Key.ENTER, modifiers: [Modifier.META_KEY]}, _ =>
+        this._confirm()
+      )
+    );
   }
 
   resetFocus() {
     this.$.messageInput.textarea.focus();
   }
 
-  _handleEnterKey() {
-    this._confirm();
-  }
-
   _handleConfirmTap(e: Event) {
     e.preventDefault();
     e.stopPropagation();
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.ts b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.ts
index 8e6521d..b3bbc8a 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.ts
+++ b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.ts
@@ -19,17 +19,14 @@
 import '../../shared/gr-dialog/gr-dialog';
 import {PolymerElement} from '@polymer/polymer/polymer-element';
 import {htmlTemplate} from './gr-confirm-move-dialog_html';
-import {KeyboardShortcutMixin} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
 import {customElement, property} from '@polymer/decorators';
 import {BranchName, RepoName} from '../../../types/common';
 import {appContext} from '../../../services/app-context';
 import {GrTypedAutocomplete} from '../../shared/gr-autocomplete/gr-autocomplete';
+import {addShortcut, Key, Modifier} from '../../../utils/dom-util';
 
 const SUGGESTIONS_LIMIT = 15;
 
-// This avoids JSC_DYNAMIC_EXTENDS_WITHOUT_JSDOC closure compiler error.
-const base = KeyboardShortcutMixin(PolymerElement);
-
 // This is used to make sure 'branch'
 // can be typed as BranchName.
 export interface GrConfirmMoveDialog {
@@ -39,7 +36,7 @@
 }
 
 @customElement('gr-confirm-move-dialog')
-export class GrConfirmMoveDialog extends base {
+export class GrConfirmMoveDialog extends PolymerElement {
   static get template() {
     return htmlTemplate;
   }
@@ -68,10 +65,27 @@
   @property({type: Object})
   _query?: (input: string) => Promise<{name: BranchName}[]>;
 
-  get keyBindings() {
-    return {
-      'ctrl+enter meta+enter': '_handleConfirmTap',
-    };
+  /** Called in disconnectedCallback. */
+  private cleanups: (() => void)[] = [];
+
+  override disconnectedCallback() {
+    super.disconnectedCallback();
+    for (const cleanup of this.cleanups) cleanup();
+    this.cleanups = [];
+  }
+
+  override connectedCallback() {
+    super.connectedCallback();
+    this.cleanups.push(
+      addShortcut(this, {key: Key.ENTER, modifiers: [Modifier.CTRL_KEY]}, e =>
+        this._handleConfirmTap(e)
+      )
+    );
+    this.cleanups.push(
+      addShortcut(this, {key: Key.ENTER, modifiers: [Modifier.META_KEY]}, e =>
+        this._handleConfirmTap(e)
+      )
+    );
   }
 
   private readonly restApiService = appContext.restApiService;
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts
index 17036e6..be57ef6 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts
@@ -38,7 +38,6 @@
   ReviewerState,
   SpecialFilePath,
 } from '../../../constants/constants';
-import {KeyboardShortcutMixin} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
 import {
   accountOrGroupKey,
   isReviewerOrCC,
@@ -116,6 +115,7 @@
 import {StorageLocation} from '../../../services/storage/gr-storage';
 import {Interaction, Timing} from '../../../constants/reporting';
 import {getReplyByReason} from '../../../utils/attention-set-util';
+import {addShortcut, Key, Modifier} from '../../../utils/dom-util';
 
 const STORAGE_DEBOUNCE_INTERVAL_MS = 400;
 
@@ -163,11 +163,8 @@
   };
 }
 
-// This avoids JSC_DYNAMIC_EXTENDS_WITHOUT_JSDOC closure compiler error.
-const base = KeyboardShortcutMixin(PolymerElement);
-
 @customElement('gr-reply-dialog')
-export class GrReplyDialog extends base {
+export class GrReplyDialog extends PolymerElement {
   static get template() {
     return htmlTemplate;
   }
@@ -368,12 +365,8 @@
 
   private storeTask?: DelayedTask;
 
-  get keyBindings() {
-    return {
-      esc: '_handleEscKey',
-      'ctrl+enter meta+enter': '_handleEnterKey',
-    };
-  }
+  /** Called in disconnectedCallback. */
+  private cleanups: (() => void)[] = [];
 
   constructor() {
     super();
@@ -391,6 +384,17 @@
       if (account) this._account = account;
     });
 
+    this.cleanups.push(
+      addShortcut(this, {key: Key.ENTER, modifiers: [Modifier.CTRL_KEY]}, _ =>
+        this._submit()
+      )
+    );
+    this.cleanups.push(
+      addShortcut(this, {key: Key.ENTER, modifiers: [Modifier.META_KEY]}, _ =>
+        this._submit()
+      )
+    );
+    this.cleanups.push(addShortcut(this, {key: Key.ESC}, _ => this.cancel()));
     this.addEventListener('comment-editing-changed', e => {
       this._commentEditing = (e as CustomEvent).detail;
     });
@@ -418,6 +422,8 @@
 
   override disconnectedCallback() {
     this.storeTask?.cancel();
+    for (const cleanup of this.cleanups) cleanup();
+    this.cleanups = [];
     super.disconnectedCallback();
   }
 
@@ -492,14 +498,6 @@
     return (selectorEl as GrLabelScoreRow).selectedValue;
   }
 
-  _handleEscKey() {
-    this.cancel();
-  }
-
-  _handleEnterKey() {
-    this._submit();
-  }
-
   @observe('_ccs.splices')
   _ccsChanged(splices: PolymerSpliceChange<AccountInfo[] | GroupInfo[]>) {
     this._reviewerTypeChanged(splices, ReviewerType.CC);
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.ts b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.ts
index e57ffc7..91b8603 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.ts
@@ -1799,7 +1799,7 @@
   test('emits cancel on esc key', () => {
     const cancelHandler = sinon.spy();
     element.addEventListener('cancel', cancelHandler);
-    pressAndReleaseKeyOn(element, 27, null, 'esc');
+    pressAndReleaseKeyOn(element, 27, null, 'Escape');
     flush();
 
     assert.isTrue(cancelHandler.called);
@@ -1808,14 +1808,14 @@
   test('should not send on enter key', () => {
     stubSaveReview(() => undefined);
     element.addEventListener('send', () => assert.fail('wrongly called'));
-    pressAndReleaseKeyOn(element, 13, null, 'enter');
+    pressAndReleaseKeyOn(element, 13, null, 'Enter');
   });
 
   test('emit send on ctrl+enter key', async () => {
     stubSaveReview(() => undefined);
     const promise = mockPromise();
     element.addEventListener('send', () => promise.resolve());
-    pressAndReleaseKeyOn(element, 13, 'ctrl', 'enter');
+    pressAndReleaseKeyOn(element, 13, 'ctrl', 'Enter');
     await promise;
   });
 
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
index e7417f4..deb45d3 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
@@ -70,7 +70,7 @@
 import {anyLineTooLong} from '../../diff/gr-diff/gr-diff-utils';
 import {getUserName} from '../../../utils/display-name-util';
 import {generateAbsoluteUrl} from '../../../utils/url-util';
-import {addShortcut} from '../../../utils/dom-util';
+import {addGlobalShortcut} from '../../../utils/dom-util';
 
 const UNRESOLVED_EXPAND_COUNT = 5;
 const NEWLINE_PATTERN = /\n/g;
@@ -239,10 +239,10 @@
   override connectedCallback() {
     super.connectedCallback();
     this.cleanups.push(
-      addShortcut({key: 'e'}, e => this.handleExpandShortcut(e))
+      addGlobalShortcut({key: 'e'}, e => this.handleExpandShortcut(e))
     );
     this.cleanups.push(
-      addShortcut({key: 'E'}, e => this.handleCollapseShortcut(e))
+      addGlobalShortcut({key: 'E'}, e => this.handleCollapseShortcut(e))
     );
     this._getLoggedIn().then(loggedIn => {
       this._showActions = loggedIn;
diff --git a/polygerrit-ui/app/utils/dom-util.ts b/polygerrit-ui/app/utils/dom-util.ts
index cdcadb0..a7ad6e1 100644
--- a/polygerrit-ui/app/utils/dom-util.ts
+++ b/polygerrit-ui/app/utils/dom-util.ts
@@ -379,7 +379,15 @@
   return true;
 }
 
+export function addGlobalShortcut(
+  shortcut: Shortcut,
+  listener: (e: KeyboardEvent) => void
+) {
+  return addShortcut(document.body, shortcut, listener);
+}
+
 export function addShortcut(
+  element: HTMLElement,
   shortcut: Shortcut,
   listener: (e: KeyboardEvent) => void
 ) {
@@ -387,8 +395,8 @@
     if (e.repeat) return;
     if (eventMatchesShortcut(e, shortcut)) listener(e);
   };
-  document.addEventListener('keydown', wrappedListener);
-  return () => document.removeEventListener('keydown', wrappedListener);
+  element.addEventListener('keydown', wrappedListener);
+  return () => element.removeEventListener('keydown', wrappedListener);
 }
 
 export function modifierPressed(e: KeyboardEvent) {