Commit 9dd148fc authored by hcarmona's avatar hcarmona Committed by Commit bot

[MD Settings] Implement search in material design passwords.

Includes test. Screenshot in bug.

BUG=626119
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:closure_compilation

Review-Url: https://codereview.chromium.org/2092763004
Cr-Commit-Position: refs/heads/master@{#406736}
parent 581d041c
...@@ -329,6 +329,9 @@ ...@@ -329,6 +329,9 @@
<message name="IDS_SETTINGS_PASSWORD_REMOVE" desc="Label for a context menu item that removes the selected password." meaning="Remove selected password."> <message name="IDS_SETTINGS_PASSWORD_REMOVE" desc="Label for a context menu item that removes the selected password." meaning="Remove selected password.">
Remove Remove
</message> </message>
<message name="IDS_SETTINGS_PASSWORD_SEARCH" desc="Placeholder/label for the text input field that allows a user to filter saved passwords.">
Search passwords
</message>
<message name="IDS_SETTINGS_PASSWORDS_VIEW_DETAILS_TITLE" desc="Title for the dialog that shows password details. This dialog lets a user see a saved password and copy the username."> <message name="IDS_SETTINGS_PASSWORDS_VIEW_DETAILS_TITLE" desc="Title for the dialog that shows password details. This dialog lets a user see a saved password and copy the username.">
Saved password details Saved password details
</message> </message>
......
...@@ -48,9 +48,12 @@ ...@@ -48,9 +48,12 @@
</settings-subpage> </settings-subpage>
</template> </template>
<template is="dom-if" name="manage-passwords"> <template is="dom-if" name="manage-passwords">
<settings-subpage page-title="$i18n{passwords}"> <settings-subpage page-title="$i18n{passwords}"
search-label="$i18n{searchPasswords}"
search-term="{{passwordFilter_}}">
<passwords-section saved-passwords="[[savedPasswords]]" <passwords-section saved-passwords="[[savedPasswords]]"
id="passwordSection" password-exceptions="[[passwordExceptions]]"> id="passwordSection" password-exceptions="[[passwordExceptions]]"
filter="[[passwordFilter_]]">
</passwords-section> </passwords-section>
</settings-subpage> </settings-subpage>
</template> </template>
......
...@@ -331,6 +331,9 @@ Polymer({ ...@@ -331,6 +331,9 @@ Polymer({
*/ */
passwordExceptions: Array, passwordExceptions: Array,
/** @private Filter applied to passwords and password exceptions. */
passwordFilter_: String,
/** /**
* An array of saved addresses. * An array of saved addresses.
* @type {!Array<!AutofillManager.AddressEntry>} * @type {!Array<!AutofillManager.AddressEntry>}
......
...@@ -68,7 +68,8 @@ ...@@ -68,7 +68,8 @@
$i18n{editPasswordPasswordLabel} $i18n{editPasswordPasswordLabel}
</div> </div>
</div> </div>
<iron-list id="passwordList" items="[[savedPasswords]]" <iron-list id="passwordList"
items="[[getFilteredPasswords_(savedPasswords, filter)]]"
class="vertical-list list-section list-with-header"> class="vertical-list list-section list-with-header">
<template> <template>
<div class="list-item"> <div class="list-item">
...@@ -105,7 +106,8 @@ ...@@ -105,7 +106,8 @@
</password-edit-dialog> </password-edit-dialog>
</template> </template>
<div class="heading">$i18n{passwordExceptionsHeading}</div> <div class="heading">$i18n{passwordExceptionsHeading}</div>
<iron-list id="passwordExceptionsList" items="[[passwordExceptions]]" <iron-list id="passwordExceptionsList"
items="[[getFilteredExceptions_(passwordExceptions, filter)]]"
class="vertical-list list-section item-list"> class="vertical-list list-section item-list">
<template> <template>
<div class="list-item two-line"> <div class="list-item two-line">
......
...@@ -53,6 +53,12 @@ Polymer({ ...@@ -53,6 +53,12 @@ Polymer({
* @private {?chrome.passwordsPrivate.PasswordUiEntry} * @private {?chrome.passwordsPrivate.PasswordUiEntry}
*/ */
activePassword: Object, activePassword: Object,
/** Filter on the saved passwords and exceptions. */
filter: {
type: String,
value: '',
},
}, },
listeners: { listeners: {
...@@ -90,6 +96,37 @@ Polymer({ ...@@ -90,6 +96,37 @@ Polymer({
this.activePassword = null; this.activePassword = null;
}, },
/**
* @param {!Array<!chrome.passwordsPrivate.PasswordUiEntry>} savedPasswords
* @param {string} filter
* @return {!Array<!chrome.passwordsPrivate.PasswordUiEntry>}
* @private
*/
getFilteredPasswords_: function(savedPasswords, filter) {
if (!filter)
return savedPasswords;
return savedPasswords.filter(function(password) {
return password.loginPair.originUrl.includes(filter) ||
password.loginPair.username.includes(filter);
});
},
/**
* @param {!Array<!chrome.passwordsPrivate.ExceptionPair>} passwordExceptions
* @param {string} filter
* @return {!Array<!chrome.passwordsPrivate.ExceptionPair>}
* @private
*/
getFilteredExceptions_: function(passwordExceptions, filter) {
if (!filter)
return passwordExceptions;
return passwordExceptions.filter(function(exception) {
return exception.exceptionUrl.includes(filter);
});
},
/** /**
* Fires an event that should delete the saved password. * Fires an event that should delete the saved password.
* @private * @private
......
...@@ -42,12 +42,23 @@ ...@@ -42,12 +42,23 @@
{ {
'target_name': 'settings_subpage', 'target_name': 'settings_subpage',
'dependencies': [ 'dependencies': [
'settings_subpage_search',
'<(DEPTH)/third_party/polymer/v1_0/components-chromium/iron-resizable-behavior/compiled_resources2.gyp:iron-resizable-behavior-extracted', '<(DEPTH)/third_party/polymer/v1_0/components-chromium/iron-resizable-behavior/compiled_resources2.gyp:iron-resizable-behavior-extracted',
'<(DEPTH)/third_party/polymer/v1_0/components-chromium/neon-animation/compiled_resources2.gyp:neon-animatable-behavior-extracted', '<(DEPTH)/third_party/polymer/v1_0/components-chromium/neon-animation/compiled_resources2.gyp:neon-animatable-behavior-extracted',
'<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
], ],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
}, },
{
'target_name': 'settings_subpage_search',
'dependencies': [
'<(DEPTH)/third_party/polymer/v1_0/components-chromium/paper-icon-button/compiled_resources2.gyp:paper-icon-button-extracted',
'<(DEPTH)/third_party/polymer/v1_0/components-chromium/paper-input/compiled_resources2.gyp:paper-input-container-extracted',
'<(DEPTH)/ui/webui/resources/cr_elements/cr_search_field/compiled_resources2.gyp:cr_search_field_behavior',
'<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
},
{ {
'target_name': 'transition_behavior', 'target_name': 'transition_behavior',
'dependencies': [ 'dependencies': [
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
<link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animatable-behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animatable-behavior.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html">
<link rel="import" href="/icons.html"> <link rel="import" href="/icons.html">
<link rel="import" href="/settings_page/settings_subpage_search.html">
<link rel="import" href="/settings_shared_css.html"> <link rel="import" href="/settings_shared_css.html">
<dom-module id="settings-subpage"> <dom-module id="settings-subpage">
...@@ -29,11 +30,20 @@ ...@@ -29,11 +30,20 @@
color: var(--settings-nav-grey); color: var(--settings-nav-grey);
font-size: 107.6923%; /* go to 14px from 13px */ font-size: 107.6923%; /* go to 14px from 13px */
} }
settings-subpage-search {
-webkit-margin-start: auto;
}
</style> </style>
<div class="settings-box first"> <div class="settings-box first">
<paper-icon-button icon="settings:arrow-back" on-tap="onTapBack_"> <paper-icon-button icon="settings:arrow-back" on-tap="onTapBack_">
</paper-icon-button> </paper-icon-button>
<h2>[[pageTitle]]</h2> <h2>[[pageTitle]]</h2>
<template is="dom-if" if="[[searchLabel]]">
<settings-subpage-search label="[[searchLabel]]"
on-search-changed="onSearchChanged_">
</settings-subpage-search>
</template>
</div> </div>
<content></content> <content></content>
</template> </template>
......
...@@ -20,6 +20,15 @@ Polymer({ ...@@ -20,6 +20,15 @@ Polymer({
properties: { properties: {
pageTitle: String, pageTitle: String,
/** Setting a |searchLabel| will enable search. */
searchLabel: String,
searchTerm: {
type: String,
notify: true,
value: '',
},
}, },
/** @private */ /** @private */
...@@ -27,4 +36,9 @@ Polymer({ ...@@ -27,4 +36,9 @@ Polymer({
// Event is caught by settings-animated-pages. // Event is caught by settings-animated-pages.
this.fire('subpage-back'); this.fire('subpage-back');
}, },
/** @private */
onSearchChanged_: function(e) {
this.searchTerm = e.detail;
},
}); });
<link rel="import" href="chrome://resources/cr_elements/cr_search_field/cr_search_field_behavior.html">
<link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input-container.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html">
<dom-module id="settings-subpage-search">
<template>
<style>
paper-input-container {
width: var(--paper-input-max-width);
}
input[type='search']::-webkit-search-cancel-button {
-webkit-appearance: none;
}
#prompt,
#searchInput {
color: var(--google-grey-500);
font-size: 13px;
line-height: 36px;
}
#clearSearch {
-webkit-margin-end: -8px;
-webkit-margin-start: 8px;
}
:root {
--paper-icon-button {
height: 36px;
width: 36px;
}
--paper-input-suffix: {
/* Required to align the icon in |clearSearch| vertically. */
line-height: 0;
}
}
</style>
<paper-input-container no-label-float on-search="onSearchTermSearch">
<label id="prompt">[[label]]</label>
<input is="iron-input" id="searchInput" type="search"
aria-labelledby="prompt" incremental>
</input>
<paper-icon-button suffix icon="cr:cancel" id="clearSearch"
on-tap="onTapClear_" title="[[clearLabel]]" hidden$="[[!lastValue_]]">
</paper-icon-button>
</paper-input-container>
</template>
<script src="settings_subpage_search.js"></script>
</dom-module>
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview
* 'settings-subpage' shows a subpage beneath a subheader. The header contains
* the subpage title and a back icon. The back icon fires an event which
* is caught by settings-animated-pages, so it requires no separate handling.
*/
Polymer({
is: 'settings-subpage-search',
behaviors: [CrSearchFieldBehavior],
/** @private */
onTapClear_: function() {
this.setValue('');
},
});
...@@ -204,6 +204,12 @@ ...@@ -204,6 +204,12 @@
<structure name="IDR_SETTINGS_CR_SETTINGS_SUBPAGE_JS" <structure name="IDR_SETTINGS_CR_SETTINGS_SUBPAGE_JS"
file="settings_page/settings_subpage.js" file="settings_page/settings_subpage.js"
type="chrome_html" /> type="chrome_html" />
<structure name="IDR_SETTINGS_CR_SETTINGS_SUBPAGE_SEARCH_HTML"
file="settings_page/settings_subpage_search.html"
type="chrome_html" />
<structure name="IDR_SETTINGS_CR_SETTINGS_SUBPAGE_SEARCH_JS"
file="settings_page/settings_subpage_search.js"
type="chrome_html" />
<structure name="IDR_SETTINGS_CR_SETTINGS_PAGE_CSS_HTML" <structure name="IDR_SETTINGS_CR_SETTINGS_PAGE_CSS_HTML"
file="settings_page_css.html" file="settings_page_css.html"
type="chrome_html" /> type="chrome_html" />
......
...@@ -772,6 +772,7 @@ void AddPasswordsAndFormsStrings(content::WebUIDataSource* html_source) { ...@@ -772,6 +772,7 @@ void AddPasswordsAndFormsStrings(content::WebUIDataSource* html_source) {
{"deletePasswordException", IDS_SETTINGS_PASSWORDS_DELETE_EXCEPTION}, {"deletePasswordException", IDS_SETTINGS_PASSWORDS_DELETE_EXCEPTION},
{"passwordsDone", IDS_SETTINGS_PASSWORD_DONE}, {"passwordsDone", IDS_SETTINGS_PASSWORD_DONE},
{"removePassword", IDS_SETTINGS_PASSWORD_REMOVE}, {"removePassword", IDS_SETTINGS_PASSWORD_REMOVE},
{"searchPasswords", IDS_SETTINGS_PASSWORD_SEARCH},
{"passwordDetailsTitle", IDS_SETTINGS_PASSWORDS_VIEW_DETAILS_TITLE}, {"passwordDetailsTitle", IDS_SETTINGS_PASSWORDS_VIEW_DETAILS_TITLE},
{"passwordViewDetails", IDS_SETTINGS_PASSWORD_VIEW_DETAILS}, {"passwordViewDetails", IDS_SETTINGS_PASSWORD_VIEW_DETAILS},
{"editPasswordWebsiteLabel", IDS_SETTINGS_PASSWORDS_WEBSITE}, {"editPasswordWebsiteLabel", IDS_SETTINGS_PASSWORDS_WEBSITE},
......
...@@ -129,7 +129,7 @@ SettingsPasswordSectionBrowserTest.prototype = { ...@@ -129,7 +129,7 @@ SettingsPasswordSectionBrowserTest.prototype = {
/** /**
* Helper method used to test for a url in a list of passwords. * Helper method used to test for a url in a list of passwords.
* @param {!Array<!chrome.passwordsPrivate.PasswordUiEntry>} passwordList * @param {!Array<!chrome.passwordsPrivate.PasswordUiEntry>} passwordList
* @param {!string} url The URL that is being searched for. * @param {string} url The URL that is being searched for.
*/ */
listContainsUrl(passwordList, url) { listContainsUrl(passwordList, url) {
for (var i = 0; i < passwordList.length; ++i) { for (var i = 0; i < passwordList.length; ++i) {
...@@ -142,7 +142,7 @@ SettingsPasswordSectionBrowserTest.prototype = { ...@@ -142,7 +142,7 @@ SettingsPasswordSectionBrowserTest.prototype = {
/** /**
* Helper method used to test for a url in a list of passwords. * Helper method used to test for a url in a list of passwords.
* @param {!Array<!chrome.passwordsPrivate.ExceptionPair>} exceptionList * @param {!Array<!chrome.passwordsPrivate.ExceptionPair>} exceptionList
* @param {!string} url The URL that is being searched for. * @param {string} url The URL that is being searched for.
*/ */
exceptionsListContainsUrl(exceptionList, url) { exceptionsListContainsUrl(exceptionList, url) {
for (var i = 0; i < exceptionList.length; ++i) { for (var i = 0; i < exceptionList.length; ++i) {
...@@ -268,6 +268,58 @@ TEST_F('SettingsPasswordSectionBrowserTest', 'uiTests', function() { ...@@ -268,6 +268,58 @@ TEST_F('SettingsPasswordSectionBrowserTest', 'uiTests', function() {
clickRemoveButton(); clickRemoveButton();
}); });
test('verifyFilterPasswords', function() {
var passwordList = [
FakeDataMaker.passwordEntry('one.com', 'show', 5),
FakeDataMaker.passwordEntry('two.com', 'shower', 3),
FakeDataMaker.passwordEntry('three.com/show', 'four', 1),
FakeDataMaker.passwordEntry('four.com', 'three', 2),
FakeDataMaker.passwordEntry('five.com', 'two', 4),
FakeDataMaker.passwordEntry('six-show.com', 'one', 6),
];
var passwordsSection = self.createPasswordsSection_(passwordList, []);
passwordsSection.filter = 'show';
Polymer.dom.flush();
var expectedPasswordList = [
FakeDataMaker.passwordEntry('one.com', 'show', 5),
FakeDataMaker.passwordEntry('two.com', 'shower', 3),
FakeDataMaker.passwordEntry('three.com/show', 'four', 1),
FakeDataMaker.passwordEntry('six-show.com', 'one', 6),
];
self.validatePasswordList(
self.getIronListChildren_(passwordsSection.$.passwordList),
expectedPasswordList);
});
test('verifyFilterPasswordExceptions', function() {
var exceptionList = [
FakeDataMaker.exceptionEntry('docsshow.google.com'),
FakeDataMaker.exceptionEntry('showmail.com'),
FakeDataMaker.exceptionEntry('google.com'),
FakeDataMaker.exceptionEntry('inbox.google.com'),
FakeDataMaker.exceptionEntry('mapsshow.google.com'),
FakeDataMaker.exceptionEntry('plus.google.comshow'),
];
var passwordsSection = self.createPasswordsSection_([], exceptionList);
passwordsSection.filter = 'show';
Polymer.dom.flush();
var expectedExceptionList = [
FakeDataMaker.exceptionEntry('docsshow.google.com'),
FakeDataMaker.exceptionEntry('showmail.com'),
FakeDataMaker.exceptionEntry('mapsshow.google.com'),
FakeDataMaker.exceptionEntry('plus.google.comshow'),
];
self.validateExceptionList_(
self.getIronListChildren_(passwordsSection.$.passwordExceptionsList),
expectedExceptionList);
});
test('verifyPasswordExceptions', function() { test('verifyPasswordExceptions', function() {
var exceptionList = [ var exceptionList = [
FakeDataMaker.exceptionEntry('docs.google.com'), FakeDataMaker.exceptionEntry('docs.google.com'),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment