Skip to content

Instantly share code, notes, and snippets.

@zpao
Created October 9, 2013 00:47
Show Gist options
  • Save zpao/6894353 to your computer and use it in GitHub Desktop.
Save zpao/6894353 to your computer and use it in GitHub Desktop.
diff --git a/src/dom/components/ReactDOMInput.js b/src/dom/components/ReactDOMInput.js
index a569a5e..698fc59 100644
--- a/src/dom/components/ReactDOMInput.js
+++ b/src/dom/components/ReactDOMInput.js
@@ -55,7 +55,7 @@ var ReactDOMInput = ReactCompositeComponent.createClass({
var defaultValue = this.props.defaultValue;
return {
checked: this.props.defaultChecked || false,
- value: defaultValue != null && defaultValue !== false ? defaultValue : ''
+ value: defaultValue != null ? defaultValue : ''
};
},
@@ -74,7 +74,7 @@ var ReactDOMInput = ReactCompositeComponent.createClass({
this.props.checked != null ? this.props.checked : this.state.checked;
var value = this.getValue();
- props.value = value != null && value !== false ?
+ props.value = value != null ?
'' + value :
this.state.value;
@@ -110,7 +110,7 @@ var ReactDOMInput = ReactCompositeComponent.createClass({
DOMPropertyOperations.setValueForProperty(
rootNode,
'value',
- value !== false ? '' + value : ''
+ value
);
}
},
diff --git a/src/dom/components/ReactDOMTextarea.js b/src/dom/components/ReactDOMTextarea.js
index 0aed044..bc05286 100644
--- a/src/dom/components/ReactDOMTextarea.js
+++ b/src/dom/components/ReactDOMTextarea.js
@@ -29,9 +29,6 @@ var merge = require('merge');
// Store a reference to the <textarea> `ReactDOMComponent`.
var textarea = ReactDOM.textarea;
-// For quickly matching children type, to test if can be treated as content.
-var CONTENT_TYPES = {'string': true, 'number': true};
-
/**
* Implements a <textarea> native component that allows setting `value`, and
* `defaultValue`. This differs from the traditional DOM API because value is
@@ -72,11 +69,7 @@ var ReactDOMTextarea = ReactCompositeComponent.createClass({
);
children = children[0];
}
- invariant(
- CONTENT_TYPES[typeof children],
- 'If you specify children to <textarea>, it must be a single string ' +
- 'or number., not an array or object.'
- );
+
defaultValue = '' + children;
}
if (defaultValue == null) {
@@ -86,7 +79,7 @@ var ReactDOMTextarea = ReactCompositeComponent.createClass({
return {
// We save the initial value so that `ReactDOMComponent` doesn't update
// `textContent` (unnecessary since we update value).
- initialValue: value != null ? value : defaultValue,
+ initialValue: '' + (value != null ? value : defaultValue),
value: defaultValue
};
},
@@ -121,7 +114,7 @@ var ReactDOMTextarea = ReactCompositeComponent.createClass({
DOMPropertyOperations.setValueForProperty(
rootNode,
'value',
- value !== false ? '' + value : ''
+ value
);
}
},
diff --git a/src/dom/components/__tests__/ReactDOMInput-test.js b/src/dom/components/__tests__/ReactDOMInput-test.js
index 61cf2f3..b575e08 100644
--- a/src/dom/components/__tests__/ReactDOMInput-test.js
+++ b/src/dom/components/__tests__/ReactDOMInput-test.js
@@ -50,11 +50,41 @@ describe('ReactDOMInput', function() {
expect(node.value).toBe('0');
});
- it('should display "" for `defaultValue` of `false`', function() {
+ it('should allow setting `defaultValue` to `false`', function () {
+ var stub = <input type="text" defaultValue="giraffe" />;
+ var node = renderTextInput(stub);
+
+ expect(node.value).toBe('giraffe');
+
+ stub.replaceProps({value: false});
+ expect(node.value).toEqual('false');
+ });
+
+ it('should display "true" for `defaultValue` of `true`', function() {
+ var stub = <input type="text" defaultValue={true} />;
+ var node = renderTextInput(stub);
+
+ expect(node.value).toBe('true');
+ });
+
+ it('should display "false" for `defaultValue` of `false`', function() {
var stub = <input type="text" defaultValue={false} />;
var node = renderTextInput(stub);
- expect(node.value).toBe('');
+ expect(node.value).toBe('false');
+ });
+
+ it('should display "foobar" for `defaultValue` of `objectToString`', function() {
+ var objectToString = {
+ toString: function() {
+ return "foobar";
+ }
+ };
+
+ var stub = <input type="text" defaultValue={objectToString} />;
+ var node = renderTextInput(stub);
+
+ expect(node.value).toBe('foobar');
});
it('should display `value` of number 0', function() {
@@ -64,11 +94,41 @@ describe('ReactDOMInput', function() {
expect(node.value).toBe('0');
});
- it('should display "" for `value` of `false`', function() {
+ it('should display "true" for `value` of `true`', function() {
+ var stub = <input type="text" value={true} />;
+ var node = renderTextInput(stub);
+
+ expect(node.value).toBe('true');
+ });
+
+ it('should display "false" for `value` of `false`', function() {
var stub = <input type="text" value={false} />;
var node = renderTextInput(stub);
- expect(node.value).toBe('');
+ expect(node.value).toBe('false');
+ });
+
+ it("should allow changing `value` to `false`", function() {
+ var stub = <input type="text" value="yolo" />;
+ var node = renderTextInput(stub);
+
+ expect(node.value).toBe('yolo');
+
+ stub.replaceProps({value: false});
+ expect(node.value).toEqual('false');
+ });
+
+ it('should display "foobar" for `value` of `objectToString`', function() {
+ var objectToString = {
+ toString: function() {
+ return "foobar";
+ }
+ };
+
+ var stub = <input type="text" value={objectToString} />;
+ var node = renderTextInput(stub);
+
+ expect(node.value).toBe('foobar');
});
it('should properly control a value of number `0`', function() {
diff --git a/src/dom/components/__tests__/ReactDOMSelect-test.js b/src/dom/components/__tests__/ReactDOMSelect-test.js
index ec2b9ae..8239e0e 100644
--- a/src/dom/components/__tests__/ReactDOMSelect-test.js
+++ b/src/dom/components/__tests__/ReactDOMSelect-test.js
@@ -116,6 +116,35 @@ describe('ReactDOMSelect', function() {
expect(node.options[2].selected).toBe(false); // gorilla
});
+ it('should allow setting `value` with `objectToString`', function() {
+ var objectToString = {
+ animal: "giraffe",
+ toString: function() {
+ return this.animal;
+ }
+ };
+
+ var stub =
+ <select multiple={true} value={[objectToString]}>
+ <option value="monkey">A monkey!</option>
+ <option value="giraffe">A giraffe!</option>
+ <option value="gorilla">A gorilla!</option>
+ </select>;
+ var node = renderSelect(stub);
+
+ expect(node.options[0].selected).toBe(false); // monkey
+ expect(node.options[1].selected).toBe(true); // giraffe
+ expect(node.options[2].selected).toBe(false); // gorilla
+
+ // Changing the `value` prop should change the selected options.
+ objectToString.animal = "monkey";
+ stub.setProps({value: [objectToString]});
+
+ expect(node.options[0].selected).toBe(true); // monkey
+ expect(node.options[1].selected).toBe(false); // giraffe
+ expect(node.options[2].selected).toBe(false); // gorilla
+ });
+
it('should allow switching to multiple', function() {
var stub =
<select defaultValue="giraffe">
diff --git a/src/dom/components/__tests__/ReactDOMTextarea-test.js b/src/dom/components/__tests__/ReactDOMTextarea-test.js
index a0fd263..9fea689 100644
--- a/src/dom/components/__tests__/ReactDOMTextarea-test.js
+++ b/src/dom/components/__tests__/ReactDOMTextarea-test.js
@@ -62,11 +62,34 @@ describe('ReactDOMTextarea', function() {
expect(node.value).toBe('0');
});
- it('should display "" for `defaultValue` of `false`', function() {
+ it('should display "false" for `defaultValue` of `false`', function() {
var stub = <textarea type="text" defaultValue={false} />;
var node = renderTextarea(stub);
- expect(node.value).toBe('');
+ expect(node.value).toBe('false');
+ });
+
+ it('should allow setting `defaultValue` to `false`', function() {
+ var stub = <textarea defaultValue="giraffe" />;
+ var node = renderTextarea(stub);
+
+ expect(node.value).toBe('giraffe');
+
+ stub.replaceProps({value: false});
+ expect(node.value).toEqual('false');
+ });
+
+ it('should display "foobar" for `defaultValue` of `objectToString`', function() {
+ var objectToString = {
+ toString: function() {
+ return "foobar";
+ }
+ };
+
+ var stub = <textarea type="text" defaultValue={objectToString} />;
+ var node = renderTextarea(stub);
+
+ expect(node.value).toBe('foobar');
});
it('should allow setting `value`', function() {
@@ -86,11 +109,56 @@ describe('ReactDOMTextarea', function() {
expect(node.value).toBe('0');
});
- it('should display "" for `value` of `false`', function() {
+ it('should display "true" for `value` of `true`', function() {
+ var stub = <textarea type="text" value={true} />;
+ var node = renderTextarea(stub);
+
+ expect(node.value).toBe('true');
+ });
+
+ it('should display "false" for `value` of `false`', function() {
var stub = <textarea type="text" value={false} />;
var node = renderTextarea(stub);
- expect(node.value).toBe('');
+ expect(node.value).toBe('false');
+ });
+
+ it('should display "foobar" for `value` of `objectToString`', function() {
+ var objectToString = {
+ toString: function() {
+ return "foobar";
+ }
+ };
+
+ var stub = <textarea type="text" value={objectToString} />;
+ var node = renderTextarea(stub);
+
+ expect(node.value).toBe('foobar');
+ });
+
+ it('should allow setting `value` to `false`', function () {
+ var stub = <textarea value="giraffe" />;
+ var node = renderTextarea(stub);
+
+ expect(node.value).toBe('giraffe');
+
+ stub.replaceProps({value: false});
+ expect(node.value).toEqual('false');
+ });
+
+ it('should allow setting `value` to `objectToString`', function () {
+ var stub = <textarea value="giraffe" />;
+ var node = renderTextarea(stub);
+
+ expect(node.value).toBe('giraffe');
+
+ var obj = {
+ toString: function() {
+ return "foo";
+ }
+ };
+ stub.replaceProps({value: obj});
+ expect(node.value).toEqual('foo');
});
it('should properly control a value of number `0`', function() {
@@ -123,6 +191,25 @@ describe('ReactDOMTextarea', function() {
expect(node.value).toBe('17');
});
+ it('should allow booleans as children', function () {
+ spyOn(console, 'warn');
+ var node = renderTextarea(<textarea>{false}</textarea>);
+ expect(console.warn.argsForCall.length).toBe(1);
+ expect(node.value).toBe('false');
+ });
+
+ it('should allow objects as children', function () {
+ spyOn(console, 'warn');
+ var obj = {
+ toString: function() {
+ return "sharkswithlasers";
+ }
+ };
+ var node = renderTextarea(<textarea>{obj}</textarea>);
+ expect(console.warn.argsForCall.length).toBe(1);
+ expect(node.value).toBe('sharkswithlasers');
+ });
+
it('should throw with multiple or invalid children', function() {
spyOn(console, 'warn');
@@ -134,13 +221,6 @@ describe('ReactDOMTextarea', function() {
expect(console.warn.argsForCall.length).toBe(1);
- expect(function() {
- ReactTestUtils.renderIntoDocument(
- <textarea><strong /></textarea>
- );
- }).toThrow();
-
- expect(console.warn.argsForCall.length).toBe(2);
});
it('should support ReactLink', function() {
diff --git a/src/utils/__tests__/escapeTextForBrowser-test.js b/src/utils/__tests__/escapeTextForBrowser-test.js
new file mode 100644
index 0000000..a14c700
--- /dev/null
+++ b/src/utils/__tests__/escapeTextForBrowser-test.js
@@ -0,0 +1,60 @@
+/**
+ * Copyright 2013 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @emails react-core
+ */
+
+"use strict";
+
+describe('escapeTextForBrowser', function() {
+
+ var escapeTextForBrowser = require('escapeTextForBrowser');
+
+ it('should escape boolean to string', function() {
+ var escaped = escapeTextForBrowser(true);
+ expect(escaped).toBe('true');
+
+ escaped = escapeTextForBrowser(false);
+ expect(escaped).toBe('false');
+ });
+
+ it('should escape object to string', function() {
+ var escaped = escapeTextForBrowser({
+ toString: function() {
+ return 'ponys';
+ }
+ });
+
+ expect(escaped).toBe('ponys');
+ });
+
+ it('should escape number to string', function() {
+ var escaped = escapeTextForBrowser(42);
+ expect(escaped).toBe('42');
+ });
+
+ it('should escape string', function() {
+ var escaped = escapeTextForBrowser('<script type=\'\' src=""></script>');
+ expect(escaped).not.toContain('<');
+ expect(escaped).not.toContain('>');
+ expect(escaped).not.toContain('\'');
+ expect(escaped).not.toContain('/');
+ expect(escaped).not.toContain('\"');
+
+ escaped = escapeTextForBrowser('&');
+ expect(escaped).toBe('&amp;');
+ });
+
+});
\ No newline at end of file
diff --git a/src/utils/escapeTextForBrowser.js b/src/utils/escapeTextForBrowser.js
index 8e49d31..c8a6106 100644
--- a/src/utils/escapeTextForBrowser.js
+++ b/src/utils/escapeTextForBrowser.js
@@ -19,8 +19,6 @@
"use strict";
-var invariant = require('invariant');
-
var ESCAPE_LOOKUP = {
"&": "&amp;",
">": "&gt;",
@@ -30,6 +28,8 @@ var ESCAPE_LOOKUP = {
"/": "&#x2f;"
};
+var ESCAPE_REGEX = /[&><"'\/]/g;
+
function escaper(match) {
return ESCAPE_LOOKUP[match];
}
@@ -37,24 +37,16 @@ function escaper(match) {
/**
* Escapes text to prevent scripting attacks.
*
- * @param {number|string} text Text value to escape.
+ * @param {*} text Text value to escape.
* @return {string} An escaped string.
*/
function escapeTextForBrowser(text) {
- var type = typeof text;
- invariant(
- type !== 'object',
- 'escapeTextForBrowser(...): Attempted to escape an object.'
- );
+ // short circuit for optimization
if (text === '') {
return '';
- } else {
- if (type === 'string') {
- return text.replace(/[&><"'\/]/g, escaper);
- } else {
- return (''+text).replace(/[&><"'\/]/g, escaper);
- }
}
+
+ return ('' + text).replace(ESCAPE_REGEX, escaper);
}
-module.exports = escapeTextForBrowser;
+module.exports = escapeTextForBrowser;
\ No newline at end of file
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment