-
-
-
- The field should read "3.", preserving the decimal place
-
-
-
-
-
- Notes: Chrome and Safari clear trailing
- decimals on blur. React makes this concession so that the
- value attribute remains in sync with the value property.
-
-
-
-
-
-
Type "0.01"
-
-
-
- The field should read "0.01"
-
-
-
-
-
-
-
-
Type "2e"
-
Type 4, to read "2e4"
-
-
-
- The field should read "2e4". The parsed value should read "20000"
-
-
-
-
-
-
-
-
Type "3.14"
-
Press "e", so that the input reads "3.14e"
-
-
-
- The field should read "3.14e", the parsed value should be empty
-
-
-
-
-
-
-
-
Type "3.14"
-
Move the text cursor to after the decimal place
-
Press "e" twice, so that the value reads "3.ee14"
-
-
-
- The field should read "3.ee14"
-
-
-
-
-
-
-
-
Type "3.0"
-
-
-
- The field should read "3.0"
-
-
-
-
-
-
-
-
Type "300"
-
Move the cursor to after the "3"
-
Type "."
-
-
-
- The field should read "3.00", not "3"
-
-
-
-
-
-
-
Type "3"
-
Select the entire value"
-
Type '-' to replace '3' with '-'
-
-
-
- The field should read "-", not be blank.
-
-
-
-
-
-
-
Type "-"
-
Type '3'
-
-
-
- The field should read "-3".
-
-
-
-
- );
- },
-});
-
-export default NumberInputs;
diff --git a/src/renderers/dom/client/wrappers/ReactDOMInput.js b/src/renderers/dom/client/wrappers/ReactDOMInput.js
index 6cdcde91201da..7263bca413f34 100644
--- a/src/renderers/dom/client/wrappers/ReactDOMInput.js
+++ b/src/renderers/dom/client/wrappers/ReactDOMInput.js
@@ -226,8 +226,7 @@ var ReactDOMInput = {
// browsers typically do this as necessary, jsdom doesn't.
node.value = '' + value;
}
- // eslint-disable-next-line
- } else if (value != node.value) {
+ } else if (node.value !== value) {
// Cast `value` to a string to ensure the value is set correctly. While
// browsers typically do this as necessary, jsdom doesn't.
node.value = '' + value;
diff --git a/src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js b/src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js
index 2240b37502ad0..554bf0a309204 100644
--- a/src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js
+++ b/src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js
@@ -32,6 +32,196 @@ describe('ReactDOMInput', () => {
spyOn(console, 'error');
});
+ it('should properly control a value even if no event listener exists', () => {
+ var container = document.createElement('div');
+ var stub = ReactDOM.render(, container);
+
+ document.body.appendChild(container);
+
+ var node = ReactDOM.findDOMNode(stub);
+ expectDev(console.error.calls.count()).toBe(1);
+
+ // Simulate a native change event
+ setUntrackedValue(node, 'giraffe');
+
+ // This must use the native event dispatching. If we simulate, we will
+ // bypass the lazy event attachment system so we won't actually test this.
+ var nativeEvent = document.createEvent('Event');
+ nativeEvent.initEvent('change', true, true);
+ node.dispatchEvent(nativeEvent);
+
+ expect(node.value).toBe('lion');
+
+ document.body.removeChild(container);
+ });
+
+ it('should control a value in reentrant events', () => {
+ // This must use the native event dispatching. If we simulate, we will
+ // bypass the lazy event attachment system so we won't actually test this.
+ var inputEvent = document.createEvent('Event');
+ inputEvent.initEvent('input', true, true);
+ // This must use the native event dispatching. If we simulate, we will
+ // bypass the lazy event attachment system so we won't actually test this.
+ var changeEvent = document.createEvent('Event');
+ changeEvent.initEvent('change', true, true);
+
+ class ControlledInputs extends React.Component {
+ state = {value: 'lion'};
+ a = null;
+ b = null;
+ switchedFocus = false;
+ change(newValue) {
+ this.setState({value: newValue});
+ // Calling focus here will blur the text box which causes a native
+ // change event. Ideally we shouldn't have to fire this ourselves.
+ // I don't know how to simulate a change event on a text box.
+ this.a.dispatchEvent(changeEvent);
+ this.b.focus();
+ }
+ blur(currentValue) {
+ this.switchedFocus = true;
+ // currentValue should be 'giraffe' here because we should not have
+ // restored it on the target yet.
+ this.setState({value: currentValue});
+ }
+ render() {
+ return (
+
+ );
+ }
+ }
+
+ var container = document.createElement('div');
+ var instance = ReactDOM.render(, container);
+
+ // We need it to be in the body to test native event dispatching.
+ document.body.appendChild(container);
+
+ instance.a.focus();
+ // Simulate a native keyup event
+ setUntrackedValue(instance.a, 'giraffe');
+
+ instance.a.dispatchEvent(inputEvent);
+
+ expect(instance.a.value).toBe('giraffe');
+ expect(instance.switchedFocus).toBe(true);
+
+ document.body.removeChild(container);
+ });
+
+ it('should control values in reentrant events with different targets', () => {
+ // This must use the native event dispatching. If we simulate, we will
+ // bypass the lazy event attachment system so we won't actually test this.
+ var inputEvent = document.createEvent('Event');
+ inputEvent.initEvent('input', true, true);
+
+ class ControlledInputs extends React.Component {
+ state = {value: 'lion'};
+ a = null;
+ b = null;
+ change(newValue) {
+ // This click will change the checkbox's value to false. Then it will
+ // invoke an inner change event. When we finally, flush, we need to
+ // reset the checkbox's value to true since that is its controlled
+ // value.
+ this.b.click();
+ }
+ render() {
+ return (
+