Commit a91079b2 authored by dpapad's avatar dpapad Committed by Commit Bot

WebUI: Port cr-input and its tests to Polymer3

Also porting cr-icons style module, which is a transitive
dependency of cr-input.

Bug: 965770
Change-Id: I6e5ec4fdf33a5b0f48a51e66e772254638200982
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1733855
Commit-Queue: Demetrios Papadopoulos <dpapad@chromium.org>
Reviewed-by: default avatarRebekah Potter <rbpotter@chromium.org>
Cr-Commit-Position: refs/heads/master@{#684129}
parent 8540108f
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import 'chrome://resources/cr_elements/cr_button/cr_button.m.js'; import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.m.js'; import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.m.js';
import 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
import 'chrome://resources/cr_elements/cr_toast/cr_toast.m.js'; import 'chrome://resources/cr_elements/cr_toast/cr_toast.m.js';
import 'chrome://resources/cr_elements/cr_toggle/cr_toggle.m.js'; import 'chrome://resources/cr_elements/cr_toggle/cr_toggle.m.js';
import 'chrome://resources/cr_elements/icons.m.js'; import 'chrome://resources/cr_elements/icons.m.js';
...@@ -37,6 +38,8 @@ class HelloPolymer3Element extends PolymerElement { ...@@ -37,6 +38,8 @@ class HelloPolymer3Element extends PolymerElement {
<option>cool</option> <option>cool</option>
</select> </select>
<cr-input></cr-input>
<div> <div>
<cr-button on-click="onClick_">Show toast</cr-button> <cr-button on-click="onClick_">Show toast</cr-button>
<cr-toast><span>I am toasted</span></cr-toast> <cr-toast><span>I am toasted</span></cr-toast>
......
...@@ -6,6 +6,7 @@ def _CheckForModularizedTests(input_api, output_api): ...@@ -6,6 +6,7 @@ def _CheckForModularizedTests(input_api, output_api):
module_tests = [ module_tests = [
'cr_elements/cr_button_tests.m.js', 'cr_elements/cr_button_tests.m.js',
'cr_elements/cr_checkbox_test.m.js', 'cr_elements/cr_checkbox_test.m.js',
'cr_elements/cr_input_test.m.js',
'cr_elements/cr_toast_test.m.js', 'cr_elements/cr_toast_test.m.js',
'cr_elements/cr_toggle_test.m.js', 'cr_elements/cr_toggle_test.m.js',
'cr_elements/cr_view_manager_test.m.js', 'cr_elements/cr_view_manager_test.m.js',
......
...@@ -51,3 +51,15 @@ var CrElementsToggleV3Test = class extends CrElementsV3FocusTest { ...@@ -51,3 +51,15 @@ var CrElementsToggleV3Test = class extends CrElementsV3FocusTest {
TEST_F('CrElementsToggleV3Test', 'All', function() { TEST_F('CrElementsToggleV3Test', 'All', function() {
mocha.run(); mocha.run();
}); });
// eslint-disable-next-line no-var
var CrElementsInputV3Test = class extends CrElementsV3FocusTest {
/** @override */
get browsePreload() {
return 'chrome://test?module=cr_elements/cr_input_test.m.js';
}
};
TEST_F('CrElementsInputV3Test', 'All', function() {
mocha.run();
});
// Copyright 2019 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 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {eventToPromise, whenAttributeIs} from 'chrome://test/settings/test_util.m.js';
suite('cr-input', function() {
let crInput;
let input;
setup(function() {
regenerateNewInput();
});
function regenerateNewInput() {
PolymerTest.clearBody();
crInput = document.createElement('cr-input');
document.body.appendChild(crInput);
input = crInput.$.input;
flush();
}
test('AttributesCorrectlySupported', function() {
const attributesToTest = [
// [externalName, internalName, defaultValue, testValue]
['autofocus', 'autofocus', false, true],
['disabled', 'disabled', false, true],
['max', 'max', '', '100'],
['min', 'min', '', '1'],
['maxlength', 'maxLength', -1, 5],
['minlength', 'minLength', -1, 5],
['pattern', 'pattern', '', '[a-z]+'],
['readonly', 'readOnly', false, true],
['required', 'required', false, true],
['tabindex', 'tabIndex', 0, -1],
['type', 'type', 'text', 'password'],
];
attributesToTest.forEach(attr => {
regenerateNewInput();
assertEquals(attr[2], input[attr[1]]);
crInput.setAttribute(attr[0], attr[3]);
assertEquals(attr[3], input[attr[1]]);
});
});
test('togglingDisableModifiesTabIndexCorrectly', function() {
// Do innerHTML instead of createElement to make sure it's correct right
// after being attached, and not messed up by disabledChanged_.
PolymerTest.clearBody();
document.body.innerHTML = `
<cr-input tabindex="14"></cr-input>
`;
crInput = document.querySelector('cr-input');
input = crInput.$.input;
flush();
assertEquals('14', crInput.getAttribute('tabindex'));
assertEquals(14, input.tabIndex);
crInput.disabled = true;
assertEquals(null, crInput.getAttribute('tabindex'));
assertEquals(true, input.disabled);
crInput.disabled = false;
assertEquals('14', crInput.getAttribute('tabindex'));
assertEquals(14, input.tabIndex);
});
test('startingWithDisableSetsTabIndexCorrectly', function() {
// Makes sure tabindex is recorded even if cr-input starts as disabled
PolymerTest.clearBody();
document.body.innerHTML = `
<cr-input tabindex="14" disabled></cr-input>
`;
crInput = document.querySelector('cr-input');
input = crInput.$.input;
flush();
return whenAttributeIs(input, 'tabindex', null).then(() => {
assertEquals(null, crInput.getAttribute('tabindex'));
assertEquals(true, input.disabled);
crInput.disabled = false;
assertEquals('14', crInput.getAttribute('tabindex'));
assertEquals(14, input.tabIndex);
});
});
test('pointerDownAndTabIndex', function() {
crInput.fire('pointerdown');
assertEquals(null, crInput.getAttribute('tabindex'));
return whenAttributeIs(crInput, 'tabindex', '0').then(() => {
assertEquals(0, input.tabIndex);
});
});
test('pointerdownWhileDisabled', function(done) {
// pointerdown while disabled doesn't mess with tabindex.
crInput.tabindex = 14;
crInput.disabled = true;
assertEquals(null, crInput.getAttribute('tabindex'));
assertEquals(true, input.disabled);
crInput.fire('pointerdown');
assertEquals(null, crInput.getAttribute('tabindex'));
// Wait one cycle to make sure tabindex is still unchanged afterwards.
setTimeout(() => {
assertEquals(null, crInput.getAttribute('tabindex'));
// Makes sure tabindex is correctly restored after reverting disabled.
crInput.disabled = false;
assertEquals('14', crInput.getAttribute('tabindex'));
assertEquals(14, input.tabIndex);
done();
});
});
test('pointerdownThenDisabledInSameCycle', function(done) {
crInput.tabindex = 14;
// Edge case: pointerdown and disabled are changed in the same cycle.
crInput.fire('pointerdown');
crInput.disabled = true;
assertEquals(null, crInput.getAttribute('tabindex'));
// Wait one cycle to make sure tabindex is still unchanged afterwards.
setTimeout(() => {
assertEquals(null, crInput.getAttribute('tabindex'));
// Makes sure tabindex is correctly restored after reverting disabled.
crInput.disabled = false;
assertEquals('14', crInput.getAttribute('tabindex'));
assertEquals(14, input.tabIndex);
done();
});
});
test('placeholderCorrectlyBound', function() {
assertFalse(input.hasAttribute('placeholder'));
crInput.placeholder = '';
assertTrue(input.hasAttribute('placeholder'));
crInput.placeholder = 'hello';
assertEquals('hello', input.getAttribute('placeholder'));
crInput.placeholder = null;
assertFalse(input.hasAttribute('placeholder'));
});
test('labelHiddenWhenEmpty', function() {
const label = crInput.$.label;
assertEquals('none', getComputedStyle(crInput.$.label).display);
crInput.label = 'foobar';
assertEquals('block', getComputedStyle(crInput.$.label).display);
assertEquals('foobar', label.textContent.trim());
});
test('valueSetCorrectly', function() {
crInput.value = 'hello';
assertEquals(crInput.value, input.value);
// |value| is copied when typing triggers inputEvent.
input.value = 'hello sir';
input.dispatchEvent(new InputEvent('input'));
assertEquals(crInput.value, input.value);
});
test('focusState', function() {
assertFalse(crInput.hasAttribute('focused_'));
const underline = crInput.$.underline;
const label = crInput.$.label;
const originalLabelColor = getComputedStyle(label).color;
assertEquals('0', getComputedStyle(underline).opacity);
assertEquals(0, underline.offsetWidth);
let whenTransitionEnd = eventToPromise('transitionend', underline);
input.focus();
assertTrue(crInput.hasAttribute('focused_'));
assertTrue(originalLabelColor != getComputedStyle(label).color);
return whenTransitionEnd
.then(() => {
assertEquals('1', getComputedStyle(underline).opacity);
assertTrue(0 != underline.offsetWidth);
})
.then(() => {
input.blur();
whenTransitionEnd = eventToPromise('transitionend', underline);
// Wait for underline to fade out.
return whenTransitionEnd;
})
.then(() => {
whenTransitionEnd = eventToPromise('transitionend', underline);
assertFalse(crInput.hasAttribute('focused_'));
assertEquals('0', getComputedStyle(underline).opacity);
// The width transition has a delay larger than the opacity transition
// duration so that the width can be reset to 0 after the underline is
// no longer visible.
return whenTransitionEnd;
})
.then(() => {
assertEquals(0, underline.offsetWidth);
});
});
test('invalidState', function() {
crInput.errorMessage = 'error';
const errorLabel = crInput.$.error;
const underline = crInput.$.underline;
const label = crInput.$.label;
const originalLabelColor = getComputedStyle(label).color;
const originalLineColor = getComputedStyle(underline).borderBottomColor;
assertEquals(crInput.errorMessage, errorLabel.textContent);
assertEquals('0', getComputedStyle(underline).opacity);
assertEquals(0, underline.offsetWidth);
assertEquals('hidden', getComputedStyle(errorLabel).visibility);
let whenTransitionEnd = eventToPromise('transitionend', underline);
crInput.invalid = true;
flush();
assertTrue(crInput.hasAttribute('invalid'));
assertEquals('visible', getComputedStyle(errorLabel).visibility);
assertTrue(originalLabelColor != getComputedStyle(label).color);
assertTrue(
originalLineColor != getComputedStyle(underline).borderBottomColor);
return whenTransitionEnd.then(() => {
assertEquals('1', getComputedStyle(underline).opacity);
assertTrue(0 != underline.offsetWidth);
});
});
test('validation', function() {
crInput.value = 'FOO';
crInput.autoValidate = true;
assertFalse(crInput.hasAttribute('required'));
assertFalse(crInput.invalid);
// Note that even with |autoValidate|, crInput.invalid only updates after
// |value| is changed.
crInput.setAttribute('required', '');
assertFalse(crInput.invalid);
crInput.value = '';
assertTrue(crInput.invalid);
crInput.value = 'BAR';
assertFalse(crInput.invalid);
const testPattern = '[a-z]+';
crInput.setAttribute('pattern', testPattern);
crInput.value = 'FOO';
assertTrue(crInput.invalid);
crInput.value = 'foo';
assertFalse(crInput.invalid);
// Without |autoValidate|, crInput.invalid should not change even if input
// value is not valid.
crInput.autoValidate = false;
crInput.value = 'ALL CAPS';
assertFalse(crInput.invalid);
assertFalse(input.checkValidity());
crInput.value = '';
assertFalse(crInput.invalid);
assertFalse(input.checkValidity());
});
test('numberValidation', function() {
crInput.type = 'number';
crInput.value = '50';
crInput.autoValidate = true;
assertFalse(crInput.invalid);
// Note that even with |autoValidate|, crInput.invalid only updates after
// |value| is changed.
const testMin = 1;
const testMax = 100;
crInput.setAttribute('min', testMin);
crInput.setAttribute('max', testMax);
crInput.value = '200';
assertTrue(crInput.invalid);
crInput.value = '20';
assertFalse(crInput.invalid);
crInput.value = '-2';
assertTrue(crInput.invalid);
crInput.value = '40';
assertFalse(crInput.invalid);
// Without |autoValidate|, crInput.invalid should not change even if input
// value is not valid.
crInput.autoValidate = false;
crInput.value = '200';
assertFalse(crInput.invalid);
assertFalse(input.checkValidity());
crInput.value = '-2';
assertFalse(crInput.invalid);
assertFalse(input.checkValidity());
});
test('ariaLabelsCorrect', function() {
assertFalse(!!crInput.inputElement.getAttribute('aria-label'));
/**
* This function assumes attributes are passed in priority order.
* @param {!Array<string>} attributes
*/
function testAriaLabel(attributes) {
PolymerTest.clearBody();
crInput = document.createElement('cr-input');
attributes.forEach(attribute => {
// Using their name as the value out of convenience.
crInput.setAttribute(attribute, attribute);
});
document.body.appendChild(crInput);
flush();
// Assuming first attribute takes priority.
assertEquals(
attributes[0], crInput.inputElement.getAttribute('aria-label'));
}
testAriaLabel(['aria-label', 'label', 'placeholder']);
testAriaLabel(['label', 'placeholder']);
testAriaLabel(['placeholder']);
});
test('select', function() {
crInput.value = '0123456789';
assertFalse(input.matches(':focus'));
crInput.select();
assertTrue(input.matches(':focus'));
assertEquals('0123456789', window.getSelection().toString());
regenerateNewInput();
crInput.value = '0123456789';
assertFalse(input.matches(':focus'));
crInput.select(2, 6);
assertTrue(input.matches(':focus'));
assertEquals('2345', window.getSelection().toString());
regenerateNewInput();
crInput.value = '';
assertFalse(input.matches(':focus'));
crInput.select();
assertTrue(input.matches(':focus'));
assertEquals('', window.getSelection().toString());
});
});
...@@ -30,6 +30,7 @@ group("closure_compile") { ...@@ -30,6 +30,7 @@ group("closure_compile") {
# Targets for auto-generated Polymer 3 JS Modules # Targets for auto-generated Polymer 3 JS Modules
"cr_button:closure_compile_module", "cr_button:closure_compile_module",
"cr_checkbox:closure_compile_module", "cr_checkbox:closure_compile_module",
"cr_input:closure_compile_module",
"cr_toast:closure_compile_module", "cr_toast:closure_compile_module",
"cr_toggle:closure_compile_module", "cr_toggle:closure_compile_module",
"cr_view_manager:closure_compile_module", "cr_view_manager:closure_compile_module",
...@@ -91,6 +92,12 @@ polymer_modulizer("md_select_css") { ...@@ -91,6 +92,12 @@ polymer_modulizer("md_select_css") {
html_type = "style-module" html_type = "style-module"
} }
polymer_modulizer("cr_icons_css") {
js_file = "cr_icons_css.m.js"
html_file = "cr_icons_css.html"
html_type = "style-module"
}
polymer_modulizer("icons") { polymer_modulizer("icons") {
js_file = "icons.m.js" js_file = "icons.m.js"
html_file = "icons.html" html_file = "icons.html"
...@@ -99,6 +106,7 @@ polymer_modulizer("icons") { ...@@ -99,6 +106,7 @@ polymer_modulizer("icons") {
group("polymer3_elements") { group("polymer3_elements") {
deps = [ deps = [
":cr_icons_css_module",
":hidden_style_css_module", ":hidden_style_css_module",
":icons_module", ":icons_module",
":md_select_css_module", ":md_select_css_module",
...@@ -106,6 +114,8 @@ group("polymer3_elements") { ...@@ -106,6 +114,8 @@ group("polymer3_elements") {
":shared_vars_css_module", ":shared_vars_css_module",
"cr_button:cr_button_module", "cr_button:cr_button_module",
"cr_checkbox:cr_checkbox_module", "cr_checkbox:cr_checkbox_module",
"cr_input:cr_input_module",
"cr_input:cr_input_style_css_module",
"cr_toast:cr_toast_module", "cr_toast:cr_toast_module",
"cr_toggle:cr_toggle_module", "cr_toggle:cr_toggle_module",
"cr_view_manager:cr_view_manager_module", "cr_view_manager:cr_view_manager_module",
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
# found in the LICENSE file. # found in the LICENSE file.
import("//third_party/closure_compiler/compile_js.gni") import("//third_party/closure_compiler/compile_js.gni")
import("//tools/polymer/polymer.gni")
js_type_check("closure_compile") { js_type_check("closure_compile") {
deps = [ deps = [
...@@ -15,3 +16,34 @@ js_library("cr_input") { ...@@ -15,3 +16,34 @@ js_library("cr_input") {
"//ui/webui/resources/js:assert", "//ui/webui/resources/js:assert",
] ]
} }
polymer_modulizer("cr_input") {
js_file = "cr_input.js"
html_file = "cr_input.html"
html_type = "dom-module"
auto_imports = [ "ui/webui/resources/html/assert.html|assert" ]
}
polymer_modulizer("cr_input_style_css") {
js_file = "cr_input_style_css.m.js"
html_file = "cr_input_style_css.html"
html_type = "style-module"
}
js_type_check("closure_compile_module") {
is_polymer3 = true
deps = [
":cr_input.m",
]
}
js_library("cr_input.m") {
sources = [
"$root_gen_dir/ui/webui/resources/cr_elements/cr_input/cr_input.m.js",
]
deps = [
"../../js:assert.m",
"//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
]
extra_deps = [ ":cr_input_module" ]
}
...@@ -170,7 +170,7 @@ Polymer({ ...@@ -170,7 +170,7 @@ Polymer({
/** @return {!HTMLInputElement} */ /** @return {!HTMLInputElement} */
get inputElement() { get inputElement() {
return this.$.input; return /** @type {!HTMLInputElement} */ (this.$.input);
}, },
/** @private */ /** @private */
......
...@@ -11,6 +11,21 @@ ...@@ -11,6 +11,21 @@
use_base_dir="false" use_base_dir="false"
type="BINDATA" type="BINDATA"
compress="gzip" /> compress="gzip" />
<include name="IDR_CR_ELEMENTS_CR_ICONS_CSS_M_JS"
file="${root_gen_dir}/ui/webui/resources/cr_elements/cr_icons_css.m.js"
use_base_dir="false"
type="BINDATA"
compress="gzip" />
<include name="IDR_CR_ELEMENTS_CR_INPUT_M_JS"
file="${root_gen_dir}/ui/webui/resources/cr_elements/cr_input/cr_input.m.js"
use_base_dir="false"
type="BINDATA"
compress="gzip" />
<include name="IDR_CR_ELEMENTS_CR_INPUT_STYLE_CSS_M_JS"
file="${root_gen_dir}/ui/webui/resources/cr_elements/cr_input/cr_input_style_css.m.js"
use_base_dir="false"
type="BINDATA"
compress="gzip" />
<include name="IDR_CR_ELEMENTS_CR_SHARED_STYLE_CSS_M_JS" <include name="IDR_CR_ELEMENTS_CR_SHARED_STYLE_CSS_M_JS"
file="${root_gen_dir}/ui/webui/resources/cr_elements/shared_style_css.m.js" file="${root_gen_dir}/ui/webui/resources/cr_elements/shared_style_css.m.js"
use_base_dir="false" use_base_dir="false"
......
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