Created
October 9, 2013 00:47
-
-
Save zpao/6894353 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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('&'); | |
+ }); | |
+ | |
+}); | |
\ 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 = { | |
"&": "&", | |
">": ">", | |
@@ -30,6 +28,8 @@ var ESCAPE_LOOKUP = { | |
"/": "/" | |
}; | |
+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