Merge branch '15-dev' into 15-stable

I would prefer to rebase but was not able to.

1) There were a couple of conflicts;
	addons/create-react-class/package.json
	src/isomorphic/classic/class/__tests__/create-react-class-integration-test.js
2) Force pushing the result of the rebase was a no-go because
   `15-stable` is a protected branch.

So merging is ok too.

For more details about all the commits in this merge, see the `15-dev`
branch commit history from cef396d092 to d095bc8391
This commit is contained in:
Flarnie Marchan
2017-06-13 07:30:42 -07:00
319 changed files with 11691 additions and 9488 deletions
+1 -3
View File
@@ -1,4 +1,5 @@
# We can probably lint these later but not important at this point
addons/
src/renderers/art
src/shared/vendor
# But not in docs/_js/examples/*
@@ -9,11 +10,8 @@ docs/_site/
docs/vendor/bundle/
# This should be more like examples/**/thirdparty/** but
# we should fix https://github.com/facebook/esprima/pull/85 first
<<<<<<< HEAD
examples/
=======
fixtures/
>>>>>>> 4a37718... Remove examples/ folder (#9323)
# Ignore built files.
build/
coverage/
+3 -3
View File
@@ -1,14 +1,14 @@
[ignore]
<<<<<<< HEAD
<PROJECT_ROOT>/examples/.*
=======
<PROJECT_ROOT>/fixtures/.*
>>>>>>> 4a37718... Remove examples/ folder (#9323)
<PROJECT_ROOT>/build/.*
<PROJECT_ROOT>/node_modules/chrome-devtools-frontend/.*
<PROJECT_ROOT>/.*/node_modules/chrome-devtools-frontend/.*
<PROJECT_ROOT>/.*/node_modules/y18n/.*
<PROJECT_ROOT>/.*/__mocks__/.*
<PROJECT_ROOT>/.*/__tests__/.*
<PROJECT_ROOT>/addons/.*
# Ignore Docs
<PROJECT_ROOT>/docs/.*
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "create-react-class",
"version": "15.5.3",
"version": "15.5.4",
"description": "Deprecated, legacy API for creating React components.",
"main": "index.js",
"license": "BSD-3-Clause",
@@ -3,12 +3,12 @@
typeof exports === "object" &&
typeof module !== "undefined"
) {
module.exports=f()
module.exports=f(require('react'))
} else if (
typeof define === "function" &&
define.amd
) {
define([],f)
define(['react'],f)
} else {
var g;
if (typeof window !== "undefined") {
@@ -27,9 +27,9 @@
g.React.addons = {};
}
g.React.addons.createFragment = f()
g.React.addons.createFragment = f(g.React)
}
})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
})(function(React){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/**
* Copyright 2015-present, Facebook, Inc.
* All rights reserved.
@@ -390,7 +390,7 @@ module.exports = createReactFragment;
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
*
*/
function makeEmptyFunction(arg) {
@@ -542,4 +542,4 @@ if ("development" !== 'production') {
module.exports = warning;
},{"./emptyFunction":2}]},{},[1])(1)
});
});
+350 -209
View File
@@ -1,5 +1,65 @@
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.LinkedInput = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/**
(function(f) {
if (typeof exports === 'object' && typeof module !== 'undefined') {
module.exports = f(require('react'));
} else if (typeof define === 'function' && define.amd) {
define(['react'], f);
} else {
var g;
if (typeof window !== 'undefined') {
g = window;
} else if (typeof global !== 'undefined') {
g = global;
} else if (typeof self !== 'undefined') {
g = self;
} else {
g = this;
}
if (typeof g.React === 'undefined') {
throw Error('React module should be required before createFragment');
} else if (typeof g.React.addons === 'undefined') {
g.React.addons = {};
}
g.LinkedInput = f(g.React);
}
})(function(React) {
var define, module, exports;
return (function e(t, n, r) {
function s(o, u) {
if (!n[o]) {
if (!t[o]) {
var a = typeof require == 'function' && require;
if (!u && a) return a(o, !0);
if (i) return i(o, !0);
var f = new Error("Cannot find module '" + o + "'");
throw ((f.code = 'MODULE_NOT_FOUND'), f);
}
var l = (n[o] = { exports: {} });
t[o][0].call(
l.exports,
function(e) {
var n = t[o][1][e];
return s(n ? n : e);
},
l,
l.exports,
e,
t,
n,
r
);
}
return n[o].exports;
}
var i = typeof require == 'function' && require;
for (var o = 0; o < r.length; o++) s(r[o]);
return s;
})(
{
1: [
function(require, module, exports) {
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
@@ -8,147 +68,193 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
'use strict';
var emptyFunction = require('fbjs/lib/emptyFunction');
var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');
var emptyFunction = require('fbjs/lib/emptyFunction');
var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');
var hasReadOnlyValue = {
button: true,
checkbox: true,
image: true,
hidden: true,
radio: true,
reset: true,
submit: true
};
var hasReadOnlyValue = {
'button': true,
'checkbox': true,
'image': true,
'hidden': true,
'radio': true,
'reset': true,
'submit': true,
};
function _assertSingleLink(inputProps) {
invariant(
inputProps.checkedLink == null || inputProps.valueLink == null,
'Cannot provide a checkedLink and a valueLink. If you want to use ' +
"checkedLink, you probably don't want to use valueLink and vice versa."
);
}
function _assertValueLink(inputProps) {
_assertSingleLink(inputProps);
invariant(
inputProps.value == null && inputProps.onChange == null,
'Cannot provide a valueLink and a value or onChange event. If you want ' +
"to use value or onChange, you probably don't want to use valueLink."
);
}
function _assertSingleLink(inputProps) {
invariant(
inputProps.checkedLink == null || inputProps.valueLink == null,
'Cannot provide a checkedLink and a valueLink. If you want to use ' +
'checkedLink, you probably don\'t want to use valueLink and vice versa.'
);
}
function _assertValueLink(inputProps) {
_assertSingleLink(inputProps);
invariant(
inputProps.value == null && inputProps.onChange == null,
'Cannot provide a valueLink and a value or onChange event. If you want ' +
'to use value or onChange, you probably don\'t want to use valueLink.'
);
}
function _assertCheckedLink(inputProps) {
_assertSingleLink(inputProps);
invariant(
inputProps.checked == null && inputProps.onChange == null,
'Cannot provide a checkedLink and a checked property or onChange event. ' +
"If you want to use checked or onChange, you probably don't want to " +
'use checkedLink'
);
}
function _assertCheckedLink(inputProps) {
_assertSingleLink(inputProps);
invariant(
inputProps.checked == null && inputProps.onChange == null,
'Cannot provide a checkedLink and a checked property or onChange event. ' +
'If you want to use checked or onChange, you probably don\'t want to ' +
'use checkedLink'
);
}
var loggedTypeFailures = {};
function getDeclarationErrorAddendum(owner) {
if (owner) {
var name = owner.getName();
if (name) {
return ' Check the render method of `' + name + '`.';
}
}
return '';
}
var loggedTypeFailures = {};
function getDeclarationErrorAddendum(owner) {
if (owner) {
var name = owner.getName();
if (name) {
return ' Check the render method of `' + name + '`.';
}
}
return '';
}
/**
/**
* Provide a linked `value` attribute for controlled forms. You should not use
* this outside of the ReactDOM controlled form components.
*/
var LinkedValueUtils = {
/**
var LinkedValueUtils = {
/**
* @param {object} inputProps Props for form component
* @return {*} current value of the input either from value prop or link.
*/
getValue: function(inputProps) {
if (inputProps.valueLink) {
_assertValueLink(inputProps);
return inputProps.valueLink.value;
}
return inputProps.value;
},
getValue: function(inputProps) {
if (inputProps.valueLink) {
_assertValueLink(inputProps);
return inputProps.valueLink.value;
}
return inputProps.value;
},
/**
/**
* @param {object} inputProps Props for form component
* @return {*} current checked status of the input either from checked prop
* or link.
*/
getChecked: function(inputProps) {
if (inputProps.checkedLink) {
_assertCheckedLink(inputProps);
return inputProps.checkedLink.value;
}
return inputProps.checked;
},
getChecked: function(inputProps) {
if (inputProps.checkedLink) {
_assertCheckedLink(inputProps);
return inputProps.checkedLink.value;
}
return inputProps.checked;
},
/**
/**
* @param {object} inputProps Props for form component
* @param {SyntheticEvent} event change event to handle
*/
executeOnChange: function(inputProps, event) {
if (inputProps.valueLink) {
_assertValueLink(inputProps);
return inputProps.valueLink.requestChange(event.target.value);
} else if (inputProps.checkedLink) {
_assertCheckedLink(inputProps);
return inputProps.checkedLink.requestChange(event.target.checked);
} else if (inputProps.onChange) {
return inputProps.onChange.call(undefined, event);
}
},
};
executeOnChange: function(inputProps, event) {
if (inputProps.valueLink) {
_assertValueLink(inputProps);
return inputProps.valueLink.requestChange(event.target.value);
} else if (inputProps.checkedLink) {
_assertCheckedLink(inputProps);
return inputProps.checkedLink.requestChange(
event.target.checked
);
} else if (inputProps.onChange) {
return inputProps.onChange.call(undefined, event);
}
}
};
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError('Cannot call a class as a function');
}
}
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _possibleConstructorReturn(self, call) {
if (!self) {
throw new ReferenceError(
"this hasn't been initialised - super() hasn't been called"
);
}
return call &&
(typeof call === 'object' || typeof call === 'function')
? call
: self;
}
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
function _inherits(subClass, superClass) {
if (typeof superClass !== 'function' && superClass !== null) {
throw new TypeError(
'Super expression must either be null or a function, not ' +
typeof superClass
);
}
subClass.prototype = Object.create(
superClass && superClass.prototype,
{
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
}
);
if (superClass)
Object.setPrototypeOf
? Object.setPrototypeOf(subClass, superClass)
: (subClass.__proto__ = superClass);
}
var LinkedInput = function (_React$Component) {
_inherits(LinkedInput, _React$Component);
var LinkedInput = (function(_React$Component) {
_inherits(LinkedInput, _React$Component);
function LinkedInput() {
_classCallCheck(this, LinkedInput);
function LinkedInput() {
_classCallCheck(this, LinkedInput);
var _this = _possibleConstructorReturn(this, _React$Component.call(this));
var _this = _possibleConstructorReturn(
this,
_React$Component.call(this)
);
_this.handleChange = _this.handleChange.bind(_this);
return _this;
}
_this.handleChange = _this.handleChange.bind(_this);
return _this;
}
LinkedInput.prototype.handleChange = function handleChange(e) {
LinkedValueUtils.executeOnChange(this.props, e);
};
LinkedInput.prototype.handleChange = function handleChange(e) {
LinkedValueUtils.executeOnChange(this.props, e);
};
LinkedInput.prototype.render = function render() {
var newProps = Object.assign({}, this.props);
newProps.value = LinkedValueUtils.getValue(this.props);
newProps.checked = LinkedValueUtils.getChecked(this.props);
newProps.onChange = this.handleChange;
delete newProps.valueLink;
delete newProps.checkedLink;
return React.createElement('input', newProps);
};
LinkedInput.prototype.render = function render() {
var newProps = Object.assign({}, this.props);
newProps.value = LinkedValueUtils.getValue(this.props);
newProps.checked = LinkedValueUtils.getChecked(this.props);
newProps.onChange = this.handleChange;
delete newProps.valueLink;
delete newProps.checkedLink;
return React.createElement('input', newProps);
};
return LinkedInput;
}(React.Component);
return LinkedInput;
})(React.Component);
module.exports = LinkedInput;
},{"fbjs/lib/emptyFunction":2,"fbjs/lib/invariant":3,"fbjs/lib/warning":4}],2:[function(require,module,exports){
"use strict";
/**
module.exports = LinkedInput;
},
{
'fbjs/lib/emptyFunction': 2,
'fbjs/lib/invariant': 3,
'fbjs/lib/warning': 4
}
],
2: [
function(require, module, exports) {
'use strict';
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
@@ -156,36 +262,40 @@ module.exports = LinkedInput;
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
*
*/
function makeEmptyFunction(arg) {
return function () {
return arg;
};
}
function makeEmptyFunction(arg) {
return function() {
return arg;
};
}
/**
/**
* This function accepts and discards inputs; it has no side effects. This is
* primarily useful idiomatically for overridable function endpoints which
* always need to be callable, since JS lacks a null-call idiom ala Cocoa.
*/
var emptyFunction = function emptyFunction() {};
var emptyFunction = function emptyFunction() {};
emptyFunction.thatReturns = makeEmptyFunction;
emptyFunction.thatReturnsFalse = makeEmptyFunction(false);
emptyFunction.thatReturnsTrue = makeEmptyFunction(true);
emptyFunction.thatReturnsNull = makeEmptyFunction(null);
emptyFunction.thatReturnsThis = function () {
return this;
};
emptyFunction.thatReturnsArgument = function (arg) {
return arg;
};
emptyFunction.thatReturns = makeEmptyFunction;
emptyFunction.thatReturnsFalse = makeEmptyFunction(false);
emptyFunction.thatReturnsTrue = makeEmptyFunction(true);
emptyFunction.thatReturnsNull = makeEmptyFunction(null);
emptyFunction.thatReturnsThis = function() {
return this;
};
emptyFunction.thatReturnsArgument = function(arg) {
return arg;
};
module.exports = emptyFunction;
},{}],3:[function(require,module,exports){
/**
module.exports = emptyFunction;
},
{}
],
3: [
function(require, module, exports) {
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
@@ -195,9 +305,8 @@ module.exports = emptyFunction;
*
*/
'use strict';
/**
'use strict';
/**
* Use invariant() to assert state which your program assumes to be true.
*
* Provide sprintf-style format (only %s is supported) and arguments
@@ -208,40 +317,49 @@ module.exports = emptyFunction;
* will remain to ensure logic does not differ in production.
*/
var validateFormat = function validateFormat(format) {};
var validateFormat = function validateFormat(format) {};
if ("development" !== 'production') {
validateFormat = function validateFormat(format) {
if (format === undefined) {
throw new Error('invariant requires an error message argument');
}
};
}
if ('development' !== 'production') {
validateFormat = function validateFormat(format) {
if (format === undefined) {
throw new Error('invariant requires an error message argument');
}
};
}
function invariant(condition, format, a, b, c, d, e, f) {
validateFormat(format);
function invariant(condition, format, a, b, c, d, e, f) {
validateFormat(format);
if (!condition) {
var error;
if (format === undefined) {
error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
} else {
var args = [a, b, c, d, e, f];
var argIndex = 0;
error = new Error(format.replace(/%s/g, function () {
return args[argIndex++];
}));
error.name = 'Invariant Violation';
}
if (!condition) {
var error;
if (format === undefined) {
error = new Error(
'Minified exception occurred; use the non-minified dev environment ' +
'for the full error message and additional helpful warnings.'
);
} else {
var args = [a, b, c, d, e, f];
var argIndex = 0;
error = new Error(
format.replace(/%s/g, function() {
return args[argIndex++];
})
);
error.name = 'Invariant Violation';
}
error.framesToPop = 1; // we don't care about invariant's own frame
throw error;
}
}
error.framesToPop = 1; // we don't care about invariant's own frame
throw error;
}
}
module.exports = invariant;
},{}],4:[function(require,module,exports){
/**
module.exports = invariant;
},
{}
],
4: [
function(require, module, exports) {
/**
* Copyright 2014-2015, Facebook, Inc.
* All rights reserved.
*
@@ -251,61 +369,84 @@ module.exports = invariant;
*
*/
'use strict';
'use strict';
var emptyFunction = require('./emptyFunction');
var emptyFunction = require('./emptyFunction');
/**
/**
* Similar to invariant but only logs a warning if the condition is not met.
* This can be used to log issues in development environments in critical
* paths. Removing the logging code for production environments will keep the
* same logic and follow the same code paths.
*/
var warning = emptyFunction;
var warning = emptyFunction;
if ("development" !== 'production') {
(function () {
var printWarning = function printWarning(format) {
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
if ('development' !== 'production') {
(function() {
var printWarning = function printWarning(format) {
for (
var _len = arguments.length,
args = Array(_len > 1 ? _len - 1 : 0),
_key = 1;
_key < _len;
_key++
) {
args[_key - 1] = arguments[_key];
}
var argIndex = 0;
var message = 'Warning: ' + format.replace(/%s/g, function () {
return args[argIndex++];
});
if (typeof console !== 'undefined') {
console.error(message);
}
try {
// --- Welcome to debugging React ---
// This error was thrown as a convenience so that you can use this stack
// to find the callsite that caused this warning to fire.
throw new Error(message);
} catch (x) {}
};
var argIndex = 0;
var message =
'Warning: ' +
format.replace(/%s/g, function() {
return args[argIndex++];
});
if (typeof console !== 'undefined') {
console.error(message);
}
try {
// --- Welcome to debugging React ---
// This error was thrown as a convenience so that you can use this stack
// to find the callsite that caused this warning to fire.
throw new Error(message);
} catch (x) {}
};
warning = function warning(condition, format) {
if (format === undefined) {
throw new Error('`warning(condition, format, ...args)` requires a warning ' + 'message argument');
}
warning = function warning(condition, format) {
if (format === undefined) {
throw new Error(
'`warning(condition, format, ...args)` requires a warning ' +
'message argument'
);
}
if (format.indexOf('Failed Composite propType: ') === 0) {
return; // Ignore CompositeComponent proptype check.
}
if (format.indexOf('Failed Composite propType: ') === 0) {
return; // Ignore CompositeComponent proptype check.
}
if (!condition) {
for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
args[_key2 - 2] = arguments[_key2];
}
if (!condition) {
for (
var _len2 = arguments.length,
args = Array(_len2 > 2 ? _len2 - 2 : 0),
_key2 = 2;
_key2 < _len2;
_key2++
) {
args[_key2 - 2] = arguments[_key2];
}
printWarning.apply(undefined, [format].concat(args));
}
};
})();
}
printWarning.apply(undefined, [format].concat(args));
}
};
})();
}
module.exports = warning;
},
{ './emptyFunction': 2 }
]
},
{},
[1]
)(1);
});
module.exports = warning;
},{"./emptyFunction":2}]},{},[1])(1)
});
+1
View File
@@ -23,6 +23,7 @@ The add-ons below are considered legacy and their use is discouraged.
- [`PureRenderMixin`](pure-render-mixin.html). Use [`React.PureComponent`](/react/docs/react-api.html#react.purecomponent) instead.
- [`shallowCompare`](shallow-compare.html), a helper function that performs a shallow comparison for props and state in a component to decide if a component should update.
- [`update`](update.html). Use [`kolodny/immutability-helper`](https://github.com/kolodny/immutability-helper) instead.
- [`ReactDOMFactories`](dom-factories.html), pre-configured DOM factories to make React easier to use without JSX.
### Deprecated Add-ons
-70
View File
@@ -1,70 +0,0 @@
import { parse, stringify } from 'query-string';
import getVersionTags from '../tags';
const React = window.React;
const Header = React.createClass({
getInitialState() {
const query = parse(window.location.search);
const version = query.version || 'local';
const versions = [version];
return { version, versions };
},
componentWillMount() {
getVersionTags()
.then(tags => {
let versions = tags.map(tag => tag.name.slice(1));
versions = [`local`, ...versions];
this.setState({ versions });
})
},
handleVersionChange(event) {
const query = parse(window.location.search);
query.version = event.target.value;
if (query.version === 'local') {
delete query.version;
}
window.location.search = stringify(query);
},
handleFixtureChange(event) {
window.location.pathname = event.target.value;
},
render() {
return (
<header className="header">
<div className="header__inner">
<span className="header__logo">
<img src="https://facebook.github.io/react/img/logo.svg" alt="" width="32" height="32" />
React Sandbox (v{React.version})
</span>
<div className="header-controls">
<label htmlFor="example">
<span className="sr-only">Select an example</span>
<select value={window.location.pathname} onChange={this.handleFixtureChange}>
<option value="/">Select a Fixture</option>
<option value="/range-inputs">Range Inputs</option>
<option value="/text-inputs">Text Inputs</option>
<option value="/number-inputs">Number Input</option>
<option value="/selects">Selects</option>
<option value="/textareas">Textareas</option>
<option value="/input-change-events">Input change events</option>
</select>
</label>
<label htmlFor="react_version">
<span className="sr-only">Select a version to test</span>
<select
value={this.state.version}
onChange={this.handleVersionChange}>
{this.state.versions.map(version => (
<option key={version} value={version}>{version}</option>
))}
</select>
</label>
</div>
</div>
</header>
);
},
});
export default Header;
@@ -1,34 +0,0 @@
const React = window.React;
import RangeInputFixtures from './range-inputs';
import TextInputFixtures from './text-inputs';
import SelectFixtures from './selects';
import TextAreaFixtures from './textareas';
import InputChangeEvents from './input-change-events';
import NumberInputFixtures from './number-inputs/';
/**
* A simple routing component that renders the appropriate
* fixture based on the location pathname.
*/
const FixturesPage = React.createClass({
render() {
switch (window.location.pathname) {
case '/text-inputs':
return <TextInputFixtures />;
case '/range-inputs':
return <RangeInputFixtures />;
case '/selects':
return <SelectFixtures />;
case '/textareas':
return <TextAreaFixtures />;
case '/input-change-events':
return <InputChangeEvents />;
case '/number-inputs':
return <NumberInputFixtures />;
default:
return <p>Please select a test fixture.</p>;
}
},
});
module.exports = FixturesPage;
@@ -0,0 +1,33 @@
const React = window.React;
import Fixture from '../../Fixture';
class NumberInputDecimal extends React.Component {
state = { value: '.98' };
changeValue = () => {
this.setState({
value: '0.98',
});
}
render() {
const {value} = this.state;
return (
<Fixture>
<div>{this.props.children}</div>
<div className="control-box">
<input
type="number"
value={value}
onChange={(e) => {
this.setState({value: e.target.value});
}}
/>
<button onClick={this.changeValue}>change.98 to 0.98</button>
</div>
</Fixture>
);
}
}
export default NumberInputDecimal;
@@ -1,37 +0,0 @@
const React = window.React;
import Fixture from '../../Fixture';
const NumberTestCase = React.createClass({
getInitialState() {
return { value: '' };
},
onChange(event) {
const parsed = parseFloat(event.target.value, 10)
const value = isNaN(parsed) ? '' : parsed
this.setState({ value })
},
render() {
return (
<Fixture>
<div>{this.props.children}</div>
<div className="control-box">
<fieldset>
<legend>Controlled</legend>
<input type="number" value={this.state.value} onChange={this.onChange} />
<span className="hint"> Value: {JSON.stringify(this.state.value)}</span>
</fieldset>
<fieldset>
<legend>Uncontrolled</legend>
<input type="number" defaultValue={0.5} />
</fieldset>
</div>
</Fixture>
);
},
});
export default NumberTestCase;
@@ -1,167 +0,0 @@
const React = window.React;
import FixtureSet from '../../FixtureSet';
import TestCase from '../../TestCase';
import NumberTestCase from './NumberTestCase';
const NumberInputs = React.createClass({
render() {
return (
<FixtureSet
title="Number inputs"
description="Number inputs inconsistently assign and report the value
property depending on the browser."
>
<TestCase
title="Backspacing"
description="The decimal place should not be lost"
>
<TestCase.Steps>
<li>Type "3.1"</li>
<li>Press backspace, eliminating the "1"</li>
</TestCase.Steps>
<TestCase.ExpectedResult>
The field should read "3.", preserving the decimal place
</TestCase.ExpectedResult>
<NumberTestCase />
<p className="footnote">
<b>Notes:</b> Chrome and Safari clear trailing
decimals on blur. React makes this concession so that the
value attribute remains in sync with the value property.
</p>
</TestCase>
<TestCase
title="Decimal precision"
description="Supports decimal precision greater than 2 places"
>
<TestCase.Steps>
<li>Type "0.01"</li>
</TestCase.Steps>
<TestCase.ExpectedResult>
The field should read "0.01"
</TestCase.ExpectedResult>
<NumberTestCase />
</TestCase>
<TestCase
title="Exponent form"
description="Supports exponent form ('2e4')"
>
<TestCase.Steps>
<li>Type "2e"</li>
<li>Type 4, to read "2e4"</li>
</TestCase.Steps>
<TestCase.ExpectedResult>
The field should read "2e4". The parsed value should read "20000"
</TestCase.ExpectedResult>
<NumberTestCase />
</TestCase>
<TestCase
title="Exponent Form"
description="Pressing 'e' at the end"
>
<TestCase.Steps>
<li>Type "3.14"</li>
<li>Press "e", so that the input reads "3.14e"</li>
</TestCase.Steps>
<TestCase.ExpectedResult>
The field should read "3.14e", the parsed value should be empty
</TestCase.ExpectedResult>
<NumberTestCase />
</TestCase>
<TestCase
title="Exponent Form"
description="Supports pressing 'ee' in the middle of a number"
>
<TestCase.Steps>
<li>Type "3.14"</li>
<li>Move the text cursor to after the decimal place</li>
<li>Press "e" twice, so that the value reads "3.ee14"</li>
</TestCase.Steps>
<TestCase.ExpectedResult>
The field should read "3.ee14"
</TestCase.ExpectedResult>
<NumberTestCase />
</TestCase>
<TestCase
title="Trailing Zeroes"
description="Typing '3.0' preserves the trailing zero"
>
<TestCase.Steps>
<li>Type "3.0"</li>
</TestCase.Steps>
<TestCase.ExpectedResult>
The field should read "3.0"
</TestCase.ExpectedResult>
<NumberTestCase />
</TestCase>
<TestCase
title="Inserting decimals precision"
description="Inserting '.' in to '300' maintains the trailing zeroes"
>
<TestCase.Steps>
<li>Type "300"</li>
<li>Move the cursor to after the "3"</li>
<li>Type "."</li>
</TestCase.Steps>
<TestCase.ExpectedResult>
The field should read "3.00", not "3"
</TestCase.ExpectedResult>
<NumberTestCase />
</TestCase>
<TestCase
title="Replacing numbers with -"
description="Replacing a number with the '-' sign should not clear the value"
>
<TestCase.Steps>
<li>Type "3"</li>
<li>Select the entire value"</li>
<li>Type '-' to replace '3' with '-'</li>
</TestCase.Steps>
<TestCase.ExpectedResult>
The field should read "-", not be blank.
</TestCase.ExpectedResult>
<NumberTestCase />
</TestCase>
<TestCase
title="Negative numbers"
description="Typing minus when inserting a negative number should work"
>
<TestCase.Steps>
<li>Type "-"</li>
<li>Type '3'</li>
</TestCase.Steps>
<TestCase.ExpectedResult>
The field should read "-3".
</TestCase.ExpectedResult>
<NumberTestCase />
</TestCase>
</FixtureSet>
);
},
});
export default NumberInputs;
+6 -3
View File
@@ -1,7 +1,7 @@
{
"name": "react-build",
"private": true,
"version": "15.5.4",
"version": "15.6.0-rc.1",
"devDependencies": {
"aliasify": "^2.0.0",
"art": "^0.10.1",
@@ -40,7 +40,6 @@
"coffee-script": "^1.8.0",
"core-js": "^2.2.1",
"coveralls": "^2.11.6",
"create-react-class": "15.5.0",
"del": "^2.0.2",
"derequire": "^2.0.3",
"eslint": "^3.10.2",
@@ -71,7 +70,7 @@
"merge-stream": "^1.0.0",
"object-assign": "^4.1.1",
"platform": "^1.1.0",
"prop-types": "15.5.7",
"prettier": "^1.2.2",
"run-sequence": "^1.1.4",
"through2": "^2.0.0",
"tmp": "~0.0.28",
@@ -84,6 +83,10 @@
"node": "4.x || 5.x || 6.x || 7.x",
"npm": "2.x || 3.x || 4.x"
},
"dependencies": {
"create-react-class": "^15.5.2",
"prop-types": "15.5.7"
},
"commonerConfig": {
"version": 7
},
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "react-addons-template",
"version": "15.5.4",
"version": "15.6.0-rc.1",
"main": "index.js",
"repository": "facebook/react",
"keywords": [
@@ -13,7 +13,7 @@
"object-assign": "^4.1.0"
},
"peerDependencies": {
"react": "^15.5.4"
"react": "^15.6.0-rc.1"
},
"files": [
"LICENSE",
+20
View File
@@ -0,0 +1,20 @@
# `react-dom-factories`
> Note:
> `ReactDOMFactories` is a legacy add-on. Consider using
> `React.createFactory` or JSX instead.
Prior to version 16.0.0, React maintained a whitelist of
pre-configured DOM factories. These predefined factories have been
moved to the `react-dom-factories` library.
## Example
```javascript
import ReactDOM from 'react-dom';
import DOM from 'react-dom-factories';
const greeting = DOM.div({ className: 'greeting' }, DOM.p(null, 'Hello, world!'));
ReactDOM.render(greeting, document.getElementById('app'))
```
+189
View File
@@ -0,0 +1,189 @@
'use strict';
/**
* Copyright 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
(function(f) {
if (typeof exports === 'object' && typeof module !== 'undefined') {
module.exports = f(require('react'));
/* global define */
} else if (typeof define === 'function' && define.amd) {
define(['react'], f);
} else {
var g;
if (typeof window !== 'undefined') {
g = window;
} else if (typeof global !== 'undefined') {
g = global;
} else if (typeof self !== 'undefined') {
g = self;
} else {
g = this;
}
if (typeof g.React === 'undefined') {
throw Error('React module should be required before ReactDOMFactories');
}
g.ReactDOMFactories = f(g.React);
}
})(function(React) {
/**
* Create a factory that creates HTML tag elements.
*/
var createDOMFactory = React.createFactory;
/**
* Creates a mapping from supported HTML tags to `ReactDOMComponent` classes.
*/
var ReactDOMFactories = {
a: createDOMFactory('a'),
abbr: createDOMFactory('abbr'),
address: createDOMFactory('address'),
area: createDOMFactory('area'),
article: createDOMFactory('article'),
aside: createDOMFactory('aside'),
audio: createDOMFactory('audio'),
b: createDOMFactory('b'),
base: createDOMFactory('base'),
bdi: createDOMFactory('bdi'),
bdo: createDOMFactory('bdo'),
big: createDOMFactory('big'),
blockquote: createDOMFactory('blockquote'),
body: createDOMFactory('body'),
br: createDOMFactory('br'),
button: createDOMFactory('button'),
canvas: createDOMFactory('canvas'),
caption: createDOMFactory('caption'),
cite: createDOMFactory('cite'),
code: createDOMFactory('code'),
col: createDOMFactory('col'),
colgroup: createDOMFactory('colgroup'),
data: createDOMFactory('data'),
datalist: createDOMFactory('datalist'),
dd: createDOMFactory('dd'),
del: createDOMFactory('del'),
details: createDOMFactory('details'),
dfn: createDOMFactory('dfn'),
dialog: createDOMFactory('dialog'),
div: createDOMFactory('div'),
dl: createDOMFactory('dl'),
dt: createDOMFactory('dt'),
em: createDOMFactory('em'),
embed: createDOMFactory('embed'),
fieldset: createDOMFactory('fieldset'),
figcaption: createDOMFactory('figcaption'),
figure: createDOMFactory('figure'),
footer: createDOMFactory('footer'),
form: createDOMFactory('form'),
h1: createDOMFactory('h1'),
h2: createDOMFactory('h2'),
h3: createDOMFactory('h3'),
h4: createDOMFactory('h4'),
h5: createDOMFactory('h5'),
h6: createDOMFactory('h6'),
head: createDOMFactory('head'),
header: createDOMFactory('header'),
hgroup: createDOMFactory('hgroup'),
hr: createDOMFactory('hr'),
html: createDOMFactory('html'),
i: createDOMFactory('i'),
iframe: createDOMFactory('iframe'),
img: createDOMFactory('img'),
input: createDOMFactory('input'),
ins: createDOMFactory('ins'),
kbd: createDOMFactory('kbd'),
keygen: createDOMFactory('keygen'),
label: createDOMFactory('label'),
legend: createDOMFactory('legend'),
li: createDOMFactory('li'),
link: createDOMFactory('link'),
main: createDOMFactory('main'),
map: createDOMFactory('map'),
mark: createDOMFactory('mark'),
menu: createDOMFactory('menu'),
menuitem: createDOMFactory('menuitem'),
meta: createDOMFactory('meta'),
meter: createDOMFactory('meter'),
nav: createDOMFactory('nav'),
noscript: createDOMFactory('noscript'),
object: createDOMFactory('object'),
ol: createDOMFactory('ol'),
optgroup: createDOMFactory('optgroup'),
option: createDOMFactory('option'),
output: createDOMFactory('output'),
p: createDOMFactory('p'),
param: createDOMFactory('param'),
picture: createDOMFactory('picture'),
pre: createDOMFactory('pre'),
progress: createDOMFactory('progress'),
q: createDOMFactory('q'),
rp: createDOMFactory('rp'),
rt: createDOMFactory('rt'),
ruby: createDOMFactory('ruby'),
s: createDOMFactory('s'),
samp: createDOMFactory('samp'),
script: createDOMFactory('script'),
section: createDOMFactory('section'),
select: createDOMFactory('select'),
small: createDOMFactory('small'),
source: createDOMFactory('source'),
span: createDOMFactory('span'),
strong: createDOMFactory('strong'),
style: createDOMFactory('style'),
sub: createDOMFactory('sub'),
summary: createDOMFactory('summary'),
sup: createDOMFactory('sup'),
table: createDOMFactory('table'),
tbody: createDOMFactory('tbody'),
td: createDOMFactory('td'),
textarea: createDOMFactory('textarea'),
tfoot: createDOMFactory('tfoot'),
th: createDOMFactory('th'),
thead: createDOMFactory('thead'),
time: createDOMFactory('time'),
title: createDOMFactory('title'),
tr: createDOMFactory('tr'),
track: createDOMFactory('track'),
u: createDOMFactory('u'),
ul: createDOMFactory('ul'),
var: createDOMFactory('var'),
video: createDOMFactory('video'),
wbr: createDOMFactory('wbr'),
// SVG
circle: createDOMFactory('circle'),
clipPath: createDOMFactory('clipPath'),
defs: createDOMFactory('defs'),
ellipse: createDOMFactory('ellipse'),
g: createDOMFactory('g'),
image: createDOMFactory('image'),
line: createDOMFactory('line'),
linearGradient: createDOMFactory('linearGradient'),
mask: createDOMFactory('mask'),
path: createDOMFactory('path'),
pattern: createDOMFactory('pattern'),
polygon: createDOMFactory('polygon'),
polyline: createDOMFactory('polyline'),
radialGradient: createDOMFactory('radialGradient'),
rect: createDOMFactory('rect'),
stop: createDOMFactory('stop'),
svg: createDOMFactory('svg'),
text: createDOMFactory('text'),
tspan: createDOMFactory('tspan'),
};
// due to wrapper and conditionals at the top, this will either become
// `module.exports ReactDOMFactories` if that is available,
// otherwise it will be defined via `define(['react'], ReactDOMFactories)`
// if that is available,
// otherwise it will be defined as global variable.
return ReactDOMFactories;
});
+25
View File
@@ -0,0 +1,25 @@
{
"name": "react-dom-factories",
"version": "15.6.0-rc",
"description": "React package for DOM factory methods.",
"main": "index.js",
"repository": "facebook/react",
"keywords": [
"react"
],
"license": "BSD-3-Clause",
"bugs": {
"url": "https://github.com/facebook/react/issues"
},
"homepage": "https://facebook.github.io/react/",
"peerDependencies": {
"react": "^15.6.0-rc.1"
},
"files": [
"LICENSE",
"PATENTS",
"README.md",
"index.js",
"lib/"
]
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "react-dom",
"version": "15.5.4",
"version": "15.6.0-rc.1",
"description": "React package for working with the DOM.",
"main": "index.js",
"repository": "facebook/react",
@@ -19,7 +19,7 @@
"prop-types": "~15.5.7"
},
"peerDependencies": {
"react": "^15.5.4"
"react": "^15.6.0-rc.1"
},
"files": [
"LICENSE",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "react-native-renderer",
"version": "15.5.4",
"version": "15.6.0-rc.1",
"description": "React package for use inside react-native.",
"main": "index.js",
"repository": "facebook/react",
@@ -18,7 +18,7 @@
"object-assign": "^4.1.0"
},
"peerDependencies": {
"react": "^15.5.4"
"react": "^15.6.0-rc.1"
},
"files": [
"LICENSE",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "react-test-renderer",
"version": "15.5.4",
"version": "15.6.0-rc.1",
"description": "React package for snapshot testing.",
"main": "index.js",
"repository": "facebook/react",
@@ -19,7 +19,7 @@
"object-assign": "^4.1.0"
},
"peerDependencies": {
"react": "^15.5.4"
"react": "^15.6.0-rc.1"
},
"files": [
"LICENSE",
+2 -1
View File
@@ -1,7 +1,7 @@
{
"name": "react",
"description": "React is a JavaScript library for building user interfaces.",
"version": "15.5.4",
"version": "15.6.0-rc.1",
"keywords": [
"react"
],
@@ -23,6 +23,7 @@
"node": ">=0.10.0"
},
"dependencies": {
"create-react-class": "^15.5.2",
"fbjs": "^0.8.9",
"loose-envify": "^1.1.0",
"object-assign": "^4.1.0",
+1 -1
View File
@@ -28,7 +28,7 @@ function compile(content, contentFilename) {
// the file path into backslashes on Windows.
filename = path.normalize(filename);
var reactRegex = new RegExp(
path.join('/', '(?:React|ReactDOM)(?:\.d)?\.ts$')
path.join('/', '(?:React|ReactDOM|PropTypes)(?:\.d)?\.ts$')
);
var jestRegex = /jest\.d\.ts/;
+1 -1
View File
@@ -11,4 +11,4 @@
'use strict';
module.exports = '15.5.4';
module.exports = '15.6.0-rc.1';
@@ -26,7 +26,6 @@ if (__DEV__) {
/**
* Creates a mapping from supported HTML tags to `ReactDOMComponent` classes.
* This is also accessible via `React.DOM`.
*
* @public
*/
@@ -141,7 +140,7 @@ var ReactDOMFactories = {
track: createDOMFactory('track'),
u: createDOMFactory('u'),
ul: createDOMFactory('ul'),
'var': createDOMFactory('var'),
var: createDOMFactory('var'),
video: createDOMFactory('video'),
wbr: createDOMFactory('wbr'),
+5 -5
View File
@@ -41,7 +41,7 @@ var ReactFragment = {
warning(
false,
'React.addons.createFragment only accepts a single object. Got: %s',
object
object,
);
return object;
}
@@ -49,7 +49,7 @@ var ReactFragment = {
warning(
false,
'React.addons.createFragment does not accept a ReactElement ' +
'without a wrapper object.'
'without a wrapper object.',
);
return object;
}
@@ -57,7 +57,7 @@ var ReactFragment = {
invariant(
object.nodeType !== 1,
'React.addons.createFragment(...): Encountered an invalid child; DOM ' +
'elements are not valid children of React components.'
'elements are not valid children of React components.',
);
var result = [];
@@ -68,7 +68,7 @@ var ReactFragment = {
warning(
false,
'React.addons.createFragment(...): Child objects should have ' +
'non-numeric keys so ordering is preserved.'
'non-numeric keys so ordering is preserved.',
);
warnedAboutNumeric = true;
}
@@ -77,7 +77,7 @@ var ReactFragment = {
object[key],
result,
key,
emptyFunction.thatReturnsArgument
emptyFunction.thatReturnsArgument,
);
}
+1 -2
View File
@@ -14,8 +14,7 @@
var LinkedStateMixin = require('LinkedStateMixin');
var React = require('React');
var ReactAddonsDOMDependencies = require('ReactAddonsDOMDependencies');
var ReactComponentWithPureRenderMixin =
require('ReactComponentWithPureRenderMixin');
var ReactComponentWithPureRenderMixin = require('ReactComponentWithPureRenderMixin');
var ReactCSSTransitionGroup = require('ReactCSSTransitionGroup');
var ReactFragment = require('ReactFragment');
var ReactTransitionGroup = require('ReactTransitionGroup');
@@ -0,0 +1,41 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
'use strict';
var React = require('React');
var {div} = require('ReactDOMFactories');
describe('ReactDOMFactories', () => {
it('allow factories to be called without warnings', () => {
spyOn(console, 'error');
spyOn(console, 'warn');
var element = div();
expect(element.type).toBe('div');
expect(console.error).not.toHaveBeenCalled();
expect(console.warn).not.toHaveBeenCalled();
});
it('warns once when accessing React.DOM methods', () => {
spyOn(console, 'warn');
var a = React.DOM.a();
var p = React.DOM.p();
expect(a.type).toBe('a');
expect(p.type).toBe('p');
expect(console.warn).toHaveBeenCalledTimes(1);
expect(console.warn.calls.first().args[0]).toContain(
'Warning: Accessing factories like React.DOM.a has been deprecated',
);
});
});
+13 -15
View File
@@ -16,7 +16,6 @@ var ReactDOM;
var ReactFragment;
describe('ReactFragment', () => {
beforeEach(() => {
React = require('React');
ReactDOM = require('ReactDOM');
@@ -33,9 +32,9 @@ describe('ReactFragment', () => {
var container = document.createElement('div');
expect(() => ReactDOM.render(element, container)).toThrowError(
'Objects are not valid as a React child (found: object with keys ' +
'{x, y, z}). If you meant to render a collection of children, use an ' +
'array instead or wrap the object using createFragment(object) from ' +
'the React add-ons.'
'{x, y, z}). If you meant to render a collection of children, use an ' +
'array instead or wrap the object using createFragment(object) from ' +
'the React add-ons.',
);
});
@@ -53,9 +52,9 @@ describe('ReactFragment', () => {
var container = document.createElement('div');
expect(() => ReactDOM.render(<Foo />, container)).toThrowError(
'Objects are not valid as a React child (found: object with keys ' +
'{a, b, c}). If you meant to render a collection of children, use an ' +
'array instead or wrap the object using createFragment(object) from ' +
'the React add-ons. Check the render method of `Foo`.'
'{a, b, c}). If you meant to render a collection of children, use an ' +
'array instead or wrap the object using createFragment(object) from ' +
'the React add-ons. Check the render method of `Foo`.',
);
});
@@ -64,9 +63,9 @@ describe('ReactFragment', () => {
var container = document.createElement('div');
expect(() => ReactDOM.render(<div>{oldEl}</div>, container)).toThrowError(
'Objects are not valid as a React child (found: object with keys ' +
'{_isReactElement, type, props}). It looks like you\'re using an ' +
'element created by a different version of React. Make sure to use ' +
'only one copy of React.'
"{_isReactElement, type, props}). It looks like you're using an " +
'element created by a different version of React. Make sure to use ' +
'only one copy of React.',
);
});
@@ -77,7 +76,7 @@ describe('ReactFragment', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'Child objects should have non-numeric keys so ordering is preserved.'
'Child objects should have non-numeric keys so ordering is preserved.',
);
});
@@ -86,7 +85,7 @@ describe('ReactFragment', () => {
ReactFragment.create(null);
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'React.addons.createFragment only accepts a single object.'
'React.addons.createFragment only accepts a single object.',
);
});
@@ -95,7 +94,7 @@ describe('ReactFragment', () => {
ReactFragment.create([]);
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'React.addons.createFragment only accepts a single object.'
'React.addons.createFragment only accepts a single object.',
);
});
@@ -105,8 +104,7 @@ describe('ReactFragment', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'React.addons.createFragment does not accept a ReactElement without a ' +
'wrapper object.'
'wrapper object.',
);
});
});
@@ -11,19 +11,19 @@
'use strict';
var PropTypes = require('prop-types');
var React = require('React');
var ReactDOM = require('ReactDOM');
var ReactTestUtils = require('ReactTestUtils');
var renderSubtreeIntoContainer = require('renderSubtreeIntoContainer');
describe('renderSubtreeIntoContainer', () => {
it('should pass context when rendering subtree elsewhere', () => {
var portal = document.createElement('div');
class Component extends React.Component {
static contextTypes = {
foo: React.PropTypes.string.isRequired,
foo: PropTypes.string.isRequired,
};
render() {
@@ -33,7 +33,7 @@ describe('renderSubtreeIntoContainer', () => {
class Parent extends React.Component {
static childContextTypes = {
foo: React.PropTypes.string.isRequired,
foo: PropTypes.string.isRequired,
};
getChildContext() {
@@ -47,9 +47,11 @@ describe('renderSubtreeIntoContainer', () => {
}
componentDidMount() {
expect(function() {
renderSubtreeIntoContainer(this, <Component />, portal);
}.bind(this)).not.toThrow();
expect(
function() {
renderSubtreeIntoContainer(this, <Component />, portal);
}.bind(this),
).not.toThrow();
}
}
@@ -62,7 +64,7 @@ describe('renderSubtreeIntoContainer', () => {
class Component extends React.Component {
static contextTypes = {
foo: React.PropTypes.string.isRequired,
foo: PropTypes.string.isRequired,
};
render() {
@@ -75,7 +77,7 @@ describe('renderSubtreeIntoContainer', () => {
// eslint-disable-next-line no-unused-vars
class Parent extends React.Component {
static childContextTypes = {
foo: React.PropTypes.string.isRequired,
foo: PropTypes.string.isRequired,
};
getChildContext() {
@@ -103,8 +105,8 @@ describe('renderSubtreeIntoContainer', () => {
class Component extends React.Component {
static contextTypes = {
foo: React.PropTypes.string.isRequired,
getFoo: React.PropTypes.func.isRequired,
foo: PropTypes.string.isRequired,
getFoo: PropTypes.func.isRequired,
};
render() {
@@ -114,8 +116,8 @@ describe('renderSubtreeIntoContainer', () => {
class Parent extends React.Component {
static childContextTypes = {
foo: React.PropTypes.string.isRequired,
getFoo: React.PropTypes.func.isRequired,
foo: PropTypes.string.isRequired,
getFoo: PropTypes.func.isRequired,
};
state = {
@@ -155,8 +157,8 @@ describe('renderSubtreeIntoContainer', () => {
class Component extends React.Component {
static contextTypes = {
foo: React.PropTypes.string.isRequired,
getFoo: React.PropTypes.func.isRequired,
foo: PropTypes.string.isRequired,
getFoo: PropTypes.func.isRequired,
};
render() {
@@ -166,8 +168,8 @@ describe('renderSubtreeIntoContainer', () => {
class Parent extends React.Component {
static childContextTypes = {
foo: React.PropTypes.string.isRequired,
getFoo: React.PropTypes.func.isRequired,
foo: PropTypes.string.isRequired,
getFoo: PropTypes.func.isRequired,
};
getChildContext() {
@@ -195,5 +197,4 @@ describe('renderSubtreeIntoContainer', () => {
ReactDOM.render(<Parent bar="changed" />, container);
expect(portal.firstChild.innerHTML).toBe('changed-changed');
});
});
+39 -35
View File
@@ -14,7 +14,6 @@
var update = require('update');
describe('update', () => {
describe('$push', () => {
it('pushes', () => {
expect(update([1], {$push: [7]})).toEqual([1, 7]);
@@ -27,12 +26,12 @@ describe('update', () => {
it('only pushes an array', () => {
expect(update.bind(null, [], {$push: 7})).toThrowError(
'update(): expected spec of $push to be an array; got 7. Did you ' +
'forget to wrap your parameter in an array?'
'forget to wrap your parameter in an array?',
);
});
it('only pushes unto an array', () => {
expect(update.bind(null, 1, {$push: 7})).toThrowError(
'update(): expected target of $push to be an array; got 1.'
'update(): expected target of $push to be an array; got 1.',
);
});
});
@@ -49,12 +48,12 @@ describe('update', () => {
it('only unshifts an array', () => {
expect(update.bind(null, [], {$unshift: 7})).toThrowError(
'update(): expected spec of $unshift to be an array; got 7. Did you ' +
'forget to wrap your parameter in an array?'
'forget to wrap your parameter in an array?',
);
});
it('only unshifts unto an array', () => {
expect(update.bind(null, 1, {$unshift: 7})).toThrowError(
'update(): expected target of $unshift to be an array; got 1.'
'update(): expected target of $unshift to be an array; got 1.',
);
});
});
@@ -71,16 +70,16 @@ describe('update', () => {
it('only splices an array of arrays', () => {
expect(update.bind(null, [], {$splice: 1})).toThrowError(
'update(): expected spec of $splice to be an array of arrays; got 1. ' +
'Did you forget to wrap your parameters in an array?'
'Did you forget to wrap your parameters in an array?',
);
expect(update.bind(null, [], {$splice: [1]})).toThrowError(
'update(): expected spec of $splice to be an array of arrays; got 1. ' +
'Did you forget to wrap your parameters in an array?'
'Did you forget to wrap your parameters in an array?',
);
});
it('only splices unto an array', () => {
expect(update.bind(null, 1, {$splice: 7})).toThrowError(
'Expected $splice target to be an array; got 1'
'Expected $splice target to be an array; got 1',
);
});
});
@@ -96,12 +95,12 @@ describe('update', () => {
});
it('only merges with an object', () => {
expect(update.bind(null, {}, {$merge: 7})).toThrowError(
'update(): $merge expects a spec of type \'object\'; got 7'
"update(): $merge expects a spec of type 'object'; got 7",
);
});
it('only merges with an object', () => {
expect(update.bind(null, 7, {$merge: {a: 'b'}})).toThrowError(
'update(): $merge expects a target of type \'object\'; got 7'
"update(): $merge expects a target of type 'object'; got 7",
);
});
});
@@ -131,32 +130,37 @@ describe('update', () => {
});
it('only applies a function', () => {
expect(update.bind(null, 2, {$apply: 123})).toThrowError(
'update(): expected spec of $apply to be a function; got 123.'
'update(): expected spec of $apply to be a function; got 123.',
);
});
});
it('should support deep updates', () => {
expect(update({
a: 'b',
c: {
d: 'e',
f: [1],
g: [2],
h: [3],
i: {j: 'k'},
l: 4,
},
}, {
c: {
d: {$set: 'm'},
f: {$push: [5]},
g: {$unshift: [6]},
h: {$splice: [[0, 1, 7]]},
i: {$merge: {n: 'o'}},
l: {$apply: (x) => x * 2},
},
})).toEqual({
expect(
update(
{
a: 'b',
c: {
d: 'e',
f: [1],
g: [2],
h: [3],
i: {j: 'k'},
l: 4,
},
},
{
c: {
d: {$set: 'm'},
f: {$push: [5]},
g: {$unshift: [6]},
h: {$splice: [[0, 1, 7]]},
i: {$merge: {n: 'o'}},
l: {$apply: x => x * 2},
},
},
),
).toEqual({
a: 'b',
c: {
d: 'm',
@@ -172,14 +176,14 @@ describe('update', () => {
it('should require a command', () => {
expect(update.bind(null, {a: 'b'}, {a: 'c'})).toThrowError(
'update(): You provided a key path to update() that did not contain ' +
'one of $push, $unshift, $splice, $set, $merge, $apply. Did you ' +
'forget to include {$set: ...}?'
'one of $push, $unshift, $splice, $set, $merge, $apply. Did you ' +
'forget to include {$set: ...}?',
);
});
it('should perform safe hasOwnProperty check', () => {
expect(update({}, {'hasOwnProperty': {$set: 'a'}})).toEqual({
'hasOwnProperty': 'a',
expect(update({}, {hasOwnProperty: {$set: 'a'}})).toEqual({
hasOwnProperty: 'a',
});
});
});
+1 -1
View File
@@ -30,7 +30,7 @@ var LinkedStateMixin = {
linkState: function(key) {
return new ReactLink(
this.state[key],
ReactStateSetters.createStateKeySetter(this, key)
ReactStateSetters.createStateKeySetter(this, key),
);
},
};
-22
View File
@@ -48,26 +48,4 @@ function ReactLink(value, requestChange) {
this.requestChange = requestChange;
}
/**
* Creates a PropType that enforces the ReactLink API and optionally checks the
* type of the value being passed inside the link. Example:
*
* MyComponent.propTypes = {
* tabIndexLink: ReactLink.PropTypes.link(React.PropTypes.number)
* }
*/
function createLinkTypeChecker(linkType) {
var shapes = {
value: linkType === undefined ?
React.PropTypes.any.isRequired :
linkType.isRequired,
requestChange: React.PropTypes.func.isRequired,
};
return React.PropTypes.shape(shapes);
}
ReactLink.PropTypes = {
link: createLinkTypeChecker,
};
module.exports = ReactLink;
@@ -28,16 +28,19 @@ function createTransitionTimeoutPropValidator(transitionType) {
// If no timeout duration is provided
if (props[timeoutPropName] == null) {
return new Error(
timeoutPropName + ' wasn\'t supplied to ReactCSSTransitionGroup: ' +
'this can cause unreliable animations and won\'t be supported in ' +
'a future version of React. See ' +
'https://fb.me/react-animation-transition-group-timeout for more ' +
'information.'
timeoutPropName +
" wasn't supplied to ReactCSSTransitionGroup: " +
"this can cause unreliable animations and won't be supported in " +
'a future version of React. See ' +
'https://fb.me/react-animation-transition-group-timeout for more ' +
'information.',
);
// If the duration isn't a number
// If the duration isn't a number
} else if (typeof props[timeoutPropName] !== 'number') {
return new Error(timeoutPropName + ' must be a number (in milliseconds)');
return new Error(
timeoutPropName + ' must be a number (in milliseconds)',
);
}
}
};
@@ -68,7 +71,7 @@ class ReactCSSTransitionGroup extends React.Component {
transitionLeave: true,
};
_wrapChild = (child) => {
_wrapChild = child => {
// We need to provide this childFactory so that
// ReactCSSTransitionGroupChild can receive updates to name, enter, and
// leave while it is leaving.
@@ -83,14 +86,14 @@ class ReactCSSTransitionGroup extends React.Component {
enterTimeout: this.props.transitionEnterTimeout,
leaveTimeout: this.props.transitionLeaveTimeout,
},
child
child,
);
};
render() {
return React.createElement(
ReactTransitionGroup,
Object.assign({}, this.props, {childFactory: this._wrapChild})
Object.assign({}, this.props, {childFactory: this._wrapChild}),
);
}
}
@@ -17,7 +17,6 @@ var ReactAddonsDOMDependencies = require('ReactAddonsDOMDependencies');
var propTypesFactory = require('prop-types/factory');
var PropTypes = propTypesFactory(React.isValidElement);
var CSSCore = require('CSSCore');
var ReactTransitionEvents = require('ReactTransitionEvents');
@@ -67,8 +66,10 @@ class ReactCSSTransitionGroupChild extends React.Component {
return;
}
var className = this.props.name[animationType] || this.props.name + '-' + animationType;
var activeClassName = this.props.name[animationType + 'Active'] || className + '-active';
var className =
this.props.name[animationType] || this.props.name + '-' + animationType;
var activeClassName =
this.props.name[animationType + 'Active'] || className + '-active';
var timeout = null;
var endListener = function(e) {
@@ -149,29 +150,29 @@ class ReactCSSTransitionGroupChild extends React.Component {
this.classNameAndNodeQueue.length = 0;
}
componentWillAppear = (done) => {
componentWillAppear = done => {
if (this.props.appear) {
this.transition('appear', done, this.props.appearTimeout);
} else {
done();
}
}
};
componentWillEnter = (done) => {
componentWillEnter = done => {
if (this.props.enter) {
this.transition('enter', done, this.props.enterTimeout);
} else {
done();
}
}
};
componentWillLeave = (done) => {
componentWillLeave = done => {
if (this.props.leave) {
this.transition('leave', done, this.props.leaveTimeout);
} else {
done();
}
}
};
render() {
return onlyChild(this.props.children);
@@ -86,7 +86,7 @@ var ReactTransitionChildMapping = {
for (i = 0; i < nextKeysPending[nextKey].length; i++) {
var pendingNextKey = nextKeysPending[nextKey][i];
childMapping[nextKeysPending[nextKey][i]] = getValueForKey(
pendingNextKey
pendingNextKey,
);
}
}
+30 -30
View File
@@ -59,14 +59,14 @@ class ReactTransitionGroup extends React.Component {
componentWillReceiveProps(nextProps) {
var nextChildMapping = ReactTransitionChildMapping.getChildMapping(
nextProps.children
nextProps.children,
);
var prevChildMapping = this.state.children;
this.setState({
children: ReactTransitionChildMapping.mergeChildMappings(
prevChildMapping,
nextChildMapping
nextChildMapping,
),
});
@@ -74,16 +74,22 @@ class ReactTransitionGroup extends React.Component {
for (key in nextChildMapping) {
var hasPrev = prevChildMapping && prevChildMapping.hasOwnProperty(key);
if (nextChildMapping[key] && !hasPrev &&
!this.currentlyTransitioningKeys[key]) {
if (
nextChildMapping[key] &&
!hasPrev &&
!this.currentlyTransitioningKeys[key]
) {
this.keysToEnter.push(key);
}
}
for (key in prevChildMapping) {
var hasNext = nextChildMapping && nextChildMapping.hasOwnProperty(key);
if (prevChildMapping[key] && !hasNext &&
!this.currentlyTransitioningKeys[key]) {
if (
prevChildMapping[key] &&
!hasNext &&
!this.currentlyTransitioningKeys[key]
) {
this.keysToLeave.push(key);
}
}
@@ -101,21 +107,19 @@ class ReactTransitionGroup extends React.Component {
keysToLeave.forEach(this.performLeave);
}
performAppear = (key) => {
performAppear = key => {
this.currentlyTransitioningKeys[key] = true;
var component = this.refs[key];
if (component.componentWillAppear) {
component.componentWillAppear(
this._handleDoneAppearing.bind(this, key)
);
component.componentWillAppear(this._handleDoneAppearing.bind(this, key));
} else {
this._handleDoneAppearing(key);
}
};
_handleDoneAppearing = (key) => {
_handleDoneAppearing = key => {
var component = this.refs[key];
if (component.componentDidAppear) {
component.componentDidAppear();
@@ -124,7 +128,7 @@ class ReactTransitionGroup extends React.Component {
delete this.currentlyTransitioningKeys[key];
var currentChildMapping = ReactTransitionChildMapping.getChildMapping(
this.props.children
this.props.children,
);
if (!currentChildMapping || !currentChildMapping.hasOwnProperty(key)) {
@@ -133,21 +137,19 @@ class ReactTransitionGroup extends React.Component {
}
};
performEnter = (key) => {
performEnter = key => {
this.currentlyTransitioningKeys[key] = true;
var component = this.refs[key];
if (component.componentWillEnter) {
component.componentWillEnter(
this._handleDoneEntering.bind(this, key)
);
component.componentWillEnter(this._handleDoneEntering.bind(this, key));
} else {
this._handleDoneEntering(key);
}
};
_handleDoneEntering = (key) => {
_handleDoneEntering = key => {
var component = this.refs[key];
if (component.componentDidEnter) {
component.componentDidEnter();
@@ -156,7 +158,7 @@ class ReactTransitionGroup extends React.Component {
delete this.currentlyTransitioningKeys[key];
var currentChildMapping = ReactTransitionChildMapping.getChildMapping(
this.props.children
this.props.children,
);
if (!currentChildMapping || !currentChildMapping.hasOwnProperty(key)) {
@@ -165,7 +167,7 @@ class ReactTransitionGroup extends React.Component {
}
};
performLeave = (key) => {
performLeave = key => {
this.currentlyTransitioningKeys[key] = true;
var component = this.refs[key];
@@ -179,7 +181,7 @@ class ReactTransitionGroup extends React.Component {
}
};
_handleDoneLeaving = (key) => {
_handleDoneLeaving = key => {
var component = this.refs[key];
if (component.componentDidLeave) {
@@ -189,7 +191,7 @@ class ReactTransitionGroup extends React.Component {
delete this.currentlyTransitioningKeys[key];
var currentChildMapping = ReactTransitionChildMapping.getChildMapping(
this.props.children
this.props.children,
);
if (currentChildMapping && currentChildMapping.hasOwnProperty(key)) {
@@ -216,10 +218,12 @@ class ReactTransitionGroup extends React.Component {
// already been removed. In case you need this behavior you can provide
// a childFactory function to wrap every child, even the ones that are
// leaving.
childrenToRender.push(React.cloneElement(
this.props.childFactory(child),
{ref: key, key: key}
));
childrenToRender.push(
React.cloneElement(this.props.childFactory(child), {
ref: key,
key: key,
}),
);
}
}
@@ -235,11 +239,7 @@ class ReactTransitionGroup extends React.Component {
delete props.transitionAppearTimeout;
delete props.component;
return React.createElement(
this.props.component,
props,
childrenToRender
);
return React.createElement(this.props.component, props, childrenToRender);
}
}
@@ -27,7 +27,7 @@ describe('ReactTransitionChildMapping', () => {
var two = <div key="two" />;
var component = <div>{one}{two}</div>;
expect(
ReactTransitionChildMapping.getChildMapping(component.props.children)
ReactTransitionChildMapping.getChildMapping(component.props.children),
).toEqual({
'.$one': one,
'.$two': two,
+17 -17
View File
@@ -9,7 +9,7 @@
* @providesModule update
*/
/* global hasOwnProperty:true */
/* global hasOwnProperty:true */
'use strict';
@@ -53,15 +53,15 @@ function invariantArrayCase(value, spec, command) {
Array.isArray(value),
'update(): expected target of %s to be an array; got %s.',
command,
value
value,
);
var specValue = spec[command];
invariant(
Array.isArray(specValue),
'update(): expected spec of %s to be an array; got %s. ' +
'Did you forget to wrap your parameter in an array?',
'Did you forget to wrap your parameter in an array?',
command,
specValue
specValue,
);
}
@@ -73,16 +73,16 @@ function update(value, spec) {
invariant(
typeof spec === 'object',
'update(): You provided a key path to update() that did not contain one ' +
'of %s. Did you forget to include {%s: ...}?',
'of %s. Did you forget to include {%s: ...}?',
ALL_COMMANDS_LIST.join(', '),
COMMAND_SET
COMMAND_SET,
);
if (hasOwnProperty.call(spec, COMMAND_SET)) {
invariant(
Object.keys(spec).length === 1,
'Cannot have more than one key in an object with %s',
COMMAND_SET
COMMAND_SET,
);
return spec[COMMAND_SET];
@@ -94,15 +94,15 @@ function update(value, spec) {
var mergeObj = spec[COMMAND_MERGE];
invariant(
mergeObj && typeof mergeObj === 'object',
'update(): %s expects a spec of type \'object\'; got %s',
"update(): %s expects a spec of type 'object'; got %s",
COMMAND_MERGE,
mergeObj
mergeObj,
);
invariant(
nextValue && typeof nextValue === 'object',
'update(): %s expects a target of type \'object\'; got %s',
"update(): %s expects a target of type 'object'; got %s",
COMMAND_MERGE,
nextValue
nextValue,
);
Object.assign(nextValue, spec[COMMAND_MERGE]);
}
@@ -126,22 +126,22 @@ function update(value, spec) {
Array.isArray(value),
'Expected %s target to be an array; got %s',
COMMAND_SPLICE,
value
value,
);
invariant(
Array.isArray(spec[COMMAND_SPLICE]),
'update(): expected spec of %s to be an array of arrays; got %s. ' +
'Did you forget to wrap your parameters in an array?',
'Did you forget to wrap your parameters in an array?',
COMMAND_SPLICE,
spec[COMMAND_SPLICE]
spec[COMMAND_SPLICE],
);
spec[COMMAND_SPLICE].forEach(function(args) {
invariant(
Array.isArray(args),
'update(): expected spec of %s to be an array of arrays; got %s. ' +
'Did you forget to wrap your parameters in an array?',
'Did you forget to wrap your parameters in an array?',
COMMAND_SPLICE,
spec[COMMAND_SPLICE]
spec[COMMAND_SPLICE],
);
nextValue.splice.apply(nextValue, args);
});
@@ -152,7 +152,7 @@ function update(value, spec) {
typeof spec[COMMAND_APPLY] === 'function',
'update(): expected spec of %s to be a function; got %s.',
COMMAND_APPLY,
spec[COMMAND_APPLY]
spec[COMMAND_APPLY],
);
nextValue = spec[COMMAND_APPLY](nextValue);
}
+76 -23
View File
@@ -11,23 +11,22 @@
'use strict';
var ReactBaseClasses = require('ReactBaseClasses');
var ReactChildren = require('ReactChildren');
var ReactComponent = require('ReactComponent');
var ReactPureComponent = require('ReactPureComponent');
var ReactClass = require('ReactClass');
var ReactDOMFactories = require('ReactDOMFactories');
var ReactElement = require('ReactElement');
var ReactPropTypes = require('ReactPropTypes');
var ReactVersion = require('ReactVersion');
var createReactClass = require('createClass');
var onlyChild = require('onlyChild');
var warning = require('warning');
var createElement = ReactElement.createElement;
var createFactory = ReactElement.createFactory;
var cloneElement = ReactElement.cloneElement;
if (__DEV__) {
var lowPriorityWarning = require('lowPriorityWarning');
var canDefineProperty = require('canDefineProperty');
var ReactElementValidator = require('ReactElementValidator');
var didWarnPropTypesDeprecated = false;
@@ -37,24 +36,39 @@ if (__DEV__) {
}
var __spread = Object.assign;
var createMixin = function(mixin) {
return mixin;
};
if (__DEV__) {
var warned = false;
var warnedForSpread = false;
var warnedForCreateMixin = false;
__spread = function() {
warning(
warned,
lowPriorityWarning(
warnedForSpread,
'React.__spread is deprecated and should not be used. Use ' +
'Object.assign directly or another helper function with similar ' +
'semantics. You may be seeing this warning due to your compiler. ' +
'See https://fb.me/react-spread-deprecation for more details.'
'Object.assign directly or another helper function with similar ' +
'semantics. You may be seeing this warning due to your compiler. ' +
'See https://fb.me/react-spread-deprecation for more details.',
);
warned = true;
warnedForSpread = true;
return Object.assign.apply(null, arguments);
};
createMixin = function(mixin) {
lowPriorityWarning(
warnedForCreateMixin,
'React.createMixin is deprecated and should not be used. ' +
'In React v16.0, it will be removed. ' +
'You can use this mixin directly instead. ' +
'See https://fb.me/createmixin-was-never-implemented for more info.',
);
warnedForCreateMixin = true;
return mixin;
};
}
var React = {
// Modern
Children: {
@@ -65,8 +79,8 @@ var React = {
only: onlyChild,
},
Component: ReactComponent,
PureComponent: ReactPureComponent,
Component: ReactBaseClasses.Component,
PureComponent: ReactBaseClasses.PureComponent,
createElement: createElement,
cloneElement: cloneElement,
@@ -75,12 +89,9 @@ var React = {
// Classic
PropTypes: ReactPropTypes,
createClass: ReactClass.createClass,
createClass: createReactClass,
createFactory: createFactory,
createMixin: function(mixin) {
// Currently a noop. Will be used to validate and trace mixins.
return mixin;
},
createMixin: createMixin,
// This looks DOM specific but these are actually isomorphic helpers
// since they are just generating DOM strings.
@@ -92,21 +103,63 @@ var React = {
__spread: __spread,
};
// TODO: Fix tests so that this deprecation warning doesn't cause failures.
if (__DEV__) {
let warnedForCreateClass = false;
if (canDefineProperty) {
Object.defineProperty(React, 'PropTypes', {
get() {
warning(
lowPriorityWarning(
didWarnPropTypesDeprecated,
'Accessing PropTypes via the main React package is deprecated. Use ' +
'the prop-types package from npm instead.'
'Accessing PropTypes via the main React package is deprecated,' +
' and will be removed in React v16.0.' +
' Use the latest available v15.* prop-types package from npm instead.' +
' For info on usage, compatibility, migration and more, see ' +
'https://fb.me/prop-types-docs',
);
didWarnPropTypesDeprecated = true;
return ReactPropTypes;
},
});
Object.defineProperty(React, 'createClass', {
get: function() {
lowPriorityWarning(
warnedForCreateClass,
'Accessing createClass via the main React package is deprecated,' +
' and will be removed in React v16.0.' +
" Use a plain JavaScript class instead. If you're not yet " +
'ready to migrate, create-react-class v15.* is available ' +
'on npm as a temporary, drop-in replacement. ' +
'For more info see https://fb.me/react-create-class',
);
warnedForCreateClass = true;
return createReactClass;
},
});
}
// React.DOM factories are deprecated. Wrap these methods so that
// invocations of the React.DOM namespace and alert users to switch
// to the `react-dom-factories` package.
React.DOM = {};
var warnedForFactories = false;
Object.keys(ReactDOMFactories).forEach(function(factory) {
React.DOM[factory] = function(...args) {
if (!warnedForFactories) {
lowPriorityWarning(
false,
'Accessing factories like React.DOM.%s has been deprecated ' +
'and will be removed in v16.0+. Use the ' +
'react-dom-factories package instead. ' +
' Version 1.0 provides a drop-in replacement.' +
' For more info, see https://fb.me/react-dom-factories',
factory,
);
warnedForFactories = true;
}
return ReactDOMFactories[factory](...args);
};
});
}
module.exports = React;
+71
View File
@@ -0,0 +1,71 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
'use strict';
describe('React', () => {
var React;
beforeEach(() => {
React = require('React');
});
it('should log a deprecation warning once when using React.__spread', () => {
spyOn(console, 'warn');
React.__spread({});
React.__spread({});
expect(console.warn.calls.count()).toBe(1);
expect(console.warn.calls.argsFor(0)[0]).toContain(
'React.__spread is deprecated and should not be used',
);
});
it('should log a deprecation warning once when using React.createMixin', () => {
spyOn(console, 'warn');
React.createMixin();
React.createMixin();
expect(console.warn.calls.count()).toBe(1);
expect(console.warn.calls.argsFor(0)[0]).toContain(
'React.createMixin is deprecated and should not be used',
);
});
it('should warn once when attempting to access React.createClass', () => {
spyOn(console, 'warn');
let createClass = React.createClass;
createClass = React.createClass;
expect(createClass).not.toBe(undefined);
expect(console.warn.calls.count()).toBe(1);
expect(console.warn.calls.argsFor(0)[0]).toContain(
'Warning: Accessing createClass via the main React package is ' +
'deprecated, and will be removed in React v16.0. ' +
"Use a plain JavaScript class instead. If you're not yet ready " +
'to migrate, create-react-class v15.* is available on npm as ' +
'a temporary, drop-in replacement. ' +
'For more info see https://fb.me/react-create-class',
);
});
it('should warn once when attempting to access React.PropTypes', () => {
spyOn(console, 'warn');
let PropTypes = React.PropTypes;
PropTypes = React.PropTypes;
expect(PropTypes).not.toBe(undefined);
expect(console.warn.calls.count()).toBe(1);
expect(console.warn.calls.argsFor(0)[0]).toContain(
'Warning: Accessing PropTypes via the main React package is ' +
'deprecated, and will be removed in React v16.0. ' +
'Use the latest available v15.* prop-types package from ' +
'npm instead. For info on usage, compatibility, migration ' +
'and more, see https://fb.me/prop-types-docs',
);
});
});
+11 -18
View File
@@ -20,13 +20,11 @@ var traverseAllChildren = require('traverseAllChildren');
var twoArgumentPooler = PooledClass.twoArgumentPooler;
var fourArgumentPooler = PooledClass.fourArgumentPooler;
var userProvidedKeyEscapeRegex = /\/+/g;
function escapeUserProvidedKey(text) {
return ('' + text).replace(userProvidedKeyEscapeRegex, '$&/');
}
/**
* PooledClass representing the bookkeeping associated with performing a child
* traversal. Allows avoiding binding callbacks.
@@ -68,13 +66,14 @@ function forEachChildren(children, forEachFunc, forEachContext) {
if (children == null) {
return children;
}
var traverseContext =
ForEachBookKeeping.getPooled(forEachFunc, forEachContext);
var traverseContext = ForEachBookKeeping.getPooled(
forEachFunc,
forEachContext,
);
traverseAllChildren(children, forEachSingleChild, traverseContext);
ForEachBookKeeping.release(traverseContext);
}
/**
* PooledClass representing the bookkeeping associated with performing a child
* mapping. Allows avoiding binding callbacks.
@@ -109,7 +108,7 @@ function mapSingleChildIntoContext(bookKeeping, child, childKey) {
mappedChild,
result,
childKey,
emptyFunction.thatReturnsArgument
emptyFunction.thatReturnsArgument,
);
} else if (mappedChild != null) {
if (ReactElement.isValidElement(mappedChild)) {
@@ -118,12 +117,10 @@ function mapSingleChildIntoContext(bookKeeping, child, childKey) {
// Keep both the (mapped) and old keys if they differ, just as
// traverseAllChildren used to do for objects as children
keyPrefix +
(
(mappedChild.key && (!child || (child.key !== mappedChild.key))) ?
escapeUserProvidedKey(mappedChild.key) + '/' :
''
) +
childKey
(mappedChild.key && (!child || child.key !== mappedChild.key)
? escapeUserProvidedKey(mappedChild.key) + '/'
: '') +
childKey,
);
}
result.push(mappedChild);
@@ -139,7 +136,7 @@ function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
array,
escapedPrefix,
func,
context
context,
);
traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
MapBookKeeping.release(traverseContext);
@@ -167,8 +164,6 @@ function mapChildren(children, func, context) {
return result;
}
function forEachSingleChildDummy(traverseContext, child, name) {
return null;
}
@@ -186,7 +181,6 @@ function countChildren(children, context) {
return traverseAllChildren(children, forEachSingleChildDummy, null);
}
/**
* Flatten a children object (typically specified as `props.children`) and
* return an array with appropriately re-keyed children.
@@ -199,12 +193,11 @@ function toArray(children) {
children,
result,
null,
emptyFunction.thatReturnsArgument
emptyFunction.thatReturnsArgument,
);
return result;
}
var ReactChildren = {
forEach: forEachChildren,
map: mapChildren,
@@ -69,7 +69,6 @@ describe('ReactChildren', () => {
var mappedChildren = ReactChildren.map(instance.props.children, callback);
expect(callback).toHaveBeenCalledWith(simpleKid, 0);
expect(mappedChildren[0]).toEqual(<span key=".$simple" />);
});
it('should pass key to returned component', () => {
@@ -103,8 +102,11 @@ describe('ReactChildren', () => {
ReactChildren.forEach(instance.props.children, callback, scopeTester);
expect(lastContext).toBe(scopeTester);
var mappedChildren =
ReactChildren.map(instance.props.children, callback, scopeTester);
var mappedChildren = ReactChildren.map(
instance.props.children,
callback,
scopeTester,
);
expect(ReactChildren.count(mappedChildren)).toBe(1);
expect(mappedChildren[0]).toBe(scopeTester);
@@ -118,9 +120,9 @@ describe('ReactChildren', () => {
var four = <div key="keyFour" />;
var mapped = [
<div key="giraffe" />, // Key should be joined to obj key
null, // Key should be added even if we don't supply it!
<div />, // Key should be added even if not supplied!
<div key="giraffe" />, // Key should be joined to obj key
null, // Key should be added even if we don't supply it!
<div />, // Key should be added even if not supplied!
<span />, // Map from null to something.
<div key="keyFour" />,
];
@@ -146,8 +148,7 @@ describe('ReactChildren', () => {
expect(callback).toHaveBeenCalledWith(four, 4);
callback.calls.reset();
var mappedChildren =
ReactChildren.map(instance.props.children, callback);
var mappedChildren = ReactChildren.map(instance.props.children, callback);
expect(callback.calls.count()).toBe(5);
expect(ReactChildren.count(mappedChildren)).toBe(4);
// Keys default to indices.
@@ -156,9 +157,7 @@ describe('ReactChildren', () => {
mappedChildren[1].key,
mappedChildren[2].key,
mappedChildren[3].key,
]).toEqual(
['giraffe/.$keyZero', '.$keyTwo', '.3', '.$keyFour']
);
]).toEqual(['giraffe/.$keyZero', '.$keyTwo', '.3', '.$keyFour']);
expect(callback).toHaveBeenCalledWith(zero, 0);
expect(callback).toHaveBeenCalledWith(one, 1);
@@ -185,15 +184,15 @@ describe('ReactChildren', () => {
// 1. If grouped in an Object, the object key combined with `key` prop
// 2. If grouped in an Array, the `key` prop, falling back to array index
var zeroMapped = <div key="giraffe" />; // Key should be overridden
var twoMapped = <div />; // Key should be added even if not supplied!
var zeroMapped = <div key="giraffe" />; // Key should be overridden
var twoMapped = <div />; // Key should be added even if not supplied!
var fourMapped = <div key="keyFour" />;
var fiveMapped = <div />;
var callback = jasmine.createSpy().and.callFake(function(kid, index) {
return index === 0 ? zeroMapped :
index === 1 ? twoMapped :
index === 2 ? fourMapped : fiveMapped;
return index === 0
? zeroMapped
: index === 1 ? twoMapped : index === 2 ? fourMapped : fiveMapped;
});
var frag = ReactFragment.create({
@@ -203,12 +202,7 @@ describe('ReactChildren', () => {
});
var instance = <div>{[frag]}</div>;
expect([
frag[0].key,
frag[1].key,
frag[2].key,
frag[3].key,
]).toEqual([
expect([frag[0].key, frag[1].key, frag[2].key, frag[3].key]).toEqual([
'firstHalfKey/.$keyZero',
'firstHalfKey/.$keyTwo',
'secondHalfKey/.$keyFour',
@@ -244,9 +238,13 @@ describe('ReactChildren', () => {
'.0:$keyFive/.$keyFiveInner',
]);
expect(mappedChildren[0]).toEqual(<div key="giraffe/.0:$firstHalfKey/.$keyZero" />);
expect(mappedChildren[0]).toEqual(
<div key="giraffe/.0:$firstHalfKey/.$keyZero" />,
);
expect(mappedChildren[1]).toEqual(<div key=".0:$firstHalfKey/.$keyTwo" />);
expect(mappedChildren[2]).toEqual(<div key="keyFour/.0:$secondHalfKey/.$keyFour" />);
expect(mappedChildren[2]).toEqual(
<div key="keyFour/.0:$secondHalfKey/.$keyFour" />,
);
expect(mappedChildren[3]).toEqual(<div key=".0:$keyFive/.$keyFiveInner" />);
});
@@ -271,21 +269,24 @@ describe('ReactChildren', () => {
);
var expectedForcedKeys = ['giraffe/.$keyZero', '.$keyOne'];
var mappedChildrenForcedKeys =
ReactChildren.map(forcedKeys.props.children, mapFn);
var mappedForcedKeys = mappedChildrenForcedKeys.map((c) => c.key);
var mappedChildrenForcedKeys = ReactChildren.map(
forcedKeys.props.children,
mapFn,
);
var mappedForcedKeys = mappedChildrenForcedKeys.map(c => c.key);
expect(mappedForcedKeys).toEqual(expectedForcedKeys);
var expectedRemappedForcedKeys = [
'giraffe/.$giraffe/.$keyZero',
'.$.$keyOne',
];
var remappedChildrenForcedKeys =
ReactChildren.map(mappedChildrenForcedKeys, mapFn);
expect(
remappedChildrenForcedKeys.map((c) => c.key)
).toEqual(expectedRemappedForcedKeys);
var remappedChildrenForcedKeys = ReactChildren.map(
mappedChildrenForcedKeys,
mapFn,
);
expect(remappedChildrenForcedKeys.map(c => c.key)).toEqual(
expectedRemappedForcedKeys,
);
});
it('should not throw if key provided is a dupe with array key', () => {
@@ -315,14 +316,10 @@ describe('ReactChildren', () => {
</div>
);
var mapped = ReactChildren.map(
instance.props.children,
element => element,
);
var mapped = ReactChildren.map(instance.props.children, element => element);
var mappedWithClone = ReactChildren.map(
instance.props.children,
element => React.cloneElement(element),
var mappedWithClone = ReactChildren.map(instance.props.children, element =>
React.cloneElement(element),
);
expect(mapped[0].key).toBe(mappedWithClone[0].key);
@@ -335,14 +332,10 @@ describe('ReactChildren', () => {
</div>
);
var mapped = ReactChildren.map(
instance.props.children,
element => element,
);
var mapped = ReactChildren.map(instance.props.children, element => element);
var mappedWithClone = ReactChildren.map(
instance.props.children,
element => React.cloneElement(element, {key: 'unique'}),
var mappedWithClone = ReactChildren.map(instance.props.children, element =>
React.cloneElement(element, {key: 'unique'}),
);
expect(mapped[0].key).toBe(mappedWithClone[0].key);
@@ -399,14 +392,16 @@ describe('ReactChildren', () => {
// 2. If grouped in an Array, the `key` prop, falling back to array index
var instance = (
<div>{[
ReactFragment.create({
firstHalfKey: [zero, one, two],
secondHalfKey: [three, four],
keyFive: five,
}),
null,
]}</div>
<div>
{[
ReactFragment.create({
firstHalfKey: [zero, one, two],
secondHalfKey: [three, four],
keyFive: five,
}),
null,
]}
</div>
);
var numberOfChildren = ReactChildren.count(instance.props.children);
expect(numberOfChildren).toBe(5);
@@ -418,10 +413,8 @@ describe('ReactChildren', () => {
expect(ReactChildren.toArray(<div />).length).toBe(1);
expect(ReactChildren.toArray([<div />]).length).toBe(1);
expect(
ReactChildren.toArray(<div />)[0].key
).toBe(
ReactChildren.toArray([<div />])[0].key
expect(ReactChildren.toArray(<div />)[0].key).toBe(
ReactChildren.toArray([<div />])[0].key,
);
var flattened = ReactChildren.toArray([
@@ -445,9 +438,9 @@ describe('ReactChildren', () => {
expect(flattened[5].key).toBe(reversed[3].key);
// null/undefined/bool are all omitted
expect(ReactChildren.toArray([1, 'two', null, undefined, true])).toEqual(
[1, 'two']
);
expect(ReactChildren.toArray([1, 'two', null, undefined, true])).toEqual([
1,
'two',
]);
});
});
@@ -12,7 +12,6 @@
'use strict';
describe('onlyChild', () => {
var React;
var ReactFragment;
var onlyChild;
@@ -35,63 +34,66 @@ describe('onlyChild', () => {
it('should fail when passed two children', () => {
expect(function() {
var instance =
var instance = (
<WrapComponent>
<div />
<span />
</WrapComponent>;
</WrapComponent>
);
onlyChild(instance.props.children);
}).toThrow();
});
it('should fail when passed nully values', () => {
expect(function() {
var instance =
var instance = (
<WrapComponent>
{null}
</WrapComponent>;
</WrapComponent>
);
onlyChild(instance.props.children);
}).toThrow();
expect(function() {
var instance =
var instance = (
<WrapComponent>
{undefined}
</WrapComponent>;
</WrapComponent>
);
onlyChild(instance.props.children);
}).toThrow();
});
it('should fail when key/value objects', () => {
expect(function() {
var instance =
var instance = (
<WrapComponent>
{ReactFragment.create({oneThing: <span />})}
</WrapComponent>;
</WrapComponent>
);
onlyChild(instance.props.children);
}).toThrow();
});
it('should not fail when passed interpolated single child', () => {
expect(function() {
var instance =
var instance = (
<WrapComponent>
{<span />}
</WrapComponent>;
</WrapComponent>
);
onlyChild(instance.props.children);
}).not.toThrow();
});
it('should return the only child', () => {
expect(function() {
var instance =
var instance = (
<WrapComponent>
<span />
</WrapComponent>;
</WrapComponent>
);
onlyChild(instance.props.children);
}).not.toThrow();
});
});
@@ -12,7 +12,6 @@
'use strict';
describe('sliceChildren', () => {
var React;
var sliceChildren;
@@ -24,11 +23,7 @@ describe('sliceChildren', () => {
});
it('should render the whole set if start zero is supplied', () => {
var fullSet = [
<div key="A" />,
<div key="B" />,
<div key="C" />,
];
var fullSet = [<div key="A" />, <div key="B" />, <div key="C" />];
var children = sliceChildren(fullSet, 0);
expect(children).toEqual([
<div key=".$A" />,
@@ -38,16 +33,9 @@ describe('sliceChildren', () => {
});
it('should render the remaining set if no end index is supplied', () => {
var fullSet = [
<div key="A" />,
<div key="B" />,
<div key="C" />,
];
var fullSet = [<div key="A" />, <div key="B" />, <div key="C" />];
var children = sliceChildren(fullSet, 1);
expect(children).toEqual([
<div key=".$B" />,
<div key=".$C" />,
]);
expect(children).toEqual([<div key=".$B" />, <div key=".$C" />]);
});
it('should exclude everything at or after the end index', () => {
@@ -58,9 +46,7 @@ describe('sliceChildren', () => {
<div key="D" />,
];
var children = sliceChildren(fullSet, 1, 2);
expect(children).toEqual([
<div key=".$B" />,
]);
expect(children).toEqual([<div key=".$B" />]);
});
it('should allow static children to be sliced', () => {
@@ -70,24 +56,16 @@ describe('sliceChildren', () => {
var el = <div>{a}{b}{c}</div>;
var children = sliceChildren(el.props.children, 1, 2);
expect(children).toEqual([
<b key=".1" />,
]);
expect(children).toEqual([<b key=".1" />]);
});
it('should slice nested children', () => {
var fullSet = [
<div key="A" />,
[
<div key="B" />,
<div key="C" />,
],
[<div key="B" />, <div key="C" />],
<div key="D" />,
];
var children = sliceChildren(fullSet, 1, 2);
expect(children).toEqual([
<div key=".1:$B" />,
]);
expect(children).toEqual([<div key=".1:$B" />]);
});
});
+1 -1
View File
@@ -31,7 +31,7 @@ var invariant = require('invariant');
function onlyChild(children) {
invariant(
ReactElement.isValidElement(children),
'React.Children.only expected to receive a single React element child.'
'React.Children.only expected to receive a single React element child.',
);
return children;
}
@@ -17,6 +17,7 @@
'use strict';
var PropTypes;
var React;
var ReactDOM;
var ReactTestUtils;
@@ -34,6 +35,7 @@ describe('ReactContextValidator', () => {
React = require('React');
ReactDOM = require('ReactDOM');
ReactTestUtils = require('ReactTestUtils');
PropTypes = require('prop-types');
reactComponentExpect = require('reactComponentExpect');
});
@@ -47,7 +49,7 @@ describe('ReactContextValidator', () => {
}
}
Component.contextTypes = {
foo: React.PropTypes.string,
foo: PropTypes.string,
};
class ComponentInFooBarContext extends React.Component {
@@ -63,12 +65,16 @@ describe('ReactContextValidator', () => {
}
}
ComponentInFooBarContext.childContextTypes = {
foo: React.PropTypes.string,
bar: React.PropTypes.number,
foo: PropTypes.string,
bar: PropTypes.number,
};
var instance = ReactTestUtils.renderIntoDocument(<ComponentInFooBarContext />);
reactComponentExpect(instance).expectRenderedChild().scalarContextEqual({foo: 'abc'});
var instance = ReactTestUtils.renderIntoDocument(
<ComponentInFooBarContext />,
);
reactComponentExpect(instance)
.expectRenderedChild()
.scalarContextEqual({foo: 'abc'});
});
it('should filter context properly in callbacks', () => {
@@ -90,8 +96,8 @@ describe('ReactContextValidator', () => {
}
}
Parent.childContextTypes = {
foo: React.PropTypes.string.isRequired,
bar: React.PropTypes.string.isRequired,
foo: PropTypes.string.isRequired,
bar: PropTypes.string.isRequired,
};
class Component extends React.Component {
@@ -118,10 +124,9 @@ describe('ReactContextValidator', () => {
}
}
Component.contextTypes = {
foo: React.PropTypes.string,
foo: PropTypes.string,
};
var container = document.createElement('div');
ReactDOM.render(<Parent foo="abc" />, container);
ReactDOM.render(<Parent foo="def" />, container);
@@ -140,7 +145,7 @@ describe('ReactContextValidator', () => {
}
}
Component.contextTypes = {
foo: React.PropTypes.string.isRequired,
foo: PropTypes.string.isRequired,
};
ReactTestUtils.renderIntoDocument(<Component />);
@@ -148,9 +153,9 @@ describe('ReactContextValidator', () => {
expect(console.error.calls.count()).toBe(1);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: Failed context type: ' +
'The context `foo` is marked as required in `Component`, but its value ' +
'is `undefined`.\n' +
' in Component (at **)'
'The context `foo` is marked as required in `Component`, but its value ' +
'is `undefined`.\n' +
' in Component (at **)',
);
class ComponentInFooStringContext extends React.Component {
@@ -165,11 +170,11 @@ describe('ReactContextValidator', () => {
}
}
ComponentInFooStringContext.childContextTypes = {
foo: React.PropTypes.string,
foo: PropTypes.string,
};
ReactTestUtils.renderIntoDocument(
<ComponentInFooStringContext fooValue={'bar'} />
<ComponentInFooStringContext fooValue={'bar'} />,
);
// Previous call should not error
@@ -187,18 +192,20 @@ describe('ReactContextValidator', () => {
}
}
ComponentInFooNumberContext.childContextTypes = {
foo: React.PropTypes.number,
foo: PropTypes.number,
};
ReactTestUtils.renderIntoDocument(<ComponentInFooNumberContext fooValue={123} />);
ReactTestUtils.renderIntoDocument(
<ComponentInFooNumberContext fooValue={123} />,
);
expect(console.error.calls.count()).toBe(2);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
'Warning: Failed context type: ' +
'Invalid context `foo` of type `number` supplied ' +
'to `Component`, expected `string`.\n' +
' in Component (at **)\n' +
' in ComponentInFooNumberContext (at **)'
'Invalid context `foo` of type `number` supplied ' +
'to `Component`, expected `string`.\n' +
' in Component (at **)\n' +
' in ComponentInFooNumberContext (at **)',
);
});
@@ -215,17 +222,17 @@ describe('ReactContextValidator', () => {
}
}
Component.childContextTypes = {
foo: React.PropTypes.string.isRequired,
bar: React.PropTypes.number,
foo: PropTypes.string.isRequired,
bar: PropTypes.number,
};
ReactTestUtils.renderIntoDocument(<Component testContext={{bar: 123}} />);
expect(console.error.calls.count()).toBe(1);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: Failed child context type: ' +
'The child context `foo` is marked as required in `Component`, but its ' +
'value is `undefined`.\n' +
' in Component (at **)'
'The child context `foo` is marked as required in `Component`, but its ' +
'value is `undefined`.\n' +
' in Component (at **)',
);
ReactTestUtils.renderIntoDocument(<Component testContext={{foo: 123}} />);
@@ -233,21 +240,18 @@ describe('ReactContextValidator', () => {
expect(console.error.calls.count()).toBe(2);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
'Warning: Failed child context type: ' +
'Invalid child context `foo` of type `number` ' +
'supplied to `Component`, expected `string`.\n' +
' in Component (at **)'
'Invalid child context `foo` of type `number` ' +
'supplied to `Component`, expected `string`.\n' +
' in Component (at **)',
);
ReactTestUtils.renderIntoDocument(
<Component testContext={{foo: 'foo', bar: 123}} />
<Component testContext={{foo: 'foo', bar: 123}} />,
);
ReactTestUtils.renderIntoDocument(
<Component testContext={{foo: 'foo'}} />
);
ReactTestUtils.renderIntoDocument(<Component testContext={{foo: 'foo'}} />);
// Previous calls should not log errors
expect(console.error.calls.count()).toBe(2);
});
});
-902
View File
@@ -1,902 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactClass
*/
'use strict';
var ReactComponent = require('ReactComponent');
var ReactElement = require('ReactElement');
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
var ReactNoopUpdateQueue = require('ReactNoopUpdateQueue');
var emptyObject = require('emptyObject');
var invariant = require('invariant');
var warning = require('warning');
import type { ReactPropTypeLocations } from 'ReactPropTypeLocations';
var MIXINS_KEY = 'mixins';
// Helper function to allow the creation of anonymous functions which do not
// have .name set to the name of the variable being assigned to.
function identity(fn) {
return fn;
}
/**
* Policies that describe methods in `ReactClassInterface`.
*/
type SpecPolicy =
/**
* These methods may be defined only once by the class specification or mixin.
*/
'DEFINE_ONCE' |
/**
* These methods may be defined by both the class specification and mixins.
* Subsequent definitions will be chained. These methods must return void.
*/
'DEFINE_MANY' |
/**
* These methods are overriding the base class.
*/
'OVERRIDE_BASE' |
/**
* These methods are similar to DEFINE_MANY, except we assume they return
* objects. We try to merge the keys of the return values of all the mixed in
* functions. If there is a key conflict we throw.
*/
'DEFINE_MANY_MERGED';
var injectedMixins = [];
/**
* Composite components are higher-level components that compose other composite
* or host components.
*
* To create a new type of `ReactClass`, pass a specification of
* your new class to `React.createClass`. The only requirement of your class
* specification is that you implement a `render` method.
*
* var MyComponent = React.createClass({
* render: function() {
* return <div>Hello World</div>;
* }
* });
*
* The class specification supports a specific protocol of methods that have
* special meaning (e.g. `render`). See `ReactClassInterface` for
* more the comprehensive protocol. Any other properties and methods in the
* class specification will be available on the prototype.
*
* @interface ReactClassInterface
* @internal
*/
var ReactClassInterface: {[key: string]: SpecPolicy} = {
/**
* An array of Mixin objects to include when defining your component.
*
* @type {array}
* @optional
*/
mixins: 'DEFINE_MANY',
/**
* An object containing properties and methods that should be defined on
* the component's constructor instead of its prototype (static methods).
*
* @type {object}
* @optional
*/
statics: 'DEFINE_MANY',
/**
* Definition of prop types for this component.
*
* @type {object}
* @optional
*/
propTypes: 'DEFINE_MANY',
/**
* Definition of context types for this component.
*
* @type {object}
* @optional
*/
contextTypes: 'DEFINE_MANY',
/**
* Definition of context types this component sets for its children.
*
* @type {object}
* @optional
*/
childContextTypes: 'DEFINE_MANY',
// ==== Definition methods ====
/**
* Invoked when the component is mounted. Values in the mapping will be set on
* `this.props` if that prop is not specified (i.e. using an `in` check).
*
* This method is invoked before `getInitialState` and therefore cannot rely
* on `this.state` or use `this.setState`.
*
* @return {object}
* @optional
*/
getDefaultProps: 'DEFINE_MANY_MERGED',
/**
* Invoked once before the component is mounted. The return value will be used
* as the initial value of `this.state`.
*
* getInitialState: function() {
* return {
* isOn: false,
* fooBaz: new BazFoo()
* }
* }
*
* @return {object}
* @optional
*/
getInitialState: 'DEFINE_MANY_MERGED',
/**
* @return {object}
* @optional
*/
getChildContext: 'DEFINE_MANY_MERGED',
/**
* Uses props from `this.props` and state from `this.state` to render the
* structure of the component.
*
* No guarantees are made about when or how often this method is invoked, so
* it must not have side effects.
*
* render: function() {
* var name = this.props.name;
* return <div>Hello, {name}!</div>;
* }
*
* @return {ReactComponent}
* @required
*/
render: 'DEFINE_ONCE',
// ==== Delegate methods ====
/**
* Invoked when the component is initially created and about to be mounted.
* This may have side effects, but any external subscriptions or data created
* by this method must be cleaned up in `componentWillUnmount`.
*
* @optional
*/
componentWillMount: 'DEFINE_MANY',
/**
* Invoked when the component has been mounted and has a DOM representation.
* However, there is no guarantee that the DOM node is in the document.
*
* Use this as an opportunity to operate on the DOM when the component has
* been mounted (initialized and rendered) for the first time.
*
* @param {DOMElement} rootNode DOM element representing the component.
* @optional
*/
componentDidMount: 'DEFINE_MANY',
/**
* Invoked before the component receives new props.
*
* Use this as an opportunity to react to a prop transition by updating the
* state using `this.setState`. Current props are accessed via `this.props`.
*
* componentWillReceiveProps: function(nextProps, nextContext) {
* this.setState({
* likesIncreasing: nextProps.likeCount > this.props.likeCount
* });
* }
*
* NOTE: There is no equivalent `componentWillReceiveState`. An incoming prop
* transition may cause a state change, but the opposite is not true. If you
* need it, you are probably looking for `componentWillUpdate`.
*
* @param {object} nextProps
* @optional
*/
componentWillReceiveProps: 'DEFINE_MANY',
/**
* Invoked while deciding if the component should be updated as a result of
* receiving new props, state and/or context.
*
* Use this as an opportunity to `return false` when you're certain that the
* transition to the new props/state/context will not require a component
* update.
*
* shouldComponentUpdate: function(nextProps, nextState, nextContext) {
* return !equal(nextProps, this.props) ||
* !equal(nextState, this.state) ||
* !equal(nextContext, this.context);
* }
*
* @param {object} nextProps
* @param {?object} nextState
* @param {?object} nextContext
* @return {boolean} True if the component should update.
* @optional
*/
shouldComponentUpdate: 'DEFINE_ONCE',
/**
* Invoked when the component is about to update due to a transition from
* `this.props`, `this.state` and `this.context` to `nextProps`, `nextState`
* and `nextContext`.
*
* Use this as an opportunity to perform preparation before an update occurs.
*
* NOTE: You **cannot** use `this.setState()` in this method.
*
* @param {object} nextProps
* @param {?object} nextState
* @param {?object} nextContext
* @param {ReactReconcileTransaction} transaction
* @optional
*/
componentWillUpdate: 'DEFINE_MANY',
/**
* Invoked when the component's DOM representation has been updated.
*
* Use this as an opportunity to operate on the DOM when the component has
* been updated.
*
* @param {object} prevProps
* @param {?object} prevState
* @param {?object} prevContext
* @param {DOMElement} rootNode DOM element representing the component.
* @optional
*/
componentDidUpdate: 'DEFINE_MANY',
/**
* Invoked when the component is about to be removed from its parent and have
* its DOM representation destroyed.
*
* Use this as an opportunity to deallocate any external resources.
*
* NOTE: There is no `componentDidUnmount` since your component will have been
* destroyed by that point.
*
* @optional
*/
componentWillUnmount: 'DEFINE_MANY',
// ==== Advanced methods ====
/**
* Updates the component's currently mounted DOM representation.
*
* By default, this implements React's rendering and reconciliation algorithm.
* Sophisticated clients may wish to override this.
*
* @param {ReactReconcileTransaction} transaction
* @internal
* @overridable
*/
updateComponent: 'OVERRIDE_BASE',
};
/**
* Mapping from class specification keys to special processing functions.
*
* Although these are declared like instance properties in the specification
* when defining classes using `React.createClass`, they are actually static
* and are accessible on the constructor instead of the prototype. Despite
* being static, they must be defined outside of the "statics" key under
* which all other static methods are defined.
*/
var RESERVED_SPEC_KEYS = {
displayName: function(Constructor, displayName) {
Constructor.displayName = displayName;
},
mixins: function(Constructor, mixins) {
if (mixins) {
for (var i = 0; i < mixins.length; i++) {
mixSpecIntoComponent(Constructor, mixins[i]);
}
}
},
childContextTypes: function(Constructor, childContextTypes) {
if (__DEV__) {
validateTypeDef(
Constructor,
childContextTypes,
'childContext'
);
}
Constructor.childContextTypes = Object.assign(
{},
Constructor.childContextTypes,
childContextTypes
);
},
contextTypes: function(Constructor, contextTypes) {
if (__DEV__) {
validateTypeDef(
Constructor,
contextTypes,
'context'
);
}
Constructor.contextTypes = Object.assign(
{},
Constructor.contextTypes,
contextTypes
);
},
/**
* Special case getDefaultProps which should move into statics but requires
* automatic merging.
*/
getDefaultProps: function(Constructor, getDefaultProps) {
if (Constructor.getDefaultProps) {
Constructor.getDefaultProps = createMergedResultFunction(
Constructor.getDefaultProps,
getDefaultProps
);
} else {
Constructor.getDefaultProps = getDefaultProps;
}
},
propTypes: function(Constructor, propTypes) {
if (__DEV__) {
validateTypeDef(
Constructor,
propTypes,
'prop'
);
}
Constructor.propTypes = Object.assign(
{},
Constructor.propTypes,
propTypes
);
},
statics: function(Constructor, statics) {
mixStaticSpecIntoComponent(Constructor, statics);
},
autobind: function() {}, // noop
};
function validateTypeDef(
Constructor,
typeDef,
location: ReactPropTypeLocations,
) {
for (var propName in typeDef) {
if (typeDef.hasOwnProperty(propName)) {
// use a warning instead of an invariant so components
// don't show up in prod but only in __DEV__
warning(
typeof typeDef[propName] === 'function',
'%s: %s type `%s` is invalid; it must be a function, usually from ' +
'React.PropTypes.',
Constructor.displayName || 'ReactClass',
ReactPropTypeLocationNames[location],
propName
);
}
}
}
function validateMethodOverride(isAlreadyDefined, name) {
var specPolicy = ReactClassInterface.hasOwnProperty(name) ?
ReactClassInterface[name] :
null;
// Disallow overriding of base class methods unless explicitly allowed.
if (ReactClassMixin.hasOwnProperty(name)) {
invariant(
specPolicy === 'OVERRIDE_BASE',
'ReactClassInterface: You are attempting to override ' +
'`%s` from your class specification. Ensure that your method names ' +
'do not overlap with React methods.',
name
);
}
// Disallow defining methods more than once unless explicitly allowed.
if (isAlreadyDefined) {
invariant(
specPolicy === 'DEFINE_MANY' ||
specPolicy === 'DEFINE_MANY_MERGED',
'ReactClassInterface: You are attempting to define ' +
'`%s` on your component more than once. This conflict may be due ' +
'to a mixin.',
name
);
}
}
/**
* Mixin helper which handles policy validation and reserved
* specification keys when building React classes.
*/
function mixSpecIntoComponent(Constructor, spec) {
if (!spec) {
if (__DEV__) {
var typeofSpec = typeof spec;
var isMixinValid = typeofSpec === 'object' && spec !== null;
warning(
isMixinValid,
'%s: You\'re attempting to include a mixin that is either null ' +
'or not an object. Check the mixins included by the component, ' +
'as well as any mixins they include themselves. ' +
'Expected object but got %s.',
Constructor.displayName || 'ReactClass',
spec === null ? null : typeofSpec
);
}
return;
}
invariant(
typeof spec !== 'function',
'ReactClass: You\'re attempting to ' +
'use a component class or function as a mixin. Instead, just use a ' +
'regular object.'
);
invariant(
!ReactElement.isValidElement(spec),
'ReactClass: You\'re attempting to ' +
'use a component as a mixin. Instead, just use a regular object.'
);
var proto = Constructor.prototype;
var autoBindPairs = proto.__reactAutoBindPairs;
// By handling mixins before any other properties, we ensure the same
// chaining order is applied to methods with DEFINE_MANY policy, whether
// mixins are listed before or after these methods in the spec.
if (spec.hasOwnProperty(MIXINS_KEY)) {
RESERVED_SPEC_KEYS.mixins(Constructor, spec.mixins);
}
for (var name in spec) {
if (!spec.hasOwnProperty(name)) {
continue;
}
if (name === MIXINS_KEY) {
// We have already handled mixins in a special case above.
continue;
}
var property = spec[name];
var isAlreadyDefined = proto.hasOwnProperty(name);
validateMethodOverride(isAlreadyDefined, name);
if (RESERVED_SPEC_KEYS.hasOwnProperty(name)) {
RESERVED_SPEC_KEYS[name](Constructor, property);
} else {
// Setup methods on prototype:
// The following member methods should not be automatically bound:
// 1. Expected ReactClass methods (in the "interface").
// 2. Overridden methods (that were mixed in).
var isReactClassMethod =
ReactClassInterface.hasOwnProperty(name);
var isFunction = typeof property === 'function';
var shouldAutoBind =
isFunction &&
!isReactClassMethod &&
!isAlreadyDefined &&
spec.autobind !== false;
if (shouldAutoBind) {
autoBindPairs.push(name, property);
proto[name] = property;
} else {
if (isAlreadyDefined) {
var specPolicy = ReactClassInterface[name];
// These cases should already be caught by validateMethodOverride.
invariant(
isReactClassMethod && (
specPolicy === 'DEFINE_MANY_MERGED' ||
specPolicy === 'DEFINE_MANY'
),
'ReactClass: Unexpected spec policy %s for key %s ' +
'when mixing in component specs.',
specPolicy,
name
);
// For methods which are defined more than once, call the existing
// methods before calling the new property, merging if appropriate.
if (specPolicy === 'DEFINE_MANY_MERGED') {
proto[name] = createMergedResultFunction(proto[name], property);
} else if (specPolicy === 'DEFINE_MANY') {
proto[name] = createChainedFunction(proto[name], property);
}
} else {
proto[name] = property;
if (__DEV__) {
// Add verbose displayName to the function, which helps when looking
// at profiling tools.
if (typeof property === 'function' && spec.displayName) {
proto[name].displayName = spec.displayName + '_' + name;
}
}
}
}
}
}
}
function mixStaticSpecIntoComponent(Constructor, statics) {
if (!statics) {
return;
}
for (var name in statics) {
var property = statics[name];
if (!statics.hasOwnProperty(name)) {
continue;
}
var isReserved = name in RESERVED_SPEC_KEYS;
invariant(
!isReserved,
'ReactClass: You are attempting to define a reserved ' +
'property, `%s`, that shouldn\'t be on the "statics" key. Define it ' +
'as an instance property instead; it will still be accessible on the ' +
'constructor.',
name
);
var isInherited = name in Constructor;
invariant(
!isInherited,
'ReactClass: You are attempting to define ' +
'`%s` on your component more than once. This conflict may be ' +
'due to a mixin.',
name
);
Constructor[name] = property;
}
}
/**
* Merge two objects, but throw if both contain the same key.
*
* @param {object} one The first object, which is mutated.
* @param {object} two The second object
* @return {object} one after it has been mutated to contain everything in two.
*/
function mergeIntoWithNoDuplicateKeys(one, two) {
invariant(
one && two && typeof one === 'object' && typeof two === 'object',
'mergeIntoWithNoDuplicateKeys(): Cannot merge non-objects.'
);
for (var key in two) {
if (two.hasOwnProperty(key)) {
invariant(
one[key] === undefined,
'mergeIntoWithNoDuplicateKeys(): ' +
'Tried to merge two objects with the same key: `%s`. This conflict ' +
'may be due to a mixin; in particular, this may be caused by two ' +
'getInitialState() or getDefaultProps() methods returning objects ' +
'with clashing keys.',
key
);
one[key] = two[key];
}
}
return one;
}
/**
* Creates a function that invokes two functions and merges their return values.
*
* @param {function} one Function to invoke first.
* @param {function} two Function to invoke second.
* @return {function} Function that invokes the two argument functions.
* @private
*/
function createMergedResultFunction(one, two) {
return function mergedResult() {
var a = one.apply(this, arguments);
var b = two.apply(this, arguments);
if (a == null) {
return b;
} else if (b == null) {
return a;
}
var c = {};
mergeIntoWithNoDuplicateKeys(c, a);
mergeIntoWithNoDuplicateKeys(c, b);
return c;
};
}
/**
* Creates a function that invokes two functions and ignores their return vales.
*
* @param {function} one Function to invoke first.
* @param {function} two Function to invoke second.
* @return {function} Function that invokes the two argument functions.
* @private
*/
function createChainedFunction(one, two) {
return function chainedFunction() {
one.apply(this, arguments);
two.apply(this, arguments);
};
}
/**
* Binds a method to the component.
*
* @param {object} component Component whose method is going to be bound.
* @param {function} method Method to be bound.
* @return {function} The bound method.
*/
function bindAutoBindMethod(component, method) {
var boundMethod = method.bind(component);
if (__DEV__) {
boundMethod.__reactBoundContext = component;
boundMethod.__reactBoundMethod = method;
boundMethod.__reactBoundArguments = null;
var componentName = component.constructor.displayName;
var _bind = boundMethod.bind;
boundMethod.bind = function(newThis, ...args) {
// User is trying to bind() an autobound method; we effectively will
// ignore the value of "this" that the user is trying to use, so
// let's warn.
if (newThis !== component && newThis !== null) {
warning(
false,
'bind(): React component methods may only be bound to the ' +
'component instance. See %s',
componentName
);
} else if (!args.length) {
warning(
false,
'bind(): You are binding a component method to the component. ' +
'React does this for you automatically in a high-performance ' +
'way, so you can safely remove this call. See %s',
componentName
);
return boundMethod;
}
var reboundMethod = _bind.apply(boundMethod, arguments);
reboundMethod.__reactBoundContext = component;
reboundMethod.__reactBoundMethod = method;
reboundMethod.__reactBoundArguments = args;
return reboundMethod;
};
}
return boundMethod;
}
/**
* Binds all auto-bound methods in a component.
*
* @param {object} component Component whose method is going to be bound.
*/
function bindAutoBindMethods(component) {
var pairs = component.__reactAutoBindPairs;
for (var i = 0; i < pairs.length; i += 2) {
var autoBindKey = pairs[i];
var method = pairs[i + 1];
component[autoBindKey] = bindAutoBindMethod(
component,
method
);
}
}
/**
* Add more to the ReactClass base class. These are all legacy features and
* therefore not already part of the modern ReactComponent.
*/
var ReactClassMixin = {
/**
* TODO: This will be deprecated because state should always keep a consistent
* type signature and the only use case for this, is to avoid that.
*/
replaceState: function(newState, callback) {
this.updater.enqueueReplaceState(this, newState);
if (callback) {
this.updater.enqueueCallback(this, callback, 'replaceState');
}
},
/**
* Checks whether or not this composite component is mounted.
* @return {boolean} True if mounted, false otherwise.
* @protected
* @final
*/
isMounted: function() {
return this.updater.isMounted(this);
},
};
var ReactClassComponent = function() {};
Object.assign(
ReactClassComponent.prototype,
ReactComponent.prototype,
ReactClassMixin
);
let didWarnDeprecated = false;
/**
* Module for creating composite components.
*
* @class ReactClass
*/
var ReactClass = {
/**
* Creates a composite component class given a class specification.
* See https://facebook.github.io/react/docs/top-level-api.html#react.createclass
*
* @param {object} spec Class specification (which must define `render`).
* @return {function} Component constructor function.
* @public
*/
createClass: function(spec) {
if (__DEV__) {
warning(
didWarnDeprecated,
'%s: React.createClass is deprecated and will be removed in version 16. ' +
'Use plain JavaScript classes instead. If you\'re not yet ready to ' +
'migrate, create-react-class is available on npm as a ' +
'drop-in replacement.',
(spec && spec.displayName) || 'A Component',
);
didWarnDeprecated = true;
}
// To keep our warnings more understandable, we'll use a little hack here to
// ensure that Constructor.name !== 'Constructor'. This makes sure we don't
// unnecessarily identify a class without displayName as 'Constructor'.
var Constructor = identity(function(props, context, updater) {
// This constructor gets overridden by mocks. The argument is used
// by mocks to assert on what gets mounted.
if (__DEV__) {
warning(
this instanceof Constructor,
'Something is calling a React component directly. Use a factory or ' +
'JSX instead. See: https://fb.me/react-legacyfactory'
);
}
// Wire up auto-binding
if (this.__reactAutoBindPairs.length) {
bindAutoBindMethods(this);
}
this.props = props;
this.context = context;
this.refs = emptyObject;
this.updater = updater || ReactNoopUpdateQueue;
this.state = null;
// ReactClasses doesn't have constructors. Instead, they use the
// getInitialState and componentWillMount methods for initialization.
var initialState = this.getInitialState ? this.getInitialState() : null;
if (__DEV__) {
// We allow auto-mocks to proceed as if they're returning null.
if (initialState === undefined &&
this.getInitialState._isMockFunction) {
// This is probably bad practice. Consider warning here and
// deprecating this convenience.
initialState = null;
}
}
invariant(
typeof initialState === 'object' && !Array.isArray(initialState),
'%s.getInitialState(): must return an object or null',
Constructor.displayName || 'ReactCompositeComponent'
);
this.state = initialState;
});
Constructor.prototype = new ReactClassComponent();
Constructor.prototype.constructor = Constructor;
Constructor.prototype.__reactAutoBindPairs = [];
injectedMixins.forEach(
mixSpecIntoComponent.bind(null, Constructor)
);
mixSpecIntoComponent(Constructor, spec);
// Initialize the defaultProps property after all mixins have been merged.
if (Constructor.getDefaultProps) {
Constructor.defaultProps = Constructor.getDefaultProps();
}
if (__DEV__) {
// This is a tag to indicate that the use of these method names is ok,
// since it's used with createClass. If it's not, then it's likely a
// mistake so we'll warn you to use the static property, property
// initializer or constructor respectively.
if (Constructor.getDefaultProps) {
Constructor.getDefaultProps.isReactClassApproved = {};
}
if (Constructor.prototype.getInitialState) {
Constructor.prototype.getInitialState.isReactClassApproved = {};
}
}
invariant(
Constructor.prototype.render,
'createClass(...): Class specification must implement a `render` method.'
);
if (__DEV__) {
warning(
!Constructor.prototype.componentShouldUpdate,
'%s has a method called ' +
'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' +
'The name is phrased as a question because the function is ' +
'expected to return a value.',
spec.displayName || 'A component'
);
warning(
!Constructor.prototype.componentWillRecieveProps,
'%s has a method called ' +
'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?',
spec.displayName || 'A component'
);
}
// Reduce time spent doing lookups by setting these on the prototype.
for (var methodName in ReactClassInterface) {
if (!Constructor.prototype[methodName]) {
Constructor.prototype[methodName] = null;
}
}
return Constructor;
},
injection: {
injectMixin: function(mixin) {
injectedMixins.push(mixin);
},
},
};
module.exports = ReactClass;
@@ -11,14 +11,15 @@
'use strict';
var PropTypes;
var React;
var ReactDOM;
var ReactTestUtils;
var createReactClass;
describe('ReactClass-spec', () => {
beforeEach(() => {
PropTypes = require('prop-types');
React = require('React');
ReactDOM = require('ReactDOM');
ReactTestUtils = require('ReactTestUtils');
@@ -34,7 +35,7 @@ describe('ReactClass-spec', () => {
expect(function() {
createReactClass({});
}).toThrowError(
'createClass(...): Class specification must implement a `render` method.'
'createClass(...): Class specification must implement a `render` method.',
);
});
@@ -46,8 +47,7 @@ describe('ReactClass-spec', () => {
},
});
expect(TestComponent.displayName)
.toBe('TestComponent');
expect(TestComponent.displayName).toBe('TestComponent');
});
it('should copy prop types onto the Constructor', () => {
@@ -62,8 +62,7 @@ describe('ReactClass-spec', () => {
});
expect(TestComponent.propTypes).toBeDefined();
expect(TestComponent.propTypes.value)
.toBe(propValidator);
expect(TestComponent.propTypes.value).toBe(propValidator);
});
it('should warn on invalid prop types', () => {
@@ -80,7 +79,7 @@ describe('ReactClass-spec', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: Component: prop type `prop` is invalid; ' +
'it must be a function, usually from React.PropTypes.'
'it must be a function, usually from React.PropTypes.',
);
});
@@ -98,7 +97,7 @@ describe('ReactClass-spec', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: Component: context type `prop` is invalid; ' +
'it must be a function, usually from React.PropTypes.'
'it must be a function, usually from React.PropTypes.',
);
});
@@ -116,7 +115,7 @@ describe('ReactClass-spec', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: Component: child context type `prop` is invalid; ' +
'it must be a function, usually from React.PropTypes.'
'it must be a function, usually from React.PropTypes.',
);
});
@@ -134,8 +133,8 @@ describe('ReactClass-spec', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: A component has a method called componentShouldUpdate(). Did you ' +
'mean shouldComponentUpdate()? The name is phrased as a question ' +
'because the function is expected to return a value.'
'mean shouldComponentUpdate()? The name is phrased as a question ' +
'because the function is expected to return a value.',
);
createReactClass({
@@ -150,8 +149,8 @@ describe('ReactClass-spec', () => {
expect(console.error.calls.count()).toBe(2);
expect(console.error.calls.argsFor(1)[0]).toBe(
'Warning: NamedComponent has a method called componentShouldUpdate(). Did you ' +
'mean shouldComponentUpdate()? The name is phrased as a question ' +
'because the function is expected to return a value.'
'mean shouldComponentUpdate()? The name is phrased as a question ' +
'because the function is expected to return a value.',
);
});
@@ -168,7 +167,7 @@ describe('ReactClass-spec', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: A component has a method called componentWillRecieveProps(). Did you ' +
'mean componentWillReceiveProps()?'
'mean componentWillReceiveProps()?',
);
});
@@ -189,9 +188,9 @@ describe('ReactClass-spec', () => {
});
}).toThrowError(
'ReactClass: You are attempting to define a reserved property, ' +
'`getDefaultProps`, that shouldn\'t be on the "statics" key. Define ' +
'it as an instance property instead; it will still be accessible on ' +
'the constructor.'
'`getDefaultProps`, that shouldn\'t be on the "statics" key. Define ' +
'it as an instance property instead; it will still be accessible on ' +
'the constructor.',
);
});
@@ -202,13 +201,13 @@ describe('ReactClass-spec', () => {
createReactClass({
mixins: [{}],
propTypes: {
foo: React.PropTypes.string,
foo: PropTypes.string,
},
contextTypes: {
foo: React.PropTypes.string,
foo: PropTypes.string,
},
childContextTypes: {
foo: React.PropTypes.string,
foo: PropTypes.string,
},
render: function() {
return <div />;
@@ -217,19 +216,19 @@ describe('ReactClass-spec', () => {
expect(console.error.calls.count()).toBe(4);
expect(console.error.calls.argsFor(0)[0]).toBe(
'createClass(...): `mixins` is now a static property and should ' +
'be defined inside "statics".'
'be defined inside "statics".',
);
expect(console.error.calls.argsFor(1)[0]).toBe(
'createClass(...): `propTypes` is now a static property and should ' +
'be defined inside "statics".'
'be defined inside "statics".',
);
expect(console.error.calls.argsFor(2)[0]).toBe(
'createClass(...): `contextTypes` is now a static property and ' +
'should be defined inside "statics".'
'should be defined inside "statics".',
);
expect(console.error.calls.argsFor(3)[0]).toBe(
'createClass(...): `childContextTypes` is now a static property and ' +
'should be defined inside "statics".'
'should be defined inside "statics".',
);
});
@@ -282,7 +281,7 @@ describe('ReactClass-spec', () => {
it('renders based on context getInitialState', () => {
var Foo = createReactClass({
contextTypes: {
className: React.PropTypes.string,
className: PropTypes.string,
},
getInitialState() {
return {className: this.context.className};
@@ -294,7 +293,7 @@ describe('ReactClass-spec', () => {
var Outer = createReactClass({
childContextTypes: {
className: React.PropTypes.string,
className: PropTypes.string,
},
getChildContext() {
return {className: 'foo'};
@@ -323,7 +322,7 @@ describe('ReactClass-spec', () => {
expect(function() {
instance = ReactTestUtils.renderIntoDocument(instance);
}).toThrowError(
'Component.getInitialState(): must return an object or null'
'Component.getInitialState(): must return an object or null',
);
});
});
@@ -337,8 +336,8 @@ describe('ReactClass-spec', () => {
return <span />;
},
});
expect(
() => ReactTestUtils.renderIntoDocument(<Component />)
expect(() =>
ReactTestUtils.renderIntoDocument(<Component />),
).not.toThrow();
});
@@ -354,7 +353,7 @@ describe('ReactClass-spec', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: Something is calling a React component directly. Use a ' +
'factory or JSX instead. See: https://fb.me/react-legacyfactory'
'factory or JSX instead. See: https://fb.me/react-legacyfactory',
);
});
@@ -362,23 +361,19 @@ describe('ReactClass-spec', () => {
var ops = [];
var Component = createReactClass({
getInitialState() {
return { step: 0 };
return {step: 0};
},
render() {
ops.push('Render: ' + this.state.step);
return <div />;
}
},
});
var instance = ReactTestUtils.renderIntoDocument(<Component />);
instance.replaceState({ step: 1 }, () => {
instance.replaceState({step: 1}, () => {
ops.push('Callback: ' + instance.state.step);
});
expect(ops).toEqual([
'Render: 0',
'Render: 1',
'Callback: 1',
]);
expect(ops).toEqual(['Render: 0', 'Render: 1', 'Callback: 1']);
});
it('isMounted works', () => {
@@ -414,7 +409,7 @@ describe('ReactClass-spec', () => {
instance = this;
this.log('render');
return <div />;
}
},
});
var container = document.createElement('div');
@@ -437,8 +432,8 @@ describe('ReactClass-spec', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toEqual(
'Warning: MyComponent: isMounted is deprecated. Instead, make sure to ' +
'clean up subscriptions and pending requests in componentWillUnmount ' +
'to prevent memory leaks.'
'clean up subscriptions and pending requests in componentWillUnmount ' +
'to prevent memory leaks.',
);
});
});
@@ -0,0 +1,426 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
'use strict';
var React;
var ReactDOM;
var ReactTestUtils;
var createReactClass;
describe('create-react-class-integration', () => {
beforeEach(() => {
React = require('React');
ReactDOM = require('ReactDOM');
ReactTestUtils = require('ReactTestUtils');
var createReactClassFactory = require('create-react-class/factory');
createReactClass = createReactClassFactory(
React.Component,
React.isValidElement,
require('ReactNoopUpdateQueue'),
);
});
it('should throw when `render` is not specified', () => {
expect(function() {
createReactClass({});
}).toThrowError(
'createClass(...): Class specification must implement a `render` method.',
);
});
it('should copy prop types onto the Constructor', () => {
var propValidator = jest.fn();
var TestComponent = createReactClass({
propTypes: {
value: propValidator,
},
render: function() {
return <div />;
},
});
expect(TestComponent.propTypes).toBeDefined();
expect(TestComponent.propTypes.value).toBe(propValidator);
});
it('should warn on invalid prop types', () => {
spyOn(console, 'error');
createReactClass({
displayName: 'Component',
propTypes: {
prop: null,
},
render: function() {
return <span>{this.props.prop}</span>;
},
});
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: Component: prop type `prop` is invalid; ' +
'it must be a function, usually from React.PropTypes.',
);
});
it('should warn on invalid context types', () => {
spyOn(console, 'error');
createReactClass({
displayName: 'Component',
contextTypes: {
prop: null,
},
render: function() {
return <span>{this.props.prop}</span>;
},
});
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: Component: context type `prop` is invalid; ' +
'it must be a function, usually from React.PropTypes.',
);
});
it('should throw on invalid child context types', () => {
spyOn(console, 'error');
createReactClass({
displayName: 'Component',
childContextTypes: {
prop: null,
},
render: function() {
return <span>{this.props.prop}</span>;
},
});
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: Component: child context type `prop` is invalid; ' +
'it must be a function, usually from React.PropTypes.',
);
});
it('should warn when mispelling shouldComponentUpdate', () => {
spyOn(console, 'error');
createReactClass({
componentShouldUpdate: function() {
return false;
},
render: function() {
return <div />;
},
});
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: A component has a method called componentShouldUpdate(). Did you ' +
'mean shouldComponentUpdate()? The name is phrased as a question ' +
'because the function is expected to return a value.',
);
createReactClass({
displayName: 'NamedComponent',
componentShouldUpdate: function() {
return false;
},
render: function() {
return <div />;
},
});
expect(console.error.calls.count()).toBe(2);
expect(console.error.calls.argsFor(1)[0]).toBe(
'Warning: NamedComponent has a method called componentShouldUpdate(). Did you ' +
'mean shouldComponentUpdate()? The name is phrased as a question ' +
'because the function is expected to return a value.',
);
});
it('should warn when mispelling componentWillReceiveProps', () => {
spyOn(console, 'error');
createReactClass({
componentWillRecieveProps: function() {
return false;
},
render: function() {
return <div />;
},
});
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: A component has a method called componentWillRecieveProps(). Did you ' +
'mean componentWillReceiveProps()?',
);
});
it('should throw if a reserved property is in statics', () => {
expect(function() {
createReactClass({
statics: {
getDefaultProps: function() {
return {
foo: 0,
};
},
},
render: function() {
return <span />;
},
});
}).toThrowError(
'ReactClass: You are attempting to define a reserved property, ' +
'`getDefaultProps`, that shouldn\'t be on the "statics" key. Define ' +
'it as an instance property instead; it will still be accessible on ' +
'the constructor.',
);
});
// TODO: Consider actually moving these to statics or drop this unit test.
xit('should warn when using deprecated non-static spec keys', () => {
spyOn(console, 'error');
createReactClass({
mixins: [{}],
propTypes: {
foo: React.PropTypes.string,
},
contextTypes: {
foo: React.PropTypes.string,
},
childContextTypes: {
foo: React.PropTypes.string,
},
render: function() {
return <div />;
},
});
expect(console.error.calls.count()).toBe(4);
expect(console.error.calls.argsFor(0)[0]).toBe(
'createClass(...): `mixins` is now a static property and should ' +
'be defined inside "statics".',
);
expect(console.error.calls.argsFor(1)[0]).toBe(
'createClass(...): `propTypes` is now a static property and should ' +
'be defined inside "statics".',
);
expect(console.error.calls.argsFor(2)[0]).toBe(
'createClass(...): `contextTypes` is now a static property and ' +
'should be defined inside "statics".',
);
expect(console.error.calls.argsFor(3)[0]).toBe(
'createClass(...): `childContextTypes` is now a static property and ' +
'should be defined inside "statics".',
);
});
it('should support statics', () => {
var Component = createReactClass({
statics: {
abc: 'def',
def: 0,
ghi: null,
jkl: 'mno',
pqr: function() {
return this;
},
},
render: function() {
return <span />;
},
});
var instance = <Component />;
instance = ReactTestUtils.renderIntoDocument(instance);
expect(instance.constructor.abc).toBe('def');
expect(Component.abc).toBe('def');
expect(instance.constructor.def).toBe(0);
expect(Component.def).toBe(0);
expect(instance.constructor.ghi).toBe(null);
expect(Component.ghi).toBe(null);
expect(instance.constructor.jkl).toBe('mno');
expect(Component.jkl).toBe('mno');
expect(instance.constructor.pqr()).toBe(Component);
expect(Component.pqr()).toBe(Component);
});
it('should work with object getInitialState() return values', () => {
var Component = createReactClass({
getInitialState: function() {
return {
occupation: 'clown',
};
},
render: function() {
return <span />;
},
});
var instance = <Component />;
instance = ReactTestUtils.renderIntoDocument(instance);
expect(instance.state.occupation).toEqual('clown');
});
it('renders based on context getInitialState', () => {
var Foo = createReactClass({
contextTypes: {
className: React.PropTypes.string,
},
getInitialState() {
return {className: this.context.className};
},
render() {
return <span className={this.state.className} />;
},
});
var Outer = createReactClass({
childContextTypes: {
className: React.PropTypes.string,
},
getChildContext() {
return {className: 'foo'};
},
render() {
return <Foo />;
},
});
var container = document.createElement('div');
ReactDOM.render(<Outer />, container);
expect(container.firstChild.className).toBe('foo');
});
it('should throw with non-object getInitialState() return values', () => {
[['an array'], 'a string', 1234].forEach(function(state) {
var Component = createReactClass({
getInitialState: function() {
return state;
},
render: function() {
return <span />;
},
});
var instance = <Component />;
expect(function() {
instance = ReactTestUtils.renderIntoDocument(instance);
}).toThrowError(
'Component.getInitialState(): must return an object or null',
);
});
});
it('should work with a null getInitialState() return value', () => {
var Component = createReactClass({
getInitialState: function() {
return null;
},
render: function() {
return <span />;
},
});
expect(() =>
ReactTestUtils.renderIntoDocument(<Component />),
).not.toThrow();
});
it('should throw when using legacy factories', () => {
spyOn(console, 'error');
var Component = createReactClass({
render() {
return <div />;
},
});
expect(() => Component()).toThrow();
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: Something is calling a React component directly. Use a ' +
'factory or JSX instead. See: https://fb.me/react-legacyfactory',
);
});
it('replaceState and callback works', () => {
var ops = [];
var Component = createReactClass({
getInitialState() {
return {step: 0};
},
render() {
ops.push('Render: ' + this.state.step);
return <div />;
},
});
var instance = ReactTestUtils.renderIntoDocument(<Component />);
instance.replaceState({step: 1}, () => {
ops.push('Callback: ' + instance.state.step);
});
expect(ops).toEqual(['Render: 0', 'Render: 1', 'Callback: 1']);
});
it('isMounted works', () => {
spyOn(console, 'error');
var ops = [];
var instance;
var Component = createReactClass({
displayName: 'MyComponent',
log(name) {
ops.push(`${name}: ${this.isMounted()}`);
},
getInitialState() {
this.log('getInitialState');
return {};
},
componentWillMount() {
this.log('componentWillMount');
},
componentDidMount() {
this.log('componentDidMount');
},
componentWillUpdate() {
this.log('componentWillUpdate');
},
componentDidUpdate() {
this.log('componentDidUpdate');
},
componentWillUnmount() {
this.log('componentWillUnmount');
},
render() {
instance = this;
this.log('render');
return <div />;
},
});
var container = document.createElement('div');
ReactDOM.render(<Component />, container);
ReactDOM.render(<Component />, container);
ReactDOM.unmountComponentAtNode(container);
instance.log('after unmount');
expect(ops).toEqual([
'getInitialState: false',
'componentWillMount: false',
'render: false',
'componentDidMount: true',
'componentWillUpdate: true',
'render: true',
'componentDidUpdate: true',
'componentWillUnmount: false',
'after unmount: false',
]);
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toEqual(
'Warning: MyComponent: isMounted is deprecated. Instead, make sure to ' +
'clean up subscriptions and pending requests in componentWillUnmount ' +
'to prevent memory leaks.',
);
});
});
@@ -0,0 +1,19 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule createClass
*/
'use strict';
var {Component} = require('ReactBaseClasses');
var {isValidElement} = require('ReactElement');
var ReactNoopUpdateQueue = require('ReactNoopUpdateQueue');
var factory = require('create-react-class/factory');
module.exports = factory(Component, isValidElement, ReactNoopUpdateQueue);
@@ -12,7 +12,7 @@
'use strict';
import type { ReactInstance } from 'ReactInstanceType';
import type {ReactInstance} from 'ReactInstanceType';
/**
* Keeps track of the current owner.
@@ -21,13 +21,11 @@ import type { ReactInstance } from 'ReactInstanceType';
* currently being constructed.
*/
var ReactCurrentOwner = {
/**
* @internal
* @type {ReactComponent}
*/
current: (null: null | ReactInstance),
};
module.exports = ReactCurrentOwner;
+26 -28
View File
@@ -59,10 +59,10 @@ function defineKeyPropWarningGetter(props, displayName) {
warning(
false,
'%s: `key` is not a prop. Trying to access it will result ' +
'in `undefined` being returned. If you need to access the same ' +
'value within the child component, you should pass it as a different ' +
'prop. (https://fb.me/react-special-props)',
displayName
'in `undefined` being returned. If you need to access the same ' +
'value within the child component, you should pass it as a different ' +
'prop. (https://fb.me/react-special-props)',
displayName,
);
}
};
@@ -80,10 +80,10 @@ function defineRefPropWarningGetter(props, displayName) {
warning(
false,
'%s: `ref` is not a prop. Trying to access it will result ' +
'in `undefined` being returned. If you need to access the same ' +
'value within the child component, you should pass it as a different ' +
'prop. (https://fb.me/react-special-props)',
displayName
'in `undefined` being returned. If you need to access the same ' +
'value within the child component, you should pass it as a different ' +
'prop. (https://fb.me/react-special-props)',
displayName,
);
}
};
@@ -203,8 +203,10 @@ ReactElement.createElement = function(type, config, children) {
source = config.__source === undefined ? null : config.__source;
// Remaining properties are added to a new props object
for (propName in config) {
if (hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
@@ -239,11 +241,13 @@ ReactElement.createElement = function(type, config, children) {
}
if (__DEV__) {
if (key || ref) {
if (typeof props.$$typeof === 'undefined' ||
props.$$typeof !== REACT_ELEMENT_TYPE) {
var displayName = typeof type === 'function' ?
(type.displayName || type.name || 'Unknown') :
type;
if (
typeof props.$$typeof === 'undefined' ||
props.$$typeof !== REACT_ELEMENT_TYPE
) {
var displayName = typeof type === 'function'
? type.displayName || type.name || 'Unknown'
: type;
if (key) {
defineKeyPropWarningGetter(props, displayName);
}
@@ -260,7 +264,7 @@ ReactElement.createElement = function(type, config, children) {
self,
source,
ReactCurrentOwner.current,
props
props,
);
};
@@ -287,7 +291,7 @@ ReactElement.cloneAndReplaceKey = function(oldElement, newKey) {
oldElement._self,
oldElement._source,
oldElement._owner,
oldElement.props
oldElement.props,
);
return newElement;
@@ -332,8 +336,10 @@ ReactElement.cloneElement = function(element, config, children) {
defaultProps = element.type.defaultProps;
}
for (propName in config) {
if (hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
if (config[propName] === undefined && defaultProps !== undefined) {
// Resolve default props
props[propName] = defaultProps[propName];
@@ -357,15 +363,7 @@ ReactElement.cloneElement = function(element, config, children) {
props.children = childArray;
}
return ReactElement(
element.type,
key,
ref,
self,
source,
owner,
props
);
return ReactElement(element.type, key, ref, self, source, owner, props);
};
/**
@@ -12,7 +12,7 @@
'use strict';
import type { ReactInstance } from 'ReactInstanceType';
import type {ReactInstance} from 'ReactInstanceType';
export type Source = {
fileName: string,
@@ -27,6 +27,7 @@ var checkReactTypeSpec = require('checkReactTypeSpec');
var canDefineProperty = require('canDefineProperty');
var getIteratorFn = require('getIteratorFn');
var warning = require('warning');
var lowPriorityWarning = require('lowPriorityWarning');
function getDeclarationErrorAddendum() {
if (ReactCurrentOwner.current) {
@@ -63,8 +64,9 @@ function getCurrentComponentErrorInfo(parentType) {
var info = getDeclarationErrorAddendum();
if (!info) {
var parentName = typeof parentType === 'string' ?
parentType : parentType.displayName || parentType.name;
var parentName = typeof parentType === 'string'
? parentType
: parentType.displayName || parentType.name;
if (parentName) {
info = ` Check the top-level render call using <${parentName}>.`;
}
@@ -89,9 +91,8 @@ function validateExplicitKey(element, parentType) {
}
element._store.validated = true;
var memoizer = ownerHasKeyUseWarning.uniqueKey || (
ownerHasKeyUseWarning.uniqueKey = {}
);
var memoizer =
ownerHasKeyUseWarning.uniqueKey || (ownerHasKeyUseWarning.uniqueKey = {});
var currentComponentErrorInfo = getCurrentComponentErrorInfo(parentType);
if (memoizer[currentComponentErrorInfo]) {
@@ -103,21 +104,22 @@ function validateExplicitKey(element, parentType) {
// property, it may be the creator of the child that's responsible for
// assigning it a key.
var childOwner = '';
if (element &&
element._owner &&
element._owner !== ReactCurrentOwner.current) {
if (
element &&
element._owner &&
element._owner !== ReactCurrentOwner.current
) {
// Give the component that originally created this child.
childOwner =
` It was passed a child from ${element._owner.getName()}.`;
childOwner = ` It was passed a child from ${element._owner.getName()}.`;
}
warning(
false,
'Each child in an array or iterator should have a unique "key" prop.' +
'%s%s See https://fb.me/react-warning-keys for more information.%s',
'%s%s See https://fb.me/react-warning-keys for more information.%s',
currentComponentErrorInfo,
childOwner,
ReactComponentTreeHook.getCurrentStackAddendum(element)
ReactComponentTreeHook.getCurrentStackAddendum(element),
);
}
@@ -182,39 +184,35 @@ function validatePropTypes(element) {
'prop',
name,
element,
null
null,
);
}
if (typeof componentClass.getDefaultProps === 'function') {
warning(
componentClass.getDefaultProps.isReactClassApproved,
'getDefaultProps is only used on classic React.createClass ' +
'definitions. Use a static property named `defaultProps` instead.'
'definitions. Use a static property named `defaultProps` instead.',
);
}
}
var ReactElementValidator = {
createElement: function(type, props, children) {
var validType = typeof type === 'string' || typeof type === 'function';
// We warn in this case but don't throw. We expect the element creation to
// succeed and there will likely be errors in render.
if (!validType) {
if (
typeof type !== 'function' &&
typeof type !== 'string'
) {
if (typeof type !== 'function' && typeof type !== 'string') {
var info = '';
if (
type === undefined ||
typeof type === 'object' &&
type !== null &&
Object.keys(type).length === 0
(typeof type === 'object' &&
type !== null &&
Object.keys(type).length === 0)
) {
info +=
' You likely forgot to export your component from the file ' +
'it\'s defined in.';
"it's defined in.";
}
var sourceInfo = getSourceInfoErrorAddendum(props);
@@ -226,14 +224,21 @@ var ReactElementValidator = {
info += ReactComponentTreeHook.getCurrentStackAddendum();
var currentSource = props !== null &&
props !== undefined &&
props.__source !== undefined
? props.__source
: null;
ReactComponentTreeHook.pushNonStandardWarningStack(true, currentSource);
warning(
false,
'React.createElement: type is invalid -- expected a string (for ' +
'built-in components) or a class/function (for composite ' +
'components) but got: %s.%s',
'built-in components) or a class/function (for composite ' +
'components) but got: %s.%s',
type == null ? type : typeof type,
info,
);
ReactComponentTreeHook.popNonStandardWarningStack();
}
}
@@ -262,37 +267,29 @@ var ReactElementValidator = {
},
createFactory: function(type) {
var validatedFactory = ReactElementValidator.createElement.bind(
null,
type
);
var validatedFactory = ReactElementValidator.createElement.bind(null, type);
// Legacy hook TODO: Warn if this is accessed
validatedFactory.type = type;
if (__DEV__) {
if (canDefineProperty) {
Object.defineProperty(
validatedFactory,
'type',
{
enumerable: false,
get: function() {
warning(
false,
'Factory.type is deprecated. Access the class directly ' +
'before passing it to createFactory.'
);
Object.defineProperty(this, 'type', {
value: type,
});
return type;
},
}
);
Object.defineProperty(validatedFactory, 'type', {
enumerable: false,
get: function() {
lowPriorityWarning(
false,
'Factory.type is deprecated. Access the class directly ' +
'before passing it to createFactory.',
);
Object.defineProperty(this, 'type', {
value: type,
});
return type;
},
});
}
}
return validatedFactory;
},
@@ -304,7 +301,6 @@ var ReactElementValidator = {
validatePropTypes(newElement);
return newElement;
},
};
module.exports = ReactElementValidator;
@@ -81,9 +81,9 @@ describe('ReactElement', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'Child: `key` is not a prop. Trying to access it will result ' +
'in `undefined` being returned. If you need to access the same ' +
'value within the child component, you should pass it as a different ' +
'prop. (https://fb.me/react-special-props)'
'in `undefined` being returned. If you need to access the same ' +
'value within the child component, you should pass it as a different ' +
'prop. (https://fb.me/react-special-props)',
);
});
@@ -95,9 +95,9 @@ describe('ReactElement', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'div: `key` is not a prop. Trying to access it will result ' +
'in `undefined` being returned. If you need to access the same ' +
'value within the child component, you should pass it as a different ' +
'prop. (https://fb.me/react-special-props)'
'in `undefined` being returned. If you need to access the same ' +
'value within the child component, you should pass it as a different ' +
'prop. (https://fb.me/react-special-props)',
);
});
@@ -123,9 +123,9 @@ describe('ReactElement', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'Child: `ref` is not a prop. Trying to access it will result ' +
'in `undefined` being returned. If you need to access the same ' +
'value within the child component, you should pass it as a different ' +
'prop. (https://fb.me/react-special-props)'
'in `undefined` being returned. If you need to access the same ' +
'value within the child component, you should pass it as a different ' +
'prop. (https://fb.me/react-special-props)',
);
});
@@ -141,7 +141,7 @@ describe('ReactElement', () => {
it('returns an immutable element', () => {
var element = React.createFactory(ComponentClass)();
expect(() => element.type = 'div').toThrow();
expect(() => (element.type = 'div')).toThrow();
});
it('does not reuse the original config object', () => {
@@ -233,7 +233,7 @@ describe('ReactElement', () => {
}
var instance = ReactTestUtils.renderIntoDocument(
React.createElement(Wrapper)
React.createElement(Wrapper),
);
expect(element._owner.getPublicInstance()).toBe(instance);
@@ -242,9 +242,12 @@ describe('ReactElement', () => {
it('merges an additional argument onto the children prop', () => {
spyOn(console, 'error');
var a = 1;
var element = React.createFactory(ComponentClass)({
children: 'text',
}, a);
var element = React.createFactory(ComponentClass)(
{
children: 'text',
},
a,
);
expect(element.props.children).toBe(a);
expect(console.error.calls.count()).toBe(0);
});
@@ -260,9 +263,12 @@ describe('ReactElement', () => {
it('overrides children if null is provided as an argument', () => {
spyOn(console, 'error');
var element = React.createFactory(ComponentClass)({
children: 'text',
}, null);
var element = React.createFactory(ComponentClass)(
{
children: 'text',
},
null,
);
expect(element.props.children).toBe(null);
expect(console.error.calls.count()).toBe(0);
});
@@ -303,18 +309,16 @@ describe('ReactElement', () => {
}
}
expect(React.isValidElement(React.createElement('div')))
.toEqual(true);
expect(React.isValidElement(React.createElement(Component)))
.toEqual(true);
expect(React.isValidElement(React.createElement('div'))).toEqual(true);
expect(React.isValidElement(React.createElement(Component))).toEqual(true);
expect(React.isValidElement(null)).toEqual(false);
expect(React.isValidElement(true)).toEqual(false);
expect(React.isValidElement({})).toEqual(false);
expect(React.isValidElement('string')).toEqual(false);
expect(React.isValidElement(React.DOM.div)).toEqual(false);
expect(React.isValidElement(React.createFactory('div'))).toEqual(false);
expect(React.isValidElement(Component)).toEqual(false);
expect(React.isValidElement({ type: 'div', props: {} })).toEqual(false);
expect(React.isValidElement({type: 'div', props: {}})).toEqual(false);
var jsonElement = JSON.stringify(React.createElement('div'));
expect(React.isValidElement(JSON.parse(jsonElement))).toBe(true);
@@ -341,7 +345,7 @@ describe('ReactElement', () => {
var container = document.createElement('div');
var instance = ReactDOM.render(
React.createElement(Component, {fruit: 'mango'}),
container
container,
);
expect(instance.props.fruit).toBe('mango');
@@ -360,12 +364,12 @@ describe('ReactElement', () => {
Component.defaultProps = {prop: 'testKey'};
var instance = ReactTestUtils.renderIntoDocument(
React.createElement(Component)
React.createElement(Component),
);
expect(instance.props.prop).toBe('testKey');
var inst2 = ReactTestUtils.renderIntoDocument(
React.createElement(Component, {prop: null})
React.createElement(Component, {prop: null}),
);
expect(inst2.props.prop).toBe(null);
});
@@ -448,23 +452,20 @@ describe('ReactElement', () => {
}
}
expect(React.isValidElement(React.createElement('div')))
.toEqual(true);
expect(React.isValidElement(React.createElement(Component)))
.toEqual(true);
expect(React.isValidElement(React.createElement('div'))).toEqual(true);
expect(React.isValidElement(React.createElement(Component))).toEqual(true);
expect(React.isValidElement(null)).toEqual(false);
expect(React.isValidElement(true)).toEqual(false);
expect(React.isValidElement({})).toEqual(false);
expect(React.isValidElement('string')).toEqual(false);
expect(React.isValidElement(React.DOM.div)).toEqual(false);
expect(React.isValidElement(React.createFactory('div'))).toEqual(false);
expect(React.isValidElement(Component)).toEqual(false);
expect(React.isValidElement({ type: 'div', props: {} })).toEqual(false);
expect(React.isValidElement({type: 'div', props: {}})).toEqual(false);
var jsonElement = JSON.stringify(React.createElement('div'));
expect(React.isValidElement(JSON.parse(jsonElement))).toBe(false);
});
});
describe('comparing jsx vs .createFactory() vs .createElement()', () => {
@@ -478,7 +479,6 @@ describe('comparing jsx vs .createFactory() vs .createElement()', () => {
Child = jest.genMockFromModule('ReactElementTestChild');
});
describe('when using jsx only', () => {
var Parent, instance;
beforeEach(() => {
@@ -491,11 +491,14 @@ describe('comparing jsx vs .createFactory() vs .createElement()', () => {
);
}
};
instance = ReactTestUtils.renderIntoDocument(<Parent/>);
instance = ReactTestUtils.renderIntoDocument(<Parent />);
});
it('should scry children but cannot', () => {
var children = ReactTestUtils.scryRenderedComponentsWithType(instance, Child);
var children = ReactTestUtils.scryRenderedComponentsWithType(
instance,
Child,
);
expect(children.length).toBe(1);
});
@@ -504,7 +507,10 @@ describe('comparing jsx vs .createFactory() vs .createElement()', () => {
});
it('can capture Child instantiation calls', () => {
expect(Child.mock.calls[0][0]).toEqual({ foo: 'foo value', children: 'children value' });
expect(Child.mock.calls[0][0]).toEqual({
foo: 'foo value',
children: 'children value',
});
});
});
@@ -514,7 +520,11 @@ describe('comparing jsx vs .createFactory() vs .createElement()', () => {
var childFactory = React.createFactory(Child);
class Parent extends React.Component {
render() {
return React.DOM.div({}, childFactory({ ref: 'child', foo: 'foo value' }, 'children value'));
return React.createElement(
'div',
{},
childFactory({ref: 'child', foo: 'foo value'}, 'children value'),
);
}
}
factory = React.createFactory(Parent);
@@ -522,7 +532,10 @@ describe('comparing jsx vs .createFactory() vs .createElement()', () => {
});
it('can properly scry children', () => {
var children = ReactTestUtils.scryRenderedComponentsWithType(instance, Child);
var children = ReactTestUtils.scryRenderedComponentsWithType(
instance,
Child,
);
expect(children.length).toBe(1);
});
@@ -531,7 +544,10 @@ describe('comparing jsx vs .createFactory() vs .createElement()', () => {
});
it('can capture Child instantiation calls', () => {
expect(Child.mock.calls[0][0]).toEqual({ foo: 'foo value', children: 'children value' });
expect(Child.mock.calls[0][0]).toEqual({
foo: 'foo value',
children: 'children value',
});
});
});
@@ -540,7 +556,15 @@ describe('comparing jsx vs .createFactory() vs .createElement()', () => {
beforeEach(() => {
class Parent extends React.Component {
render() {
return React.DOM.div({}, React.createElement(Child, { ref: 'child', foo: 'foo value' }, 'children value'));
return React.createElement(
'div',
{},
React.createElement(
Child,
{ref: 'child', foo: 'foo value'},
'children value',
),
);
}
}
factory = React.createFactory(Parent);
@@ -548,7 +572,10 @@ describe('comparing jsx vs .createFactory() vs .createElement()', () => {
});
it('should scry children but cannot', () => {
var children = ReactTestUtils.scryRenderedComponentsWithType(instance, Child);
var children = ReactTestUtils.scryRenderedComponentsWithType(
instance,
Child,
);
expect(children.length).toBe(1);
});
@@ -557,8 +584,10 @@ describe('comparing jsx vs .createFactory() vs .createElement()', () => {
});
it('can capture Child instantiation calls', () => {
expect(Child.mock.calls[0][0]).toEqual({ foo: 'foo value', children: 'children value' });
expect(Child.mock.calls[0][0]).toEqual({
foo: 'foo value',
children: 'children value',
});
});
});
});
@@ -11,6 +11,7 @@
'use strict';
var PropTypes;
var React;
var ReactDOM;
var ReactTestUtils;
@@ -22,6 +23,7 @@ describe('ReactElementClone', () => {
React = require('React');
ReactDOM = require('ReactDOM');
ReactTestUtils = require('ReactTestUtils');
PropTypes = require('prop-types');
// NOTE: We're explicitly not using JSX here. This is intended to test
// classic JS without JSX.
@@ -42,7 +44,7 @@ describe('ReactElementClone', () => {
render() {
return (
<div className="parent">
{React.cloneElement(this.props.child, { className: 'xyz' })}
{React.cloneElement(this.props.child, {className: 'xyz'})}
</div>
);
}
@@ -66,7 +68,7 @@ describe('ReactElementClone', () => {
render() {
return (
<div className="parent">
{React.cloneElement(this.props.child, { className: 'xyz' })}
{React.cloneElement(this.props.child, {className: 'xyz'})}
</div>
);
}
@@ -91,7 +93,7 @@ describe('ReactElementClone', () => {
render() {
return (
<div>
{React.cloneElement(this.props.child, { className: 'xyz' })}
{React.cloneElement(this.props.child, {className: 'xyz'})}
</div>
);
}
@@ -120,7 +122,7 @@ describe('ReactElementClone', () => {
}
ReactTestUtils.renderIntoDocument(
React.cloneElement(<Component />, {children: 'xyz'})
React.cloneElement(<Component />, {children: 'xyz'}),
);
});
@@ -133,7 +135,7 @@ describe('ReactElementClone', () => {
}
ReactTestUtils.renderIntoDocument(
React.cloneElement(<Component>xyz</Component>, {})
React.cloneElement(<Component>xyz</Component>, {}),
);
});
@@ -146,34 +148,41 @@ describe('ReactElementClone', () => {
var clone = React.cloneElement(
<Component>xyz</Component>,
{ children: <Component /> },
<div />,
<span />
);
expect(clone.props.children).toEqual([
{children: <Component />},
<div />,
<span />,
]);
);
expect(clone.props.children).toEqual([<div />, <span />]);
});
it('should override children if undefined is provided as an argument', () => {
var element = React.createElement(ComponentClass, {
children: 'text',
}, undefined);
var element = React.createElement(
ComponentClass,
{
children: 'text',
},
undefined,
);
expect(element.props.children).toBe(undefined);
var element2 = React.cloneElement(React.createElement(ComponentClass, {
children: 'text',
}), {}, undefined);
var element2 = React.cloneElement(
React.createElement(ComponentClass, {
children: 'text',
}),
{},
undefined,
);
expect(element2.props.children).toBe(undefined);
});
it('should support keys and refs', () => {
class Parent extends React.Component {
render() {
var clone =
React.cloneElement(this.props.children, {key: 'xyz', ref: 'xyz'});
var clone = React.cloneElement(this.props.children, {
key: 'xyz',
ref: 'xyz',
});
expect(clone.key).toBe('xyz');
expect(clone.ref).toBe('xyz');
return <div>{clone}</div>;
@@ -218,7 +227,7 @@ describe('ReactElementClone', () => {
}
ReactTestUtils.renderIntoDocument(
React.cloneElement(<Component myprop="abc" />, {myprop: 'xyz'})
React.cloneElement(<Component myprop="abc" />, {myprop: 'xyz'}),
);
});
@@ -250,7 +259,7 @@ describe('ReactElementClone', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'Each child in an array or iterator should have a unique "key" prop.'
'Each child in an array or iterator should have a unique "key" prop.',
);
});
@@ -286,7 +295,7 @@ describe('ReactElementClone', () => {
}
}
Component.propTypes = {
color: React.PropTypes.string.isRequired,
color: PropTypes.string.isRequired,
};
class Parent extends React.Component {
render() {
@@ -295,21 +304,20 @@ describe('ReactElementClone', () => {
}
class GrandParent extends React.Component {
render() {
return React.createElement(
Parent,
{ child: React.createElement(Component, {color: 'red'}) }
);
return React.createElement(Parent, {
child: React.createElement(Component, {color: 'red'}),
});
}
}
ReactTestUtils.renderIntoDocument(React.createElement(GrandParent));
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: Failed prop type: ' +
'Invalid prop `color` of type `number` supplied to `Component`, ' +
'expected `string`.\n' +
' in Component (created by GrandParent)\n' +
' in Parent (created by GrandParent)\n' +
' in GrandParent'
'Invalid prop `color` of type `number` supplied to `Component`, ' +
'expected `string`.\n' +
' in Component (created by GrandParent)\n' +
' in Parent (created by GrandParent)\n' +
' in GrandParent',
);
});
@@ -359,5 +367,4 @@ describe('ReactElementClone', () => {
expect(Object.isFrozen(element.props)).toBe(true);
expect(clone.props).toEqual({foo: 'ef'});
});
});
@@ -14,6 +14,7 @@
// NOTE: We're explicitly not using JSX in this file. This is intended to test
// classic JS without JSX.
var PropTypes;
var React;
var ReactDOM;
var ReactTestUtils;
@@ -31,6 +32,7 @@ describe('ReactElementValidator', () => {
React = require('React');
ReactDOM = require('ReactDOM');
ReactTestUtils = require('ReactTestUtils');
PropTypes = require('prop-types');
ComponentClass = class extends React.Component {
render() {
return React.createElement('div');
@@ -46,7 +48,7 @@ describe('ReactElementValidator', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'Each child in an array or iterator should have a unique "key" prop.'
'Each child in an array or iterator should have a unique "key" prop.',
);
});
@@ -64,19 +66,17 @@ describe('ReactElementValidator', () => {
class ComponentWrapper extends React.Component {
render() {
return InnerComponent({childSet: [Component(), Component()] });
return InnerComponent({childSet: [Component(), Component()]});
}
}
ReactTestUtils.renderIntoDocument(
React.createElement(ComponentWrapper)
);
ReactTestUtils.renderIntoDocument(React.createElement(ComponentWrapper));
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'Each child in an array or iterator should have a unique "key" prop. ' +
'Check the render method of `InnerClass`. ' +
'It was passed a child from ComponentWrapper. '
'Check the render method of `InnerClass`. ' +
'It was passed a child from ComponentWrapper. ',
);
});
@@ -86,37 +86,31 @@ describe('ReactElementValidator', () => {
function Anonymous() {
return <div />;
}
Object.defineProperty(Anonymous, 'name', { value: undefined });
Object.defineProperty(Anonymous, 'name', {value: undefined});
var divs = [
<div />,
<div />,
];
var divs = [<div />, <div />];
ReactTestUtils.renderIntoDocument(<Anonymous>{divs}</Anonymous>);
expect(console.error.calls.count()).toBe(1);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: Each child in an array or iterator should have a unique ' +
'"key" prop. See https://fb.me/react-warning-keys for more information.\n' +
' in div (at **)'
'"key" prop. See https://fb.me/react-warning-keys for more information.\n' +
' in div (at **)',
);
});
it('warns for keys for arrays of elements with no owner info', () => {
spyOn(console, 'error');
var divs = [
<div />,
<div />,
];
var divs = [<div />, <div />];
ReactTestUtils.renderIntoDocument(<div>{divs}</div>);
expect(console.error.calls.count()).toBe(1);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: Each child in an array or iterator should have a unique ' +
'"key" prop. Check the top-level render call using <div>. See ' +
'https://fb.me/react-warning-keys for more information.\n' +
' in div (at **)'
'"key" prop. Check the top-level render call using <div>. See ' +
'https://fb.me/react-warning-keys for more information.\n' +
' in div (at **)',
);
});
@@ -140,12 +134,12 @@ describe('ReactElementValidator', () => {
expect(console.error.calls.count()).toBe(1);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: Each child in an array or iterator should have a unique ' +
'"key" prop. Check the render method of `Component`. See ' +
'https://fb.me/react-warning-keys for more information.\n' +
' in div (at **)\n' +
' in Component (at **)\n' +
' in Parent (at **)\n' +
' in GrandParent (at **)'
'"key" prop. Check the render method of `Component`. See ' +
'https://fb.me/react-warning-keys for more information.\n' +
' in div (at **)\n' +
' in Component (at **)\n' +
' in Parent (at **)\n' +
' in GrandParent (at **)',
);
});
@@ -165,7 +159,7 @@ describe('ReactElementValidator', () => {
<Wrapper>
<span />
<span />
</Wrapper>
</Wrapper>,
);
expect(console.error.calls.count()).toBe(0);
@@ -191,7 +185,7 @@ describe('ReactElementValidator', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'Each child in an array or iterator should have a unique "key" prop.'
'Each child in an array or iterator should have a unique "key" prop.',
);
});
@@ -258,7 +252,7 @@ describe('ReactElementValidator', () => {
return React.createElement('div', null, 'My color is ' + props.color);
}
MyComp.propTypes = {
color: React.PropTypes.string,
color: PropTypes.string,
};
function ParentComp() {
return React.createElement(MyComp, {color: 123});
@@ -266,10 +260,10 @@ describe('ReactElementValidator', () => {
ReactTestUtils.renderIntoDocument(React.createElement(ParentComp));
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: Failed prop type: ' +
'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
'expected `string`.\n' +
' in MyComp (created by ParentComp)\n' +
' in ParentComp'
'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
'expected `string`.\n' +
' in MyComp (created by ParentComp)\n' +
' in ParentComp',
);
});
@@ -284,35 +278,35 @@ describe('ReactElementValidator', () => {
expect(console.error.calls.count()).toBe(6);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: undefined. You likely forgot to export your ' +
'component from the file it\'s defined in.'
'(for built-in components) or a class/function (for composite ' +
'components) but got: undefined. You likely forgot to export your ' +
"component from the file it's defined in.",
);
expect(console.error.calls.argsFor(1)[0]).toBe(
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: null.'
'(for built-in components) or a class/function (for composite ' +
'components) but got: null.',
);
expect(console.error.calls.argsFor(2)[0]).toBe(
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: boolean.'
'(for built-in components) or a class/function (for composite ' +
'components) but got: boolean.',
);
expect(console.error.calls.argsFor(3)[0]).toBe(
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: number.'
'(for built-in components) or a class/function (for composite ' +
'components) but got: number.',
);
expect(console.error.calls.argsFor(4)[0]).toBe(
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: object.'
'(for built-in components) or a class/function (for composite ' +
'components) but got: object.',
);
expect(console.error.calls.argsFor(5)[0]).toBe(
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: object. You likely forgot to export your ' +
'component from the file it\'s defined in.'
'(for built-in components) or a class/function (for composite ' +
'components) but got: object. You likely forgot to export your ' +
"component from the file it's defined in.",
);
React.createElement('div');
expect(console.error.calls.count()).toBe(6);
@@ -327,15 +321,15 @@ describe('ReactElementValidator', () => {
ReactTestUtils.renderIntoDocument(React.createElement(ParentComp));
}).toThrowError(
'Element type is invalid: expected a string (for built-in components) ' +
'or a class/function (for composite components) but got: null. Check ' +
'the render method of `ParentComp`.'
'or a class/function (for composite components) but got: null. Check ' +
'the render method of `ParentComp`.',
);
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: null. Check the render method of `ParentComp`.' +
'\n in ParentComp'
'(for built-in components) or a class/function (for composite ' +
'components) but got: null. Check the render method of `ParentComp`.' +
'\n in ParentComp',
);
});
@@ -347,7 +341,7 @@ describe('ReactElementValidator', () => {
return React.createElement('span', null, this.props.prop);
}
}
Component.propTypes = {prop: React.PropTypes.string.isRequired};
Component.propTypes = {prop: PropTypes.string.isRequired};
Component.defaultProps = {prop: null};
ReactTestUtils.renderIntoDocument(React.createElement(Component));
@@ -355,8 +349,8 @@ describe('ReactElementValidator', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: Failed prop type: The prop `prop` is marked as required in ' +
'`Component`, but its value is `null`.\n' +
' in Component'
'`Component`, but its value is `null`.\n' +
' in Component',
);
});
@@ -368,18 +362,18 @@ describe('ReactElementValidator', () => {
return React.createElement('span', null, this.props.prop);
}
}
Component.propTypes = {prop: React.PropTypes.string.isRequired};
Component.propTypes = {prop: PropTypes.string.isRequired};
Component.defaultProps = {prop: 'text'};
ReactTestUtils.renderIntoDocument(
React.createElement(Component, {prop:null})
React.createElement(Component, {prop: null}),
);
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: Failed prop type: The prop `prop` is marked as required in ' +
'`Component`, but its value is `null`.\n' +
' in Component'
'`Component`, but its value is `null`.\n' +
' in Component',
);
});
@@ -392,33 +386,31 @@ describe('ReactElementValidator', () => {
}
}
Component.propTypes = {
prop: React.PropTypes.string.isRequired,
prop: PropTypes.string.isRequired,
};
ReactTestUtils.renderIntoDocument(React.createElement(Component));
ReactTestUtils.renderIntoDocument(
React.createElement(Component)
);
ReactTestUtils.renderIntoDocument(
React.createElement(Component, {prop: 42})
React.createElement(Component, {prop: 42}),
);
expect(console.error.calls.count()).toBe(2);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: Failed prop type: ' +
'The prop `prop` is marked as required in `Component`, but its value ' +
'is `undefined`.\n' +
' in Component'
'The prop `prop` is marked as required in `Component`, but its value ' +
'is `undefined`.\n' +
' in Component',
);
expect(console.error.calls.argsFor(1)[0]).toBe(
'Warning: Failed prop type: ' +
'Invalid prop `prop` of type `number` supplied to ' +
'`Component`, expected `string`.\n' +
' in Component'
'Invalid prop `prop` of type `number` supplied to ' +
'`Component`, expected `string`.\n' +
' in Component',
);
ReactTestUtils.renderIntoDocument(
React.createElement(Component, {prop: 'string'})
React.createElement(Component, {prop: 'string'}),
);
// Should not error for strings
@@ -431,42 +423,41 @@ describe('ReactElementValidator', () => {
class Component extends React.Component {
render() {
return React.createElement('span', null, this.props.myProp.value);
}
}
Component.propTypes = {
myProp: React.PropTypes.shape,
myProp: PropTypes.shape,
};
ReactTestUtils.renderIntoDocument(
React.createElement(Component, {myProp: {value: 'hi'}})
React.createElement(Component, {myProp: {value: 'hi'}}),
);
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: Component: type specification of prop `myProp` is invalid; ' +
'the type checker function must return `null` or an `Error` but ' +
'returned a function. You may have forgotten to pass an argument to ' +
'the type checker creator (arrayOf, instanceOf, objectOf, oneOf, ' +
'oneOfType, and shape all require an argument).'
'the type checker function must return `null` or an `Error` but ' +
'returned a function. You may have forgotten to pass an argument to ' +
'the type checker creator (arrayOf, instanceOf, objectOf, oneOf, ' +
'oneOfType, and shape all require an argument).',
);
});
it('should warn when accessing .type on an element factory', () => {
spyOn(console, 'error');
spyOn(console, 'warn');
function TestComponent() {
return <div />;
}
var TestFactory = React.createFactory(TestComponent);
expect(TestFactory.type).toBe(TestComponent);
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
expect(console.warn.calls.count()).toBe(1);
expect(console.warn.calls.argsFor(0)[0]).toBe(
'Warning: Factory.type is deprecated. Access the class directly before ' +
'passing it to createFactory.'
'passing it to createFactory.',
);
// Warn once, not again
expect(TestFactory.type).toBe(TestComponent);
expect(console.error.calls.count()).toBe(1);
expect(console.warn.calls.count()).toBe(1);
});
it('does not warn when using DOM node as children', () => {
@@ -511,7 +502,7 @@ describe('ReactElementValidator', () => {
// shouldn't blow up either.
var child = {
$$typeof: (<div />).$$typeof,
$$typeof: <div />.$$typeof,
type: 'span',
key: null,
ref: null,
@@ -529,10 +520,60 @@ describe('ReactElementValidator', () => {
expect(console.error.calls.count()).toBe(1);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: undefined. You likely forgot to export your ' +
'component from the file it\'s defined in. Check your code at **.'
'(for built-in components) or a class/function (for composite ' +
'components) but got: undefined. You likely forgot to export your ' +
"component from the file it's defined in. Check your code at **.",
);
});
it('provides stack via non-standard console.reactStack for invalid types', () => {
spyOn(console, 'error');
function Foo() {
var Bad = undefined;
return React.createElement(Bad);
}
function App() {
return React.createElement('div', null, React.createElement(Foo));
}
try {
console.reactStack = jest.fn();
console.reactStackEnd = jest.fn();
expect(() => {
ReactTestUtils.renderIntoDocument(React.createElement(App));
}).toThrow(
'Element type is invalid: expected a string (for built-in components) ' +
'or a class/function (for composite components) but got: undefined. ' +
"You likely forgot to export your component from the file it's " +
'defined in. Check the render method of `Foo`.',
);
expect(console.reactStack.mock.calls.length).toBe(1);
expect(console.reactStackEnd.mock.calls.length).toBe(1);
var stack = console.reactStack.mock.calls[0][0];
expect(Array.isArray(stack)).toBe(true);
expect(stack.map(frame => frame.name)).toEqual([
'Foo', // <Bad> is inside Foo
'App', // <Foo> is inside App
'App', // <div> is inside App
null, // <App> is outside a component
]);
expect(
stack.map(frame => frame.fileName && frame.fileName.slice(-8)),
).toEqual([null, null, null, null]);
expect(stack.map(frame => frame.lineNumber)).toEqual([
null,
null,
null,
null,
]);
} finally {
delete console.reactStack;
delete console.reactStackEnd;
}
});
});
@@ -28,16 +28,18 @@ function typeCheckFail(declaration, value, message) {
'testComponent',
'prop',
null,
ReactPropTypesSecret
ReactPropTypesSecret,
);
expect(error instanceof Error).toBe(true);
expect(error.message).toBe(message);
}
function typeCheckFailRequiredValues(declaration) {
var specifiedButIsNullMsg = 'The prop `testProp` is marked as required in ' +
var specifiedButIsNullMsg =
'The prop `testProp` is marked as required in ' +
'`testComponent`, but its value is `null`.';
var unspecifiedMsg = 'The prop `testProp` is marked as required in ' +
var unspecifiedMsg =
'The prop `testProp` is marked as required in ' +
'`testComponent`, but its value is \`undefined\`.';
var props1 = {testProp: null};
var error1 = declaration(
@@ -46,7 +48,7 @@ function typeCheckFailRequiredValues(declaration) {
'testComponent',
'prop',
null,
ReactPropTypesSecret
ReactPropTypesSecret,
);
expect(error1 instanceof Error).toBe(true);
expect(error1.message).toBe(specifiedButIsNullMsg);
@@ -57,7 +59,7 @@ function typeCheckFailRequiredValues(declaration) {
'testComponent',
'prop',
null,
ReactPropTypesSecret
ReactPropTypesSecret,
);
expect(error2 instanceof Error).toBe(true);
expect(error2.message).toBe(unspecifiedMsg);
@@ -68,7 +70,7 @@ function typeCheckFailRequiredValues(declaration) {
'testComponent',
'prop',
null,
ReactPropTypesSecret
ReactPropTypesSecret,
);
expect(error3 instanceof Error).toBe(true);
expect(error3.message).toBe(unspecifiedMsg);
@@ -82,7 +84,7 @@ function typeCheckPass(declaration, value) {
'testComponent',
'prop',
null,
ReactPropTypesSecret
ReactPropTypesSecret,
);
expect(error).toBe(null);
}
@@ -92,16 +94,11 @@ function expectWarningInDevelopment(declaration, value) {
var propName = 'testProp' + Math.random().toString();
var componentName = 'testComponent' + Math.random().toString();
for (var i = 0; i < 3; i++) {
declaration(
props,
propName,
componentName,
'prop'
);
declaration(props, propName, componentName, 'prop');
}
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'You are manually calling a React.PropTypes validation '
'You are manually calling a React.PropTypes validation ',
);
console.error.calls.reset();
}
@@ -121,31 +118,31 @@ describe('ReactPropTypes', () => {
PropTypes.string,
[],
'Invalid prop `testProp` of type `array` supplied to ' +
'`testComponent`, expected `string`.'
'`testComponent`, expected `string`.',
);
typeCheckFail(
PropTypes.string,
false,
'Invalid prop `testProp` of type `boolean` supplied to ' +
'`testComponent`, expected `string`.'
'`testComponent`, expected `string`.',
);
typeCheckFail(
PropTypes.string,
0,
'Invalid prop `testProp` of type `number` supplied to ' +
'`testComponent`, expected `string`.'
'`testComponent`, expected `string`.',
);
typeCheckFail(
PropTypes.string,
{},
'Invalid prop `testProp` of type `object` supplied to ' +
'`testComponent`, expected `string`.'
'`testComponent`, expected `string`.',
);
typeCheckFail(
PropTypes.string,
Symbol(),
'Invalid prop `testProp` of type `symbol` supplied to ' +
'`testComponent`, expected `string`.'
'`testComponent`, expected `string`.',
);
});
@@ -154,13 +151,13 @@ describe('ReactPropTypes', () => {
PropTypes.string,
new Date(),
'Invalid prop `testProp` of type `date` supplied to ' +
'`testComponent`, expected `string`.'
'`testComponent`, expected `string`.',
);
typeCheckFail(
PropTypes.string,
/please/,
'Invalid prop `testProp` of type `regexp` supplied to ' +
'`testComponent`, expected `string`.'
'`testComponent`, expected `string`.',
);
});
@@ -260,9 +257,9 @@ describe('ReactPropTypes', () => {
describe('ArrayOf Type', () => {
it('should fail for invalid argument', () => {
typeCheckFail(
PropTypes.arrayOf({ foo: PropTypes.string }),
{ foo: 'bar' },
'Property `testProp` of component `testComponent` has invalid PropType notation inside arrayOf.'
PropTypes.arrayOf({foo: PropTypes.string}),
{foo: 'bar'},
'Property `testProp` of component `testComponent` has invalid PropType notation inside arrayOf.',
);
});
@@ -276,14 +273,14 @@ describe('ReactPropTypes', () => {
it('should support arrayOf with complex types', () => {
typeCheckPass(
PropTypes.arrayOf(PropTypes.shape({a: PropTypes.number.isRequired})),
[{a: 1}, {a: 2}]
[{a: 1}, {a: 2}],
);
function Thing() {}
typeCheckPass(
PropTypes.arrayOf(PropTypes.instanceOf(Thing)),
[new Thing(), new Thing()]
);
typeCheckPass(PropTypes.arrayOf(PropTypes.instanceOf(Thing)), [
new Thing(),
new Thing(),
]);
});
it('should warn with invalid items in the array', () => {
@@ -291,7 +288,7 @@ describe('ReactPropTypes', () => {
PropTypes.arrayOf(PropTypes.number),
[1, 2, 'b'],
'Invalid prop `testProp[2]` of type `string` supplied to ' +
'`testComponent`, expected `number`.'
'`testComponent`, expected `number`.',
);
});
@@ -303,7 +300,9 @@ describe('ReactPropTypes', () => {
PropTypes.arrayOf(PropTypes.instanceOf(Thing)),
[new Thing(), 'xyz'],
'Invalid prop `testProp[1]` of type `String` supplied to ' +
'`testComponent`, expected instance of `' + name + '`.'
'`testComponent`, expected instance of `' +
name +
'`.',
);
});
@@ -312,19 +311,19 @@ describe('ReactPropTypes', () => {
PropTypes.arrayOf(PropTypes.number),
{'0': 'maybe-array', length: 1},
'Invalid prop `testProp` of type `object` supplied to ' +
'`testComponent`, expected an array.'
'`testComponent`, expected an array.',
);
typeCheckFail(
PropTypes.arrayOf(PropTypes.number),
123,
'Invalid prop `testProp` of type `number` supplied to ' +
'`testComponent`, expected an array.'
'`testComponent`, expected an array.',
);
typeCheckFail(
PropTypes.arrayOf(PropTypes.number),
'string',
'Invalid prop `testProp` of type `string` supplied to ' +
'`testComponent`, expected an array.'
'`testComponent`, expected an array.',
);
});
@@ -339,26 +338,32 @@ describe('ReactPropTypes', () => {
it('should warn for missing required values', () => {
typeCheckFailRequiredValues(
PropTypes.arrayOf(PropTypes.number).isRequired
PropTypes.arrayOf(PropTypes.number).isRequired,
);
});
it('should warn if called manually in development', () => {
spyOn(console, 'error');
expectWarningInDevelopment(PropTypes.arrayOf({foo: PropTypes.string}), {
foo: 'bar',
});
expectWarningInDevelopment(PropTypes.arrayOf(PropTypes.number), [
1,
2,
'b',
]);
expectWarningInDevelopment(PropTypes.arrayOf(PropTypes.number), {
'0': 'maybe-array',
length: 1,
});
expectWarningInDevelopment(
PropTypes.arrayOf({ foo: PropTypes.string }),
{ foo: 'bar' }
PropTypes.arrayOf(PropTypes.number).isRequired,
null,
);
expectWarningInDevelopment(
PropTypes.arrayOf(PropTypes.number),
[1, 2, 'b']
PropTypes.arrayOf(PropTypes.number).isRequired,
undefined,
);
expectWarningInDevelopment(
PropTypes.arrayOf(PropTypes.number),
{'0': 'maybe-array', length: 1}
);
expectWarningInDevelopment(PropTypes.arrayOf(PropTypes.number).isRequired, null);
expectWarningInDevelopment(PropTypes.arrayOf(PropTypes.number).isRequired, undefined);
});
});
@@ -384,25 +389,25 @@ describe('ReactPropTypes', () => {
PropTypes.element,
[<div />, <div />],
'Invalid prop `testProp` of type `array` supplied to `testComponent`, ' +
'expected a single ReactElement.'
'expected a single ReactElement.',
);
typeCheckFail(
PropTypes.element,
123,
'Invalid prop `testProp` of type `number` supplied to `testComponent`, ' +
'expected a single ReactElement.'
'expected a single ReactElement.',
);
typeCheckFail(
PropTypes.element,
'foo',
'Invalid prop `testProp` of type `string` supplied to `testComponent`, ' +
'expected a single ReactElement.'
'expected a single ReactElement.',
);
typeCheckFail(
PropTypes.element,
false,
'Invalid prop `testProp` of type `boolean` supplied to `testComponent`, ' +
'expected a single ReactElement.'
'expected a single ReactElement.',
);
});
@@ -443,7 +448,6 @@ describe('ReactPropTypes', () => {
expectWarningInDevelopment(PropTypes.element.isRequired, null);
expectWarningInDevelopment(PropTypes.element.isRequired, undefined);
});
});
describe('Instance Types', () => {
@@ -458,43 +462,57 @@ describe('ReactPropTypes', () => {
PropTypes.instanceOf(Person),
false,
'Invalid prop `testProp` of type `Boolean` supplied to ' +
'`testComponent`, expected instance of `' + personName + '`.'
'`testComponent`, expected instance of `' +
personName +
'`.',
);
typeCheckFail(
PropTypes.instanceOf(Person),
{},
'Invalid prop `testProp` of type `Object` supplied to ' +
'`testComponent`, expected instance of `' + personName + '`.'
'`testComponent`, expected instance of `' +
personName +
'`.',
);
typeCheckFail(
PropTypes.instanceOf(Person),
'',
'Invalid prop `testProp` of type `String` supplied to ' +
'`testComponent`, expected instance of `' + personName + '`.'
'`testComponent`, expected instance of `' +
personName +
'`.',
);
typeCheckFail(
PropTypes.instanceOf(Date),
{},
'Invalid prop `testProp` of type `Object` supplied to ' +
'`testComponent`, expected instance of `' + dateName + '`.'
'`testComponent`, expected instance of `' +
dateName +
'`.',
);
typeCheckFail(
PropTypes.instanceOf(RegExp),
{},
'Invalid prop `testProp` of type `Object` supplied to ' +
'`testComponent`, expected instance of `' + regExpName + '`.'
'`testComponent`, expected instance of `' +
regExpName +
'`.',
);
typeCheckFail(
PropTypes.instanceOf(Person),
new Cat(),
'Invalid prop `testProp` of type `Cat` supplied to ' +
'`testComponent`, expected instance of `' + personName + '`.'
'`testComponent`, expected instance of `' +
personName +
'`.',
);
typeCheckFail(
PropTypes.instanceOf(Person),
Object.create(null),
'Invalid prop `testProp` of type `<<anonymous>>` supplied to ' +
'`testComponent`, expected instance of `' + personName + '`.'
'`testComponent`, expected instance of `' +
personName +
'`.',
);
});
@@ -524,9 +542,11 @@ describe('ReactPropTypes', () => {
expectWarningInDevelopment(PropTypes.instanceOf(Date), {});
expectWarningInDevelopment(PropTypes.instanceOf(Date), new Date());
expectWarningInDevelopment(PropTypes.instanceOf(Date).isRequired, {});
expectWarningInDevelopment(PropTypes.instanceOf(Date).isRequired, new Date());
expectWarningInDevelopment(
PropTypes.instanceOf(Date).isRequired,
new Date(),
);
});
});
describe('React Component Types', () => {
@@ -539,7 +559,8 @@ describe('ReactPropTypes', () => {
});
it('should warn for invalid values', () => {
var failMessage = 'Invalid prop `testProp` supplied to ' +
var failMessage =
'Invalid prop `testProp` supplied to ' +
'`testComponent`, expected a ReactNode.';
typeCheckFail(PropTypes.node, true, failMessage);
typeCheckFail(PropTypes.node, function() {}, failMessage);
@@ -565,18 +586,21 @@ describe('ReactPropTypes', () => {
// Object of renderable things
var frag = ReactFragment.create;
typeCheckPass(PropTypes.node, frag({
k0: 123,
k1: 'Some string',
k2: <div />,
k3: frag({
k30: <MyComponent />,
k31: frag({k310: <a />}),
k32: 'Another string',
typeCheckPass(
PropTypes.node,
frag({
k0: 123,
k1: 'Some string',
k2: <div />,
k3: frag({
k30: <MyComponent />,
k31: frag({k310: <a />}),
k32: 'Another string',
}),
k4: null,
k5: undefined,
}),
k4: null,
k5: undefined,
}));
);
expect(console.error.calls.count()).toBe(0);
});
@@ -603,7 +627,10 @@ describe('ReactPropTypes', () => {
return {
next: function() {
var done = ++i > 2;
return {value: done ? undefined : ['#' + i, <MyComponent />], done: done};
return {
value: done ? undefined : ['#' + i, <MyComponent />],
done: done,
};
},
};
},
@@ -634,45 +661,46 @@ describe('ReactPropTypes', () => {
expectWarningInDevelopment(PropTypes.node.isRequired, undefined);
expectWarningInDevelopment(PropTypes.node.isRequired, undefined);
});
});
describe('ObjectOf Type', () => {
it('should fail for invalid argument', () => {
typeCheckFail(
PropTypes.objectOf({ foo: PropTypes.string }),
{ foo: 'bar' },
'Property `testProp` of component `testComponent` has invalid PropType notation inside objectOf.'
PropTypes.objectOf({foo: PropTypes.string}),
{foo: 'bar'},
'Property `testProp` of component `testComponent` has invalid PropType notation inside objectOf.',
);
});
it('should support the objectOf propTypes', () => {
typeCheckPass(PropTypes.objectOf(PropTypes.number), {a: 1, b: 2, c: 3});
typeCheckPass(
PropTypes.objectOf(PropTypes.string),
{a: 'a', b: 'b', c: 'c'}
);
typeCheckPass(
PropTypes.objectOf(PropTypes.oneOf(['a', 'b'])),
{a: 'a', b: 'b'}
);
typeCheckPass(
PropTypes.objectOf(PropTypes.symbol),
{a: Symbol(), b: Symbol(), c: Symbol()}
);
typeCheckPass(PropTypes.objectOf(PropTypes.string), {
a: 'a',
b: 'b',
c: 'c',
});
typeCheckPass(PropTypes.objectOf(PropTypes.oneOf(['a', 'b'])), {
a: 'a',
b: 'b',
});
typeCheckPass(PropTypes.objectOf(PropTypes.symbol), {
a: Symbol(),
b: Symbol(),
c: Symbol(),
});
});
it('should support objectOf with complex types', () => {
typeCheckPass(
PropTypes.objectOf(PropTypes.shape({a: PropTypes.number.isRequired})),
{a: {a: 1}, b: {a: 2}}
{a: {a: 1}, b: {a: 2}},
);
function Thing() {}
typeCheckPass(
PropTypes.objectOf(PropTypes.instanceOf(Thing)),
{a: new Thing(), b: new Thing()}
);
typeCheckPass(PropTypes.objectOf(PropTypes.instanceOf(Thing)), {
a: new Thing(),
b: new Thing(),
});
});
it('should warn with invalid items in the object', () => {
@@ -680,7 +708,7 @@ describe('ReactPropTypes', () => {
PropTypes.objectOf(PropTypes.number),
{a: 1, b: 2, c: 'b'},
'Invalid prop `testProp.c` of type `string` supplied to `testComponent`, ' +
'expected `number`.'
'expected `number`.',
);
});
@@ -692,7 +720,9 @@ describe('ReactPropTypes', () => {
PropTypes.objectOf(PropTypes.instanceOf(Thing)),
{a: new Thing(), b: 'xyz'},
'Invalid prop `testProp.b` of type `String` supplied to ' +
'`testComponent`, expected instance of `' + name + '`.'
'`testComponent`, expected instance of `' +
name +
'`.',
);
});
@@ -701,25 +731,25 @@ describe('ReactPropTypes', () => {
PropTypes.objectOf(PropTypes.number),
[1, 2],
'Invalid prop `testProp` of type `array` supplied to ' +
'`testComponent`, expected an object.'
'`testComponent`, expected an object.',
);
typeCheckFail(
PropTypes.objectOf(PropTypes.number),
123,
'Invalid prop `testProp` of type `number` supplied to ' +
'`testComponent`, expected an object.'
'`testComponent`, expected an object.',
);
typeCheckFail(
PropTypes.objectOf(PropTypes.number),
'string',
'Invalid prop `testProp` of type `string` supplied to ' +
'`testComponent`, expected an object.'
'`testComponent`, expected an object.',
);
typeCheckFail(
PropTypes.objectOf(PropTypes.symbol),
Symbol(),
'Invalid prop `testProp` of type `symbol` supplied to ' +
'`testComponent`, expected an object.'
'`testComponent`, expected an object.',
);
});
@@ -734,23 +764,26 @@ describe('ReactPropTypes', () => {
it('should warn for missing required values', () => {
typeCheckFailRequiredValues(
PropTypes.objectOf(PropTypes.number).isRequired
PropTypes.objectOf(PropTypes.number).isRequired,
);
});
it('should warn if called manually in development', () => {
spyOn(console, 'error');
expectWarningInDevelopment(
PropTypes.objectOf({ foo: PropTypes.string }),
{ foo: 'bar' }
);
expectWarningInDevelopment(
PropTypes.objectOf(PropTypes.number),
{a: 1, b: 2, c: 'b'}
);
expectWarningInDevelopment(PropTypes.objectOf({foo: PropTypes.string}), {
foo: 'bar',
});
expectWarningInDevelopment(PropTypes.objectOf(PropTypes.number), {
a: 1,
b: 2,
c: 'b',
});
expectWarningInDevelopment(PropTypes.objectOf(PropTypes.number), [1, 2]);
expectWarningInDevelopment(PropTypes.objectOf(PropTypes.number), null);
expectWarningInDevelopment(PropTypes.objectOf(PropTypes.number), undefined);
expectWarningInDevelopment(
PropTypes.objectOf(PropTypes.number),
undefined,
);
});
});
@@ -761,8 +794,9 @@ describe('ReactPropTypes', () => {
PropTypes.oneOf('red', 'blue');
expect(console.error).toHaveBeenCalled();
expect(console.error.calls.argsFor(0)[0])
.toContain('Invalid argument supplied to oneOf, expected an instance of array.');
expect(console.error.calls.argsFor(0)[0]).toContain(
'Invalid argument supplied to oneOf, expected an instance of array.',
);
typeCheckPass(PropTypes.oneOf('red', 'blue'), 'red');
});
@@ -772,25 +806,25 @@ describe('ReactPropTypes', () => {
PropTypes.oneOf(['red', 'blue']),
true,
'Invalid prop `testProp` of value `true` supplied to ' +
'`testComponent`, expected one of ["red","blue"].'
'`testComponent`, expected one of ["red","blue"].',
);
typeCheckFail(
PropTypes.oneOf(['red', 'blue']),
[],
'Invalid prop `testProp` of value `` supplied to `testComponent`, ' +
'expected one of ["red","blue"].'
'expected one of ["red","blue"].',
);
typeCheckFail(
PropTypes.oneOf(['red', 'blue']),
'',
'Invalid prop `testProp` of value `` supplied to `testComponent`, ' +
'expected one of ["red","blue"].'
'expected one of ["red","blue"].',
);
typeCheckFail(
PropTypes.oneOf([0, 'false']),
false,
'Invalid prop `testProp` of value `false` supplied to ' +
'`testComponent`, expected one of [0,"false"].'
'`testComponent`, expected one of [0,"false"].',
);
});
@@ -824,8 +858,9 @@ describe('ReactPropTypes', () => {
PropTypes.oneOfType(PropTypes.string, PropTypes.number);
expect(console.error).toHaveBeenCalled();
expect(console.error.calls.argsFor(0)[0])
.toContain('Invalid argument supplied to oneOfType, expected an instance of array.');
expect(console.error.calls.argsFor(0)[0]).toContain(
'Invalid argument supplied to oneOfType, expected an instance of array.',
);
typeCheckPass(PropTypes.oneOf(PropTypes.string, PropTypes.number), []);
});
@@ -834,7 +869,7 @@ describe('ReactPropTypes', () => {
typeCheckFail(
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
[],
'Invalid prop `testProp` supplied to `testComponent`.'
'Invalid prop `testProp` supplied to `testComponent`.',
);
var checker = PropTypes.oneOfType([
@@ -844,15 +879,12 @@ describe('ReactPropTypes', () => {
typeCheckFail(
checker,
{c: 1},
'Invalid prop `testProp` supplied to `testComponent`.'
'Invalid prop `testProp` supplied to `testComponent`.',
);
});
it('should not warn if one of the types are valid', () => {
var checker = PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]);
var checker = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
typeCheckPass(checker, null);
typeCheckPass(checker, 'foo');
typeCheckPass(checker, 123);
@@ -867,16 +899,18 @@ describe('ReactPropTypes', () => {
it('should be implicitly optional and not warn without values', () => {
typeCheckPass(
PropTypes.oneOfType([PropTypes.string, PropTypes.number]), null
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
null,
);
typeCheckPass(
PropTypes.oneOfType([PropTypes.string, PropTypes.number]), undefined
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
undefined,
);
});
it('should warn for missing required values', () => {
typeCheckFailRequiredValues(
PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired
PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
);
});
@@ -884,18 +918,17 @@ describe('ReactPropTypes', () => {
spyOn(console, 'error');
expectWarningInDevelopment(
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
[]
[],
);
expectWarningInDevelopment(
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
null
null,
);
expectWarningInDevelopment(
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
undefined
undefined,
);
});
});
describe('Shape Types', () => {
@@ -904,13 +937,13 @@ describe('ReactPropTypes', () => {
PropTypes.shape({}),
'some string',
'Invalid prop `testProp` of type `string` supplied to ' +
'`testComponent`, expected `object`.'
'`testComponent`, expected `object`.',
);
typeCheckFail(
PropTypes.shape({}),
['array'],
'Invalid prop `testProp` of type `array` supplied to ' +
'`testComponent`, expected `object`.'
'`testComponent`, expected `object`.',
);
});
@@ -937,7 +970,7 @@ describe('ReactPropTypes', () => {
PropTypes.shape({key: PropTypes.number.isRequired}),
{},
'The prop `testProp.key` is marked as required in `testComponent`, ' +
'but its value is `undefined`.'
'but its value is `undefined`.',
);
});
@@ -949,44 +982,49 @@ describe('ReactPropTypes', () => {
}),
{},
'The prop `testProp.key` is marked as required in `testComponent`, ' +
'but its value is `undefined`.'
'but its value is `undefined`.',
);
});
it('should warn for invalid key types', () => {
typeCheckFail(PropTypes.shape({key: PropTypes.number}),
typeCheckFail(
PropTypes.shape({key: PropTypes.number}),
{key: 'abc'},
'Invalid prop `testProp.key` of type `string` supplied to `testComponent`, ' +
'expected `number`.'
'expected `number`.',
);
});
it('should be implicitly optional and not warn without values', () => {
typeCheckPass(
PropTypes.shape(PropTypes.shape({key: PropTypes.number})), null
PropTypes.shape(PropTypes.shape({key: PropTypes.number})),
null,
);
typeCheckPass(
PropTypes.shape(PropTypes.shape({key: PropTypes.number})), undefined
PropTypes.shape(PropTypes.shape({key: PropTypes.number})),
undefined,
);
});
it('should warn for missing required values', () => {
typeCheckFailRequiredValues(
PropTypes.shape({key: PropTypes.number}).isRequired
PropTypes.shape({key: PropTypes.number}).isRequired,
);
});
it('should warn if called manually in development', () => {
spyOn(console, 'error');
expectWarningInDevelopment(PropTypes.shape({}), 'some string');
expectWarningInDevelopment(PropTypes.shape({ foo: PropTypes.number }), { foo: 42 });
expectWarningInDevelopment(PropTypes.shape({foo: PropTypes.number}), {
foo: 42,
});
expectWarningInDevelopment(
PropTypes.shape({key: PropTypes.number}).isRequired,
null
null,
);
expectWarningInDevelopment(
PropTypes.shape({key: PropTypes.number}).isRequired,
undefined
undefined,
);
expectWarningInDevelopment(PropTypes.element, <div />);
});
@@ -998,13 +1036,13 @@ describe('ReactPropTypes', () => {
PropTypes.symbol,
'hello',
'Invalid prop `testProp` of type `string` supplied to ' +
'`testComponent`, expected `symbol`.'
'`testComponent`, expected `symbol`.',
);
typeCheckFail(
PropTypes.symbol,
function() { },
function() {},
'Invalid prop `testProp` of type `function` supplied to ' +
'`testComponent`, expected `symbol`.'
'`testComponent`, expected `symbol`.',
);
typeCheckFail(
PropTypes.symbol,
@@ -1012,7 +1050,7 @@ describe('ReactPropTypes', () => {
'@@toStringTag': 'Katana',
},
'Invalid prop `testProp` of type `object` supplied to ' +
'`testComponent`, expected `symbol`.'
'`testComponent`, expected `symbol`.',
);
});
@@ -1061,15 +1099,15 @@ describe('ReactPropTypes', () => {
expect(spy.calls.argsFor(0)[1]).toBe('num');
});
it('should have received the validator\'s return value', () => {
it("should have received the validator's return value", () => {
spyOn(console, 'error');
var spy = jasmine.createSpy().and.callFake(
function(props, propName, componentName) {
var spy = jasmine
.createSpy()
.and.callFake(function(props, propName, componentName) {
if (props[propName] !== 5) {
return new Error('num must be 5!');
}
}
);
});
Component = class extends React.Component {
static propTypes = {num: spy};
@@ -1082,33 +1120,31 @@ describe('ReactPropTypes', () => {
ReactTestUtils.renderIntoDocument(instance);
expect(console.error.calls.count()).toBe(1);
expect(
console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)')
console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)'),
).toBe(
'Warning: Failed prop type: num must be 5!\n' +
' in Component (at **)'
' in Component (at **)',
);
});
it('should not warn if the validator returned null',
() => {
spyOn(console, 'error');
var spy = jasmine.createSpy().and.callFake(
function(props, propName, componentName) {
return null;
}
);
Component = class extends React.Component {
static propTypes = {num: spy};
it('should not warn if the validator returned null', () => {
spyOn(console, 'error');
var spy = jasmine
.createSpy()
.and.callFake(function(props, propName, componentName) {
return null;
});
Component = class extends React.Component {
static propTypes = {num: spy};
render() {
return <div />;
}
};
render() {
return <div />;
}
};
var instance = <Component num={5} />;
ReactTestUtils.renderIntoDocument(instance);
expect(console.error.calls.count()).toBe(0);
}
);
var instance = <Component num={5} />;
ReactTestUtils.renderIntoDocument(instance);
expect(console.error.calls.count()).toBe(0);
});
});
});
+93 -34
View File
@@ -17,23 +17,26 @@ var ReactCurrentOwner = require('ReactCurrentOwner');
var invariant = require('invariant');
var warning = require('warning');
import type { ReactElement, Source } from 'ReactElementType';
import type { DebugID } from 'ReactInstanceType';
import type {ReactElement, Source} from 'ReactElementType';
import type {DebugID} from 'ReactInstanceType';
function isNative(fn) {
// Based on isNative() from Lodash
var funcToString = Function.prototype.toString;
var hasOwnProperty = Object.prototype.hasOwnProperty;
var reIsNative = RegExp('^' + funcToString
// Take an example native function source for comparison
.call(hasOwnProperty)
// Strip regex characters so we can use it for regex
.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&')
// Remove hasOwnProperty from the template to make it generic
.replace(
/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,
'$1.*?'
) + '$'
var reIsNative = RegExp(
'^' +
funcToString
// Take an example native function source for comparison
.call(hasOwnProperty)
// Strip regex characters so we can use it for regex
.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&')
// Remove hasOwnProperty from the template to make it generic
.replace(
/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,
'$1.*?',
) +
'$',
);
try {
var source = funcToString.call(fn);
@@ -43,7 +46,7 @@ function isNative(fn) {
}
}
var canUseCollections = (
var canUseCollections =
// Array.from
typeof Array.from === 'function' &&
// Map
@@ -59,8 +62,7 @@ var canUseCollections = (
// Set.prototype.keys
Set.prototype != null &&
typeof Set.prototype.keys === 'function' &&
isNative(Set.prototype.keys)
);
isNative(Set.prototype.keys);
var setItem;
var getItem;
@@ -96,7 +98,6 @@ if (canUseCollections) {
getRootIDs = function() {
return Array.from(rootIDSet.keys());
};
} else {
var itemByKey = {};
var rootByKey = {};
@@ -151,13 +152,16 @@ function purgeDeep(id) {
}
function describeComponentFrame(name, source, ownerName) {
return '\n in ' + (name || 'Unknown') + (
source ?
' (at ' + source.fileName.replace(/^.*[\\\/]/, '') + ':' +
source.lineNumber + ')' :
ownerName ?
' (created by ' + ownerName + ')' :
''
return (
'\n in ' +
(name || 'Unknown') +
(source
? ' (at ' +
source.fileName.replace(/^.*[\\\/]/, '') +
':' +
source.lineNumber +
')'
: ownerName ? ' (created by ' + ownerName + ')' : '')
);
}
@@ -184,8 +188,8 @@ function describeID(id: DebugID): string {
warning(
element,
'ReactComponentTreeHook: Missing React element for debugID %s when ' +
'building stack',
id
'building stack',
id,
);
return describeComponentFrame(name, element && element._source, ownerName);
}
@@ -202,19 +206,19 @@ var ReactComponentTreeHook = {
invariant(
nextChild,
'Expected hook events to fire for the child ' +
'before its parent includes it in onSetChildren().'
'before its parent includes it in onSetChildren().',
);
invariant(
nextChild.childIDs != null ||
typeof nextChild.element !== 'object' ||
nextChild.element == null,
typeof nextChild.element !== 'object' ||
nextChild.element == null,
'Expected onSetChildren() to fire for a container child ' +
'before its parent includes it in onSetChildren().'
'before its parent includes it in onSetChildren().',
);
invariant(
nextChild.isMounted,
'Expected onMountComponent() to fire for the child ' +
'before its parent includes it in onSetChildren().'
'before its parent includes it in onSetChildren().',
);
if (nextChild.parentID == null) {
nextChild.parentID = id;
@@ -225,15 +229,19 @@ var ReactComponentTreeHook = {
invariant(
nextChild.parentID === id,
'Expected onBeforeMountComponent() parent and onSetChildren() to ' +
'be consistent (%s has parents %s and %s).',
'be consistent (%s has parents %s and %s).',
nextChildID,
nextChild.parentID,
id
id,
);
}
},
onBeforeMountComponent(id: DebugID, element: ReactElement, parentID: DebugID): void {
onBeforeMountComponent(
id: DebugID,
element: ReactElement,
parentID: DebugID,
): void {
var item = {
element,
parentID,
@@ -318,7 +326,7 @@ var ReactComponentTreeHook = {
info += describeComponentFrame(
name,
topElement._source,
owner && owner.getName()
owner && owner.getName(),
);
}
@@ -394,6 +402,57 @@ var ReactComponentTreeHook = {
getRootIDs,
getRegisteredIDs: getItemIDs,
pushNonStandardWarningStack(
isCreatingElement: boolean,
currentSource: ?Source,
) {
if (typeof console.reactStack !== 'function') {
return;
}
var stack = [];
var currentOwner = ReactCurrentOwner.current;
var id = currentOwner && currentOwner._debugID;
try {
if (isCreatingElement) {
stack.push({
name: id ? ReactComponentTreeHook.getDisplayName(id) : null,
fileName: currentSource ? currentSource.fileName : null,
lineNumber: currentSource ? currentSource.lineNumber : null,
});
}
while (id) {
var element = ReactComponentTreeHook.getElement(id);
var parentID = ReactComponentTreeHook.getParentID(id);
var ownerID = ReactComponentTreeHook.getOwnerID(id);
var ownerName = ownerID
? ReactComponentTreeHook.getDisplayName(ownerID)
: null;
var source = element && element._source;
stack.push({
name: ownerName,
fileName: source ? source.fileName : null,
lineNumber: source ? source.lineNumber : null,
});
id = parentID;
}
} catch (err) {
// Internal state is messed up.
// Stop building the stack (it's just a nice to have).
}
console.reactStack(stack);
},
popNonStandardWarningStack() {
if (typeof console.reactStackEnd !== 'function') {
return;
}
console.reactStackEnd();
},
};
module.exports = ReactComponentTreeHook;
+19
View File
@@ -0,0 +1,19 @@
/*!
* Copyright 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/**
* TypeScript Definition File for React.
*
* Full type definitions are not yet officially supported. These are mostly
* just helpers for the unit test.
*/
declare module 'prop-types' {
export var string : any;
}
@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactComponent
* @providesModule ReactBaseClasses
*/
'use strict';
@@ -16,7 +16,7 @@ var ReactNoopUpdateQueue = require('ReactNoopUpdateQueue');
var canDefineProperty = require('canDefineProperty');
var emptyObject = require('emptyObject');
var invariant = require('invariant');
var warning = require('warning');
var lowPriorityWarning = require('lowPriorityWarning');
/**
* Base class helpers for the updating state of a component.
@@ -60,10 +60,10 @@ ReactComponent.prototype.isReactComponent = {};
ReactComponent.prototype.setState = function(partialState, callback) {
invariant(
typeof partialState === 'object' ||
typeof partialState === 'function' ||
partialState == null,
typeof partialState === 'function' ||
partialState == null,
'setState(...): takes an object of state variables to update or a ' +
'function which returns an object of state variables.'
'function which returns an object of state variables.',
);
this.updater.enqueueSetState(this, partialState);
if (callback) {
@@ -102,23 +102,23 @@ if (__DEV__) {
isMounted: [
'isMounted',
'Instead, make sure to clean up subscriptions and pending requests in ' +
'componentWillUnmount to prevent memory leaks.',
'componentWillUnmount to prevent memory leaks.',
],
replaceState: [
'replaceState',
'Refactor your code to use setState instead (see ' +
'https://github.com/facebook/react/issues/3236).',
'https://github.com/facebook/react/issues/3236).',
],
};
var defineDeprecationWarning = function(methodName, info) {
if (canDefineProperty) {
Object.defineProperty(ReactComponent.prototype, methodName, {
get: function() {
warning(
lowPriorityWarning(
false,
'%s(...) is deprecated in plain JavaScript React classes. %s',
info[0],
info[1]
info[1],
);
return undefined;
},
@@ -132,4 +132,28 @@ if (__DEV__) {
}
}
module.exports = ReactComponent;
/**
* Base class helpers for the updating state of a component.
*/
function ReactPureComponent(props, context, updater) {
// Duplicated from ReactComponent.
this.props = props;
this.context = context;
this.refs = emptyObject;
// We initialize the default updater but the real one gets injected by the
// renderer.
this.updater = updater || ReactNoopUpdateQueue;
}
function ComponentDummy() {}
ComponentDummy.prototype = ReactComponent.prototype;
ReactPureComponent.prototype = new ComponentDummy();
ReactPureComponent.prototype.constructor = ReactPureComponent;
// Avoid an extra prototype jump for these methods.
Object.assign(ReactPureComponent.prototype, ReactComponent.prototype);
ReactPureComponent.prototype.isPureReactComponent = true;
module.exports = {
Component: ReactComponent,
PureComponent: ReactPureComponent,
};
@@ -19,11 +19,12 @@ function warnNoop(publicInstance, callerName) {
warning(
false,
'%s(...): Can only update a mounted or mounting component. ' +
'This usually means you called %s() on an unmounted component. ' +
'This is a no-op. Please check the code for the %s component.',
'This usually means you called %s() on an unmounted component. ' +
'This is a no-op. Please check the code for the %s component.',
callerName,
callerName,
constructor && (constructor.displayName || constructor.name) || 'ReactClass'
(constructor && (constructor.displayName || constructor.name)) ||
'ReactClass',
);
}
}
@@ -32,7 +33,6 @@ function warnNoop(publicInstance, callerName) {
* This is the abstract API for an update queue.
*/
var ReactNoopUpdateQueue = {
/**
* Checks whether or not this composite component is mounted.
* @param {ReactClass} publicInstance The instance we want to test.
@@ -52,7 +52,7 @@ var ReactNoopUpdateQueue = {
* @param {?function} callback Called after state is updated.
* @internal
*/
enqueueCallback: function(publicInstance, callback) { },
enqueueCallback: function(publicInstance, callback) {},
/**
* Forces an update. This should only be invoked when it is known with
@@ -1,40 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactPureComponent
*/
'use strict';
var ReactComponent = require('ReactComponent');
var ReactNoopUpdateQueue = require('ReactNoopUpdateQueue');
var emptyObject = require('emptyObject');
/**
* Base class helpers for the updating state of a component.
*/
function ReactPureComponent(props, context, updater) {
// Duplicated from ReactComponent.
this.props = props;
this.context = context;
this.refs = emptyObject;
// We initialize the default updater but the real one gets injected by the
// renderer.
this.updater = updater || ReactNoopUpdateQueue;
}
function ComponentDummy() {}
ComponentDummy.prototype = ReactComponent.prototype;
ReactPureComponent.prototype = new ComponentDummy();
ReactPureComponent.prototype.constructor = ReactPureComponent;
// Avoid an extra prototype jump for these methods.
Object.assign(ReactPureComponent.prototype, ReactComponent.prototype);
ReactPureComponent.prototype.isPureReactComponent = true;
module.exports = ReactPureComponent;
@@ -26,7 +26,6 @@ describe('ReactClassEquivalence', () => {
var result2 = runJest('ReactES6Class-test.js');
compareResults(result1, result2);
});
});
function runJest(testFile) {
@@ -35,14 +34,13 @@ function runJest(testFile) {
var setupFile = path.resolve(
'scripts',
'jest',
'setupSpecEquivalenceReporter.js'
'setupSpecEquivalenceReporter.js',
);
var result = spawnSync(
'node',
[jestBin, testFile, '--setupTestFrameworkScriptFile', setupFile],
{cwd},
);
var result = spawnSync('node', [
jestBin,
testFile,
'--setupTestFrameworkScriptFile',
setupFile,
], {cwd});
if (result.error) {
throw result.error;
@@ -51,12 +49,12 @@ function runJest(testFile) {
if (result.status !== 0) {
throw new Error(
'jest process exited with: ' +
result.status +
'\n' +
'stdout: ' +
result.stdout.toString() +
'stderr: ' +
result.stderr.toString()
result.status +
'\n' +
'stdout: ' +
result.stdout.toString() +
'stderr: ' +
result.stderr.toString(),
);
}
@@ -7,6 +7,7 @@ LICENSE file in the root directory of this source tree. An additional grant
of patent rights can be found in the PATENTS file in the same directory.
###
PropTypes = null
React = null
ReactDOM = null
@@ -21,6 +22,7 @@ describe 'ReactCoffeeScriptClass', ->
beforeEach ->
React = require 'React'
ReactDOM = require 'ReactDOM'
PropTypes = require 'prop-types'
container = document.createElement 'div'
attachedListener = null
renderedName = null
@@ -102,8 +104,8 @@ describe 'ReactCoffeeScriptClass', ->
it 'renders based on context in the constructor', ->
class Foo extends React.Component
@contextTypes:
tag: React.PropTypes.string
className: React.PropTypes.string
tag: PropTypes.string
className: PropTypes.string
constructor: (props, context) ->
super props, context
@@ -118,8 +120,8 @@ describe 'ReactCoffeeScriptClass', ->
class Outer extends React.Component
@childContextTypes:
tag: React.PropTypes.string
className: React.PropTypes.string
tag: PropTypes.string
className: PropTypes.string
getChildContext: ->
tag: 'span'
@@ -376,16 +378,16 @@ describe 'ReactCoffeeScriptClass', ->
undefined
it 'should throw AND warn when trying to access classic APIs', ->
spyOn console, 'error'
spyOn console, 'warn'
instance =
test Inner(name: 'foo'), 'DIV', 'foo'
expect(-> instance.replaceState {}).toThrow()
expect(-> instance.isMounted()).toThrow()
expect(console.error.calls.count()).toBe 2
expect(console.error.calls.argsFor(0)[0]).toContain(
expect(console.warn.calls.count()).toBe 2
expect(console.warn.calls.argsFor(0)[0]).toContain(
'replaceState(...) is deprecated in plain JavaScript React classes'
)
expect(console.error.calls.argsFor(1)[0]).toContain(
expect(console.warn.calls.argsFor(1)[0]).toContain(
'isMounted(...) is deprecated in plain JavaScript React classes'
)
undefined
@@ -393,13 +395,13 @@ describe 'ReactCoffeeScriptClass', ->
it 'supports this.context passed via getChildContext', ->
class Bar extends React.Component
@contextTypes:
bar: React.PropTypes.string
bar: PropTypes.string
render: ->
div className: @context.bar
class Foo extends React.Component
@childContextTypes:
bar: React.PropTypes.string
bar: PropTypes.string
getChildContext: ->
bar: 'bar-through-context'
render: ->
@@ -11,11 +11,11 @@
'use strict';
var PropTypes;
var React;
var ReactDOM;
describe('ReactES6Class', () => {
var container;
var freeze = function(expectation) {
Object.freeze(expectation);
@@ -28,6 +28,7 @@ describe('ReactES6Class', () => {
beforeEach(() => {
React = require('React');
ReactDOM = require('ReactDOM');
PropTypes = require('prop-types');
container = document.createElement('div');
attachedListener = null;
renderedName = null;
@@ -52,19 +53,19 @@ describe('ReactES6Class', () => {
}
it('preserves the name of the class for use in error messages', () => {
class Foo extends React.Component { }
class Foo extends React.Component {}
expect(Foo.name).toBe('Foo');
});
it('throws if no render function is defined', () => {
spyOn(console, 'error');
class Foo extends React.Component { }
class Foo extends React.Component {}
expect(() => ReactDOM.render(<Foo />, container)).toThrow();
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: Foo(...): No `render` method found on the returned component ' +
'instance: you may have forgotten to define `render`.'
'instance: you may have forgotten to define `render`.',
);
});
@@ -124,8 +125,8 @@ describe('ReactES6Class', () => {
}
}
Foo.contextTypes = {
tag: React.PropTypes.string,
className: React.PropTypes.string,
tag: PropTypes.string,
className: PropTypes.string,
};
class Outer extends React.Component {
@@ -137,8 +138,8 @@ describe('ReactES6Class', () => {
}
}
Outer.childContextTypes = {
tag: React.PropTypes.string,
className: React.PropTypes.string,
tag: PropTypes.string,
className: PropTypes.string,
};
test(<Outer />, 'SPAN', 'foo');
});
@@ -174,7 +175,7 @@ describe('ReactES6Class', () => {
}
}
expect(() => test(<Foo />, 'span', '')).toThrowError(
'Foo.state: must be set to an object or null'
'Foo.state: must be set to an object or null',
);
});
});
@@ -203,10 +204,7 @@ describe('ReactES6Class', () => {
}
render() {
return (
<Inner
name={this.state.bar}
onClick={this.handleClick.bind(this)}
/>
<Inner name={this.state.bar} onClick={this.handleClick.bind(this)} />
);
}
}
@@ -225,12 +223,7 @@ describe('ReactES6Class', () => {
this.setState({bar: 'bar'});
}
render() {
return (
<Inner
name={this.state.bar}
onClick={this.handleClick}
/>
);
return <Inner name={this.state.bar} onClick={this.handleClick} />;
}
}
test(<Foo initialValue="foo" />, 'DIV', 'foo');
@@ -295,23 +288,25 @@ describe('ReactES6Class', () => {
}
}
test(<Foo value="foo" />, 'SPAN', 'foo');
expect(lifeCycles).toEqual([
'will-mount',
'did-mount',
]);
expect(lifeCycles).toEqual(['will-mount', 'did-mount']);
lifeCycles = []; // reset
test(<Foo value="bar" />, 'SPAN', 'bar');
expect(lifeCycles).toEqual([
'receive-props', freeze({value: 'bar'}),
'should-update', freeze({value: 'bar'}), {},
'will-update', freeze({value: 'bar'}), {},
'did-update', freeze({value: 'foo'}), {},
'receive-props',
freeze({value: 'bar'}),
'should-update',
freeze({value: 'bar'}),
{},
'will-update',
freeze({value: 'bar'}),
{},
'did-update',
freeze({value: 'foo'}),
{},
]);
lifeCycles = []; // reset
ReactDOM.unmountComponentAtNode(container);
expect(lifeCycles).toEqual([
'will-unmount',
]);
expect(lifeCycles).toEqual(['will-unmount']);
});
it('warns when classic properties are defined on the instance, but does not invoke them.', () => {
@@ -341,16 +336,16 @@ describe('ReactES6Class', () => {
expect(getDefaultPropsWasCalled).toBe(false);
expect(console.error.calls.count()).toBe(4);
expect(console.error.calls.argsFor(0)[0]).toContain(
'getInitialState was defined on Foo, a plain JavaScript class.'
'getInitialState was defined on Foo, a plain JavaScript class.',
);
expect(console.error.calls.argsFor(1)[0]).toContain(
'getDefaultProps was defined on Foo, a plain JavaScript class.'
'getDefaultProps was defined on Foo, a plain JavaScript class.',
);
expect(console.error.calls.argsFor(2)[0]).toContain(
'propTypes was defined as an instance property on Foo.'
'propTypes was defined as an instance property on Foo.',
);
expect(console.error.calls.argsFor(3)[0]).toContain(
'contextTypes was defined as an instance property on Foo.'
'contextTypes was defined as an instance property on Foo.',
);
});
@@ -385,9 +380,9 @@ describe('ReactES6Class', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: ' +
'NamedComponent has a method called componentShouldUpdate(). Did you ' +
'mean shouldComponentUpdate()? The name is phrased as a question ' +
'because the function is expected to return a value.'
'NamedComponent has a method called componentShouldUpdate(). Did you ' +
'mean shouldComponentUpdate()? The name is phrased as a question ' +
'because the function is expected to return a value.',
);
});
@@ -407,22 +402,22 @@ describe('ReactES6Class', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: ' +
'NamedComponent has a method called componentWillRecieveProps(). Did ' +
'you mean componentWillReceiveProps()?'
'NamedComponent has a method called componentWillRecieveProps(). Did ' +
'you mean componentWillReceiveProps()?',
);
});
it('should throw AND warn when trying to access classic APIs', () => {
spyOn(console, 'error');
spyOn(console, 'warn');
var instance = test(<Inner name="foo" />, 'DIV', 'foo');
expect(() => instance.replaceState({})).toThrow();
expect(() => instance.isMounted()).toThrow();
expect(console.error.calls.count()).toBe(2);
expect(console.error.calls.argsFor(0)[0]).toContain(
'replaceState(...) is deprecated in plain JavaScript React classes'
expect(console.warn.calls.count()).toBe(2);
expect(console.warn.calls.argsFor(0)[0]).toContain(
'replaceState(...) is deprecated in plain JavaScript React classes',
);
expect(console.error.calls.argsFor(1)[0]).toContain(
'isMounted(...) is deprecated in plain JavaScript React classes'
expect(console.warn.calls.argsFor(1)[0]).toContain(
'isMounted(...) is deprecated in plain JavaScript React classes',
);
});
@@ -432,7 +427,7 @@ describe('ReactES6Class', () => {
return <div className={this.context.bar} />;
}
}
Bar.contextTypes = {bar: React.PropTypes.string};
Bar.contextTypes = {bar: PropTypes.string};
class Foo extends React.Component {
getChildContext() {
return {bar: 'bar-through-context'};
@@ -441,7 +436,7 @@ describe('ReactES6Class', () => {
return <Bar />;
}
}
Foo.childContextTypes = {bar: React.PropTypes.string};
Foo.childContextTypes = {bar: PropTypes.string};
test(<Foo />, 'DIV', 'bar-through-context');
});
@@ -460,5 +455,4 @@ describe('ReactES6Class', () => {
var node = ReactDOM.findDOMNode(instance);
expect(node).toBe(container.firstChild);
});
});
@@ -93,5 +93,4 @@ describe('ReactPureComponent', () => {
ReactDOM.render(<Component />, document.createElement('div'));
expect(renders).toBe(1);
});
});
@@ -1,3 +1,4 @@
/// <reference path="../PropTypes.d.ts" />
/// <reference path="../React.d.ts" />
/// <reference path="../ReactDOM.d.ts" />
@@ -12,6 +13,7 @@
import React = require('React');
import ReactDOM = require('ReactDOM');
import PropTypes = require('prop-types');
// Before Each
@@ -85,8 +87,8 @@ class StateBasedOnProps extends React.Component {
// it renders based on context in the constructor
class StateBasedOnContext extends React.Component {
static contextTypes = {
tag: React.PropTypes.string,
className: React.PropTypes.string
tag: PropTypes.string,
className: PropTypes.string
};
state = {
tag: this.context.tag,
@@ -100,8 +102,8 @@ class StateBasedOnContext extends React.Component {
class ProvideChildContextTypes extends React.Component {
static childContextTypes = {
tag: React.PropTypes.string,
className: React.PropTypes.string
tag: PropTypes.string,
className: PropTypes.string
};
getChildContext() {
return { tag: 'span', className: 'foo' };
@@ -278,13 +280,13 @@ class MisspelledComponent2 extends React.Component {
// it supports this.context passed via getChildContext
class ReadContext extends React.Component {
static contextTypes = { bar: React.PropTypes.string };
static contextTypes = { bar: PropTypes.string };
render() {
return React.createElement('div', { className: this.context.bar });
}
}
class ProvideContext extends React.Component {
static childContextTypes = { bar: React.PropTypes.string };
static childContextTypes = { bar: PropTypes.string };
getChildContext() {
return { bar: 'bar-through-context' };
}
@@ -500,18 +502,18 @@ describe('ReactTypeScriptClass', function() {
});
it('should throw AND warn when trying to access classic APIs', function() {
spyOn(console, 'error');
spyOn(console, 'warn');
var instance = test(
React.createElement(Inner, {name: 'foo'}),
'DIV','foo'
);
expect(() => instance.replaceState({})).toThrow();
expect(() => instance.isMounted()).toThrow();
expect((<any>console.error).calls.count()).toBe(2);
expect((<any>console.error).calls.argsFor(0)[0]).toContain(
expect((<any>console.warn).calls.count()).toBe(2);
expect((<any>console.warn).calls.argsFor(0)[0]).toContain(
'replaceState(...) is deprecated in plain JavaScript React classes'
);
expect((<any>console.error).calls.argsFor(1)[0]).toContain(
expect((<any>console.warn).calls.argsFor(1)[0]).toContain(
'isMounted(...) is deprecated in plain JavaScript React classes'
);
});
@@ -64,7 +64,7 @@ describe('ReactJSXElement', () => {
it('returns an immutable element', () => {
var element = <Component />;
expect(() => element.type = 'div').toThrow();
expect(() => (element.type = 'div')).toThrow();
});
it('does not reuse the object that is spread into props', () => {
@@ -80,7 +80,7 @@ describe('ReactJSXElement', () => {
expect(element.type).toBe(Component);
expect(element.key).toBe('12');
expect(element.ref).toBe('34');
var expectation = {foo:'56'};
var expectation = {foo: '56'};
Object.freeze(expectation);
expect(element.props).toEqual(expectation);
});
@@ -90,7 +90,7 @@ describe('ReactJSXElement', () => {
expect(element.type).toBe(Component);
expect(element.key).toBe('12');
expect(element.ref).toBe(null);
var expectation = {foo:'56'};
var expectation = {foo: '56'};
Object.freeze(expectation);
expect(element.props).toEqual(expectation);
});
@@ -124,7 +124,7 @@ describe('ReactJSXElement', () => {
var element2 = React.cloneElement(
<Component children="text" />,
{},
undefined
undefined,
);
expect(element2.props.children).toBe(undefined);
});
@@ -165,7 +165,7 @@ describe('ReactJSXElement', () => {
expect(React.isValidElement({})).toEqual(false);
expect(React.isValidElement('string')).toEqual(false);
expect(React.isValidElement(Component)).toEqual(false);
expect(React.isValidElement({ type: 'div', props: {} })).toEqual(false);
expect(React.isValidElement({type: 'div', props: {}})).toEqual(false);
});
it('is indistinguishable from a plain object', () => {
@@ -178,10 +178,7 @@ describe('ReactJSXElement', () => {
Component.defaultProps = {fruit: 'persimmon'};
var container = document.createElement('div');
var instance = ReactDOM.render(
<Component fruit="mango" />,
container
);
var instance = ReactDOM.render(<Component fruit="mango" />, container);
expect(instance.props.fruit).toBe('mango');
ReactDOM.render(<Component />, container);
@@ -199,9 +196,9 @@ describe('ReactJSXElement', () => {
var instance = ReactTestUtils.renderIntoDocument(<NormalizingComponent />);
expect(instance.props.prop).toBe('testKey');
var inst2 =
ReactTestUtils.renderIntoDocument(<NormalizingComponent prop={null} />);
var inst2 = ReactTestUtils.renderIntoDocument(
<NormalizingComponent prop={null} />,
);
expect(inst2.props.prop).toBe(null);
});
});
@@ -14,6 +14,7 @@
// TODO: All these warnings should become static errors using Flow instead
// of dynamic errors when using JSX with Flow.
var PropTypes;
var React;
var ReactDOM;
var ReactTestUtils;
@@ -32,6 +33,7 @@ describe('ReactJSXElementValidator', () => {
React = require('React');
ReactDOM = require('ReactDOM');
ReactTestUtils = require('ReactTestUtils');
PropTypes = require('prop-types');
Component = class extends React.Component {
render() {
@@ -45,7 +47,7 @@ describe('ReactJSXElementValidator', () => {
}
};
RequiredPropComponent.displayName = 'RequiredPropComponent';
RequiredPropComponent.propTypes = {prop: React.PropTypes.string.isRequired};
RequiredPropComponent.propTypes = {prop: PropTypes.string.isRequired};
});
it('warns for keys for arrays of elements in children position', () => {
@@ -55,7 +57,7 @@ describe('ReactJSXElementValidator', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'Each child in an array or iterator should have a unique "key" prop.'
'Each child in an array or iterator should have a unique "key" prop.',
);
});
@@ -70,11 +72,7 @@ describe('ReactJSXElementValidator', () => {
class ComponentWrapper extends React.Component {
render() {
return (
<InnerComponent
childSet={[<Component />, <Component />]}
/>
);
return <InnerComponent childSet={[<Component />, <Component />]} />;
}
}
@@ -83,8 +81,8 @@ describe('ReactJSXElementValidator', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'Each child in an array or iterator should have a unique "key" prop. ' +
'Check the render method of `InnerComponent`. ' +
'It was passed a child from ComponentWrapper. '
'Check the render method of `InnerComponent`. ' +
'It was passed a child from ComponentWrapper. ',
);
});
@@ -107,14 +105,16 @@ describe('ReactJSXElementValidator', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'Each child in an array or iterator should have a unique "key" prop.'
'Each child in an array or iterator should have a unique "key" prop.',
);
});
it('does not warns for arrays of elements with keys', () => {
spyOn(console, 'error');
void <Component>{[<Component key="#1" />, <Component key="#2" />]}</Component>;
void (
<Component>{[<Component key="#1" />, <Component key="#2" />]}</Component>
);
expect(console.error.calls.count()).toBe(0);
});
@@ -193,7 +193,7 @@ describe('ReactJSXElementValidator', () => {
}
}
MyComp.propTypes = {
color: React.PropTypes.string,
color: PropTypes.string,
};
class ParentComp extends React.Component {
render() {
@@ -203,10 +203,10 @@ describe('ReactJSXElementValidator', () => {
ReactTestUtils.renderIntoDocument(<ParentComp />);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: Failed prop type: ' +
'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
'expected `string`.\n' +
' in MyComp (at **)\n' +
' in ParentComp (at **)'
'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
'expected `string`.\n' +
' in MyComp (at **)\n' +
' in ParentComp (at **)',
);
});
@@ -216,7 +216,7 @@ describe('ReactJSXElementValidator', () => {
return null;
}
MyComp.propTypes = {
color: React.PropTypes.string,
color: PropTypes.string,
};
function MiddleComp(props) {
return <MyComp color={props.color} />;
@@ -239,11 +239,11 @@ describe('ReactJSXElementValidator', () => {
// If it doesn't, it means we're using information from the old element.
expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: Failed prop type: ' +
'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
'expected `string`.\n' +
' in MyComp (at **)\n' +
' in MiddleComp (at **)\n' +
' in ParentComp (at **)'
'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
'expected `string`.\n' +
' in MyComp (at **)\n' +
' in MiddleComp (at **)\n' +
' in ParentComp (at **)',
);
});
@@ -261,28 +261,28 @@ describe('ReactJSXElementValidator', () => {
expect(console.error.calls.count()).toBe(4);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: undefined. You likely forgot to export your ' +
'component from the file it\'s defined in. ' +
'Check your code at **.'
'(for built-in components) or a class/function (for composite ' +
'components) but got: undefined. You likely forgot to export your ' +
"component from the file it's defined in. " +
'Check your code at **.',
);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: null. ' +
'Check your code at **.'
'(for built-in components) or a class/function (for composite ' +
'components) but got: null. ' +
'Check your code at **.',
);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe(
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: boolean. ' +
'Check your code at **.'
'(for built-in components) or a class/function (for composite ' +
'components) but got: boolean. ' +
'Check your code at **.',
);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(3)[0])).toBe(
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: number. ' +
'Check your code at **.'
'(for built-in components) or a class/function (for composite ' +
'components) but got: number. ' +
'Check your code at **.',
);
void <Div />;
expect(console.error.calls.count()).toBe(4);
@@ -298,8 +298,8 @@ describe('ReactJSXElementValidator', () => {
expect(console.error.calls.count()).toBe(1);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: Failed prop type: The prop `prop` is marked as required in ' +
'`RequiredPropComponent`, but its value is `null`.\n' +
' in RequiredPropComponent (at **)'
'`RequiredPropComponent`, but its value is `null`.\n' +
' in RequiredPropComponent (at **)',
);
});
@@ -311,8 +311,8 @@ describe('ReactJSXElementValidator', () => {
expect(console.error.calls.count()).toBe(1);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: Failed prop type: The prop `prop` is marked as required in ' +
'`RequiredPropComponent`, but its value is `null`.\n' +
' in RequiredPropComponent (at **)'
'`RequiredPropComponent`, but its value is `null`.\n' +
' in RequiredPropComponent (at **)',
);
});
@@ -325,16 +325,16 @@ describe('ReactJSXElementValidator', () => {
expect(console.error.calls.count()).toBe(2);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: Failed prop type: ' +
'The prop `prop` is marked as required in `RequiredPropComponent`, but ' +
'its value is `undefined`.\n' +
' in RequiredPropComponent (at **)'
'The prop `prop` is marked as required in `RequiredPropComponent`, but ' +
'its value is `undefined`.\n' +
' in RequiredPropComponent (at **)',
);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
'Warning: Failed prop type: ' +
'Invalid prop `prop` of type `number` supplied to ' +
'`RequiredPropComponent`, expected `string`.\n' +
' in RequiredPropComponent (at **)'
'Invalid prop `prop` of type `number` supplied to ' +
'`RequiredPropComponent`, expected `string`.\n' +
' in RequiredPropComponent (at **)',
);
ReactTestUtils.renderIntoDocument(<RequiredPropComponent prop="string" />);
@@ -361,7 +361,7 @@ describe('ReactJSXElementValidator', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'NullPropTypeComponent: prop type `prop` is invalid; it must be a ' +
'function, usually from React.PropTypes.'
'function, usually from React.PropTypes.',
);
});
@@ -379,7 +379,7 @@ describe('ReactJSXElementValidator', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'NullContextTypeComponent: context type `prop` is invalid; it must ' +
'be a function, usually from React.PropTypes.'
'be a function, usually from React.PropTypes.',
);
});
@@ -397,8 +397,58 @@ describe('ReactJSXElementValidator', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'getDefaultProps is only used on classic React.createClass definitions.' +
' Use a static property named `defaultProps` instead.'
' Use a static property named `defaultProps` instead.',
);
});
it('provides stack via non-standard console.reactStack for invalid types', () => {
spyOn(console, 'error');
function Foo() {
var Bad = undefined;
return <Bad />;
}
function App() {
return <div><Foo /></div>;
}
try {
console.reactStack = jest.fn();
console.reactStackEnd = jest.fn();
expect(() => {
ReactTestUtils.renderIntoDocument(<App />);
}).toThrow(
'Element type is invalid: expected a string (for built-in components) ' +
'or a class/function (for composite components) but got: undefined. ' +
"You likely forgot to export your component from the file it's " +
'defined in. Check the render method of `Foo`.',
);
expect(console.reactStack.mock.calls.length).toBe(1);
expect(console.reactStackEnd.mock.calls.length).toBe(1);
var stack = console.reactStack.mock.calls[0][0];
expect(Array.isArray(stack)).toBe(true);
expect(stack.map(frame => frame.name)).toEqual([
'Foo', // <Bad> is inside Foo
'App', // <Foo> is inside App
'App', // <div> is inside App
null, // <App> is outside a component
]);
expect(
stack.map(frame => frame.fileName && frame.fileName.slice(-8)),
).toEqual(['-test.js', '-test.js', '-test.js', '-test.js']);
expect(stack.map(frame => typeof frame.lineNumber)).toEqual([
'number',
'number',
'number',
'number',
]);
} finally {
delete console.reactStack;
delete console.reactStackEnd;
}
});
});
+93 -104
View File
@@ -12,7 +12,7 @@
'use strict';
require('art/modes/current').setCurrent(
require('art/modes/fast-noSideEffects') // Flip this to DOM mode for debugging
require('art/modes/fast-noSideEffects'), // Flip this to DOM mode for debugging
);
const Transform = require('art/core/transform');
@@ -24,6 +24,7 @@ const ReactInstanceMap = require('ReactInstanceMap');
const ReactMultiChild = require('ReactMultiChild');
const ReactUpdates = require('ReactUpdates');
const createReactClass = require('createClass');
const emptyObject = require('emptyObject');
const invariant = require('invariant');
@@ -67,8 +68,10 @@ function createComponent(name) {
*/
function injectAfter(parentNode, referenceNode, node) {
let beforeNode;
if (node.parentNode === parentNode &&
node.previousSibling === referenceNode) {
if (
node.parentNode === parentNode &&
node.previousSibling === referenceNode
) {
return;
}
if (referenceNode == null) {
@@ -83,7 +86,7 @@ function injectAfter(parentNode, referenceNode, node) {
// checks and the behavior isn't well-defined.
invariant(
node !== beforeNode,
'ReactART: Can not insert node before itself'
'ReactART: Can not insert node before itself',
);
node.injectBefore(beforeNode);
} else if (node.parentNode !== parentNode) {
@@ -94,7 +97,6 @@ function injectAfter(parentNode, referenceNode, node) {
// ContainerMixin for components that can hold ART nodes
const ContainerMixin = assign({}, ReactMultiChild.Mixin, {
/**
* Moves a child component to the supplied index.
*
@@ -153,11 +155,7 @@ const ContainerMixin = assign({}, ReactMultiChild.Mixin, {
// Shorthands
mountAndInjectChildren: function(children, transaction, context) {
const mountedImages = this.mountChildren(
children,
transaction,
context
);
const mountedImages = this.mountChildren(children, transaction, context);
// Each mount image corresponds to one of the flattened children
let i = 0;
for (let key in this._renderedChildren) {
@@ -168,15 +166,13 @@ const ContainerMixin = assign({}, ReactMultiChild.Mixin, {
i++;
}
}
}
},
});
// Surface is a React DOM Component, not an ART component. It serves as the
// entry point into the ART reconciler.
const Surface = React.createClass({
const Surface = createReactClass({
displayName: 'Surface',
mixins: [ContainerMixin],
@@ -192,15 +188,17 @@ const Surface = React.createClass({
this,
this.props.children,
transaction,
ReactInstanceMap.get(this)._context
ReactInstanceMap.get(this)._context,
);
ReactUpdates.ReactReconcileTransaction.release(transaction);
},
componentDidUpdate: function(oldProps) {
const node = this.node;
if (this.props.width != oldProps.width ||
this.props.height != oldProps.height) {
if (
this.props.width != oldProps.width ||
this.props.height != oldProps.height
) {
node.resize(+this.props.width, +this.props.height);
}
@@ -210,7 +208,7 @@ const Surface = React.createClass({
this,
this.props.children,
transaction,
ReactInstanceMap.get(this)._context
ReactInstanceMap.get(this)._context,
);
ReactUpdates.ReactReconcileTransaction.release(transaction);
@@ -241,8 +239,7 @@ const Surface = React.createClass({
title={props.title}
/>
);
}
},
});
// Various nodes that can go into a surface
@@ -253,11 +250,10 @@ const EventTypes = {
onMouseOut: 'mouseout',
onMouseUp: 'mouseup',
onMouseDown: 'mousedown',
onClick: 'click'
onClick: 'click',
};
const NodeMixin = {
construct: function(element) {
this._currentElement = element;
},
@@ -312,10 +308,12 @@ const NodeMixin = {
applyNodeProps: function(oldProps, props) {
const node = this.node;
const scaleX = props.scaleX != null ? props.scaleX :
props.scale != null ? props.scale : 1;
const scaleY = props.scaleY != null ? props.scaleY :
props.scale != null ? props.scale : 1;
const scaleX = props.scaleX != null
? props.scaleX
: props.scale != null ? props.scale : 1;
const scaleY = props.scaleY != null
? props.scaleY
: props.scale != null ? props.scale : 1;
pooledTransform
.transformTo(1, 0, 0, 1, 0, 0)
@@ -327,9 +325,14 @@ const NodeMixin = {
pooledTransform.transform(props.transform);
}
if (node.xx !== pooledTransform.xx || node.yx !== pooledTransform.yx ||
node.xy !== pooledTransform.xy || node.yy !== pooledTransform.yy ||
node.x !== pooledTransform.x || node.y !== pooledTransform.y) {
if (
node.xx !== pooledTransform.xx ||
node.yx !== pooledTransform.yx ||
node.xy !== pooledTransform.xy ||
node.yy !== pooledTransform.yy ||
node.x !== pooledTransform.x ||
node.y !== pooledTransform.y
) {
node.transformTo(pooledTransform);
}
@@ -357,21 +360,19 @@ const NodeMixin = {
mountComponentIntoNode: function(rootID, container) {
throw new Error(
'You cannot render an ART component standalone. ' +
'You need to wrap it in a Surface.'
'You need to wrap it in a Surface.',
);
}
},
};
// Group
const Group = createComponent('Group', NodeMixin, ContainerMixin, {
mountComponent: function(
transaction,
nativeParent,
nativeContainerInfo,
context
context,
) {
this.node = Mode.Group();
const props = this._currentElement.props;
@@ -397,55 +398,54 @@ const Group = createComponent('Group', NodeMixin, ContainerMixin, {
unmountComponent: function() {
this.destroyEventListeners();
this.unmountChildren();
}
},
});
// ClippingRectangle
const ClippingRectangle = createComponent(
'ClippingRectangle', NodeMixin, ContainerMixin, {
'ClippingRectangle',
NodeMixin,
ContainerMixin,
{
mountComponent: function(
transaction,
nativeParent,
nativeContainerInfo,
context,
) {
this.node = Mode.ClippingRectangle();
const props = this._currentElement.props;
this.applyClippingProps(emptyObject, props);
this.mountAndInjectChildren(props.children, transaction, context);
return this.node;
},
mountComponent: function(
transaction,
nativeParent,
nativeContainerInfo,
context
) {
this.node = Mode.ClippingRectangle();
const props = this._currentElement.props;
this.applyClippingProps(emptyObject, props);
this.mountAndInjectChildren(props.children, transaction, context);
return this.node;
receiveComponent: function(nextComponent, transaction, context) {
const props = nextComponent.props;
const oldProps = this._currentElement.props;
this.applyClippingProps(oldProps, props);
this.updateChildren(props.children, transaction, context);
this._currentElement = nextComponent;
},
applyClippingProps: function(oldProps, props) {
this.node.width = props.width;
this.node.height = props.height;
this.node.x = props.x;
this.node.y = props.y;
this.applyNodeProps(oldProps, props);
},
unmountComponent: function() {
this.destroyEventListeners();
this.unmountChildren();
},
},
receiveComponent: function(nextComponent, transaction, context) {
const props = nextComponent.props;
const oldProps = this._currentElement.props;
this.applyClippingProps(oldProps, props);
this.updateChildren(props.children, transaction, context);
this._currentElement = nextComponent;
},
applyClippingProps: function(oldProps, props) {
this.node.width = props.width;
this.node.height = props.height;
this.node.x = props.x;
this.node.y = props.y;
this.applyNodeProps(oldProps, props);
},
unmountComponent: function() {
this.destroyEventListeners();
this.unmountChildren();
}
});
);
// Renderables
const RenderableMixin = assign({}, NodeMixin, {
applyRenderableProps: function(oldProps, props) {
if (oldProps.fill !== props.fill) {
if (props.fill && props.fill.applyFill) {
@@ -468,7 +468,7 @@ const RenderableMixin = assign({}, NodeMixin, {
props.strokeWidth,
props.strokeCap,
props.strokeJoin,
props.strokeDash
props.strokeDash,
);
}
this.applyNodeProps(oldProps, props);
@@ -476,14 +476,12 @@ const RenderableMixin = assign({}, NodeMixin, {
unmountComponent: function() {
this.destroyEventListeners();
}
},
});
// Shape
const Shape = createComponent('Shape', RenderableMixin, {
construct: function(element) {
this._currentElement = element;
this._oldDelta = null;
@@ -494,7 +492,7 @@ const Shape = createComponent('Shape', RenderableMixin, {
transaction,
nativeParent,
nativeContainerInfo,
context
context,
) {
this.node = Mode.Shape();
const props = this._currentElement.props;
@@ -514,30 +512,25 @@ const Shape = createComponent('Shape', RenderableMixin, {
const oldPath = this._oldPath;
const path = props.d || childrenAsString(props.children);
if (path.delta !== oldDelta ||
path !== oldPath ||
oldProps.width !== props.width ||
oldProps.height !== props.height) {
this.node.draw(
path,
props.width,
props.height
);
if (
path.delta !== oldDelta ||
path !== oldPath ||
oldProps.width !== props.width ||
oldProps.height !== props.height
) {
this.node.draw(path, props.width, props.height);
this._oldPath = path;
this._oldDelta = path.delta;
}
this.applyRenderableProps(oldProps, props);
}
},
});
// Text
const Text = createComponent('Text', RenderableMixin, {
construct: function(element) {
this._currentElement = element;
this._oldString = null;
@@ -547,7 +540,7 @@ const Text = createComponent('Text', RenderableMixin, {
transaction,
nativeParent,
nativeContainerInfo,
context
context,
) {
const props = this._currentElement.props;
const newString = childrenAsString(props.children);
@@ -580,23 +573,19 @@ const Text = createComponent('Text', RenderableMixin, {
const oldString = this._oldString;
const newString = childrenAsString(props.children);
if (oldString !== newString ||
!this.isSameFont(oldProps.font, props.font) ||
oldProps.alignment !== props.alignment ||
oldProps.path !== props.path) {
this.node.draw(
newString,
props.font,
props.alignment,
props.path
);
if (
oldString !== newString ||
!this.isSameFont(oldProps.font, props.font) ||
oldProps.alignment !== props.alignment ||
oldProps.path !== props.path
) {
this.node.draw(newString, props.font, props.alignment, props.path);
this._oldString = newString;
}
this.applyRenderableProps(oldProps, props);
this._currentElement = nextComponent;
}
},
});
// Declarative fill type objects - API design not finalized
+43 -40
View File
@@ -49,7 +49,6 @@ function testDOMNodeStructure(domNode, expectedStructure) {
}
describe('ReactART', () => {
beforeEach(() => {
ARTCurrentMode.setCurrent(ARTSVGMode);
@@ -59,29 +58,33 @@ describe('ReactART', () => {
TestComponent = class extends React.Component {
render() {
var a =
var a = (
<Shape
d="M0,0l50,0l0,50l-50,0z"
fill={new ReactART.LinearGradient(["black", "white"])}
fill={new ReactART.LinearGradient(['black', 'white'])}
key="a"
width={50} height={50}
x={50} y={50}
width={50}
height={50}
x={50}
y={50}
opacity={0.1}
/>;
/>
);
var b =
var b = (
<Shape
fill="#3C5A99"
key="b"
scale={0.5}
x={50} y={50}
x={50}
y={50}
title="This is an F"
cursor="pointer">
M64.564,38.583H54l0.008-5.834c0-3.035,0.293-4.666,4.657-4.666
h5.833V16.429h-9.33c-11.213,0-15.159,5.654-15.159,15.16v6.994
h-6.99v11.652h6.99v33.815H54V50.235h9.331L64.564,38.583z
</Shape>;
</Shape>
);
var c = <Group key="c" />;
@@ -113,22 +116,20 @@ describe('ReactART', () => {
width: '150',
height: '200',
children: [
{ nodeName: 'defs' },
{nodeName: 'defs'},
{
nodeName: 'g',
children: [
{
nodeName: 'defs',
children: [
{ nodeName: 'linearGradient' }
]
children: [{nodeName: 'linearGradient'}],
},
{ nodeName: 'path' },
{ nodeName: 'path' },
{ nodeName: 'g' }
]
}
]
{nodeName: 'path'},
{nodeName: 'path'},
{nodeName: 'g'},
],
},
],
};
var realNode = ReactDOM.findDOMNode(instance);
@@ -137,22 +138,25 @@ describe('ReactART', () => {
it('should be able to reorder components', () => {
var container = document.createElement('div');
var instance = ReactDOM.render(<TestComponent flipped={false} />, container);
var instance = ReactDOM.render(
<TestComponent flipped={false} />,
container,
);
var expectedStructure = {
nodeName: 'svg',
children: [
{ nodeName: 'defs' },
{nodeName: 'defs'},
{
nodeName: 'g',
children: [
{ nodeName: 'defs' },
{ nodeName: 'path', opacity: '0.1' },
{ nodeName: 'path', opacity: Missing },
{ nodeName: 'g' }
]
}
]
{nodeName: 'defs'},
{nodeName: 'path', opacity: '0.1'},
{nodeName: 'path', opacity: Missing},
{nodeName: 'g'},
],
},
],
};
var realNode = ReactDOM.findDOMNode(instance);
@@ -163,17 +167,17 @@ describe('ReactART', () => {
var expectedNewStructure = {
nodeName: 'svg',
children: [
{ nodeName: 'defs' },
{nodeName: 'defs'},
{
nodeName: 'g',
children: [
{ nodeName: 'defs' },
{ nodeName: 'path', opacity: Missing },
{ nodeName: 'path', opacity: '0.1' },
{ nodeName: 'g' }
]
}
]
{nodeName: 'defs'},
{nodeName: 'path', opacity: Missing},
{nodeName: 'path', opacity: '0.1'},
{nodeName: 'g'},
],
},
],
};
testDOMNodeStructure(realNode, expectedNewStructure);
@@ -187,7 +191,7 @@ describe('ReactART', () => {
var chars = this.props.chars.split('');
return (
<Surface>
{chars.map((text) => <Shape key={text} title={text} />)}
{chars.map(text => <Shape key={text} title={text} />)}
</Surface>
);
}
@@ -225,7 +229,7 @@ describe('ReactART', () => {
<Group>
<CustomShape />
</Group>
</Surface>
</Surface>,
);
expect(mounted).toBe(true);
});
@@ -294,5 +298,4 @@ describe('ReactART', () => {
ReactDOM.render(<Outer mountCustomShape={true} />, container);
expect(ref.constructor).toBe(CustomShape);
});
});
+21 -17
View File
@@ -43,11 +43,11 @@ var ReactDOM = {
// Allows for debugging when the hook is injected on the page.
if (
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject === 'function') {
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject === 'function'
) {
__REACT_DEVTOOLS_GLOBAL_HOOK__.inject({
ComponentTree: {
getClosestInstanceFromNode:
ReactDOMComponentTree.getClosestInstanceFromNode,
getClosestInstanceFromNode: ReactDOMComponentTree.getClosestInstanceFromNode,
getNodeFromInstance: function(inst) {
// inst is an internal instance (but could be a composite)
if (inst._renderedComponent) {
@@ -68,21 +68,25 @@ if (
if (__DEV__) {
var ExecutionEnvironment = require('ExecutionEnvironment');
if (ExecutionEnvironment.canUseDOM && window.top === window.self) {
// First check if devtools is not installed
if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') {
// If we're in Chrome or Firefox, provide a download link if not installed.
if ((navigator.userAgent.indexOf('Chrome') > -1 &&
if (
(navigator.userAgent.indexOf('Chrome') > -1 &&
navigator.userAgent.indexOf('Edge') === -1) ||
navigator.userAgent.indexOf('Firefox') > -1) {
navigator.userAgent.indexOf('Firefox') > -1
) {
// Firefox does not have the issue with devtools loaded over file://
var showFileUrlMessage = window.location.protocol.indexOf('http') === -1 &&
var showFileUrlMessage =
window.location.protocol.indexOf('http') === -1 &&
navigator.userAgent.indexOf('Firefox') === -1;
console.debug(
'Download the React DevTools ' +
(showFileUrlMessage ? 'and use an HTTP server (instead of a file: URL) ' : '') +
'for a better development experience: ' +
'https://fb.me/react-devtools'
(showFileUrlMessage
? 'and use an HTTP server (instead of a file: URL) '
: '') +
'for a better development experience: ' +
'https://fb.me/react-devtools',
);
}
}
@@ -90,10 +94,10 @@ if (__DEV__) {
var testFunc = function testFn() {};
warning(
(testFunc.name || testFunc.toString()).indexOf('testFn') !== -1,
'It looks like you\'re using a minified copy of the development build ' +
'of React. When deploying React apps to production, make sure to use ' +
'the production build which skips development warnings and is faster. ' +
'See https://fb.me/react-minification for more details.'
"It looks like you're using a minified copy of the development build " +
'of React. When deploying React apps to production, make sure to use ' +
'the production build which skips development warnings and is faster. ' +
'See https://fb.me/react-minification for more details.',
);
// If we're in IE8, check to see if we are in compatibility mode and provide
@@ -104,8 +108,8 @@ if (__DEV__) {
warning(
!ieCompatibilityMode,
'Internet Explorer is running in compatibility mode; please add the ' +
'following tag to your HTML to prevent this from happening: ' +
'<meta http-equiv="X-UA-Compatible" content="IE=edge" />'
'following tag to your HTML to prevent this from happening: ' +
'<meta http-equiv="X-UA-Compatible" content="IE=edge" />',
);
var expectedFeatures = [
@@ -126,7 +130,7 @@ if (__DEV__) {
warning(
false,
'One or more ES5 shims expected by React are not available: ' +
'https://fb.me/react-warning-polyfills'
'https://fb.me/react-warning-polyfills',
);
break;
}
+3 -2
View File
@@ -13,5 +13,6 @@ var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
var useFiber = ReactDOMFeatureFlags.useFiber;
module.exports =
useFiber ? require('ReactDOMFiber') : require.requireActual('ReactDOM');
module.exports = useFiber
? require('ReactDOMFiber')
: require.requireActual('ReactDOM');
@@ -62,7 +62,7 @@ describe('ReactDOMProduction', () => {
<Component key={2}>B</Component>
<Component key={3}>C</Component>
</div>,
container
container,
);
expect(container.firstChild).toBe(inst);
@@ -75,7 +75,7 @@ describe('ReactDOMProduction', () => {
<Component key={1}>A</Component>
<Component key={3}>C</Component>
</div>,
container
container,
);
expect(inst.className).toBe('red');
@@ -119,10 +119,7 @@ describe('ReactDOMProduction', () => {
}
var container = document.createElement('div');
var inst = ReactDOM.render(
<Component x={1} />,
container
);
var inst = ReactDOM.render(<Component x={1} />, container);
expect(log).toEqual([
['componentWillMount'],
['render'],
@@ -140,15 +137,10 @@ describe('ReactDOMProduction', () => {
log = [];
inst.setState({y: 2});
expect(log).toEqual([
['shouldComponentUpdate', {x: 1}, {y: 2}],
]);
expect(log).toEqual([['shouldComponentUpdate', {x: 1}, {y: 2}]]);
log = [];
ReactDOM.render(
<Component x={2} />,
container
);
ReactDOM.render(<Component x={2} />, container);
expect(log).toEqual([
['componentWillReceiveProps', {x: 2}],
['shouldComponentUpdate', {x: 2}, {y: 2}],
@@ -158,10 +150,7 @@ describe('ReactDOMProduction', () => {
]);
log = [];
ReactDOM.render(
<Component x={2} />,
container
);
ReactDOM.render(<Component x={2} />, container);
expect(log).toEqual([
['componentWillReceiveProps', {x: 2}],
['shouldComponentUpdate', {x: 2}, {y: 2}],
@@ -169,9 +158,7 @@ describe('ReactDOMProduction', () => {
log = [];
ReactDOM.unmountComponentAtNode(container);
expect(log).toEqual([
['componentWillUnmount'],
]);
expect(log).toEqual([['componentWillUnmount']]);
});
it('should throw with an error code in production', () => {
@@ -186,9 +173,9 @@ describe('ReactDOMProduction', () => {
ReactDOM.render(<Component />, container);
}).toThrowError(
'Minified React error #109; visit ' +
'http://facebook.github.io/react/docs/error-decoder.html?invariant=109&args[]=Component' +
' for the full message or use the non-minified dev environment' +
' for full errors and additional helpful warnings.'
'http://facebook.github.io/react/docs/error-decoder.html?invariant=109&args[]=Component' +
' for the full message or use the non-minified dev environment' +
' for full errors and additional helpful warnings.',
);
});
});
@@ -84,8 +84,10 @@ var reactTopListenersCounter = 0;
var topEventMapping = {
topAbort: 'abort',
topAnimationEnd: getVendorPrefixedEventName('animationend') || 'animationend',
topAnimationIteration: getVendorPrefixedEventName('animationiteration') || 'animationiteration',
topAnimationStart: getVendorPrefixedEventName('animationstart') || 'animationstart',
topAnimationIteration: getVendorPrefixedEventName('animationiteration') ||
'animationiteration',
topAnimationStart: getVendorPrefixedEventName('animationstart') ||
'animationstart',
topBlur: 'blur',
topCanPlay: 'canplay',
topCanPlayThrough: 'canplaythrough',
@@ -142,7 +144,8 @@ var topEventMapping = {
topTouchEnd: 'touchend',
topTouchMove: 'touchmove',
topTouchStart: 'touchstart',
topTransitionEnd: getVendorPrefixedEventName('transitionend') || 'transitionend',
topTransitionEnd: getVendorPrefixedEventName('transitionend') ||
'transitionend',
topVolumeChange: 'volumechange',
topWaiting: 'waiting',
topWheel: 'wheel',
@@ -174,7 +177,6 @@ function getListeningForDocument(mountAt) {
* @internal
*/
var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, {
/**
* Injectable event backend
*/
@@ -186,7 +188,7 @@ var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, {
*/
injectReactEventListener: function(ReactEventListener) {
ReactEventListener.setHandleTopLevel(
ReactBrowserEventEmitter.handleTopLevel
ReactBrowserEventEmitter.handleTopLevel,
);
ReactBrowserEventEmitter.ReactEventListener = ReactEventListener;
},
@@ -207,10 +209,8 @@ var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, {
* @return {boolean} True if callbacks are enabled.
*/
isEnabled: function() {
return !!(
ReactBrowserEventEmitter.ReactEventListener &&
ReactBrowserEventEmitter.ReactEventListener.isEnabled()
);
return !!(ReactBrowserEventEmitter.ReactEventListener &&
ReactBrowserEventEmitter.ReactEventListener.isEnabled());
},
/**
@@ -242,22 +242,21 @@ var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, {
for (var i = 0; i < dependencies.length; i++) {
var dependency = dependencies[i];
if (!(
isListening.hasOwnProperty(dependency) &&
isListening[dependency]
)) {
if (
!(isListening.hasOwnProperty(dependency) && isListening[dependency])
) {
if (dependency === 'topWheel') {
if (isEventSupported('wheel')) {
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
'topWheel',
'wheel',
mountAt
mountAt,
);
} else if (isEventSupported('mousewheel')) {
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
'topWheel',
'mousewheel',
mountAt
mountAt,
);
} else {
// Firefox needs to capture a different mouse scroll event.
@@ -265,37 +264,34 @@ var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, {
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
'topWheel',
'DOMMouseScroll',
mountAt
mountAt,
);
}
} else if (dependency === 'topScroll') {
if (isEventSupported('scroll', true)) {
ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(
'topScroll',
'scroll',
mountAt
mountAt,
);
} else {
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
'topScroll',
'scroll',
ReactBrowserEventEmitter.ReactEventListener.WINDOW_HANDLE
ReactBrowserEventEmitter.ReactEventListener.WINDOW_HANDLE,
);
}
} else if (dependency === 'topFocus' ||
dependency === 'topBlur') {
} else if (dependency === 'topFocus' || dependency === 'topBlur') {
if (isEventSupported('focus', true)) {
ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(
'topFocus',
'focus',
mountAt
mountAt,
);
ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(
'topBlur',
'blur',
mountAt
mountAt,
);
} else if (isEventSupported('focusin')) {
// IE has `focusin` and `focusout` events which bubble.
@@ -303,12 +299,12 @@ var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, {
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
'topFocus',
'focusin',
mountAt
mountAt,
);
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
'topBlur',
'focusout',
mountAt
mountAt,
);
}
@@ -319,7 +315,7 @@ var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, {
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
dependency,
topEventMapping[dependency],
mountAt
mountAt,
);
}
@@ -332,7 +328,7 @@ var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, {
return ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
topLevelType,
handlerBaseName,
handle
handle,
);
},
@@ -340,7 +336,7 @@ var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, {
return ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(
topLevelType,
handlerBaseName,
handle
handle,
);
},
@@ -378,7 +374,6 @@ var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, {
isMonitoringScrollValue = true;
}
},
});
module.exports = ReactBrowserEventEmitter;
@@ -26,12 +26,12 @@ var internalInstanceKey =
* Check if a given node should be cached.
*/
function shouldPrecacheNode(node, nodeID) {
return (node.nodeType === 1 &&
node.getAttribute(ATTR_NAME) === String(nodeID)) ||
(node.nodeType === 8 &&
node.nodeValue === ' react-text: ' + nodeID + ' ') ||
(node.nodeType === 8 &&
node.nodeValue === ' react-empty: ' + nodeID + ' ');
return (
(node.nodeType === 1 && node.getAttribute(ATTR_NAME) === String(nodeID)) ||
(node.nodeType === 8 &&
node.nodeValue === ' react-text: ' + nodeID + ' ') ||
(node.nodeType === 8 && node.nodeValue === ' react-empty: ' + nodeID + ' ')
);
}
/**
@@ -166,7 +166,7 @@ function getNodeFromInstance(inst) {
// invariant for a missing parent, which is super confusing.
invariant(
inst._hostNode !== undefined,
'getNodeFromInstance: Invalid argument.'
'getNodeFromInstance: Invalid argument.',
);
if (inst._hostNode) {
@@ -179,7 +179,7 @@ function getNodeFromInstance(inst) {
parents.push(inst);
invariant(
inst._hostParent,
'React DOM tree root should always have a node reference.'
'React DOM tree root should always have a node reference.',
);
inst = inst._hostParent;
}
@@ -18,7 +18,6 @@ var ReactDOMComponentTree = require('ReactDOMComponentTree');
* Operations used to process updates to DOM nodes.
*/
var ReactDOMIDOperations = {
/**
* Updates a component's children by processing a series of updates.
*
@@ -99,7 +99,7 @@ function getModernOffsets(node) {
selection.anchorNode,
selection.anchorOffset,
selection.focusNode,
selection.focusOffset
selection.focusOffset,
);
var rangeLength = isSelectionCollapsed ? 0 : currentRange.toString().length;
@@ -112,7 +112,7 @@ function getModernOffsets(node) {
tempRange.startContainer,
tempRange.startOffset,
tempRange.endContainer,
tempRange.endOffset
tempRange.endOffset,
);
var start = isTempRangeCollapsed ? 0 : tempRange.toString().length;
@@ -176,8 +176,7 @@ function setModernOffsets(node, offsets) {
var selection = window.getSelection();
var length = node[getTextContentAccessor()].length;
var start = Math.min(offsets.start, length);
var end = offsets.end === undefined ?
start : Math.min(offsets.end, length);
var end = offsets.end === undefined ? start : Math.min(offsets.end, length);
// IE 11 uses modern selection, but doesn't support the extend method.
// Flip backward selections, so we can set with a single range.
@@ -205,11 +204,10 @@ function setModernOffsets(node, offsets) {
}
}
var useIEOffsets = (
var useIEOffsets =
ExecutionEnvironment.canUseDOM &&
'selection' in document &&
!('getSelection' in window)
);
!('getSelection' in window);
var ReactDOMSelection = {
/**
@@ -89,7 +89,7 @@ function traverseTwoPhase(inst, fn, arg) {
inst = inst._hostParent;
}
var i;
for (i = path.length; i-- > 0;) {
for (i = path.length; i-- > 0; ) {
fn(path[i], 'captured', arg);
}
for (i = 0; i < path.length; i++) {
@@ -120,7 +120,7 @@ function traverseEnterLeave(from, to, fn, argFrom, argTo) {
for (i = 0; i < pathFrom.length; i++) {
fn(pathFrom[i], 'bubbled', argFrom);
}
for (i = pathTo.length; i-- > 0;) {
for (i = pathTo.length; i-- > 0; ) {
fn(pathTo[i], 'captured', argTo);
}
}
@@ -52,13 +52,13 @@ Object.assign(TopLevelCallbackBookKeeping.prototype, {
});
PooledClass.addPoolingTo(
TopLevelCallbackBookKeeping,
PooledClass.twoArgumentPooler
PooledClass.twoArgumentPooler,
);
function handleTopLevelImpl(bookKeeping) {
var nativeEventTarget = getEventTarget(bookKeeping.nativeEvent);
var targetInst = ReactDOMComponentTree.getClosestInstanceFromNode(
nativeEventTarget
nativeEventTarget,
);
// Loop through the hierarchy, in case there's any nested components.
@@ -77,7 +77,7 @@ function handleTopLevelImpl(bookKeeping) {
bookKeeping.topLevelType,
targetInst,
bookKeeping.nativeEvent,
getEventTarget(bookKeeping.nativeEvent)
getEventTarget(bookKeeping.nativeEvent),
);
}
}
@@ -105,7 +105,6 @@ var ReactEventListener = {
return ReactEventListener._enabled;
},
/**
* Traps top-level events by using event bubbling.
*
@@ -123,7 +122,7 @@ var ReactEventListener = {
return EventListener.listen(
element,
handlerBaseName,
ReactEventListener.dispatchEvent.bind(null, topLevelType)
ReactEventListener.dispatchEvent.bind(null, topLevelType),
);
},
@@ -144,7 +143,7 @@ var ReactEventListener = {
return EventListener.capture(
element,
handlerBaseName,
ReactEventListener.dispatchEvent.bind(null, topLevelType)
ReactEventListener.dispatchEvent.bind(null, topLevelType),
);
},
@@ -160,7 +159,7 @@ var ReactEventListener = {
var bookKeeping = TopLevelCallbackBookKeeping.getPooled(
topLevelType,
nativeEvent
nativeEvent,
);
try {
// Event queue being processed in the same cycle allows
+18 -19
View File
@@ -28,13 +28,13 @@ function isInDocument(node) {
* Input selection module for React.
*/
var ReactInputSelection = {
hasSelectionCapabilities: function(elem) {
var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();
return nodeName && (
(nodeName === 'input' && elem.type === 'text') ||
nodeName === 'textarea' ||
elem.contentEditable === 'true'
return (
nodeName &&
((nodeName === 'input' && elem.type === 'text') ||
nodeName === 'textarea' ||
elem.contentEditable === 'true')
);
},
@@ -42,10 +42,9 @@ var ReactInputSelection = {
var focusedElem = getActiveElement();
return {
focusedElem: focusedElem,
selectionRange:
ReactInputSelection.hasSelectionCapabilities(focusedElem) ?
ReactInputSelection.getSelection(focusedElem) :
null,
selectionRange: ReactInputSelection.hasSelectionCapabilities(focusedElem)
? ReactInputSelection.getSelection(focusedElem)
: null,
};
},
@@ -58,13 +57,9 @@ var ReactInputSelection = {
var curFocusedElem = getActiveElement();
var priorFocusedElem = priorSelectionInformation.focusedElem;
var priorSelectionRange = priorSelectionInformation.selectionRange;
if (curFocusedElem !== priorFocusedElem &&
isInDocument(priorFocusedElem)) {
if (curFocusedElem !== priorFocusedElem && isInDocument(priorFocusedElem)) {
if (ReactInputSelection.hasSelectionCapabilities(priorFocusedElem)) {
ReactInputSelection.setSelection(
priorFocusedElem,
priorSelectionRange
);
ReactInputSelection.setSelection(priorFocusedElem, priorSelectionRange);
}
focusNode(priorFocusedElem);
}
@@ -85,8 +80,10 @@ var ReactInputSelection = {
start: input.selectionStart,
end: input.selectionEnd,
};
} else if (document.selection &&
(input.nodeName && input.nodeName.toLowerCase() === 'input')) {
} else if (
document.selection &&
(input.nodeName && input.nodeName.toLowerCase() === 'input')
) {
// IE8 input.
var range = document.selection.createRange();
// There can only be one selection per document in IE, so it must
@@ -121,8 +118,10 @@ var ReactInputSelection = {
if ('selectionStart' in input) {
input.selectionStart = start;
input.selectionEnd = Math.min(end, input.value.length);
} else if (document.selection &&
(input.nodeName && input.nodeName.toLowerCase() === 'input')) {
} else if (
document.selection &&
(input.nodeName && input.nodeName.toLowerCase() === 'input')
) {
var range = input.createTextRange();
range.collapse(true);
range.moveStart('character', start);
+145 -122
View File
@@ -80,7 +80,7 @@ function internalGetID(node) {
// If node is something like a window, document, or text node, none of
// which support attributes or a .getAttribute method, gracefully return
// the empty string, as if the attribute were missing.
return node.getAttribute && node.getAttribute(ATTR_NAME) || '';
return (node.getAttribute && node.getAttribute(ATTR_NAME)) || '';
}
/**
@@ -96,16 +96,15 @@ function mountComponentIntoNode(
container,
transaction,
shouldReuseMarkup,
context
context,
) {
var markerName;
if (ReactFeatureFlags.logTopLevelRenders) {
var wrappedElement = wrapperInstance._currentElement.props.child;
var type = wrappedElement.type;
markerName = 'React mount: ' + (
typeof type === 'string' ? type :
type.displayName || type.name
);
markerName =
'React mount: ' +
(typeof type === 'string' ? type : type.displayName || type.name);
console.time(markerName);
}
@@ -115,7 +114,7 @@ function mountComponentIntoNode(
null,
ReactDOMContainerInfo(wrapperInstance, container),
context,
0 /* parentDebugID */
0 /* parentDebugID */,
);
if (markerName) {
@@ -128,7 +127,7 @@ function mountComponentIntoNode(
container,
wrapperInstance,
shouldReuseMarkup,
transaction
transaction,
);
}
@@ -143,11 +142,11 @@ function batchedMountComponentIntoNode(
componentInstance,
container,
shouldReuseMarkup,
context
context,
) {
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(
/* useCreateElement */
!shouldReuseMarkup && ReactDOMFeatureFlags.useCreateElement
!shouldReuseMarkup && ReactDOMFeatureFlags.useCreateElement,
);
transaction.perform(
mountComponentIntoNode,
@@ -156,7 +155,7 @@ function batchedMountComponentIntoNode(
container,
transaction,
shouldReuseMarkup,
context
context,
);
ReactUpdates.ReactReconcileTransaction.release(transaction);
}
@@ -217,7 +216,9 @@ function hasNonRootReactChild(container) {
*/
function nodeIsRenderedByOtherInstance(container) {
var rootEl = getReactRootElementInContainer(container);
return !!(rootEl && isReactNode(rootEl) && !ReactDOMComponentTree.getInstanceFromNode(rootEl));
return !!(rootEl &&
isReactNode(rootEl) &&
!ReactDOMComponentTree.getInstanceFromNode(rootEl));
}
/**
@@ -228,11 +229,10 @@ function nodeIsRenderedByOtherInstance(container) {
* @internal
*/
function isValidContainer(node) {
return !!(node && (
node.nodeType === ELEMENT_NODE_TYPE ||
node.nodeType === DOC_NODE_TYPE ||
node.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE
));
return !!(node &&
(node.nodeType === ELEMENT_NODE_TYPE ||
node.nodeType === DOC_NODE_TYPE ||
node.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE));
}
/**
@@ -243,17 +243,19 @@ function isValidContainer(node) {
* @internal
*/
function isReactNode(node) {
return isValidContainer(node) && (node.hasAttribute(ROOT_ATTR_NAME) || node.hasAttribute(ATTR_NAME));
return (
isValidContainer(node) &&
(node.hasAttribute(ROOT_ATTR_NAME) || node.hasAttribute(ATTR_NAME))
);
}
function getHostRootInstanceInContainer(container) {
var rootEl = getReactRootElementInContainer(container);
var prevHostInstance =
rootEl && ReactDOMComponentTree.getInstanceFromNode(rootEl);
return (
prevHostInstance && !prevHostInstance._hostParent ?
prevHostInstance : null
);
return prevHostInstance && !prevHostInstance._hostParent
? prevHostInstance
: null;
}
function getTopLevelWrapperInContainer(container) {
@@ -298,7 +300,6 @@ TopLevelWrapper.isReactTopLevelWrapper = true;
* Inside of `container`, the first element rendered is the "reactRoot".
*/
var ReactMount = {
TopLevelWrapper: TopLevelWrapper,
/**
@@ -326,13 +327,18 @@ var ReactMount = {
* @param {?function} callback function triggered on completion
*/
_updateRootComponent: function(
prevComponent,
nextElement,
nextContext,
container,
callback) {
prevComponent,
nextElement,
nextContext,
container,
callback,
) {
ReactMount.scrollMonitor(container, function() {
ReactUpdateQueue.enqueueElementInternal(prevComponent, nextElement, nextContext);
ReactUpdateQueue.enqueueElementInternal(
prevComponent,
nextElement,
nextContext,
);
if (callback) {
ReactUpdateQueue.enqueueCallbackInternal(prevComponent, callback);
}
@@ -353,7 +359,7 @@ var ReactMount = {
nextElement,
container,
shouldReuseMarkup,
context
context,
) {
// Various parts of our code (such as ReactCompositeComponent's
// _renderValidatedComponent) assume that calls to render aren't nested;
@@ -361,16 +367,16 @@ var ReactMount = {
warning(
ReactCurrentOwner.current == null,
'_renderNewRootComponent(): Render methods should be a pure function ' +
'of props and state; triggering nested component updates from ' +
'render is not allowed. If necessary, trigger nested updates in ' +
'componentDidUpdate. Check the render method of %s.',
ReactCurrentOwner.current && ReactCurrentOwner.current.getName() ||
'ReactCompositeComponent'
'of props and state; triggering nested component updates from ' +
'render is not allowed. If necessary, trigger nested updates in ' +
'componentDidUpdate. Check the render method of %s.',
(ReactCurrentOwner.current && ReactCurrentOwner.current.getName()) ||
'ReactCompositeComponent',
);
invariant(
isValidContainer(container),
'_registerComponent(...): Target container is not a DOM element.'
'_registerComponent(...): Target container is not a DOM element.',
);
ReactBrowserEventEmitter.ensureScrollValueMonitoring();
@@ -385,7 +391,7 @@ var ReactMount = {
componentInstance,
container,
shouldReuseMarkup,
context
context,
);
var wrapperID = componentInstance._instance.rootID;
@@ -407,53 +413,61 @@ var ReactMount = {
* @param {?function} callback function triggered on completion
* @return {ReactComponent} Component instance rendered in `container`.
*/
renderSubtreeIntoContainer: function(parentComponent, nextElement, container, callback) {
renderSubtreeIntoContainer: function(
parentComponent,
nextElement,
container,
callback,
) {
invariant(
parentComponent != null && ReactInstanceMap.has(parentComponent),
'parentComponent must be a valid React Component'
'parentComponent must be a valid React Component',
);
return ReactMount._renderSubtreeIntoContainer(
parentComponent,
nextElement,
container,
callback
callback,
);
},
_renderSubtreeIntoContainer: function(parentComponent, nextElement, container, callback) {
_renderSubtreeIntoContainer: function(
parentComponent,
nextElement,
container,
callback,
) {
ReactUpdateQueue.validateCallback(callback, 'ReactDOM.render');
invariant(
React.isValidElement(nextElement),
'ReactDOM.render(): Invalid component element.%s',
(
typeof nextElement === 'string' ?
' Instead of passing a string like \'div\', pass ' +
'React.createElement(\'div\') or <div />.' :
typeof nextElement === 'function' ?
' Instead of passing a class like Foo, pass ' +
'React.createElement(Foo) or <Foo />.' :
// Check if it quacks like an element
nextElement != null && nextElement.props !== undefined ?
' This may be caused by unintentionally loading two independent ' +
'copies of React.' :
''
)
typeof nextElement === 'string'
? " Instead of passing a string like 'div', pass " +
"React.createElement('div') or <div />."
: typeof nextElement === 'function'
? ' Instead of passing a class like Foo, pass ' +
'React.createElement(Foo) or <Foo />.'
: // Check if it quacks like an element
nextElement != null && nextElement.props !== undefined
? ' This may be caused by unintentionally loading two independent ' +
'copies of React.'
: '',
);
warning(
!container || !container.tagName ||
container.tagName.toUpperCase() !== 'BODY',
!container ||
!container.tagName ||
container.tagName.toUpperCase() !== 'BODY',
'render(): Rendering components directly into document.body is ' +
'discouraged, since its children are often manipulated by third-party ' +
'scripts and browser extensions. This may lead to subtle ' +
'reconciliation issues. Try rendering into a container element created ' +
'for your app.'
'discouraged, since its children are often manipulated by third-party ' +
'scripts and browser extensions. This may lead to subtle ' +
'reconciliation issues. Try rendering into a container element created ' +
'for your app.',
);
var nextWrappedElement = React.createElement(
TopLevelWrapper,
{ child: nextElement }
);
var nextWrappedElement = React.createElement(TopLevelWrapper, {
child: nextElement,
});
var nextContext;
if (parentComponent) {
@@ -470,15 +484,17 @@ var ReactMount = {
var prevElement = prevWrappedElement.props.child;
if (shouldUpdateReactComponent(prevElement, nextElement)) {
var publicInst = prevComponent._renderedComponent.getPublicInstance();
var updatedCallback = callback && function() {
callback.call(publicInst);
};
var updatedCallback =
callback &&
function() {
callback.call(publicInst);
};
ReactMount._updateRootComponent(
prevComponent,
nextWrappedElement,
nextContext,
container,
updatedCallback
updatedCallback,
);
return publicInst;
} else {
@@ -495,9 +511,9 @@ var ReactMount = {
warning(
!containerHasNonRootReactChild,
'render(...): Replacing React-rendered children with a new root ' +
'component. If you intended to update the children of this node, ' +
'you should instead have the existing children update their state ' +
'and render the new components instead of calling ReactDOM.render.'
'component. If you intended to update the children of this node, ' +
'you should instead have the existing children update their state ' +
'and render the new components instead of calling ReactDOM.render.',
);
if (!containerHasReactMarkup || reactRootElement.nextSibling) {
@@ -507,8 +523,8 @@ var ReactMount = {
warning(
false,
'render(): Target node has markup rendered by React, but there ' +
'are unrelated nodes as well. This is most commonly caused by ' +
'white-space inserted around server-rendered markup.'
'are unrelated nodes as well. This is most commonly caused by ' +
'white-space inserted around server-rendered markup.',
);
break;
}
@@ -525,7 +541,7 @@ var ReactMount = {
nextWrappedElement,
container,
shouldReuseMarkup,
nextContext
nextContext,
)._renderedComponent.getPublicInstance();
if (callback) {
callback.call(component);
@@ -533,7 +549,6 @@ var ReactMount = {
return component;
},
/**
* Renders a React component into the DOM in the supplied `container`.
* See https://facebook.github.io/react/docs/top-level-api.html#reactdom.render
@@ -548,7 +563,12 @@ var ReactMount = {
* @return {ReactComponent} Component instance rendered in `container`.
*/
render: function(nextElement, container, callback) {
return ReactMount._renderSubtreeIntoContainer(null, nextElement, container, callback);
return ReactMount._renderSubtreeIntoContainer(
null,
nextElement,
container,
callback,
);
},
/**
@@ -567,23 +587,23 @@ var ReactMount = {
warning(
ReactCurrentOwner.current == null,
'unmountComponentAtNode(): Render methods should be a pure function ' +
'of props and state; triggering nested component updates from render ' +
'is not allowed. If necessary, trigger nested updates in ' +
'componentDidUpdate. Check the render method of %s.',
ReactCurrentOwner.current && ReactCurrentOwner.current.getName() ||
'ReactCompositeComponent'
'of props and state; triggering nested component updates from render ' +
'is not allowed. If necessary, trigger nested updates in ' +
'componentDidUpdate. Check the render method of %s.',
(ReactCurrentOwner.current && ReactCurrentOwner.current.getName()) ||
'ReactCompositeComponent',
);
invariant(
isValidContainer(container),
'unmountComponentAtNode(...): Target container is not a DOM element.'
'unmountComponentAtNode(...): Target container is not a DOM element.',
);
if (__DEV__) {
warning(
!nodeIsRenderedByOtherInstance(container),
'unmountComponentAtNode(): The node you\'re attempting to unmount ' +
'was rendered by another copy of React.'
"unmountComponentAtNode(): The node you're attempting to unmount " +
'was rendered by another copy of React.',
);
}
@@ -600,15 +620,13 @@ var ReactMount = {
if (__DEV__) {
warning(
!containerHasNonRootReactChild,
'unmountComponentAtNode(): The node you\'re attempting to unmount ' +
'was rendered by React and is not a top-level container. %s',
(
isContainerReactRoot ?
'You may have accidentally passed in a React root node instead ' +
'of its container.' :
'Instead, have the parent component update its state and ' +
'rerender in order to remove this component.'
)
"unmountComponentAtNode(): The node you're attempting to unmount " +
'was rendered by React and is not a top-level container. %s',
isContainerReactRoot
? 'You may have accidentally passed in a React root node instead ' +
'of its container.'
: 'Instead, have the parent component update its state and ' +
'rerender in order to remove this component.',
);
}
@@ -619,7 +637,7 @@ var ReactMount = {
unmountComponentFromNode,
prevComponent,
container,
false
false,
);
return true;
},
@@ -629,11 +647,11 @@ var ReactMount = {
container,
instance,
shouldReuseMarkup,
transaction
transaction,
) {
invariant(
isValidContainer(container),
'mountComponentIntoNode(...): Target container is not valid.'
'mountComponentIntoNode(...): Target container is not valid.',
);
if (shouldReuseMarkup) {
@@ -643,14 +661,14 @@ var ReactMount = {
return;
} else {
var checksum = rootElement.getAttribute(
ReactMarkupChecksum.CHECKSUM_ATTR_NAME
ReactMarkupChecksum.CHECKSUM_ATTR_NAME,
);
rootElement.removeAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME);
var rootMarkup = rootElement.outerHTML;
rootElement.setAttribute(
ReactMarkupChecksum.CHECKSUM_ATTR_NAME,
checksum
checksum,
);
var normalizedMarkup = markup;
@@ -668,41 +686,44 @@ var ReactMount = {
normalizer = document.createElement('iframe');
document.body.appendChild(normalizer);
normalizer.contentDocument.write(markup);
normalizedMarkup = normalizer.contentDocument.documentElement.outerHTML;
normalizedMarkup =
normalizer.contentDocument.documentElement.outerHTML;
document.body.removeChild(normalizer);
}
}
var diffIndex = firstDifferenceIndex(normalizedMarkup, rootMarkup);
var difference = ' (client) ' +
var difference =
' (client) ' +
normalizedMarkup.substring(diffIndex - 20, diffIndex + 20) +
'\n (server) ' + rootMarkup.substring(diffIndex - 20, diffIndex + 20);
'\n (server) ' +
rootMarkup.substring(diffIndex - 20, diffIndex + 20);
invariant(
container.nodeType !== DOC_NODE_TYPE,
'You\'re trying to render a component to the document using ' +
'server rendering but the checksum was invalid. This usually ' +
'means you rendered a different component type or props on ' +
'the client from the one on the server, or your render() ' +
'methods are impure. React cannot handle this case due to ' +
'cross-browser quirks by rendering at the document root. You ' +
'should look for environment dependent code in your components ' +
'and ensure the props are the same client and server side:\n%s',
difference
"You're trying to render a component to the document using " +
'server rendering but the checksum was invalid. This usually ' +
'means you rendered a different component type or props on ' +
'the client from the one on the server, or your render() ' +
'methods are impure. React cannot handle this case due to ' +
'cross-browser quirks by rendering at the document root. You ' +
'should look for environment dependent code in your components ' +
'and ensure the props are the same client and server side:\n%s',
difference,
);
if (__DEV__) {
warning(
false,
'React attempted to reuse markup in a container but the ' +
'checksum was invalid. This generally means that you are ' +
'using server rendering and the markup generated on the ' +
'server was not what the client was expecting. React injected ' +
'new markup to compensate which works but you have lost many ' +
'of the benefits of server rendering. Instead, figure out ' +
'why the markup being generated is different on the client ' +
'or server:\n%s',
difference
'checksum was invalid. This generally means that you are ' +
'using server rendering and the markup generated on the ' +
'server was not what the client was expecting. React injected ' +
'new markup to compensate which works but you have lost many ' +
'of the benefits of server rendering. Instead, figure out ' +
'why the markup being generated is different on the client ' +
'or server:\n%s',
difference,
);
}
}
@@ -710,10 +731,10 @@ var ReactMount = {
invariant(
container.nodeType !== DOC_NODE_TYPE,
'You\'re trying to render a component to the document but ' +
'you didn\'t use server rendering. We can\'t do this ' +
"You're trying to render a component to the document but " +
"you didn't use server rendering. We can't do this " +
'without using server rendering due to cross-browser quirks. ' +
'See ReactDOMServer.renderToString() for server rendering.'
'See ReactDOMServer.renderToString() for server rendering.',
);
if (transaction.useCreateElement) {
@@ -727,7 +748,9 @@ var ReactMount = {
}
if (__DEV__) {
var hostNode = ReactDOMComponentTree.getInstanceFromNode(container.firstChild);
var hostNode = ReactDOMComponentTree.getInstanceFromNode(
container.firstChild,
);
if (hostNode._debugID !== 0) {
ReactInstrumentation.debugTool.onHostOperation({
instanceID: hostNode._debugID,
@@ -19,7 +19,6 @@ var ReactInstrumentation = require('ReactInstrumentation');
var Transaction = require('Transaction');
var ReactUpdateQueue = require('ReactUpdateQueue');
/**
* Ensures that, when possible, the selection range (currently selected text
* input) is not disturbed by performing the transaction.
@@ -174,7 +173,6 @@ var Mixin = {
},
};
Object.assign(ReactReconcileTransaction.prototype, Transaction, Mixin);
PooledClass.addPoolingTo(ReactReconcileTransaction);
@@ -54,7 +54,6 @@ function getInternal(node) {
return ReactDOMComponentTree.getInstanceFromNode(node);
}
describe('ReactBrowserEventEmitter', () => {
beforeEach(() => {
jest.resetModuleRegistry();
@@ -69,11 +68,11 @@ describe('ReactBrowserEventEmitter', () => {
TapEventPlugin = require('TapEventPlugin');
ReactTestUtils.renderIntoDocument(
<div ref={(c) => GRANDPARENT = c}>
<div ref={(c) => PARENT = c}>
<div ref={(c) => CHILD = c} />
<div ref={c => (GRANDPARENT = c)}>
<div ref={c => (PARENT = c)}>
<div ref={c => (CHILD = c)} />
</div>
</div>
</div>,
);
idCallOrder = [];
@@ -108,34 +107,31 @@ describe('ReactBrowserEventEmitter', () => {
expect(LISTENER.mock.calls.length).toBe(1);
});
it(
'should not invoke handlers if ReactBrowserEventEmitter is disabled',
() => {
registerSimpleTestHandler();
ReactBrowserEventEmitter.setEnabled(false);
ReactTestUtils.SimulateNative.click(CHILD);
expect(LISTENER.mock.calls.length).toBe(0);
ReactBrowserEventEmitter.setEnabled(true);
ReactTestUtils.SimulateNative.click(CHILD);
expect(LISTENER.mock.calls.length).toBe(1);
}
);
it('should not invoke handlers if ReactBrowserEventEmitter is disabled', () => {
registerSimpleTestHandler();
ReactBrowserEventEmitter.setEnabled(false);
ReactTestUtils.SimulateNative.click(CHILD);
expect(LISTENER.mock.calls.length).toBe(0);
ReactBrowserEventEmitter.setEnabled(true);
ReactTestUtils.SimulateNative.click(CHILD);
expect(LISTENER.mock.calls.length).toBe(1);
});
it('should bubble simply', () => {
EventPluginHub.putListener(
getInternal(CHILD),
ON_CLICK_KEY,
recordID.bind(null, getInternal(CHILD))
recordID.bind(null, getInternal(CHILD)),
);
EventPluginHub.putListener(
getInternal(PARENT),
ON_CLICK_KEY,
recordID.bind(null, getInternal(PARENT))
recordID.bind(null, getInternal(PARENT)),
);
EventPluginHub.putListener(
getInternal(GRANDPARENT),
ON_CLICK_KEY,
recordID.bind(null, getInternal(GRANDPARENT))
recordID.bind(null, getInternal(GRANDPARENT)),
);
ReactTestUtils.Simulate.click(CHILD);
expect(idCallOrder.length).toBe(3);
@@ -148,20 +144,16 @@ describe('ReactBrowserEventEmitter', () => {
EventPluginHub.putListener(
getInternal(CHILD),
ON_CLICK_KEY,
recordID.bind(null, getInternal(CHILD))
);
EventPluginHub.putListener(
getInternal(PARENT),
ON_CLICK_KEY,
function() {
recordID(getInternal(PARENT));
throw new Error('Handler interrupted');
}
recordID.bind(null, getInternal(CHILD)),
);
EventPluginHub.putListener(getInternal(PARENT), ON_CLICK_KEY, function() {
recordID(getInternal(PARENT));
throw new Error('Handler interrupted');
});
EventPluginHub.putListener(
getInternal(GRANDPARENT),
ON_CLICK_KEY,
recordID.bind(null, getInternal(GRANDPARENT))
recordID.bind(null, getInternal(GRANDPARENT)),
);
expect(function() {
ReactTestUtils.Simulate.click(CHILD);
@@ -173,30 +165,24 @@ describe('ReactBrowserEventEmitter', () => {
});
it('should set currentTarget', () => {
EventPluginHub.putListener(
getInternal(CHILD),
ON_CLICK_KEY,
function(event) {
recordID(getInternal(CHILD));
expect(event.currentTarget).toBe(CHILD);
}
);
EventPluginHub.putListener(
getInternal(PARENT),
ON_CLICK_KEY,
function(event) {
recordID(getInternal(PARENT));
expect(event.currentTarget).toBe(PARENT);
}
);
EventPluginHub.putListener(
getInternal(GRANDPARENT),
ON_CLICK_KEY,
function(event) {
recordID(getInternal(GRANDPARENT));
expect(event.currentTarget).toBe(GRANDPARENT);
}
);
EventPluginHub.putListener(getInternal(CHILD), ON_CLICK_KEY, function(
event,
) {
recordID(getInternal(CHILD));
expect(event.currentTarget).toBe(CHILD);
});
EventPluginHub.putListener(getInternal(PARENT), ON_CLICK_KEY, function(
event,
) {
recordID(getInternal(PARENT));
expect(event.currentTarget).toBe(PARENT);
});
EventPluginHub.putListener(getInternal(GRANDPARENT), ON_CLICK_KEY, function(
event,
) {
recordID(getInternal(GRANDPARENT));
expect(event.currentTarget).toBe(GRANDPARENT);
});
ReactTestUtils.Simulate.click(CHILD);
expect(idCallOrder.length).toBe(3);
expect(idCallOrder[0]).toBe(getInternal(CHILD));
@@ -208,17 +194,17 @@ describe('ReactBrowserEventEmitter', () => {
EventPluginHub.putListener(
getInternal(CHILD),
ON_CLICK_KEY,
recordID.bind(null, getInternal(CHILD))
recordID.bind(null, getInternal(CHILD)),
);
EventPluginHub.putListener(
getInternal(PARENT),
ON_CLICK_KEY,
recordIDAndStopPropagation.bind(null, getInternal(PARENT))
recordIDAndStopPropagation.bind(null, getInternal(PARENT)),
);
EventPluginHub.putListener(
getInternal(GRANDPARENT),
ON_CLICK_KEY,
recordID.bind(null, getInternal(GRANDPARENT))
recordID.bind(null, getInternal(GRANDPARENT)),
);
ReactTestUtils.Simulate.click(CHILD);
expect(idCallOrder.length).toBe(2);
@@ -230,17 +216,17 @@ describe('ReactBrowserEventEmitter', () => {
EventPluginHub.putListener(
getInternal(CHILD),
ON_CLICK_KEY,
recordIDAndStopPropagation.bind(null, getInternal(CHILD))
recordIDAndStopPropagation.bind(null, getInternal(CHILD)),
);
EventPluginHub.putListener(
getInternal(PARENT),
ON_CLICK_KEY,
recordID.bind(null, getInternal(PARENT))
recordID.bind(null, getInternal(PARENT)),
);
EventPluginHub.putListener(
getInternal(GRANDPARENT),
ON_CLICK_KEY,
recordID.bind(null, getInternal(GRANDPARENT))
recordID.bind(null, getInternal(GRANDPARENT)),
);
ReactTestUtils.Simulate.click(CHILD);
expect(idCallOrder.length).toBe(1);
@@ -251,17 +237,17 @@ describe('ReactBrowserEventEmitter', () => {
EventPluginHub.putListener(
getInternal(CHILD),
ON_CLICK_KEY,
recordIDAndReturnFalse.bind(null, getInternal(CHILD))
recordIDAndReturnFalse.bind(null, getInternal(CHILD)),
);
EventPluginHub.putListener(
getInternal(PARENT),
ON_CLICK_KEY,
recordID.bind(null, getInternal(PARENT))
recordID.bind(null, getInternal(PARENT)),
);
EventPluginHub.putListener(
getInternal(GRANDPARENT),
ON_CLICK_KEY,
recordID.bind(null, getInternal(GRANDPARENT))
recordID.bind(null, getInternal(GRANDPARENT)),
);
spyOn(console, 'error');
ReactTestUtils.Simulate.click(CHILD);
@@ -289,12 +275,12 @@ describe('ReactBrowserEventEmitter', () => {
EventPluginHub.putListener(
getInternal(CHILD),
ON_CLICK_KEY,
handleChildClick
handleChildClick,
);
EventPluginHub.putListener(
getInternal(PARENT),
ON_CLICK_KEY,
handleParentClick
handleParentClick,
);
ReactTestUtils.Simulate.click(CHILD);
expect(handleParentClick.mock.calls.length).toBe(1);
@@ -306,13 +292,13 @@ describe('ReactBrowserEventEmitter', () => {
EventPluginHub.putListener(
getInternal(PARENT),
ON_CLICK_KEY,
handleParentClick
handleParentClick,
);
};
EventPluginHub.putListener(
getInternal(CHILD),
ON_CLICK_KEY,
handleChildClick
handleChildClick,
);
ReactTestUtils.Simulate.click(CHILD);
expect(handleParentClick.mock.calls.length).toBe(0);
@@ -322,7 +308,7 @@ describe('ReactBrowserEventEmitter', () => {
EventPluginHub.putListener(
getInternal(CHILD),
ON_MOUSE_ENTER_KEY,
recordID.bind(null, getInternal(CHILD))
recordID.bind(null, getInternal(CHILD)),
);
ReactTestUtils.Simulate.mouseEnter(CHILD);
expect(idCallOrder.length).toBe(1);
@@ -333,15 +319,15 @@ describe('ReactBrowserEventEmitter', () => {
EventPluginHub.putListener(
getInternal(CHILD),
ON_TOUCH_TAP_KEY,
recordID.bind(null, getInternal(CHILD))
recordID.bind(null, getInternal(CHILD)),
);
ReactTestUtils.SimulateNative.touchStart(
CHILD,
ReactTestUtils.nativeTouchData(0, 0)
ReactTestUtils.nativeTouchData(0, 0),
);
ReactTestUtils.SimulateNative.touchEnd(
CHILD,
ReactTestUtils.nativeTouchData(0, 0)
ReactTestUtils.nativeTouchData(0, 0),
);
expect(idCallOrder.length).toBe(1);
expect(idCallOrder[0]).toBe(getInternal(CHILD));
@@ -351,15 +337,15 @@ describe('ReactBrowserEventEmitter', () => {
EventPluginHub.putListener(
getInternal(CHILD),
ON_TOUCH_TAP_KEY,
recordID.bind(null, getInternal(CHILD))
recordID.bind(null, getInternal(CHILD)),
);
ReactTestUtils.SimulateNative.touchStart(
CHILD,
ReactTestUtils.nativeTouchData(0, 0)
ReactTestUtils.nativeTouchData(0, 0),
);
ReactTestUtils.SimulateNative.touchEnd(
CHILD,
ReactTestUtils.nativeTouchData(0, tapMoveThreshold - 1)
ReactTestUtils.nativeTouchData(0, tapMoveThreshold - 1),
);
expect(idCallOrder.length).toBe(1);
expect(idCallOrder[0]).toBe(getInternal(CHILD));
@@ -369,15 +355,15 @@ describe('ReactBrowserEventEmitter', () => {
EventPluginHub.putListener(
getInternal(CHILD),
ON_TOUCH_TAP_KEY,
recordID.bind(null, getInternal(CHILD))
recordID.bind(null, getInternal(CHILD)),
);
ReactTestUtils.SimulateNative.touchStart(
CHILD,
ReactTestUtils.nativeTouchData(0, 0)
ReactTestUtils.nativeTouchData(0, 0),
);
ReactTestUtils.SimulateNative.touchEnd(
CHILD,
ReactTestUtils.nativeTouchData(0, tapMoveThreshold + 1)
ReactTestUtils.nativeTouchData(0, tapMoveThreshold + 1),
);
expect(idCallOrder.length).toBe(0);
});
@@ -426,25 +412,25 @@ describe('ReactBrowserEventEmitter', () => {
EventPluginHub.putListener(
getInternal(CHILD),
ON_TOUCH_TAP_KEY,
recordID.bind(null, getInternal(CHILD))
recordID.bind(null, getInternal(CHILD)),
);
EventPluginHub.putListener(
getInternal(PARENT),
ON_TOUCH_TAP_KEY,
recordID.bind(null, getInternal(PARENT))
recordID.bind(null, getInternal(PARENT)),
);
EventPluginHub.putListener(
getInternal(GRANDPARENT),
ON_TOUCH_TAP_KEY,
recordID.bind(null, getInternal(GRANDPARENT))
recordID.bind(null, getInternal(GRANDPARENT)),
);
ReactTestUtils.SimulateNative.touchStart(
CHILD,
ReactTestUtils.nativeTouchData(0, 0)
ReactTestUtils.nativeTouchData(0, 0),
);
ReactTestUtils.SimulateNative.touchEnd(
CHILD,
ReactTestUtils.nativeTouchData(0, 0)
ReactTestUtils.nativeTouchData(0, 0),
);
expect(idCallOrder.length).toBe(3);
expect(idCallOrder[0]).toBe(getInternal(CHILD));
@@ -467,5 +453,4 @@ describe('ReactBrowserEventEmitter', () => {
document.createEvent = originalCreateEvent;
}
});
});
@@ -53,16 +53,14 @@ describe('ReactDOM', () => {
});
it('should allow children to be passed as an argument', () => {
var argDiv = ReactTestUtils.renderIntoDocument(
div(null, 'child')
);
var argDiv = ReactTestUtils.renderIntoDocument(div(null, 'child'));
var argNode = ReactDOM.findDOMNode(argDiv);
expect(argNode.innerHTML).toBe('child');
});
it('should overwrite props.children with children argument', () => {
var conflictDiv = ReactTestUtils.renderIntoDocument(
div({children: 'fakechild'}, 'child')
div({children: 'fakechild'}, 'child'),
);
var conflictNode = ReactDOM.findDOMNode(conflictDiv);
expect(conflictNode.innerHTML).toBe('child');
@@ -77,45 +75,45 @@ describe('ReactDOM', () => {
<div>
<div key="theDog" className="dog" />,
<div key="theBird" className="bird" />
</div>
</div>,
);
// Warm the cache with theDog
myDiv = ReactTestUtils.renderIntoDocument(
<div>
<div key="theDog" className="dogbeforedelete" />,
<div key="theBird" className="bird" />,
</div>
</div>,
);
// Remove theDog - this should purge the cache
myDiv = ReactTestUtils.renderIntoDocument(
<div>
<div key="theBird" className="bird" />,
</div>
</div>,
);
// Now, put theDog back. It's now a different DOM node.
myDiv = ReactTestUtils.renderIntoDocument(
<div>
<div key="theDog" className="dog" />,
<div key="theBird" className="bird" />,
</div>
</div>,
);
// Change the className of theDog. It will use the same element
myDiv = ReactTestUtils.renderIntoDocument(
<div>
<div key="theDog" className="bigdog" />,
<div key="theBird" className="bird" />,
</div>
</div>,
);
var root = ReactDOM.findDOMNode(myDiv);
var dog = root.childNodes[0];
expect(dog.className).toBe('bigdog');
});
it('allow React.DOM factories to be called without warnings', () => {
spyOn(console, 'error');
it('throws warning when React.DOM factories are called', () => {
spyOn(console, 'warn');
var element = React.DOM.div();
expect(element.type).toBe('div');
expect(console.error.calls.count()).toBe(0);
expect(console.warn.calls.count()).toBe(1);
});
it('throws in render() if the mount callback is not a function', () => {
@@ -135,15 +133,15 @@ describe('ReactDOM', () => {
var myDiv = document.createElement('div');
expect(() => ReactDOM.render(<A />, myDiv, 'no')).toThrowError(
'ReactDOM.render(...): Expected the last optional `callback` argument ' +
'to be a function. Instead received: string.'
'to be a function. Instead received: string.',
);
expect(() => ReactDOM.render(<A />, myDiv, {})).toThrowError(
'ReactDOM.render(...): Expected the last optional `callback` argument ' +
'to be a function. Instead received: Object.'
'to be a function. Instead received: Object.',
);
expect(() => ReactDOM.render(<A />, myDiv, new Foo())).toThrowError(
'ReactDOM.render(...): Expected the last optional `callback` argument ' +
'to be a function. Instead received: Foo (keys: a, b).'
'to be a function. Instead received: Foo (keys: a, b).',
);
});
@@ -166,15 +164,15 @@ describe('ReactDOM', () => {
expect(() => ReactDOM.render(<A />, myDiv, 'no')).toThrowError(
'ReactDOM.render(...): Expected the last optional `callback` argument ' +
'to be a function. Instead received: string.'
'to be a function. Instead received: string.',
);
expect(() => ReactDOM.render(<A />, myDiv, {})).toThrowError(
'ReactDOM.render(...): Expected the last optional `callback` argument ' +
'to be a function. Instead received: Object.'
'to be a function. Instead received: Object.',
);
expect(() => ReactDOM.render(<A />, myDiv, new Foo())).toThrowError(
'ReactDOM.render(...): Expected the last optional `callback` argument ' +
'to be a function. Instead received: Foo (keys: a, b).'
'to be a function. Instead received: Foo (keys: a, b).',
);
});
});
@@ -88,7 +88,7 @@ describe('ReactDOMComponentTree', () => {
function renderAndGetClosest(sel) {
return ReactDOMComponentTree.getClosestInstanceFromNode(
renderAndQuery(sel)
renderAndQuery(sel),
);
}
@@ -101,11 +101,12 @@ describe('ReactDOMComponentTree', () => {
// This one's a text component!
var root = renderAndQuery(null);
var inst = ReactDOMComponentTree.getInstanceFromNode(root.children[0].childNodes[2]);
var inst = ReactDOMComponentTree.getInstanceFromNode(
root.children[0].childNodes[2],
);
expect(inst._stringText).toBe('goodbye.');
expect(renderAndGetClosest('b')._currentElement.type).toBe('main');
expect(renderAndGetClosest('img')._currentElement.type).toBe('main');
});
});
@@ -23,13 +23,15 @@ describe('ReactDOMIDOperations', () => {
var html = '\n \t <span> \n testContent \t </span> \n \t';
ReactDOMIDOperations.dangerouslyProcessChildrenUpdates(
stubInstance,
[{
type: 'SET_MARKUP',
content: html,
fromIndex: null,
toIndex: null,
}],
[]
[
{
type: 'SET_MARKUP',
content: html,
fromIndex: null,
toIndex: null,
},
],
[],
);
expect(stubNode.innerHTML).toBe(html);
@@ -15,7 +15,6 @@ var React;
var ReactDOMServer;
describe('ReactDOMSVG', () => {
beforeEach(() => {
React = require('React');
ReactDOMServer = require('ReactDOMServer');
@@ -25,9 +24,8 @@ describe('ReactDOMSVG', () => {
var markup = ReactDOMServer.renderToString(
<svg>
<image xlinkHref="http://i.imgur.com/w7GCRPb.png" />
</svg>
</svg>,
);
expect(markup).toContain('xlink:href="http://i.imgur.com/w7GCRPb.png"');
});
});
@@ -113,7 +113,11 @@ describe('ReactDOMTreeTraversal', () => {
var target = null;
var expectedAggregation = [];
ReactDOMTreeTraversal.traverseEnterLeave(
target, target, argAggregator, ARG, ARG2
target,
target,
argAggregator,
ARG,
ARG2,
);
expect(aggregatedArgs).toEqual(expectedAggregation);
});
@@ -124,7 +128,11 @@ describe('ReactDOMTreeTraversal', () => {
var enter = getInst(parent.refs.P_P1_C1.refs.DIV_1);
var expectedAggregation = [];
ReactDOMTreeTraversal.traverseEnterLeave(
leave, enter, argAggregator, ARG, ARG2
leave,
enter,
argAggregator,
ARG,
ARG2,
);
expect(aggregatedArgs).toEqual(expectedAggregation);
});
@@ -139,7 +147,11 @@ describe('ReactDOMTreeTraversal', () => {
{node: parent.refs.P_P1_C1.refs.DIV_2, phase: 'captured', arg: ARG2},
];
ReactDOMTreeTraversal.traverseEnterLeave(
leave, enter, argAggregator, ARG, ARG2
leave,
enter,
argAggregator,
ARG,
ARG2,
);
expect(aggregatedArgs).toEqual(expectedAggregation);
});
@@ -152,7 +164,11 @@ describe('ReactDOMTreeTraversal', () => {
{node: parent.refs.P_P1_C1.refs.DIV_1, phase: 'bubbled', arg: ARG},
];
ReactDOMTreeTraversal.traverseEnterLeave(
leave, enter, argAggregator, ARG, ARG2
leave,
enter,
argAggregator,
ARG,
ARG2,
);
expect(aggregatedArgs).toEqual(expectedAggregation);
});
@@ -167,7 +183,11 @@ describe('ReactDOMTreeTraversal', () => {
{node: parent.refs.P_P1_C1.refs.DIV, phase: 'captured', arg: ARG2},
];
ReactDOMTreeTraversal.traverseEnterLeave(
leave, enter, argAggregator, ARG, ARG2
leave,
enter,
argAggregator,
ARG,
ARG2,
);
expect(aggregatedArgs).toEqual(expectedAggregation);
});
@@ -180,7 +200,11 @@ describe('ReactDOMTreeTraversal', () => {
{node: parent.refs.P, phase: 'captured', arg: ARG2},
];
ReactDOMTreeTraversal.traverseEnterLeave(
leave, enter, argAggregator, ARG, ARG2
leave,
enter,
argAggregator,
ARG,
ARG2,
);
expect(aggregatedArgs).toEqual(expectedAggregation);
});
@@ -195,7 +219,11 @@ describe('ReactDOMTreeTraversal', () => {
{node: parent.refs.P, phase: 'bubbled', arg: ARG},
];
ReactDOMTreeTraversal.traverseEnterLeave(
leave, enter, argAggregator, ARG, ARG2
leave,
enter,
argAggregator,
ARG,
ARG2,
);
expect(aggregatedArgs).toEqual(expectedAggregation);
});
@@ -210,7 +238,11 @@ describe('ReactDOMTreeTraversal', () => {
{node: parent.refs.P, phase: 'bubbled', arg: ARG},
];
ReactDOMTreeTraversal.traverseEnterLeave(
leave, enter, argAggregator, ARG, ARG2
leave,
enter,
argAggregator,
ARG,
ARG2,
);
expect(aggregatedArgs).toEqual(expectedAggregation);
});
@@ -221,7 +253,8 @@ describe('ReactDOMTreeTraversal', () => {
var parent = renderParentIntoDocument();
var ancestors = [
// Common ancestor with self is self.
{one: parent.refs.P_P1_C1.refs.DIV_1,
{
one: parent.refs.P_P1_C1.refs.DIV_1,
two: parent.refs.P_P1_C1.refs.DIV_1,
com: parent.refs.P_P1_C1.refs.DIV_1,
},
@@ -263,11 +296,10 @@ describe('ReactDOMTreeTraversal', () => {
var plan = ancestors[i];
var firstCommon = ReactDOMTreeTraversal.getLowestCommonAncestor(
getInst(plan.one),
getInst(plan.two)
getInst(plan.two),
);
expect(firstCommon).toBe(getInst(plan.com));
}
});
});
});
@@ -32,7 +32,7 @@ describe('ReactEventIndependence', () => {
dangerouslySetInnerHTML={{
__html: '<button data-reactid=".z">click me</div>',
}}
/>
/>,
);
ReactTestUtils.SimulateNative.click(div.firstChild);
expect(clicks).toBe(1);
@@ -44,7 +44,7 @@ describe('ReactEventIndependence', () => {
outer.setAttribute('data-reactid', '.z');
var inner = ReactDOM.render(
<button onClick={() => clicks++}>click me</button>,
outer
outer,
);
ReactTestUtils.SimulateNative.click(inner);
expect(clicks).toBe(1);
@@ -55,7 +55,7 @@ describe('ReactEventIndependence', () => {
var container = document.createElement('div');
var button = ReactDOM.render(
<button onClick={() => clicks++}>click me</button>,
container
container,
);
// Now we unmount the component, as if caused by a non-React event handler
@@ -66,5 +66,4 @@ describe('ReactEventIndependence', () => {
// Since the tree is unmounted, we don't dispatch the click event.
expect(clicks).toBe(0);
});
});
@@ -11,7 +11,6 @@
'use strict';
var EVENT_TARGET_PARAM = 1;
describe('ReactEventListener', () => {
@@ -38,19 +37,16 @@ describe('ReactEventListener', () => {
var otherNode = document.createElement('h1');
var component = ReactDOM.render(<div />, document.createElement('div'));
expect(handleTopLevel.mock.calls.length).toBe(0);
ReactEventListener.dispatchEvent(
'topMouseOut',
{
type: 'mouseout',
fromElement: otherNode,
target: otherNode,
srcElement: otherNode,
toElement: ReactDOM.findDOMNode(component),
relatedTarget: ReactDOM.findDOMNode(component),
view: window,
path: [otherNode, otherNode],
},
);
ReactEventListener.dispatchEvent('topMouseOut', {
type: 'mouseout',
fromElement: otherNode,
target: otherNode,
srcElement: otherNode,
toElement: ReactDOM.findDOMNode(component),
relatedTarget: ReactDOM.findDOMNode(component),
view: window,
path: [otherNode, otherNode],
});
expect(handleTopLevel.mock.calls.length).toBe(1);
});
@@ -61,8 +57,7 @@ describe('ReactEventListener', () => {
var parentContainer = document.createElement('div');
var parentControl = <div>Parent</div>;
childControl = ReactDOM.render(childControl, childContainer);
parentControl =
ReactDOM.render(parentControl, parentContainer);
parentControl = ReactDOM.render(parentControl, parentContainer);
ReactDOM.findDOMNode(parentControl).appendChild(childContainer);
var callback = ReactEventListener.dispatchEvent.bind(null, 'test');
@@ -72,10 +67,12 @@ describe('ReactEventListener', () => {
var calls = handleTopLevel.mock.calls;
expect(calls.length).toBe(2);
expect(calls[0][EVENT_TARGET_PARAM])
.toBe(ReactDOMComponentTree.getInstanceFromNode(childControl));
expect(calls[1][EVENT_TARGET_PARAM])
.toBe(ReactDOMComponentTree.getInstanceFromNode(parentControl));
expect(calls[0][EVENT_TARGET_PARAM]).toBe(
ReactDOMComponentTree.getInstanceFromNode(childControl),
);
expect(calls[1][EVENT_TARGET_PARAM]).toBe(
ReactDOMComponentTree.getInstanceFromNode(parentControl),
);
});
it('should propagate events two levels down', () => {
@@ -86,10 +83,11 @@ describe('ReactEventListener', () => {
var grandParentContainer = document.createElement('div');
var grandParentControl = <div>Parent</div>;
childControl = ReactDOM.render(childControl, childContainer);
parentControl =
ReactDOM.render(parentControl, parentContainer);
grandParentControl =
ReactDOM.render(grandParentControl, grandParentContainer);
parentControl = ReactDOM.render(parentControl, parentContainer);
grandParentControl = ReactDOM.render(
grandParentControl,
grandParentContainer,
);
ReactDOM.findDOMNode(parentControl).appendChild(childContainer);
ReactDOM.findDOMNode(grandParentControl).appendChild(parentContainer);
@@ -100,12 +98,15 @@ describe('ReactEventListener', () => {
var calls = handleTopLevel.mock.calls;
expect(calls.length).toBe(3);
expect(calls[0][EVENT_TARGET_PARAM])
.toBe(ReactDOMComponentTree.getInstanceFromNode(childControl));
expect(calls[1][EVENT_TARGET_PARAM])
.toBe(ReactDOMComponentTree.getInstanceFromNode(parentControl));
expect(calls[2][EVENT_TARGET_PARAM])
.toBe(ReactDOMComponentTree.getInstanceFromNode(grandParentControl));
expect(calls[0][EVENT_TARGET_PARAM]).toBe(
ReactDOMComponentTree.getInstanceFromNode(childControl),
);
expect(calls[1][EVENT_TARGET_PARAM]).toBe(
ReactDOMComponentTree.getInstanceFromNode(parentControl),
);
expect(calls[2][EVENT_TARGET_PARAM]).toBe(
ReactDOMComponentTree.getInstanceFromNode(grandParentControl),
);
});
it('should not get confused by disappearing elements', () => {
@@ -114,8 +115,7 @@ describe('ReactEventListener', () => {
var parentContainer = document.createElement('div');
var parentControl = <div>Parent</div>;
childControl = ReactDOM.render(childControl, childContainer);
parentControl =
ReactDOM.render(parentControl, parentContainer);
parentControl = ReactDOM.render(parentControl, parentContainer);
ReactDOM.findDOMNode(parentControl).appendChild(childContainer);
// ReactBrowserEventEmitter.handleTopLevel might remove the
@@ -123,13 +123,16 @@ describe('ReactEventListener', () => {
// node when the first event handlers are called; we'll still
// expect to receive a second call for the parent control.
var childNode = ReactDOM.findDOMNode(childControl);
handleTopLevel.mockImplementation(
function(topLevelType, topLevelTarget, topLevelTargetID, nativeEvent) {
if (topLevelTarget === childNode) {
ReactDOM.unmountComponentAtNode(childContainer);
}
handleTopLevel.mockImplementation(function(
topLevelType,
topLevelTarget,
topLevelTargetID,
nativeEvent,
) {
if (topLevelTarget === childNode) {
ReactDOM.unmountComponentAtNode(childContainer);
}
);
});
var callback = ReactEventListener.dispatchEvent.bind(null, 'test');
callback({
@@ -138,41 +141,42 @@ describe('ReactEventListener', () => {
var calls = handleTopLevel.mock.calls;
expect(calls.length).toBe(2);
expect(calls[0][EVENT_TARGET_PARAM])
.toBe(ReactDOMComponentTree.getInstanceFromNode(childNode));
expect(calls[1][EVENT_TARGET_PARAM])
.toBe(ReactDOMComponentTree.getInstanceFromNode(parentControl));
expect(calls[0][EVENT_TARGET_PARAM]).toBe(
ReactDOMComponentTree.getInstanceFromNode(childNode),
);
expect(calls[1][EVENT_TARGET_PARAM]).toBe(
ReactDOMComponentTree.getInstanceFromNode(parentControl),
);
});
it('should batch between handlers from different roots', () => {
var childContainer = document.createElement('div');
var parentContainer = document.createElement('div');
var childControl = ReactDOM.render(
<div>Child</div>,
childContainer
);
var parentControl = ReactDOM.render(
<div>Parent</div>,
parentContainer
);
var childControl = ReactDOM.render(<div>Child</div>, childContainer);
var parentControl = ReactDOM.render(<div>Parent</div>, parentContainer);
ReactDOM.findDOMNode(parentControl).appendChild(childContainer);
// Suppose an event handler in each root enqueues an update to the
// childControl element -- the two updates should get batched together.
var childNode = ReactDOM.findDOMNode(childControl);
handleTopLevel.mockImplementation(
function(topLevelType, topLevelTarget, topLevelTargetID, nativeEvent) {
ReactDOM.render(
<div>{topLevelTarget === childNode ? '1' : '2'}</div>,
childContainer
);
// Since we're batching, neither update should yet have gone through.
expect(childNode.textContent).toBe('Child');
}
);
handleTopLevel.mockImplementation(function(
topLevelType,
topLevelTarget,
topLevelTargetID,
nativeEvent,
) {
ReactDOM.render(
<div>{topLevelTarget === childNode ? '1' : '2'}</div>,
childContainer,
);
// Since we're batching, neither update should yet have gone through.
expect(childNode.textContent).toBe('Child');
});
var callback =
ReactEventListener.dispatchEvent.bind(ReactEventListener, 'test');
var callback = ReactEventListener.dispatchEvent.bind(
ReactEventListener,
'test',
);
callback({
target: childNode,
});
@@ -204,7 +208,8 @@ describe('ReactEventListener', () => {
var calls = handleTopLevel.mock.calls;
expect(calls.length).toBe(1);
expect(calls[0][EVENT_TARGET_PARAM])
.toBe(ReactDOMComponentTree.getInstanceFromNode(instance.getInner()));
expect(calls[0][EVENT_TARGET_PARAM]).toBe(
ReactDOMComponentTree.getInstanceFromNode(instance.getInner()),
);
});
});
@@ -45,7 +45,7 @@ describe('ReactMount', () => {
expect(function() {
ReactDOM.unmountComponentAtNode(nodeArray);
}).toThrowError(
'unmountComponentAtNode(...): Target container is not a DOM element.'
'unmountComponentAtNode(...): Target container is not a DOM element.',
);
});
});
@@ -55,7 +55,7 @@ describe('ReactMount', () => {
ReactTestUtils.renderIntoDocument('div');
}).toThrowError(
'ReactDOM.render(): Invalid component element. Instead of passing a ' +
'string like \'div\', pass React.createElement(\'div\') or <div />.'
"string like 'div', pass React.createElement('div') or <div />.",
);
});
@@ -70,7 +70,7 @@ describe('ReactMount', () => {
ReactTestUtils.renderIntoDocument(Component);
}).toThrowError(
'ReactDOM.render(): Invalid component element. Instead of passing a ' +
'class like Foo, pass React.createElement(Foo) or <Foo />.'
'class like Foo, pass React.createElement(Foo) or <Foo />.',
);
});
@@ -160,25 +160,26 @@ describe('ReactMount', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'Rendering components directly into document.body is discouraged'
'Rendering components directly into document.body is discouraged',
);
});
it('should account for escaping on a checksum mismatch', () => {
var div = document.createElement('div');
var markup = ReactDOMServer.renderToString(
<div>This markup contains an nbsp entity: &nbsp; server text</div>);
<div>This markup contains an nbsp entity: &nbsp; server text</div>,
);
div.innerHTML = markup;
spyOn(console, 'error');
ReactDOM.render(
<div>This markup contains an nbsp entity: &nbsp; client text</div>,
div
div,
);
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
' (client) nbsp entity: &nbsp; client text</div>\n' +
' (server) nbsp entity: &nbsp; server text</div>'
' (server) nbsp entity: &nbsp; server text</div>',
);
});
@@ -223,9 +224,9 @@ describe('ReactMount', () => {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: render(...): Replacing React-rendered children with a new ' +
'root component. If you intended to update the children of this node, ' +
'you should instead have the existing children update their state and ' +
'render the new components instead of calling ReactDOM.render.'
'root component. If you intended to update the children of this node, ' +
'you should instead have the existing children update their state and ' +
'render the new components instead of calling ReactDOM.render.',
);
});
@@ -248,8 +249,8 @@ describe('ReactMount', () => {
ReactDOMOther.unmountComponentAtNode(container);
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: unmountComponentAtNode(): The node you\'re attempting to unmount ' +
'was rendered by another copy of React.'
"Warning: unmountComponentAtNode(): The node you're attempting to unmount " +
'was rendered by another copy of React.',
);
// Don't throw a warning if the correct React copy unmounts the node
@@ -43,10 +43,11 @@ describe('ReactMount', () => {
it('should warn when unmounting a non-container root node', () => {
var mainContainerDiv = document.createElement('div');
var component =
var component = (
<div>
<div />
</div>;
</div>
);
ReactDOM.render(component, mainContainerDiv);
// Test that unmounting at a root node gives a helpful warning
@@ -55,22 +56,23 @@ describe('ReactMount', () => {
ReactDOM.unmountComponentAtNode(rootDiv);
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: unmountComponentAtNode(): The node you\'re attempting to ' +
'unmount was rendered by React and is not a top-level container. You ' +
'may have accidentally passed in a React root node instead of its ' +
'container.'
"Warning: unmountComponentAtNode(): The node you're attempting to " +
'unmount was rendered by React and is not a top-level container. You ' +
'may have accidentally passed in a React root node instead of its ' +
'container.',
);
});
it('should warn when unmounting a non-container, non-root node', () => {
var mainContainerDiv = document.createElement('div');
var component =
var component = (
<div>
<div>
<div />
</div>
</div>;
</div>
);
ReactDOM.render(component, mainContainerDiv);
// Test that unmounting at a non-root node gives a different warning
@@ -79,10 +81,10 @@ describe('ReactMount', () => {
ReactDOM.unmountComponentAtNode(nonRootDiv);
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: unmountComponentAtNode(): The node you\'re attempting to ' +
'unmount was rendered by React and is not a top-level container. ' +
'Instead, have the parent component update its state and rerender in ' +
'order to remove this component.'
"Warning: unmountComponentAtNode(): The node you're attempting to " +
'unmount was rendered by React and is not a top-level container. ' +
'Instead, have the parent component update its state and rerender in ' +
'order to remove this component.',
);
});
});
@@ -166,7 +166,7 @@ describe('rendering React components at document', () => {
}
var markup = ReactDOMServer.renderToString(
<Component text="Hello world" />
<Component text="Hello world" />,
);
testDocument = getTestDocument(markup);
@@ -194,7 +194,7 @@ describe('rendering React components at document', () => {
}
var markup = ReactDOMServer.renderToString(
<Component text="Goodbye world" />
<Component text="Goodbye world" />,
);
testDocument = getTestDocument(markup);
@@ -202,16 +202,16 @@ describe('rendering React components at document', () => {
// Notice the text is different!
ReactDOM.render(<Component text="Hello world" />, testDocument);
}).toThrowError(
'You\'re trying to render a component to the document using ' +
'server rendering but the checksum was invalid. This usually ' +
'means you rendered a different component type or props on ' +
'the client from the one on the server, or your render() methods ' +
'are impure. React cannot handle this case due to cross-browser ' +
'quirks by rendering at the document root. You should look for ' +
'environment dependent code in your components and ensure ' +
'the props are the same client and server side:\n' +
' (client) dy data-reactid="4">Hello world</body></\n' +
' (server) dy data-reactid="4">Goodbye world</body>'
"You're trying to render a component to the document using " +
'server rendering but the checksum was invalid. This usually ' +
'means you rendered a different component type or props on ' +
'the client from the one on the server, or your render() methods ' +
'are impure. React cannot handle this case due to cross-browser ' +
'quirks by rendering at the document root. You should look for ' +
'environment dependent code in your components and ensure ' +
'the props are the same client and server side:\n' +
' (client) dy data-reactid="4">Hello world</body></\n' +
' (server) dy data-reactid="4">Goodbye world</body>',
);
});
@@ -238,15 +238,15 @@ describe('rendering React components at document', () => {
expect(function() {
ReactDOM.render(<Component />, container);
}).toThrowError(
'You\'re trying to render a component to the document but you didn\'t ' +
'use server rendering. We can\'t do this without using server ' +
'rendering due to cross-browser quirks. See ' +
'ReactDOMServer.renderToString() for server rendering.'
"You're trying to render a component to the document but you didn't " +
"use server rendering. We can't do this without using server " +
'rendering due to cross-browser quirks. See ' +
'ReactDOMServer.renderToString() for server rendering.',
);
});
it('supports findDOMNode on full-page components', () => {
var tree =
var tree = (
<html>
<head>
<title>Hello World</title>
@@ -254,7 +254,8 @@ describe('rendering React components at document', () => {
<body>
Hello world
</body>
</html>;
</html>
);
var markup = ReactDOMServer.renderToString(tree);
testDocument = getTestDocument(markup);
@@ -38,7 +38,7 @@ describe('findDOMNode', () => {
expect(function() {
ReactDOM.findDOMNode({foo: 'bar'});
}).toThrowError(
'Element appears to be neither ReactComponent nor DOMNode (keys: foo)'
'Element appears to be neither ReactComponent nor DOMNode (keys: foo)',
);
});
@@ -54,7 +54,7 @@ describe('findDOMNode', () => {
ReactDOM.unmountComponentAtNode(container);
expect(() => ReactDOM.findDOMNode(inst)).toThrowError(
'findDOMNode was called on an unmounted component.'
'findDOMNode was called on an unmounted component.',
);
});
@@ -65,11 +65,10 @@ describe('findDOMNode', () => {
}
render() {
return <div/>;
return <div />;
}
}
expect(() => ReactTestUtils.renderIntoDocument(<Bar/>)).not.toThrow();
expect(() => ReactTestUtils.renderIntoDocument(<Bar />)).not.toThrow();
});
});
@@ -0,0 +1,158 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
'use strict';
var React = require('React');
var ReactTestUtils = require('ReactTestUtils');
var inputValueTracking = require('inputValueTracking');
describe('inputValueTracking', function() {
var input, checkbox, mockComponent;
beforeEach(function() {
input = document.createElement('input');
input.type = 'text';
checkbox = document.createElement('input');
checkbox.type = 'checkbox';
mockComponent = {_hostNode: input, _wrapperState: {}};
});
it('should attach tracker to wrapper state', function() {
inputValueTracking.track(mockComponent);
expect(mockComponent._wrapperState.hasOwnProperty('valueTracker')).toBe(
true,
);
});
it('should define `value` on the instance node', function() {
inputValueTracking.track(mockComponent);
expect(input.hasOwnProperty('value')).toBe(true);
});
it('should define `checked` on the instance node', function() {
mockComponent._hostNode = checkbox;
inputValueTracking.track(mockComponent);
expect(checkbox.hasOwnProperty('checked')).toBe(true);
});
it('should initialize with the current value', function() {
input.value = 'foo';
inputValueTracking.track(mockComponent);
var tracker = mockComponent._wrapperState.valueTracker;
expect(tracker.getValue()).toEqual('foo');
});
it('should initialize with the current `checked`', function() {
mockComponent._hostNode = checkbox;
checkbox.checked = true;
inputValueTracking.track(mockComponent);
var tracker = mockComponent._wrapperState.valueTracker;
expect(tracker.getValue()).toEqual('true');
});
it('should track value changes', function() {
input.value = 'foo';
inputValueTracking.track(mockComponent);
var tracker = mockComponent._wrapperState.valueTracker;
input.value = 'bar';
expect(tracker.getValue()).toEqual('bar');
});
it('should tracked`checked` changes', function() {
mockComponent._hostNode = checkbox;
checkbox.checked = true;
inputValueTracking.track(mockComponent);
var tracker = mockComponent._wrapperState.valueTracker;
checkbox.checked = false;
expect(tracker.getValue()).toEqual('false');
});
it('should update value manually', function() {
input.value = 'foo';
inputValueTracking.track(mockComponent);
var tracker = mockComponent._wrapperState.valueTracker;
tracker.setValue('bar');
expect(tracker.getValue()).toEqual('bar');
});
it('should coerce value to a string', function() {
input.value = 'foo';
inputValueTracking.track(mockComponent);
var tracker = mockComponent._wrapperState.valueTracker;
tracker.setValue(500);
expect(tracker.getValue()).toEqual('500');
});
it('should update value if it changed and return result', function() {
inputValueTracking.track(mockComponent);
input.value = 'foo';
var tracker = mockComponent._wrapperState.valueTracker;
expect(inputValueTracking.updateValueIfChanged(mockComponent)).toBe(false);
tracker.setValue('bar');
expect(inputValueTracking.updateValueIfChanged(mockComponent)).toBe(true);
expect(tracker.getValue()).toEqual('foo');
});
it('should track value and return true when updating untracked instance', function() {
input.value = 'foo';
expect(inputValueTracking.updateValueIfChanged(mockComponent)).toBe(true);
var tracker = mockComponent._wrapperState.valueTracker;
expect(tracker.getValue()).toEqual('foo');
});
it('should return tracker from node', function() {
var node = ReactTestUtils.renderIntoDocument(
<input type="text" defaultValue="foo" />,
);
var tracker = inputValueTracking._getTrackerFromNode(node);
expect(tracker.getValue()).toEqual('foo');
});
it('should stop tracking', function() {
inputValueTracking.track(mockComponent);
expect(mockComponent._wrapperState.hasOwnProperty('valueTracker')).toBe(
true,
);
inputValueTracking.stopTracking(mockComponent);
expect(mockComponent._wrapperState.hasOwnProperty('valueTracker')).toBe(
false,
);
expect(input.hasOwnProperty('value')).toBe(false);
});
});
@@ -15,22 +15,107 @@ var validateDOMNesting;
// https://html.spec.whatwg.org/multipage/syntax.html#special
var specialTags = [
'address', 'applet', 'area', 'article', 'aside', 'base', 'basefont',
'bgsound', 'blockquote', 'body', 'br', 'button', 'caption', 'center', 'col',
'colgroup', 'dd', 'details', 'dir', 'div', 'dl', 'dt', 'embed', 'fieldset',
'figcaption', 'figure', 'footer', 'form', 'frame', 'frameset', 'h1', 'h2',
'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'iframe',
'img', 'input', 'isindex', 'li', 'link', 'listing', 'main', 'marquee', 'menu',
'menuitem', 'meta', 'nav', 'noembed', 'noframes', 'noscript', 'object', 'ol',
'p', 'param', 'plaintext', 'pre', 'script', 'section', 'select', 'source',
'style', 'summary', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot',
'th', 'thead', 'title', 'tr', 'track', 'ul', 'wbr', 'xmp',
'address',
'applet',
'area',
'article',
'aside',
'base',
'basefont',
'bgsound',
'blockquote',
'body',
'br',
'button',
'caption',
'center',
'col',
'colgroup',
'dd',
'details',
'dir',
'div',
'dl',
'dt',
'embed',
'fieldset',
'figcaption',
'figure',
'footer',
'form',
'frame',
'frameset',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'head',
'header',
'hgroup',
'hr',
'html',
'iframe',
'img',
'input',
'isindex',
'li',
'link',
'listing',
'main',
'marquee',
'menu',
'menuitem',
'meta',
'nav',
'noembed',
'noframes',
'noscript',
'object',
'ol',
'p',
'param',
'plaintext',
'pre',
'script',
'section',
'select',
'source',
'style',
'summary',
'table',
'tbody',
'td',
'template',
'textarea',
'tfoot',
'th',
'thead',
'title',
'tr',
'track',
'ul',
'wbr',
'xmp',
];
// https://html.spec.whatwg.org/multipage/syntax.html#formatting
var formattingTags = [
'a', 'b', 'big', 'code', 'em', 'font', 'i', 'nobr', 's', 'small', 'strike',
'strong', 'tt', 'u',
'a',
'b',
'big',
'code',
'em',
'font',
'i',
'nobr',
's',
'small',
'strike',
'strong',
'tt',
'u',
];
function isTagStackValid(stack) {
@@ -39,8 +124,11 @@ function isTagStackValid(stack) {
if (!validateDOMNesting.isTagValidInContext(stack[i], ancestorInfo)) {
return false;
}
ancestorInfo =
validateDOMNesting.updatedAncestorInfo(ancestorInfo, stack[i], null);
ancestorInfo = validateDOMNesting.updatedAncestorInfo(
ancestorInfo,
stack[i],
null,
);
}
return true;
}
@@ -17,15 +17,13 @@ var FallbackCompositionState = require('FallbackCompositionState');
var SyntheticCompositionEvent = require('SyntheticCompositionEvent');
var SyntheticInputEvent = require('SyntheticInputEvent');
import type { TopLevelTypes } from 'EventConstants';
import type {TopLevelTypes} from 'EventConstants';
var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
var START_KEYCODE = 229;
var canUseCompositionEvent = (
ExecutionEnvironment.canUseDOM &&
'CompositionEvent' in window
);
var canUseCompositionEvent =
ExecutionEnvironment.canUseDOM && 'CompositionEvent' in window;
var documentMode = null;
if (ExecutionEnvironment.canUseDOM && 'documentMode' in document) {
@@ -35,23 +33,19 @@ if (ExecutionEnvironment.canUseDOM && 'documentMode' in document) {
// Webkit offers a very useful `textInput` event that can be used to
// directly represent `beforeInput`. The IE `textinput` event is not as
// useful, so we don't use it.
var canUseTextInputEvent = (
var canUseTextInputEvent =
ExecutionEnvironment.canUseDOM &&
'TextEvent' in window &&
!documentMode &&
!isPresto()
);
!isPresto();
// In IE9+, we have access to composition events, but the data supplied
// by the native compositionend event may be incorrect. Japanese ideographic
// spaces, for instance (\u3000) are not recorded correctly.
var useFallbackCompositionData = (
var useFallbackCompositionData =
ExecutionEnvironment.canUseDOM &&
(
!canUseCompositionEvent ||
(documentMode && documentMode > 8 && documentMode <= 11)
)
);
(!canUseCompositionEvent ||
(documentMode && documentMode > 8 && documentMode <= 11));
/**
* Opera <= 12 includes TextEvent in window, but does not fire
@@ -143,7 +137,6 @@ function isKeypressCommand(nativeEvent) {
);
}
/**
* Translate native top level events into event types.
*
@@ -170,10 +163,7 @@ function getCompositionEventType(topLevelType) {
* @return {boolean}
*/
function isFallbackCompositionStart(topLevelType, nativeEvent) {
return (
topLevelType === 'topKeyDown' &&
nativeEvent.keyCode === START_KEYCODE
);
return topLevelType === 'topKeyDown' && nativeEvent.keyCode === START_KEYCODE;
}
/**
@@ -187,11 +177,11 @@ function isFallbackCompositionEnd(topLevelType, nativeEvent) {
switch (topLevelType) {
case 'topKeyUp':
// Command keys insert or clear IME input.
return (END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1);
return END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1;
case 'topKeyDown':
// Expect IME keyCode on each keydown. If we get any other
// code we must have exited earlier.
return (nativeEvent.keyCode !== START_KEYCODE);
return nativeEvent.keyCode !== START_KEYCODE;
case 'topKeyPress':
case 'topMouseDown':
case 'topBlur':
@@ -229,7 +219,7 @@ function extractCompositionEvent(
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget
nativeEventTarget,
) {
var eventType;
var fallbackData;
@@ -252,8 +242,9 @@ function extractCompositionEvent(
// The current composition is stored statically and must not be
// overwritten while composition continues.
if (!currentComposition && eventType === eventTypes.compositionStart) {
currentComposition =
FallbackCompositionState.getPooled(nativeEventTarget);
currentComposition = FallbackCompositionState.getPooled(
nativeEventTarget,
);
} else if (eventType === eventTypes.compositionEnd) {
if (currentComposition) {
fallbackData = currentComposition.getData();
@@ -265,7 +256,7 @@ function extractCompositionEvent(
eventType,
targetInst,
nativeEvent,
nativeEventTarget
nativeEventTarget,
);
if (fallbackData) {
@@ -348,9 +339,11 @@ function getFallbackBeforeInputChars(topLevelType: TopLevelTypes, nativeEvent) {
// If composition event is available, we extract a string only at
// compositionevent, otherwise extract it at fallback events.
if (currentComposition) {
if (topLevelType === 'topCompositionEnd'
|| (!canUseCompositionEvent
&& isFallbackCompositionEnd(topLevelType, nativeEvent))) {
if (
topLevelType === 'topCompositionEnd' ||
(!canUseCompositionEvent &&
isFallbackCompositionEnd(topLevelType, nativeEvent))
) {
var chars = currentComposition.getData();
FallbackCompositionState.release(currentComposition);
currentComposition = null;
@@ -402,7 +395,7 @@ function extractBeforeInputEvent(
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget
nativeEventTarget,
) {
var chars;
@@ -422,7 +415,7 @@ function extractBeforeInputEvent(
eventTypes.beforeInput,
targetInst,
nativeEvent,
nativeEventTarget
nativeEventTarget,
);
event.data = chars;
@@ -449,27 +442,26 @@ function extractBeforeInputEvent(
* `composition` event types.
*/
var BeforeInputEventPlugin = {
eventTypes: eventTypes,
extractEvents: function(
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget
nativeEventTarget,
) {
return [
extractCompositionEvent(
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget
nativeEventTarget,
),
extractBeforeInputEvent(
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget
nativeEventTarget,
),
];
},
@@ -18,6 +18,7 @@ var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactUpdates = require('ReactUpdates');
var SyntheticEvent = require('SyntheticEvent');
var inputValueTracking = require('inputValueTracking');
var getEventTarget = require('getEventTarget');
var isEventSupported = require('isEventSupported');
var isTextInputElement = require('isTextInputElement');
@@ -41,13 +42,22 @@ var eventTypes = {
},
};
function createAndAccumulateChangeEvent(inst, nativeEvent, target) {
var event = SyntheticEvent.getPooled(
eventTypes.change,
inst,
nativeEvent,
target,
);
event.type = 'change';
EventPropagators.accumulateTwoPhaseDispatches(event);
return event;
}
/**
* For IE shims
*/
var activeElement = null;
var activeElementInst = null;
var activeElementValue = null;
var activeElementValueProp = null;
/**
* SECTION: handle `change` event
@@ -55,27 +65,24 @@ var activeElementValueProp = null;
function shouldUseChangeEvent(elem) {
var nodeName = elem.nodeName && elem.nodeName.toLowerCase();
return (
nodeName === 'select' ||
(nodeName === 'input' && elem.type === 'file')
nodeName === 'select' || (nodeName === 'input' && elem.type === 'file')
);
}
var doesChangeEventBubble = false;
if (ExecutionEnvironment.canUseDOM) {
// See `handleChange` comment below
doesChangeEventBubble = isEventSupported('change') && (
!document.documentMode || document.documentMode > 8
);
doesChangeEventBubble =
isEventSupported('change') &&
(!document.documentMode || document.documentMode > 8);
}
function manualDispatchChangeEvent(nativeEvent) {
var event = SyntheticEvent.getPooled(
eventTypes.change,
var event = createAndAccumulateChangeEvent(
activeElementInst,
nativeEvent,
getEventTarget(nativeEvent)
getEventTarget(nativeEvent),
);
EventPropagators.accumulateTwoPhaseDispatches(event);
// If change and propertychange bubbled, we'd just bind to it like all the
// other events and have it go through ReactBrowserEventEmitter. Since it
@@ -111,19 +118,24 @@ function stopWatchingForChangeEventIE8() {
activeElementInst = null;
}
function getTargetInstForChangeEvent(
topLevelType,
targetInst
) {
function getInstIfValueChanged(targetInst, nativeEvent) {
var updated = inputValueTracking.updateValueIfChanged(targetInst);
var simulated =
nativeEvent.simulated === true &&
ChangeEventPlugin._allowSimulatedPassThrough;
if (updated || simulated) {
return targetInst;
}
}
function getTargetInstForChangeEvent(topLevelType, targetInst) {
if (topLevelType === 'topChange') {
return targetInst;
}
}
function handleEventsForChangeEventIE8(
topLevelType,
target,
targetInst
) {
function handleEventsForChangeEventIE8(topLevelType, target, targetInst) {
if (topLevelType === 'topFocus') {
// stopWatching() should be a noop here but we call it just in case we
// missed a blur event somehow.
@@ -134,7 +146,6 @@ function handleEventsForChangeEventIE8(
}
}
/**
* SECTION: handle `input` event
*/
@@ -142,117 +153,56 @@ var isInputEventSupported = false;
if (ExecutionEnvironment.canUseDOM) {
// IE9 claims to support the input event but fails to trigger it when
// deleting text, so we ignore its input events.
// IE10+ fire input events to often, such when a placeholder
// changes or when an input with a placeholder is focused.
isInputEventSupported = isEventSupported('input') && (
!document.documentMode || document.documentMode > 11
);
isInputEventSupported =
isEventSupported('input') &&
(!('documentMode' in document) || document.documentMode > 9);
}
/**
* (For IE <=11) Replacement getter/setter for the `value` property that gets
* set on the active element.
*/
var newValueProp = {
get: function() {
return activeElementValueProp.get.call(this);
},
set: function(val) {
// Cast to a string so we can do equality checks.
activeElementValue = '' + val;
activeElementValueProp.set.call(this, val);
},
};
/**
* (For IE <=11) Starts tracking propertychange events on the passed-in element
* (For IE <=9) Starts tracking propertychange events on the passed-in element
* and override the value property so that we can distinguish user events from
* value changes in JS.
*/
function startWatchingForValueChange(target, targetInst) {
activeElement = target;
activeElementInst = targetInst;
activeElementValue = target.value;
activeElementValueProp = Object.getOwnPropertyDescriptor(
target.constructor.prototype,
'value'
);
// Not guarded in a canDefineProperty check: IE8 supports defineProperty only
// on DOM elements
Object.defineProperty(activeElement, 'value', newValueProp);
if (activeElement.attachEvent) {
activeElement.attachEvent('onpropertychange', handlePropertyChange);
} else {
activeElement.addEventListener('propertychange', handlePropertyChange, false);
}
activeElement.attachEvent('onpropertychange', handlePropertyChange);
}
/**
* (For IE <=11) Removes the event listeners from the currently-tracked element,
* (For IE <=9) Removes the event listeners from the currently-tracked element,
* if any exists.
*/
function stopWatchingForValueChange() {
if (!activeElement) {
return;
}
// delete restores the original property definition
delete activeElement.value;
if (activeElement.detachEvent) {
activeElement.detachEvent('onpropertychange', handlePropertyChange);
} else {
activeElement.removeEventListener('propertychange', handlePropertyChange, false);
}
activeElement.detachEvent('onpropertychange', handlePropertyChange);
activeElement = null;
activeElementInst = null;
activeElementValue = null;
activeElementValueProp = null;
}
/**
* (For IE <=11) Handles a propertychange event, sending a `change` event if
* (For IE <=9) Handles a propertychange event, sending a `change` event if
* the value of the active element has changed.
*/
function handlePropertyChange(nativeEvent) {
if (nativeEvent.propertyName !== 'value') {
return;
}
var value = nativeEvent.srcElement.value;
if (value === activeElementValue) {
return;
}
activeElementValue = value;
manualDispatchChangeEvent(nativeEvent);
}
/**
* If a `change` event should be fired, returns the target's ID.
*/
function getTargetInstForInputEvent(
topLevelType,
targetInst
) {
if (topLevelType === 'topInput') {
// In modern browsers (i.e., not IE8 or IE9), the input event is exactly
// what we want so fall through here and trigger an abstract event
return targetInst;
if (getInstIfValueChanged(activeElementInst, nativeEvent)) {
manualDispatchChangeEvent(nativeEvent);
}
}
function handleEventsForInputEventIE(
topLevelType,
target,
targetInst
) {
function handleEventsForInputEventPolyfill(topLevelType, target, targetInst) {
if (topLevelType === 'topFocus') {
// In IE8, we can capture almost all .value changes by adding a
// propertychange handler and looking for events with propertyName
// equal to 'value'
// In IE9-11, propertychange fires for most input events but is buggy and
// In IE9, propertychange fires for most input events but is buggy and
// doesn't fire when text is deleted, but conveniently, selectionchange
// appears to fire in all of the remaining cases so we catch those and
// forward the event if the value has changed
@@ -270,13 +220,16 @@ function handleEventsForInputEventIE(
}
// For IE8 and IE9.
function getTargetInstForInputEventIE(
function getTargetInstForInputEventPolyfill(
topLevelType,
targetInst
targetInst,
nativeEvent,
) {
if (topLevelType === 'topSelectionChange' ||
topLevelType === 'topKeyUp' ||
topLevelType === 'topKeyDown') {
if (
topLevelType === 'topSelectionChange' ||
topLevelType === 'topKeyUp' ||
topLevelType === 'topKeyDown'
) {
// On the selectionchange event, the target is just document which isn't
// helpful for us so just check activeElement instead.
//
@@ -287,14 +240,10 @@ function getTargetInstForInputEventIE(
// keystroke if user does a key repeat (it'll be a little delayed: right
// before the second keystroke). Other input methods (e.g., paste) seem to
// fire selectionchange normally.
if (activeElement && activeElement.value !== activeElementValue) {
activeElementValue = activeElement.value;
return activeElementInst;
}
return getInstIfValueChanged(activeElementInst, nativeEvent);
}
}
/**
* SECTION: handle `click` event
*/
@@ -302,18 +251,27 @@ function shouldUseClickEvent(elem) {
// Use the `click` event to detect changes to checkbox and radio inputs.
// This approach works across all browsers, whereas `change` does not fire
// until `blur` in IE8.
var nodeName = elem.nodeName;
return (
(elem.nodeName && elem.nodeName.toLowerCase() === 'input') &&
nodeName &&
nodeName.toLowerCase() === 'input' &&
(elem.type === 'checkbox' || elem.type === 'radio')
);
}
function getTargetInstForClickEvent(
topLevelType,
targetInst
) {
function getTargetInstForClickEvent(topLevelType, targetInst, nativeEvent) {
if (topLevelType === 'topClick') {
return targetInst;
return getInstIfValueChanged(targetInst, nativeEvent);
}
}
function getTargetInstForInputOrChangeEvent(
topLevelType,
targetInst,
nativeEvent,
) {
if (topLevelType === 'topInput' || topLevelType === 'topChange') {
return getInstIfValueChanged(targetInst, nativeEvent);
}
}
@@ -348,17 +306,20 @@ function handleControlledInputBlur(inst, node) {
* - select
*/
var ChangeEventPlugin = {
eventTypes: eventTypes,
_allowSimulatedPassThrough: true,
_isInputEventSupported: isInputEventSupported,
extractEvents: function(
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget
nativeEventTarget,
) {
var targetNode = targetInst ?
ReactDOMComponentTree.getNodeFromInstance(targetInst) : window;
var targetNode = targetInst
? ReactDOMComponentTree.getNodeFromInstance(targetInst)
: window;
var getTargetInstFunc, handleEventFunc;
if (shouldUseChangeEvent(targetNode)) {
@@ -369,36 +330,29 @@ var ChangeEventPlugin = {
}
} else if (isTextInputElement(targetNode)) {
if (isInputEventSupported) {
getTargetInstFunc = getTargetInstForInputEvent;
getTargetInstFunc = getTargetInstForInputOrChangeEvent;
} else {
getTargetInstFunc = getTargetInstForInputEventIE;
handleEventFunc = handleEventsForInputEventIE;
getTargetInstFunc = getTargetInstForInputEventPolyfill;
handleEventFunc = handleEventsForInputEventPolyfill;
}
} else if (shouldUseClickEvent(targetNode)) {
getTargetInstFunc = getTargetInstForClickEvent;
}
if (getTargetInstFunc) {
var inst = getTargetInstFunc(topLevelType, targetInst);
var inst = getTargetInstFunc(topLevelType, targetInst, nativeEvent);
if (inst) {
var event = SyntheticEvent.getPooled(
eventTypes.change,
var event = createAndAccumulateChangeEvent(
inst,
nativeEvent,
nativeEventTarget
nativeEventTarget,
);
event.type = 'change';
EventPropagators.accumulateTwoPhaseDispatches(event);
return event;
}
}
if (handleEventFunc) {
handleEventFunc(
topLevelType,
targetNode,
targetInst
);
handleEventFunc(topLevelType, targetNode, targetInst);
}
// When blurring, set the value attribute for number inputs
@@ -406,7 +360,6 @@ var ChangeEventPlugin = {
handleControlledInputBlur(targetInst, targetNode);
}
},
};
module.exports = ChangeEventPlugin;

Some files were not shown because too many files have changed in this diff Show More