Bug 1550099 - Add Edit button that toggles between editing/not-editing a LoginItem. r=MattN,Pike
authorJared Wein <jwein@mozilla.com>
Tue, 14 May 2019 20:07:15 +0000
changeset 532687 48567f3c473a35b8320f060181d32cb570deacb0
parent 532686 ed88d536588fda46f7afab3db01690bab602e34a
child 532688 044fba1f3da7b9400382184918a9d325db3f3c90
push id11270
push userrgurzau@mozilla.com
push dateWed, 15 May 2019 15:07:19 +0000
treeherdermozilla-beta@571bc76da583 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN, Pike
bugs1550099
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 1550099 - Add Edit button that toggles between editing/not-editing a LoginItem. r=MattN,Pike Differential Revision: https://phabricator.services.mozilla.com/D30960
browser/components/aboutlogins/content/aboutLogins.ftl
browser/components/aboutlogins/content/aboutLogins.html
browser/components/aboutlogins/content/components/login-item.css
browser/components/aboutlogins/content/components/login-item.js
browser/components/aboutlogins/tests/browser/browser_updateLogin.js
--- a/browser/components/aboutlogins/content/aboutLogins.ftl
+++ b/browser/components/aboutlogins/content/aboutLogins.ftl
@@ -20,15 +20,16 @@ login-list =
     { $count ->
         [one] { $count } entry
        *[other] { $count } entries
     }
 
 login-item =
   .cancel-button = Cancel
   .delete-button = Delete
+  .edit-button = Edit
   .hostname-label = Website Address
   .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
@@ -26,16 +26,17 @@
     </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,
                                  delete-button,
+                                 edit-button,
                                  hostname-label,
                                  password-label,
                                  save-changes-button,
                                  time-created,
                                  time-changed,
                                  time-used,
                                  username-label"></login-item>
 
@@ -56,16 +57,17 @@
       <span class="username"></span>
     </template>
 
     <template id="login-item-template">
       <link rel="stylesheet" type="text/css" href="chrome://global/skin/in-content/common.css">
       <link rel="stylesheet" href="chrome://browser/content/aboutlogins/components/login-item.css">
       <div class="header">
         <h2 class="title"></h2>
+        <button class="edit-button"></button>
         <button class="delete-button"></button>
       </div>
       <label>
         <span class="hostname-label field-label"></span>
         <span class="hostname"/>
       </label>
       <label>
         <span class="username-label field-label"></span>
--- a/browser/components/aboutlogins/content/components/login-item.css
+++ b/browser/components/aboutlogins/content/components/login-item.css
@@ -3,16 +3,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 :host {
   padding-top: 36px;
   padding-left: 40px;
   padding-right: 40px;
 }
 
+:host(:not([editing])) .save-changes-button,
+:host(:not([editing])) .cancel-button {
+  display: none;
+}
+
 .header {
   display: flex;
   border-bottom: 1px solid var(--in-content-box-border-color);
 }
 
 .title {
   margin-top: 0;
   margin-bottom: 0;
--- a/browser/components/aboutlogins/content/components/login-item.js
+++ b/browser/components/aboutlogins/content/components/login-item.js
@@ -19,32 +19,34 @@ 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 [
       ".delete-button",
+      ".edit-button",
       ".save-changes-button",
       ".cancel-button",
     ]) {
       let button = this.shadowRoot.querySelector(selector);
       button.addEventListener("click", this);
     }
 
     window.addEventListener("AboutLoginsLoginSelected", this);
 
     this.render();
   }
 
   static get reflectedFluentIDs() {
     return [
       "cancel-button",
       "delete-button",
+      "edit-button",
       "hostname-label",
       "password-label",
       "save-changes-button",
       "time-created",
       "time-changed",
       "time-used",
       "username-label",
     ];
@@ -70,43 +72,48 @@ class LoginItem extends ReflectedFluentE
 
   handleEvent(event) {
     switch (event.type) {
       case "AboutLoginsLoginSelected": {
         this.setLogin(event.detail);
         break;
       }
       case "click": {
+        if (event.target.classList.contains("cancel-button")) {
+          this.toggleEditing();
+          this.render();
+          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")) {
+          this.toggleEditing();
+          return;
+        }
         if (event.target.classList.contains("save-changes-button")) {
           let loginUpdates = {
             guid: this._login.guid,
           };
           let formUsername = this.shadowRoot.querySelector("modal-input[name='username']").value.trim();
           if (formUsername != this._login.username) {
             loginUpdates.username = formUsername;
           }
           let formPassword = this.shadowRoot.querySelector("modal-input[name='password']").value.trim();
           if (formPassword != this._login.password) {
             loginUpdates.password = formPassword;
           }
           document.dispatchEvent(new CustomEvent("AboutLoginsUpdateLogin", {
             bubbles: true,
             detail: loginUpdates,
           }));
-          return;
-        }
-        if (event.target.classList.contains("cancel-button")) {
-          this.render();
         }
         break;
       }
     }
   }
 
   setLogin(login) {
     this._login = login;
@@ -114,20 +121,29 @@ class LoginItem extends ReflectedFluentE
   }
 
   loginModified(login) {
     if (login.guid != this._login.guid) {
       return;
     }
 
     this._login = login;
+    this.toggleEditing(false);
     this.render();
   }
 
   loginRemoved(login) {
     if (login.guid != this._login.guid) {
       return;
     }
     this._login = {};
     this.render();
   }
+
+  toggleEditing(force) {
+    let shouldEdit = force !== undefined ? force : !this.hasAttribute("editing");
+    this.shadowRoot.querySelector(".edit-button").disabled = shouldEdit;
+    this.shadowRoot.querySelectorAll("modal-input")
+                   .forEach(el => el.toggleAttribute("editing", shouldEdit));
+    this.toggleAttribute("editing", shouldEdit);
+  }
 }
 customElements.define("login-item", LoginItem);
--- a/browser/components/aboutlogins/tests/browser/browser_updateLogin.js
+++ b/browser/components/aboutlogins/tests/browser/browser_updateLogin.js
@@ -42,25 +42,32 @@ add_task(async function test_login_item(
       return loginItem._login.guid == loginListItem.getAttribute("guid") &&
              loginItem._login.guid == login.guid;
     }, "Waiting for login item to get populated");
     ok(loginItemPopulated, "The login item should get populated");
 
     let usernameInput = loginItem.shadowRoot.querySelector("modal-input[name='username']");
     let passwordInput = loginItem.shadowRoot.querySelector("modal-input[name='password']");
 
+    let editButton = loginItem.shadowRoot.querySelector(".edit-button");
+    editButton.click();
+    await Promise.resolve();
+
     usernameInput.value += "-undome";
     passwordInput.value += "-undome";
 
     let cancelButton = loginItem.shadowRoot.querySelector(".cancel-button");
     cancelButton.click();
     await Promise.resolve();
     is(usernameInput.value, login.username, "Username change should be reverted");
     is(passwordInput.value, login.password, "Password change should be reverted");
 
+    editButton.click();
+    await Promise.resolve();
+
     usernameInput.value += "-saveme";
     passwordInput.value += "-saveme";
 
     let saveChangesButton = loginItem.shadowRoot.querySelector(".save-changes-button");
     saveChangesButton.click();
 
     await ContentTaskUtils.waitForCondition(() => {
       return loginListItem._login.username == usernameInput.value &&