Commit 6e6760f6 authored by Martin Kreichgauer's avatar Martin Kreichgauer Committed by Commit Bot

webui: make cr-dialog capture enter key presses from any input element

Currently, the cr-dialog key press handler only triggers an action
button click when an enter keypress event has an 'CR-INPUT' target. This
means that if a cr-input is embedded in another custom element inside
the cr-dialog, enter key presses to that cr-input  will be ignored
because the corresponding event gets retargeted to the custom element.

Instead, make the keypress handler check that the first element in the
composedPath() is an 'INPUT'.

Bug: 955859
Change-Id: I486158fadbc32b95c3c3284240bfe4ae690d78d0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1677597
Commit-Queue: Martin Kreichgauer <martinkr@google.com>
Reviewed-by: default avatarDemetrios Papadopoulos <dpapad@chromium.org>
Cr-Commit-Position: refs/heads/master@{#672635}
parent b1fca6e9
...@@ -220,25 +220,32 @@ suite('cr-dialog', function() { ...@@ -220,25 +220,32 @@ suite('cr-dialog', function() {
assertTrue(clicked); assertTrue(clicked);
}); });
test('enter keys from cr-inputs (only) are processed', function() { test('enter keys from certain inputs only are processed', function() {
document.body.innerHTML = ` document.body.innerHTML = `
<cr-dialog> <cr-dialog>
<div slot="title">title</div> <div slot="title">title</div>
<div slot="body"> <div slot="body">
<cr-input></cr-input> <input></input>
<input type="text"></input>
<input type="password"></input>
<input type="checkbox"></input>
<foobar></foobar> <foobar></foobar>
<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('cr-input'); const inputElement = document.body.querySelector('input:not([type])');
const inputTextElement = document.body.querySelector('input[type="text"]');
const inputPasswordElement =
document.body.querySelector('input[type="password"]');
const inputCheckboxElement =
document.body.querySelector('input[type="checkbox"]');
const otherElement = document.body.querySelector('foobar'); const otherElement = document.body.querySelector('foobar');
const actionButton = document.body.querySelector('.action-button'); const actionButton = document.body.querySelector('.action-button');
assertTrue(!!inputElement); const crInputElement = document.body.querySelector('cr-input');
assertTrue(!!otherElement);
assertTrue(!!actionButton);
// MockInteractions triggers event listeners synchronously. // MockInteractions triggers event listeners synchronously.
let clickedCounter = 0; let clickedCounter = 0;
...@@ -246,8 +253,59 @@ suite('cr-dialog', function() { ...@@ -246,8 +253,59 @@ suite('cr-dialog', function() {
clickedCounter++; clickedCounter++;
}); });
// Only certain types of <input> trigger a dialog submit.
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);
assertEquals(3, clickedCounter);
// Also trigger dialog submit if code synthesizes enter on a cr-input
// without targeting the underlying input.
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.
let clickedCounter = 0;
actionButton.addEventListener('click', function() {
clickedCounter++;
});
pressEnter(inputElement); pressEnter(inputElement);
assertEquals(1, clickedCounter); assertEquals(1, clickedCounter);
......
...@@ -265,11 +265,17 @@ Polymer({ ...@@ -265,11 +265,17 @@ Polymer({
return; return;
} }
// Accept Enter keys from either the dialog, or a child input element. // Accept Enter keys from either the dialog, or a child input or cr-input
if (e.target != this && e.target.tagName != 'CR-INPUT') { // element (but consider that the event may have been retargeted).
const origTarget = e.composedPath()[0];
const accept = e.target == this || origTarget.tagName == 'CR-INPUT' ||
(origTarget.tagName == 'INPUT' &&
// <cr-input> can only be text-like; apply the same limit to <input> so
// that e.g. enter on a checkbox does not submit the dialog.
['text', 'password', 'number', 'search'].includes(origTarget.type));
if (!accept) {
return; return;
} }
const actionButton = const actionButton =
this.querySelector('.action-button:not([disabled]):not([hidden])'); this.querySelector('.action-button:not([disabled]):not([hidden])');
if (actionButton) { if (actionButton) {
......
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