Bug 1550093 - Create CustomElement for copy-to-clipboard. r=MattN,Pike draft
authorpulselistener
Sat, 18 May 2019 06:46:27 +0000
changeset 2008549 60cb43b5e3c834b6b8946e77189110b814ab5570
parent 2008548 9c8942c55a3966c800e52ce079f2089bc7026fea
child 2008550 e8958c487d3882e87007238638b3bf4652f3bfba
push id363922
push userreviewbot
push dateSat, 18 May 2019 06:47:24 +0000
treeherdertry@8bf9b3332afe [default view] [failures only]
reviewersMattN, Pike
bugs1550093
milestone68.0a1
Bug 1550093 - Create CustomElement for copy-to-clipboard. r=MattN,Pike Differential Revision: https://phabricator.services.mozilla.com/D31519 Differential Diff: PHID-DIFF-j72ahyks32ymzxxpzngj
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)