Merge changes Ife0bef08,I3c3d5da1

* changes:
  Convert some helper classes to TypeScript
  Rename files from js to ts to preserve history
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-constants.js b/polygerrit-ui/app/elements/change/gr-file-list-constants.ts
similarity index 99%
rename from polygerrit-ui/app/elements/change/gr-file-list-constants.js
rename to polygerrit-ui/app/elements/change/gr-file-list-constants.ts
index 5bba786..7c07e68 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-constants.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list-constants.ts
@@ -22,4 +22,3 @@
     SOME: 'some',
   },
 };
-
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
index 635336f..342d5bb 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
@@ -334,7 +334,7 @@
   }
 
   _computeExpandAllTitle(_expandAllState) {
-    if (_expandAllState === ExpandAllState.COLLAPSED_ALL) {
+    if (_expandAllState === ExpandAllState.COLLAPSE_ALL) {
       return this.createTitle(
           Shortcut.COLLAPSE_ALL_MESSAGES, ShortcutSection.ACTIONS);
     }
diff --git a/polygerrit-ui/app/elements/shared/gr-count-string-formatter/gr-count-string-formatter.js b/polygerrit-ui/app/elements/shared/gr-count-string-formatter/gr-count-string-formatter.ts
similarity index 72%
rename from polygerrit-ui/app/elements/shared/gr-count-string-formatter/gr-count-string-formatter.js
rename to polygerrit-ui/app/elements/shared/gr-count-string-formatter/gr-count-string-formatter.ts
index 1c3a689..bbbce16 100644
--- a/polygerrit-ui/app/elements/shared/gr-count-string-formatter/gr-count-string-formatter.js
+++ b/polygerrit-ui/app/elements/shared/gr-count-string-formatter/gr-count-string-formatter.ts
@@ -17,40 +17,28 @@
 export const GrCountStringFormatter = {
   /**
    * Returns a count plus string that is pluralized when necessary.
-   *
-   * @param {number} count
-   * @param {string} noun
-   * @return {string}
    */
-  computePluralString(count, noun) {
+  computePluralString(count: number, noun: string): string {
     return this.computeString(count, noun) + (count > 1 ? 's' : '');
   },
 
   /**
    * Returns a count plus string that is not pluralized.
-   *
-   * @param {number} count
-   * @param {string} noun
-   * @return {string}
    */
-  computeString(count, noun) {
+  computeString(count: number, noun: string): string {
     if (count === 0) {
       return '';
     }
-    return count + ' ' + noun;
+    return `${count} ${noun}`;
   },
 
   /**
    * Returns a count plus arbitrary text.
-   *
-   * @param {number} count
-   * @param {string} text
-   * @return {string}
    */
-  computeShortString(count, text) {
+  computeShortString(count: number, text: string): string {
     if (count === 0) {
       return '';
     }
-    return count + text;
+    return `${count}${text}`;
   },
 };
diff --git a/polygerrit-ui/app/mixins/gr-tooltip-mixin/gr-tooltip-mixin.ts b/polygerrit-ui/app/mixins/gr-tooltip-mixin/gr-tooltip-mixin.ts
index a361e2e..516877e 100644
--- a/polygerrit-ui/app/mixins/gr-tooltip-mixin/gr-tooltip-mixin.ts
+++ b/polygerrit-ui/app/mixins/gr-tooltip-mixin/gr-tooltip-mixin.ts
@@ -19,18 +19,30 @@
 import {getRootElement} from '../../scripts/rootElement';
 import {property, observe} from '@polymer/decorators';
 import {dedupingMixin} from '@polymer/polymer/lib/utils/mixin';
-import {PolymerElementConstructor} from '@polymer/polymer/interfaces';
-import {ElementMixinConstructor} from '@polymer/polymer/lib/mixins/element-mixin';
 import {GrTooltip} from '../../elements/shared/gr-tooltip/gr-tooltip';
+import {PolymerElement} from '@polymer/polymer';
+import {Constructor} from '../../utils/common-util';
 
 const BOTTOM_OFFSET = 7.2; // Height of the arrow in tooltip.
 
+/** The interface corresponding to TooltipMixin */
+export interface TooltipMixinInterface {
+  hasTooltip: boolean;
+  positionBelow: boolean;
+  _isTouchDevice: boolean;
+  _tooltip: GrTooltip | null;
+  _titleText: string;
+  _hasSetupTooltipListeners: boolean;
+}
+
 /**
  * @polymer
  * @mixinFunction
  */
 export const TooltipMixin = dedupingMixin(
-  (superClass: PolymerElementConstructor & ElementMixinConstructor) => {
+  <T extends Constructor<PolymerElement>>(
+    superClass: T
+  ): T & Constructor<TooltipMixinInterface> => {
     /**
      * @polymer
      * @mixinClass
@@ -66,7 +78,8 @@
       // Hanlder for hiding the tooltip, will be attached to certain events
       private readonly hideHandler: () => void;
 
-      constructor() {
+      // tslint:disable-next-line:no-any Required for constructor signature.
+      constructor(..._: any[]) {
         super();
         this.windowScrollHandler = () => this._handleWindowScroll();
         this.showHandler = () => this._handleShowTooltip();
diff --git a/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.js b/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.js
index 75b0b00..4c813b5 100644
--- a/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.js
+++ b/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.js
@@ -392,8 +392,10 @@
   }
 
   getShortcut(shortcutName) {
-    const binding = this.bindings.get(shortcutName);
-    return binding ? this.describeBinding(binding) : '';
+    const bindings = this.bindings.get(shortcutName);
+    return bindings
+      ? bindings.map(binding => this.describeBinding(binding)).join(', ')
+      : '';
   }
 
   activeShortcutsBySection() {
diff --git a/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin_test.js b/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin_test.js
index 3ab7b6d..534ead4 100644
--- a/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin_test.js
+++ b/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin_test.js
@@ -28,7 +28,7 @@
 
 const withinOverlayFixture = fixtureFromTemplate(html`
 <gr-overlay>
-  <keyboard-shortcut-mixin-test-element>      
+  <keyboard-shortcut-mixin-test-element>
   </keyboard-shortcut-mixin-test-element>
 </gr-overlay>
 `);
@@ -73,6 +73,15 @@
           [']', '}', 'right']);
     });
 
+    test('getShortcut', () => {
+      const mgr = new ShortcutManager();
+      const NEXT_FILE = Shortcut.NEXT_FILE;
+
+      assert.isUndefined(mgr.getBindingsForShortcut(NEXT_FILE));
+      mgr.bindShortcut(NEXT_FILE, ']', '}', 'right');
+      assert.equal(mgr.getShortcut(NEXT_FILE), '], }, →');
+    });
+
     suite('binding descriptions', () => {
       function mapToObject(m) {
         const o = {};
diff --git a/polygerrit-ui/app/scripts/hiddenscroll.js b/polygerrit-ui/app/scripts/hiddenscroll.ts
similarity index 83%
rename from polygerrit-ui/app/scripts/hiddenscroll.js
rename to polygerrit-ui/app/scripts/hiddenscroll.ts
index a580b05..b4364be 100644
--- a/polygerrit-ui/app/scripts/hiddenscroll.js
+++ b/polygerrit-ui/app/scripts/hiddenscroll.ts
@@ -15,18 +15,17 @@
  * limitations under the License.
  */
 
-let hiddenscroll = undefined;
+let hiddenscroll: boolean | undefined = undefined;
 
 window.addEventListener('WebComponentsReady', () => {
   const elem = document.createElement('div');
-  elem.setAttribute(
-      'style', 'width:100px;height:100px;overflow:scroll');
+  elem.setAttribute('style', 'width:100px;height:100px;overflow:scroll');
   document.body.appendChild(elem);
   hiddenscroll = elem.offsetWidth === elem.clientWidth;
   elem.remove();
 });
 
-export function _setHiddenScroll(value) {
+export function _setHiddenScroll(value: boolean) {
   hiddenscroll = value;
 }
 
diff --git a/polygerrit-ui/app/utils/access-util.js b/polygerrit-ui/app/utils/access-util.ts
similarity index 87%
rename from polygerrit-ui/app/utils/access-util.js
rename to polygerrit-ui/app/utils/access-util.ts
index 2578cfa..179fe74 100644
--- a/polygerrit-ui/app/utils/access-util.js
+++ b/polygerrit-ui/app/utils/access-util.ts
@@ -122,22 +122,30 @@
   },
 };
 
+interface AccessPermission {
+  id: string;
+  name: string;
+}
+
 /**
- * @param {!Object} obj
  * @return {!Array} returns a sorted array sorted by the id of the original
  *    object.
  */
-export function toSortedPermissionsArray(obj) {
-  if (!obj) { return []; }
+export function toSortedPermissionsArray(
+  obj: Record<string, AccessPermission>
+) {
+  if (!obj) {
+    return [];
+  }
   return Object.keys(obj)
-      .map(key => {
-        return {
-          id: key,
-          value: obj[key],
-        };
-      })
-      .sort((a, b) =>
-        // Since IDs are strings, use localeCompare.
-        a.id.localeCompare(b.id)
-      );
+    .map(key => {
+      return {
+        id: key,
+        value: obj[key],
+      };
+    })
+    .sort((a, b) =>
+      // Since IDs are strings, use localeCompare.
+      a.id.localeCompare(b.id)
+    );
 }
diff --git a/polygerrit-ui/app/utils/common-util.ts b/polygerrit-ui/app/utils/common-util.ts
index a7aca4c..ba8784f 100644
--- a/polygerrit-ui/app/utils/common-util.ts
+++ b/polygerrit-ui/app/utils/common-util.ts
@@ -31,3 +31,7 @@
   // Typescript rules don't allow to use obj.hasOwnProperty directly
   return Object.prototype.hasOwnProperty.call(obj, prop);
 }
+
+// TODO(TS): move to common types once we have type utils
+// tslint:disable-next-line:no-any Required for constructor signature.
+export type Constructor<T> = new (...args: any[]) => T;