Commit 6072bcf5 authored by Bailey Berro's avatar Bailey Berro Committed by Commit Bot

Introduce cr-searchable-drop-down polymer component.

This change takes the drop-down-search-box from the "Add Cups Printer"
dialog and makes it a cr-element so that it can be used elsewhere.

- Updates cups_add_printers_dialog to use cr-searchable-drop-down

Bug: 866146
Test: browser_tests
Change-Id: Ib0d953c3dbb1f51dad94a73451eb924d12d1b39e
Reviewed-on: https://chromium-review.googlesource.com/1145774
Commit-Queue: Bailey Berro <baileyberro@chromium.org>
Reviewed-by: default avatarScott Chen <scottchen@chromium.org>
Reviewed-by: default avatarXiaoqian Dai <xdai@chromium.org>
Cr-Commit-Position: refs/heads/master@{#577722}
parent 5eb7fff7
<link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html"> <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
<link rel="import" href="chrome://resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html">
<link rel="import" href="chrome://resources/html/md_select_css.html"> <link rel="import" href="chrome://resources/html/md_select_css.html">
<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
...@@ -174,16 +175,16 @@ ...@@ -174,16 +175,16 @@
<div slot="dialog-title">$i18n{selectManufacturerAndModelTitle}</div> <div slot="dialog-title">$i18n{selectManufacturerAndModelTitle}</div>
<div slot="dialog-body"> <div slot="dialog-body">
<div class="settings-box two-line"> <div class="settings-box two-line">
<drop-down-search-box items="[[manufacturerList]]" <cr-searchable-drop-down items="[[manufacturerList]]"
label="$i18n{printerManufacturer}" label="$i18n{printerManufacturer}"
value="{{activePrinter.ppdManufacturer}}"> value="{{activePrinter.ppdManufacturer}}">
</drop-down-search-box> </cr-searchable-drop-down>
</div> </div>
<div class="settings-box two-line"> <div class="settings-box two-line">
<drop-down-search-box items="[[modelList]]" <cr-searchable-drop-down items="[[modelList]]"
label="$i18n{printerModel}" label="$i18n{printerModel}"
value="{{activePrinter.ppdModel}}"> value="{{activePrinter.ppdModel}}">
</drop-down-search-box> </cr-searchable-drop-down>
</div> </div>
<div class="settings-box two-line last"> <div class="settings-box two-line last">
<cr-input class="browse-file-input" readonly value="[[newUserPPD_]]" <cr-input class="browse-file-input" readonly value="[[newUserPPD_]]"
......
<link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
<link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
<link rel="import" href="chrome://resources/cr_elements/cr_scrollable_behavior.html"> <link rel="import" href="chrome://resources/cr_elements/cr_scrollable_behavior.html">
<link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-dropdown/iron-dropdown.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
<link rel="import" href="cups_printer_shared_css.html"> <link rel="import" href="cups_printer_shared_css.html">
<link rel="import" href="cups_printers_browser_proxy.html"> <link rel="import" href="cups_printers_browser_proxy.html">
...@@ -29,46 +27,6 @@ ...@@ -29,46 +27,6 @@
</template> </template>
</dom-module> </dom-module>
<dom-module id="drop-down-search-box">
<template>
<style include="cups-printer-shared">
cr-input {
--cr-input-error-display: none;
}
iron-dropdown,
cr-input {
width: 270px;
}
iron-dropdown {
height: 270px;
}
iron-dropdown [slot='dropdown-content'] {
background-color: white;
box-shadow: 0 2px 6px var(--paper-grey-500);
min-width: 128px;
padding: 8px 0;
}
</style>
<!-- |value| is one-way binding on purpose so that it doesn't change
immediately as the user types. -->
<cr-input label="[[label]]" on-click="onClick_" value="[[value]]"
on-value-changed="onInputValueChanged_" id="search">
</cr-input>
<iron-dropdown horizontal-align="left" vertical-align="top"
vertical-offset="52">
<div slot="dropdown-content">
<template is="dom-repeat" items="[[items]]"
filter="[[filterItems_(searchTerm_)]]">
<button class="list-item" on-click="onSelect_">[[item]]</button>
</template>
</div>
</iron-dropdown>
</template>
</dom-module>
<dom-module id="add-printer-dialog"> <dom-module id="add-printer-dialog">
<template> <template>
<style include="settings-shared"> <style include="settings-shared">
......
...@@ -29,62 +29,6 @@ Polymer({ ...@@ -29,62 +29,6 @@ Polymer({
}, },
}); });
/** 'drop-down-search-box' implements a search box with suggestions dropdown. */
Polymer({
is: 'drop-down-search-box',
properties: {
/** @type {!Array<string>} */
items: {
type: Array,
},
/** @type {string} */
value: {
type: String,
notify: true,
},
/** @private {string} */
searchTerm_: String,
label: String,
},
/**
* @param {!Event} event
* @private
*/
onClick_: function(event) {
this.$$('iron-dropdown').open();
},
/** @private */
onInputValueChanged_: function() {
this.searchTerm_ = this.$.search.value;
},
/**
* @param {{model:Object}} event
* @private
*/
onSelect_: function(event) {
this.$$('iron-dropdown').close();
this.value = event.model.item;
this.searchTerm_ = '';
},
/** @private */
filterItems_: function(searchTerm) {
if (!searchTerm)
return null;
return function(item) {
return item.toLowerCase().includes(searchTerm.toLowerCase());
};
},
});
/** 'add-printer-dialog' is the template of the Add Printer dialog. */ /** 'add-printer-dialog' is the template of the Add Printer dialog. */
Polymer({ Polymer({
is: 'add-printer-dialog', is: 'add-printer-dialog',
......
...@@ -350,3 +350,27 @@ CrElementsRadioButtonTest.prototype = { ...@@ -350,3 +350,27 @@ CrElementsRadioButtonTest.prototype = {
TEST_F('CrElementsRadioButtonTest', 'All', function() { TEST_F('CrElementsRadioButtonTest', 'All', function() {
mocha.run(); mocha.run();
}); });
/**
* @constructor
* @extends {CrElementsBrowserTest}
*/
function CrElementsSearchableDropDownTest() {}
CrElementsSearchableDropDownTest.prototype = {
__proto__: CrElementsBrowserTest.prototype,
/** @override */
browsePreload: 'chrome://resources/cr_elements/cr_searchable_drop_down/' +
'cr_searchable_drop_down.html',
/** @override */
extraLibraries: CrElementsBrowserTest.prototype.extraLibraries.concat([
'../settings/test_util.js',
'cr_searchable_drop_down_tests.js',
]),
};
TEST_F('CrElementsSearchableDropDownTest', 'All', function() {
mocha.run();
});
// Copyright 2018 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.
suite('cr-searchable-drop-down', function() {
let dropDown;
/**
* @param {!Array<string>} items The list of items to be populated in the
* drop down.
*/
function setItems(items) {
dropDown.items = items;
Polymer.dom.flush();
}
/** @return {!NodeList} */
function getList() {
return dropDown.shadowRoot.querySelectorAll('.list-item');
}
/**
* @param {string} searchTerm The string used to filter the list of items in
* the drop down.
*/
function search(searchTerm) {
let input = dropDown.shadowRoot.querySelector('cr-input');
input.value = searchTerm;
input.fire('input');
Polymer.dom.flush();
}
setup(function() {
PolymerTest.clearBody();
document.body.innerHTML = `
<cr-searchable-drop-down label="test drop down">
</cr-searchable-drop-down>
`;
dropDown = document.querySelector('cr-searchable-drop-down');
Polymer.dom.flush();
});
test('correct list items', function() {
setItems(['one', 'two', 'three']);
let itemList = getList();
assertEquals(3, itemList.length);
assertEquals('one', itemList[0].textContent);
assertEquals('two', itemList[1].textContent);
assertEquals('three', itemList[2].textContent);
});
test('filter works correctly', function() {
setItems(['cat', 'hat', 'rat', 'rake']);
search('c');
assertEquals(1, getList().length);
assertEquals('cat', getList()[0].textContent);
search('at');
assertEquals(3, getList().length);
assertEquals('cat', getList()[0].textContent);
assertEquals('hat', getList()[1].textContent);
assertEquals('rat', getList()[2].textContent);
search('ra');
assertEquals(2, getList().length);
assertEquals('rat', getList()[0].textContent);
assertEquals('rake', getList()[1].textContent);
});
test('value is set on click', function() {
setItems(['dog', 'cat', 'mouse']);
assertNotEquals('dog', dropDown.value);
getList()[0].click();
assertEquals('dog', dropDown.value);
// Make sure final value does not change while searching.
search('ta');
assertEquals('dog', dropDown.value);
});
// If the update-value-on-input flag is passed, final value should be whatever
// is in the search box.
test('value is set on click and on search', function() {
dropDown.updateValueOnInput = true;
setItems(['dog', 'cat', 'mouse']);
assertNotEquals('dog', dropDown.value);
getList()[0].click();
assertEquals('dog', dropDown.value);
// Make sure final value does change while searching.
search('ta');
assertEquals('ta', dropDown.value);
});
});
...@@ -401,41 +401,3 @@ suite('CupsAddPrinterDialogTests', function() { ...@@ -401,41 +401,3 @@ suite('CupsAddPrinterDialogTests', function() {
}); });
}); });
}); });
suite('CupsDropDownSearchBoxTests', function() {
let searchBox;
setup(function() {
PolymerTest.clearBody();
searchBox = document.createElement('drop-down-search-box');
document.body.appendChild(searchBox);
assertTrue(!!searchBox);
Polymer.dom.flush();
});
test('searchingWithInput', function() {
function testFilteredLength(filter, length) {
searchBox.$.search.value = filter;
Polymer.dom.flush();
assertEquals(
length, searchBox.shadowRoot.querySelectorAll('.list-item').length);
}
searchBox.items = ['a', 'aa', 'b', 'c'];
testFilteredLength('a', 2);
// Makes sure final value doesn't change while searching.
assertTrue(searchBox.value === undefined);
testFilteredLength('aa', 1);
testFilteredLength('a', 2);
testFilteredLength('ab', 0);
testFilteredLength('b', 1);
testFilteredLength('', 4);
testFilteredLength('c', 1);
// Makes sure final value doesn't change while searching.
assertTrue(searchBox.value === undefined);
// Clicking on an item sets the final value of the search box.
searchBox.$$('.list-item').click();
assertEquals('c', searchBox.value);
});
});
...@@ -18,6 +18,7 @@ group("closure_compile") { ...@@ -18,6 +18,7 @@ group("closure_compile") {
"cr_link_row:closure_compile", "cr_link_row:closure_compile",
"cr_profile_avatar_selector:closure_compile", "cr_profile_avatar_selector:closure_compile",
"cr_radio_button:closure_compile", "cr_radio_button:closure_compile",
"cr_searchable_drop_down:closure_compile",
"cr_slider:closure_compile", "cr_slider:closure_compile",
"cr_toast:closure_compile", "cr_toast:closure_compile",
"cr_toggle:closure_compile", "cr_toggle:closure_compile",
......
# Copyright 2018 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.
import("//third_party/closure_compiler/compile_js.gni")
js_type_check("closure_compile") {
deps = [
":cr_searchable_drop_down",
]
}
js_library("cr_searchable_drop_down") {
}
<link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
<link rel="import" href="chrome://resources/cr_elements/cr_scrollable_behavior.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-dropdown/iron-dropdown.html">
<dom-module id="cr-searchable-drop-down">
<template>
<style>
cr-input {
--cr-input-error-display: none;
}
iron-dropdown,
cr-input {
width: 270px;
}
iron-dropdown {
height: 270px;
}
iron-dropdown [slot='dropdown-content'] {
background-color: white;
box-shadow: 0 2px 6px var(--paper-grey-500);
min-width: 128px;
padding: 8px 0;
}
.list-item {
background: none;
border: none;
box-sizing: border-box;
color: var(--paper-grey-900);
font: inherit;
min-height: 32px;
padding: 0 24px;
text-align: start;
width: 100%;
@apply --settings-actionable;
}
.list-item:focus {
background-color: var(--google-grey-200);
outline: none;
}
</style>
<!-- |value| is one-way binding on purpose so that it doesn't change
immediately as the user types unless the update-value-on-input flag is
explicitly used. -->
<cr-input label="[[label]]" on-click="onClick_" value="[[value]]"
on-input="onInput_" id="search">
</cr-input>
<iron-dropdown horizontal-align="left" vertical-align="top"
vertical-offset="52">
<div slot="dropdown-content">
<template is="dom-repeat" items="[[items]]"
filter="[[filterItems_(searchTerm_)]]">
<button class="list-item" on-click="onSelect_">[[item]]</button>
</template>
</div>
</iron-dropdown>
</template>
<script src="cr_searchable_drop_down.js"></script>
</dom-module>
\ No newline at end of file
// Copyright 2018 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 'cr-searchable-drop-down' implements a search box with a
* suggestions drop down.
*
* If the update-value-on-input flag is set, value will be set to whatever is
* in the input box. Otherwise, value will only be set when an element in items
* is clicked.
*/
Polymer({
is: 'cr-searchable-drop-down',
properties: {
/** @type {!Array<string>} */
items: Array,
/** @type {string} */
value: {
type: String,
notify: true,
},
/** @private {string} */
searchTerm_: String,
/** @type {string} */
label: {
type: String,
value: '',
},
/** @type {boolean} */
updateValueOnInput: Boolean,
},
/** @private */
onClick_: function() {
this.$$('iron-dropdown').open();
},
/** @private */
onInput_: function() {
this.searchTerm_ = this.$.search.value;
if (this.updateValueOnInput) {
this.value = this.$.search.value;
}
},
/**
* @param {{model:Object}} event
* @private
*/
onSelect_: function(event) {
this.$$('iron-dropdown').close();
this.value = event.model.item;
this.searchTerm_ = '';
},
/** @private */
filterItems_: function(searchTerm) {
if (!searchTerm)
return null;
return function(item) {
return item.toLowerCase().includes(searchTerm.toLowerCase());
};
},
});
\ No newline at end of file
...@@ -118,6 +118,14 @@ ...@@ -118,6 +118,14 @@
file="cr_elements/cr_slider/cr_slider.js" file="cr_elements/cr_slider/cr_slider.js"
type="chrome_html" type="chrome_html"
compress="gzip" /> compress="gzip" />
<structure name="IDR_CR_ELEMENTS_CR_SEARCHABLE_DROP_DOWN_HTML"
file="cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html"
type="chrome_html"
compress="gzip" />
<structure name="IDR_CR_ELEMENTS_CR_SEARCHABLE_DROP_DOWN_JS"
file="cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js"
type="chrome_html"
compress="gzip" />
<if expr="chromeos"> <if expr="chromeos">
<structure name="IDR_CR_ELEMENTS_CHROMEOS_CR_PICTURE_CR_CAMERA_HTML" <structure name="IDR_CR_ELEMENTS_CHROMEOS_CR_PICTURE_CR_CAMERA_HTML"
file="cr_elements/chromeos/cr_picture/cr_camera.html" file="cr_elements/chromeos/cr_picture/cr_camera.html"
......
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