Skip to content

Commit

Permalink
Fix email cursor jump (#18379)
Browse files Browse the repository at this point in the history
* add email input fixture to show cursor jump

* fix cursor jump in email input

Co-authored-by: Peter Potapov <dr.potapoff-peter@yandex.ru>

* add regression tests to ensure attributes are working

Co-authored-by: Peter Potapov <dr.potapoff-peter@yandex.ru>
  • Loading branch information
bl00mber and Nizarius authored Apr 1, 2020
1 parent 5ee0efe commit 9b88b78
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 4 deletions.
1 change: 1 addition & 0 deletions fixtures/dom/src/components/Header.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class Header extends React.Component {
<option value="/text-inputs">Text Inputs</option>
<option value="/number-inputs">Number Input</option>
<option value="/password-inputs">Password Input</option>
<option value="/email-inputs">Email Input</option>
<option value="/selects">Selects</option>
<option value="/textareas">Textareas</option>
<option value="/input-change-events">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import Fixture from '../../Fixture';

const React = window.React;

class EmailDisabledAttributesTestCase extends React.Component {
state = {value: 'a@fb.com'};
onChange = event => {
this.setState({value: event.target.value});
};
render() {
return (
<Fixture>
<div>{this.props.children}</div>

<div className="control-box">
<fieldset>
<legend>Controlled</legend>
<input
type="email"
value={this.state.value}
onChange={this.onChange}
/>
<span className="hint">
{' '}
Value: {JSON.stringify(this.state.value)}
</span>
</fieldset>

<fieldset>
<legend>Uncontrolled</legend>
<input type="email" defaultValue="" />
</fieldset>
</div>
</Fixture>
);
}
}

export default EmailDisabledAttributesTestCase;
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Fixture from '../../Fixture';

const React = window.React;

class EmailAttributesTestCase extends React.Component {
state = {value: 'a@fb.com'};
onChange = event => {
this.setState({value: event.target.value});
};
render() {
return (
<Fixture>
<div>{this.props.children}</div>

<div className="control-box">
<fieldset>
<legend>Controlled</legend>
<input
type="email"
pattern=".+@fb.com"
maxlength={17}
multiple={true}
value={this.state.value}
onChange={this.onChange}
/>
<span className="hint">
{' '}
Value: {JSON.stringify(this.state.value)}
</span>
</fieldset>

<fieldset>
<legend>Uncontrolled</legend>
<input
type="email"
defaultValue=""
pattern=".+@fb.com"
maxlength={17}
multiple={true}
/>
</fieldset>
</div>
</Fixture>
);
}
}

export default EmailAttributesTestCase;
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import Fixture from '../../Fixture';

const React = window.React;

class JumpingCursorTestCase extends React.Component {
state = {value: ''};
onChange = event => {
this.setState({value: event.target.value});
};
render() {
return (
<Fixture>
<div>{this.props.children}</div>

<div className="control-box">
<fieldset>
<legend>Controlled</legend>
<input
type="email"
value={this.state.value}
onChange={this.onChange}
/>
<span className="hint">
{' '}
Value: {JSON.stringify(this.state.value)}
</span>
</fieldset>

<fieldset>
<legend>Uncontrolled</legend>
<input type="email" defaultValue="" />
</fieldset>
</div>
</Fixture>
);
}
}

export default JumpingCursorTestCase;
68 changes: 68 additions & 0 deletions fixtures/dom/src/components/fixtures/email-inputs/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import FixtureSet from '../../FixtureSet';
import TestCase from '../../TestCase';
import JumpingCursorTestCase from './JumpingCursorTestCase';
import EmailEnabledAttributesTestCase from './EmailEnabledAttributesTestCase';
import EmailDisabledAttributesTestCase from './EmailDisabledAttributesTestCase';

const React = window.React;

function EmailInputs() {
return (
<FixtureSet title="Email inputs">
<TestCase
title="Spaces in email inputs"
description={`
Some browsers are trying to remove spaces from email inputs and after
doing this place cursor to the beginning.
`}
affectedBrowsers="Chrome">
<TestCase.Steps>
<li>Type space and character</li>
<li>Type character, space, character, delete last character</li>
</TestCase.Steps>

<TestCase.ExpectedResult>Cursor not moving.</TestCase.ExpectedResult>

<JumpingCursorTestCase />
</TestCase>

<TestCase
title="Attributes enabled"
description={`
Test enabled pattern, maxlength, multiple attributes.
`}>
<TestCase.Steps>
<li>Type after existing text ',b@tt.com'</li>
<li>Try to type spaces after typed text</li>
</TestCase.Steps>

<TestCase.ExpectedResult>
Spaces not added. When cursor hovered over input, popup "Please match
the requested format." is showed.
</TestCase.ExpectedResult>

<EmailEnabledAttributesTestCase />
</TestCase>

<TestCase
title="Attributes disabled"
description={`
Test disabled maxlength, multiple attributes.
`}>
<TestCase.Steps>
<li>Type after existing text ',b@tt.com'</li>
<li>Try to type spaces after typed text</li>
</TestCase.Steps>

<TestCase.ExpectedResult>
Spaces are added freely. When cursor hovered over input, popup "A part
following '@' should not contain the symbol ','." is showed.
</TestCase.ExpectedResult>

<EmailDisabledAttributesTestCase />
</TestCase>
</FixtureSet>
);
}

export default EmailInputs;
4 changes: 2 additions & 2 deletions packages/react-dom/src/client/ReactDOMInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -410,8 +410,8 @@ export function setDefaultValue(
value: *,
) {
if (
// Focused number inputs synchronize on blur. See ChangeEventPlugin.js
type !== 'number' ||
// Focused number and email inputs synchronize on blur. See ChangeEventPlugin.js
(type !== 'number' && type !== 'email') ||
node.ownerDocument.activeElement !== node
) {
if (value == null) {
Expand Down
8 changes: 6 additions & 2 deletions packages/react-dom/src/events/ChangeEventPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,13 +236,17 @@ function getTargetInstForInputOrChangeEvent(topLevelType, targetInst) {
function handleControlledInputBlur(node) {
let state = node._wrapperState;

if (!state || !state.controlled || node.type !== 'number') {
if (
!state ||
!state.controlled ||
(node.type !== 'number' && node.type !== 'email')
) {
return;
}

if (!disableInputAttributeSyncing) {
// If controlled, assign the value attribute to the current value on blur
setDefaultValue(node, 'number', node.value);
setDefaultValue(node, node.type, node.value);
}
}

Expand Down

0 comments on commit 9b88b78

Please sign in to comment.