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