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

WebUI cr-dialog: Change logic for accepting 'Enter' from input elements.

Problems with previous logic
 - Did not accept 'enter' for cr-input instances with type
   url' (example "add bookmark dialog")
 - Erroneously accepted 'enter' for cr-input instances with type
   'search' (example "add languages dialog")

Fixed by iterating on the event's |composedPath()| and looking whether a
cr-input exists.

Moreover, there was logic to limit <input> elements to the same types
supported with <cr-input> which seems unnecessary (we only care about
processing cr-input instances). Changed it to not accept <input>
elements until the need arises.

Fixed: 1010819
Change-Id: I25cf23c088318ee4b8003b65984cb735c8aec1b0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1839032Reviewed-by: default avatarMartin Kreichgauer <martinkr@google.com>
Reviewed-by: default avatarJohn Lee <johntlee@chromium.org>
Commit-Queue: Demetrios Papadopoulos <dpapad@chromium.org>
Cr-Commit-Position: refs/heads/master@{#703049}
parent b2949d9e
...@@ -238,27 +238,43 @@ suite('cr-dialog', function() { ...@@ -238,27 +238,43 @@ suite('cr-dialog', function() {
<cr-dialog> <cr-dialog>
<div slot="title">title</div> <div slot="title">title</div>
<div slot="body"> <div slot="body">
<input></input>
<input type="text"></input>
<input type="password"></input>
<input type="checkbox"></input>
<foobar></foobar> <foobar></foobar>
<input type="checkbox">
<input type="text">
<cr-input type="search"></cr-input>
<cr-input type="text"></cr-input>
<div id="withShadow"></div>
<button class="action-button">active</button> <button class="action-button">active</button>
<cr-input></cr-input>
</div> </div>
</cr-dialog>`; </cr-dialog>`;
const dialog = document.body.querySelector('cr-dialog'); const dialog = document.body.querySelector('cr-dialog');
const inputElement = document.body.querySelector('input:not([type])'); const otherElement = document.body.querySelector('foobar');
const inputTextElement = document.body.querySelector('input[type="text"]');
const inputPasswordElement =
document.body.querySelector('input[type="password"]');
const inputCheckboxElement = const inputCheckboxElement =
document.body.querySelector('input[type="checkbox"]'); document.body.querySelector('input[type="checkbox"]');
const otherElement = document.body.querySelector('foobar'); const inputTextElement = document.body.querySelector('input[type="text"]');
// Manually set the |type| property since cr-input is not actually imported
// as part of this test, and therefore the element is not upgraded, as it
// would normally.
const crTextInputElement =
document.body.querySelector('cr-input[type="text"]');
crTextInputElement.type = 'text';
const crSearchInputElement =
document.body.querySelector('cr-input[type="search"]');
crSearchInputElement.type = 'search';
// Attach a cr-input element nested within another element.
const containerElement = document.body.querySelector('#withShadow');
const shadow = containerElement.attachShadow({mode: 'open'});
const crInputNested = document.createElement('cr-input');
crInputNested.type = 'text';
shadow.appendChild(crInputNested);
const actionButton = document.body.querySelector('.action-button'); const actionButton = document.body.querySelector('.action-button');
const crInputElement = document.body.querySelector('cr-input');
// MockInteractions triggers event listeners synchronously. // MockInteractions triggers event listeners synchronously.
let clickedCounter = 0; let clickedCounter = 0;
...@@ -266,62 +282,25 @@ suite('cr-dialog', function() { ...@@ -266,62 +282,25 @@ suite('cr-dialog', function() {
clickedCounter++; clickedCounter++;
}); });
// Only certain types of <input> trigger a dialog submit. // Enter on anything other than cr-input should not be accepted.
pressEnter(otherElement); pressEnter(otherElement);
assertEquals(0, clickedCounter); assertEquals(0, clickedCounter);
// "type" defaults to text, which triggers the click.
pressEnter(inputElement);
assertEquals(1, clickedCounter);
pressEnter(inputTextElement);
assertEquals(2, clickedCounter);
pressEnter(inputPasswordElement);
assertEquals(3, clickedCounter);
pressEnter(inputCheckboxElement); pressEnter(inputCheckboxElement);
assertEquals(3, clickedCounter); assertEquals(0, clickedCounter);
// Also trigger dialog submit if code synthesizes enter on a cr-input pressEnter(inputTextElement);
// without targeting the underlying input. assertEquals(0, clickedCounter);
pressEnter(crInputElement);
assertEquals(4, clickedCounter);
});
// Test that enter key presses trigger an action button click, even if the
// even was retargeted, e.g. because the input was really a cr-input, the
// cr-input was part of another custom element, etc.
test('enter keys are processed even if event was retargeted', function() {
document.body.innerHTML = `
<dom-module id="test-element">
<template><input></input></template>
</dom-module>
<cr-dialog>
<div slot="title">title</div>
<div slot="body">
<test-element></test-element>
<button class="action-button">active</button>
</div>
</cr-dialog>`;
Polymer({
is: 'test-element',
});
const dialog = document.body.querySelector('cr-dialog');
const inputWrapper = document.body.querySelector('test-element');
assertTrue(!!inputWrapper);
const inputElement = inputWrapper.shadowRoot.querySelector('input');
const actionButton = document.body.querySelector('.action-button');
assertTrue(!!inputElement);
assertTrue(!!actionButton);
// MockInteractions triggers event listeners synchronously. // Enter on a cr-input with type "search" should not be accepted.
let clickedCounter = 0; pressEnter(crSearchInputElement);
actionButton.addEventListener('click', function() { assertEquals(0, clickedCounter);
clickedCounter++;
});
pressEnter(inputElement); // Enter on a cr-input with type "text" should be accepted.
pressEnter(crTextInputElement);
assertEquals(1, clickedCounter); assertEquals(1, clickedCounter);
// Enter on a nested <cr-input> should be accepted.
pressEnter(crInputNested);
assertEquals(2, clickedCounter);
}); });
test('focuses [autofocus] instead of title when present', function() { test('focuses [autofocus] instead of title when present', function() {
......
...@@ -265,14 +265,14 @@ Polymer({ ...@@ -265,14 +265,14 @@ Polymer({
return; return;
} }
// Accept Enter keys from either the dialog, or a child input or cr-input // Accept Enter keys from either the dialog itself, or a child cr-input,
// element (but consider that the event may have been retargeted). // considering that the event may have been retargeted, for example if the
const origTarget = e.composedPath()[0]; // cr-input is nested inside another element. Also exclude inputs of type
const accept = e.target == this || origTarget.tagName == 'CR-INPUT' || // 'search', since hitting 'Enter' on a search field most likely intends to
(origTarget.tagName == 'INPUT' && // trigger searching.
// <cr-input> can only be text-like; apply the same limit to <input> so const accept = e.target === this ||
// that e.g. enter on a checkbox does not submit the dialog. e.composedPath().some(
['text', 'password', 'number', 'search'].includes(origTarget.type)); el => el.tagName == 'CR-INPUT' && el.type != 'search');
if (!accept) { if (!accept) {
return; return;
} }
......
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