@samitouri / QOS-React / commits / e0a03c1b4d

Extend input type check in selection capabilities (#12062) (#12135)

* Do not set selection when prior selection is undefined (#12062) `restoreSelection` did not account for input elements that have changed type after the commit phase. The new `text` input supported selection but the old `email` did not and `setSelection` was incorrectly trying to restore `null` selection state. We also extend input type check in selection capabilities to cover cases where input type is `search`, `tel`, `url`, or `password`. * Add link to HTML spec for element types and selection * Add reset button to ReplaceEmailInput This commit adds a button to restore the original state of the ReplaceEmailInput fixture so that it can be run multiple times without refreshing the page.

Spyros Ioakeimidis committed May 30, 2018 at 13:08 UTC e0a03c1b4d84453122c4e9d1a5e0cec52bef9066
3 files changed +71 -2
fixtures/dom/src/components/fixtures/text-inputs/ReplaceEmailInput.js new
+40
@@ -0,0 +1,40 @@
1 +import Fixture from '../../Fixture';
2 +
3 +const React = window.React;
4 +
5 +class ReplaceEmailInput extends React.Component {
6 + state = {
7 + formSubmitted: false,
8 + };
9 +
10 + onReset = () => {
11 + this.setState({formSubmitted: false});
12 + };
13 +
14 + onSubmit = event => {
15 + event.preventDefault();
16 + this.setState({formSubmitted: true});
17 + };
18 +
19 + render() {
20 + return (
21 + <Fixture>
22 + <form className="control-box" onSubmit={this.onSubmit}>
23 + <fieldset>
24 + <legend>Email</legend>
25 + {!this.state.formSubmitted ? (
26 + <input type="email" />
27 + ) : (
28 + <input type="text" disabled={true} />
29 + )}
30 + </fieldset>
31 + </form>
32 + <button type="button" onClick={this.onReset}>
33 + Reset
34 + </button>
35 + </Fixture>
36 + );
37 + }
38 +}
39 +
40 +export default ReplaceEmailInput;
fixtures/dom/src/components/fixtures/text-inputs/index.js
+16
@@ -2,6 +2,7 @@ import Fixture from '../../Fixture';
2 import FixtureSet from '../../FixtureSet';
3 import TestCase from '../../TestCase';
4 import InputTestCase from './InputTestCase';
5 +import ReplaceEmailInput from './ReplaceEmailInput';
6
7 const React = window.React;
8
@@ -125,6 +126,21 @@ class TextInputFixtures extends React.Component {
126 <InputTestCase type="url" defaultValue="" />
127 </TestCase>
128
129 + <TestCase
130 + title="Replacing email input with text disabled input"
131 + relatedIssues="12062">
132 + <TestCase.Steps>
133 + <li>Type "test@test.com"</li>
134 + <li>Press enter</li>
135 + </TestCase.Steps>
136 +
137 + <TestCase.ExpectedResult>
138 + There should be no selection-related error in the console.
139 + </TestCase.ExpectedResult>
140 +
141 + <ReplaceEmailInput />
142 + </TestCase>
143 +
144 <TestCase title="All inputs" description="General test of all inputs">
145 <InputTestCase type="text" defaultValue="Text" />
146 <InputTestCase type="email" defaultValue="user@example.com" />
packages/react-dom/src/client/ReactInputSelection.js
+15 -2
@@ -22,11 +22,21 @@ function isInDocument(node) {
22 * Input selection module for React.
23 */
24
25 +/**
26 + * @hasSelectionCapabilities: we get the element types that support selection
27 + * from https://html.spec.whatwg.org/#do-not-apply, looking at `selectionStart`
28 + * and `selectionEnd` rows.
29 + */
30 export function hasSelectionCapabilities(elem) {
31 const nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();
32 return (
33 nodeName &&
29 - ((nodeName === 'input' && elem.type === 'text') ||
34 + ((nodeName === 'input' &&
35 + (elem.type === 'text' ||
36 + elem.type === 'search' ||
37 + elem.type === 'tel' ||
38 + elem.type === 'url' ||
39 + elem.type === 'password')) ||
40 nodeName === 'textarea' ||
41 elem.contentEditable === 'true')
42 );
@@ -52,7 +62,10 @@ export function restoreSelection(priorSelectionInformation) {
62 const priorFocusedElem = priorSelectionInformation.focusedElem;
63 const priorSelectionRange = priorSelectionInformation.selectionRange;
64 if (curFocusedElem !== priorFocusedElem && isInDocument(priorFocusedElem)) {
55 - if (hasSelectionCapabilities(priorFocusedElem)) {
65 + if (
66 + priorSelectionRange !== null &&
67 + hasSelectionCapabilities(priorFocusedElem)
68 + ) {
69 setSelection(priorFocusedElem, priorSelectionRange);
70 }
71