Skip to content

Instantly share code, notes, and snippets.

@zpao
Created October 3, 2013 18:10
Show Gist options
  • Save zpao/6814295 to your computer and use it in GitHub Desktop.
Save zpao/6814295 to your computer and use it in GitHub Desktop.
commit 9e2d6e904015bb1c31af23190ebc05a41cbe21c6
Author: Paul O’Shannessy <paul@oshannessy.com>
Date: Thu Oct 3 11:05:42 2013 -0700
yup
diff --git a/src/dom/components/ReactDOMInput.js b/src/dom/components/ReactDOMInput.js
index a569a5e..cfd4982 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,9 +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 ?
- '' + value :
- this.state.value;
+ props.value = value != null ? value : this.state.value;
props.onChange = this._handleChange;
diff --git a/src/dom/components/ReactDOMTextarea.js b/src/dom/components/ReactDOMTextarea.js
index 0aed044..5cd84de 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,6 @@ 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) {
@@ -85,8 +77,10 @@ var ReactDOMTextarea = ReactCompositeComponent.createClass({
var value = this.getValue();
return {
// We save the initial value so that `ReactDOMComponent` doesn't update
- // `textContent` (unnecessary since we update value).
- initialValue: value != null ? value : defaultValue,
+ // `textContent` (unnecessary since we update value). We need to ensure
+ // that this value is a string since it will be used as a child of the
+ // <textarea>.
+ initialValue: '' + (value != null ? value : defaultValue),
value: defaultValue
};
},
@@ -121,7 +115,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..c6415da 100644
--- a/src/dom/components/__tests__/ReactDOMInput-test.js
+++ b/src/dom/components/__tests__/ReactDOMInput-test.js
@@ -50,11 +50,11 @@ describe('ReactDOMInput', 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 = <input type="text" defaultValue={false} />;
var node = renderTextInput(stub);
- expect(node.value).toBe('');
+ expect(node.value).toBe('false');
});
it('should display `value` of number 0', function() {
@@ -64,11 +64,11 @@ describe('ReactDOMInput', function() {
expect(node.value).toBe('0');
});
- it('should display "" for `value` of `false`', function() {
+ 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 properly control a value of number `0`', function() {
diff --git a/src/dom/components/__tests__/ReactDOMTextarea-test.js b/src/dom/components/__tests__/ReactDOMTextarea-test.js
index a0fd263..0fbc4ee 100644
--- a/src/dom/components/__tests__/ReactDOMTextarea-test.js
+++ b/src/dom/components/__tests__/ReactDOMTextarea-test.js
@@ -62,11 +62,19 @@ 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 display toString for `defaultValue` of object', function() {
+ var obj = { toString: function() { return 'hello'; } };
+ var stub = <textarea type="text" defaultValue={obj} />;
+ var node = renderTextarea(stub);
+
+ expect(node.value).toBe('hello');
});
it('should allow setting `value`', function() {
@@ -79,6 +87,27 @@ describe('ReactDOMTextarea', function() {
expect(node.value).toEqual('gorilla');
});
+ 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 object', function() {
+ var obj = { toString: function() { return 'hello'; } };
+ var stub = <textarea value="giraffe" />;
+ var node = renderTextarea(stub);
+
+ expect(node.value).toBe('giraffe');
+
+ stub.replaceProps({value: obj});
+ expect(node.value).toBe('hello');
+ });
+
it('should display `value` of number 0', function() {
var stub = <textarea value={0} />;
var node = renderTextarea(stub);
@@ -86,11 +115,11 @@ describe('ReactDOMTextarea', function() {
expect(node.value).toBe('0');
});
- it('should display "" for `value` of `false`', function() {
+ 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 properly control a value of number `0`', function() {
@@ -123,6 +152,21 @@ 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 'hello'; } };
+ var node = renderTextarea(<textarea>{obj}</textarea>);
+ expect(console.warn.argsForCall.length).toBe(1);
+ expect(node.value).toBe('hello');
+ });
+
it('should throw with multiple or invalid children', function() {
spyOn(console, 'warn');
@@ -134,13 +178,16 @@ describe('ReactDOMTextarea', function() {
expect(console.warn.argsForCall.length).toBe(1);
+ var node;
expect(function() {
- ReactTestUtils.renderIntoDocument(
+ node = renderTextarea(
<textarea><strong /></textarea>
);
- }).toThrow();
+ }).not.toThrow();
+ // Should still warn about using defaultValue/value
expect(console.warn.argsForCall.length).toBe(2);
+ expect(node.value).toBe('[object Object]');
});
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..e6ccdfd
--- /dev/null
+++ b/src/utils/__tests__/escapeTextForBrowser-test.js
@@ -0,0 +1,68 @@
+/**
+ * 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;
+
+ beforeEach(function() {
+ escapeTextForBrowser = require('escapeTextForBrowser');
+ });
+
+ it('should convert numbers to strings', function() {
+ expect(escapeTextForBrowser(4)).toBe('4');
+ expect(escapeTextForBrowser(1.01)).toBe('1.01');
+ });
+
+ it('should convert booleans to strings', function() {
+ expect(escapeTextForBrowser(true)).toBe('true');
+ expect(escapeTextForBrowser(false)).toBe('false');
+ });
+
+ it('should convert Arrays to strings', function() {
+ var arr = [1, 2, 'foo'];
+ expect(escapeTextForBrowser(arr)).toBe('1,2,foo');
+ });
+
+ it('should convert simple objects without toString to strings', function() {
+ var obj = {};
+ expect(escapeTextForBrowser(obj)).toBe('[object Object]');
+ });
+
+ it('should convert simple objects to strings', function() {
+ var obj = { toString: function() { return 'hello'; } };
+ expect(escapeTextForBrowser(obj)).toBe('hello');
+ });
+
+ it('should convert objects with inheritance to strings', function() {
+ var Obj = function() {};
+ Obj.prototype.toString = function() { return 'goodbye'; };
+ expect(escapeTextForBrowser(new Obj())).toBe('goodbye');
+ });
+
+ it('should convert escape special characters in strings', function() {
+ expect(escapeTextForBrowser('harold & maude')).toBe('harold &amp; maude');
+ });
+
+ it('should convert escape special characters in objects', function() {
+ var obj = { toString: function() { return '"real" talk'; } };
+ expect(escapeTextForBrowser(obj)).toBe('&quot;real&quot; talk');
+ });
+});
+
diff --git a/src/utils/escapeTextForBrowser.js b/src/utils/escapeTextForBrowser.js
index 8e49d31..4198b9f 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,23 +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.'
- );
if (text === '') {
return '';
} else {
- if (type === 'string') {
- return text.replace(/[&><"'\/]/g, escaper);
- } else {
- return (''+text).replace(/[&><"'\/]/g, escaper);
- }
+ // Make sure text is in fact a string. This will call toString on other
+ // types (e.g. booleans, objects).
+ return ('' + text).replace(ESCAPE_REGEX, escaper);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment