Merge "gr-copy-clipboard to lit"
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts
index 5b940a8..c6975e6 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts
@@ -344,7 +344,7 @@
           <span class="headerSubject">[[_change.subject]]</span>
           <gr-copy-clipboard
             class="changeCopyClipboard"
-            hide-input=""
+            hideInput=""
             text="[[_computeCopyTextForTitle(_change)]]"
           >
           </gr-copy-clipboard>
diff --git a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_html.ts b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_html.ts
index 65ca8b5..02fa090 100644
--- a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_html.ts
@@ -31,9 +31,9 @@
       >[[_computeShortHash(change, commitInfo, serverConfig)]]</a
     >
     <gr-copy-clipboard
-      has-tooltip=""
-      button-title="Copy full SHA to clipboard"
-      hide-input=""
+      hasTooltip=""
+      buttonTitle="Copy full SHA to clipboard"
+      hideInput=""
       text="[[commitInfo.commit]]"
     >
     </gr-copy-clipboard>
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_html.ts b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_html.ts
index 3b2cacf..4d04744 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_html.ts
@@ -410,7 +410,7 @@
               </span>
               <gr-file-status-chip file="[[file]]"></gr-file-status-chip>
               <gr-copy-clipboard
-                hide-input=""
+                hideInput=""
                 text="[[file.__path]]"
               ></gr-copy-clipboard>
             </a>
@@ -418,7 +418,7 @@
               <div class="oldPath" title$="[[file.old_path]]">
                 [[file.old_path]]
                 <gr-copy-clipboard
-                  hide-input=""
+                  hideInput=""
                   text="[[file.old_path]]"
                 ></gr-copy-clipboard>
               </div>
diff --git a/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_html.ts b/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_html.ts
index ab24168..f4641c2 100644
--- a/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_html.ts
+++ b/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_html.ts
@@ -70,9 +70,9 @@
               </td>
               <td>
                 <gr-copy-clipboard
-                  has-tooltip=""
-                  button-title="Copy GPG public key to clipboard"
-                  hide-input=""
+                  hasTooltip=""
+                  buttonTitle="Copy GPG public key to clipboard"
+                  hideInput=""
                   text="[[key.key]]"
                 >
                 </gr-copy-clipboard>
diff --git a/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_html.ts b/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_html.ts
index 549fc93..811b85c 100644
--- a/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_html.ts
+++ b/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_html.ts
@@ -78,9 +78,9 @@
         <span class="title">New Password:</span>
         <span class="value">[[_generatedPassword]]</span>
         <gr-copy-clipboard
-          has-tooltip=""
-          button-title="Copy password to clipboard"
-          hide-input=""
+          hasTooltip=""
+          buttonTitle="Copy password to clipboard"
+          hideInput=""
           text="[[_generatedPassword]]"
         >
         </gr-copy-clipboard>
diff --git a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_html.ts b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_html.ts
index 0bee1d3..e853b58 100644
--- a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_html.ts
+++ b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_html.ts
@@ -76,9 +76,9 @@
               </td>
               <td>
                 <gr-copy-clipboard
-                  has-tooltip=""
-                  button-title="Copy SSH public key to clipboard"
-                  hide-input=""
+                  hasTooltip=""
+                  buttonTitle="Copy SSH public key to clipboard"
+                  hideInput=""
                   text="[[key.ssh_public_key]]"
                 >
                 </gr-copy-clipboard>
diff --git a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.ts b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.ts
index 2fe6fed..4a2bcee 100644
--- a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.ts
+++ b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.ts
@@ -15,16 +15,14 @@
  * limitations under the License.
  */
 import '@polymer/iron-input/iron-input';
-import '../../../styles/shared-styles';
 import '../gr-button/gr-button';
 import '../gr-icons/gr-icons';
-import {dom, EventApi} from '@polymer/polymer/lib/legacy/polymer.dom';
-import {PolymerElement} from '@polymer/polymer/polymer-element';
-import {htmlTemplate} from './gr-copy-clipboard_html';
-import {GrButton} from '../gr-button/gr-button';
-import {customElement, property} from '@polymer/decorators';
 import {IronIconElement} from '@polymer/iron-icon';
-import {assertIsDefined} from '../../../utils/common-util';
+import {assertIsDefined, queryAndAssert} from '../../../utils/common-util';
+import {classMap} from 'lit-html/directives/class-map';
+import {css, customElement, html, property} from 'lit-element';
+import {GrLitElement} from '../../lit/gr-lit-element';
+import {GrButton} from '../gr-button/gr-button';
 
 const COPY_TIMEOUT_MS = 1000;
 
@@ -33,17 +31,8 @@
     'gr-copy-clipboard': GrCopyClipboard;
   }
 }
-
-export interface GrCopyClipboard {
-  $: {button: GrButton; icon: IronIconElement; input: HTMLInputElement};
-}
-
 @customElement('gr-copy-clipboard')
-export class GrCopyClipboard extends PolymerElement {
-  static get template() {
-    return htmlTemplate;
-  }
-
+export class GrCopyClipboard extends GrLitElement {
   @property({type: String})
   text: string | undefined;
 
@@ -56,29 +45,121 @@
   @property({type: Boolean})
   hideInput = false;
 
-  focusOnCopy() {
-    this.$.button.focus();
+  static get styles() {
+    return [
+      css`
+        .text {
+          align-items: center;
+          display: flex;
+          flex-wrap: wrap;
+        }
+        .copyText {
+          flex-grow: 1;
+          margin-right: var(--spacing-s);
+        }
+        .hideInput {
+          display: none;
+        }
+        input#input {
+          font-family: var(--monospace-font-family);
+          font-size: var(--font-size-mono);
+          line-height: var(--line-height-mono);
+          width: 100%;
+        }
+        /*
+         * Typically icons are 20px, which is the normal line-height.
+         * The copy icon is too prominent at 20px, so we choose 16px
+         * here, but add 2x2px padding below, so the entire
+         * component should still fit nicely into a normal inline
+         * layout flow.
+         */
+        #icon {
+          height: 16px;
+          width: 16px;
+        }
+        iron-icon {
+          color: var(--deemphasized-text-color);
+          vertical-align: top;
+        }
+      `,
+    ];
   }
 
-  _computeInputClass(hideInput: boolean) {
-    return hideInput ? 'hideInput' : '';
+  render() {
+    // To pass CSS mixins for @apply to Polymer components, they need to appear
+    // in <style> inside the template.
+    const customStyle = html`
+      <style>
+        iron-icon {
+          --iron-icon-height: 20px;
+          --iron-icon-width: 20px;
+        }
+        gr-button {
+          --gr-button: {
+            padding: 2px;
+          }
+        }
+      </style>
+    `;
+    return html`${customStyle}
+      <div class="text">
+        <iron-input
+          class="copyText"
+          type="text"
+          @click="${this._handleInputClick}"
+          readonly=""
+          bind-value=${this.text}
+        >
+          <input
+            id="input"
+            is="iron-input"
+            class="${classMap({hideInput: this.hideInput})}"
+            type="text"
+            @click="${this._handleInputClick}"
+            readonly=""
+            .value=${this.text}
+            part="text-container-style"
+          />
+        </iron-input>
+        <gr-button
+          id="copy-clipboard-button"
+          link=""
+          ?has-tooltip=${this.hasTooltip}
+          class="copyToClipboard"
+          title="${this.buttonTitle}"
+          @click="${this._copyToClipboard}"
+          aria-label="Click to copy to clipboard"
+        >
+          <iron-icon id="icon" icon="gr-icons:content-copy"></iron-icon>
+        </gr-button>
+      </div> `;
+  }
+
+  focusOnCopy() {
+    queryAndAssert<GrButton>(this, '#copy-clipboard-button').focus();
   }
 
   _handleInputClick(e: MouseEvent) {
     e.preventDefault();
-    ((dom(e) as EventApi).rootTarget as HTMLInputElement).select();
+    const rootTarget = e.composedPath()[0];
+    (rootTarget as HTMLInputElement).select();
   }
 
   _copyToClipboard(e: MouseEvent) {
     e.preventDefault();
     e.stopPropagation();
 
+    this.text = queryAndAssert<HTMLInputElement>(this, '#input').value;
     assertIsDefined(this.text, 'text');
-    this.$.icon.icon = 'gr-icons:check';
+    this.iconEl.icon = 'gr-icons:check';
     navigator.clipboard.writeText(this.text);
     setTimeout(
-      () => (this.$.icon.icon = 'gr-icons:content-copy'),
+      () => (this.iconEl.icon = 'gr-icons:content-copy'),
       COPY_TIMEOUT_MS
     );
   }
+
+  private get iconEl(): IronIconElement {
+    return queryAndAssert<IronIconElement>(this, '#icon');
+  }
 }
diff --git a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_html.ts b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_html.ts
deleted file mode 100644
index 3ccc46f..0000000
--- a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_html.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-/**
- * @license
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import {html} from '@polymer/polymer/lib/utils/html-tag';
-
-export const htmlTemplate = html`
-  <style>
-    .text {
-      align-items: center;
-      display: flex;
-      flex-wrap: wrap;
-    }
-    .copyText {
-      flex-grow: 1;
-      margin-right: var(--spacing-s);
-    }
-    .hideInput {
-      display: none;
-    }
-    input#input {
-      font-family: var(--monospace-font-family);
-      font-size: var(--font-size-mono);
-      line-height: var(--line-height-mono);
-      @apply --text-container-style;
-      width: 100%;
-    }
-    /*
-       * Typically icons are 20px, which is the normal line-height.
-       * The copy icon is too prominent at 20px, so we choose 16px
-       * here, but add 2x2px padding below, so the entire
-       * component should still fit nicely into a normal inline
-       * layout flow.
-       */
-    #icon {
-      height: 16px;
-      width: 16px;
-    }
-    iron-icon {
-      color: var(--deemphasized-text-color);
-      vertical-align: top;
-      --iron-icon-height: 20px;
-      --iron-icon-width: 20px;
-    }
-    gr-button {
-      --gr-button: {
-        padding: 2px;
-      }
-    }
-  </style>
-  <div class="text">
-    <iron-input
-      class="copyText"
-      type="text"
-      bind-value="[[text]]"
-      on-click="_handleInputClick"
-      readonly=""
-    >
-      <input
-        id="input"
-        is="iron-input"
-        class$="[[_computeInputClass(hideInput)]]"
-        type="text"
-        bind-value="[[text]]"
-        on-click="_handleInputClick"
-        readonly=""
-      />
-    </iron-input>
-    <gr-button
-      id="button"
-      link=""
-      has-tooltip="[[hasTooltip]]"
-      class="copyToClipboard"
-      title="[[buttonTitle]]"
-      on-click="_copyToClipboard"
-      aria-label="Click to copy to clipboard"
-    >
-      <iron-icon id="icon" icon="gr-icons:content-copy"></iron-icon>
-    </gr-button>
-  </div>
-`;
diff --git a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.js b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.js
index 55b2483..45847d7 100644
--- a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.js
@@ -17,7 +17,7 @@
 
 import '../../../test/common-test-setup-karma.js';
 import './gr-copy-clipboard.js';
-import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
+import {queryAndAssert} from '../../../test/test-utils.js';
 
 const basicFixture = fixtureFromElement('gr-copy-clipboard');
 
@@ -32,18 +32,18 @@
   });
 
   test('copy to clipboard', () => {
-    const clipboardSpy = sinon.spy(element, '_copyToClipboard');
+    const clipboardSpy = sinon.spy(navigator.clipboard, 'writeText');
     const copyBtn = element.shadowRoot
         .querySelector('.copyToClipboard');
-    MockInteractions.tap(copyBtn);
+    MockInteractions.click(copyBtn);
     assert.isTrue(clipboardSpy.called);
   });
 
   test('focusOnCopy', () => {
     element.focusOnCopy();
-    assert.deepEqual(dom(element.root).activeElement,
-        element.shadowRoot
-            .querySelector('.copyToClipboard'));
+    const activeElement = element.shadowRoot.activeElement;
+    const button = element.shadowRoot.querySelector('.copyToClipboard');
+    assert.deepEqual(activeElement, button);
   });
 
   test('_handleInputClick', () => {
@@ -58,16 +58,17 @@
     assert.equal(inputElement.selectionEnd, element.text.length - 1);
   });
 
-  test('hideInput', () => {
+  test('hideInput', async () => {
     // iron-input as parent should never be hidden as copy won't work
     // on nested hidden elements
     const ironInputElement = element.shadowRoot.querySelector('iron-input');
     assert.notEqual(getComputedStyle(ironInputElement).display, 'none');
 
-    assert.notEqual(getComputedStyle(element.$.input).display, 'none');
+    const input = queryAndAssert(element, 'input');
+    assert.notEqual(getComputedStyle(input).display, 'none');
     element.hideInput = true;
-    flush();
-    assert.equal(getComputedStyle(element.$.input).display, 'none');
+    await flush();
+    assert.equal(getComputedStyle(input).display, 'none');
   });
 
   test('stop events propagation', () => {
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_html.ts b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_html.ts
index c163924..18a46a0 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_html.ts
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_html.ts
@@ -127,7 +127,7 @@
     <span id="triggerText">[[text]]</span>
     <gr-copy-clipboard
       hidden="[[!showCopyForTriggerText]]"
-      hide-input=""
+      hideInput=""
       text="[[text]]"
     ></gr-copy-clipboard>
   </gr-button>
diff --git a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_html.ts b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_html.ts
index 6c7f0d8..e43460d 100644
--- a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_html.ts
+++ b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_html.ts
@@ -45,17 +45,15 @@
       /* Should roughly match the height of .commandContainer without padding. */
       line-height: 26px;
     }
-    .commandContainer gr-copy-clipboard {
-      --text-container-style: {
-        border: none;
-      }
+    .commandContainer gr-copy-clipboard::part(text-container-style) {
+      border: none;
     }
   </style>
   <label>[[label]]</label>
   <div class="commandContainer">
     <gr-copy-clipboard
       text="[[command]]"
-      has-tooltip
+      hasTooltip
       button-title="[[tooltip]]"
     ></gr-copy-clipboard>
   </div>