Bug 1550099 - Add reveal button for modal-input[type=password]. r?MattN draft
authorpulselistener
Mon, 13 May 2019 19:26:22 +0000
changeset 1995827 d019957adf61db2f371b49538132652798f85bd6
parent 1995826 01f0c3d7f893e7a03bd5093602a494f201083b78
child 1995828 7e497a4d17da1b2b4428323b4d0c7dc18c00fc9d
push id360873
push userreviewbot
push dateMon, 13 May 2019 19:26:50 +0000
treeherdertry@7e497a4d17da [default view] [failures only]
reviewersMattN
bugs1550099
milestone68.0a1
Bug 1550099 - Add reveal button for modal-input[type=password]. r?MattN Differential Revision: https://phabricator.services.mozilla.com/D30961 Differential Diff: PHID-DIFF-gx6iuknecwwqemumniou
browser/components/aboutlogins/content/aboutLogins.ftl
browser/components/aboutlogins/content/aboutLogins.html
browser/components/aboutlogins/content/components/login-item.js
browser/components/aboutlogins/content/components/modal-input.css
browser/components/aboutlogins/content/components/modal-input.js
browser/components/aboutlogins/tests/mochitest/test_modal_input.html
--- a/browser/components/aboutlogins/content/aboutLogins.ftl
+++ b/browser/components/aboutlogins/content/aboutLogins.ftl
@@ -22,14 +22,15 @@ login-list =
        *[other] { $count } entries
     }
 
 login-item =
   .cancel-button = Cancel
   .delete-button = Delete
   .edit-button = Edit
   .hostname-label = Website Address
+  .modal-input-reveal-button = 👀
   .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
@@ -28,16 +28,17 @@
                 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,
                                  delete-button,
                                  edit-button,
                                  hostname-label,
+                                 modal-input-reveal-button,
                                  password-label,
                                  save-changes-button,
                                  time-created,
                                  time-changed,
                                  time-used,
                                  username-label"></login-item>
 
     <template id="login-list-template">
@@ -90,11 +91,12 @@
       <input type="text"/>
     </template>
 
     <template id="modal-input-template">
       <link rel="stylesheet" type="text/css" 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"/>
+      <button class="reveal-button"/>
     </template>
   </body>
 </html>
--- a/browser/components/aboutlogins/content/components/login-item.js
+++ b/browser/components/aboutlogins/content/components/login-item.js
@@ -38,29 +38,40 @@ class LoginItem extends ReflectedFluentE
   }
 
   static get reflectedFluentIDs() {
     return [
       "cancel-button",
       "delete-button",
       "edit-button",
       "hostname-label",
+      "modal-input-reveal-button",
       "password-label",
       "save-changes-button",
       "time-created",
       "time-changed",
       "time-used",
       "username-label",
     ];
   }
 
   static get observedAttributes() {
     return this.reflectedFluentIDs;
   }
 
+  handleSpecialCaseFluentString(attrName) {
+    if (attrName != "modal-input-reveal-button") {
+      return false;
+    }
+
+    this.shadowRoot.querySelector("modal-input[name='password']")
+                   .setAttribute("reveal-button", this.getAttribute(attrName));
+    return true;
+  }
+
   render() {
     let l10nArgs = {
       timeCreated: this._login.timeCreated || "",
       timeChanged: this._login.timePasswordChanged || "",
       timeUsed: this._login.timeLastUsed || "",
     };
     document.l10n.setAttributes(this, "login-item", l10nArgs);
     let hostnameNoScheme = this._login.hostname && new URL(this._login.hostname).hostname;
--- a/browser/components/aboutlogins/content/components/modal-input.css
+++ b/browser/components/aboutlogins/content/components/modal-input.css
@@ -1,8 +1,12 @@
 /* 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([editing]) .locked-value,
 :host(:not([editing])) .unlocked-value {
   display: none;
 }
+
+:host(:not([type="password"])) .reveal-button {
+  display: none;
+}
--- a/browser/components/aboutlogins/content/components/modal-input.js
+++ b/browser/components/aboutlogins/content/components/modal-input.js
@@ -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/. */
 
-class ModalInput extends HTMLElement {
+/* globals ReflectedFluentElement */
+
+class ModalInput extends ReflectedFluentElement {
   static get LOCKED_PASSWORD_DISPLAY() {
     return "••••••••";
   }
 
   connectedCallback() {
     if (this.children.length) {
       return;
     }
@@ -19,23 +21,31 @@ class ModalInput extends HTMLElement {
     if (this.hasAttribute("value")) {
       this.value = this.getAttribute("value");
     }
 
     if (this.getAttribute("type") == "password") {
       let unlockedValue = this.shadowRoot.querySelector(".unlocked-value");
       unlockedValue.setAttribute("type", "password");
     }
+
+    this.shadowRoot.querySelector(".reveal-button").addEventListener("click", this);
+  }
+
+  static get reflectedFluentIDs() {
+    return ["reveal-button"];
   }
 
   static get observedAttributes() {
-    return ["editing", "type", "value"];
+    return ["editing", "type", "value"].concat(ModalInput.reflectedFluentIDs);
   }
 
   attributeChangedCallback(attr, oldValue, newValue) {
+    super.attributeChangedCallback(attr, oldValue, newValue);
+
     if (!this.shadowRoot) {
       return;
     }
 
     let lockedValue = this.shadowRoot.querySelector(".locked-value");
     let unlockedValue = this.shadowRoot.querySelector(".unlocked-value");
 
     switch (attr) {
@@ -57,16 +67,37 @@ class ModalInput extends HTMLElement {
       }
       case "value": {
         this.value = newValue;
         break;
       }
     }
   }
 
+  handleEvent(event) {
+    switch (event.type) {
+      case "click": {
+        if (event.target.classList.contains("reveal-button")) {
+          let lockedValue = this.shadowRoot.querySelector(".locked-value");
+          let unlockedValue = this.shadowRoot.querySelector(".unlocked-value");
+          let editing = this.hasAttribute("editing");
+          if ((editing && unlockedValue.getAttribute("type") == "password") ||
+              (!editing && lockedValue.textContent == this.constructor.LOCKED_PASSWORD_DISPLAY)) {
+            lockedValue.textContent = this.value;
+            unlockedValue.setAttribute("type", "text");
+          } else {
+            lockedValue.textContent = this.constructor.LOCKED_PASSWORD_DISPLAY;
+            unlockedValue.setAttribute("type", "password");
+          }
+        }
+        break;
+      }
+    }
+  }
+
   get value() {
     return this.hasAttribute("editing") ? this.shadowRoot.querySelector(".unlocked-value").value.trim()
                                         : this.getAttribute("value") || "";
   }
 
   set value(val) {
     if (this.getAttribute("value") != val) {
       this.setAttribute("value", val);
--- a/browser/components/aboutlogins/tests/mochitest/test_modal_input.html
+++ b/browser/components/aboutlogins/tests/mochitest/test_modal_input.html
@@ -2,16 +2,17 @@
 <html>
 <!--
 Test the modal-input component
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the modal-input component</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script src="reflected-fluent-element.js"></script>
   <script src="modal-input.js"></script>
   <script src="aboutlogins_common.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
   <p id="display">
   </p>