Merge branch '15-dev' into 15-stable

This commit is contained in:
Dan Abramov
2017-01-06 19:39:14 +00:00
60 changed files with 839 additions and 169 deletions
+50
View File
@@ -0,0 +1,50 @@
# Manual Testing Fixtures
This folder exists for **React contributors** only.
If you use React you don't need to worry about it.
These fixtures verify that the built React distributions are usable in different environments.
**They are not running automatically.** (At least not yet, feel free to contribute to automate them.)
Run them when you make changes to how we package React, ReactDOM, and addons.
## How to Run
First, build React and the fixtures:
```
cd react
npm run build
cd fixtures
node build-all.js
```
Then run a local server at the root of the repo, e.g.
```
npm i -g pushstate-server
cd ..
pushstate-server .
```
(Too complicated? Send a PR to simplify this :-).
Then open the corresponding URLs, for example:
```
open http://localhost:9000/fixtures/globals.html
open http://localhost:9000/fixtures/requirejs.html
open http://localhost:9000/fixtures/systemjs.html
open http://localhost:9000/fixtures/browserify/index.html
open http://localhost:9000/fixtures/brunch/index.html
open http://localhost:9000/fixtures/rjs/index.html
open http://localhost:9000/fixtures/systemjs-builder/index.html
open http://localhost:9000/fixtures/webpack/index.html
open http://localhost:9000/fixtures/webpack-alias/index.html
```
You should see two things:
* "Hello World" fading in with an animation.
* No errors in the console.
+1
View File
@@ -0,0 +1 @@
output.js
+16
View File
@@ -0,0 +1,16 @@
<html>
<body>
<style>
.example-appear {
opacity: 0.01;
}
.example-appear.example-appear-active {
opacity: 1;
transition: opacity .5s ease-in;
}
</style>
<div id="container"></div>
<script src="output.js"></script>
</body>
</html>
+16
View File
@@ -0,0 +1,16 @@
var React = require('react');
var CSSTransitionGroup = require('react-addons-css-transition-group');
var ReactDOM = require('react-dom');
ReactDOM.render(
React.createElement(CSSTransitionGroup, {
transitionName: 'example',
transitionAppear: true,
transitionAppearTimeout: 500,
transitionEnterTimeout: 0,
transitionLeaveTimeout: 0,
}, React.createElement('h1', null,
'Hello World!'
)),
document.getElementById('container')
);
+10
View File
@@ -0,0 +1,10 @@
{
"name": "webpack-test",
"private": true,
"dependencies": {
"browserify": "^13.3.0"
},
"scripts": {
"build": "rm -f output.js && NODE_PATH=../../build/packages browserify ./input.js -o output.js"
}
}
+2
View File
@@ -0,0 +1,2 @@
output.js
output.js.map
+16
View File
@@ -0,0 +1,16 @@
var React = require('react');
var CSSTransitionGroup = require('react-addons-css-transition-group');
var ReactDOM = require('react-dom');
ReactDOM.render(
React.createElement(CSSTransitionGroup, {
transitionName: 'example',
transitionAppear: true,
transitionAppearTimeout: 500,
transitionEnterTimeout: 0,
transitionLeaveTimeout: 0,
}, React.createElement('h1', null,
'Hello World!'
)),
document.getElementById('container')
);
+10
View File
@@ -0,0 +1,10 @@
exports.config = {
paths: {
public: '.',
},
files: {
javascripts: {
joinTo: 'output.js',
},
},
};
+17
View File
@@ -0,0 +1,17 @@
<html>
<body>
<style>
.example-appear {
opacity: 0.01;
}
.example-appear.example-appear-active {
opacity: 1;
transition: opacity .5s ease-in;
}
</style>
<div id="container"></div>
<script src="output.js"></script>
<script>require('initialize');</script>
</body>
</html>
+16
View File
@@ -0,0 +1,16 @@
var React = require('react');
var CSSTransitionGroup = require('react-addons-css-transition-group');
var ReactDOM = require('react-dom');
ReactDOM.render(
React.createElement(CSSTransitionGroup, {
transitionName: 'example',
transitionAppear: true,
transitionAppearTimeout: 500,
transitionEnterTimeout: 0,
transitionLeaveTimeout: 0,
}, React.createElement('h1', null,
'Hello World!'
)),
document.getElementById('container')
);
+10
View File
@@ -0,0 +1,10 @@
{
"name": "brunch-test",
"devDependencies": {
"brunch": "^2.9.1",
"javascript-brunch": "^2.0.0"
},
"scripts": {
"build": "rm -rf public && ln -fs ../../../build/packages/react node_modules/react && ln -fs ../../../build/packages/react-dom node_modules/react-dom && ln -fs ../../../build/packages/react-addons-css-transition-group node_modules/react-addons-css-transition-group && brunch build"
}
}
+30
View File
@@ -0,0 +1,30 @@
var fs = require('fs');
var path = require('path');
var { spawnSync } = require('child_process');
var fixtureDirs = fs.readdirSync(__dirname).filter((file) => {
return fs.statSync(path.join(__dirname, file)).isDirectory();
});
var cmdArgs = [
{cmd: 'npm', args: ['install']},
{cmd: 'npm', args: ['run', 'build']},
];
for (const dir of fixtureDirs) {
for (const cmdArg of cmdArgs) {
const opts = {
cwd: path.join(__dirname, dir),
stdio: 'inherit',
};
let result = spawnSync(cmdArg.cmd, cmdArg.args, opts);
if (result.status !== 0) {
throw new Error('Failed to build fixtures.');
}
}
}
console.log('-------------------------');
console.log('All fixtures were built!');
console.log('Now make sure to open each HTML file in this directory and each index.html in subdirectories.');
console.log('-------------------------');
+32
View File
@@ -0,0 +1,32 @@
<html>
<body>
<script src="../build/react-with-addons.js"></script>
<script src="../build/react-dom.js"></script>
<style>
.example-appear {
opacity: 0.01;
}
.example-appear.example-appear-active {
opacity: 1;
transition: opacity .5s ease-in;
}
</style>
<div id="container"></div>
<script>
var CSSTransitionGroup = React.addons.CSSTransitionGroup;
ReactDOM.render(
React.createElement(CSSTransitionGroup, {
transitionName: 'example',
transitionAppear: true,
transitionAppearTimeout: 500,
transitionEnterTimeout: 0,
transitionLeaveTimeout: 0,
}, React.createElement('h1', null,
'Hello World!'
)),
document.getElementById('container')
);
</script>
</body>
</html>
+40
View File
@@ -0,0 +1,40 @@
<html>
<body>
<script src="https://unpkg.com/requirejs@2.3.2/require.js"></script>
<style>
.example-appear {
opacity: 0.01;
}
.example-appear.example-appear-active {
opacity: 1;
transition: opacity .5s ease-in;
}
</style>
<div id="container"></div>
<script>
requirejs.config({
paths: {
react: '../build/react-with-addons',
'react-dom': '../build/react-dom'
}
});
require(['react', 'react-dom'], function(React, ReactDOM) {
var CSSTransitionGroup = React.addons.CSSTransitionGroup;
ReactDOM.render(
React.createElement(CSSTransitionGroup, {
transitionName: 'example',
transitionAppear: true,
transitionAppearTimeout: 500,
transitionEnterTimeout: 0,
transitionLeaveTimeout: 0,
}, React.createElement('h1', null,
'Hello World!'
)),
document.getElementById('container')
);
});
</script>
</body>
</html>
+1
View File
@@ -0,0 +1 @@
output.js
+10
View File
@@ -0,0 +1,10 @@
module.exports = {
baseUrl: '.',
name: 'input',
out: 'output.js',
optimize: 'none',
paths: {
react: '../../build/react-with-addons',
'react-dom': '../../build/react-dom',
},
};
+17
View File
@@ -0,0 +1,17 @@
<html>
<body>
<style>
.example-appear {
opacity: 0.01;
}
.example-appear.example-appear-active {
opacity: 1;
transition: opacity .5s ease-in;
}
</style>
<div id="container"></div>
<script src="https://unpkg.com/requirejs@2.3.2/require.js"></script>
<script src="output.js"></script>
</body>
</html>
+15
View File
@@ -0,0 +1,15 @@
require(['react', 'react-dom'], function(React, ReactDOM) {
var CSSTransitionGroup = React.addons.CSSTransitionGroup;
ReactDOM.render(
React.createElement(CSSTransitionGroup, {
transitionName: 'example',
transitionAppear: true,
transitionAppearTimeout: 500,
transitionEnterTimeout: 0,
transitionLeaveTimeout: 0,
}, React.createElement('h1', null,
'Hello World!'
)),
document.getElementById('container')
);
});
+10
View File
@@ -0,0 +1,10 @@
{
"name": "rjs-test",
"private": true,
"dependencies": {
"requirejs": "^2.3.2"
},
"scripts": {
"build": "rm -f output.js && r.js -o config.js"
}
}
+1
View File
@@ -0,0 +1 @@
output.js
+12
View File
@@ -0,0 +1,12 @@
var Builder = require('systemjs-builder');
var builder = new Builder('/', './config.js');
builder
.buildStatic('./input.js', './output.js')
.then(function() {
console.log('Build complete');
})
.catch(function(err) {
console.log('Build error');
console.log(err);
});
+6
View File
@@ -0,0 +1,6 @@
System.config({
paths: {
react: '../../build/react-with-addons.js',
'react-dom': '../../build/react-dom.js',
},
});
+16
View File
@@ -0,0 +1,16 @@
<html>
<body>
<style>
.example-appear {
opacity: 0.01;
}
.example-appear.example-appear-active {
opacity: 1;
transition: opacity .5s ease-in;
}
</style>
<div id="container"></div>
<script src="output.js"></script>
</body>
</html>
+16
View File
@@ -0,0 +1,16 @@
import React from 'react';
import ReactDOM from 'react-dom';
var CSSTransitionGroup = React.addons.CSSTransitionGroup;
ReactDOM.render(
React.createElement(CSSTransitionGroup, {
transitionName: 'example',
transitionAppear: true,
transitionAppearTimeout: 500,
transitionEnterTimeout: 0,
transitionLeaveTimeout: 0,
}, React.createElement('h1', null,
'Hello World!'
)),
document.getElementById('container')
);
+10
View File
@@ -0,0 +1,10 @@
{
"name": "systemjs-builder-test",
"private": true,
"dependencies": {
"systemjs-builder": "^0.15.34"
},
"scripts": {
"build": "rm -f output.js && node build.js"
}
}
+46
View File
@@ -0,0 +1,46 @@
<html>
<body>
<script src="https://unpkg.com/systemjs@0.19.41/dist/system.js"></script>
<style>
.example-appear {
opacity: 0.01;
}
.example-appear.example-appear-active {
opacity: 1;
transition: opacity .5s ease-in;
}
</style>
<div id="container"></div>
<script>
System.config({
paths: {
react: '../build/react-with-addons.js',
'react-dom': '../build/react-dom.js'
}
});
Promise.all([
System.import("react"),
System.import("react-dom")
]).then(function (deps) {
var React = deps[0];
var ReactDOM = deps[1];
var CSSTransitionGroup = React.addons.CSSTransitionGroup;
ReactDOM.render(
React.createElement(CSSTransitionGroup, {
transitionName: 'example',
transitionAppear: true,
transitionAppearTimeout: 500,
transitionEnterTimeout: 0,
transitionLeaveTimeout: 0,
}, React.createElement('h1', null,
'Hello World!'
)),
document.getElementById('container')
);
});
</script>
</body>
</html>
+1
View File
@@ -0,0 +1 @@
output.js
+13
View File
@@ -0,0 +1,13 @@
module.exports = {
entry: './input',
output: {
filename: 'output.js',
},
resolve: {
root: '../../build/packages',
alias: {
'react': 'react/dist/react-with-addons',
'react-dom': 'react-dom/dist/react-dom',
},
},
};
+16
View File
@@ -0,0 +1,16 @@
<html>
<body>
<style>
.example-appear {
opacity: 0.01;
}
.example-appear.example-appear-active {
opacity: 1;
transition: opacity .5s ease-in;
}
</style>
<div id="container"></div>
<script src="output.js"></script>
</body>
</html>
+16
View File
@@ -0,0 +1,16 @@
var React = require('react');
var ReactDOM = require('react-dom');
var CSSTransitionGroup = React.addons.CSSTransitionGroup;
ReactDOM.render(
React.createElement(CSSTransitionGroup, {
transitionName: 'example',
transitionAppear: true,
transitionAppearTimeout: 500,
transitionEnterTimeout: 0,
transitionLeaveTimeout: 0,
}, React.createElement('h1', null,
'Hello World!'
)),
document.getElementById('container')
);
+10
View File
@@ -0,0 +1,10 @@
{
"name": "webpack-test",
"private": true,
"dependencies": {
"webpack": "^1.14.0"
},
"scripts": {
"build": "rm -f output.js && webpack --config config.js"
}
}
+1
View File
@@ -0,0 +1 @@
output.js
+9
View File
@@ -0,0 +1,9 @@
module.exports = {
entry: './input',
output: {
filename: 'output.js',
},
resolve: {
root: '../../build/packages',
},
};
+16
View File
@@ -0,0 +1,16 @@
<html>
<body>
<style>
.example-appear {
opacity: 0.01;
}
.example-appear.example-appear-active {
opacity: 1;
transition: opacity .5s ease-in;
}
</style>
<div id="container"></div>
<script src="output.js"></script>
</body>
</html>
+16
View File
@@ -0,0 +1,16 @@
var React = require('react');
var CSSTransitionGroup = require('react-addons-css-transition-group');
var ReactDOM = require('react-dom');
ReactDOM.render(
React.createElement(CSSTransitionGroup, {
transitionName: 'example',
transitionAppear: true,
transitionAppearTimeout: 500,
transitionEnterTimeout: 0,
transitionLeaveTimeout: 0,
}, React.createElement('h1', null,
'Hello World!'
)),
document.getElementById('container')
);
+10
View File
@@ -0,0 +1,10 @@
{
"name": "webpack-test",
"private": true,
"dependencies": {
"webpack": "^1.14.0"
},
"scripts": {
"build": "rm -f output.js && webpack --config config.js"
}
}
+4 -1
View File
@@ -8,7 +8,10 @@
"react-addon"
],
"license": "BSD-3-Clause",
"dependencies": {},
"dependencies": {
"fbjs": "^0.8.4",
"object-assign": "^4.1.0"
},
"peerDependencies": {
"react": "^15.4.1"
},
@@ -14,6 +14,10 @@
"url": "https://github.com/facebook/react/issues"
},
"homepage": "https://facebook.github.io/react/",
"dependencies": {
"fbjs": "^0.8.4",
"object-assign": "^4.1.0"
},
"peerDependencies": {
"react": "^15.4.1"
},
+8 -7
View File
@@ -12,25 +12,26 @@
'use strict';
var ReactDOM = require('ReactDOM');
var ReactInstanceMap = require('ReactInstanceMap');
exports.getReactDOM = function() {
return ReactDOM;
};
exports.getReactInstanceMap = function() {
return ReactInstanceMap;
};
if (__DEV__) {
var ReactPerf = require('ReactPerf');
var ReactTestUtils = require('ReactTestUtils');
var ReactPerf;
var ReactTestUtils;
exports.getReactPerf = function() {
if (!ReactPerf) {
ReactPerf = require('ReactPerf');
}
return ReactPerf;
};
exports.getReactTestUtils = function() {
if (!ReactTestUtils) {
ReactTestUtils = require('ReactTestUtils');
}
return ReactTestUtils;
};
}
+12 -45
View File
@@ -12,7 +12,6 @@
'use strict';
var React = require('React');
var ReactAddonsDOMDependencies = require('ReactAddonsDOMDependencies');
var ReactTransitionChildMapping = require('ReactTransitionChildMapping');
var emptyFunction = require('emptyFunction');
@@ -56,17 +55,9 @@ class ReactTransitionGroup extends React.Component {
}
componentWillReceiveProps(nextProps) {
var nextChildMapping;
if (__DEV__) {
nextChildMapping = ReactTransitionChildMapping.getChildMapping(
nextProps.children,
ReactAddonsDOMDependencies.getReactInstanceMap().get(this)._debugID
);
} else {
nextChildMapping = ReactTransitionChildMapping.getChildMapping(
nextProps.children
);
}
var nextChildMapping = ReactTransitionChildMapping.getChildMapping(
nextProps.children
);
var prevChildMapping = this.state.children;
this.setState({
@@ -129,17 +120,9 @@ class ReactTransitionGroup extends React.Component {
delete this.currentlyTransitioningKeys[key];
var currentChildMapping;
if (__DEV__) {
currentChildMapping = ReactTransitionChildMapping.getChildMapping(
this.props.children,
ReactAddonsDOMDependencies.getReactInstanceMap().get(this)._debugID
);
} else {
currentChildMapping = ReactTransitionChildMapping.getChildMapping(
this.props.children
);
}
var currentChildMapping = ReactTransitionChildMapping.getChildMapping(
this.props.children
);
if (!currentChildMapping || !currentChildMapping.hasOwnProperty(key)) {
// This was removed before it had fully appeared. Remove it.
@@ -169,17 +152,9 @@ class ReactTransitionGroup extends React.Component {
delete this.currentlyTransitioningKeys[key];
var currentChildMapping;
if (__DEV__) {
currentChildMapping = ReactTransitionChildMapping.getChildMapping(
this.props.children,
ReactAddonsDOMDependencies.getReactInstanceMap().get(this)._debugID
);
} else {
currentChildMapping = ReactTransitionChildMapping.getChildMapping(
this.props.children
);
}
var currentChildMapping = ReactTransitionChildMapping.getChildMapping(
this.props.children
);
if (!currentChildMapping || !currentChildMapping.hasOwnProperty(key)) {
// This was removed before it had fully entered. Remove it.
@@ -210,17 +185,9 @@ class ReactTransitionGroup extends React.Component {
delete this.currentlyTransitioningKeys[key];
var currentChildMapping;
if (__DEV__) {
currentChildMapping = ReactTransitionChildMapping.getChildMapping(
this.props.children,
ReactAddonsDOMDependencies.getReactInstanceMap().get(this)._debugID
);
} else {
currentChildMapping = ReactTransitionChildMapping.getChildMapping(
this.props.children
);
}
var currentChildMapping = ReactTransitionChildMapping.getChildMapping(
this.props.children
);
if (currentChildMapping && currentChildMapping.hasOwnProperty(key)) {
// This entered again before it fully left. Add it again.
@@ -20,10 +20,6 @@ var ReactTransitionGroup;
describe('ReactTransitionGroup', () => {
var container;
function normalizeCodeLocInfo(str) {
return str.replace(/\(at .+?:\d+\)/g, '(at **)');
}
beforeEach(() => {
React = require('React');
ReactDOM = require('ReactDOM');
@@ -296,7 +292,7 @@ describe('ReactTransitionGroup', () => {
]);
});
it('should warn for duplicated keys with component stack info', () => {
it('should warn for duplicated keys', () => {
spyOn(console, 'error');
class Component extends React.Component {
@@ -315,13 +311,11 @@ describe('ReactTransitionGroup', () => {
'Child keys must be unique; when two children share a key, ' +
'only the first child will be used.'
);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
expect(console.error.calls.argsFor(1)[0]).toBe(
'Warning: flattenChildren(...): ' +
'Encountered two children with the same key, `1`. ' +
'Child keys must be unique; when two children share a key, ' +
'only the first child will be used.\n' +
' in ReactTransitionGroup (at **)\n' +
' in Component (at **)'
'only the first child will be used.'
);
});
});
@@ -187,13 +187,31 @@ var ReactElementValidator = {
// We warn in this case but don't throw. We expect the element creation to
// succeed and there will likely be errors in render.
if (!validType) {
warning(
false,
'React.createElement: type should not be null, undefined, boolean, or ' +
'number. It should be a string (for DOM elements) or a ReactClass ' +
'(for composite components).%s',
getDeclarationErrorAddendum()
);
if (
typeof type !== 'function' &&
typeof type !== 'string'
) {
var info = '';
if (
type === undefined ||
typeof type === 'object' &&
type !== null &&
Object.keys(type).length === 0
) {
info +=
' You likely forgot to export your component from the file ' +
'it\'s defined in.';
}
info += getDeclarationErrorAddendum();
warning(
false,
'React.createElement: type is invalid -- expected a string (for ' +
'built-in components) or a class/function (for composite ' +
'components) but got: %s.%s',
type == null ? type : typeof type,
info,
);
}
}
var element = ReactElement.createElement.apply(this, arguments);
@@ -289,35 +289,49 @@ describe('ReactElementValidator', () => {
);
});
it('gives a helpful error when passing null, undefined, boolean, or number', () => {
it('gives a helpful error when passing invalid types', () => {
spyOn(console, 'error');
React.createElement(undefined);
React.createElement(null);
React.createElement(true);
React.createElement(123);
expect(console.error.calls.count()).toBe(4);
React.createElement({x: 17});
React.createElement({});
expect(console.error.calls.count()).toBe(6);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: React.createElement: type should not be null, undefined, ' +
'boolean, or number. It should be a string (for DOM elements) or a ' +
'ReactClass (for composite components).'
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: undefined. You likely forgot to export your ' +
'component from the file it\'s defined in.'
);
expect(console.error.calls.argsFor(1)[0]).toBe(
'Warning: React.createElement: type should not be null, undefined, ' +
'boolean, or number. It should be a string (for DOM elements) or a ' +
'ReactClass (for composite components).'
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: null.'
);
expect(console.error.calls.argsFor(2)[0]).toBe(
'Warning: React.createElement: type should not be null, undefined, ' +
'boolean, or number. It should be a string (for DOM elements) or a ' +
'ReactClass (for composite components).'
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: boolean.'
);
expect(console.error.calls.argsFor(3)[0]).toBe(
'Warning: React.createElement: type should not be null, undefined, ' +
'boolean, or number. It should be a string (for DOM elements) or a ' +
'ReactClass (for composite components).'
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: number.'
);
expect(console.error.calls.argsFor(4)[0]).toBe(
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: object.'
);
expect(console.error.calls.argsFor(5)[0]).toBe(
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: object. You likely forgot to export your ' +
'component from the file it\'s defined in.'
);
React.createElement('div');
expect(console.error.calls.count()).toBe(4);
expect(console.error.calls.count()).toBe(6);
});
it('includes the owner name when passing null, undefined, boolean, or number', () => {
@@ -336,10 +350,9 @@ describe('ReactElementValidator', () => {
);
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: React.createElement: type should not be null, undefined, ' +
'boolean, or number. It should be a string (for DOM elements) or a ' +
'ReactClass (for composite components). Check the render method of ' +
'`ParentComp`.'
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: null. Check the render method of `ParentComp`.'
);
});
@@ -537,9 +550,10 @@ describe('ReactElementValidator', () => {
void <Foo>{[<div />]}</Foo>;
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: React.createElement: type should not be null, undefined, ' +
'boolean, or number. It should be a string (for DOM elements) or a ' +
'ReactClass (for composite components).'
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: undefined. You likely forgot to export your ' +
'component from the file it\'s defined in.'
);
});
@@ -319,6 +319,25 @@ describe 'ReactCoffeeScriptClass', ->
)
undefined
it 'does not warn about getInitialState() on class components
if state is also defined.', ->
spyOn console, 'error'
class Foo extends React.Component
constructor: (props) ->
super props
@state = bar: @props.initialValue
getInitialState: ->
{}
render: ->
span
className: 'foo'
test React.createElement(Foo), 'SPAN', 'foo'
expect(console.error.calls.count()).toBe 0
undefined
it 'should warn when misspelling shouldComponentUpdate', ->
spyOn console, 'error'
class NamedComponent extends React.Component
@@ -354,6 +354,21 @@ describe('ReactES6Class', () => {
);
});
it('does not warn about getInitialState() on class components if state is also defined.', () => {
spyOn(console, 'error');
class Foo extends React.Component {
state = this.getInitialState();
getInitialState() {
return {};
}
render() {
return <span className="foo" />;
}
}
test(<Foo />, 'SPAN', 'foo');
expect(console.error.calls.count()).toBe(0);
});
it('should warn when misspelling shouldComponentUpdate', () => {
spyOn(console, 'error');
@@ -454,6 +454,24 @@ describe('ReactTypeScriptClass', function() {
);
});
it('does not warn about getInitialState() on class components ' +
'if state is also defined.', () => {
spyOn(console, 'error');
class Example extends React.Component {
state = {};
getInitialState() {
return {};
}
render() {
return React.createElement('span', {className: 'foo'});
}
}
test(React.createElement(Example), 'SPAN', 'foo');
expect((<any>console.error).calls.count()).toBe(0);
});
it('should warn when misspelling shouldComponentUpdate', function() {
spyOn(console, 'error');
@@ -218,21 +218,26 @@ describe('ReactJSXElementValidator', () => {
void <True />;
void <Num />;
expect(console.error.calls.count()).toBe(4);
expect(console.error.calls.argsFor(0)[0]).toContain(
'type should not be null, undefined, boolean, or number. It should be ' +
'a string (for DOM elements) or a ReactClass (for composite components).'
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: undefined. You likely forgot to export your ' +
'component from the file it\'s defined in.'
);
expect(console.error.calls.argsFor(1)[0]).toContain(
'type should not be null, undefined, boolean, or number. It should be ' +
'a string (for DOM elements) or a ReactClass (for composite components).'
expect(console.error.calls.argsFor(1)[0]).toBe(
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: null.'
);
expect(console.error.calls.argsFor(2)[0]).toContain(
'type should not be null, undefined, boolean, or number. It should be ' +
'a string (for DOM elements) or a ReactClass (for composite components).'
expect(console.error.calls.argsFor(2)[0]).toBe(
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: boolean.'
);
expect(console.error.calls.argsFor(3)[0]).toContain(
'type should not be null, undefined, boolean, or number. It should be ' +
'a string (for DOM elements) or a ReactClass (for composite components).'
expect(console.error.calls.argsFor(3)[0]).toBe(
'Warning: React.createElement: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: number.'
);
void <Div />;
expect(console.error.calls.count()).toBe(4);
@@ -22,6 +22,18 @@ var Flags = ReactDOMComponentFlags;
var internalInstanceKey =
'__reactInternalInstance$' + Math.random().toString(36).slice(2);
/**
* Check if a given node should be cached.
*/
function shouldPrecacheNode(node, nodeID) {
return (node.nodeType === 1 &&
node.getAttribute(ATTR_NAME) === String(nodeID)) ||
(node.nodeType === 8 &&
node.nodeValue === ' react-text: ' + nodeID + ' ') ||
(node.nodeType === 8 &&
node.nodeValue === ' react-empty: ' + nodeID + ' ');
}
/**
* Drill down (through composites and empty components) until we get a host or
* host text component.
@@ -87,12 +99,7 @@ function precacheChildNodes(inst, node) {
}
// We assume the child nodes are in the same order as the child instances.
for (; childNode !== null; childNode = childNode.nextSibling) {
if ((childNode.nodeType === 1 &&
childNode.getAttribute(ATTR_NAME) === String(childID)) ||
(childNode.nodeType === 8 &&
childNode.nodeValue === ' react-text: ' + childID + ' ') ||
(childNode.nodeType === 8 &&
childNode.nodeValue === ' react-empty: ' + childID + ' ')) {
if (shouldPrecacheNode(childNode, childID)) {
precacheNode(childInst, childNode);
continue outer;
}
@@ -213,7 +213,17 @@ var ReactDOMInput = {
}
} else {
if (props.value == null && props.defaultValue != null) {
node.defaultValue = '' + props.defaultValue;
// In Chrome, assigning defaultValue to certain input types triggers input validation.
// For number inputs, the display value loses trailing decimal points. For email inputs,
// Chrome raises "The specified value <x> is not a valid email address".
//
// Here we check to see if the defaultValue has actually changed, avoiding these problems
// when the user is inputting text
//
// https://github.com/facebook/react/issues/7253
if (node.defaultValue !== '' + props.defaultValue) {
node.defaultValue = '' + props.defaultValue;
}
}
if (props.checked == null && props.defaultChecked != null) {
node.defaultChecked = !!props.defaultChecked;
@@ -167,9 +167,15 @@ var ReactDOMTextarea = {
// This is in postMount because we need access to the DOM node, which is not
// available until after the component has mounted.
var node = ReactDOMComponentTree.getNodeFromInstance(inst);
var textContent = node.textContent;
// Warning: node.value may be the empty string at this point (IE11) if placeholder is set.
node.value = node.textContent; // Detach value from defaultValue
// Only set node.value if textContent is equal to the expected
// initial value. In IE10/IE11 there is a bug where the placeholder attribute
// will populate textContent as well.
// https://developer.microsoft.com/microsoft-edge/platform/issues/101525/
if (textContent === inst._wrapperState.initialValue) {
node.value = textContent;
}
},
};
@@ -42,6 +42,28 @@ describe('ReactDOMInput', () => {
expect(node.value).toBe('0');
});
it('only assigns defaultValue if it changes', () => {
class Test extends React.Component {
render() {
return (<input defaultValue="0" />);
}
}
var component = ReactTestUtils.renderIntoDocument(<Test />);
var node = ReactDOM.findDOMNode(component);
Object.defineProperty(node, 'defaultValue', {
get() {
return '0';
},
set(value) {
throw new Error(`defaultValue was assigned ${value}, but it did not change!`);
},
});
component.forceUpdate();
});
it('should display "true" for `defaultValue` of `true`', () => {
var stub = <input type="text" defaultValue={true} />;
stub = ReactTestUtils.renderIntoDocument(stub);
+10 -4
View File
@@ -828,12 +828,18 @@ ReactDOMComponent.Mixin = {
var contentToUse =
CONTENT_TYPES[typeof props.children] ? props.children : null;
var childrenToUse = contentToUse != null ? null : props.children;
// TODO: Validate that text is allowed as a child of this node
if (contentToUse != null) {
// TODO: Validate that text is allowed as a child of this node
if (__DEV__) {
setAndValidateContentChildDev.call(this, contentToUse);
// Avoid setting textContent when the text is empty. In IE11 setting
// textContent on a text area will cause the placeholder to not
// show within the textarea until it has been focused and blurred again.
// https://github.com/facebook/react/issues/6731#issuecomment-254874553
if (contentToUse !== '') {
if (__DEV__) {
setAndValidateContentChildDev.call(this, contentToUse);
}
DOMLazyTree.queueText(lazyTree, contentToUse);
}
DOMLazyTree.queueText(lazyTree, contentToUse);
} else if (childrenToUse != null) {
var mountImages = this.mountChildren(
childrenToUse,
@@ -270,7 +270,8 @@ var ReactCompositeComponent = {
// catch them here, at initialization time, instead.
warning(
!inst.getInitialState ||
inst.getInitialState.isReactClassApproved,
inst.getInitialState.isReactClassApproved ||
inst.state,
'getInitialState was defined on %s, a plain JavaScript class. ' +
'This is only supported for classes created using React.createClass. ' +
'Did you mean to define a state property instead?',
@@ -14,8 +14,6 @@
var invariant = require('invariant');
var genericComponentClass = null;
// This registry keeps track of wrapper classes around host tags.
var tagToComponentClass = {};
var textComponentClass = null;
var ReactHostComponentInjection = {
@@ -29,11 +27,6 @@ var ReactHostComponentInjection = {
injectTextComponentClass: function(componentClass) {
textComponentClass = componentClass;
},
// 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) {
Object.assign(tagToComponentClass, componentClasses);
},
};
/**
@@ -327,7 +327,9 @@ describe('ReactComponent', () => {
var X = undefined;
expect(() => ReactTestUtils.renderIntoDocument(<X />)).toThrowError(
'Element type is invalid: expected a string (for built-in components) ' +
'or a class/function (for composite components) but got: undefined.'
'or a class/function (for composite components) but got: undefined. ' +
'You likely forgot to export your component from the file it\'s ' +
'defined in.'
);
var Y = null;
@@ -340,4 +342,23 @@ describe('ReactComponent', () => {
expect(console.error.calls.count()).toBe(2);
});
it('includes owner name in the error about badly-typed elements', () => {
spyOn(console, 'error');
function Foo() {
var X = undefined;
return <X />;
}
expect(() => ReactTestUtils.renderIntoDocument(<Foo />)).toThrowError(
'Element type is invalid: expected a string (for built-in components) ' +
'or a class/function (for composite components) but got: undefined. ' +
'You likely forgot to export your component from the file it\'s ' +
'defined in. Check the render method of `Foo`.'
);
// One warning for each element creation
expect(console.error.calls.count()).toBe(1);
});
});
@@ -72,14 +72,33 @@ function instantiateReactComponent(node, shouldHaveDebugID) {
instance = ReactEmptyComponent.create(instantiateReactComponent);
} else if (typeof node === 'object') {
var element = node;
invariant(
element && (typeof element.type === 'function' ||
typeof element.type === 'string'),
'Element type is invalid: expected a string (for built-in components) ' +
'or a class/function (for composite components) but got: %s.%s',
element.type == null ? element.type : typeof element.type,
getDeclarationErrorAddendum(element._owner)
);
var type = element.type;
if (
typeof type !== 'function' &&
typeof type !== 'string'
) {
var info = '';
if (__DEV__) {
if (
type === undefined ||
typeof type === 'object' &&
type !== null &&
Object.keys(type).length === 0
) {
info +=
' You likely forgot to export your component from the file ' +
'it\'s defined in.';
}
}
info += getDeclarationErrorAddendum(element._owner);
invariant(
false,
'Element type is invalid: expected a string (for built-in components) ' +
'or a class/function (for composite components) but got: %s.%s',
type == null ? type : typeof type,
info,
);
}
// Special case string values
if (typeof element.type === 'string') {
-12
View File
@@ -65,17 +65,6 @@ var fourArgumentPooler = function(a1, a2, a3, a4) {
}
};
var fiveArgumentPooler = function(a1, a2, a3, a4, a5) {
var Klass = this;
if (Klass.instancePool.length) {
var instance = Klass.instancePool.pop();
Klass.call(instance, a1, a2, a3, a4, a5);
return instance;
} else {
return new Klass(a1, a2, a3, a4, a5);
}
};
var standardReleaser = function(instance) {
var Klass = this;
invariant(
@@ -127,7 +116,6 @@ var PooledClass = {
twoArgumentPooler: (twoArgumentPooler: Pooler),
threeArgumentPooler: (threeArgumentPooler: Pooler),
fourArgumentPooler: (fourArgumentPooler: Pooler),
fiveArgumentPooler: (fiveArgumentPooler: Pooler),
};
module.exports = PooledClass;
+14 -14
View File
@@ -11,24 +11,24 @@
'use strict';
var React = require('React');
var ReactDOM = require('ReactDOM');
var ReactDOMUMDEntry = Object.assign({
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
ReactInstanceMap: require('ReactInstanceMap'),
},
}, ReactDOM);
var ReactDOMUMDEntry = ReactDOM;
if (__DEV__) {
Object.assign(
ReactDOMUMDEntry.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
{
// ReactPerf and ReactTestUtils currently only work with the DOM renderer
// so we expose them from here, but only in DEV mode.
ReactPerf: require('ReactPerf'),
ReactTestUtils: require('ReactTestUtils'),
}
);
ReactDOMUMDEntry.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = {
// ReactPerf and ReactTestUtils currently only work with the DOM renderer
// so we expose them from here, but only in DEV mode.
ReactPerf: require('ReactPerf'),
ReactTestUtils: require('ReactTestUtils'),
};
}
// Inject ReactDOM into React for the addons UMD build that depends on ReactDOM (TransitionGroup).
// We can remove this after we deprecate and remove the addons UMD build.
if (React.addons) {
React.__SECRET_INJECTED_REACT_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = ReactDOMUMDEntry;
}
module.exports = ReactDOMUMDEntry;
+1
View File
@@ -15,6 +15,7 @@ var ReactWithAddons = require('ReactWithAddons');
// `version` will be added here by the React module.
var ReactWithAddonsUMDEntry = Object.assign({
__SECRET_INJECTED_REACT_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: null, // Will be injected by ReactDOM UMD build.
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
ReactCurrentOwner: require('ReactCurrentOwner'),
},
@@ -9,24 +9,28 @@
* @providesModule ReactAddonsDOMDependenciesUMDShim
*/
/* globals ReactDOM */
'use strict';
exports.getReactDOM = function() {
return ReactDOM;
};
var ReactDOM;
exports.getReactInstanceMap = function() {
return ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactInstanceMap;
};
function getReactDOM() {
if (!ReactDOM) {
// This is safe to use because current module only exists in the addons build:
var ReactWithAddonsUMDEntry = require('ReactWithAddonsUMDEntry');
// This is injected by the ReactDOM UMD build:
ReactDOM = ReactWithAddonsUMDEntry.__SECRET_INJECTED_REACT_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
}
return ReactDOM;
}
exports.getReactDOM = getReactDOM;
if (__DEV__) {
exports.getReactPerf = function() {
return ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactPerf;
return getReactDOM().__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactPerf;
};
exports.getReactTestUtils = function() {
return ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactTestUtils;
return getReactDOM().__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactTestUtils;
};
}