Bug 1576272 - Show the empty-search results view of about:logins if the page is opened with a filter that doesn't match any stored logins. r=MattN a=lizzard
authorJared Wein <jwein@mozilla.com>
Wed, 04 Sep 2019 00:36:43 +0000
changeset 554973 37a190bdd95c45eca6f04145e53aef889529f3d0
parent 554972 e3a3522439095006fe7d78e7831fba79fd9cecb0
child 554974 6fb937a562b9d5852f88e9ce02f6c9511b2f6874
push id2165
push userffxbld-merge
push dateMon, 14 Oct 2019 16:30:58 +0000
treeherdermozilla-release@0eae18af659f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN, lizzard
bugs1576272
milestone70.0
Bug 1576272 - Show the empty-search results view of about:logins if the page is opened with a filter that doesn't match any stored logins. r=MattN a=lizzard Differential Revision: https://phabricator.services.mozilla.com/D44207
browser/components/aboutlogins/content/aboutLogins.css
browser/components/aboutlogins/content/components/login-item.js
browser/components/aboutlogins/content/components/login-list.js
browser/components/aboutlogins/tests/browser/browser_openFiltered.js
--- a/browser/components/aboutlogins/content/aboutLogins.css
+++ b/browser/components/aboutlogins/content/aboutLogins.css
@@ -33,18 +33,20 @@ fxaccounts-button,
 menu-button {
   margin-inline-start: 18px;
 }
 
 login-list {
   grid-area: logins;
 }
 
-:root:not(.no-logins) login-intro,
+:root:not(.no-logins):not(.empty-search):not(.login-selected) login-intro,
 login-item[data-editing="true"] + login-intro,
+.login-selected login-intro,
+.empty-search:not(.login-selected) login-item:not([data-editing="true"]),
 .no-logins login-item:not([data-editing="true"]) {
   display: none;
 }
 
 login-intro,
 login-item {
   grid-area: login;
 }
--- a/browser/components/aboutlogins/content/components/login-item.js
+++ b/browser/components/aboutlogins/content/components/login-item.js
@@ -474,16 +474,17 @@ export default class LoginItem extends H
 
     this._form.reset();
 
     if (login.guid) {
       delete this.dataset.isNewLogin;
     } else {
       this.dataset.isNewLogin = true;
     }
+    document.documentElement.classList.toggle("login-selected", login.guid);
     this._toggleEditing(!login.guid);
 
     this._revealCheckbox.checked = false;
 
     clearTimeout(this._copyUsernameTimeoutId);
     clearTimeout(this._copyPasswordTimeoutId);
     for (let copyButton of [
       this._copyUsernameButton,
--- a/browser/components/aboutlogins/content/components/login-list.js
+++ b/browser/components/aboutlogins/content/components/login-list.js
@@ -66,17 +66,21 @@ export default class LoginList extends H
     this._list.addEventListener("click", this);
     this.addEventListener("keydown", this);
     this._createLoginButton.addEventListener("click", this);
   }
 
   render() {
     let visibleLoginGuids = this._applyFilter();
     this._updateVisibleLoginCount(visibleLoginGuids.size);
-    this.classList.toggle("empty-search", visibleLoginGuids.size == 0);
+    this.classList.toggle("empty-search", !visibleLoginGuids.size);
+    document.documentElement.classList.toggle(
+      "empty-search",
+      this._filter && !visibleLoginGuids.size
+    );
 
     // Add all of the logins that are not in the DOM yet.
     let fragment = document.createDocumentFragment();
     for (let guid of this._loginGuidsSortedOrder) {
       if (this._logins[guid].listItem) {
         continue;
       }
       let login = this._logins[guid].login;
@@ -98,21 +102,16 @@ export default class LoginList extends H
       listItem.classList.toggle(
         "breached",
         !!this._breachesByLoginGUID &&
           this._breachesByLoginGUID.has(listItem.dataset.guid)
       );
       listItem.hidden = !visibleLoginGuids.has(listItem.dataset.guid);
     }
 
-    let createLoginSelected =
-      this._selectedGuid == null && Object.keys(this._logins).length > 0;
-    this.classList.toggle("create-login-selected", createLoginSelected);
-    this._createLoginButton.disabled = createLoginSelected;
-
     // Re-arrange the login-list-items according to their sort
     for (let i = this._loginGuidsSortedOrder.length - 1; i >= 0; i--) {
       let guid = this._loginGuidsSortedOrder[i];
       let { listItem } = this._logins[guid];
       this._list.insertBefore(
         listItem,
         this._blankLoginListItem.nextElementSibling
       );
--- a/browser/components/aboutlogins/tests/browser/browser_openFiltered.js
+++ b/browser/components/aboutlogins/tests/browser/browser_openFiltered.js
@@ -47,16 +47,26 @@ add_task(async function test_query_param
     }, "Waiting for logins to be cached");
 
     let loginItem = Cu.waiveXrays(content.document.querySelector("login-item"));
     await ContentTaskUtils.waitForCondition(
       () => loginItem._login.guid == logins[0].guid,
       "Waiting for TEST_LOGIN1 to be selected for the login-item view"
     );
 
+    ok(
+      ContentTaskUtils.is_visible(loginItem),
+      "login-item should be visible when a login is selected"
+    );
+    let loginIntro = content.document.querySelector("login-intro");
+    ok(
+      ContentTaskUtils.is_hidden(loginIntro),
+      "login-intro should be hidden when a login is selected"
+    );
+
     let loginFilter = content.document.querySelector("login-filter");
     let xRayLoginFilter = Cu.waiveXrays(loginFilter);
     is(
       xRayLoginFilter.value,
       logins[0].origin,
       "The filter should be prepopulated"
     );
     is(
@@ -85,8 +95,89 @@ add_task(async function test_query_param
     is(hiddenLoginListItems.length, 1, "One login should be hidden");
     is(
       hiddenLoginListItems[0].dataset.guid,
       logins[1].guid,
       "TEST_LOGIN2 should be hidden"
     );
   });
 });
+
+add_task(async function test_query_parameter_filter_no_logins_for_site() {
+  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+  const HOSTNAME_WITH_NO_LOGINS = "xxx-no-logins-for-site-xxx";
+  let tabOpenedPromise = BrowserTestUtils.waitForNewTab(
+    gBrowser,
+    "about:logins?filter=" + encodeURIComponent(HOSTNAME_WITH_NO_LOGINS),
+    true
+  );
+  LoginHelper.openPasswordManager(window, {
+    filterString: HOSTNAME_WITH_NO_LOGINS,
+    entryPoint: "preferences",
+  });
+  await tabOpenedPromise;
+
+  let browser = gBrowser.selectedBrowser;
+  await ContentTask.spawn(browser, null, async () => {
+    let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
+    await ContentTaskUtils.waitForCondition(() => {
+      return loginList._loginGuidsSortedOrder.length == 2;
+    }, "Waiting for logins to be cached");
+    is(
+      loginList._loginGuidsSortedOrder.length,
+      2,
+      "login list should have two logins stored"
+    );
+
+    ok(
+      ContentTaskUtils.is_hidden(loginList._list),
+      "the login list should be hidden when there is a search with no results"
+    );
+    let intro = loginList.shadowRoot.querySelector(".intro");
+    ok(
+      ContentTaskUtils.is_hidden(intro),
+      "the intro should be hidden when there is a search with no results"
+    );
+    let emptySearchMessage = loginList.shadowRoot.querySelector(
+      ".empty-search-message"
+    );
+    ok(
+      ContentTaskUtils.is_visible(emptySearchMessage),
+      "the empty search message should be visible when there is a search with no results"
+    );
+
+    let visibleLoginListItems = loginList.shadowRoot.querySelectorAll(
+      ".login-list-item:not(#new-login-list-item):not([hidden])"
+    );
+    is(visibleLoginListItems.length, 0, "No login should be visible");
+
+    ok(
+      !loginList._createLoginButton.disabled,
+      "create button should be enabled"
+    );
+
+    let loginItem = content.document.querySelector("login-item");
+    ok(!loginItem.dataset.isNewLogin, "should not be in create mode");
+    ok(!loginItem.dataset.editing, "should not be in edit mode");
+    ok(
+      ContentTaskUtils.is_hidden(loginItem),
+      "login-item should be hidden when a login is not selected and we're not in create mode"
+    );
+    let loginIntro = content.document.querySelector("login-intro");
+    ok(
+      ContentTaskUtils.is_visible(loginIntro),
+      "login-intro should be visible when a login is not selected and we're not in create mode"
+    );
+
+    loginList._createLoginButton.click();
+
+    ok(loginItem.dataset.isNewLogin, "should be in create mode");
+    ok(loginItem.dataset.editing, "should be in edit mode");
+    ok(
+      ContentTaskUtils.is_visible(loginItem),
+      "login-item should be visible in create mode"
+    );
+    ok(
+      ContentTaskUtils.is_hidden(loginIntro),
+      "login-intro should be hidden in create mode"
+    );
+  });
+});