Skip to content

Commit bc7d887

Browse files
committed
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`.
1 parent 5cd5f63 commit bc7d887

4 files changed

Lines changed: 83 additions & 26 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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+
render() {
11+
return (
12+
<Fixture>
13+
<form
14+
className="control-box"
15+
onSubmit={event => {
16+
event.preventDefault();
17+
this.setState({formSubmitted: true});
18+
}}>
19+
<fieldset>
20+
<legend>Email</legend>
21+
{!this.state.formSubmitted ? (
22+
<input type="email" />
23+
) : (
24+
<input type="text" disabled={true} />
25+
)}
26+
</fieldset>
27+
</form>
28+
</Fixture>
29+
);
30+
}
31+
}
32+
33+
export default ReplaceEmailInput;

fixtures/dom/src/components/fixtures/text-inputs/index.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Fixture from '../../Fixture';
22
import FixtureSet from '../../FixtureSet';
33
import TestCase from '../../TestCase';
44
import InputTestCase from './InputTestCase';
5+
import ReplaceEmailInput from './ReplaceEmailInput';
56

67
const React = window.React;
78

@@ -110,6 +111,21 @@ class TextInputFixtures extends React.Component {
110111
<InputTestCase type="url" defaultValue="" />
111112
</TestCase>
112113

114+
<TestCase
115+
title="Replacing email input with text disabled input"
116+
relatedIssues="12062">
117+
<TestCase.Steps>
118+
<li>Type "test@test.com"</li>
119+
<li>Press enter</li>
120+
</TestCase.Steps>
121+
122+
<TestCase.ExpectedResult>
123+
There should be no selection-related error in the console.
124+
</TestCase.ExpectedResult>
125+
126+
<ReplaceEmailInput />
127+
</TestCase>
128+
113129
<TestCase title="All inputs" description="General test of all inputs">
114130
<InputTestCase type="text" defaultValue="Text" />
115131
<InputTestCase type="email" defaultValue="user@example.com" />

packages/react-dom/src/client/ReactInputSelection.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@ export function hasSelectionCapabilities(elem) {
2626
const nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();
2727
return (
2828
nodeName &&
29-
((nodeName === 'input' && elem.type === 'text') ||
29+
((nodeName === 'input' &&
30+
(elem.type === 'text' ||
31+
elem.type === 'search' ||
32+
elem.type === 'tel' ||
33+
elem.type === 'url' ||
34+
elem.type === 'password')) ||
3035
nodeName === 'textarea' ||
3136
elem.contentEditable === 'true')
3237
);
@@ -52,7 +57,10 @@ export function restoreSelection(priorSelectionInformation) {
5257
const priorFocusedElem = priorSelectionInformation.focusedElem;
5358
const priorSelectionRange = priorSelectionInformation.selectionRange;
5459
if (curFocusedElem !== priorFocusedElem && isInDocument(priorFocusedElem)) {
55-
if (hasSelectionCapabilities(priorFocusedElem)) {
60+
if (
61+
priorSelectionRange !== null &&
62+
hasSelectionCapabilities(priorFocusedElem)
63+
) {
5664
setSelection(priorFocusedElem, priorSelectionRange);
5765
}
5866

scripts/rollup/results.json

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -46,43 +46,43 @@
4646
"filename": "react-dom.development.js",
4747
"bundleType": "UMD_DEV",
4848
"packageName": "react-dom",
49-
"size": 591513,
50-
"gzip": 138743
49+
"size": 591647,
50+
"gzip": 138778
5151
},
5252
{
5353
"filename": "react-dom.production.min.js",
5454
"bundleType": "UMD_PROD",
5555
"packageName": "react-dom",
56-
"size": 96778,
57-
"gzip": 31445
56+
"size": 96862,
57+
"gzip": 31467
5858
},
5959
{
6060
"filename": "react-dom.development.js",
6161
"bundleType": "NODE_DEV",
6262
"packageName": "react-dom",
63-
"size": 575526,
64-
"gzip": 134516
63+
"size": 575660,
64+
"gzip": 134558
6565
},
6666
{
6767
"filename": "react-dom.production.min.js",
6868
"bundleType": "NODE_PROD",
6969
"packageName": "react-dom",
70-
"size": 95503,
71-
"gzip": 30619
70+
"size": 95587,
71+
"gzip": 30644
7272
},
7373
{
7474
"filename": "ReactDOM-dev.js",
7575
"bundleType": "FB_DEV",
7676
"packageName": "react-dom",
77-
"size": 594783,
78-
"gzip": 136782
77+
"size": 594973,
78+
"gzip": 136823
7979
},
8080
{
8181
"filename": "ReactDOM-prod.js",
8282
"bundleType": "FB_PROD",
8383
"packageName": "react-dom",
84-
"size": 279046,
85-
"gzip": 53062
84+
"size": 279248,
85+
"gzip": 53103
8686
},
8787
{
8888
"filename": "react-dom-test-utils.development.js",
@@ -401,31 +401,31 @@
401401
},
402402
{
403403
"filename": "react-is.development.js",
404-
"bundleType": "NODE_DEV",
404+
"bundleType": "UMD_DEV",
405405
"packageName": "react-is",
406-
"size": 3358,
407-
"gzip": 1015
406+
"size": 3547,
407+
"gzip": 1071
408408
},
409409
{
410410
"filename": "react-is.production.min.js",
411-
"bundleType": "NODE_PROD",
411+
"bundleType": "UMD_PROD",
412412
"packageName": "react-is",
413-
"size": 1433,
414-
"gzip": 607
413+
"size": 1515,
414+
"gzip": 670
415415
},
416416
{
417417
"filename": "react-is.development.js",
418-
"bundleType": "UMD_DEV",
418+
"bundleType": "NODE_DEV",
419419
"packageName": "react-is",
420-
"size": 3547,
421-
"gzip": 1071
420+
"size": 3358,
421+
"gzip": 1015
422422
},
423423
{
424424
"filename": "react-is.production.min.js",
425-
"bundleType": "UMD_PROD",
425+
"bundleType": "NODE_PROD",
426426
"packageName": "react-is",
427-
"size": 1515,
428-
"gzip": 670
427+
"size": 1433,
428+
"gzip": 607
429429
}
430430
]
431431
}

0 commit comments

Comments
 (0)