Bug 1550093 - Create CustomElement for copy-to-clipboard. r=MattN,Pike
authorJared Wein <jwein@mozilla.com>
Fri, 17 May 2019 18:26:01 +0000
changeset 533251 6bd4c5122a6a47b0b25090c334befd38404adf5d
parent 533250 c07f5967e0fc880bb9db128e98369f648a1884e2
child 533252 f764dd06add44c2077a245ec2c92a1d5668ba068
push id11276
push userrgurzau@mozilla.com
push dateMon, 20 May 2019 13:11:24 +0000
treeherdermozilla-beta@847755a7c325 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN, Pike
bugs1550093
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1550093 - Create CustomElement for copy-to-clipboard. r=MattN,Pike Differential Revision: https://phabricator.services.mozilla.com/D31519
browser/components/aboutlogins/content/aboutLogins.ftl
browser/components/aboutlogins/content/aboutLogins.html
browser/components/aboutlogins/content/components/copy-to-clipboard-button.css
browser/components/aboutlogins/content/components/copy-to-clipboard-button.js
browser/components/aboutlogins/content/components/login-item.js
browser/components/aboutlogins/jar.mn
--- a/browser/components/aboutlogins/content/aboutLogins.ftl
+++ b/browser/components/aboutlogins/content/aboutLogins.ftl
@@ -24,15 +24,19 @@ login-list =
 
 login-item =
   .cancel-button = Cancel
   .delete-button = Delete
   .edit-button = Edit
   .hostname-label = Website Address
   .modal-input-reveal-checkbox-hide = Hide password
   .modal-input-reveal-checkbox-show = Show password
+  .copied-password-button = Copied!
+  .copied-username-button = Copied!
+  .copy-password-button = Copy
+  .copy-username-button = Copy
   .open-site-button = Launch
   .password-label = Password
   .save-changes-button = Save Changes
   .time-created = Created: { DATETIME($timeCreated, day: "numeric", month: "long", year: "numeric") }
   .time-changed = Last changed: { DATETIME($timeChanged, day: "numeric", month: "long", year: "numeric") }
   .time-used = Last used: { DATETIME($timeUsed, day: "numeric", month: "long", year: "numeric") }
   .username-label = Username
--- a/browser/components/aboutlogins/content/aboutLogins.html
+++ b/browser/components/aboutlogins/content/aboutLogins.html
@@ -5,16 +5,17 @@
 <!DOCTYPE html>
 <html>
   <head>
     <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; connect-src https:; img-src https: data: blob:; style-src 'unsafe-inline';"/>
     <title data-l10n-id="about-logins-page-title"></title>
     <link rel="localization" href="browser/aboutLogins.ftl">
     <script defer="defer" src="chrome://browser/content/aboutlogins/components/reflected-fluent-element.js"></script>
+    <script defer="defer" src="chrome://browser/content/aboutlogins/components/copy-to-clipboard-button.js"></script>
     <script defer="defer" src="chrome://browser/content/aboutlogins/components/login-filter.js"></script>
     <script defer="defer" src="chrome://browser/content/aboutlogins/components/login-item.js"></script>
     <script defer="defer" src="chrome://browser/content/aboutlogins/components/login-list.js"></script>
     <script defer="defer" src="chrome://browser/content/aboutlogins/components/login-list-item.js"></script>
     <script defer="defer" src="chrome://browser/content/aboutlogins/components/modal-input.js"></script>
     <script defer="defer" src="chrome://browser/content/aboutlogins/aboutLogins.js"></script>
     <link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
     <link rel="stylesheet" href="chrome://browser/content/aboutlogins/aboutLogins.css">
@@ -26,16 +27,20 @@
                     data-l10n-attrs="placeholder"></login-filter>
     </header>
     <login-list data-l10n-id="login-list"
                 data-l10n-attrs="count"
                 data-l10n-args='{"count": 0}'></login-list>
     <login-item data-l10n-id="login-item"
                 data-l10n-args='{"timeCreated": 0, "timeChanged": 0, "timeUsed": 0}'
                 data-l10n-attrs="cancel-button,
+                                 copy-password-button,
+                                 copy-username-button,
+                                 copied-password-button,
+                                 copied-username-button,
                                  delete-button,
                                  edit-button,
                                  hostname-label,
                                  modal-input-reveal-checkbox-hide,
                                  modal-input-reveal-checkbox-show,
                                  open-site-button,
                                  password-label,
                                  save-changes-button,
@@ -76,22 +81,24 @@
         </label>
         <button class="open-site-button"></button>
       </div>
       <div class="detail-row">
         <label>
           <span class="username-label field-label"></span>
           <modal-input name="username"/>
         </label>
+        <copy-to-clipboard-button class="copy-username-button"></copy-to-clipboard-button>
       </div>
       <div class="detail-row">
         <label>
           <span class="password-label field-label"></span>
           <modal-input type="password" name="password"/>
         </label>
+        <copy-to-clipboard-button class="copy-password-button"></copy-to-clipboard-button>
       </div>
       <p class="time-created meta-info"></p>
       <p class="time-changed meta-info"></p>
       <p class="time-used meta-info"></p>
       <button class="save-changes-button"></button>
       <button class="cancel-button"></button>
     </template>
 
@@ -103,10 +110,19 @@
 
     <template id="modal-input-template">
       <link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
       <link rel="stylesheet" href="chrome://browser/content/aboutlogins/components/modal-input.css">
       <span class="locked-value"></span>
       <input type="text" class="unlocked-value"/>
       <input type="checkbox" class="reveal-checkbox"/>
     </template>
+
+    <template id="copy-to-clipboard-button-template">
+      <link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
+      <link rel="stylesheet" href="chrome://browser/content/aboutlogins/components/copy-to-clipboard-button.css">
+      <button class="copy-button">
+        <span class="copied-button-text"></span>
+        <span class="copy-button-text"></span>
+      </button>
+    </template>
   </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/content/components/copy-to-clipboard-button.css
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+:host(:not([copied])) .copied-button-text,
+:host([copied]) .copy-button-text {
+  display: none;
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/content/components/copy-to-clipboard-button.js
@@ -0,0 +1,47 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* globals ReflectedFluentElement */
+
+class CopyToClipboardButton extends ReflectedFluentElement {
+  connectedCallback() {
+    if (this.children.length) {
+      return;
+    }
+
+    let CopyToClipboardButtonTemplate = document.querySelector("#copy-to-clipboard-button-template");
+    this.attachShadow({mode: "open"})
+        .appendChild(CopyToClipboardButtonTemplate.content.cloneNode(true));
+
+    this.shadowRoot.querySelector(".copy-button").addEventListener("click", this);
+  }
+
+  static get reflectedFluentIDs() {
+    return ["copy-button-text", "copied-button-text"];
+  }
+
+  static get observedAttributes() {
+    return CopyToClipboardButton.reflectedFluentIDs;
+  }
+
+  handleSpecialCaseFluentString(attrName) {
+    if (attrName != "copied-button-text" &&
+        attrName != "copy-button-text") {
+      return false;
+    }
+
+    let span = this.shadowRoot.querySelector("." + attrName);
+    span.textContent = this.getAttribute(attrName);
+    return true;
+  }
+
+  handleEvent(event) {
+    if (event.type != "click") {
+      return;
+    }
+
+    this.setAttribute("copied", "");
+  }
+}
+customElements.define("copy-to-clipboard-button", CopyToClipboardButton);
--- a/browser/components/aboutlogins/content/components/login-item.js
+++ b/browser/components/aboutlogins/content/components/login-item.js
@@ -18,16 +18,18 @@ class LoginItem extends ReflectedFluentE
 
     let loginItemTemplate = document.querySelector("#login-item-template");
     this.attachShadow({mode: "open"})
         .appendChild(loginItemTemplate.content.cloneNode(true));
 
     this.reflectFluentStrings();
 
     for (let selector of [
+      ".copy-password-button",
+      ".copy-username-button",
       ".delete-button",
       ".edit-button",
       ".open-site-button",
       ".save-changes-button",
       ".cancel-button",
     ]) {
       let button = this.shadowRoot.querySelector(selector);
       button.addEventListener("click", this);
@@ -36,16 +38,20 @@ class LoginItem extends ReflectedFluentE
     window.addEventListener("AboutLoginsLoginSelected", this);
 
     this.render();
   }
 
   static get reflectedFluentIDs() {
     return [
       "cancel-button",
+      "copied-password-button",
+      "copied-username-button",
+      "copy-password-button",
+      "copy-username-button",
       "delete-button",
       "edit-button",
       "hostname-label",
       "modal-input-reveal-checkbox-hide",
       "modal-input-reveal-checkbox-show",
       "open-site-button",
       "password-label",
       "save-changes-button",
@@ -57,16 +63,30 @@ class LoginItem extends ReflectedFluentE
   }
 
   static get observedAttributes() {
     return this.reflectedFluentIDs;
   }
 
   handleSpecialCaseFluentString(attrName) {
     switch (attrName) {
+      case "copied-password-button":
+      case "copy-password-button": {
+        let copyPasswordButton = this.shadowRoot.querySelector(".copy-password-button");
+        let newAttrName = attrName.substr(0, attrName.indexOf("-")) + "-button-text";
+        copyPasswordButton.setAttribute(newAttrName, this.getAttribute(attrName));
+        break;
+      }
+      case "copied-username-button":
+      case "copy-username-button": {
+        let copyUsernameButton = this.shadowRoot.querySelector(".copy-username-button");
+        let newAttrName = attrName.substr(0, attrName.indexOf("-")) + "-button-text";
+        copyUsernameButton.setAttribute(newAttrName, this.getAttribute(attrName));
+        break;
+      }
       case "modal-input-reveal-checkbox-hide": {
         this.shadowRoot.querySelector("modal-input[name='password']")
                        .setAttribute("reveal-checkbox-hide", this.getAttribute(attrName));
         break;
       }
       case "modal-input-reveal-checkbox-show": {
         this.shadowRoot.querySelector("modal-input[name='password']")
                        .setAttribute("reveal-checkbox-show", this.getAttribute(attrName));
@@ -99,16 +119,22 @@ class LoginItem extends ReflectedFluentE
         break;
       }
       case "click": {
         if (event.target.classList.contains("cancel-button")) {
           this.toggleEditing();
           this.render();
           return;
         }
+        if (event.target.classList.contains("copy-password-button")) {
+          return;
+        }
+        if (event.target.classList.contains("copy-username-button")) {
+          return;
+        }
         if (event.target.classList.contains("delete-button")) {
           document.dispatchEvent(new CustomEvent("AboutLoginsDeleteLogin", {
             bubbles: true,
             detail: this._login,
           }));
           return;
         }
         if (event.target.classList.contains("edit-button")) {
--- a/browser/components/aboutlogins/jar.mn
+++ b/browser/components/aboutlogins/jar.mn
@@ -1,13 +1,15 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 browser.jar:
+  content/browser/aboutlogins/components/copy-to-clipboard-button.css (content/components/copy-to-clipboard-button.css)
+  content/browser/aboutlogins/components/copy-to-clipboard-button.js  (content/components/copy-to-clipboard-button.js)
   content/browser/aboutlogins/components/login-filter.css      (content/components/login-filter.css)
   content/browser/aboutlogins/components/login-filter.js       (content/components/login-filter.js)
   content/browser/aboutlogins/components/login-item.css        (content/components/login-item.css)
   content/browser/aboutlogins/components/login-item.js         (content/components/login-item.js)
   content/browser/aboutlogins/components/login-list.css        (content/components/login-list.css)
   content/browser/aboutlogins/components/login-list.js         (content/components/login-list.js)
   content/browser/aboutlogins/components/login-list-item.css   (content/components/login-list-item.css)
   content/browser/aboutlogins/components/login-list-item.js    (content/components/login-list-item.js)