Skip to content

Instantly share code, notes, and snippets.

@zpao
Created April 5, 2016 20:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zpao/81c9bd52c01211cc0b5c65833fe951d9 to your computer and use it in GitHub Desktop.
Save zpao/81c9bd52c01211cc0b5c65833fe951d9 to your computer and use it in GitHub Desktop.
diff --git a/src/React.js b/src/React.js
deleted file mode 100644
index 5aa15e8..0000000
--- a/src/React.js
+++ /dev/null
@@ -1,28 +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 React
- */
-
-'use strict';
-
-var ReactDOM = require('ReactDOM');
-var ReactDOMServer = require('ReactDOMServer');
-var ReactIsomorphic = require('ReactIsomorphic');
-
-var assign = require('Object.assign');
-
-// `version` will be added here by ReactIsomorphic.
-var React = {};
-
-assign(React, ReactIsomorphic);
-
-React.__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = ReactDOM;
-React.__SECRET_DOM_SERVER_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = ReactDOMServer;
-
-module.exports = React;
diff --git a/src/addons/ReactWithAddons.js b/src/addons/ReactWithAddons.js
index 755a1dc..bd13ea4 100644
--- a/src/addons/ReactWithAddons.js
+++ b/src/addons/ReactWithAddons.js
@@ -9,13 +9,6 @@
* @providesModule ReactWithAddons
*/
-/**
- * This module exists purely in the open source project, and is meant as a way
- * to create a separate standalone build of React. This build has "addons", or
- * functionality we've built and think might be useful but doesn't have a good
- * place to live inside React core.
- */
-
'use strict';
var LinkedStateMixin = require('LinkedStateMixin');
diff --git a/src/addons/transitions/ReactCSSTransitionGroup.js b/src/addons/transitions/ReactCSSTransitionGroup.js
index 5320188..4177ec7 100644
--- a/src/addons/transitions/ReactCSSTransitionGroup.js
+++ b/src/addons/transitions/ReactCSSTransitionGroup.js
@@ -13,8 +13,6 @@
var React = require('React');
-var assign = require('Object.assign');
-
var ReactTransitionGroup = require('ReactTransitionGroup');
var ReactCSSTransitionGroupChild = require('ReactCSSTransitionGroupChild');
@@ -87,7 +85,7 @@ var ReactCSSTransitionGroup = React.createClass({
render: function() {
return React.createElement(
ReactTransitionGroup,
- assign({}, this.props, {childFactory: this._wrapChild})
+ Object.assign({}, this.props, {childFactory: this._wrapChild})
);
},
});
diff --git a/src/addons/transitions/ReactTransitionGroup.js b/src/addons/transitions/ReactTransitionGroup.js
index 816edfa..6d6df1a 100644
--- a/src/addons/transitions/ReactTransitionGroup.js
+++ b/src/addons/transitions/ReactTransitionGroup.js
@@ -14,7 +14,6 @@
var React = require('React');
var ReactTransitionChildMapping = require('ReactTransitionChildMapping');
-var assign = require('Object.assign');
var emptyFunction = require('emptyFunction');
var ReactTransitionGroup = React.createClass({
@@ -193,7 +192,7 @@ var ReactTransitionGroup = React.createClass({
this.performEnter(key);
} else {
this.setState(function(state) {
- var newChildren = assign({}, state.children);
+ var newChildren = Object.assign({}, state.children);
delete newChildren[key];
return {children: newChildren};
});
diff --git a/src/addons/update.js b/src/addons/update.js
index 7c8ba4e..cc2434e 100644
--- a/src/addons/update.js
+++ b/src/addons/update.js
@@ -13,7 +13,6 @@
'use strict';
-var assign = require('Object.assign');
var keyOf = require('keyOf');
var invariant = require('invariant');
var hasOwnProperty = {}.hasOwnProperty;
@@ -22,7 +21,7 @@ function shallowCopy(x) {
if (Array.isArray(x)) {
return x.concat();
} else if (x && typeof x === 'object') {
- return assign(new x.constructor(), x);
+ return Object.assign(new x.constructor(), x);
} else {
return x;
}
@@ -102,7 +101,7 @@ function update(value, spec) {
COMMAND_MERGE,
nextValue
);
- assign(nextValue, spec[COMMAND_MERGE]);
+ Object.assign(nextValue, spec[COMMAND_MERGE]);
}
if (hasOwnProperty.call(spec, COMMAND_PUSH)) {
diff --git a/src/isomorphic/React.js b/src/isomorphic/React.js
new file mode 100644
index 0000000..37109d6
--- /dev/null
+++ b/src/isomorphic/React.js
@@ -0,0 +1,73 @@
+/**
+ * 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 React
+ */
+
+'use strict';
+
+var ReactChildren = require('ReactChildren');
+var ReactComponent = require('ReactComponent');
+var ReactClass = require('ReactClass');
+var ReactDOMFactories = require('ReactDOMFactories');
+var ReactElement = require('ReactElement');
+var ReactElementValidator = require('ReactElementValidator');
+var ReactPropTypes = require('ReactPropTypes');
+var ReactVersion = require('ReactVersion');
+
+var onlyChild = require('onlyChild');
+
+var createElement = ReactElement.createElement;
+var createFactory = ReactElement.createFactory;
+var cloneElement = ReactElement.cloneElement;
+
+if (__DEV__) {
+ createElement = ReactElementValidator.createElement;
+ createFactory = ReactElementValidator.createFactory;
+ cloneElement = ReactElementValidator.cloneElement;
+}
+
+var React = {
+
+ // Modern
+
+ Children: {
+ map: ReactChildren.map,
+ forEach: ReactChildren.forEach,
+ count: ReactChildren.count,
+ toArray: ReactChildren.toArray,
+ only: onlyChild,
+ },
+
+ Component: ReactComponent,
+
+ createElement: createElement,
+ cloneElement: cloneElement,
+ isValidElement: ReactElement.isValidElement,
+
+ // Classic
+
+ PropTypes: ReactPropTypes,
+ createClass: ReactClass.createClass,
+ createFactory: createFactory,
+ createMixin: function(mixin) {
+ // Currently a noop. Will be used to validate and trace mixins.
+ return mixin;
+ },
+
+ // This looks DOM specific but these are actually isomorphic helpers
+ // since they are just generating DOM strings.
+ DOM: ReactDOMFactories,
+
+ version: ReactVersion,
+
+ // Hook for JSX spread, don't use this for anything else.
+ __spread: Object.assign,
+};
+
+module.exports = React;
diff --git a/src/isomorphic/ReactDebugInstanceMap.js b/src/isomorphic/ReactDebugInstanceMap.js
new file mode 100644
index 0000000..50dddf4
--- /dev/null
+++ b/src/isomorphic/ReactDebugInstanceMap.js
@@ -0,0 +1,124 @@
+/**
+ * Copyright 2016-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 ReactDebugInstanceMap
+ */
+
+'use strict';
+
+var warning = require('warning');
+
+function checkValidInstance(internalInstance) {
+ if (!internalInstance) {
+ warning(
+ false,
+ 'There is an internal error in the React developer tools integration. ' +
+ 'Instead of an internal instance, received %s. ' +
+ 'Please report this as a bug in React.',
+ internalInstance
+ );
+ return false;
+ }
+ var isValid = typeof internalInstance.mountComponent === 'function';
+ warning(
+ isValid,
+ 'There is an internal error in the React developer tools integration. ' +
+ 'Instead of an internal instance, received an object with the following ' +
+ 'keys: %s. Please report this as a bug in React.',
+ Object.keys(internalInstance).join(', ')
+ );
+ return isValid;
+}
+
+var idCounter = 1;
+var instancesByIDs = {};
+var instancesToIDs;
+
+function getIDForInstance(internalInstance) {
+ if (!instancesToIDs) {
+ instancesToIDs = new WeakMap();
+ }
+ if (instancesToIDs.has(internalInstance)) {
+ return instancesToIDs.get(internalInstance);
+ } else {
+ var instanceID = (idCounter++).toString();
+ instancesToIDs.set(internalInstance, instanceID);
+ return instanceID;
+ }
+}
+
+function getInstanceByID(instanceID) {
+ return instancesByIDs[instanceID] || null;
+}
+
+function isRegisteredInstance(internalInstance) {
+ var instanceID = getIDForInstance(internalInstance);
+ if (instanceID) {
+ return instancesByIDs.hasOwnProperty(instanceID);
+ } else {
+ return false;
+ }
+}
+
+function registerInstance(internalInstance) {
+ var instanceID = getIDForInstance(internalInstance);
+ if (instanceID) {
+ instancesByIDs[instanceID] = internalInstance;
+ }
+}
+
+function unregisterInstance(internalInstance) {
+ var instanceID = getIDForInstance(internalInstance);
+ if (instanceID) {
+ delete instancesByIDs[instanceID];
+ }
+}
+
+var ReactDebugInstanceMap = {
+ getIDForInstance(internalInstance) {
+ if (!checkValidInstance(internalInstance)) {
+ return null;
+ }
+ return getIDForInstance(internalInstance);
+ },
+ getInstanceByID(instanceID) {
+ return getInstanceByID(instanceID);
+ },
+ isRegisteredInstance(internalInstance) {
+ if (!checkValidInstance(internalInstance)) {
+ return false;
+ }
+ return isRegisteredInstance(internalInstance);
+ },
+ registerInstance(internalInstance) {
+ if (!checkValidInstance(internalInstance)) {
+ return;
+ }
+ warning(
+ !isRegisteredInstance(internalInstance),
+ 'There is an internal error in the React developer tools integration. ' +
+ 'A registered instance should not be registered again. ' +
+ 'Please report this as a bug in React.'
+ );
+ registerInstance(internalInstance);
+ },
+ unregisterInstance(internalInstance) {
+ if (!checkValidInstance(internalInstance)) {
+ return;
+ }
+ warning(
+ isRegisteredInstance(internalInstance),
+ 'There is an internal error in the React developer tools integration. ' +
+ 'An unregistered instance should not be unregistered again. ' +
+ 'Please report this as a bug in React.'
+ );
+ unregisterInstance(internalInstance);
+ },
+};
+
+module.exports = ReactDebugInstanceMap;
diff --git a/src/isomorphic/ReactIsomorphic.js b/src/isomorphic/ReactIsomorphic.js
deleted file mode 100644
index 8eca054..0000000
--- a/src/isomorphic/ReactIsomorphic.js
+++ /dev/null
@@ -1,74 +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 ReactIsomorphic
- */
-
-'use strict';
-
-var ReactChildren = require('ReactChildren');
-var ReactComponent = require('ReactComponent');
-var ReactClass = require('ReactClass');
-var ReactDOMFactories = require('ReactDOMFactories');
-var ReactElement = require('ReactElement');
-var ReactElementValidator = require('ReactElementValidator');
-var ReactPropTypes = require('ReactPropTypes');
-var ReactVersion = require('ReactVersion');
-
-var assign = require('Object.assign');
-var onlyChild = require('onlyChild');
-
-var createElement = ReactElement.createElement;
-var createFactory = ReactElement.createFactory;
-var cloneElement = ReactElement.cloneElement;
-
-if (__DEV__) {
- createElement = ReactElementValidator.createElement;
- createFactory = ReactElementValidator.createFactory;
- cloneElement = ReactElementValidator.cloneElement;
-}
-
-var React = {
-
- // Modern
-
- Children: {
- map: ReactChildren.map,
- forEach: ReactChildren.forEach,
- count: ReactChildren.count,
- toArray: ReactChildren.toArray,
- only: onlyChild,
- },
-
- Component: ReactComponent,
-
- createElement: createElement,
- cloneElement: cloneElement,
- isValidElement: ReactElement.isValidElement,
-
- // Classic
-
- PropTypes: ReactPropTypes,
- createClass: ReactClass.createClass,
- createFactory: createFactory,
- createMixin: function(mixin) {
- // Currently a noop. Will be used to validate and trace mixins.
- return mixin;
- },
-
- // This looks DOM specific but these are actually isomorphic helpers
- // since they are just generating DOM strings.
- DOM: ReactDOMFactories,
-
- version: ReactVersion,
-
- // Hook for JSX spread, don't use this for anything else.
- __spread: assign,
-};
-
-module.exports = React;
diff --git a/src/isomorphic/__tests__/ReactDebugInstanceMap-test.js b/src/isomorphic/__tests__/ReactDebugInstanceMap-test.js
new file mode 100644
index 0000000..d9a063e
--- /dev/null
+++ b/src/isomorphic/__tests__/ReactDebugInstanceMap-test.js
@@ -0,0 +1,173 @@
+/**
+ * Copyright 2016-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('ReactDebugInstanceMap', function() {
+ var React;
+ var ReactDebugInstanceMap;
+ var ReactDOM;
+
+ beforeEach(function() {
+ jest.resetModuleRegistry();
+ React = require('React');
+ ReactDebugInstanceMap = require('ReactDebugInstanceMap');
+ ReactDOM = require('ReactDOM');
+ });
+
+ function createStubInstance() {
+ return { mountComponent: () => {} };
+ }
+
+ it('should register and unregister instances', function() {
+ var inst1 = createStubInstance();
+ var inst2 = createStubInstance();
+
+ expect(ReactDebugInstanceMap.isRegisteredInstance(inst1)).toBe(false);
+ expect(ReactDebugInstanceMap.isRegisteredInstance(inst2)).toBe(false);
+
+ ReactDebugInstanceMap.registerInstance(inst1);
+ expect(ReactDebugInstanceMap.isRegisteredInstance(inst1)).toBe(true);
+ expect(ReactDebugInstanceMap.isRegisteredInstance(inst2)).toBe(false);
+
+ ReactDebugInstanceMap.registerInstance(inst2);
+ expect(ReactDebugInstanceMap.isRegisteredInstance(inst1)).toBe(true);
+ expect(ReactDebugInstanceMap.isRegisteredInstance(inst2)).toBe(true);
+
+ ReactDebugInstanceMap.unregisterInstance(inst2);
+ expect(ReactDebugInstanceMap.isRegisteredInstance(inst1)).toBe(true);
+ expect(ReactDebugInstanceMap.isRegisteredInstance(inst2)).toBe(false);
+
+ ReactDebugInstanceMap.unregisterInstance(inst1);
+ expect(ReactDebugInstanceMap.isRegisteredInstance(inst1)).toBe(false);
+ expect(ReactDebugInstanceMap.isRegisteredInstance(inst2)).toBe(false);
+ });
+
+ it('should assign stable IDs', function() {
+ var inst1 = createStubInstance();
+ var inst2 = createStubInstance();
+
+ var inst1ID = ReactDebugInstanceMap.getIDForInstance(inst1);
+ var inst2ID = ReactDebugInstanceMap.getIDForInstance(inst2);
+ expect(typeof inst1ID).toBe('string');
+ expect(typeof inst2ID).toBe('string');
+ expect(inst1ID).not.toBe(inst2ID);
+
+ ReactDebugInstanceMap.registerInstance(inst1);
+ ReactDebugInstanceMap.registerInstance(inst2);
+ expect(ReactDebugInstanceMap.getIDForInstance(inst1)).toBe(inst1ID);
+ expect(ReactDebugInstanceMap.getIDForInstance(inst2)).toBe(inst2ID);
+
+ ReactDebugInstanceMap.unregisterInstance(inst1);
+ ReactDebugInstanceMap.unregisterInstance(inst2);
+ expect(ReactDebugInstanceMap.getIDForInstance(inst1)).toBe(inst1ID);
+ expect(ReactDebugInstanceMap.getIDForInstance(inst2)).toBe(inst2ID);
+ });
+
+ it('should retrieve registered instance by its ID', function() {
+ var inst1 = createStubInstance();
+ var inst2 = createStubInstance();
+
+ var inst1ID = ReactDebugInstanceMap.getIDForInstance(inst1);
+ var inst2ID = ReactDebugInstanceMap.getIDForInstance(inst2);
+ expect(ReactDebugInstanceMap.getInstanceByID(inst1ID)).toBe(null);
+ expect(ReactDebugInstanceMap.getInstanceByID(inst2ID)).toBe(null);
+
+ ReactDebugInstanceMap.registerInstance(inst1);
+ ReactDebugInstanceMap.registerInstance(inst2);
+ expect(ReactDebugInstanceMap.getInstanceByID(inst1ID)).toBe(inst1);
+ expect(ReactDebugInstanceMap.getInstanceByID(inst2ID)).toBe(inst2);
+
+ ReactDebugInstanceMap.unregisterInstance(inst1);
+ ReactDebugInstanceMap.unregisterInstance(inst2);
+ expect(ReactDebugInstanceMap.getInstanceByID(inst1ID)).toBe(null);
+ expect(ReactDebugInstanceMap.getInstanceByID(inst2ID)).toBe(null);
+ });
+
+ it('should warn when registering an instance twice', function() {
+ spyOn(console, 'error');
+
+ var inst = createStubInstance();
+ ReactDebugInstanceMap.registerInstance(inst);
+ expect(console.error.argsForCall.length).toBe(0);
+
+ ReactDebugInstanceMap.registerInstance(inst);
+ expect(console.error.argsForCall.length).toBe(1);
+ expect(console.error.argsForCall[0][0]).toContain(
+ 'There is an internal error in the React developer tools integration. ' +
+ 'A registered instance should not be registered again. ' +
+ 'Please report this as a bug in React.'
+ );
+
+ ReactDebugInstanceMap.unregisterInstance(inst);
+ ReactDebugInstanceMap.registerInstance(inst);
+ expect(console.error.argsForCall.length).toBe(1);
+ });
+
+ it('should warn when unregistering an instance twice', function() {
+ spyOn(console, 'error');
+ var inst = createStubInstance();
+
+ ReactDebugInstanceMap.unregisterInstance(inst);
+ expect(console.error.argsForCall.length).toBe(1);
+ expect(console.error.argsForCall[0][0]).toContain(
+ 'There is an internal error in the React developer tools integration. ' +
+ 'An unregistered instance should not be unregistered again. ' +
+ 'Please report this as a bug in React.'
+ );
+
+ ReactDebugInstanceMap.registerInstance(inst);
+ ReactDebugInstanceMap.unregisterInstance(inst);
+ expect(console.error.argsForCall.length).toBe(1);
+
+ ReactDebugInstanceMap.unregisterInstance(inst);
+ expect(console.error.argsForCall.length).toBe(2);
+ expect(console.error.argsForCall[1][0]).toContain(
+ 'There is an internal error in the React developer tools integration. ' +
+ 'An unregistered instance should not be unregistered again. ' +
+ 'Please report this as a bug in React.'
+ );
+ });
+
+ it('should warn about anything than is not an internal instance', function() {
+ class Foo extends React.Component {
+ render() {
+ return <div />;
+ }
+ }
+
+ spyOn(console, 'error');
+ var warningCount = 0;
+ var div = document.createElement('div');
+ var publicInst = ReactDOM.render(<Foo />, div);
+
+ [false, null, undefined, {}, div, publicInst].forEach(falsyValue => {
+ ReactDebugInstanceMap.registerInstance(falsyValue);
+ warningCount++;
+ expect(ReactDebugInstanceMap.getIDForInstance(falsyValue)).toBe(null);
+ warningCount++;
+ expect(ReactDebugInstanceMap.isRegisteredInstance(falsyValue)).toBe(false);
+ warningCount++;
+ ReactDebugInstanceMap.unregisterInstance(falsyValue);
+ warningCount++;
+ });
+
+ expect(console.error.argsForCall.length).toBe(warningCount);
+ for (var i = 0; i < warningCount.length; i++) {
+ // Ideally we could check for the more detailed error message here
+ // but it depends on the input type and is meant for internal bugs
+ // anyway so I don't think it's worth complicating the test with it.
+ expect(console.error.argsForCall[i][0]).toContain(
+ 'There is an internal error in the React developer tools integration.'
+ );
+ }
+ });
+});
diff --git a/src/isomorphic/classic/__tests__/ReactContextValidator-test.js b/src/isomorphic/classic/__tests__/ReactContextValidator-test.js
index 891cce6..cc09080 100644
--- a/src/isomorphic/classic/__tests__/ReactContextValidator-test.js
+++ b/src/isomorphic/classic/__tests__/ReactContextValidator-test.js
@@ -31,8 +31,6 @@ describe('ReactContextValidator', function() {
ReactDOM = require('ReactDOM');
ReactTestUtils = require('ReactTestUtils');
reactComponentExpect = require('reactComponentExpect');
-
- spyOn(console, 'error');
});
// TODO: This behavior creates a runtime dependency on propTypes. We should
@@ -133,6 +131,8 @@ describe('ReactContextValidator', function() {
});
it('should check context types', function() {
+ spyOn(console, 'error');
+
var Component = React.createClass({
contextTypes: {
foo: React.PropTypes.string.isRequired,
@@ -202,6 +202,8 @@ describe('ReactContextValidator', function() {
});
it('should check child context types', function() {
+ spyOn(console, 'error');
+
var Component = React.createClass({
childContextTypes: {
foo: React.PropTypes.string.isRequired,
diff --git a/src/isomorphic/classic/class/ReactClass.js b/src/isomorphic/classic/class/ReactClass.js
index 223e58b..56f479a 100644
--- a/src/isomorphic/classic/class/ReactClass.js
+++ b/src/isomorphic/classic/class/ReactClass.js
@@ -17,7 +17,6 @@ var ReactPropTypeLocations = require('ReactPropTypeLocations');
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
var ReactNoopUpdateQueue = require('ReactNoopUpdateQueue');
-var assign = require('Object.assign');
var emptyObject = require('emptyObject');
var invariant = require('invariant');
var keyMirror = require('keyMirror');
@@ -331,7 +330,7 @@ var RESERVED_SPEC_KEYS = {
ReactPropTypeLocations.childContext
);
}
- Constructor.childContextTypes = assign(
+ Constructor.childContextTypes = Object.assign(
{},
Constructor.childContextTypes,
childContextTypes
@@ -345,7 +344,7 @@ var RESERVED_SPEC_KEYS = {
ReactPropTypeLocations.context
);
}
- Constructor.contextTypes = assign(
+ Constructor.contextTypes = Object.assign(
{},
Constructor.contextTypes,
contextTypes
@@ -373,7 +372,7 @@ var RESERVED_SPEC_KEYS = {
ReactPropTypeLocations.prop
);
}
- Constructor.propTypes = assign(
+ Constructor.propTypes = Object.assign(
{},
Constructor.propTypes,
propTypes
@@ -710,7 +709,7 @@ var ReactClassMixin = {
replaceState: function(newState, callback) {
this.updater.enqueueReplaceState(this, newState);
if (callback) {
- this.updater.enqueueCallback(this, callback);
+ this.updater.enqueueCallback(this, callback, 'replaceState');
}
},
@@ -726,7 +725,7 @@ var ReactClassMixin = {
};
var ReactClassComponent = function() {};
-assign(
+Object.assign(
ReactClassComponent.prototype,
ReactComponent.prototype,
ReactClassMixin
diff --git a/src/isomorphic/classic/class/__tests__/ReactClass-test.js b/src/isomorphic/classic/class/__tests__/ReactClass-test.js
index cce8554..07a1e4b 100644
--- a/src/isomorphic/classic/class/__tests__/ReactClass-test.js
+++ b/src/isomorphic/classic/class/__tests__/ReactClass-test.js
@@ -21,7 +21,6 @@ describe('ReactClass-spec', function() {
React = require('React');
ReactDOM = require('ReactDOM');
ReactTestUtils = require('ReactTestUtils');
- spyOn(console, 'error');
});
it('should throw when `render` is not specified', function() {
@@ -60,76 +59,62 @@ describe('ReactClass-spec', function() {
});
it('should warn on invalid prop types', function() {
- var warn = console.error;
- console.error = jest.genMockFn();
- try {
-
- React.createClass({
- displayName: 'Component',
- propTypes: {
- prop: null,
- },
- render: function() {
- return <span>{this.props.prop}</span>;
- },
- });
- expect(console.error.mock.calls.length).toBe(1);
- expect(console.error.mock.calls[0][0]).toBe(
- 'Warning: Component: prop type `prop` is invalid; ' +
- 'it must be a function, usually from React.PropTypes.'
- );
- } finally {
- console.error = warn;
- }
+ spyOn(console, 'error');
+ React.createClass({
+ displayName: 'Component',
+ propTypes: {
+ prop: null,
+ },
+ render: function() {
+ return <span>{this.props.prop}</span>;
+ },
+ });
+ expect(console.error.argsForCall.length).toBe(1);
+ expect(console.error.argsForCall[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', function() {
- var warn = console.error;
- console.error = jest.genMockFn();
- try {
- React.createClass({
- displayName: 'Component',
- contextTypes: {
- prop: null,
- },
- render: function() {
- return <span>{this.props.prop}</span>;
- },
- });
- expect(console.error.mock.calls.length).toBe(1);
- expect(console.error.mock.calls[0][0]).toBe(
- 'Warning: Component: context type `prop` is invalid; ' +
- 'it must be a function, usually from React.PropTypes.'
- );
- } finally {
- console.error = warn;
- }
+ spyOn(console, 'error');
+ React.createClass({
+ displayName: 'Component',
+ contextTypes: {
+ prop: null,
+ },
+ render: function() {
+ return <span>{this.props.prop}</span>;
+ },
+ });
+ expect(console.error.argsForCall.length).toBe(1);
+ expect(console.error.argsForCall[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', function() {
- var warn = console.error;
- console.error = jest.genMockFn();
- try {
- React.createClass({
- displayName: 'Component',
- childContextTypes: {
- prop: null,
- },
- render: function() {
- return <span>{this.props.prop}</span>;
- },
- });
- expect(console.error.mock.calls.length).toBe(1);
- expect(console.error.mock.calls[0][0]).toBe(
- 'Warning: Component: child context type `prop` is invalid; ' +
- 'it must be a function, usually from React.PropTypes.'
- );
- } finally {
- console.error = warn;
- }
+ spyOn(console, 'error');
+ React.createClass({
+ displayName: 'Component',
+ childContextTypes: {
+ prop: null,
+ },
+ render: function() {
+ return <span>{this.props.prop}</span>;
+ },
+ });
+ expect(console.error.argsForCall.length).toBe(1);
+ expect(console.error.argsForCall[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', function() {
+ spyOn(console, 'error');
+
React.createClass({
componentShouldUpdate: function() {
return false;
@@ -163,6 +148,7 @@ describe('ReactClass-spec', function() {
});
it('should warn when mispelling componentWillReceiveProps', function() {
+ spyOn(console, 'error');
React.createClass({
componentWillRecieveProps: function() {
return false;
@@ -204,6 +190,7 @@ describe('ReactClass-spec', function() {
// TODO: Consider actually moving these to statics or drop this unit test.
xit('should warn when using deprecated non-static spec keys', function() {
+ spyOn(console, 'error');
React.createClass({
mixins: [{}],
propTypes: {
@@ -348,6 +335,7 @@ describe('ReactClass-spec', function() {
});
it('should throw when using legacy factories', function() {
+ spyOn(console, 'error');
var Component = React.createClass({
render() {
return <div />;
@@ -355,7 +343,7 @@ describe('ReactClass-spec', function() {
});
expect(() => Component()).toThrow();
- expect(console.error.calls.length).toBe(1);
+ expect(console.error.argsForCall.length).toBe(1);
expect(console.error.argsForCall[0][0]).toBe(
'Warning: Something is calling a React component directly. Use a ' +
'factory or JSX instead. See: https://fb.me/react-legacyfactory'
diff --git a/src/isomorphic/classic/element/ReactElement.js b/src/isomorphic/classic/element/ReactElement.js
index 6e37159..ee46a5f 100644
--- a/src/isomorphic/classic/element/ReactElement.js
+++ b/src/isomorphic/classic/element/ReactElement.js
@@ -13,7 +13,6 @@
var ReactCurrentOwner = require('ReactCurrentOwner');
-var assign = require('Object.assign');
var warning = require('warning');
var canDefineProperty = require('canDefineProperty');
@@ -253,7 +252,7 @@ ReactElement.cloneElement = function(element, config, children) {
var propName;
// Original props are copied
- var props = assign({}, element.props);
+ var props = Object.assign({}, element.props);
// Reserved names are extracted
var key = element.key;
diff --git a/src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js b/src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
index abfa976..9601e67 100644
--- a/src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
+++ b/src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
@@ -241,7 +241,6 @@ describe('ReactPropTypes', function() {
return <div>{this.props.label}</div>;
},
});
- spyOn(console, 'error');
});
it('should support components', () => {
@@ -258,6 +257,8 @@ describe('ReactPropTypes', function() {
});
it('should be able to define a single child as label', () => {
+ spyOn(console, 'error');
+
var instance = <Component label={<div />} />;
instance = ReactTestUtils.renderIntoDocument(instance);
@@ -265,6 +266,8 @@ describe('ReactPropTypes', function() {
});
it('should warn when passing no label and isRequired is set', () => {
+ spyOn(console, 'error');
+
var instance = <Component />;
instance = ReactTestUtils.renderIntoDocument(instance);
@@ -782,7 +785,6 @@ describe('ReactPropTypes', function() {
describe('Custom validator', function() {
beforeEach(function() {
jest.resetModuleRegistry();
- spyOn(console, 'error');
});
it('should have been called with the right params', function() {
@@ -820,6 +822,8 @@ describe('ReactPropTypes', function() {
});
it('should have received the validator\'s return value', function() {
+ spyOn(console, 'error');
+
var spy = jasmine.createSpy().andCallFake(
function(props, propName, componentName) {
if (props[propName] !== 5) {
@@ -845,6 +849,8 @@ describe('ReactPropTypes', function() {
it('should not warn if the validator returned null',
function() {
+ spyOn(console, 'error');
+
var spy = jasmine.createSpy().andCallFake(
function(props, propName, componentName) {
return null;
diff --git a/src/isomorphic/deprecated/OrderedMap.js b/src/isomorphic/deprecated/OrderedMap.js
index 92cea05..4336cdc 100644
--- a/src/isomorphic/deprecated/OrderedMap.js
+++ b/src/isomorphic/deprecated/OrderedMap.js
@@ -11,7 +11,6 @@
'use strict';
-var assign = require('Object.assign');
var invariant = require('invariant');
var PREFIX = 'key:';
@@ -475,7 +474,7 @@ var OrderedMapMethods = {
},
};
-assign(OrderedMapImpl.prototype, OrderedMapMethods);
+Object.assign(OrderedMapImpl.prototype, OrderedMapMethods);
var OrderedMap = {
from: function(orderedMap) {
diff --git a/src/isomorphic/deprecated/ReactPropTransferer.js b/src/isomorphic/deprecated/ReactPropTransferer.js
index b3e2a82..da009ca 100644
--- a/src/isomorphic/deprecated/ReactPropTransferer.js
+++ b/src/isomorphic/deprecated/ReactPropTransferer.js
@@ -11,7 +11,6 @@
'use strict';
-var assign = require('Object.assign');
var emptyFunction = require('emptyFunction');
var joinClasses = require('joinClasses');
@@ -36,7 +35,7 @@ var transferStrategyMerge = createTransferStrategy(function(a, b) {
// `merge` overrides the first object's (`props[key]` above) keys using the
// second object's (`value`) keys. An object's style's existing `propA` would
// get overridden. Flip the order here.
- return assign({}, b, a);
+ return Object.assign({}, b, a);
});
/**
@@ -100,7 +99,7 @@ var ReactPropTransferer = {
* @return {object} a new object containing both sets of props merged.
*/
mergeProps: function(oldProps, newProps) {
- return transferInto(assign({}, oldProps), newProps);
+ return transferInto(Object.assign({}, oldProps), newProps);
},
};
diff --git a/src/isomorphic/modern/class/ReactComponent.js b/src/isomorphic/modern/class/ReactComponent.js
index a7d8b4b..a775abd 100644
--- a/src/isomorphic/modern/class/ReactComponent.js
+++ b/src/isomorphic/modern/class/ReactComponent.js
@@ -76,7 +76,7 @@ ReactComponent.prototype.setState = function(partialState, callback) {
}
this.updater.enqueueSetState(this, partialState);
if (callback) {
- this.updater.enqueueCallback(this, callback);
+ this.updater.enqueueCallback(this, callback, 'setState');
}
};
@@ -97,7 +97,7 @@ ReactComponent.prototype.setState = function(partialState, callback) {
ReactComponent.prototype.forceUpdate = function(callback) {
this.updater.enqueueForceUpdate(this);
if (callback) {
- this.updater.enqueueCallback(this, callback);
+ this.updater.enqueueCallback(this, callback, 'forceUpdate');
}
};
diff --git a/src/renderers/dom/__tests__/ReactDOMProduction-test.js b/src/renderers/dom/__tests__/ReactDOMProduction-test.js
index 8850655..dd5a96a 100644
--- a/src/renderers/dom/__tests__/ReactDOMProduction-test.js
+++ b/src/renderers/dom/__tests__/ReactDOMProduction-test.js
@@ -18,7 +18,7 @@ describe('ReactDOMProduction', function() {
var ReactDOM;
beforeEach(function() {
- __DEV__ = true;
+ __DEV__ = false;
oldProcess = process;
global.process = {env: {NODE_ENV: 'production'}};
@@ -28,7 +28,7 @@ describe('ReactDOMProduction', function() {
});
afterEach(function() {
- __DEV__ = false;
+ __DEV__ = true;
global.process = oldProcess;
});
diff --git a/src/renderers/dom/client/ReactBrowserEventEmitter.js b/src/renderers/dom/client/ReactBrowserEventEmitter.js
index 5f5dc8d..dd7334f 100644
--- a/src/renderers/dom/client/ReactBrowserEventEmitter.js
+++ b/src/renderers/dom/client/ReactBrowserEventEmitter.js
@@ -16,7 +16,6 @@ var EventPluginRegistry = require('EventPluginRegistry');
var ReactEventEmitterMixin = require('ReactEventEmitterMixin');
var ViewportMetrics = require('ViewportMetrics');
-var assign = require('Object.assign');
var getVendorPrefixedEventName = require('getVendorPrefixedEventName');
var isEventSupported = require('isEventSupported');
@@ -175,7 +174,7 @@ function getListeningForDocument(mountAt) {
*
* @internal
*/
-var ReactBrowserEventEmitter = assign({}, ReactEventEmitterMixin, {
+var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, {
/**
* Injectable event backend
diff --git a/src/renderers/dom/client/ReactEventListener.js b/src/renderers/dom/client/ReactEventListener.js
index 1d69e02..bbdbbcf 100644
--- a/src/renderers/dom/client/ReactEventListener.js
+++ b/src/renderers/dom/client/ReactEventListener.js
@@ -17,7 +17,6 @@ var PooledClass = require('PooledClass');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactUpdates = require('ReactUpdates');
-var assign = require('Object.assign');
var getEventTarget = require('getEventTarget');
var getUnboundedScrollPosition = require('getUnboundedScrollPosition');
@@ -44,7 +43,7 @@ function TopLevelCallbackBookKeeping(topLevelType, nativeEvent) {
this.nativeEvent = nativeEvent;
this.ancestors = [];
}
-assign(TopLevelCallbackBookKeeping.prototype, {
+Object.assign(TopLevelCallbackBookKeeping.prototype, {
destructor: function() {
this.topLevelType = null;
this.nativeEvent = null;
diff --git a/src/renderers/dom/client/ReactMount.js b/src/renderers/dom/client/ReactMount.js
index ac6e684..7f6f8ca 100644
--- a/src/renderers/dom/client/ReactMount.js
+++ b/src/renderers/dom/client/ReactMount.js
@@ -382,6 +382,7 @@ var ReactMount = {
},
_renderSubtreeIntoContainer: function(parentComponent, nextElement, container, callback) {
+ ReactUpdateQueue.validateCallback(callback, 'ReactDOM.render');
invariant(
ReactElement.isValidElement(nextElement),
'ReactDOM.render(): Invalid component element.%s',
diff --git a/src/renderers/dom/client/ReactReconcileTransaction.js b/src/renderers/dom/client/ReactReconcileTransaction.js
index 1835a2d..fedd883 100644
--- a/src/renderers/dom/client/ReactReconcileTransaction.js
+++ b/src/renderers/dom/client/ReactReconcileTransaction.js
@@ -17,7 +17,6 @@ var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
var ReactInputSelection = require('ReactInputSelection');
var Transaction = require('Transaction');
-var assign = require('Object.assign');
/**
* Ensures that, when possible, the selection range (currently selected text
@@ -160,7 +159,7 @@ var Mixin = {
};
-assign(ReactReconcileTransaction.prototype, Transaction.Mixin, Mixin);
+Object.assign(ReactReconcileTransaction.prototype, Transaction.Mixin, Mixin);
PooledClass.addPoolingTo(ReactReconcileTransaction);
diff --git a/src/renderers/dom/client/__tests__/ReactDOM-test.js b/src/renderers/dom/client/__tests__/ReactDOM-test.js
index b55381f..9c3cef5 100644
--- a/src/renderers/dom/client/__tests__/ReactDOM-test.js
+++ b/src/renderers/dom/client/__tests__/ReactDOM-test.js
@@ -118,4 +118,63 @@ describe('ReactDOM', function() {
expect(console.error.argsForCall.length).toBe(0);
});
+ it('throws in render() if the mount callback is not a function', function() {
+ function Foo() {
+ this.a = 1;
+ this.b = 2;
+ }
+ var A = React.createClass({
+ getInitialState: function() {
+ return {};
+ },
+ render: function() {
+ return <div />;
+ },
+ });
+
+ var myDiv = document.createElement('div');
+ expect(() => ReactDOM.render(<A />, myDiv, 'no')).toThrow(
+ 'ReactDOM.render(...): Expected the last optional `callback` argument ' +
+ 'to be a function. Instead received: string.'
+ );
+ expect(() => ReactDOM.render(<A />, myDiv, {})).toThrow(
+ 'ReactDOM.render(...): Expected the last optional `callback` argument ' +
+ 'to be a function. Instead received: Object.'
+ );
+ expect(() => ReactDOM.render(<A />, myDiv, new Foo())).toThrow(
+ 'ReactDOM.render(...): Expected the last optional `callback` argument ' +
+ 'to be a function. Instead received: Foo (keys: a, b).'
+ );
+ });
+
+ it('throws in render() if the update callback is not a function', function() {
+ function Foo() {
+ this.a = 1;
+ this.b = 2;
+ }
+ var A = React.createClass({
+ getInitialState: function() {
+ return {};
+ },
+ render: function() {
+ return <div />;
+ },
+ });
+
+ var myDiv = document.createElement('div');
+ ReactDOM.render(<A />, myDiv);
+
+ expect(() => ReactDOM.render(<A />, myDiv, 'no')).toThrow(
+ 'ReactDOM.render(...): Expected the last optional `callback` argument ' +
+ 'to be a function. Instead received: string.'
+ );
+ expect(() => ReactDOM.render(<A />, myDiv, {})).toThrow(
+ 'ReactDOM.render(...): Expected the last optional `callback` argument ' +
+ 'to be a function. Instead received: Object.'
+ );
+ expect(() => ReactDOM.render(<A />, myDiv, new Foo())).toThrow(
+ 'ReactDOM.render(...): Expected the last optional `callback` argument ' +
+ 'to be a function. Instead received: Foo (keys: a, b).'
+ );
+ });
});
diff --git a/src/renderers/dom/client/__tests__/findDOMNode-test.js b/src/renderers/dom/client/__tests__/findDOMNode-test.js
index cdcc780..1f861dd 100644
--- a/src/renderers/dom/client/__tests__/findDOMNode-test.js
+++ b/src/renderers/dom/client/__tests__/findDOMNode-test.js
@@ -58,4 +58,17 @@ describe('findDOMNode', function() {
);
});
+ it('findDOMNode should not throw an error when called within a component that is not mounted', function() {
+ var Bar = React.createClass({
+ componentWillMount: function() {
+ expect(ReactDOM.findDOMNode(this)).toBeNull();
+ },
+ render: function() {
+ return <div/>;
+ },
+ });
+
+ expect(() => ReactTestUtils.renderIntoDocument(<Bar/>)).not.toThrow();
+ });
+
});
diff --git a/src/renderers/dom/client/eventPlugins/FallbackCompositionState.js b/src/renderers/dom/client/eventPlugins/FallbackCompositionState.js
index 68c1284..e26e259 100644
--- a/src/renderers/dom/client/eventPlugins/FallbackCompositionState.js
+++ b/src/renderers/dom/client/eventPlugins/FallbackCompositionState.js
@@ -13,7 +13,6 @@
var PooledClass = require('PooledClass');
-var assign = require('Object.assign');
var getTextContentAccessor = require('getTextContentAccessor');
/**
@@ -33,7 +32,7 @@ function FallbackCompositionState(root) {
this._fallbackText = null;
}
-assign(FallbackCompositionState.prototype, {
+Object.assign(FallbackCompositionState.prototype, {
destructor: function() {
this._root = null;
this._startText = null;
diff --git a/src/renderers/dom/client/syntheticEvents/SyntheticEvent.js b/src/renderers/dom/client/syntheticEvents/SyntheticEvent.js
index daad64d..d3cdf3c 100644
--- a/src/renderers/dom/client/syntheticEvents/SyntheticEvent.js
+++ b/src/renderers/dom/client/syntheticEvents/SyntheticEvent.js
@@ -13,7 +13,6 @@
var PooledClass = require('PooledClass');
-var assign = require('Object.assign');
var emptyFunction = require('emptyFunction');
var warning = require('warning');
@@ -111,7 +110,7 @@ function SyntheticEvent(dispatchConfig, targetInst, nativeEvent, nativeEventTarg
return this;
}
-assign(SyntheticEvent.prototype, {
+Object.assign(SyntheticEvent.prototype, {
preventDefault: function() {
this.defaultPrevented = true;
@@ -229,11 +228,11 @@ SyntheticEvent.augmentClass = function(Class, Interface) {
E.prototype = Super.prototype;
var prototype = new E();
- assign(prototype, Class.prototype);
+ Object.assign(prototype, Class.prototype);
Class.prototype = prototype;
Class.prototype.constructor = Class;
- Class.Interface = assign({}, Super.Interface, Interface);
+ Class.Interface = Object.assign({}, Super.Interface, Interface);
Class.augmentClass = Super.augmentClass;
PooledClass.addPoolingTo(Class, PooledClass.fourArgumentPooler);
diff --git a/src/renderers/dom/client/syntheticEvents/__tests__/SyntheticClipboardEvent-test.js b/src/renderers/dom/client/syntheticEvents/__tests__/SyntheticClipboardEvent-test.js
new file mode 100644
index 0000000..7776f54
--- /dev/null
+++ b/src/renderers/dom/client/syntheticEvents/__tests__/SyntheticClipboardEvent-test.js
@@ -0,0 +1,74 @@
+/**
+ * Copyright 2016-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 SyntheticClipboardEvent;
+
+describe('SyntheticClipboardEvent', function() {
+ var createEvent;
+
+ beforeEach(function() {
+ SyntheticClipboardEvent = require('SyntheticClipboardEvent');
+ createEvent = function(nativeEvent) {
+ var target = require('getEventTarget')(nativeEvent);
+ return SyntheticClipboardEvent.getPooled({}, '', nativeEvent, target);
+ };
+ });
+
+ describe('ClipboardEvent interface', function() {
+ describe('clipboardData', function() {
+ describe('when event has clipboardData', function() {
+ it("returns event's clipboardData", function() {
+ // Mock clipboardData since native implementation doesn't have a constructor
+ var clipboardData = jasmine.createSpyObj(
+ 'clipboardData',
+ ['dropEffect', 'effectAllowed', 'files', 'items', 'types']
+ );
+ var clipboardEvent = createEvent({clipboardData: clipboardData});
+
+ expect(clipboardEvent.clipboardData).toBe(clipboardData);
+ });
+ });
+ });
+ });
+
+ describe('EventInterface', function() {
+ it('normalizes properties from the Event interface', function() {
+ var target = document.createElement('div');
+ var syntheticEvent = createEvent({srcElement: target});
+
+ expect(syntheticEvent.target).toBe(target);
+ expect(syntheticEvent.type).toBe(undefined);
+ });
+
+ it('is able to `preventDefault` and `stopPropagation`', function() {
+ var nativeEvent = {};
+ var syntheticEvent = createEvent(nativeEvent);
+
+ expect(syntheticEvent.isDefaultPrevented()).toBe(false);
+ syntheticEvent.preventDefault();
+ expect(syntheticEvent.isDefaultPrevented()).toBe(true);
+
+ expect(syntheticEvent.isPropagationStopped()).toBe(false);
+ syntheticEvent.stopPropagation();
+ expect(syntheticEvent.isPropagationStopped()).toBe(true);
+ });
+
+ it('is able to `persist`', function() {
+ var syntheticEvent = createEvent({});
+
+ expect(syntheticEvent.isPersistent()).toBe(false);
+ syntheticEvent.persist();
+ expect(syntheticEvent.isPersistent()).toBe(true);
+ });
+ });
+});
diff --git a/src/renderers/dom/client/syntheticEvents/__tests__/SyntheticKeyboardEvent-test.js b/src/renderers/dom/client/syntheticEvents/__tests__/SyntheticKeyboardEvent-test.js
new file mode 100644
index 0000000..9c75a70
--- /dev/null
+++ b/src/renderers/dom/client/syntheticEvents/__tests__/SyntheticKeyboardEvent-test.js
@@ -0,0 +1,123 @@
+/**
+ * Copyright 2016-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 SyntheticKeyboardEvent;
+var getEventCharCode;
+
+describe('SyntheticKeyboardEvent', function() {
+ var createEvent;
+
+ beforeEach(function() {
+ // Mock getEventCharCode for proper unit testing
+ jest.mock('getEventCharCode');
+ getEventCharCode = require('getEventCharCode');
+
+ SyntheticKeyboardEvent = require('SyntheticKeyboardEvent');
+ createEvent = function(nativeEvent) {
+ var target = require('getEventTarget')(nativeEvent);
+ return SyntheticKeyboardEvent.getPooled({}, '', nativeEvent, target);
+ };
+ });
+
+ describe('KeyboardEvent interface', function() {
+ describe('charCode', function() {
+ describe('when event is `keypress`', function() {
+ it('returns whatever getEventCharCode returns', function() {
+ getEventCharCode.mockReturnValue(100500);
+ var keyboardEvent = createEvent({type: 'keypress', charCode: 50});
+
+ expect(keyboardEvent.charCode).toBe(100500);
+ });
+ });
+
+ describe('when event is not `keypress`', function() {
+ it('returns 0', function() {
+ var keyboardEvent = createEvent({type: 'keyup', charCode: 50});
+ expect(keyboardEvent.charCode).toBe(0);
+ });
+ });
+ });
+
+ describe('keyCode', function() {
+ describe('when event is `keydown` or `keyup`', function() {
+ it('returns a passed keyCode', function() {
+ var keyboardEvent = createEvent({type: 'keyup', keyCode: 40});
+ expect(keyboardEvent.keyCode).toBe(40);
+ });
+ });
+
+ describe('when event is `keypress`', function() {
+ it('returns 0', function() {
+ var keyboardEvent = createEvent({type: 'keypress', charCode: 40});
+ expect(keyboardEvent.keyCode).toBe(0);
+ });
+ });
+ });
+
+ describe('which', function() {
+ describe('when event is `keypress`', function() {
+ it('returns whatever getEventCharCode returns', function() {
+ getEventCharCode.mockReturnValue(9001);
+ var keyboardEvent = createEvent({type: 'keypress', charCode: 50});
+
+ expect(keyboardEvent.which).toBe(9001);
+ });
+ });
+
+ describe('when event is `keydown` or `keyup`', function() {
+ it('returns a passed keyCode', function() {
+ var keyboardEvent = createEvent({type: 'keyup', keyCode: 40});
+ expect(keyboardEvent.which).toBe(40);
+ });
+ });
+
+ describe('when event type is unknown', function() {
+ it('returns 0', function() {
+ var keyboardEvent = createEvent({type: 'keysmack', keyCode: 40});
+ expect(keyboardEvent.which).toBe(0);
+ });
+ });
+ });
+ });
+
+ describe('EventInterface', function() {
+ it('normalizes properties from the Event interface', function() {
+ var target = document.createElement('div');
+ var syntheticEvent = createEvent({srcElement: target});
+
+ expect(syntheticEvent.target).toBe(target);
+ expect(syntheticEvent.type).toBe(undefined);
+ });
+
+ it('is able to `preventDefault` and `stopPropagation`', function() {
+ var nativeEvent = {};
+ var syntheticEvent = createEvent(nativeEvent);
+
+ expect(syntheticEvent.isDefaultPrevented()).toBe(false);
+ syntheticEvent.preventDefault();
+ expect(syntheticEvent.isDefaultPrevented()).toBe(true);
+
+ expect(syntheticEvent.isPropagationStopped()).toBe(false);
+ syntheticEvent.stopPropagation();
+ expect(syntheticEvent.isPropagationStopped()).toBe(true);
+ });
+
+ it('is able to `persist`', function() {
+ var syntheticEvent = createEvent({});
+
+ expect(syntheticEvent.isPersistent()).toBe(false);
+ syntheticEvent.persist();
+ expect(syntheticEvent.isPersistent()).toBe(true);
+ });
+ });
+});
diff --git a/src/renderers/dom/client/utils/DOMChildrenOperations.js b/src/renderers/dom/client/utils/DOMChildrenOperations.js
index 40bf13e..f123aef 100644
--- a/src/renderers/dom/client/utils/DOMChildrenOperations.js
+++ b/src/renderers/dom/client/utils/DOMChildrenOperations.js
@@ -129,8 +129,6 @@ var DOMChildrenOperations = {
dangerouslyReplaceNodeWithMarkup: Danger.dangerouslyReplaceNodeWithMarkup,
- updateTextContent: setTextContent,
-
replaceDelimitedText: replaceDelimitedText,
/**
@@ -180,7 +178,6 @@ var DOMChildrenOperations = {
};
ReactPerf.measureMethods(DOMChildrenOperations, 'DOMChildrenOperations', {
- updateTextContent: 'updateTextContent',
replaceDelimitedText: 'replaceDelimitedText',
});
diff --git a/src/renderers/dom/client/utils/__tests__/getEventCharCode-test.js b/src/renderers/dom/client/utils/__tests__/getEventCharCode-test.js
new file mode 100644
index 0000000..c375ffa
--- /dev/null
+++ b/src/renderers/dom/client/utils/__tests__/getEventCharCode-test.js
@@ -0,0 +1,89 @@
+/**
+ * Copyright 2016-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 getEventCharCode = require('getEventCharCode');
+
+describe('getEventCharCode', function() {
+ describe('when charCode is present in nativeEvent', function() {
+ describe('when charCode is 0 and keyCode is 13', function() {
+ it('returns 13', function() {
+ var nativeEvent = new KeyboardEvent(
+ 'keypress', {charCode: 0, keyCode: 13}
+ );
+
+ expect(getEventCharCode(nativeEvent)).toBe(13);
+ });
+ });
+
+ describe('when charCode is not 0 and/or keyCode is not 13', function() {
+ describe('when charCode is 32 or bigger', function() {
+ it('returns charCode', function() {
+ var nativeEvent = new KeyboardEvent('keypress', {charCode: 32});
+
+ expect(getEventCharCode(nativeEvent)).toBe(32);
+ });
+ });
+
+ describe('when charCode is smaller than 32', function() {
+ describe('when charCode is 13', function() {
+ it('returns 13', function() {
+ var nativeEvent = new KeyboardEvent('keypress', {charCode: 13});
+
+ expect(getEventCharCode(nativeEvent)).toBe(13);
+ });
+ });
+
+ describe('when charCode is not 13', function() {
+ it('returns 0', function() {
+ var nativeEvent = new KeyboardEvent('keypress', {charCode: 31});
+
+ expect(getEventCharCode(nativeEvent)).toBe(0);
+ });
+ });
+ });
+ });
+ });
+
+ /**
+ nativeEvent is represented as a plain object here to ease testing, because
+ KeyboardEvent's 'charCode' event key cannot be deleted to simulate a missing
+ charCode key.
+ */
+ describe('when charCode is not present in nativeEvent', function() {
+ describe('when keyCode is 32 or bigger', function() {
+ it('returns keyCode', function() {
+ var nativeEvent = {'keyCode': 32};
+
+ expect(getEventCharCode(nativeEvent)).toBe(32);
+ });
+ });
+
+ describe('when keyCode is smaller than 32', function() {
+ describe('when keyCode is 13', function() {
+ it('returns 13', function() {
+ var nativeEvent = {'keyCode': 13};
+
+ expect(getEventCharCode(nativeEvent)).toBe(13);
+ });
+ });
+
+ describe('when keyCode is not 13', function() {
+ it('returns 0', function() {
+ var nativeEvent = {'keyCode': 31};
+
+ expect(getEventCharCode(nativeEvent)).toBe(0);
+ });
+ });
+ });
+ });
+});
diff --git a/src/renderers/dom/client/utils/__tests__/getEventKey-test.js b/src/renderers/dom/client/utils/__tests__/getEventKey-test.js
new file mode 100644
index 0000000..09644c6
--- /dev/null
+++ b/src/renderers/dom/client/utils/__tests__/getEventKey-test.js
@@ -0,0 +1,80 @@
+/**
+ * Copyright 2016-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 getEventKey = require('getEventKey');
+
+describe('getEventKey', function() {
+ describe('when key is implemented in a browser', function() {
+ describe('when key is not normalized', function() {
+ it('returns a normalized value', function() {
+ var nativeEvent = new KeyboardEvent('keypress', {key: 'Del'});
+
+ expect(getEventKey(nativeEvent)).toBe('Delete');
+ });
+ });
+
+ describe('when key is normalized', function() {
+ it('returns a key', function() {
+ var nativeEvent = new KeyboardEvent('keypress', {key: 'f'});
+
+ expect(getEventKey(nativeEvent)).toBe('f');
+ });
+ });
+ });
+
+ describe('when key is not implemented in a browser', function() {
+ describe('when event type is keypress', function() {
+ describe('when charCode is 13', function() {
+ it("returns 'Enter'", function() {
+ var nativeEvent = new KeyboardEvent('keypress', {charCode: 13});
+
+ expect(getEventKey(nativeEvent)).toBe('Enter');
+ });
+ });
+
+ describe('when charCode is not 13', function() {
+ it('returns a string from a charCode', function() {
+ var nativeEvent = new KeyboardEvent('keypress', {charCode: 65});
+
+ expect(getEventKey(nativeEvent)).toBe('A');
+ });
+ });
+ });
+
+ describe('when event type is keydown or keyup', function() {
+ describe('when keyCode is recognized', function() {
+ it('returns a translated key', function() {
+ var nativeEvent = new KeyboardEvent('keydown', {keyCode: 45});
+
+ expect(getEventKey(nativeEvent)).toBe('Insert');
+ });
+ });
+
+ describe('when keyCode is not recognized', function() {
+ it('returns Unidentified', function() {
+ var nativeEvent = new KeyboardEvent('keydown', {keyCode: 1337});
+
+ expect(getEventKey(nativeEvent)).toBe('Unidentified');
+ });
+ });
+ });
+
+ describe('when event type is unknown', function() {
+ it('returns an empty string', function() {
+ var nativeEvent = new KeyboardEvent('keysmack');
+
+ expect(getEventKey(nativeEvent)).toBe('');
+ });
+ });
+ });
+});
diff --git a/src/renderers/dom/client/utils/setInnerHTML.js b/src/renderers/dom/client/utils/setInnerHTML.js
index 222ddfb..1e491bf 100644
--- a/src/renderers/dom/client/utils/setInnerHTML.js
+++ b/src/renderers/dom/client/utils/setInnerHTML.js
@@ -79,6 +79,7 @@ if (ExecutionEnvironment.canUseDOM) {
}
};
}
+ testElement = null;
}
module.exports = setInnerHTML;
diff --git a/src/renderers/dom/client/validateDOMNesting.js b/src/renderers/dom/client/validateDOMNesting.js
index d479ea9..431d80f 100644
--- a/src/renderers/dom/client/validateDOMNesting.js
+++ b/src/renderers/dom/client/validateDOMNesting.js
@@ -11,7 +11,6 @@
'use strict';
-var assign = require('Object.assign');
var emptyFunction = require('emptyFunction');
var warning = require('warning');
@@ -76,7 +75,7 @@ if (__DEV__) {
};
var updatedAncestorInfo = function(oldInfo, tag, instance) {
- var ancestorInfo = assign({}, oldInfo || emptyAncestorInfo);
+ var ancestorInfo = Object.assign({}, oldInfo || emptyAncestorInfo);
var info = {tag: tag, instance: instance};
if (inScopeTags.indexOf(tag) !== -1) {
diff --git a/src/renderers/dom/client/wrappers/ReactDOMInput.js b/src/renderers/dom/client/wrappers/ReactDOMInput.js
index e52c199..e9cd9b5 100644
--- a/src/renderers/dom/client/wrappers/ReactDOMInput.js
+++ b/src/renderers/dom/client/wrappers/ReactDOMInput.js
@@ -16,7 +16,6 @@ var LinkedValueUtils = require('LinkedValueUtils');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactUpdates = require('ReactUpdates');
-var assign = require('Object.assign');
var invariant = require('invariant');
var warning = require('warning');
@@ -69,7 +68,7 @@ var ReactDOMInput = {
var value = LinkedValueUtils.getValue(props);
var checked = LinkedValueUtils.getChecked(props);
- var nativeProps = assign({
+ var nativeProps = Object.assign({
// Make sure we set .type before any other properties (setting .value
// before .type means .value is lost in IE11 and below)
type: undefined,
diff --git a/src/renderers/dom/client/wrappers/ReactDOMOption.js b/src/renderers/dom/client/wrappers/ReactDOMOption.js
index 31e6050..917ed0e 100644
--- a/src/renderers/dom/client/wrappers/ReactDOMOption.js
+++ b/src/renderers/dom/client/wrappers/ReactDOMOption.js
@@ -14,7 +14,6 @@
var ReactChildren = require('ReactChildren');
var ReactDOMSelect = require('ReactDOMSelect');
-var assign = require('Object.assign');
var warning = require('warning');
/**
@@ -59,7 +58,7 @@ var ReactDOMOption = {
},
getNativeProps: function(inst, props) {
- var nativeProps = assign({selected: undefined, children: undefined}, props);
+ var nativeProps = Object.assign({selected: undefined, children: undefined}, props);
// Read state only from initial mount because <select> updates value
// manually; we need the initial state only for server rendering
diff --git a/src/renderers/dom/client/wrappers/ReactDOMSelect.js b/src/renderers/dom/client/wrappers/ReactDOMSelect.js
index f0ac23f..2247fb6 100644
--- a/src/renderers/dom/client/wrappers/ReactDOMSelect.js
+++ b/src/renderers/dom/client/wrappers/ReactDOMSelect.js
@@ -15,7 +15,6 @@ var LinkedValueUtils = require('LinkedValueUtils');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactUpdates = require('ReactUpdates');
-var assign = require('Object.assign');
var warning = require('warning');
var didWarnValueLink = false;
@@ -159,7 +158,7 @@ function updateOptions(inst, multiple, propValue) {
*/
var ReactDOMSelect = {
getNativeProps: function(inst, props) {
- return assign({}, props, {
+ return Object.assign({}, props, {
onChange: inst._wrapperState.onChange,
value: undefined,
});
diff --git a/src/renderers/dom/client/wrappers/ReactDOMTextarea.js b/src/renderers/dom/client/wrappers/ReactDOMTextarea.js
index ed5ac82..bf5508b 100644
--- a/src/renderers/dom/client/wrappers/ReactDOMTextarea.js
+++ b/src/renderers/dom/client/wrappers/ReactDOMTextarea.js
@@ -16,7 +16,6 @@ var LinkedValueUtils = require('LinkedValueUtils');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactUpdates = require('ReactUpdates');
-var assign = require('Object.assign');
var invariant = require('invariant');
var warning = require('warning');
@@ -68,7 +67,7 @@ var ReactDOMTextarea = {
// Always set children to the same thing. In IE9, the selection range will
// get reset if `textContent` is mutated.
- var nativeProps = assign({}, props, {
+ var nativeProps = Object.assign({}, props, {
defaultValue: undefined,
value: undefined,
children: inst._wrapperState.initialValue,
diff --git a/src/renderers/dom/server/ReactServerRenderingTransaction.js b/src/renderers/dom/server/ReactServerRenderingTransaction.js
index 6e585ef..176a550 100644
--- a/src/renderers/dom/server/ReactServerRenderingTransaction.js
+++ b/src/renderers/dom/server/ReactServerRenderingTransaction.js
@@ -14,7 +14,6 @@
var PooledClass = require('PooledClass');
var Transaction = require('Transaction');
-var assign = require('Object.assign');
/**
* Executed within the scope of the `Transaction` instance. Consider these as
@@ -64,7 +63,7 @@ var Mixin = {
};
-assign(
+Object.assign(
ReactServerRenderingTransaction.prototype,
Transaction.Mixin,
Mixin
diff --git a/src/renderers/dom/shared/CSSPropertyOperations.js b/src/renderers/dom/shared/CSSPropertyOperations.js
index 5044488..aac1b75 100644
--- a/src/renderers/dom/shared/CSSPropertyOperations.js
+++ b/src/renderers/dom/shared/CSSPropertyOperations.js
@@ -52,7 +52,7 @@ if (__DEV__) {
var warnedStyleValues = {};
var warnedForNaNValue = false;
- var warnHyphenatedStyleName = function(name) {
+ var warnHyphenatedStyleName = function(name, owner) {
if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
return;
}
@@ -60,13 +60,14 @@ if (__DEV__) {
warnedStyleNames[name] = true;
warning(
false,
- 'Unsupported style property %s. Did you mean %s?',
+ 'Unsupported style property %s. Did you mean %s?%s',
name,
- camelizeStyleName(name)
+ camelizeStyleName(name),
+ checkRenderMessage(owner)
);
};
- var warnBadVendoredStyleName = function(name) {
+ var warnBadVendoredStyleName = function(name, owner) {
if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
return;
}
@@ -74,13 +75,14 @@ if (__DEV__) {
warnedStyleNames[name] = true;
warning(
false,
- 'Unsupported vendor-prefixed style property %s. Did you mean %s?',
+ 'Unsupported vendor-prefixed style property %s. Did you mean %s?%s',
name,
- name.charAt(0).toUpperCase() + name.slice(1)
+ name.charAt(0).toUpperCase() + name.slice(1),
+ checkRenderMessage(owner)
);
};
- var warnStyleValueWithSemicolon = function(name, value) {
+ var warnStyleValueWithSemicolon = function(name, value, owner) {
if (warnedStyleValues.hasOwnProperty(value) && warnedStyleValues[value]) {
return;
}
@@ -88,14 +90,15 @@ if (__DEV__) {
warnedStyleValues[value] = true;
warning(
false,
- 'Style property values shouldn\'t contain a semicolon. ' +
+ 'Style property values shouldn\'t contain a semicolon.%s ' +
'Try "%s: %s" instead.',
+ checkRenderMessage(owner),
name,
value.replace(badStyleValueWithSemicolonPattern, '')
);
};
- var warnStyleValueIsNaN = function(name, value) {
+ var warnStyleValueIsNaN = function(name, value, owner) {
if (warnedForNaNValue) {
return;
}
@@ -103,26 +106,42 @@ if (__DEV__) {
warnedForNaNValue = true;
warning(
false,
- '`NaN` is an invalid value for the `%s` css style property',
- name
+ '`NaN` is an invalid value for the `%s` css style property.%s',
+ name,
+ checkRenderMessage(owner)
);
};
+ var checkRenderMessage = function(owner) {
+ if (owner) {
+ var name = owner.getName();
+ if (name) {
+ return ' Check the render method of `' + name + '`.';
+ }
+ }
+ return '';
+ };
+
/**
* @param {string} name
* @param {*} value
+ * @param {ReactDOMComponent} component
*/
- var warnValidStyle = function(name, value) {
+ var warnValidStyle = function(name, value, component) {
+ var owner;
+ if (component) {
+ owner = component._currentElement._owner;
+ }
if (name.indexOf('-') > -1) {
- warnHyphenatedStyleName(name);
+ warnHyphenatedStyleName(name, owner);
} else if (badVendoredStyleNamePattern.test(name)) {
- warnBadVendoredStyleName(name);
+ warnBadVendoredStyleName(name, owner);
} else if (badStyleValueWithSemicolonPattern.test(value)) {
- warnStyleValueWithSemicolon(name, value);
+ warnStyleValueWithSemicolon(name, value, owner);
}
if (typeof value === 'number' && isNaN(value)) {
- warnStyleValueIsNaN(name, value);
+ warnStyleValueIsNaN(name, value, owner);
}
};
}
@@ -153,7 +172,7 @@ var CSSPropertyOperations = {
}
var styleValue = styles[styleName];
if (__DEV__) {
- warnValidStyle(styleName, styleValue);
+ warnValidStyle(styleName, styleValue, component);
}
if (styleValue != null) {
serialized += processStyleName(styleName) + ':';
@@ -170,6 +189,7 @@ var CSSPropertyOperations = {
*
* @param {DOMElement} node
* @param {object} styles
+ * @param {ReactDOMComponent} component
*/
setValueForStyles: function(node, styles, component) {
var style = node.style;
@@ -178,7 +198,7 @@ var CSSPropertyOperations = {
continue;
}
if (__DEV__) {
- warnValidStyle(styleName, styles[styleName]);
+ warnValidStyle(styleName, styles[styleName], component);
}
var styleValue = dangerousStyleValue(
styleName,
diff --git a/src/renderers/dom/shared/DOMPropertyOperations.js b/src/renderers/dom/shared/DOMPropertyOperations.js
index 1ce9e29..6108e4d 100644
--- a/src/renderers/dom/shared/DOMPropertyOperations.js
+++ b/src/renderers/dom/shared/DOMPropertyOperations.js
@@ -149,8 +149,10 @@ var DOMPropertyOperations = {
var propName = propertyInfo.propertyName;
// Must explicitly cast values for HAS_SIDE_EFFECTS-properties to the
// property type before comparing; only `value` does and is string.
+ // Must set `value` property if it is not null and not yet set.
if (!propertyInfo.hasSideEffects ||
- ('' + node[propName]) !== ('' + value)) {
+ ('' + node[propName]) !== ('' + value) ||
+ !node.hasAttribute(propertyInfo.attributeName)) {
// Contrary to `setAttribute`, object properties are properly
// `toString`ed by IE8/9.
node[propName] = value;
diff --git a/src/renderers/dom/shared/ReactDOMComponent.js b/src/renderers/dom/shared/ReactDOMComponent.js
index 21a4310..808706d 100644
--- a/src/renderers/dom/shared/ReactDOMComponent.js
+++ b/src/renderers/dom/shared/ReactDOMComponent.js
@@ -35,7 +35,6 @@ var ReactDOMTextarea = require('ReactDOMTextarea');
var ReactMultiChild = require('ReactMultiChild');
var ReactPerf = require('ReactPerf');
-var assign = require('Object.assign');
var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
var invariant = require('invariant');
var isEventSupported = require('isEventSupported');
@@ -53,9 +52,14 @@ var registrationNameModules = EventPluginRegistry.registrationNameModules;
// For quickly matching children type, to test if can be treated as content.
var CONTENT_TYPES = {'string': true, 'number': true};
-var CHILDREN = keyOf({children: null});
var STYLE = keyOf({style: null});
var HTML = keyOf({__html: null});
+var RESERVED_PROPS = {
+ children: null,
+ dangerouslySetInnerHTML: null,
+ suppressContentEditableWarning: null,
+};
+
function getDeclarationErrorAddendum(internalInstance) {
if (internalInstance) {
@@ -182,6 +186,13 @@ function assertValidProps(component, props) {
'those nodes are unexpectedly modified or duplicated. This is ' +
'probably not intentional.'
);
+ warning(
+ props.onFocusIn == null &&
+ props.onFocusOut == null,
+ 'React uses onFocus and onBlur instead of onFocusIn and onFocusOut. ' +
+ 'All React events are normalized to bubble, so onFocusIn and onFocusOut ' +
+ 'are not needed/supported by React.'
+ );
}
invariant(
props.style == null || typeof props.style === 'object',
@@ -369,7 +380,7 @@ var newlineEatingTags = {
// For HTML, certain tags cannot have children. This has the same purpose as
// `omittedCloseTags` except that `menuitem` should still have its closing tag.
-var voidElementTags = assign({
+var voidElementTags = Object.assign({
'menuitem': true,
}, omittedCloseTags);
@@ -629,13 +640,13 @@ ReactDOMComponent.Mixin = {
// See `_updateDOMProperties`. style block
this._previousStyle = propValue;
}
- propValue = this._previousStyleCopy = assign({}, props.style);
+ propValue = this._previousStyleCopy = Object.assign({}, props.style);
}
propValue = CSSPropertyOperations.createMarkupForStyles(propValue, this);
}
var markup = null;
if (this._tag != null && isCustomComponent(this._tag, props)) {
- if (propKey !== CHILDREN) {
+ if (!RESERVED_PROPS.hasOwnProperty(propKey)) {
markup = DOMPropertyOperations.createMarkupForCustomAttribute(propKey, propValue);
}
} else {
@@ -875,7 +886,7 @@ ReactDOMComponent.Mixin = {
);
this._previousStyle = nextProp;
}
- nextProp = this._previousStyleCopy = assign({}, nextProp);
+ nextProp = this._previousStyleCopy = Object.assign({}, nextProp);
} else {
this._previousStyleCopy = null;
}
@@ -907,14 +918,13 @@ ReactDOMComponent.Mixin = {
deleteListener(this, propKey);
}
} else if (isCustomComponent(this._tag, nextProps)) {
- if (propKey === CHILDREN) {
- nextProp = null;
+ if (!RESERVED_PROPS.hasOwnProperty(propKey)) {
+ DOMPropertyOperations.setValueForAttribute(
+ getNode(this),
+ propKey,
+ nextProp
+ );
}
- DOMPropertyOperations.setValueForAttribute(
- getNode(this),
- propKey,
- nextProp
- );
} else if (
DOMProperty.properties[propKey] ||
DOMProperty.isCustomAttribute(propKey)) {
@@ -1053,7 +1063,7 @@ ReactPerf.measureMethods(ReactDOMComponent.Mixin, 'ReactDOMComponent', {
receiveComponent: 'receiveComponent',
});
-assign(
+Object.assign(
ReactDOMComponent.prototype,
ReactDOMComponent.Mixin,
ReactMultiChild.Mixin
diff --git a/src/renderers/dom/shared/ReactDOMEmptyComponent.js b/src/renderers/dom/shared/ReactDOMEmptyComponent.js
index c15ec12..81f8940 100644
--- a/src/renderers/dom/shared/ReactDOMEmptyComponent.js
+++ b/src/renderers/dom/shared/ReactDOMEmptyComponent.js
@@ -14,7 +14,6 @@
var DOMLazyTree = require('DOMLazyTree');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
-var assign = require('Object.assign');
var ReactDOMEmptyComponent = function(instantiate) {
// ReactCompositeComponent uses this:
@@ -25,7 +24,7 @@ var ReactDOMEmptyComponent = function(instantiate) {
this._nativeContainerInfo = null;
this._domID = null;
};
-assign(ReactDOMEmptyComponent.prototype, {
+Object.assign(ReactDOMEmptyComponent.prototype, {
mountComponent: function(
transaction,
nativeParent,
diff --git a/src/renderers/dom/shared/ReactDOMTextComponent.js b/src/renderers/dom/shared/ReactDOMTextComponent.js
index cba2353..9a69c7f 100644
--- a/src/renderers/dom/shared/ReactDOMTextComponent.js
+++ b/src/renderers/dom/shared/ReactDOMTextComponent.js
@@ -16,7 +16,6 @@ var DOMLazyTree = require('DOMLazyTree');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactPerf = require('ReactPerf');
-var assign = require('Object.assign');
var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
var invariant = require('invariant');
var validateDOMNesting = require('validateDOMNesting');
@@ -51,7 +50,7 @@ var ReactDOMTextComponent = function(text) {
this._commentNodes = null;
};
-assign(ReactDOMTextComponent.prototype, {
+Object.assign(ReactDOMTextComponent.prototype, {
/**
* Creates the markup for this text node. This node is not intended to have
diff --git a/src/renderers/dom/shared/__tests__/CSSPropertyOperations-test.js b/src/renderers/dom/shared/__tests__/CSSPropertyOperations-test.js
index 075c29c..d33d2e6 100644
--- a/src/renderers/dom/shared/__tests__/CSSPropertyOperations-test.js
+++ b/src/renderers/dom/shared/__tests__/CSSPropertyOperations-test.js
@@ -108,75 +108,116 @@ describe('CSSPropertyOperations', function() {
});
it('should warn when using hyphenated style names', function() {
+ var Comp = React.createClass({
+ displayName: 'Comp',
+ render: function() {
+ return <div style={{ 'background-color': 'crimson' }}/>;
+ },
+ });
spyOn(console, 'error');
-
- expect(CSSPropertyOperations.createMarkupForStyles({
- 'background-color': 'crimson',
- })).toBe('background-color:crimson;');
-
+ var root = document.createElement('div');
+ ReactDOM.render(<Comp />, root);
expect(console.error.argsForCall.length).toBe(1);
- expect(console.error.argsForCall[0][0]).toContain('backgroundColor');
+ expect(console.error.argsForCall[0][0]).toEqual(
+ 'Warning: Unsupported style property background-color. Did you mean backgroundColor? ' +
+ 'Check the render method of `Comp`.'
+ );
});
it('should warn when updating hyphenated style names', function() {
+ var Comp = React.createClass({
+ displayName: 'Comp',
+ render: function() {
+ return <div style={this.props.style} />;
+ },
+ });
spyOn(console, 'error');
-
- var root = document.createElement('div');
var styles = {
'-ms-transform': 'translate3d(0, 0, 0)',
'-webkit-transform': 'translate3d(0, 0, 0)',
};
-
- ReactDOM.render(<div />, root);
- ReactDOM.render(<div style={styles} />, root);
+ var root = document.createElement('div');
+ ReactDOM.render(<Comp />, root);
+ ReactDOM.render(<Comp style={styles} />, root);
expect(console.error.argsForCall.length).toBe(2);
- expect(console.error.argsForCall[0][0]).toContain('msTransform');
- expect(console.error.argsForCall[1][0]).toContain('WebkitTransform');
+ expect(console.error.argsForCall[0][0]).toEqual(
+ 'Warning: Unsupported style property -ms-transform. Did you mean msTransform? ' +
+ 'Check the render method of `Comp`.'
+ );
+ expect(console.error.argsForCall[1][0]).toEqual(
+ 'Warning: Unsupported style property -webkit-transform. Did you mean WebkitTransform? ' +
+ 'Check the render method of `Comp`.'
+ );
});
it('warns when miscapitalizing vendored style names', function() {
- spyOn(console, 'error');
-
- CSSPropertyOperations.createMarkupForStyles({
- msTransform: 'translate3d(0, 0, 0)',
- oTransform: 'translate3d(0, 0, 0)',
- webkitTransform: 'translate3d(0, 0, 0)',
+ var Comp = React.createClass({
+ displayName: 'Comp',
+ render: function() {
+ return (<div style={{
+ msTransform: 'translate3d(0, 0, 0)',
+ oTransform: 'translate3d(0, 0, 0)',
+ webkitTransform: 'translate3d(0, 0, 0)',
+ }} />);
+ },
});
-
+ spyOn(console, 'error');
+ var root = document.createElement('div');
+ ReactDOM.render(<Comp />, root);
// msTransform is correct already and shouldn't warn
expect(console.error.argsForCall.length).toBe(2);
- expect(console.error.argsForCall[0][0]).toContain('oTransform');
- expect(console.error.argsForCall[0][0]).toContain('OTransform');
- expect(console.error.argsForCall[1][0]).toContain('webkitTransform');
- expect(console.error.argsForCall[1][0]).toContain('WebkitTransform');
+ expect(console.error.argsForCall[0][0]).toEqual(
+ 'Warning: Unsupported vendor-prefixed style property oTransform. ' +
+ 'Did you mean OTransform? Check the render method of `Comp`.'
+ );
+ expect(console.error.argsForCall[1][0]).toEqual(
+ 'Warning: Unsupported vendor-prefixed style property webkitTransform. ' +
+ 'Did you mean WebkitTransform? Check the render method of `Comp`.'
+ );
});
it('should warn about style having a trailing semicolon', function() {
- spyOn(console, 'error');
-
- CSSPropertyOperations.createMarkupForStyles({
- fontFamily: 'Helvetica, arial',
- backgroundImage: 'url(foo;bar)',
- backgroundColor: 'blue;',
- color: 'red; ',
+ var Comp = React.createClass({
+ displayName: 'Comp',
+ render: function() {
+ return (<div style={{
+ fontFamily: 'Helvetica, arial',
+ backgroundImage: 'url(foo;bar)',
+ backgroundColor: 'blue;',
+ color: 'red; ',
+ }} />);
+ },
});
-
+ spyOn(console, 'error');
+ var root = document.createElement('div');
+ ReactDOM.render(<Comp />, root);
expect(console.error.calls.length).toBe(2);
- expect(console.error.argsForCall[0][0]).toContain('Try "backgroundColor: blue" instead');
- expect(console.error.argsForCall[1][0]).toContain('Try "color: red" instead');
+ expect(console.error.argsForCall[0][0]).toEqual(
+ 'Warning: Style property values shouldn\'t contain a semicolon. ' +
+ 'Check the render method of `Comp`. Try "backgroundColor: blue" instead.',
+ );
+ expect(console.error.argsForCall[1][0]).toEqual(
+ 'Warning: Style property values shouldn\'t contain a semicolon. ' +
+ 'Check the render method of `Comp`. Try "color: red" instead.',
+ );
});
it('should warn about style containing a NaN value', function() {
- spyOn(console, 'error');
-
- CSSPropertyOperations.createMarkupForStyles({
- fontSize: NaN,
+ var Comp = React.createClass({
+ displayName: 'Comp',
+ render: function() {
+ return <div style={{ fontSize: NaN }}/>;
+ },
});
+ spyOn(console, 'error');
+ var root = document.createElement('div');
+ ReactDOM.render(<Comp />, root);
expect(console.error.calls.length).toBe(1);
expect(console.error.argsForCall[0][0]).toEqual(
- 'Warning: `NaN` is an invalid value for the `fontSize` css style property'
+ 'Warning: `NaN` is an invalid value for the `fontSize` css style property. ' +
+ 'Check the render method of `Comp`.'
);
});
});
diff --git a/src/renderers/dom/shared/__tests__/DOMPropertyOperations-test.js b/src/renderers/dom/shared/__tests__/DOMPropertyOperations-test.js
index d6d7306..14b7228 100644
--- a/src/renderers/dom/shared/__tests__/DOMPropertyOperations-test.js
+++ b/src/renderers/dom/shared/__tests__/DOMPropertyOperations-test.js
@@ -221,6 +221,14 @@ describe('DOMPropertyOperations', function() {
expect(stubNode.getAttribute('role')).toBe('<html>');
});
+ it('should not remove empty attributes for special properties', function() {
+ stubNode = document.createElement('input');
+ DOMPropertyOperations.setValueForProperty(stubNode, 'value', '');
+ // JSDOM does not behave correctly for attributes/properties
+ //expect(stubNode.getAttribute('value')).toBe('');
+ expect(stubNode.value).toBe('');
+ });
+
it('should remove for falsey boolean properties', function() {
DOMPropertyOperations.setValueForProperty(
stubNode,
diff --git a/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js b/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
index d349620..66eaa96 100644
--- a/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
+++ b/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
@@ -11,7 +11,6 @@
'use strict';
-var assign = require('Object.assign');
describe('ReactDOMComponent', function() {
var React;
@@ -198,7 +197,7 @@ describe('ReactDOMComponent', function() {
expect(console.error.argsForCall.length).toBe(1);
expect(console.error.argsForCall[0][0]).toEqual(
- 'Warning: `NaN` is an invalid value for the `fontSize` css style property',
+ 'Warning: `NaN` is an invalid value for the `fontSize` css style property.',
);
});
@@ -236,16 +235,52 @@ describe('ReactDOMComponent', function() {
expect(stubStyle.display).toEqual('');
});
- it('should skip child object attribute on web components', function() {
+ it('should skip reserved props on web components', function() {
var container = document.createElement('div');
- // Test initial render to null
- ReactDOM.render(<my-component children={['foo']} />, container);
+ ReactDOM.render(
+ <my-component
+ children={['foo']}
+ suppressContentEditableWarning={true}
+ />,
+ container
+ );
expect(container.firstChild.hasAttribute('children')).toBe(false);
+ expect(
+ container.firstChild.hasAttribute('suppressContentEditableWarning')
+ ).toBe(false);
- // Test updates to null
- ReactDOM.render(<my-component children={['foo']} />, container);
+ ReactDOM.render(
+ <my-component
+ children={['bar']}
+ suppressContentEditableWarning={false}
+ />,
+ container
+ );
expect(container.firstChild.hasAttribute('children')).toBe(false);
+ expect(
+ container.firstChild.hasAttribute('suppressContentEditableWarning')
+ ).toBe(false);
+ });
+
+ it('should skip dangerouslySetInnerHTML on web components', function() {
+ var container = document.createElement('div');
+
+ ReactDOM.render(
+ <my-component dangerouslySetInnerHTML={{__html: 'hi'}} />,
+ container
+ );
+ expect(
+ container.firstChild.hasAttribute('dangerouslySetInnerHTML')
+ ).toBe(false);
+
+ ReactDOM.render(
+ <my-component dangerouslySetInnerHTML={{__html: 'bye'}} />,
+ container
+ );
+ expect(
+ container.firstChild.hasAttribute('dangerouslySetInnerHTML')
+ ).toBe(false);
});
it('should remove attributes', function() {
@@ -478,10 +513,10 @@ describe('ReactDOMComponent', function() {
expect(nodeValueSetter.mock.calls.length).toBe(2);
ReactDOM.render(<div value="" />, container);
- expect(nodeValueSetter.mock.calls.length).toBe(2);
+ expect(nodeValueSetter.mock.calls.length).toBe(3);
ReactDOM.render(<div />, container);
- expect(nodeValueSetter.mock.calls.length).toBe(2);
+ expect(nodeValueSetter.mock.calls.length).toBe(3);
});
it('should not incur unnecessary DOM mutations for boolean properties', function() {
@@ -587,7 +622,7 @@ describe('ReactDOMComponent', function() {
this._currentElement = {props: initialProps};
this._rootNodeID = 'test';
};
- assign(NodeStub.prototype, ReactDOMComponent.Mixin);
+ Object.assign(NodeStub.prototype, ReactDOMComponent.Mixin);
genMarkup = function(props) {
var transaction = new ReactReconcileTransaction();
@@ -636,7 +671,7 @@ describe('ReactDOMComponent', function() {
this._currentElement = {props: initialProps};
this._rootNodeID = 'test';
};
- assign(NodeStub.prototype, ReactDOMComponent.Mixin);
+ Object.assign(NodeStub.prototype, ReactDOMComponent.Mixin);
genMarkup = function(props) {
var transaction = new ReactReconcileTransaction();
@@ -1198,5 +1233,17 @@ describe('ReactDOMComponent', function() {
expect(console.error.argsForCall.length).toBe(1);
expect(console.error.argsForCall[0][0]).toContain('className');
});
+
+ it('should warn about props that are no longer supported', function() {
+ spyOn(console, 'error');
+ ReactTestUtils.renderIntoDocument(<div />);
+ expect(console.error.argsForCall.length).toBe(0);
+
+ ReactTestUtils.renderIntoDocument(<div onFocusIn={() => {}} />);
+ expect(console.error.argsForCall.length).toBe(1);
+
+ ReactTestUtils.renderIntoDocument(<div onFocusOut={() => {}} />);
+ expect(console.error.argsForCall.length).toBe(2);
+ });
});
});
diff --git a/src/renderers/shared/event/__tests__/EventPluginRegistry-test.js b/src/renderers/shared/event/__tests__/EventPluginRegistry-test.js
index d781ba2..7f06e58 100644
--- a/src/renderers/shared/event/__tests__/EventPluginRegistry-test.js
+++ b/src/renderers/shared/event/__tests__/EventPluginRegistry-test.js
@@ -11,7 +11,6 @@
'use strict';
-var assign = require('Object.assign');
describe('EventPluginRegistry', function() {
var EventPluginRegistry;
@@ -22,7 +21,7 @@ describe('EventPluginRegistry', function() {
EventPluginRegistry._resetEventPlugins();
createPlugin = function(properties) {
- return assign({extractEvents: function() {}}, properties);
+ return Object.assign({extractEvents: function() {}}, properties);
};
});
diff --git a/src/renderers/shared/reconciler/ReactCompositeComponent.js b/src/renderers/shared/reconciler/ReactCompositeComponent.js
index 9e63629..0472e1f 100644
--- a/src/renderers/shared/reconciler/ReactCompositeComponent.js
+++ b/src/renderers/shared/reconciler/ReactCompositeComponent.js
@@ -24,7 +24,6 @@ var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
var ReactReconciler = require('ReactReconciler');
var ReactUpdateQueue = require('ReactUpdateQueue');
-var assign = require('Object.assign');
var emptyObject = require('emptyObject');
var invariant = require('invariant');
var shouldUpdateReactComponent = require('shouldUpdateReactComponent');
@@ -526,7 +525,7 @@ var ReactCompositeComponentMixin = {
name
);
}
- return assign({}, currentContext, childContext);
+ return Object.assign({}, currentContext, childContext);
}
return currentContext;
},
@@ -759,10 +758,10 @@ var ReactCompositeComponentMixin = {
return queue[0];
}
- var nextState = assign({}, replace ? queue[0] : inst.state);
+ var nextState = Object.assign({}, replace ? queue[0] : inst.state);
for (var i = replace ? 1 : 0; i < queue.length; i++) {
var partial = queue[i];
- assign(
+ Object.assign(
nextState,
typeof partial === 'function' ?
partial.call(inst, nextState, props, context) :
diff --git a/src/renderers/shared/reconciler/ReactDefaultBatchingStrategy.js b/src/renderers/shared/reconciler/ReactDefaultBatchingStrategy.js
index 2023f59..904dc1f 100644
--- a/src/renderers/shared/reconciler/ReactDefaultBatchingStrategy.js
+++ b/src/renderers/shared/reconciler/ReactDefaultBatchingStrategy.js
@@ -14,7 +14,6 @@
var ReactUpdates = require('ReactUpdates');
var Transaction = require('Transaction');
-var assign = require('Object.assign');
var emptyFunction = require('emptyFunction');
var RESET_BATCHED_UPDATES = {
@@ -35,7 +34,7 @@ function ReactDefaultBatchingStrategyTransaction() {
this.reinitializeTransaction();
}
-assign(
+Object.assign(
ReactDefaultBatchingStrategyTransaction.prototype,
Transaction.Mixin,
{
diff --git a/src/renderers/shared/reconciler/ReactNativeComponent.js b/src/renderers/shared/reconciler/ReactNativeComponent.js
index 42fe110..7f28034 100644
--- a/src/renderers/shared/reconciler/ReactNativeComponent.js
+++ b/src/renderers/shared/reconciler/ReactNativeComponent.js
@@ -11,7 +11,6 @@
'use strict';
-var assign = require('Object.assign');
var invariant = require('invariant');
var autoGenerateWrapperClass = null;
@@ -34,7 +33,7 @@ var ReactNativeComponentInjection = {
// This accepts a keyed object with classes as values. Each key represents a
// tag. That particular tag will use this class instead of the generic one.
injectComponentClasses: function(componentClasses) {
- assign(tagToComponentClass, componentClasses);
+ Object.assign(tagToComponentClass, componentClasses);
},
};
diff --git a/src/renderers/shared/reconciler/ReactSimpleEmptyComponent.js b/src/renderers/shared/reconciler/ReactSimpleEmptyComponent.js
index e38fdcb..0565a01 100644
--- a/src/renderers/shared/reconciler/ReactSimpleEmptyComponent.js
+++ b/src/renderers/shared/reconciler/ReactSimpleEmptyComponent.js
@@ -13,13 +13,12 @@
var ReactReconciler = require('ReactReconciler');
-var assign = require('Object.assign');
var ReactSimpleEmptyComponent = function(placeholderElement, instantiate) {
this._currentElement = null;
this._renderedComponent = instantiate(placeholderElement);
};
-assign(ReactSimpleEmptyComponent.prototype, {
+Object.assign(ReactSimpleEmptyComponent.prototype, {
mountComponent: function(
transaction,
nativeParent,
diff --git a/src/renderers/shared/reconciler/ReactUpdateQueue.js b/src/renderers/shared/reconciler/ReactUpdateQueue.js
index d2e7cad..65bdd49 100644
--- a/src/renderers/shared/reconciler/ReactUpdateQueue.js
+++ b/src/renderers/shared/reconciler/ReactUpdateQueue.js
@@ -22,6 +22,19 @@ function enqueueUpdate(internalInstance) {
ReactUpdates.enqueueUpdate(internalInstance);
}
+function formatUnexpectedArgument(arg) {
+ var type = typeof arg;
+ if (type !== 'object') {
+ return type;
+ }
+ var displayName = arg.constructor && arg.constructor.name || type;
+ var keys = Object.keys(arg);
+ if (keys.length > 0 && keys.length < 20) {
+ return `${displayName} (keys: ${keys.join(', ')})`;
+ }
+ return displayName;
+}
+
function getInternalInstanceReadyForUpdate(publicInstance, callerName) {
var internalInstance = ReactInstanceMap.get(publicInstance);
if (!internalInstance) {
@@ -103,17 +116,11 @@ var ReactUpdateQueue = {
*
* @param {ReactClass} publicInstance The instance to use as `this` context.
* @param {?function} callback Called after state is updated.
+ * @param {string} callerName Name of the calling function in the public API.
* @internal
*/
- enqueueCallback: function(publicInstance, callback) {
- invariant(
- typeof callback === 'function',
- 'enqueueCallback(...): You called `setProps`, `replaceProps`, ' +
- '`setState`, `replaceState`, or `forceUpdate` with a callback of type ' +
- '%s. A function is expected',
- typeof callback === 'object' && Object.keys(callback).length && Object.keys(callback).length < 20 ?
- typeof callback + ' (keys: ' + Object.keys(callback) + ')' : typeof callback
- );
+ enqueueCallback: function(publicInstance, callback, callerName) {
+ ReactUpdateQueue.validateCallback(callback, callerName);
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);
// Previously we would throw an error if we didn't have an internal
@@ -138,14 +145,6 @@ var ReactUpdateQueue = {
},
enqueueCallbackInternal: function(internalInstance, callback) {
- invariant(
- typeof callback === 'function',
- 'enqueueCallback(...): You called `setProps`, `replaceProps`, ' +
- '`setState`, `replaceState`, or `forceUpdate` with a callback of type ' +
- '%s. A function is expected',
- typeof callback === 'object' && Object.keys(callback).length && Object.keys(callback).length < 20 ?
- typeof callback + ' (keys: ' + Object.keys(callback) + ')' : typeof callback
- );
if (internalInstance._pendingCallbacks) {
internalInstance._pendingCallbacks.push(callback);
} else {
@@ -242,6 +241,16 @@ var ReactUpdateQueue = {
enqueueUpdate(internalInstance);
},
+ validateCallback: function(callback, callerName) {
+ invariant(
+ !callback || typeof callback === 'function',
+ '%s(...): Expected the last optional `callback` argument to be a ' +
+ 'function. Instead received: %s.',
+ callerName,
+ formatUnexpectedArgument(callback)
+ );
+ },
+
};
module.exports = ReactUpdateQueue;
diff --git a/src/renderers/shared/reconciler/ReactUpdates.js b/src/renderers/shared/reconciler/ReactUpdates.js
index 3f67588..6fd1d2f 100644
--- a/src/renderers/shared/reconciler/ReactUpdates.js
+++ b/src/renderers/shared/reconciler/ReactUpdates.js
@@ -18,7 +18,6 @@ var ReactPerf = require('ReactPerf');
var ReactReconciler = require('ReactReconciler');
var Transaction = require('Transaction');
-var assign = require('Object.assign');
var invariant = require('invariant');
var dirtyComponents = [];
@@ -74,7 +73,7 @@ function ReactUpdatesFlushTransaction() {
);
}
-assign(
+Object.assign(
ReactUpdatesFlushTransaction.prototype,
Transaction.Mixin,
{
diff --git a/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js b/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js
index 9d5b062..c33fb33 100644
--- a/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js
+++ b/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js
@@ -68,8 +68,6 @@ describe('ReactCompositeComponent', function() {
<b></b>;
},
});
-
- spyOn(console, 'error');
});
it('should support module pattern components', function() {
@@ -123,7 +121,6 @@ describe('ReactCompositeComponent', function() {
container.innerHTML = markup;
ReactDOM.render(<Parent />, container);
- expect(console.error).not.toHaveBeenCalled();
});
it('should react to state changes from callbacks', function() {
@@ -173,6 +170,8 @@ describe('ReactCompositeComponent', function() {
});
it('should auto bind methods and values correctly', function() {
+ spyOn(console, 'error');
+
var ComponentClass = React.createClass({
getInitialState: function() {
return {valueToReturn: 'hi'};
@@ -273,6 +272,8 @@ describe('ReactCompositeComponent', function() {
});
it('should warn about `forceUpdate` on unmounted components', function() {
+ spyOn(console, 'error');
+
var container = document.createElement('div');
document.body.appendChild(container);
@@ -303,6 +304,8 @@ describe('ReactCompositeComponent', function() {
});
it('should warn about `setState` on unmounted components', function() {
+ spyOn(console, 'error');
+
var container = document.createElement('div');
document.body.appendChild(container);
@@ -367,17 +370,16 @@ describe('ReactCompositeComponent', function() {
});
var instance = ReactDOM.render(<Component />, container);
-
instance.setState({value: 1});
- expect(console.error.calls.length).toBe(0);
ReactDOM.unmountComponentAtNode(container);
- expect(console.error.calls.length).toBe(0);
expect(cbCalled).toBe(false);
});
it('should warn about `setState` in render', function() {
+ spyOn(console, 'error');
+
var container = document.createElement('div');
var renderedState = -1;
@@ -425,6 +427,8 @@ describe('ReactCompositeComponent', function() {
});
it('should warn about `setState` in getChildContext', function() {
+ spyOn(console, 'error');
+
var container = document.createElement('div');
var renderPasses = 0;
@@ -499,6 +503,8 @@ describe('ReactCompositeComponent', function() {
});
it('should warn when shouldComponentUpdate() returns undefined', function() {
+ spyOn(console, 'error');
+
var Component = React.createClass({
getInitialState: function() {
return {bogus: false};
@@ -524,6 +530,8 @@ describe('ReactCompositeComponent', function() {
});
it('should warn when componentDidUnmount method is defined', function() {
+ spyOn(console, 'error');
+
var Component = React.createClass({
componentDidUnmount: function() {
},
@@ -659,8 +667,6 @@ describe('ReactCompositeComponent', function() {
parentInstance.setState({flag: true});
expect(parentInstance.state.flag).toBe(true);
- expect(console.error.argsForCall.length).toBe(0);
-
reactComponentExpect(childInstance).scalarContextEqual({foo: 'bar', flag: true});
});
@@ -719,8 +725,6 @@ describe('ReactCompositeComponent', function() {
// We update <Parent /> while <Child /> is still a static prop relative to this update
wrapper.refs.parent.setState({flag: false});
- expect(console.error.argsForCall.length).toBe(0);
-
expect(wrapper.refs.parent.state.flag).toEqual(false);
reactComponentExpect(wrapper.refs.child).scalarContextEqual({flag: false});
@@ -840,8 +844,6 @@ describe('ReactCompositeComponent', function() {
});
expect(parentInstance.state.flag).toBe(true);
- expect(console.error.argsForCall.length).toBe(0);
-
reactComponentExpect(childInstance).scalarContextEqual({foo: 'bar', depth: 0});
});
@@ -1036,6 +1038,8 @@ describe('ReactCompositeComponent', function() {
});
it('should disallow nested render calls', function() {
+ spyOn(console, 'error');
+
var Inner = React.createClass({
render: function() {
return <div />;
@@ -1176,8 +1180,6 @@ describe('ReactCompositeComponent', function() {
var div = document.createElement('div');
ReactDOM.render(<Parent><Component /></Parent>, div);
-
- expect(console.error.argsForCall.length).toBe(0);
});
it('should replace state', function() {
@@ -1270,15 +1272,11 @@ describe('ReactCompositeComponent', function() {
});
ReactDOM.render(<Outer><Component /></Outer>, container);
-
- expect(console.error.calls.length).toBe(0);
-
ReactDOM.render(<Outer />, container);
-
- expect(console.error.calls.length).toBe(0);
});
it('should warn when mutated props are passed', function() {
+ spyOn(console, 'error');
var container = document.createElement('div');
diff --git a/src/renderers/shared/reconciler/__tests__/ReactUpdates-test.js b/src/renderers/shared/reconciler/__tests__/ReactUpdates-test.js
index 22dddac..f59193d 100644
--- a/src/renderers/shared/reconciler/__tests__/ReactUpdates-test.js
+++ b/src/renderers/shared/reconciler/__tests__/ReactUpdates-test.js
@@ -937,4 +937,91 @@ describe('ReactUpdates', function() {
ReactFeatureFlags.logTopLevelRenders = false;
}
});
+
+ it('throws in setState if the update callback is not a function', function() {
+ function Foo() {
+ this.a = 1;
+ this.b = 2;
+ }
+ var A = React.createClass({
+ getInitialState: function() {
+ return {};
+ },
+ render: function() {
+ return <div />;
+ },
+ });
+ var component = ReactTestUtils.renderIntoDocument(<A />);
+
+ expect(() => component.setState({}, 'no')).toThrow(
+ 'setState(...): Expected the last optional `callback` argument ' +
+ 'to be a function. Instead received: string.'
+ );
+ expect(() => component.setState({}, {})).toThrow(
+ 'setState(...): Expected the last optional `callback` argument ' +
+ 'to be a function. Instead received: Object.'
+ );
+ expect(() => component.setState({}, new Foo())).toThrow(
+ 'setState(...): Expected the last optional `callback` argument ' +
+ 'to be a function. Instead received: Foo (keys: a, b).'
+ );
+ });
+
+ it('throws in replaceState if the update callback is not a function', function() {
+ function Foo() {
+ this.a = 1;
+ this.b = 2;
+ }
+ var A = React.createClass({
+ getInitialState: function() {
+ return {};
+ },
+ render: function() {
+ return <div />;
+ },
+ });
+ var component = ReactTestUtils.renderIntoDocument(<A />);
+
+ expect(() => component.replaceState({}, 'no')).toThrow(
+ 'replaceState(...): Expected the last optional `callback` argument ' +
+ 'to be a function. Instead received: string.'
+ );
+ expect(() => component.replaceState({}, {})).toThrow(
+ 'replaceState(...): Expected the last optional `callback` argument ' +
+ 'to be a function. Instead received: Object.'
+ );
+ expect(() => component.replaceState({}, new Foo())).toThrow(
+ 'replaceState(...): Expected the last optional `callback` argument ' +
+ 'to be a function. Instead received: Foo (keys: a, b).'
+ );
+ });
+
+ it('throws in forceUpdate if the update callback is not a function', function() {
+ function Foo() {
+ this.a = 1;
+ this.b = 2;
+ }
+ var A = React.createClass({
+ getInitialState: function() {
+ return {};
+ },
+ render: function() {
+ return <div />;
+ },
+ });
+ var component = ReactTestUtils.renderIntoDocument(<A />);
+
+ expect(() => component.forceUpdate('no')).toThrow(
+ 'forceUpdate(...): Expected the last optional `callback` argument ' +
+ 'to be a function. Instead received: string.'
+ );
+ expect(() => component.forceUpdate({})).toThrow(
+ 'forceUpdate(...): Expected the last optional `callback` argument ' +
+ 'to be a function. Instead received: Object.'
+ );
+ expect(() => component.forceUpdate(new Foo())).toThrow(
+ 'forceUpdate(...): Expected the last optional `callback` argument ' +
+ 'to be a function. Instead received: Foo (keys: a, b).'
+ );
+ });
});
diff --git a/src/renderers/shared/reconciler/instantiateReactComponent.js b/src/renderers/shared/reconciler/instantiateReactComponent.js
index cb0208f..796e037 100644
--- a/src/renderers/shared/reconciler/instantiateReactComponent.js
+++ b/src/renderers/shared/reconciler/instantiateReactComponent.js
@@ -15,7 +15,6 @@ var ReactCompositeComponent = require('ReactCompositeComponent');
var ReactEmptyComponent = require('ReactEmptyComponent');
var ReactNativeComponent = require('ReactNativeComponent');
-var assign = require('Object.assign');
var invariant = require('invariant');
var warning = require('warning');
@@ -23,7 +22,7 @@ var warning = require('warning');
var ReactCompositeComponentWrapper = function(element) {
this.construct(element);
};
-assign(
+Object.assign(
ReactCompositeComponentWrapper.prototype,
ReactCompositeComponent.Mixin,
{
diff --git a/src/shared/stubs/Object.assign.js b/src/shared/stubs/Object.assign.js
deleted file mode 100644
index d72103c..0000000
--- a/src/shared/stubs/Object.assign.js
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * Copyright 2014-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 Object.assign
- */
-
-// https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign
-
-'use strict';
-
-function assign(target, sources) {
- if (target == null) {
- throw new TypeError('Object.assign target cannot be null or undefined');
- }
-
- var to = Object(target);
- var hasOwnProperty = Object.prototype.hasOwnProperty;
-
- for (var nextIndex = 1; nextIndex < arguments.length; nextIndex++) {
- var nextSource = arguments[nextIndex];
- if (nextSource == null) {
- continue;
- }
-
- var from = Object(nextSource);
-
- // We don't currently support accessors nor proxies. Therefore this
- // copy cannot throw. If we ever supported this then we must handle
- // exceptions and side-effects. We don't support symbols so they won't
- // be transferred.
-
- for (var key in from) {
- if (hasOwnProperty.call(from, key)) {
- to[key] = from[key];
- }
- }
- }
-
- return to;
-}
-
-module.exports = assign;
diff --git a/src/shared/utils/CallbackQueue.js b/src/shared/utils/CallbackQueue.js
index c594e30..647c388 100644
--- a/src/shared/utils/CallbackQueue.js
+++ b/src/shared/utils/CallbackQueue.js
@@ -13,7 +13,6 @@
var PooledClass = require('PooledClass');
-var assign = require('Object.assign');
var invariant = require('invariant');
/**
@@ -32,7 +31,7 @@ function CallbackQueue() {
this._contexts = null;
}
-assign(CallbackQueue.prototype, {
+Object.assign(CallbackQueue.prototype, {
/**
* Enqueues a callback to be invoked when `notifyAll` is invoked.
diff --git a/src/shared/utils/__tests__/Transaction-test.js b/src/shared/utils/__tests__/Transaction-test.js
index 8385702..ee474a1 100644
--- a/src/shared/utils/__tests__/Transaction-test.js
+++ b/src/shared/utils/__tests__/Transaction-test.js
@@ -11,7 +11,6 @@
'use strict';
-var assign = require('Object.assign');
var Transaction;
@@ -46,7 +45,7 @@ describe('Transaction', function() {
this.secondCloseParam = INIT_ERRORED; // WILL be set to something else
this.lastCloseParam = INIT_ERRORED; // WON'T be set to something else
};
- assign(TestTransaction.prototype, Transaction.Mixin);
+ Object.assign(TestTransaction.prototype, Transaction.Mixin);
TestTransaction.prototype.getTransactionWrappers = function() {
return [
{
@@ -97,7 +96,7 @@ describe('Transaction', function() {
this.secondCloseParam = INIT_ERRORED; // WILL be set to something else
this.lastCloseParam = INIT_ERRORED; // WILL be set to something else
};
- assign(TestTransaction.prototype, Transaction.Mixin);
+ Object.assign(TestTransaction.prototype, Transaction.Mixin);
TestTransaction.prototype.getTransactionWrappers = function() {
return [
{
@@ -158,7 +157,7 @@ describe('Transaction', function() {
this.secondCloseParam = INIT_ERRORED; // WILL be set to something else
this.lastCloseParam = INIT_ERRORED; // WILL be set to something else
};
- assign(TestTransaction.prototype, Transaction.Mixin);
+ Object.assign(TestTransaction.prototype, Transaction.Mixin);
// Now, none of the close/inits throw, but the operation we wrap will throw.
TestTransaction.prototype.getTransactionWrappers = function() {
return [
@@ -222,7 +221,7 @@ describe('Transaction', function() {
var TestTransaction = function() {
this.reinitializeTransaction();
};
- assign(TestTransaction.prototype, Transaction.Mixin);
+ Object.assign(TestTransaction.prototype, Transaction.Mixin);
var exceptionMsg = 'This exception should throw.';
TestTransaction.prototype.getTransactionWrappers = function() {
return [
@@ -251,7 +250,7 @@ describe('Transaction', function() {
this.reinitializeTransaction();
this.firstCloseParam = INIT_ERRORED; // WILL be set to something else
};
- assign(TestTransaction.prototype, Transaction.Mixin);
+ Object.assign(TestTransaction.prototype, Transaction.Mixin);
TestTransaction.prototype.getTransactionWrappers = function() {
return [
{
@@ -279,7 +278,7 @@ describe('Transaction', function() {
var NestedTransaction = function() {
this.reinitializeTransaction();
};
- assign(NestedTransaction.prototype, Transaction.Mixin);
+ Object.assign(NestedTransaction.prototype, Transaction.Mixin);
NestedTransaction.prototype.getTransactionWrappers = function() {
return [
{
diff --git a/src/shared/utils/deprecated.js b/src/shared/utils/deprecated.js
index c34e0f4..fd8edec 100644
--- a/src/shared/utils/deprecated.js
+++ b/src/shared/utils/deprecated.js
@@ -11,7 +11,6 @@
'use strict';
-var assign = require('Object.assign');
var warning = require('warning');
/**
@@ -48,7 +47,7 @@ function deprecated(fnName, newModule, newPackage, ctx, fn) {
};
// We need to make sure all properties of the original fn are copied over.
// In particular, this is needed to support PropTypes
- return assign(newFn, fn);
+ return Object.assign(newFn, fn);
}
return fn;
diff --git a/src/test/ReactDefaultPerf.js b/src/test/ReactDefaultPerf.js
index 782f0e3..5cb43e9 100644
--- a/src/test/ReactDefaultPerf.js
+++ b/src/test/ReactDefaultPerf.js
@@ -18,6 +18,7 @@ var ReactMount = require('ReactMount');
var ReactPerf = require('ReactPerf');
var performanceNow = require('performanceNow');
+var warning = require('warning');
function roundFloat(val) {
return Math.floor(val * 100) / 100;
@@ -51,6 +52,31 @@ function getID(inst) {
}
}
+function stripComplexValues(key, value) {
+ if (typeof value !== 'object' || Array.isArray(value) || value == null) {
+ return value;
+ }
+ var prototype = Object.getPrototypeOf(value);
+ if (!prototype || prototype === Object.prototype) {
+ return value;
+ }
+ return '<not serializable>';
+}
+
+// This implementation of ReactPerf is going away some time mid 15.x.
+// While we plan to keep most of the API, the actual format of measurements
+// will change dramatically. To signal this, we wrap them into an opaque-ish
+// object to discourage reaching into it until the API stabilizes.
+function wrapLegacyMeasurements(measurements) {
+ return { __unstable_this_format_will_change: measurements };
+}
+function unwrapLegacyMeasurements(measurements) {
+ return measurements && measurements.__unstable_this_format_will_change || measurements;
+}
+
+var warnedAboutPrintDOM = false;
+var warnedAboutGetMeasurementsSummaryMap = false;
+
var ReactDefaultPerf = {
_allMeasurements: [], // last item in the list is the current one
_mountStack: [0],
@@ -71,11 +97,11 @@ var ReactDefaultPerf = {
},
getLastMeasurements: function() {
- return ReactDefaultPerf._allMeasurements;
+ return wrapLegacyMeasurements(ReactDefaultPerf._allMeasurements);
},
printExclusive: function(measurements) {
- measurements = measurements || ReactDefaultPerf._allMeasurements;
+ measurements = unwrapLegacyMeasurements(measurements || ReactDefaultPerf._allMeasurements);
var summary = ReactDefaultPerfAnalysis.getExclusiveSummary(measurements);
console.table(summary.map(function(item) {
return {
@@ -93,7 +119,7 @@ var ReactDefaultPerf = {
},
printInclusive: function(measurements) {
- measurements = measurements || ReactDefaultPerf._allMeasurements;
+ measurements = unwrapLegacyMeasurements(measurements || ReactDefaultPerf._allMeasurements);
var summary = ReactDefaultPerfAnalysis.getInclusiveSummary(measurements);
console.table(summary.map(function(item) {
return {
@@ -109,6 +135,17 @@ var ReactDefaultPerf = {
},
getMeasurementsSummaryMap: function(measurements) {
+ warning(
+ warnedAboutGetMeasurementsSummaryMap,
+ '`ReactPerf.getMeasurementsSummaryMap(...)` is deprecated. Use ' +
+ '`ReactPerf.getWasted(...)` instead.'
+ );
+ warnedAboutGetMeasurementsSummaryMap = true;
+ return ReactDefaultPerf.getWasted(measurements);
+ },
+
+ getWasted: function(measurements) {
+ measurements = unwrapLegacyMeasurements(measurements);
var summary = ReactDefaultPerfAnalysis.getInclusiveSummary(
measurements,
true
@@ -123,8 +160,8 @@ var ReactDefaultPerf = {
},
printWasted: function(measurements) {
- measurements = measurements || ReactDefaultPerf._allMeasurements;
- console.table(ReactDefaultPerf.getMeasurementsSummaryMap(measurements));
+ measurements = unwrapLegacyMeasurements(measurements || ReactDefaultPerf._allMeasurements);
+ console.table(ReactDefaultPerf.getWasted(measurements));
console.log(
'Total time:',
ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms'
@@ -132,13 +169,23 @@ var ReactDefaultPerf = {
},
printDOM: function(measurements) {
- measurements = measurements || ReactDefaultPerf._allMeasurements;
+ warning(
+ warnedAboutPrintDOM,
+ '`ReactPerf.printDOM(...)` is deprecated. Use ' +
+ '`ReactPerf.printOperations(...)` instead.'
+ );
+ warnedAboutPrintDOM = true;
+ return ReactDefaultPerf.printOperations(measurements);
+ },
+
+ printOperations: function(measurements) {
+ measurements = unwrapLegacyMeasurements(measurements || ReactDefaultPerf._allMeasurements);
var summary = ReactDefaultPerfAnalysis.getDOMSummary(measurements);
console.table(summary.map(function(item) {
var result = {};
result[DOMProperty.ID_ATTRIBUTE_NAME] = item.id;
result.type = item.type;
- result.args = JSON.stringify(item.args);
+ result.args = JSON.stringify(item.args, stripComplexValues);
return result;
}));
console.log(
diff --git a/src/test/ReactDefaultPerfAnalysis.js b/src/test/ReactDefaultPerfAnalysis.js
index 80452c4..bf7cb30 100644
--- a/src/test/ReactDefaultPerfAnalysis.js
+++ b/src/test/ReactDefaultPerfAnalysis.js
@@ -11,7 +11,6 @@
'use strict';
-var assign = require('Object.assign');
// Don't try to save users less than 1.2ms (a number I made up)
var DONT_CARE_THRESHOLD = 1.2;
@@ -28,7 +27,6 @@ var DOM_OPERATION_TYPES = {
'setValueForStyles': 'update styles',
'replaceNodeWithMarkup': 'replace',
'replaceDelimitedText': 'replace',
- 'updateTextContent': 'set textContent',
};
function getTotalTime(measurements) {
@@ -66,7 +64,7 @@ function getExclusiveSummary(measurements) {
for (var i = 0; i < measurements.length; i++) {
var measurement = measurements[i];
- var allIDs = assign(
+ var allIDs = Object.assign(
{},
measurement.exclusive,
measurement.inclusive
@@ -118,7 +116,7 @@ function getInclusiveSummary(measurements, onlyClean) {
for (var i = 0; i < measurements.length; i++) {
var measurement = measurements[i];
- var allIDs = assign(
+ var allIDs = Object.assign(
{},
measurement.exclusive,
measurement.inclusive
@@ -186,7 +184,7 @@ function getUnchangedComponents(measurement) {
}
});
});
- var allIDs = assign({}, measurement.exclusive, measurement.inclusive);
+ var allIDs = Object.assign({}, measurement.exclusive, measurement.inclusive);
for (var id in allIDs) {
var isDirty = false;
diff --git a/src/test/ReactTestUtils.js b/src/test/ReactTestUtils.js
index 4f8e4eb..f331bf6 100644
--- a/src/test/ReactTestUtils.js
+++ b/src/test/ReactTestUtils.js
@@ -25,7 +25,6 @@ var ReactInstanceMap = require('ReactInstanceMap');
var ReactUpdates = require('ReactUpdates');
var SyntheticEvent = require('SyntheticEvent');
-var assign = require('Object.assign');
var emptyObject = require('emptyObject');
var findDOMNode = require('findDOMNode');
var invariant = require('invariant');
@@ -396,7 +395,7 @@ NoopInternalComponent.prototype = {
var ShallowComponentWrapper = function(element) {
this.construct(element);
};
-assign(
+Object.assign(
ShallowComponentWrapper.prototype,
ReactCompositeComponent.Mixin, {
_instantiateReactComponent: function(element) {
@@ -499,7 +498,7 @@ function makeSimulator(eventType) {
fakeNativeEvent,
node
);
- assign(event, eventData);
+ Object.assign(event, eventData);
if (dispatchConfig.phasedRegistrationNames) {
EventPropagators.accumulateTwoPhaseDispatches(event);
@@ -560,7 +559,7 @@ buildSimulators();
function makeNativeSimulator(eventType) {
return function(domComponentOrNode, nativeEventData) {
var fakeNativeEvent = new Event(eventType);
- assign(fakeNativeEvent, nativeEventData);
+ Object.assign(fakeNativeEvent, nativeEventData);
if (ReactTestUtils.isDOMComponent(domComponentOrNode)) {
ReactTestUtils.simulateNativeEventOnDOMComponent(
eventType,
diff --git a/src/test/__tests__/ReactDefaultPerf-test.js b/src/test/__tests__/ReactDefaultPerf-test.js
index 42c3251..cc16c88 100644
--- a/src/test/__tests__/ReactDefaultPerf-test.js
+++ b/src/test/__tests__/ReactDefaultPerf-test.js
@@ -14,6 +14,7 @@
describe('ReactDefaultPerf', function() {
var React;
var ReactDOM;
+ var ReactDOMFeatureFlags;
var ReactDefaultPerf;
var ReactTestUtils;
var ReactDefaultPerfAnalysis;
@@ -28,8 +29,14 @@ describe('ReactDefaultPerf', function() {
return now++;
});
+ if (typeof console.table !== 'function') {
+ console.table = () => {};
+ console.table.isFake = true;
+ }
+
React = require('React');
ReactDOM = require('ReactDOM');
+ ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
ReactDefaultPerf = require('ReactDefaultPerf');
ReactTestUtils = require('ReactTestUtils');
ReactDefaultPerfAnalysis = require('ReactDefaultPerfAnalysis');
@@ -54,11 +61,17 @@ describe('ReactDefaultPerf', function() {
});
});
+ afterEach(function() {
+ if (console.table.isFake) {
+ delete console.table;
+ }
+ });
+
function measure(fn) {
ReactDefaultPerf.start();
fn();
ReactDefaultPerf.stop();
- return ReactDefaultPerf.getLastMeasurements();
+ return ReactDefaultPerf.getLastMeasurements().__unstable_this_format_will_change;
}
it('should count no-op update as waste', function() {
@@ -68,7 +81,7 @@ describe('ReactDefaultPerf', function() {
ReactDOM.render(<App />, container);
});
- var summary = ReactDefaultPerf.getMeasurementsSummaryMap(measurements);
+ var summary = ReactDefaultPerf.getWasted(measurements);
expect(summary.length).toBe(2);
/*eslint-disable dot-notation */
@@ -94,7 +107,7 @@ describe('ReactDefaultPerf', function() {
ReactDOM.render(<App flipSecond={true} />, container);
});
- var summary = ReactDefaultPerf.getMeasurementsSummaryMap(measurements);
+ var summary = ReactDefaultPerf.getWasted(measurements);
expect(summary.length).toBe(1);
/*eslint-disable dot-notation */
@@ -108,7 +121,7 @@ describe('ReactDefaultPerf', function() {
function expectNoWaste(fn) {
var measurements = measure(fn);
- var summary = ReactDefaultPerf.getMeasurementsSummaryMap(measurements);
+ var summary = ReactDefaultPerf.getWasted(measurements);
expect(summary).toEqual([]);
}
@@ -226,4 +239,49 @@ describe('ReactDefaultPerf', function() {
expect(summary).toEqual([]);
});
+ it('should print a table after calling printOperations', function() {
+ var container = document.createElement('div');
+ var measurements = measure(() => {
+ ReactDOM.render(<Div>hey</Div>, container);
+ });
+ spyOn(console, 'table');
+ ReactDefaultPerf.printOperations(measurements);
+ expect(console.table.calls.length).toBe(1);
+ expect(console.table.argsForCall[0][0]).toEqual([{
+ 'data-reactid': '',
+ type: 'set innerHTML',
+ args: ReactDOMFeatureFlags.useCreateElement ?
+ '{"node":"<not serializable>","children":[],"html":null,"text":null}' :
+ '"<div data-reactroot=\\"\\" data-reactid=\\"1\\">hey</div>"',
+ }]);
+ });
+
+ it('warns once when using getMeasurementsSummaryMap', function() {
+ var measurements = measure(() => {});
+ spyOn(console, 'error');
+ ReactDefaultPerf.getMeasurementsSummaryMap(measurements);
+ expect(console.error.calls.length).toBe(1);
+ expect(console.error.argsForCall[0][0]).toContain(
+ '`ReactPerf.getMeasurementsSummaryMap(...)` is deprecated. Use ' +
+ '`ReactPerf.getWasted(...)` instead.'
+ );
+
+ ReactDefaultPerf.getMeasurementsSummaryMap(measurements);
+ expect(console.error.calls.length).toBe(1);
+ });
+
+ it('warns once when using printDOM', function() {
+ var measurements = measure(() => {});
+ spyOn(console, 'error');
+ ReactDefaultPerf.printDOM(measurements);
+ expect(console.error.calls.length).toBe(1);
+ expect(console.error.argsForCall[0][0]).toContain(
+ '`ReactPerf.printDOM(...)` is deprecated. Use ' +
+ '`ReactPerf.printOperations(...)` instead.'
+ );
+
+ ReactDefaultPerf.printDOM(measurements);
+ expect(console.error.calls.length).toBe(1);
+ });
+
});
diff --git a/src/test/reactComponentExpect.js b/src/test/reactComponentExpect.js
index ad0966c..0905387 100644
--- a/src/test/reactComponentExpect.js
+++ b/src/test/reactComponentExpect.js
@@ -15,7 +15,6 @@
var ReactInstanceMap = require('ReactInstanceMap');
var ReactTestUtils = require('ReactTestUtils');
-var assign = require('Object.assign');
var invariant = require('invariant');
function reactComponentExpect(instance) {
@@ -47,7 +46,7 @@ function reactComponentExpectInternal(internalInstance) {
this._instance = internalInstance;
}
-assign(reactComponentExpectInternal.prototype, {
+Object.assign(reactComponentExpectInternal.prototype, {
// Getters -------------------------------------------------------------------
/**
diff --git a/src/umd/ReactUMDEntry.js b/src/umd/ReactUMDEntry.js
new file mode 100644
index 0000000..dda6487
--- /dev/null
+++ b/src/umd/ReactUMDEntry.js
@@ -0,0 +1,25 @@
+/**
+ * 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 ReactUMDEntry
+ */
+
+'use strict';
+
+var ReactDOM = require('ReactDOM');
+var ReactDOMServer = require('ReactDOMServer');
+var React = require('React');
+
+
+// `version` will be added here by ReactIsomorphic.
+var ReactUMDEntry = Object.assign({
+ __SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactDOM,
+ __SECRET_DOM_SERVER_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactDOMServer,
+}, React);
+
+module.exports = ReactUMDEntry;
diff --git a/src/umd/ReactWithAddonsUMDEntry.js b/src/umd/ReactWithAddonsUMDEntry.js
new file mode 100644
index 0000000..cf59eb9
--- /dev/null
+++ b/src/umd/ReactWithAddonsUMDEntry.js
@@ -0,0 +1,25 @@
+/**
+ * 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 ReactWithAddonsUMDEntry
+ */
+
+'use strict';
+
+var ReactDOM = require('ReactDOM');
+var ReactDOMServer = require('ReactDOMServer');
+var ReactWithAddons = require('ReactWithAddons');
+
+
+// `version` will be added here by ReactIsomorphic.
+var ReactWithAddonsUMDEntry = Object.assign({
+ __SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactDOM,
+ __SECRET_DOM_SERVER_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactDOMServer,
+}, ReactWithAddons);
+
+module.exports = ReactWithAddonsUMDEntry;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment