diff --git a/.eslintignore b/.eslintignore index 2d7c565d32..cdcaed49e2 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,5 @@ # We can probably lint these later but not important at this point +addons/ src/renderers/art src/shared/vendor # But not in docs/_js/examples/* @@ -9,11 +10,8 @@ docs/_site/ docs/vendor/bundle/ # This should be more like examples/**/thirdparty/** but # we should fix https://github.com/facebook/esprima/pull/85 first -<<<<<<< HEAD examples/ -======= fixtures/ ->>>>>>> 4a37718... Remove examples/ folder (#9323) # Ignore built files. build/ coverage/ diff --git a/.flowconfig b/.flowconfig index 1b15b4d7d2..6b1984b129 100644 --- a/.flowconfig +++ b/.flowconfig @@ -1,14 +1,14 @@ [ignore] -<<<<<<< HEAD /examples/.* -======= /fixtures/.* ->>>>>>> 4a37718... Remove examples/ folder (#9323) /build/.* +/node_modules/chrome-devtools-frontend/.* +/.*/node_modules/chrome-devtools-frontend/.* /.*/node_modules/y18n/.* /.*/__mocks__/.* /.*/__tests__/.* +/addons/.* # Ignore Docs /docs/.* diff --git a/addons/create-react-class/create-react-class.min.js b/addons/create-react-class/create-react-class.min.js index b7d37d627d..62c6379dbc 100644 --- a/addons/create-react-class/create-react-class.min.js +++ b/addons/create-react-class/create-react-class.min.js @@ -1 +1 @@ -!function(f){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=f();else if("function"==typeof define&&define.amd)define([],f);else{var g;if(g="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,void 0===g.React)throw Error("React module should be required before createClass");g.createReactClass=f()}}(function(){return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof require&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n||e)},l,l.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof require&&require,o=0;o1?a-1:0),c=1;c1?e-1:0),o=1;o2?o-2:0),i=2;i 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - args[_key - 1] = arguments[_key]; - } + if ('development' !== 'production') { + (function() { + var printWarning = function printWarning(format) { + for ( + var _len = arguments.length, + args = Array(_len > 1 ? _len - 1 : 0), + _key = 1; + _key < _len; + _key++ + ) { + args[_key - 1] = arguments[_key]; + } - var argIndex = 0; - var message = 'Warning: ' + format.replace(/%s/g, function () { - return args[argIndex++]; - }); - if (typeof console !== 'undefined') { - console.error(message); - } - try { - // --- Welcome to debugging React --- - // This error was thrown as a convenience so that you can use this stack - // to find the callsite that caused this warning to fire. - throw new Error(message); - } catch (x) {} - }; + var argIndex = 0; + var message = + 'Warning: ' + + format.replace(/%s/g, function() { + return args[argIndex++]; + }); + if (typeof console !== 'undefined') { + console.error(message); + } + try { + // --- Welcome to debugging React --- + // This error was thrown as a convenience so that you can use this stack + // to find the callsite that caused this warning to fire. + throw new Error(message); + } catch (x) {} + }; - warning = function warning(condition, format) { - if (format === undefined) { - throw new Error('`warning(condition, format, ...args)` requires a warning ' + 'message argument'); - } + warning = function warning(condition, format) { + if (format === undefined) { + throw new Error( + '`warning(condition, format, ...args)` requires a warning ' + + 'message argument' + ); + } - if (format.indexOf('Failed Composite propType: ') === 0) { - return; // Ignore CompositeComponent proptype check. - } + if (format.indexOf('Failed Composite propType: ') === 0) { + return; // Ignore CompositeComponent proptype check. + } - if (!condition) { - for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) { - args[_key2 - 2] = arguments[_key2]; - } + if (!condition) { + for ( + var _len2 = arguments.length, + args = Array(_len2 > 2 ? _len2 - 2 : 0), + _key2 = 2; + _key2 < _len2; + _key2++ + ) { + args[_key2 - 2] = arguments[_key2]; + } - printWarning.apply(undefined, [format].concat(args)); - } - }; - })(); -} + printWarning.apply(undefined, [format].concat(args)); + } + }; + })(); + } + + module.exports = warning; + }, + { './emptyFunction': 2 } + ] + }, + {}, + [1] + )(1); +}); -module.exports = warning; -},{"./emptyFunction":2}]},{},[1])(1) -}); \ No newline at end of file diff --git a/docs/docs/addons.md b/docs/docs/addons.md index d0f6a770b8..b9575b3fa3 100644 --- a/docs/docs/addons.md +++ b/docs/docs/addons.md @@ -23,6 +23,7 @@ The add-ons below are considered legacy and their use is discouraged. - [`PureRenderMixin`](pure-render-mixin.html). Use [`React.PureComponent`](/react/docs/react-api.html#react.purecomponent) instead. - [`shallowCompare`](shallow-compare.html), a helper function that performs a shallow comparison for props and state in a component to decide if a component should update. - [`update`](update.html). Use [`kolodny/immutability-helper`](https://github.com/kolodny/immutability-helper) instead. +- [`ReactDOMFactories`](dom-factories.html), pre-configured DOM factories to make React easier to use without JSX. ### Deprecated Add-ons diff --git a/fixtures/dom/src/components/Header.js b/fixtures/dom/src/components/Header.js deleted file mode 100644 index 5f717d8be1..0000000000 --- a/fixtures/dom/src/components/Header.js +++ /dev/null @@ -1,70 +0,0 @@ -import { parse, stringify } from 'query-string'; -import getVersionTags from '../tags'; -const React = window.React; - -const Header = React.createClass({ - getInitialState() { - const query = parse(window.location.search); - const version = query.version || 'local'; - const versions = [version]; - return { version, versions }; - }, - componentWillMount() { - getVersionTags() - .then(tags => { - let versions = tags.map(tag => tag.name.slice(1)); - versions = [`local`, ...versions]; - this.setState({ versions }); - }) - }, - handleVersionChange(event) { - const query = parse(window.location.search); - query.version = event.target.value; - if (query.version === 'local') { - delete query.version; - } - window.location.search = stringify(query); - }, - handleFixtureChange(event) { - window.location.pathname = event.target.value; - }, - render() { - return ( -
-
- - - React Sandbox (v{React.version}) - - -
- - -
-
-
- ); - }, -}); - -export default Header; diff --git a/fixtures/dom/src/components/fixtures/index.js b/fixtures/dom/src/components/fixtures/index.js deleted file mode 100644 index 516e8ad3ae..0000000000 --- a/fixtures/dom/src/components/fixtures/index.js +++ /dev/null @@ -1,34 +0,0 @@ -const React = window.React; -import RangeInputFixtures from './range-inputs'; -import TextInputFixtures from './text-inputs'; -import SelectFixtures from './selects'; -import TextAreaFixtures from './textareas'; -import InputChangeEvents from './input-change-events'; -import NumberInputFixtures from './number-inputs/'; - -/** - * A simple routing component that renders the appropriate - * fixture based on the location pathname. - */ -const FixturesPage = React.createClass({ - render() { - switch (window.location.pathname) { - case '/text-inputs': - return ; - case '/range-inputs': - return ; - case '/selects': - return ; - case '/textareas': - return ; - case '/input-change-events': - return ; - case '/number-inputs': - return ; - default: - return

Please select a test fixture.

; - } - }, -}); - -module.exports = FixturesPage; diff --git a/fixtures/dom/src/components/fixtures/number-inputs/NumberInputDecimal.js b/fixtures/dom/src/components/fixtures/number-inputs/NumberInputDecimal.js new file mode 100644 index 0000000000..552deb04d7 --- /dev/null +++ b/fixtures/dom/src/components/fixtures/number-inputs/NumberInputDecimal.js @@ -0,0 +1,33 @@ +const React = window.React; + +import Fixture from '../../Fixture'; + +class NumberInputDecimal extends React.Component { + state = { value: '.98' }; + changeValue = () => { + this.setState({ + value: '0.98', + }); + } + render() { + const {value} = this.state; + return ( + +
{this.props.children}
+ +
+ { + this.setState({value: e.target.value}); + }} + /> + +
+
+ ); + } +} + +export default NumberInputDecimal; diff --git a/fixtures/dom/src/components/fixtures/number-inputs/NumberTestCase.js b/fixtures/dom/src/components/fixtures/number-inputs/NumberTestCase.js deleted file mode 100644 index 2c072d478e..0000000000 --- a/fixtures/dom/src/components/fixtures/number-inputs/NumberTestCase.js +++ /dev/null @@ -1,37 +0,0 @@ -const React = window.React; - -import Fixture from '../../Fixture'; - -const NumberTestCase = React.createClass({ - getInitialState() { - return { value: '' }; - }, - onChange(event) { - const parsed = parseFloat(event.target.value, 10) - const value = isNaN(parsed) ? '' : parsed - - this.setState({ value }) - }, - render() { - return ( - -
{this.props.children}
- -
-
- Controlled - - Value: {JSON.stringify(this.state.value)} -
- -
- Uncontrolled - -
-
-
- ); - }, -}); - -export default NumberTestCase; diff --git a/fixtures/dom/src/components/fixtures/number-inputs/index.js b/fixtures/dom/src/components/fixtures/number-inputs/index.js deleted file mode 100644 index 2c88c333ed..0000000000 --- a/fixtures/dom/src/components/fixtures/number-inputs/index.js +++ /dev/null @@ -1,167 +0,0 @@ -const React = window.React; - -import FixtureSet from '../../FixtureSet'; -import TestCase from '../../TestCase'; -import NumberTestCase from './NumberTestCase'; - -const NumberInputs = React.createClass({ - render() { - return ( - - - -
  • Type "3.1"
  • -
  • Press backspace, eliminating the "1"
  • -
    - - - The field should read "3.", preserving the decimal place - - - - -

    - Notes: Chrome and Safari clear trailing - decimals on blur. React makes this concession so that the - value attribute remains in sync with the value property. -

    -
    - - - -
  • Type "0.01"
  • -
    - - - The field should read "0.01" - - - -
    - - - -
  • Type "2e"
  • -
  • Type 4, to read "2e4"
  • -
    - - - The field should read "2e4". The parsed value should read "20000" - - - -
    - - - -
  • Type "3.14"
  • -
  • Press "e", so that the input reads "3.14e"
  • -
    - - - The field should read "3.14e", the parsed value should be empty - - - -
    - - - -
  • Type "3.14"
  • -
  • Move the text cursor to after the decimal place
  • -
  • Press "e" twice, so that the value reads "3.ee14"
  • -
    - - - The field should read "3.ee14" - - - -
    - - - -
  • Type "3.0"
  • -
    - - - The field should read "3.0" - - - -
    - - - -
  • Type "300"
  • -
  • Move the cursor to after the "3"
  • -
  • Type "."
  • -
    - - - The field should read "3.00", not "3" - - -
    - - - -
  • Type "3"
  • -
  • Select the entire value"
  • -
  • Type '-' to replace '3' with '-'
  • -
    - - - The field should read "-", not be blank. - - -
    - - - -
  • Type "-"
  • -
  • Type '3'
  • -
    - - - The field should read "-3". - - -
    -
    - ); - }, -}); - -export default NumberInputs; diff --git a/package.json b/package.json index 2142490155..93e208daf2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-build", "private": true, - "version": "15.5.4", + "version": "15.6.0-rc.1", "devDependencies": { "aliasify": "^2.0.0", "art": "^0.10.1", @@ -40,7 +40,6 @@ "coffee-script": "^1.8.0", "core-js": "^2.2.1", "coveralls": "^2.11.6", - "create-react-class": "15.5.0", "del": "^2.0.2", "derequire": "^2.0.3", "eslint": "^3.10.2", @@ -71,7 +70,7 @@ "merge-stream": "^1.0.0", "object-assign": "^4.1.1", "platform": "^1.1.0", - "prop-types": "15.5.7", + "prettier": "^1.2.2", "run-sequence": "^1.1.4", "through2": "^2.0.0", "tmp": "~0.0.28", @@ -84,6 +83,10 @@ "node": "4.x || 5.x || 6.x || 7.x", "npm": "2.x || 3.x || 4.x" }, + "dependencies": { + "create-react-class": "^15.5.2", + "prop-types": "15.5.7" + }, "commonerConfig": { "version": 7 }, diff --git a/packages/react-addons/package.json b/packages/react-addons/package.json index 2d5c7fd750..f71e0fb117 100644 --- a/packages/react-addons/package.json +++ b/packages/react-addons/package.json @@ -1,6 +1,6 @@ { "name": "react-addons-template", - "version": "15.5.4", + "version": "15.6.0-rc.1", "main": "index.js", "repository": "facebook/react", "keywords": [ @@ -13,7 +13,7 @@ "object-assign": "^4.1.0" }, "peerDependencies": { - "react": "^15.5.4" + "react": "^15.6.0-rc.1" }, "files": [ "LICENSE", diff --git a/packages/react-dom-factories/README.md b/packages/react-dom-factories/README.md new file mode 100644 index 0000000000..3951563f9e --- /dev/null +++ b/packages/react-dom-factories/README.md @@ -0,0 +1,20 @@ +# `react-dom-factories` + +> Note: +> `ReactDOMFactories` is a legacy add-on. Consider using +> `React.createFactory` or JSX instead. + +Prior to version 16.0.0, React maintained a whitelist of +pre-configured DOM factories. These predefined factories have been +moved to the `react-dom-factories` library. + +## Example + +```javascript +import ReactDOM from 'react-dom'; +import DOM from 'react-dom-factories'; + +const greeting = DOM.div({ className: 'greeting' }, DOM.p(null, 'Hello, world!')); + +ReactDOM.render(greeting, document.getElementById('app')) +``` diff --git a/packages/react-dom-factories/index.js b/packages/react-dom-factories/index.js new file mode 100644 index 0000000000..ceca5fb48c --- /dev/null +++ b/packages/react-dom-factories/index.js @@ -0,0 +1,189 @@ +'use strict'; + +/** + * Copyright 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +(function(f) { + if (typeof exports === 'object' && typeof module !== 'undefined') { + module.exports = f(require('react')); + /* global define */ + } else if (typeof define === 'function' && define.amd) { + define(['react'], f); + } else { + var g; + if (typeof window !== 'undefined') { + g = window; + } else if (typeof global !== 'undefined') { + g = global; + } else if (typeof self !== 'undefined') { + g = self; + } else { + g = this; + } + + if (typeof g.React === 'undefined') { + throw Error('React module should be required before ReactDOMFactories'); + } + + g.ReactDOMFactories = f(g.React); + } +})(function(React) { + /** + * Create a factory that creates HTML tag elements. + */ + var createDOMFactory = React.createFactory; + + /** + * Creates a mapping from supported HTML tags to `ReactDOMComponent` classes. + */ + var ReactDOMFactories = { + a: createDOMFactory('a'), + abbr: createDOMFactory('abbr'), + address: createDOMFactory('address'), + area: createDOMFactory('area'), + article: createDOMFactory('article'), + aside: createDOMFactory('aside'), + audio: createDOMFactory('audio'), + b: createDOMFactory('b'), + base: createDOMFactory('base'), + bdi: createDOMFactory('bdi'), + bdo: createDOMFactory('bdo'), + big: createDOMFactory('big'), + blockquote: createDOMFactory('blockquote'), + body: createDOMFactory('body'), + br: createDOMFactory('br'), + button: createDOMFactory('button'), + canvas: createDOMFactory('canvas'), + caption: createDOMFactory('caption'), + cite: createDOMFactory('cite'), + code: createDOMFactory('code'), + col: createDOMFactory('col'), + colgroup: createDOMFactory('colgroup'), + data: createDOMFactory('data'), + datalist: createDOMFactory('datalist'), + dd: createDOMFactory('dd'), + del: createDOMFactory('del'), + details: createDOMFactory('details'), + dfn: createDOMFactory('dfn'), + dialog: createDOMFactory('dialog'), + div: createDOMFactory('div'), + dl: createDOMFactory('dl'), + dt: createDOMFactory('dt'), + em: createDOMFactory('em'), + embed: createDOMFactory('embed'), + fieldset: createDOMFactory('fieldset'), + figcaption: createDOMFactory('figcaption'), + figure: createDOMFactory('figure'), + footer: createDOMFactory('footer'), + form: createDOMFactory('form'), + h1: createDOMFactory('h1'), + h2: createDOMFactory('h2'), + h3: createDOMFactory('h3'), + h4: createDOMFactory('h4'), + h5: createDOMFactory('h5'), + h6: createDOMFactory('h6'), + head: createDOMFactory('head'), + header: createDOMFactory('header'), + hgroup: createDOMFactory('hgroup'), + hr: createDOMFactory('hr'), + html: createDOMFactory('html'), + i: createDOMFactory('i'), + iframe: createDOMFactory('iframe'), + img: createDOMFactory('img'), + input: createDOMFactory('input'), + ins: createDOMFactory('ins'), + kbd: createDOMFactory('kbd'), + keygen: createDOMFactory('keygen'), + label: createDOMFactory('label'), + legend: createDOMFactory('legend'), + li: createDOMFactory('li'), + link: createDOMFactory('link'), + main: createDOMFactory('main'), + map: createDOMFactory('map'), + mark: createDOMFactory('mark'), + menu: createDOMFactory('menu'), + menuitem: createDOMFactory('menuitem'), + meta: createDOMFactory('meta'), + meter: createDOMFactory('meter'), + nav: createDOMFactory('nav'), + noscript: createDOMFactory('noscript'), + object: createDOMFactory('object'), + ol: createDOMFactory('ol'), + optgroup: createDOMFactory('optgroup'), + option: createDOMFactory('option'), + output: createDOMFactory('output'), + p: createDOMFactory('p'), + param: createDOMFactory('param'), + picture: createDOMFactory('picture'), + pre: createDOMFactory('pre'), + progress: createDOMFactory('progress'), + q: createDOMFactory('q'), + rp: createDOMFactory('rp'), + rt: createDOMFactory('rt'), + ruby: createDOMFactory('ruby'), + s: createDOMFactory('s'), + samp: createDOMFactory('samp'), + script: createDOMFactory('script'), + section: createDOMFactory('section'), + select: createDOMFactory('select'), + small: createDOMFactory('small'), + source: createDOMFactory('source'), + span: createDOMFactory('span'), + strong: createDOMFactory('strong'), + style: createDOMFactory('style'), + sub: createDOMFactory('sub'), + summary: createDOMFactory('summary'), + sup: createDOMFactory('sup'), + table: createDOMFactory('table'), + tbody: createDOMFactory('tbody'), + td: createDOMFactory('td'), + textarea: createDOMFactory('textarea'), + tfoot: createDOMFactory('tfoot'), + th: createDOMFactory('th'), + thead: createDOMFactory('thead'), + time: createDOMFactory('time'), + title: createDOMFactory('title'), + tr: createDOMFactory('tr'), + track: createDOMFactory('track'), + u: createDOMFactory('u'), + ul: createDOMFactory('ul'), + var: createDOMFactory('var'), + video: createDOMFactory('video'), + wbr: createDOMFactory('wbr'), + + // SVG + circle: createDOMFactory('circle'), + clipPath: createDOMFactory('clipPath'), + defs: createDOMFactory('defs'), + ellipse: createDOMFactory('ellipse'), + g: createDOMFactory('g'), + image: createDOMFactory('image'), + line: createDOMFactory('line'), + linearGradient: createDOMFactory('linearGradient'), + mask: createDOMFactory('mask'), + path: createDOMFactory('path'), + pattern: createDOMFactory('pattern'), + polygon: createDOMFactory('polygon'), + polyline: createDOMFactory('polyline'), + radialGradient: createDOMFactory('radialGradient'), + rect: createDOMFactory('rect'), + stop: createDOMFactory('stop'), + svg: createDOMFactory('svg'), + text: createDOMFactory('text'), + tspan: createDOMFactory('tspan'), + }; + + // due to wrapper and conditionals at the top, this will either become + // `module.exports ReactDOMFactories` if that is available, + // otherwise it will be defined via `define(['react'], ReactDOMFactories)` + // if that is available, + // otherwise it will be defined as global variable. + return ReactDOMFactories; +}); + diff --git a/packages/react-dom-factories/package.json b/packages/react-dom-factories/package.json new file mode 100644 index 0000000000..5ac0e03cba --- /dev/null +++ b/packages/react-dom-factories/package.json @@ -0,0 +1,25 @@ +{ + "name": "react-dom-factories", + "version": "15.6.0-rc", + "description": "React package for DOM factory methods.", + "main": "index.js", + "repository": "facebook/react", + "keywords": [ + "react" + ], + "license": "BSD-3-Clause", + "bugs": { + "url": "https://github.com/facebook/react/issues" + }, + "homepage": "https://facebook.github.io/react/", + "peerDependencies": { + "react": "^15.6.0-rc.1" + }, + "files": [ + "LICENSE", + "PATENTS", + "README.md", + "index.js", + "lib/" + ] +} diff --git a/packages/react-dom/package.json b/packages/react-dom/package.json index c4ebbe19ee..93f32fae56 100644 --- a/packages/react-dom/package.json +++ b/packages/react-dom/package.json @@ -1,6 +1,6 @@ { "name": "react-dom", - "version": "15.5.4", + "version": "15.6.0-rc.1", "description": "React package for working with the DOM.", "main": "index.js", "repository": "facebook/react", @@ -19,7 +19,7 @@ "prop-types": "~15.5.7" }, "peerDependencies": { - "react": "^15.5.4" + "react": "^15.6.0-rc.1" }, "files": [ "LICENSE", diff --git a/packages/react-native-renderer/package.json b/packages/react-native-renderer/package.json index 32584d0948..93c33592c3 100644 --- a/packages/react-native-renderer/package.json +++ b/packages/react-native-renderer/package.json @@ -1,6 +1,6 @@ { "name": "react-native-renderer", - "version": "15.5.4", + "version": "15.6.0-rc.1", "description": "React package for use inside react-native.", "main": "index.js", "repository": "facebook/react", @@ -18,7 +18,7 @@ "object-assign": "^4.1.0" }, "peerDependencies": { - "react": "^15.5.4" + "react": "^15.6.0-rc.1" }, "files": [ "LICENSE", diff --git a/packages/react-test-renderer/package.json b/packages/react-test-renderer/package.json index 5cd0fc0c3f..b8154cebbb 100644 --- a/packages/react-test-renderer/package.json +++ b/packages/react-test-renderer/package.json @@ -1,6 +1,6 @@ { "name": "react-test-renderer", - "version": "15.5.4", + "version": "15.6.0-rc.1", "description": "React package for snapshot testing.", "main": "index.js", "repository": "facebook/react", @@ -19,7 +19,7 @@ "object-assign": "^4.1.0" }, "peerDependencies": { - "react": "^15.5.4" + "react": "^15.6.0-rc.1" }, "files": [ "LICENSE", diff --git a/packages/react/package.json b/packages/react/package.json index 40211543e9..fe0970cce5 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,7 +1,7 @@ { "name": "react", "description": "React is a JavaScript library for building user interfaces.", - "version": "15.5.4", + "version": "15.6.0-rc.1", "keywords": [ "react" ], @@ -23,6 +23,7 @@ "node": ">=0.10.0" }, "dependencies": { + "create-react-class": "^15.5.2", "fbjs": "^0.8.9", "loose-envify": "^1.1.0", "object-assign": "^4.1.0", diff --git a/scripts/jest/ts-preprocessor.js b/scripts/jest/ts-preprocessor.js index e7cdad0e97..e6b298f5f0 100644 --- a/scripts/jest/ts-preprocessor.js +++ b/scripts/jest/ts-preprocessor.js @@ -28,7 +28,7 @@ function compile(content, contentFilename) { // the file path into backslashes on Windows. filename = path.normalize(filename); var reactRegex = new RegExp( - path.join('/', '(?:React|ReactDOM)(?:\.d)?\.ts$') + path.join('/', '(?:React|ReactDOM|PropTypes)(?:\.d)?\.ts$') ); var jestRegex = /jest\.d\.ts/; diff --git a/src/ReactVersion.js b/src/ReactVersion.js index f1670f8749..ae5c64d58f 100644 --- a/src/ReactVersion.js +++ b/src/ReactVersion.js @@ -11,4 +11,4 @@ 'use strict'; -module.exports = '15.5.4'; +module.exports = '15.6.0-rc.1'; diff --git a/src/isomorphic/classic/element/ReactDOMFactories.js b/src/addons/ReactDOMFactories.js similarity index 98% rename from src/isomorphic/classic/element/ReactDOMFactories.js rename to src/addons/ReactDOMFactories.js index 4890fa676c..f1f82c3a01 100644 --- a/src/isomorphic/classic/element/ReactDOMFactories.js +++ b/src/addons/ReactDOMFactories.js @@ -26,7 +26,6 @@ if (__DEV__) { /** * Creates a mapping from supported HTML tags to `ReactDOMComponent` classes. - * This is also accessible via `React.DOM`. * * @public */ @@ -141,7 +140,7 @@ var ReactDOMFactories = { track: createDOMFactory('track'), u: createDOMFactory('u'), ul: createDOMFactory('ul'), - 'var': createDOMFactory('var'), + var: createDOMFactory('var'), video: createDOMFactory('video'), wbr: createDOMFactory('wbr'), diff --git a/src/addons/ReactFragment.js b/src/addons/ReactFragment.js index 5d83e84533..0ed35d4ae6 100644 --- a/src/addons/ReactFragment.js +++ b/src/addons/ReactFragment.js @@ -41,7 +41,7 @@ var ReactFragment = { warning( false, 'React.addons.createFragment only accepts a single object. Got: %s', - object + object, ); return object; } @@ -49,7 +49,7 @@ var ReactFragment = { warning( false, 'React.addons.createFragment does not accept a ReactElement ' + - 'without a wrapper object.' + 'without a wrapper object.', ); return object; } @@ -57,7 +57,7 @@ var ReactFragment = { invariant( object.nodeType !== 1, 'React.addons.createFragment(...): Encountered an invalid child; DOM ' + - 'elements are not valid children of React components.' + 'elements are not valid children of React components.', ); var result = []; @@ -68,7 +68,7 @@ var ReactFragment = { warning( false, 'React.addons.createFragment(...): Child objects should have ' + - 'non-numeric keys so ordering is preserved.' + 'non-numeric keys so ordering is preserved.', ); warnedAboutNumeric = true; } @@ -77,7 +77,7 @@ var ReactFragment = { object[key], result, key, - emptyFunction.thatReturnsArgument + emptyFunction.thatReturnsArgument, ); } diff --git a/src/addons/ReactWithAddons.js b/src/addons/ReactWithAddons.js index 4d556e9223..8cf081c982 100644 --- a/src/addons/ReactWithAddons.js +++ b/src/addons/ReactWithAddons.js @@ -14,8 +14,7 @@ var LinkedStateMixin = require('LinkedStateMixin'); var React = require('React'); var ReactAddonsDOMDependencies = require('ReactAddonsDOMDependencies'); -var ReactComponentWithPureRenderMixin = - require('ReactComponentWithPureRenderMixin'); +var ReactComponentWithPureRenderMixin = require('ReactComponentWithPureRenderMixin'); var ReactCSSTransitionGroup = require('ReactCSSTransitionGroup'); var ReactFragment = require('ReactFragment'); var ReactTransitionGroup = require('ReactTransitionGroup'); diff --git a/src/addons/__tests__/ReactDOMFactories-test.js b/src/addons/__tests__/ReactDOMFactories-test.js new file mode 100644 index 0000000000..641faa3f00 --- /dev/null +++ b/src/addons/__tests__/ReactDOMFactories-test.js @@ -0,0 +1,41 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @emails react-core + */ + +'use strict'; + +var React = require('React'); +var {div} = require('ReactDOMFactories'); + +describe('ReactDOMFactories', () => { + it('allow factories to be called without warnings', () => { + spyOn(console, 'error'); + spyOn(console, 'warn'); + var element = div(); + expect(element.type).toBe('div'); + expect(console.error).not.toHaveBeenCalled(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('warns once when accessing React.DOM methods', () => { + spyOn(console, 'warn'); + + var a = React.DOM.a(); + var p = React.DOM.p(); + + expect(a.type).toBe('a'); + expect(p.type).toBe('p'); + + expect(console.warn).toHaveBeenCalledTimes(1); + expect(console.warn.calls.first().args[0]).toContain( + 'Warning: Accessing factories like React.DOM.a has been deprecated', + ); + }); +}); diff --git a/src/addons/__tests__/ReactFragment-test.js b/src/addons/__tests__/ReactFragment-test.js index 0f278686a3..1fc6f03d2a 100644 --- a/src/addons/__tests__/ReactFragment-test.js +++ b/src/addons/__tests__/ReactFragment-test.js @@ -16,7 +16,6 @@ var ReactDOM; var ReactFragment; describe('ReactFragment', () => { - beforeEach(() => { React = require('React'); ReactDOM = require('ReactDOM'); @@ -33,9 +32,9 @@ describe('ReactFragment', () => { var container = document.createElement('div'); expect(() => ReactDOM.render(element, container)).toThrowError( 'Objects are not valid as a React child (found: object with keys ' + - '{x, y, z}). If you meant to render a collection of children, use an ' + - 'array instead or wrap the object using createFragment(object) from ' + - 'the React add-ons.' + '{x, y, z}). If you meant to render a collection of children, use an ' + + 'array instead or wrap the object using createFragment(object) from ' + + 'the React add-ons.', ); }); @@ -53,9 +52,9 @@ describe('ReactFragment', () => { var container = document.createElement('div'); expect(() => ReactDOM.render(, container)).toThrowError( 'Objects are not valid as a React child (found: object with keys ' + - '{a, b, c}). If you meant to render a collection of children, use an ' + - 'array instead or wrap the object using createFragment(object) from ' + - 'the React add-ons. Check the render method of `Foo`.' + '{a, b, c}). If you meant to render a collection of children, use an ' + + 'array instead or wrap the object using createFragment(object) from ' + + 'the React add-ons. Check the render method of `Foo`.', ); }); @@ -64,9 +63,9 @@ describe('ReactFragment', () => { var container = document.createElement('div'); expect(() => ReactDOM.render(
    {oldEl}
    , container)).toThrowError( 'Objects are not valid as a React child (found: object with keys ' + - '{_isReactElement, type, props}). It looks like you\'re using an ' + - 'element created by a different version of React. Make sure to use ' + - 'only one copy of React.' + "{_isReactElement, type, props}). It looks like you're using an " + + 'element created by a different version of React. Make sure to use ' + + 'only one copy of React.', ); }); @@ -77,7 +76,7 @@ describe('ReactFragment', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( - 'Child objects should have non-numeric keys so ordering is preserved.' + 'Child objects should have non-numeric keys so ordering is preserved.', ); }); @@ -86,7 +85,7 @@ describe('ReactFragment', () => { ReactFragment.create(null); expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( - 'React.addons.createFragment only accepts a single object.' + 'React.addons.createFragment only accepts a single object.', ); }); @@ -95,7 +94,7 @@ describe('ReactFragment', () => { ReactFragment.create([]); expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( - 'React.addons.createFragment only accepts a single object.' + 'React.addons.createFragment only accepts a single object.', ); }); @@ -105,8 +104,7 @@ describe('ReactFragment', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( 'React.addons.createFragment does not accept a ReactElement without a ' + - 'wrapper object.' + 'wrapper object.', ); }); - }); diff --git a/src/addons/__tests__/renderSubtreeIntoContainer-test.js b/src/addons/__tests__/renderSubtreeIntoContainer-test.js index 1ee3bd6a93..0af5d79625 100644 --- a/src/addons/__tests__/renderSubtreeIntoContainer-test.js +++ b/src/addons/__tests__/renderSubtreeIntoContainer-test.js @@ -11,19 +11,19 @@ 'use strict'; +var PropTypes = require('prop-types'); var React = require('React'); var ReactDOM = require('ReactDOM'); var ReactTestUtils = require('ReactTestUtils'); var renderSubtreeIntoContainer = require('renderSubtreeIntoContainer'); describe('renderSubtreeIntoContainer', () => { - it('should pass context when rendering subtree elsewhere', () => { var portal = document.createElement('div'); class Component extends React.Component { static contextTypes = { - foo: React.PropTypes.string.isRequired, + foo: PropTypes.string.isRequired, }; render() { @@ -33,7 +33,7 @@ describe('renderSubtreeIntoContainer', () => { class Parent extends React.Component { static childContextTypes = { - foo: React.PropTypes.string.isRequired, + foo: PropTypes.string.isRequired, }; getChildContext() { @@ -47,9 +47,11 @@ describe('renderSubtreeIntoContainer', () => { } componentDidMount() { - expect(function() { - renderSubtreeIntoContainer(this, , portal); - }.bind(this)).not.toThrow(); + expect( + function() { + renderSubtreeIntoContainer(this, , portal); + }.bind(this), + ).not.toThrow(); } } @@ -62,7 +64,7 @@ describe('renderSubtreeIntoContainer', () => { class Component extends React.Component { static contextTypes = { - foo: React.PropTypes.string.isRequired, + foo: PropTypes.string.isRequired, }; render() { @@ -75,7 +77,7 @@ describe('renderSubtreeIntoContainer', () => { // eslint-disable-next-line no-unused-vars class Parent extends React.Component { static childContextTypes = { - foo: React.PropTypes.string.isRequired, + foo: PropTypes.string.isRequired, }; getChildContext() { @@ -103,8 +105,8 @@ describe('renderSubtreeIntoContainer', () => { class Component extends React.Component { static contextTypes = { - foo: React.PropTypes.string.isRequired, - getFoo: React.PropTypes.func.isRequired, + foo: PropTypes.string.isRequired, + getFoo: PropTypes.func.isRequired, }; render() { @@ -114,8 +116,8 @@ describe('renderSubtreeIntoContainer', () => { class Parent extends React.Component { static childContextTypes = { - foo: React.PropTypes.string.isRequired, - getFoo: React.PropTypes.func.isRequired, + foo: PropTypes.string.isRequired, + getFoo: PropTypes.func.isRequired, }; state = { @@ -155,8 +157,8 @@ describe('renderSubtreeIntoContainer', () => { class Component extends React.Component { static contextTypes = { - foo: React.PropTypes.string.isRequired, - getFoo: React.PropTypes.func.isRequired, + foo: PropTypes.string.isRequired, + getFoo: PropTypes.func.isRequired, }; render() { @@ -166,8 +168,8 @@ describe('renderSubtreeIntoContainer', () => { class Parent extends React.Component { static childContextTypes = { - foo: React.PropTypes.string.isRequired, - getFoo: React.PropTypes.func.isRequired, + foo: PropTypes.string.isRequired, + getFoo: PropTypes.func.isRequired, }; getChildContext() { @@ -195,5 +197,4 @@ describe('renderSubtreeIntoContainer', () => { ReactDOM.render(, container); expect(portal.firstChild.innerHTML).toBe('changed-changed'); }); - }); diff --git a/src/addons/__tests__/update-test.js b/src/addons/__tests__/update-test.js index fe557da38f..15cf7ff75d 100644 --- a/src/addons/__tests__/update-test.js +++ b/src/addons/__tests__/update-test.js @@ -14,7 +14,6 @@ var update = require('update'); describe('update', () => { - describe('$push', () => { it('pushes', () => { expect(update([1], {$push: [7]})).toEqual([1, 7]); @@ -27,12 +26,12 @@ describe('update', () => { it('only pushes an array', () => { expect(update.bind(null, [], {$push: 7})).toThrowError( 'update(): expected spec of $push to be an array; got 7. Did you ' + - 'forget to wrap your parameter in an array?' + 'forget to wrap your parameter in an array?', ); }); it('only pushes unto an array', () => { expect(update.bind(null, 1, {$push: 7})).toThrowError( - 'update(): expected target of $push to be an array; got 1.' + 'update(): expected target of $push to be an array; got 1.', ); }); }); @@ -49,12 +48,12 @@ describe('update', () => { it('only unshifts an array', () => { expect(update.bind(null, [], {$unshift: 7})).toThrowError( 'update(): expected spec of $unshift to be an array; got 7. Did you ' + - 'forget to wrap your parameter in an array?' + 'forget to wrap your parameter in an array?', ); }); it('only unshifts unto an array', () => { expect(update.bind(null, 1, {$unshift: 7})).toThrowError( - 'update(): expected target of $unshift to be an array; got 1.' + 'update(): expected target of $unshift to be an array; got 1.', ); }); }); @@ -71,16 +70,16 @@ describe('update', () => { it('only splices an array of arrays', () => { expect(update.bind(null, [], {$splice: 1})).toThrowError( 'update(): expected spec of $splice to be an array of arrays; got 1. ' + - 'Did you forget to wrap your parameters in an array?' + 'Did you forget to wrap your parameters in an array?', ); expect(update.bind(null, [], {$splice: [1]})).toThrowError( 'update(): expected spec of $splice to be an array of arrays; got 1. ' + - 'Did you forget to wrap your parameters in an array?' + 'Did you forget to wrap your parameters in an array?', ); }); it('only splices unto an array', () => { expect(update.bind(null, 1, {$splice: 7})).toThrowError( - 'Expected $splice target to be an array; got 1' + 'Expected $splice target to be an array; got 1', ); }); }); @@ -96,12 +95,12 @@ describe('update', () => { }); it('only merges with an object', () => { expect(update.bind(null, {}, {$merge: 7})).toThrowError( - 'update(): $merge expects a spec of type \'object\'; got 7' + "update(): $merge expects a spec of type 'object'; got 7", ); }); it('only merges with an object', () => { expect(update.bind(null, 7, {$merge: {a: 'b'}})).toThrowError( - 'update(): $merge expects a target of type \'object\'; got 7' + "update(): $merge expects a target of type 'object'; got 7", ); }); }); @@ -131,32 +130,37 @@ describe('update', () => { }); it('only applies a function', () => { expect(update.bind(null, 2, {$apply: 123})).toThrowError( - 'update(): expected spec of $apply to be a function; got 123.' + 'update(): expected spec of $apply to be a function; got 123.', ); }); }); it('should support deep updates', () => { - expect(update({ - a: 'b', - c: { - d: 'e', - f: [1], - g: [2], - h: [3], - i: {j: 'k'}, - l: 4, - }, - }, { - c: { - d: {$set: 'm'}, - f: {$push: [5]}, - g: {$unshift: [6]}, - h: {$splice: [[0, 1, 7]]}, - i: {$merge: {n: 'o'}}, - l: {$apply: (x) => x * 2}, - }, - })).toEqual({ + expect( + update( + { + a: 'b', + c: { + d: 'e', + f: [1], + g: [2], + h: [3], + i: {j: 'k'}, + l: 4, + }, + }, + { + c: { + d: {$set: 'm'}, + f: {$push: [5]}, + g: {$unshift: [6]}, + h: {$splice: [[0, 1, 7]]}, + i: {$merge: {n: 'o'}}, + l: {$apply: x => x * 2}, + }, + }, + ), + ).toEqual({ a: 'b', c: { d: 'm', @@ -172,14 +176,14 @@ describe('update', () => { it('should require a command', () => { expect(update.bind(null, {a: 'b'}, {a: 'c'})).toThrowError( 'update(): You provided a key path to update() that did not contain ' + - 'one of $push, $unshift, $splice, $set, $merge, $apply. Did you ' + - 'forget to include {$set: ...}?' + 'one of $push, $unshift, $splice, $set, $merge, $apply. Did you ' + + 'forget to include {$set: ...}?', ); }); it('should perform safe hasOwnProperty check', () => { - expect(update({}, {'hasOwnProperty': {$set: 'a'}})).toEqual({ - 'hasOwnProperty': 'a', + expect(update({}, {hasOwnProperty: {$set: 'a'}})).toEqual({ + hasOwnProperty: 'a', }); }); }); diff --git a/src/addons/link/LinkedStateMixin.js b/src/addons/link/LinkedStateMixin.js index 9e446f4836..4a646095df 100644 --- a/src/addons/link/LinkedStateMixin.js +++ b/src/addons/link/LinkedStateMixin.js @@ -30,7 +30,7 @@ var LinkedStateMixin = { linkState: function(key) { return new ReactLink( this.state[key], - ReactStateSetters.createStateKeySetter(this, key) + ReactStateSetters.createStateKeySetter(this, key), ); }, }; diff --git a/src/addons/link/ReactLink.js b/src/addons/link/ReactLink.js index d327b4f8ec..273c4fdcc1 100644 --- a/src/addons/link/ReactLink.js +++ b/src/addons/link/ReactLink.js @@ -48,26 +48,4 @@ function ReactLink(value, requestChange) { this.requestChange = requestChange; } -/** - * Creates a PropType that enforces the ReactLink API and optionally checks the - * type of the value being passed inside the link. Example: - * - * MyComponent.propTypes = { - * tabIndexLink: ReactLink.PropTypes.link(React.PropTypes.number) - * } - */ -function createLinkTypeChecker(linkType) { - var shapes = { - value: linkType === undefined ? - React.PropTypes.any.isRequired : - linkType.isRequired, - requestChange: React.PropTypes.func.isRequired, - }; - return React.PropTypes.shape(shapes); -} - -ReactLink.PropTypes = { - link: createLinkTypeChecker, -}; - module.exports = ReactLink; diff --git a/src/addons/transitions/ReactCSSTransitionGroup.js b/src/addons/transitions/ReactCSSTransitionGroup.js index 517c4faee2..4c1fb3b6e4 100644 --- a/src/addons/transitions/ReactCSSTransitionGroup.js +++ b/src/addons/transitions/ReactCSSTransitionGroup.js @@ -28,16 +28,19 @@ function createTransitionTimeoutPropValidator(transitionType) { // If no timeout duration is provided if (props[timeoutPropName] == null) { return new Error( - timeoutPropName + ' wasn\'t supplied to ReactCSSTransitionGroup: ' + - 'this can cause unreliable animations and won\'t be supported in ' + - 'a future version of React. See ' + - 'https://fb.me/react-animation-transition-group-timeout for more ' + - 'information.' + timeoutPropName + + " wasn't supplied to ReactCSSTransitionGroup: " + + "this can cause unreliable animations and won't be supported in " + + 'a future version of React. See ' + + 'https://fb.me/react-animation-transition-group-timeout for more ' + + 'information.', ); - // If the duration isn't a number + // If the duration isn't a number } else if (typeof props[timeoutPropName] !== 'number') { - return new Error(timeoutPropName + ' must be a number (in milliseconds)'); + return new Error( + timeoutPropName + ' must be a number (in milliseconds)', + ); } } }; @@ -68,7 +71,7 @@ class ReactCSSTransitionGroup extends React.Component { transitionLeave: true, }; - _wrapChild = (child) => { + _wrapChild = child => { // We need to provide this childFactory so that // ReactCSSTransitionGroupChild can receive updates to name, enter, and // leave while it is leaving. @@ -83,14 +86,14 @@ class ReactCSSTransitionGroup extends React.Component { enterTimeout: this.props.transitionEnterTimeout, leaveTimeout: this.props.transitionLeaveTimeout, }, - child + child, ); }; render() { return React.createElement( ReactTransitionGroup, - Object.assign({}, this.props, {childFactory: this._wrapChild}) + Object.assign({}, this.props, {childFactory: this._wrapChild}), ); } } diff --git a/src/addons/transitions/ReactCSSTransitionGroupChild.js b/src/addons/transitions/ReactCSSTransitionGroupChild.js index 536d0a2543..94bf1fd6f0 100644 --- a/src/addons/transitions/ReactCSSTransitionGroupChild.js +++ b/src/addons/transitions/ReactCSSTransitionGroupChild.js @@ -17,7 +17,6 @@ var ReactAddonsDOMDependencies = require('ReactAddonsDOMDependencies'); var propTypesFactory = require('prop-types/factory'); var PropTypes = propTypesFactory(React.isValidElement); - var CSSCore = require('CSSCore'); var ReactTransitionEvents = require('ReactTransitionEvents'); @@ -67,8 +66,10 @@ class ReactCSSTransitionGroupChild extends React.Component { return; } - var className = this.props.name[animationType] || this.props.name + '-' + animationType; - var activeClassName = this.props.name[animationType + 'Active'] || className + '-active'; + var className = + this.props.name[animationType] || this.props.name + '-' + animationType; + var activeClassName = + this.props.name[animationType + 'Active'] || className + '-active'; var timeout = null; var endListener = function(e) { @@ -149,29 +150,29 @@ class ReactCSSTransitionGroupChild extends React.Component { this.classNameAndNodeQueue.length = 0; } - componentWillAppear = (done) => { + componentWillAppear = done => { if (this.props.appear) { this.transition('appear', done, this.props.appearTimeout); } else { done(); } - } + }; - componentWillEnter = (done) => { + componentWillEnter = done => { if (this.props.enter) { this.transition('enter', done, this.props.enterTimeout); } else { done(); } - } + }; - componentWillLeave = (done) => { + componentWillLeave = done => { if (this.props.leave) { this.transition('leave', done, this.props.leaveTimeout); } else { done(); } - } + }; render() { return onlyChild(this.props.children); diff --git a/src/addons/transitions/ReactTransitionChildMapping.js b/src/addons/transitions/ReactTransitionChildMapping.js index a01bd42526..4ba6795dfc 100644 --- a/src/addons/transitions/ReactTransitionChildMapping.js +++ b/src/addons/transitions/ReactTransitionChildMapping.js @@ -86,7 +86,7 @@ var ReactTransitionChildMapping = { for (i = 0; i < nextKeysPending[nextKey].length; i++) { var pendingNextKey = nextKeysPending[nextKey][i]; childMapping[nextKeysPending[nextKey][i]] = getValueForKey( - pendingNextKey + pendingNextKey, ); } } diff --git a/src/addons/transitions/ReactTransitionGroup.js b/src/addons/transitions/ReactTransitionGroup.js index f067a7efb4..cd576aa852 100644 --- a/src/addons/transitions/ReactTransitionGroup.js +++ b/src/addons/transitions/ReactTransitionGroup.js @@ -59,14 +59,14 @@ class ReactTransitionGroup extends React.Component { componentWillReceiveProps(nextProps) { var nextChildMapping = ReactTransitionChildMapping.getChildMapping( - nextProps.children + nextProps.children, ); var prevChildMapping = this.state.children; this.setState({ children: ReactTransitionChildMapping.mergeChildMappings( prevChildMapping, - nextChildMapping + nextChildMapping, ), }); @@ -74,16 +74,22 @@ class ReactTransitionGroup extends React.Component { for (key in nextChildMapping) { var hasPrev = prevChildMapping && prevChildMapping.hasOwnProperty(key); - if (nextChildMapping[key] && !hasPrev && - !this.currentlyTransitioningKeys[key]) { + if ( + nextChildMapping[key] && + !hasPrev && + !this.currentlyTransitioningKeys[key] + ) { this.keysToEnter.push(key); } } for (key in prevChildMapping) { var hasNext = nextChildMapping && nextChildMapping.hasOwnProperty(key); - if (prevChildMapping[key] && !hasNext && - !this.currentlyTransitioningKeys[key]) { + if ( + prevChildMapping[key] && + !hasNext && + !this.currentlyTransitioningKeys[key] + ) { this.keysToLeave.push(key); } } @@ -101,21 +107,19 @@ class ReactTransitionGroup extends React.Component { keysToLeave.forEach(this.performLeave); } - performAppear = (key) => { + performAppear = key => { this.currentlyTransitioningKeys[key] = true; var component = this.refs[key]; if (component.componentWillAppear) { - component.componentWillAppear( - this._handleDoneAppearing.bind(this, key) - ); + component.componentWillAppear(this._handleDoneAppearing.bind(this, key)); } else { this._handleDoneAppearing(key); } }; - _handleDoneAppearing = (key) => { + _handleDoneAppearing = key => { var component = this.refs[key]; if (component.componentDidAppear) { component.componentDidAppear(); @@ -124,7 +128,7 @@ class ReactTransitionGroup extends React.Component { delete this.currentlyTransitioningKeys[key]; var currentChildMapping = ReactTransitionChildMapping.getChildMapping( - this.props.children + this.props.children, ); if (!currentChildMapping || !currentChildMapping.hasOwnProperty(key)) { @@ -133,21 +137,19 @@ class ReactTransitionGroup extends React.Component { } }; - performEnter = (key) => { + performEnter = key => { this.currentlyTransitioningKeys[key] = true; var component = this.refs[key]; if (component.componentWillEnter) { - component.componentWillEnter( - this._handleDoneEntering.bind(this, key) - ); + component.componentWillEnter(this._handleDoneEntering.bind(this, key)); } else { this._handleDoneEntering(key); } }; - _handleDoneEntering = (key) => { + _handleDoneEntering = key => { var component = this.refs[key]; if (component.componentDidEnter) { component.componentDidEnter(); @@ -156,7 +158,7 @@ class ReactTransitionGroup extends React.Component { delete this.currentlyTransitioningKeys[key]; var currentChildMapping = ReactTransitionChildMapping.getChildMapping( - this.props.children + this.props.children, ); if (!currentChildMapping || !currentChildMapping.hasOwnProperty(key)) { @@ -165,7 +167,7 @@ class ReactTransitionGroup extends React.Component { } }; - performLeave = (key) => { + performLeave = key => { this.currentlyTransitioningKeys[key] = true; var component = this.refs[key]; @@ -179,7 +181,7 @@ class ReactTransitionGroup extends React.Component { } }; - _handleDoneLeaving = (key) => { + _handleDoneLeaving = key => { var component = this.refs[key]; if (component.componentDidLeave) { @@ -189,7 +191,7 @@ class ReactTransitionGroup extends React.Component { delete this.currentlyTransitioningKeys[key]; var currentChildMapping = ReactTransitionChildMapping.getChildMapping( - this.props.children + this.props.children, ); if (currentChildMapping && currentChildMapping.hasOwnProperty(key)) { @@ -216,10 +218,12 @@ class ReactTransitionGroup extends React.Component { // already been removed. In case you need this behavior you can provide // a childFactory function to wrap every child, even the ones that are // leaving. - childrenToRender.push(React.cloneElement( - this.props.childFactory(child), - {ref: key, key: key} - )); + childrenToRender.push( + React.cloneElement(this.props.childFactory(child), { + ref: key, + key: key, + }), + ); } } @@ -235,11 +239,7 @@ class ReactTransitionGroup extends React.Component { delete props.transitionAppearTimeout; delete props.component; - return React.createElement( - this.props.component, - props, - childrenToRender - ); + return React.createElement(this.props.component, props, childrenToRender); } } diff --git a/src/addons/transitions/__tests__/ReactTransitionChildMapping-test.js b/src/addons/transitions/__tests__/ReactTransitionChildMapping-test.js index 621549691f..b6c7058989 100644 --- a/src/addons/transitions/__tests__/ReactTransitionChildMapping-test.js +++ b/src/addons/transitions/__tests__/ReactTransitionChildMapping-test.js @@ -27,7 +27,7 @@ describe('ReactTransitionChildMapping', () => { var two =
    ; var component =
    {one}{two}
    ; expect( - ReactTransitionChildMapping.getChildMapping(component.props.children) + ReactTransitionChildMapping.getChildMapping(component.props.children), ).toEqual({ '.$one': one, '.$two': two, diff --git a/src/addons/update.js b/src/addons/update.js index 6e3c41785a..ce5fa018d1 100644 --- a/src/addons/update.js +++ b/src/addons/update.js @@ -9,7 +9,7 @@ * @providesModule update */ - /* global hasOwnProperty:true */ +/* global hasOwnProperty:true */ 'use strict'; @@ -53,15 +53,15 @@ function invariantArrayCase(value, spec, command) { Array.isArray(value), 'update(): expected target of %s to be an array; got %s.', command, - value + value, ); var specValue = spec[command]; invariant( Array.isArray(specValue), 'update(): expected spec of %s to be an array; got %s. ' + - 'Did you forget to wrap your parameter in an array?', + 'Did you forget to wrap your parameter in an array?', command, - specValue + specValue, ); } @@ -73,16 +73,16 @@ function update(value, spec) { invariant( typeof spec === 'object', 'update(): You provided a key path to update() that did not contain one ' + - 'of %s. Did you forget to include {%s: ...}?', + 'of %s. Did you forget to include {%s: ...}?', ALL_COMMANDS_LIST.join(', '), - COMMAND_SET + COMMAND_SET, ); if (hasOwnProperty.call(spec, COMMAND_SET)) { invariant( Object.keys(spec).length === 1, 'Cannot have more than one key in an object with %s', - COMMAND_SET + COMMAND_SET, ); return spec[COMMAND_SET]; @@ -94,15 +94,15 @@ function update(value, spec) { var mergeObj = spec[COMMAND_MERGE]; invariant( mergeObj && typeof mergeObj === 'object', - 'update(): %s expects a spec of type \'object\'; got %s', + "update(): %s expects a spec of type 'object'; got %s", COMMAND_MERGE, - mergeObj + mergeObj, ); invariant( nextValue && typeof nextValue === 'object', - 'update(): %s expects a target of type \'object\'; got %s', + "update(): %s expects a target of type 'object'; got %s", COMMAND_MERGE, - nextValue + nextValue, ); Object.assign(nextValue, spec[COMMAND_MERGE]); } @@ -126,22 +126,22 @@ function update(value, spec) { Array.isArray(value), 'Expected %s target to be an array; got %s', COMMAND_SPLICE, - value + value, ); invariant( Array.isArray(spec[COMMAND_SPLICE]), 'update(): expected spec of %s to be an array of arrays; got %s. ' + - 'Did you forget to wrap your parameters in an array?', + 'Did you forget to wrap your parameters in an array?', COMMAND_SPLICE, - spec[COMMAND_SPLICE] + spec[COMMAND_SPLICE], ); spec[COMMAND_SPLICE].forEach(function(args) { invariant( Array.isArray(args), 'update(): expected spec of %s to be an array of arrays; got %s. ' + - 'Did you forget to wrap your parameters in an array?', + 'Did you forget to wrap your parameters in an array?', COMMAND_SPLICE, - spec[COMMAND_SPLICE] + spec[COMMAND_SPLICE], ); nextValue.splice.apply(nextValue, args); }); @@ -152,7 +152,7 @@ function update(value, spec) { typeof spec[COMMAND_APPLY] === 'function', 'update(): expected spec of %s to be a function; got %s.', COMMAND_APPLY, - spec[COMMAND_APPLY] + spec[COMMAND_APPLY], ); nextValue = spec[COMMAND_APPLY](nextValue); } diff --git a/src/isomorphic/React.js b/src/isomorphic/React.js index 39b6aa31ce..29cfea6dad 100644 --- a/src/isomorphic/React.js +++ b/src/isomorphic/React.js @@ -11,23 +11,22 @@ 'use strict'; +var ReactBaseClasses = require('ReactBaseClasses'); var ReactChildren = require('ReactChildren'); -var ReactComponent = require('ReactComponent'); -var ReactPureComponent = require('ReactPureComponent'); -var ReactClass = require('ReactClass'); var ReactDOMFactories = require('ReactDOMFactories'); var ReactElement = require('ReactElement'); var ReactPropTypes = require('ReactPropTypes'); var ReactVersion = require('ReactVersion'); +var createReactClass = require('createClass'); var onlyChild = require('onlyChild'); -var warning = require('warning'); var createElement = ReactElement.createElement; var createFactory = ReactElement.createFactory; var cloneElement = ReactElement.cloneElement; if (__DEV__) { + var lowPriorityWarning = require('lowPriorityWarning'); var canDefineProperty = require('canDefineProperty'); var ReactElementValidator = require('ReactElementValidator'); var didWarnPropTypesDeprecated = false; @@ -37,24 +36,39 @@ if (__DEV__) { } var __spread = Object.assign; +var createMixin = function(mixin) { + return mixin; +}; if (__DEV__) { - var warned = false; + var warnedForSpread = false; + var warnedForCreateMixin = false; __spread = function() { - warning( - warned, + lowPriorityWarning( + warnedForSpread, 'React.__spread is deprecated and should not be used. Use ' + - 'Object.assign directly or another helper function with similar ' + - 'semantics. You may be seeing this warning due to your compiler. ' + - 'See https://fb.me/react-spread-deprecation for more details.' + 'Object.assign directly or another helper function with similar ' + + 'semantics. You may be seeing this warning due to your compiler. ' + + 'See https://fb.me/react-spread-deprecation for more details.', ); - warned = true; + warnedForSpread = true; return Object.assign.apply(null, arguments); }; + + createMixin = function(mixin) { + lowPriorityWarning( + warnedForCreateMixin, + 'React.createMixin is deprecated and should not be used. ' + + 'In React v16.0, it will be removed. ' + + 'You can use this mixin directly instead. ' + + 'See https://fb.me/createmixin-was-never-implemented for more info.', + ); + warnedForCreateMixin = true; + return mixin; + }; } var React = { - // Modern Children: { @@ -65,8 +79,8 @@ var React = { only: onlyChild, }, - Component: ReactComponent, - PureComponent: ReactPureComponent, + Component: ReactBaseClasses.Component, + PureComponent: ReactBaseClasses.PureComponent, createElement: createElement, cloneElement: cloneElement, @@ -75,12 +89,9 @@ var React = { // Classic PropTypes: ReactPropTypes, - createClass: ReactClass.createClass, + createClass: createReactClass, createFactory: createFactory, - createMixin: function(mixin) { - // Currently a noop. Will be used to validate and trace mixins. - return mixin; - }, + createMixin: createMixin, // This looks DOM specific but these are actually isomorphic helpers // since they are just generating DOM strings. @@ -92,21 +103,63 @@ var React = { __spread: __spread, }; -// TODO: Fix tests so that this deprecation warning doesn't cause failures. if (__DEV__) { + let warnedForCreateClass = false; if (canDefineProperty) { Object.defineProperty(React, 'PropTypes', { get() { - warning( + lowPriorityWarning( didWarnPropTypesDeprecated, - 'Accessing PropTypes via the main React package is deprecated. Use ' + - 'the prop-types package from npm instead.' + 'Accessing PropTypes via the main React package is deprecated,' + + ' and will be removed in React v16.0.' + + ' Use the latest available v15.* prop-types package from npm instead.' + + ' For info on usage, compatibility, migration and more, see ' + + 'https://fb.me/prop-types-docs', ); didWarnPropTypesDeprecated = true; return ReactPropTypes; }, }); + + Object.defineProperty(React, 'createClass', { + get: function() { + lowPriorityWarning( + warnedForCreateClass, + 'Accessing createClass via the main React package is deprecated,' + + ' and will be removed in React v16.0.' + + " Use a plain JavaScript class instead. If you're not yet " + + 'ready to migrate, create-react-class v15.* is available ' + + 'on npm as a temporary, drop-in replacement. ' + + 'For more info see https://fb.me/react-create-class', + ); + warnedForCreateClass = true; + return createReactClass; + }, + }); } + + // React.DOM factories are deprecated. Wrap these methods so that + // invocations of the React.DOM namespace and alert users to switch + // to the `react-dom-factories` package. + React.DOM = {}; + var warnedForFactories = false; + Object.keys(ReactDOMFactories).forEach(function(factory) { + React.DOM[factory] = function(...args) { + if (!warnedForFactories) { + lowPriorityWarning( + false, + 'Accessing factories like React.DOM.%s has been deprecated ' + + 'and will be removed in v16.0+. Use the ' + + 'react-dom-factories package instead. ' + + ' Version 1.0 provides a drop-in replacement.' + + ' For more info, see https://fb.me/react-dom-factories', + factory, + ); + warnedForFactories = true; + } + return ReactDOMFactories[factory](...args); + }; + }); } module.exports = React; diff --git a/src/isomorphic/__tests__/React-test.js b/src/isomorphic/__tests__/React-test.js new file mode 100644 index 0000000000..7a2d0c9ac9 --- /dev/null +++ b/src/isomorphic/__tests__/React-test.js @@ -0,0 +1,71 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @emails react-core + */ + +'use strict'; + +describe('React', () => { + var React; + + beforeEach(() => { + React = require('React'); + }); + + it('should log a deprecation warning once when using React.__spread', () => { + spyOn(console, 'warn'); + React.__spread({}); + React.__spread({}); + expect(console.warn.calls.count()).toBe(1); + expect(console.warn.calls.argsFor(0)[0]).toContain( + 'React.__spread is deprecated and should not be used', + ); + }); + + it('should log a deprecation warning once when using React.createMixin', () => { + spyOn(console, 'warn'); + React.createMixin(); + React.createMixin(); + expect(console.warn.calls.count()).toBe(1); + expect(console.warn.calls.argsFor(0)[0]).toContain( + 'React.createMixin is deprecated and should not be used', + ); + }); + + it('should warn once when attempting to access React.createClass', () => { + spyOn(console, 'warn'); + let createClass = React.createClass; + createClass = React.createClass; + expect(createClass).not.toBe(undefined); + expect(console.warn.calls.count()).toBe(1); + expect(console.warn.calls.argsFor(0)[0]).toContain( + 'Warning: Accessing createClass via the main React package is ' + + 'deprecated, and will be removed in React v16.0. ' + + "Use a plain JavaScript class instead. If you're not yet ready " + + 'to migrate, create-react-class v15.* is available on npm as ' + + 'a temporary, drop-in replacement. ' + + 'For more info see https://fb.me/react-create-class', + ); + }); + + it('should warn once when attempting to access React.PropTypes', () => { + spyOn(console, 'warn'); + let PropTypes = React.PropTypes; + PropTypes = React.PropTypes; + expect(PropTypes).not.toBe(undefined); + expect(console.warn.calls.count()).toBe(1); + expect(console.warn.calls.argsFor(0)[0]).toContain( + 'Warning: Accessing PropTypes via the main React package is ' + + 'deprecated, and will be removed in React v16.0. ' + + 'Use the latest available v15.* prop-types package from ' + + 'npm instead. For info on usage, compatibility, migration ' + + 'and more, see https://fb.me/prop-types-docs', + ); + }); +}); diff --git a/src/isomorphic/children/ReactChildren.js b/src/isomorphic/children/ReactChildren.js index 75670dbe36..645c361ff6 100644 --- a/src/isomorphic/children/ReactChildren.js +++ b/src/isomorphic/children/ReactChildren.js @@ -20,13 +20,11 @@ var traverseAllChildren = require('traverseAllChildren'); var twoArgumentPooler = PooledClass.twoArgumentPooler; var fourArgumentPooler = PooledClass.fourArgumentPooler; - var userProvidedKeyEscapeRegex = /\/+/g; function escapeUserProvidedKey(text) { return ('' + text).replace(userProvidedKeyEscapeRegex, '$&/'); } - /** * PooledClass representing the bookkeeping associated with performing a child * traversal. Allows avoiding binding callbacks. @@ -68,13 +66,14 @@ function forEachChildren(children, forEachFunc, forEachContext) { if (children == null) { return children; } - var traverseContext = - ForEachBookKeeping.getPooled(forEachFunc, forEachContext); + var traverseContext = ForEachBookKeeping.getPooled( + forEachFunc, + forEachContext, + ); traverseAllChildren(children, forEachSingleChild, traverseContext); ForEachBookKeeping.release(traverseContext); } - /** * PooledClass representing the bookkeeping associated with performing a child * mapping. Allows avoiding binding callbacks. @@ -109,7 +108,7 @@ function mapSingleChildIntoContext(bookKeeping, child, childKey) { mappedChild, result, childKey, - emptyFunction.thatReturnsArgument + emptyFunction.thatReturnsArgument, ); } else if (mappedChild != null) { if (ReactElement.isValidElement(mappedChild)) { @@ -118,12 +117,10 @@ function mapSingleChildIntoContext(bookKeeping, child, childKey) { // Keep both the (mapped) and old keys if they differ, just as // traverseAllChildren used to do for objects as children keyPrefix + - ( - (mappedChild.key && (!child || (child.key !== mappedChild.key))) ? - escapeUserProvidedKey(mappedChild.key) + '/' : - '' - ) + - childKey + (mappedChild.key && (!child || child.key !== mappedChild.key) + ? escapeUserProvidedKey(mappedChild.key) + '/' + : '') + + childKey, ); } result.push(mappedChild); @@ -139,7 +136,7 @@ function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) { array, escapedPrefix, func, - context + context, ); traverseAllChildren(children, mapSingleChildIntoContext, traverseContext); MapBookKeeping.release(traverseContext); @@ -167,8 +164,6 @@ function mapChildren(children, func, context) { return result; } - - function forEachSingleChildDummy(traverseContext, child, name) { return null; } @@ -186,7 +181,6 @@ function countChildren(children, context) { return traverseAllChildren(children, forEachSingleChildDummy, null); } - /** * Flatten a children object (typically specified as `props.children`) and * return an array with appropriately re-keyed children. @@ -199,12 +193,11 @@ function toArray(children) { children, result, null, - emptyFunction.thatReturnsArgument + emptyFunction.thatReturnsArgument, ); return result; } - var ReactChildren = { forEach: forEachChildren, map: mapChildren, diff --git a/src/isomorphic/children/__tests__/ReactChildren-test.js b/src/isomorphic/children/__tests__/ReactChildren-test.js index 6eb4c89e2d..d6e6a8bfc5 100644 --- a/src/isomorphic/children/__tests__/ReactChildren-test.js +++ b/src/isomorphic/children/__tests__/ReactChildren-test.js @@ -69,7 +69,6 @@ describe('ReactChildren', () => { var mappedChildren = ReactChildren.map(instance.props.children, callback); expect(callback).toHaveBeenCalledWith(simpleKid, 0); expect(mappedChildren[0]).toEqual(); - }); it('should pass key to returned component', () => { @@ -103,8 +102,11 @@ describe('ReactChildren', () => { ReactChildren.forEach(instance.props.children, callback, scopeTester); expect(lastContext).toBe(scopeTester); - var mappedChildren = - ReactChildren.map(instance.props.children, callback, scopeTester); + var mappedChildren = ReactChildren.map( + instance.props.children, + callback, + scopeTester, + ); expect(ReactChildren.count(mappedChildren)).toBe(1); expect(mappedChildren[0]).toBe(scopeTester); @@ -118,9 +120,9 @@ describe('ReactChildren', () => { var four =
    ; var mapped = [ -
    , // Key should be joined to obj key - null, // Key should be added even if we don't supply it! -
    , // Key should be added even if not supplied! +
    , // Key should be joined to obj key + null, // Key should be added even if we don't supply it! +
    , // Key should be added even if not supplied! , // Map from null to something.
    , ]; @@ -146,8 +148,7 @@ describe('ReactChildren', () => { expect(callback).toHaveBeenCalledWith(four, 4); callback.calls.reset(); - var mappedChildren = - ReactChildren.map(instance.props.children, callback); + var mappedChildren = ReactChildren.map(instance.props.children, callback); expect(callback.calls.count()).toBe(5); expect(ReactChildren.count(mappedChildren)).toBe(4); // Keys default to indices. @@ -156,9 +157,7 @@ describe('ReactChildren', () => { mappedChildren[1].key, mappedChildren[2].key, mappedChildren[3].key, - ]).toEqual( - ['giraffe/.$keyZero', '.$keyTwo', '.3', '.$keyFour'] - ); + ]).toEqual(['giraffe/.$keyZero', '.$keyTwo', '.3', '.$keyFour']); expect(callback).toHaveBeenCalledWith(zero, 0); expect(callback).toHaveBeenCalledWith(one, 1); @@ -185,15 +184,15 @@ describe('ReactChildren', () => { // 1. If grouped in an Object, the object key combined with `key` prop // 2. If grouped in an Array, the `key` prop, falling back to array index - var zeroMapped =
    ; // Key should be overridden - var twoMapped =
    ; // Key should be added even if not supplied! + var zeroMapped =
    ; // Key should be overridden + var twoMapped =
    ; // Key should be added even if not supplied! var fourMapped =
    ; var fiveMapped =
    ; var callback = jasmine.createSpy().and.callFake(function(kid, index) { - return index === 0 ? zeroMapped : - index === 1 ? twoMapped : - index === 2 ? fourMapped : fiveMapped; + return index === 0 + ? zeroMapped + : index === 1 ? twoMapped : index === 2 ? fourMapped : fiveMapped; }); var frag = ReactFragment.create({ @@ -203,12 +202,7 @@ describe('ReactChildren', () => { }); var instance =
    {[frag]}
    ; - expect([ - frag[0].key, - frag[1].key, - frag[2].key, - frag[3].key, - ]).toEqual([ + expect([frag[0].key, frag[1].key, frag[2].key, frag[3].key]).toEqual([ 'firstHalfKey/.$keyZero', 'firstHalfKey/.$keyTwo', 'secondHalfKey/.$keyFour', @@ -244,9 +238,13 @@ describe('ReactChildren', () => { '.0:$keyFive/.$keyFiveInner', ]); - expect(mappedChildren[0]).toEqual(
    ); + expect(mappedChildren[0]).toEqual( +
    , + ); expect(mappedChildren[1]).toEqual(
    ); - expect(mappedChildren[2]).toEqual(
    ); + expect(mappedChildren[2]).toEqual( +
    , + ); expect(mappedChildren[3]).toEqual(
    ); }); @@ -271,21 +269,24 @@ describe('ReactChildren', () => { ); var expectedForcedKeys = ['giraffe/.$keyZero', '.$keyOne']; - var mappedChildrenForcedKeys = - ReactChildren.map(forcedKeys.props.children, mapFn); - var mappedForcedKeys = mappedChildrenForcedKeys.map((c) => c.key); + var mappedChildrenForcedKeys = ReactChildren.map( + forcedKeys.props.children, + mapFn, + ); + var mappedForcedKeys = mappedChildrenForcedKeys.map(c => c.key); expect(mappedForcedKeys).toEqual(expectedForcedKeys); var expectedRemappedForcedKeys = [ 'giraffe/.$giraffe/.$keyZero', '.$.$keyOne', ]; - var remappedChildrenForcedKeys = - ReactChildren.map(mappedChildrenForcedKeys, mapFn); - expect( - remappedChildrenForcedKeys.map((c) => c.key) - ).toEqual(expectedRemappedForcedKeys); - + var remappedChildrenForcedKeys = ReactChildren.map( + mappedChildrenForcedKeys, + mapFn, + ); + expect(remappedChildrenForcedKeys.map(c => c.key)).toEqual( + expectedRemappedForcedKeys, + ); }); it('should not throw if key provided is a dupe with array key', () => { @@ -315,14 +316,10 @@ describe('ReactChildren', () => {
    ); - var mapped = ReactChildren.map( - instance.props.children, - element => element, - ); + var mapped = ReactChildren.map(instance.props.children, element => element); - var mappedWithClone = ReactChildren.map( - instance.props.children, - element => React.cloneElement(element), + var mappedWithClone = ReactChildren.map(instance.props.children, element => + React.cloneElement(element), ); expect(mapped[0].key).toBe(mappedWithClone[0].key); @@ -335,14 +332,10 @@ describe('ReactChildren', () => {
    ); - var mapped = ReactChildren.map( - instance.props.children, - element => element, - ); + var mapped = ReactChildren.map(instance.props.children, element => element); - var mappedWithClone = ReactChildren.map( - instance.props.children, - element => React.cloneElement(element, {key: 'unique'}), + var mappedWithClone = ReactChildren.map(instance.props.children, element => + React.cloneElement(element, {key: 'unique'}), ); expect(mapped[0].key).toBe(mappedWithClone[0].key); @@ -399,14 +392,16 @@ describe('ReactChildren', () => { // 2. If grouped in an Array, the `key` prop, falling back to array index var instance = ( -
    {[ - ReactFragment.create({ - firstHalfKey: [zero, one, two], - secondHalfKey: [three, four], - keyFive: five, - }), - null, - ]}
    +
    + {[ + ReactFragment.create({ + firstHalfKey: [zero, one, two], + secondHalfKey: [three, four], + keyFive: five, + }), + null, + ]} +
    ); var numberOfChildren = ReactChildren.count(instance.props.children); expect(numberOfChildren).toBe(5); @@ -418,10 +413,8 @@ describe('ReactChildren', () => { expect(ReactChildren.toArray(
    ).length).toBe(1); expect(ReactChildren.toArray([
    ]).length).toBe(1); - expect( - ReactChildren.toArray(
    )[0].key - ).toBe( - ReactChildren.toArray([
    ])[0].key + expect(ReactChildren.toArray(
    )[0].key).toBe( + ReactChildren.toArray([
    ])[0].key, ); var flattened = ReactChildren.toArray([ @@ -445,9 +438,9 @@ describe('ReactChildren', () => { expect(flattened[5].key).toBe(reversed[3].key); // null/undefined/bool are all omitted - expect(ReactChildren.toArray([1, 'two', null, undefined, true])).toEqual( - [1, 'two'] - ); + expect(ReactChildren.toArray([1, 'two', null, undefined, true])).toEqual([ + 1, + 'two', + ]); }); - }); diff --git a/src/isomorphic/children/__tests__/onlyChild-test.js b/src/isomorphic/children/__tests__/onlyChild-test.js index 8de1c9b807..0c8870c6d7 100644 --- a/src/isomorphic/children/__tests__/onlyChild-test.js +++ b/src/isomorphic/children/__tests__/onlyChild-test.js @@ -12,7 +12,6 @@ 'use strict'; describe('onlyChild', () => { - var React; var ReactFragment; var onlyChild; @@ -35,63 +34,66 @@ describe('onlyChild', () => { it('should fail when passed two children', () => { expect(function() { - var instance = + var instance = (
    - ; + + ); onlyChild(instance.props.children); }).toThrow(); }); it('should fail when passed nully values', () => { expect(function() { - var instance = + var instance = ( {null} - ; + + ); onlyChild(instance.props.children); }).toThrow(); expect(function() { - var instance = + var instance = ( {undefined} - ; + + ); onlyChild(instance.props.children); }).toThrow(); }); it('should fail when key/value objects', () => { expect(function() { - var instance = + var instance = ( {ReactFragment.create({oneThing: })} - ; + + ); onlyChild(instance.props.children); }).toThrow(); }); - it('should not fail when passed interpolated single child', () => { expect(function() { - var instance = + var instance = ( {} - ; + + ); onlyChild(instance.props.children); }).not.toThrow(); }); - it('should return the only child', () => { expect(function() { - var instance = + var instance = ( - ; + + ); onlyChild(instance.props.children); }).not.toThrow(); }); - }); diff --git a/src/isomorphic/children/__tests__/sliceChildren-test.js b/src/isomorphic/children/__tests__/sliceChildren-test.js index 5b41e1f24b..d255324a72 100644 --- a/src/isomorphic/children/__tests__/sliceChildren-test.js +++ b/src/isomorphic/children/__tests__/sliceChildren-test.js @@ -12,7 +12,6 @@ 'use strict'; describe('sliceChildren', () => { - var React; var sliceChildren; @@ -24,11 +23,7 @@ describe('sliceChildren', () => { }); it('should render the whole set if start zero is supplied', () => { - var fullSet = [ -
    , -
    , -
    , - ]; + var fullSet = [
    ,
    ,
    ]; var children = sliceChildren(fullSet, 0); expect(children).toEqual([
    , @@ -38,16 +33,9 @@ describe('sliceChildren', () => { }); it('should render the remaining set if no end index is supplied', () => { - var fullSet = [ -
    , -
    , -
    , - ]; + var fullSet = [
    ,
    ,
    ]; var children = sliceChildren(fullSet, 1); - expect(children).toEqual([ -
    , -
    , - ]); + expect(children).toEqual([
    ,
    ]); }); it('should exclude everything at or after the end index', () => { @@ -58,9 +46,7 @@ describe('sliceChildren', () => {
    , ]; var children = sliceChildren(fullSet, 1, 2); - expect(children).toEqual([ -
    , - ]); + expect(children).toEqual([
    ]); }); it('should allow static children to be sliced', () => { @@ -70,24 +56,16 @@ describe('sliceChildren', () => { var el =
    {a}{b}{c}
    ; var children = sliceChildren(el.props.children, 1, 2); - expect(children).toEqual([ - , - ]); + expect(children).toEqual([]); }); it('should slice nested children', () => { var fullSet = [
    , - [ -
    , -
    , - ], + [
    ,
    ],
    , ]; var children = sliceChildren(fullSet, 1, 2); - expect(children).toEqual([ -
    , - ]); + expect(children).toEqual([
    ]); }); - }); diff --git a/src/isomorphic/children/onlyChild.js b/src/isomorphic/children/onlyChild.js index 43ec44f29c..8801fd6dc2 100644 --- a/src/isomorphic/children/onlyChild.js +++ b/src/isomorphic/children/onlyChild.js @@ -31,7 +31,7 @@ var invariant = require('invariant'); function onlyChild(children) { invariant( ReactElement.isValidElement(children), - 'React.Children.only expected to receive a single React element child.' + 'React.Children.only expected to receive a single React element child.', ); return children; } diff --git a/src/isomorphic/classic/__tests__/ReactContextValidator-test.js b/src/isomorphic/classic/__tests__/ReactContextValidator-test.js index f6efecc9b8..29a9ac8913 100644 --- a/src/isomorphic/classic/__tests__/ReactContextValidator-test.js +++ b/src/isomorphic/classic/__tests__/ReactContextValidator-test.js @@ -17,6 +17,7 @@ 'use strict'; +var PropTypes; var React; var ReactDOM; var ReactTestUtils; @@ -34,6 +35,7 @@ describe('ReactContextValidator', () => { React = require('React'); ReactDOM = require('ReactDOM'); ReactTestUtils = require('ReactTestUtils'); + PropTypes = require('prop-types'); reactComponentExpect = require('reactComponentExpect'); }); @@ -47,7 +49,7 @@ describe('ReactContextValidator', () => { } } Component.contextTypes = { - foo: React.PropTypes.string, + foo: PropTypes.string, }; class ComponentInFooBarContext extends React.Component { @@ -63,12 +65,16 @@ describe('ReactContextValidator', () => { } } ComponentInFooBarContext.childContextTypes = { - foo: React.PropTypes.string, - bar: React.PropTypes.number, + foo: PropTypes.string, + bar: PropTypes.number, }; - var instance = ReactTestUtils.renderIntoDocument(); - reactComponentExpect(instance).expectRenderedChild().scalarContextEqual({foo: 'abc'}); + var instance = ReactTestUtils.renderIntoDocument( + , + ); + reactComponentExpect(instance) + .expectRenderedChild() + .scalarContextEqual({foo: 'abc'}); }); it('should filter context properly in callbacks', () => { @@ -90,8 +96,8 @@ describe('ReactContextValidator', () => { } } Parent.childContextTypes = { - foo: React.PropTypes.string.isRequired, - bar: React.PropTypes.string.isRequired, + foo: PropTypes.string.isRequired, + bar: PropTypes.string.isRequired, }; class Component extends React.Component { @@ -118,10 +124,9 @@ describe('ReactContextValidator', () => { } } Component.contextTypes = { - foo: React.PropTypes.string, + foo: PropTypes.string, }; - var container = document.createElement('div'); ReactDOM.render(, container); ReactDOM.render(, container); @@ -140,7 +145,7 @@ describe('ReactContextValidator', () => { } } Component.contextTypes = { - foo: React.PropTypes.string.isRequired, + foo: PropTypes.string.isRequired, }; ReactTestUtils.renderIntoDocument(); @@ -148,9 +153,9 @@ describe('ReactContextValidator', () => { expect(console.error.calls.count()).toBe(1); expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( 'Warning: Failed context type: ' + - 'The context `foo` is marked as required in `Component`, but its value ' + - 'is `undefined`.\n' + - ' in Component (at **)' + 'The context `foo` is marked as required in `Component`, but its value ' + + 'is `undefined`.\n' + + ' in Component (at **)', ); class ComponentInFooStringContext extends React.Component { @@ -165,11 +170,11 @@ describe('ReactContextValidator', () => { } } ComponentInFooStringContext.childContextTypes = { - foo: React.PropTypes.string, + foo: PropTypes.string, }; ReactTestUtils.renderIntoDocument( - + , ); // Previous call should not error @@ -187,18 +192,20 @@ describe('ReactContextValidator', () => { } } ComponentInFooNumberContext.childContextTypes = { - foo: React.PropTypes.number, + foo: PropTypes.number, }; - ReactTestUtils.renderIntoDocument(); + ReactTestUtils.renderIntoDocument( + , + ); expect(console.error.calls.count()).toBe(2); expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe( 'Warning: Failed context type: ' + - 'Invalid context `foo` of type `number` supplied ' + - 'to `Component`, expected `string`.\n' + - ' in Component (at **)\n' + - ' in ComponentInFooNumberContext (at **)' + 'Invalid context `foo` of type `number` supplied ' + + 'to `Component`, expected `string`.\n' + + ' in Component (at **)\n' + + ' in ComponentInFooNumberContext (at **)', ); }); @@ -215,17 +222,17 @@ describe('ReactContextValidator', () => { } } Component.childContextTypes = { - foo: React.PropTypes.string.isRequired, - bar: React.PropTypes.number, + foo: PropTypes.string.isRequired, + bar: PropTypes.number, }; ReactTestUtils.renderIntoDocument(); expect(console.error.calls.count()).toBe(1); expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( 'Warning: Failed child context type: ' + - 'The child context `foo` is marked as required in `Component`, but its ' + - 'value is `undefined`.\n' + - ' in Component (at **)' + 'The child context `foo` is marked as required in `Component`, but its ' + + 'value is `undefined`.\n' + + ' in Component (at **)', ); ReactTestUtils.renderIntoDocument(); @@ -233,21 +240,18 @@ describe('ReactContextValidator', () => { expect(console.error.calls.count()).toBe(2); expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe( 'Warning: Failed child context type: ' + - 'Invalid child context `foo` of type `number` ' + - 'supplied to `Component`, expected `string`.\n' + - ' in Component (at **)' + 'Invalid child context `foo` of type `number` ' + + 'supplied to `Component`, expected `string`.\n' + + ' in Component (at **)', ); ReactTestUtils.renderIntoDocument( - + , ); - ReactTestUtils.renderIntoDocument( - - ); + ReactTestUtils.renderIntoDocument(); // Previous calls should not log errors expect(console.error.calls.count()).toBe(2); }); - }); diff --git a/src/isomorphic/classic/class/ReactClass.js b/src/isomorphic/classic/class/ReactClass.js deleted file mode 100644 index 9c9506e630..0000000000 --- a/src/isomorphic/classic/class/ReactClass.js +++ /dev/null @@ -1,902 +0,0 @@ -/** - * Copyright 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule ReactClass - */ - -'use strict'; - -var ReactComponent = require('ReactComponent'); -var ReactElement = require('ReactElement'); -var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames'); -var ReactNoopUpdateQueue = require('ReactNoopUpdateQueue'); - -var emptyObject = require('emptyObject'); -var invariant = require('invariant'); -var warning = require('warning'); - -import type { ReactPropTypeLocations } from 'ReactPropTypeLocations'; - -var MIXINS_KEY = 'mixins'; - -// Helper function to allow the creation of anonymous functions which do not -// have .name set to the name of the variable being assigned to. -function identity(fn) { - return fn; -} - -/** - * Policies that describe methods in `ReactClassInterface`. - */ -type SpecPolicy = - /** - * These methods may be defined only once by the class specification or mixin. - */ - 'DEFINE_ONCE' | - /** - * These methods may be defined by both the class specification and mixins. - * Subsequent definitions will be chained. These methods must return void. - */ - 'DEFINE_MANY' | - /** - * These methods are overriding the base class. - */ - 'OVERRIDE_BASE' | - /** - * These methods are similar to DEFINE_MANY, except we assume they return - * objects. We try to merge the keys of the return values of all the mixed in - * functions. If there is a key conflict we throw. - */ - 'DEFINE_MANY_MERGED'; - - -var injectedMixins = []; - -/** - * Composite components are higher-level components that compose other composite - * or host components. - * - * To create a new type of `ReactClass`, pass a specification of - * your new class to `React.createClass`. The only requirement of your class - * specification is that you implement a `render` method. - * - * var MyComponent = React.createClass({ - * render: function() { - * return
    Hello World
    ; - * } - * }); - * - * The class specification supports a specific protocol of methods that have - * special meaning (e.g. `render`). See `ReactClassInterface` for - * more the comprehensive protocol. Any other properties and methods in the - * class specification will be available on the prototype. - * - * @interface ReactClassInterface - * @internal - */ -var ReactClassInterface: {[key: string]: SpecPolicy} = { - - /** - * An array of Mixin objects to include when defining your component. - * - * @type {array} - * @optional - */ - mixins: 'DEFINE_MANY', - - /** - * An object containing properties and methods that should be defined on - * the component's constructor instead of its prototype (static methods). - * - * @type {object} - * @optional - */ - statics: 'DEFINE_MANY', - - /** - * Definition of prop types for this component. - * - * @type {object} - * @optional - */ - propTypes: 'DEFINE_MANY', - - /** - * Definition of context types for this component. - * - * @type {object} - * @optional - */ - contextTypes: 'DEFINE_MANY', - - /** - * Definition of context types this component sets for its children. - * - * @type {object} - * @optional - */ - childContextTypes: 'DEFINE_MANY', - - // ==== Definition methods ==== - - /** - * Invoked when the component is mounted. Values in the mapping will be set on - * `this.props` if that prop is not specified (i.e. using an `in` check). - * - * This method is invoked before `getInitialState` and therefore cannot rely - * on `this.state` or use `this.setState`. - * - * @return {object} - * @optional - */ - getDefaultProps: 'DEFINE_MANY_MERGED', - - /** - * Invoked once before the component is mounted. The return value will be used - * as the initial value of `this.state`. - * - * getInitialState: function() { - * return { - * isOn: false, - * fooBaz: new BazFoo() - * } - * } - * - * @return {object} - * @optional - */ - getInitialState: 'DEFINE_MANY_MERGED', - - /** - * @return {object} - * @optional - */ - getChildContext: 'DEFINE_MANY_MERGED', - - /** - * Uses props from `this.props` and state from `this.state` to render the - * structure of the component. - * - * No guarantees are made about when or how often this method is invoked, so - * it must not have side effects. - * - * render: function() { - * var name = this.props.name; - * return
    Hello, {name}!
    ; - * } - * - * @return {ReactComponent} - * @required - */ - render: 'DEFINE_ONCE', - - - - // ==== Delegate methods ==== - - /** - * Invoked when the component is initially created and about to be mounted. - * This may have side effects, but any external subscriptions or data created - * by this method must be cleaned up in `componentWillUnmount`. - * - * @optional - */ - componentWillMount: 'DEFINE_MANY', - - /** - * Invoked when the component has been mounted and has a DOM representation. - * However, there is no guarantee that the DOM node is in the document. - * - * Use this as an opportunity to operate on the DOM when the component has - * been mounted (initialized and rendered) for the first time. - * - * @param {DOMElement} rootNode DOM element representing the component. - * @optional - */ - componentDidMount: 'DEFINE_MANY', - - /** - * Invoked before the component receives new props. - * - * Use this as an opportunity to react to a prop transition by updating the - * state using `this.setState`. Current props are accessed via `this.props`. - * - * componentWillReceiveProps: function(nextProps, nextContext) { - * this.setState({ - * likesIncreasing: nextProps.likeCount > this.props.likeCount - * }); - * } - * - * NOTE: There is no equivalent `componentWillReceiveState`. An incoming prop - * transition may cause a state change, but the opposite is not true. If you - * need it, you are probably looking for `componentWillUpdate`. - * - * @param {object} nextProps - * @optional - */ - componentWillReceiveProps: 'DEFINE_MANY', - - /** - * Invoked while deciding if the component should be updated as a result of - * receiving new props, state and/or context. - * - * Use this as an opportunity to `return false` when you're certain that the - * transition to the new props/state/context will not require a component - * update. - * - * shouldComponentUpdate: function(nextProps, nextState, nextContext) { - * return !equal(nextProps, this.props) || - * !equal(nextState, this.state) || - * !equal(nextContext, this.context); - * } - * - * @param {object} nextProps - * @param {?object} nextState - * @param {?object} nextContext - * @return {boolean} True if the component should update. - * @optional - */ - shouldComponentUpdate: 'DEFINE_ONCE', - - /** - * Invoked when the component is about to update due to a transition from - * `this.props`, `this.state` and `this.context` to `nextProps`, `nextState` - * and `nextContext`. - * - * Use this as an opportunity to perform preparation before an update occurs. - * - * NOTE: You **cannot** use `this.setState()` in this method. - * - * @param {object} nextProps - * @param {?object} nextState - * @param {?object} nextContext - * @param {ReactReconcileTransaction} transaction - * @optional - */ - componentWillUpdate: 'DEFINE_MANY', - - /** - * Invoked when the component's DOM representation has been updated. - * - * Use this as an opportunity to operate on the DOM when the component has - * been updated. - * - * @param {object} prevProps - * @param {?object} prevState - * @param {?object} prevContext - * @param {DOMElement} rootNode DOM element representing the component. - * @optional - */ - componentDidUpdate: 'DEFINE_MANY', - - /** - * Invoked when the component is about to be removed from its parent and have - * its DOM representation destroyed. - * - * Use this as an opportunity to deallocate any external resources. - * - * NOTE: There is no `componentDidUnmount` since your component will have been - * destroyed by that point. - * - * @optional - */ - componentWillUnmount: 'DEFINE_MANY', - - - - // ==== Advanced methods ==== - - /** - * Updates the component's currently mounted DOM representation. - * - * By default, this implements React's rendering and reconciliation algorithm. - * Sophisticated clients may wish to override this. - * - * @param {ReactReconcileTransaction} transaction - * @internal - * @overridable - */ - updateComponent: 'OVERRIDE_BASE', - -}; - -/** - * Mapping from class specification keys to special processing functions. - * - * Although these are declared like instance properties in the specification - * when defining classes using `React.createClass`, they are actually static - * and are accessible on the constructor instead of the prototype. Despite - * being static, they must be defined outside of the "statics" key under - * which all other static methods are defined. - */ -var RESERVED_SPEC_KEYS = { - displayName: function(Constructor, displayName) { - Constructor.displayName = displayName; - }, - mixins: function(Constructor, mixins) { - if (mixins) { - for (var i = 0; i < mixins.length; i++) { - mixSpecIntoComponent(Constructor, mixins[i]); - } - } - }, - childContextTypes: function(Constructor, childContextTypes) { - if (__DEV__) { - validateTypeDef( - Constructor, - childContextTypes, - 'childContext' - ); - } - Constructor.childContextTypes = Object.assign( - {}, - Constructor.childContextTypes, - childContextTypes - ); - }, - contextTypes: function(Constructor, contextTypes) { - if (__DEV__) { - validateTypeDef( - Constructor, - contextTypes, - 'context' - ); - } - Constructor.contextTypes = Object.assign( - {}, - Constructor.contextTypes, - contextTypes - ); - }, - /** - * Special case getDefaultProps which should move into statics but requires - * automatic merging. - */ - getDefaultProps: function(Constructor, getDefaultProps) { - if (Constructor.getDefaultProps) { - Constructor.getDefaultProps = createMergedResultFunction( - Constructor.getDefaultProps, - getDefaultProps - ); - } else { - Constructor.getDefaultProps = getDefaultProps; - } - }, - propTypes: function(Constructor, propTypes) { - if (__DEV__) { - validateTypeDef( - Constructor, - propTypes, - 'prop' - ); - } - Constructor.propTypes = Object.assign( - {}, - Constructor.propTypes, - propTypes - ); - }, - statics: function(Constructor, statics) { - mixStaticSpecIntoComponent(Constructor, statics); - }, - autobind: function() {}, // noop -}; - -function validateTypeDef( - Constructor, - typeDef, - location: ReactPropTypeLocations, -) { - for (var propName in typeDef) { - if (typeDef.hasOwnProperty(propName)) { - // use a warning instead of an invariant so components - // don't show up in prod but only in __DEV__ - warning( - typeof typeDef[propName] === 'function', - '%s: %s type `%s` is invalid; it must be a function, usually from ' + - 'React.PropTypes.', - Constructor.displayName || 'ReactClass', - ReactPropTypeLocationNames[location], - propName - ); - } - } -} - -function validateMethodOverride(isAlreadyDefined, name) { - var specPolicy = ReactClassInterface.hasOwnProperty(name) ? - ReactClassInterface[name] : - null; - - // Disallow overriding of base class methods unless explicitly allowed. - if (ReactClassMixin.hasOwnProperty(name)) { - invariant( - specPolicy === 'OVERRIDE_BASE', - 'ReactClassInterface: You are attempting to override ' + - '`%s` from your class specification. Ensure that your method names ' + - 'do not overlap with React methods.', - name - ); - } - - // Disallow defining methods more than once unless explicitly allowed. - if (isAlreadyDefined) { - invariant( - specPolicy === 'DEFINE_MANY' || - specPolicy === 'DEFINE_MANY_MERGED', - 'ReactClassInterface: You are attempting to define ' + - '`%s` on your component more than once. This conflict may be due ' + - 'to a mixin.', - name - ); - } -} - -/** - * Mixin helper which handles policy validation and reserved - * specification keys when building React classes. - */ -function mixSpecIntoComponent(Constructor, spec) { - if (!spec) { - if (__DEV__) { - var typeofSpec = typeof spec; - var isMixinValid = typeofSpec === 'object' && spec !== null; - - warning( - isMixinValid, - '%s: You\'re attempting to include a mixin that is either null ' + - 'or not an object. Check the mixins included by the component, ' + - 'as well as any mixins they include themselves. ' + - 'Expected object but got %s.', - Constructor.displayName || 'ReactClass', - spec === null ? null : typeofSpec - ); - } - - return; - } - - invariant( - typeof spec !== 'function', - 'ReactClass: You\'re attempting to ' + - 'use a component class or function as a mixin. Instead, just use a ' + - 'regular object.' - ); - invariant( - !ReactElement.isValidElement(spec), - 'ReactClass: You\'re attempting to ' + - 'use a component as a mixin. Instead, just use a regular object.' - ); - - var proto = Constructor.prototype; - var autoBindPairs = proto.__reactAutoBindPairs; - - // By handling mixins before any other properties, we ensure the same - // chaining order is applied to methods with DEFINE_MANY policy, whether - // mixins are listed before or after these methods in the spec. - if (spec.hasOwnProperty(MIXINS_KEY)) { - RESERVED_SPEC_KEYS.mixins(Constructor, spec.mixins); - } - - for (var name in spec) { - if (!spec.hasOwnProperty(name)) { - continue; - } - - if (name === MIXINS_KEY) { - // We have already handled mixins in a special case above. - continue; - } - - var property = spec[name]; - var isAlreadyDefined = proto.hasOwnProperty(name); - validateMethodOverride(isAlreadyDefined, name); - - if (RESERVED_SPEC_KEYS.hasOwnProperty(name)) { - RESERVED_SPEC_KEYS[name](Constructor, property); - } else { - // Setup methods on prototype: - // The following member methods should not be automatically bound: - // 1. Expected ReactClass methods (in the "interface"). - // 2. Overridden methods (that were mixed in). - var isReactClassMethod = - ReactClassInterface.hasOwnProperty(name); - var isFunction = typeof property === 'function'; - var shouldAutoBind = - isFunction && - !isReactClassMethod && - !isAlreadyDefined && - spec.autobind !== false; - - if (shouldAutoBind) { - autoBindPairs.push(name, property); - proto[name] = property; - } else { - if (isAlreadyDefined) { - var specPolicy = ReactClassInterface[name]; - - // These cases should already be caught by validateMethodOverride. - invariant( - isReactClassMethod && ( - specPolicy === 'DEFINE_MANY_MERGED' || - specPolicy === 'DEFINE_MANY' - ), - 'ReactClass: Unexpected spec policy %s for key %s ' + - 'when mixing in component specs.', - specPolicy, - name - ); - - // For methods which are defined more than once, call the existing - // methods before calling the new property, merging if appropriate. - if (specPolicy === 'DEFINE_MANY_MERGED') { - proto[name] = createMergedResultFunction(proto[name], property); - } else if (specPolicy === 'DEFINE_MANY') { - proto[name] = createChainedFunction(proto[name], property); - } - } else { - proto[name] = property; - if (__DEV__) { - // Add verbose displayName to the function, which helps when looking - // at profiling tools. - if (typeof property === 'function' && spec.displayName) { - proto[name].displayName = spec.displayName + '_' + name; - } - } - } - } - } - } -} - -function mixStaticSpecIntoComponent(Constructor, statics) { - if (!statics) { - return; - } - for (var name in statics) { - var property = statics[name]; - if (!statics.hasOwnProperty(name)) { - continue; - } - - var isReserved = name in RESERVED_SPEC_KEYS; - invariant( - !isReserved, - 'ReactClass: You are attempting to define a reserved ' + - 'property, `%s`, that shouldn\'t be on the "statics" key. Define it ' + - 'as an instance property instead; it will still be accessible on the ' + - 'constructor.', - name - ); - - var isInherited = name in Constructor; - invariant( - !isInherited, - 'ReactClass: You are attempting to define ' + - '`%s` on your component more than once. This conflict may be ' + - 'due to a mixin.', - name - ); - Constructor[name] = property; - } -} - -/** - * Merge two objects, but throw if both contain the same key. - * - * @param {object} one The first object, which is mutated. - * @param {object} two The second object - * @return {object} one after it has been mutated to contain everything in two. - */ -function mergeIntoWithNoDuplicateKeys(one, two) { - invariant( - one && two && typeof one === 'object' && typeof two === 'object', - 'mergeIntoWithNoDuplicateKeys(): Cannot merge non-objects.' - ); - - for (var key in two) { - if (two.hasOwnProperty(key)) { - invariant( - one[key] === undefined, - 'mergeIntoWithNoDuplicateKeys(): ' + - 'Tried to merge two objects with the same key: `%s`. This conflict ' + - 'may be due to a mixin; in particular, this may be caused by two ' + - 'getInitialState() or getDefaultProps() methods returning objects ' + - 'with clashing keys.', - key - ); - one[key] = two[key]; - } - } - return one; -} - -/** - * Creates a function that invokes two functions and merges their return values. - * - * @param {function} one Function to invoke first. - * @param {function} two Function to invoke second. - * @return {function} Function that invokes the two argument functions. - * @private - */ -function createMergedResultFunction(one, two) { - return function mergedResult() { - var a = one.apply(this, arguments); - var b = two.apply(this, arguments); - if (a == null) { - return b; - } else if (b == null) { - return a; - } - var c = {}; - mergeIntoWithNoDuplicateKeys(c, a); - mergeIntoWithNoDuplicateKeys(c, b); - return c; - }; -} - -/** - * Creates a function that invokes two functions and ignores their return vales. - * - * @param {function} one Function to invoke first. - * @param {function} two Function to invoke second. - * @return {function} Function that invokes the two argument functions. - * @private - */ -function createChainedFunction(one, two) { - return function chainedFunction() { - one.apply(this, arguments); - two.apply(this, arguments); - }; -} - -/** - * Binds a method to the component. - * - * @param {object} component Component whose method is going to be bound. - * @param {function} method Method to be bound. - * @return {function} The bound method. - */ -function bindAutoBindMethod(component, method) { - var boundMethod = method.bind(component); - if (__DEV__) { - boundMethod.__reactBoundContext = component; - boundMethod.__reactBoundMethod = method; - boundMethod.__reactBoundArguments = null; - var componentName = component.constructor.displayName; - var _bind = boundMethod.bind; - boundMethod.bind = function(newThis, ...args) { - // User is trying to bind() an autobound method; we effectively will - // ignore the value of "this" that the user is trying to use, so - // let's warn. - if (newThis !== component && newThis !== null) { - warning( - false, - 'bind(): React component methods may only be bound to the ' + - 'component instance. See %s', - componentName - ); - } else if (!args.length) { - warning( - false, - 'bind(): You are binding a component method to the component. ' + - 'React does this for you automatically in a high-performance ' + - 'way, so you can safely remove this call. See %s', - componentName - ); - return boundMethod; - } - var reboundMethod = _bind.apply(boundMethod, arguments); - reboundMethod.__reactBoundContext = component; - reboundMethod.__reactBoundMethod = method; - reboundMethod.__reactBoundArguments = args; - return reboundMethod; - }; - } - return boundMethod; -} - -/** - * Binds all auto-bound methods in a component. - * - * @param {object} component Component whose method is going to be bound. - */ -function bindAutoBindMethods(component) { - var pairs = component.__reactAutoBindPairs; - for (var i = 0; i < pairs.length; i += 2) { - var autoBindKey = pairs[i]; - var method = pairs[i + 1]; - component[autoBindKey] = bindAutoBindMethod( - component, - method - ); - } -} - -/** - * Add more to the ReactClass base class. These are all legacy features and - * therefore not already part of the modern ReactComponent. - */ -var ReactClassMixin = { - - /** - * TODO: This will be deprecated because state should always keep a consistent - * type signature and the only use case for this, is to avoid that. - */ - replaceState: function(newState, callback) { - this.updater.enqueueReplaceState(this, newState); - if (callback) { - this.updater.enqueueCallback(this, callback, 'replaceState'); - } - }, - - /** - * Checks whether or not this composite component is mounted. - * @return {boolean} True if mounted, false otherwise. - * @protected - * @final - */ - isMounted: function() { - return this.updater.isMounted(this); - }, -}; - -var ReactClassComponent = function() {}; -Object.assign( - ReactClassComponent.prototype, - ReactComponent.prototype, - ReactClassMixin -); - -let didWarnDeprecated = false; - -/** - * Module for creating composite components. - * - * @class ReactClass - */ -var ReactClass = { - - /** - * Creates a composite component class given a class specification. - * See https://facebook.github.io/react/docs/top-level-api.html#react.createclass - * - * @param {object} spec Class specification (which must define `render`). - * @return {function} Component constructor function. - * @public - */ - createClass: function(spec) { - if (__DEV__) { - warning( - didWarnDeprecated, - '%s: React.createClass is deprecated and will be removed in version 16. ' + - 'Use plain JavaScript classes instead. If you\'re not yet ready to ' + - 'migrate, create-react-class is available on npm as a ' + - 'drop-in replacement.', - (spec && spec.displayName) || 'A Component', - ); - didWarnDeprecated = true; - } - - // To keep our warnings more understandable, we'll use a little hack here to - // ensure that Constructor.name !== 'Constructor'. This makes sure we don't - // unnecessarily identify a class without displayName as 'Constructor'. - var Constructor = identity(function(props, context, updater) { - // This constructor gets overridden by mocks. The argument is used - // by mocks to assert on what gets mounted. - - if (__DEV__) { - warning( - this instanceof Constructor, - 'Something is calling a React component directly. Use a factory or ' + - 'JSX instead. See: https://fb.me/react-legacyfactory' - ); - } - - // Wire up auto-binding - if (this.__reactAutoBindPairs.length) { - bindAutoBindMethods(this); - } - - this.props = props; - this.context = context; - this.refs = emptyObject; - this.updater = updater || ReactNoopUpdateQueue; - - this.state = null; - - // ReactClasses doesn't have constructors. Instead, they use the - // getInitialState and componentWillMount methods for initialization. - - var initialState = this.getInitialState ? this.getInitialState() : null; - if (__DEV__) { - // We allow auto-mocks to proceed as if they're returning null. - if (initialState === undefined && - this.getInitialState._isMockFunction) { - // This is probably bad practice. Consider warning here and - // deprecating this convenience. - initialState = null; - } - } - invariant( - typeof initialState === 'object' && !Array.isArray(initialState), - '%s.getInitialState(): must return an object or null', - Constructor.displayName || 'ReactCompositeComponent' - ); - - this.state = initialState; - }); - Constructor.prototype = new ReactClassComponent(); - Constructor.prototype.constructor = Constructor; - Constructor.prototype.__reactAutoBindPairs = []; - - injectedMixins.forEach( - mixSpecIntoComponent.bind(null, Constructor) - ); - - mixSpecIntoComponent(Constructor, spec); - - // Initialize the defaultProps property after all mixins have been merged. - if (Constructor.getDefaultProps) { - Constructor.defaultProps = Constructor.getDefaultProps(); - } - - if (__DEV__) { - // This is a tag to indicate that the use of these method names is ok, - // since it's used with createClass. If it's not, then it's likely a - // mistake so we'll warn you to use the static property, property - // initializer or constructor respectively. - if (Constructor.getDefaultProps) { - Constructor.getDefaultProps.isReactClassApproved = {}; - } - if (Constructor.prototype.getInitialState) { - Constructor.prototype.getInitialState.isReactClassApproved = {}; - } - } - - invariant( - Constructor.prototype.render, - 'createClass(...): Class specification must implement a `render` method.' - ); - - if (__DEV__) { - warning( - !Constructor.prototype.componentShouldUpdate, - '%s has a method called ' + - 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + - 'The name is phrased as a question because the function is ' + - 'expected to return a value.', - spec.displayName || 'A component' - ); - warning( - !Constructor.prototype.componentWillRecieveProps, - '%s has a method called ' + - 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?', - spec.displayName || 'A component' - ); - } - - // Reduce time spent doing lookups by setting these on the prototype. - for (var methodName in ReactClassInterface) { - if (!Constructor.prototype[methodName]) { - Constructor.prototype[methodName] = null; - } - } - - return Constructor; - }, - - injection: { - injectMixin: function(mixin) { - injectedMixins.push(mixin); - }, - }, - -}; - -module.exports = ReactClass; diff --git a/src/isomorphic/classic/class/__tests__/ReactCreateClass-test.js b/src/isomorphic/classic/class/__tests__/ReactCreateClass-test.js index 9c1a44ea5d..61f4fafa0d 100644 --- a/src/isomorphic/classic/class/__tests__/ReactCreateClass-test.js +++ b/src/isomorphic/classic/class/__tests__/ReactCreateClass-test.js @@ -11,14 +11,15 @@ 'use strict'; +var PropTypes; var React; var ReactDOM; var ReactTestUtils; var createReactClass; describe('ReactClass-spec', () => { - beforeEach(() => { + PropTypes = require('prop-types'); React = require('React'); ReactDOM = require('ReactDOM'); ReactTestUtils = require('ReactTestUtils'); @@ -34,7 +35,7 @@ describe('ReactClass-spec', () => { expect(function() { createReactClass({}); }).toThrowError( - 'createClass(...): Class specification must implement a `render` method.' + 'createClass(...): Class specification must implement a `render` method.', ); }); @@ -46,8 +47,7 @@ describe('ReactClass-spec', () => { }, }); - expect(TestComponent.displayName) - .toBe('TestComponent'); + expect(TestComponent.displayName).toBe('TestComponent'); }); it('should copy prop types onto the Constructor', () => { @@ -62,8 +62,7 @@ describe('ReactClass-spec', () => { }); expect(TestComponent.propTypes).toBeDefined(); - expect(TestComponent.propTypes.value) - .toBe(propValidator); + expect(TestComponent.propTypes.value).toBe(propValidator); }); it('should warn on invalid prop types', () => { @@ -80,7 +79,7 @@ describe('ReactClass-spec', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: Component: prop type `prop` is invalid; ' + - 'it must be a function, usually from React.PropTypes.' + 'it must be a function, usually from React.PropTypes.', ); }); @@ -98,7 +97,7 @@ describe('ReactClass-spec', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: Component: context type `prop` is invalid; ' + - 'it must be a function, usually from React.PropTypes.' + 'it must be a function, usually from React.PropTypes.', ); }); @@ -116,7 +115,7 @@ describe('ReactClass-spec', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: Component: child context type `prop` is invalid; ' + - 'it must be a function, usually from React.PropTypes.' + 'it must be a function, usually from React.PropTypes.', ); }); @@ -134,8 +133,8 @@ describe('ReactClass-spec', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: A component has a method called componentShouldUpdate(). Did you ' + - 'mean shouldComponentUpdate()? The name is phrased as a question ' + - 'because the function is expected to return a value.' + 'mean shouldComponentUpdate()? The name is phrased as a question ' + + 'because the function is expected to return a value.', ); createReactClass({ @@ -150,8 +149,8 @@ describe('ReactClass-spec', () => { expect(console.error.calls.count()).toBe(2); expect(console.error.calls.argsFor(1)[0]).toBe( 'Warning: NamedComponent has a method called componentShouldUpdate(). Did you ' + - 'mean shouldComponentUpdate()? The name is phrased as a question ' + - 'because the function is expected to return a value.' + 'mean shouldComponentUpdate()? The name is phrased as a question ' + + 'because the function is expected to return a value.', ); }); @@ -168,7 +167,7 @@ describe('ReactClass-spec', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: A component has a method called componentWillRecieveProps(). Did you ' + - 'mean componentWillReceiveProps()?' + 'mean componentWillReceiveProps()?', ); }); @@ -189,9 +188,9 @@ describe('ReactClass-spec', () => { }); }).toThrowError( 'ReactClass: You are attempting to define a reserved property, ' + - '`getDefaultProps`, that shouldn\'t be on the "statics" key. Define ' + - 'it as an instance property instead; it will still be accessible on ' + - 'the constructor.' + '`getDefaultProps`, that shouldn\'t be on the "statics" key. Define ' + + 'it as an instance property instead; it will still be accessible on ' + + 'the constructor.', ); }); @@ -202,13 +201,13 @@ describe('ReactClass-spec', () => { createReactClass({ mixins: [{}], propTypes: { - foo: React.PropTypes.string, + foo: PropTypes.string, }, contextTypes: { - foo: React.PropTypes.string, + foo: PropTypes.string, }, childContextTypes: { - foo: React.PropTypes.string, + foo: PropTypes.string, }, render: function() { return
    ; @@ -217,19 +216,19 @@ describe('ReactClass-spec', () => { expect(console.error.calls.count()).toBe(4); expect(console.error.calls.argsFor(0)[0]).toBe( 'createClass(...): `mixins` is now a static property and should ' + - 'be defined inside "statics".' + 'be defined inside "statics".', ); expect(console.error.calls.argsFor(1)[0]).toBe( 'createClass(...): `propTypes` is now a static property and should ' + - 'be defined inside "statics".' + 'be defined inside "statics".', ); expect(console.error.calls.argsFor(2)[0]).toBe( 'createClass(...): `contextTypes` is now a static property and ' + - 'should be defined inside "statics".' + 'should be defined inside "statics".', ); expect(console.error.calls.argsFor(3)[0]).toBe( 'createClass(...): `childContextTypes` is now a static property and ' + - 'should be defined inside "statics".' + 'should be defined inside "statics".', ); }); @@ -282,7 +281,7 @@ describe('ReactClass-spec', () => { it('renders based on context getInitialState', () => { var Foo = createReactClass({ contextTypes: { - className: React.PropTypes.string, + className: PropTypes.string, }, getInitialState() { return {className: this.context.className}; @@ -294,7 +293,7 @@ describe('ReactClass-spec', () => { var Outer = createReactClass({ childContextTypes: { - className: React.PropTypes.string, + className: PropTypes.string, }, getChildContext() { return {className: 'foo'}; @@ -323,7 +322,7 @@ describe('ReactClass-spec', () => { expect(function() { instance = ReactTestUtils.renderIntoDocument(instance); }).toThrowError( - 'Component.getInitialState(): must return an object or null' + 'Component.getInitialState(): must return an object or null', ); }); }); @@ -337,8 +336,8 @@ describe('ReactClass-spec', () => { return ; }, }); - expect( - () => ReactTestUtils.renderIntoDocument() + expect(() => + ReactTestUtils.renderIntoDocument(), ).not.toThrow(); }); @@ -354,7 +353,7 @@ describe('ReactClass-spec', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: Something is calling a React component directly. Use a ' + - 'factory or JSX instead. See: https://fb.me/react-legacyfactory' + 'factory or JSX instead. See: https://fb.me/react-legacyfactory', ); }); @@ -362,23 +361,19 @@ describe('ReactClass-spec', () => { var ops = []; var Component = createReactClass({ getInitialState() { - return { step: 0 }; + return {step: 0}; }, render() { ops.push('Render: ' + this.state.step); return
    ; - } + }, }); var instance = ReactTestUtils.renderIntoDocument(); - instance.replaceState({ step: 1 }, () => { + instance.replaceState({step: 1}, () => { ops.push('Callback: ' + instance.state.step); }); - expect(ops).toEqual([ - 'Render: 0', - 'Render: 1', - 'Callback: 1', - ]); + expect(ops).toEqual(['Render: 0', 'Render: 1', 'Callback: 1']); }); it('isMounted works', () => { @@ -414,7 +409,7 @@ describe('ReactClass-spec', () => { instance = this; this.log('render'); return
    ; - } + }, }); var container = document.createElement('div'); @@ -437,8 +432,8 @@ describe('ReactClass-spec', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toEqual( 'Warning: MyComponent: isMounted is deprecated. Instead, make sure to ' + - 'clean up subscriptions and pending requests in componentWillUnmount ' + - 'to prevent memory leaks.' + 'clean up subscriptions and pending requests in componentWillUnmount ' + + 'to prevent memory leaks.', ); }); }); diff --git a/src/isomorphic/classic/class/__tests__/create-react-class-integration-test.js b/src/isomorphic/classic/class/__tests__/create-react-class-integration-test.js new file mode 100644 index 0000000000..4c89c732ac --- /dev/null +++ b/src/isomorphic/classic/class/__tests__/create-react-class-integration-test.js @@ -0,0 +1,426 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @emails react-core + */ + +'use strict'; + +var React; +var ReactDOM; +var ReactTestUtils; +var createReactClass; + +describe('create-react-class-integration', () => { + beforeEach(() => { + React = require('React'); + ReactDOM = require('ReactDOM'); + ReactTestUtils = require('ReactTestUtils'); + var createReactClassFactory = require('create-react-class/factory'); + createReactClass = createReactClassFactory( + React.Component, + React.isValidElement, + require('ReactNoopUpdateQueue'), + ); + }); + + it('should throw when `render` is not specified', () => { + expect(function() { + createReactClass({}); + }).toThrowError( + 'createClass(...): Class specification must implement a `render` method.', + ); + }); + + it('should copy prop types onto the Constructor', () => { + var propValidator = jest.fn(); + var TestComponent = createReactClass({ + propTypes: { + value: propValidator, + }, + render: function() { + return
    ; + }, + }); + + expect(TestComponent.propTypes).toBeDefined(); + expect(TestComponent.propTypes.value).toBe(propValidator); + }); + + it('should warn on invalid prop types', () => { + spyOn(console, 'error'); + createReactClass({ + displayName: 'Component', + propTypes: { + prop: null, + }, + render: function() { + return {this.props.prop}; + }, + }); + expect(console.error.calls.count()).toBe(1); + expect(console.error.calls.argsFor(0)[0]).toBe( + 'Warning: Component: prop type `prop` is invalid; ' + + 'it must be a function, usually from React.PropTypes.', + ); + }); + + it('should warn on invalid context types', () => { + spyOn(console, 'error'); + createReactClass({ + displayName: 'Component', + contextTypes: { + prop: null, + }, + render: function() { + return {this.props.prop}; + }, + }); + expect(console.error.calls.count()).toBe(1); + expect(console.error.calls.argsFor(0)[0]).toBe( + 'Warning: Component: context type `prop` is invalid; ' + + 'it must be a function, usually from React.PropTypes.', + ); + }); + + it('should throw on invalid child context types', () => { + spyOn(console, 'error'); + createReactClass({ + displayName: 'Component', + childContextTypes: { + prop: null, + }, + render: function() { + return {this.props.prop}; + }, + }); + expect(console.error.calls.count()).toBe(1); + expect(console.error.calls.argsFor(0)[0]).toBe( + 'Warning: Component: child context type `prop` is invalid; ' + + 'it must be a function, usually from React.PropTypes.', + ); + }); + + it('should warn when mispelling shouldComponentUpdate', () => { + spyOn(console, 'error'); + + createReactClass({ + componentShouldUpdate: function() { + return false; + }, + render: function() { + return
    ; + }, + }); + expect(console.error.calls.count()).toBe(1); + expect(console.error.calls.argsFor(0)[0]).toBe( + 'Warning: A component has a method called componentShouldUpdate(). Did you ' + + 'mean shouldComponentUpdate()? The name is phrased as a question ' + + 'because the function is expected to return a value.', + ); + + createReactClass({ + displayName: 'NamedComponent', + componentShouldUpdate: function() { + return false; + }, + render: function() { + return
    ; + }, + }); + expect(console.error.calls.count()).toBe(2); + expect(console.error.calls.argsFor(1)[0]).toBe( + 'Warning: NamedComponent has a method called componentShouldUpdate(). Did you ' + + 'mean shouldComponentUpdate()? The name is phrased as a question ' + + 'because the function is expected to return a value.', + ); + }); + + it('should warn when mispelling componentWillReceiveProps', () => { + spyOn(console, 'error'); + createReactClass({ + componentWillRecieveProps: function() { + return false; + }, + render: function() { + return
    ; + }, + }); + expect(console.error.calls.count()).toBe(1); + expect(console.error.calls.argsFor(0)[0]).toBe( + 'Warning: A component has a method called componentWillRecieveProps(). Did you ' + + 'mean componentWillReceiveProps()?', + ); + }); + + it('should throw if a reserved property is in statics', () => { + expect(function() { + createReactClass({ + statics: { + getDefaultProps: function() { + return { + foo: 0, + }; + }, + }, + + render: function() { + return ; + }, + }); + }).toThrowError( + 'ReactClass: You are attempting to define a reserved property, ' + + '`getDefaultProps`, that shouldn\'t be on the "statics" key. Define ' + + 'it as an instance property instead; it will still be accessible on ' + + 'the constructor.', + ); + }); + + // TODO: Consider actually moving these to statics or drop this unit test. + + xit('should warn when using deprecated non-static spec keys', () => { + spyOn(console, 'error'); + createReactClass({ + mixins: [{}], + propTypes: { + foo: React.PropTypes.string, + }, + contextTypes: { + foo: React.PropTypes.string, + }, + childContextTypes: { + foo: React.PropTypes.string, + }, + render: function() { + return
    ; + }, + }); + expect(console.error.calls.count()).toBe(4); + expect(console.error.calls.argsFor(0)[0]).toBe( + 'createClass(...): `mixins` is now a static property and should ' + + 'be defined inside "statics".', + ); + expect(console.error.calls.argsFor(1)[0]).toBe( + 'createClass(...): `propTypes` is now a static property and should ' + + 'be defined inside "statics".', + ); + expect(console.error.calls.argsFor(2)[0]).toBe( + 'createClass(...): `contextTypes` is now a static property and ' + + 'should be defined inside "statics".', + ); + expect(console.error.calls.argsFor(3)[0]).toBe( + 'createClass(...): `childContextTypes` is now a static property and ' + + 'should be defined inside "statics".', + ); + }); + + it('should support statics', () => { + var Component = createReactClass({ + statics: { + abc: 'def', + def: 0, + ghi: null, + jkl: 'mno', + pqr: function() { + return this; + }, + }, + + render: function() { + return ; + }, + }); + var instance = ; + instance = ReactTestUtils.renderIntoDocument(instance); + expect(instance.constructor.abc).toBe('def'); + expect(Component.abc).toBe('def'); + expect(instance.constructor.def).toBe(0); + expect(Component.def).toBe(0); + expect(instance.constructor.ghi).toBe(null); + expect(Component.ghi).toBe(null); + expect(instance.constructor.jkl).toBe('mno'); + expect(Component.jkl).toBe('mno'); + expect(instance.constructor.pqr()).toBe(Component); + expect(Component.pqr()).toBe(Component); + }); + + it('should work with object getInitialState() return values', () => { + var Component = createReactClass({ + getInitialState: function() { + return { + occupation: 'clown', + }; + }, + render: function() { + return ; + }, + }); + var instance = ; + instance = ReactTestUtils.renderIntoDocument(instance); + expect(instance.state.occupation).toEqual('clown'); + }); + + it('renders based on context getInitialState', () => { + var Foo = createReactClass({ + contextTypes: { + className: React.PropTypes.string, + }, + getInitialState() { + return {className: this.context.className}; + }, + render() { + return ; + }, + }); + + var Outer = createReactClass({ + childContextTypes: { + className: React.PropTypes.string, + }, + getChildContext() { + return {className: 'foo'}; + }, + render() { + return ; + }, + }); + + var container = document.createElement('div'); + ReactDOM.render(, container); + expect(container.firstChild.className).toBe('foo'); + }); + + it('should throw with non-object getInitialState() return values', () => { + [['an array'], 'a string', 1234].forEach(function(state) { + var Component = createReactClass({ + getInitialState: function() { + return state; + }, + render: function() { + return ; + }, + }); + var instance = ; + expect(function() { + instance = ReactTestUtils.renderIntoDocument(instance); + }).toThrowError( + 'Component.getInitialState(): must return an object or null', + ); + }); + }); + + it('should work with a null getInitialState() return value', () => { + var Component = createReactClass({ + getInitialState: function() { + return null; + }, + render: function() { + return ; + }, + }); + expect(() => + ReactTestUtils.renderIntoDocument(), + ).not.toThrow(); + }); + + it('should throw when using legacy factories', () => { + spyOn(console, 'error'); + var Component = createReactClass({ + render() { + return
    ; + }, + }); + + expect(() => Component()).toThrow(); + expect(console.error.calls.count()).toBe(1); + expect(console.error.calls.argsFor(0)[0]).toBe( + 'Warning: Something is calling a React component directly. Use a ' + + 'factory or JSX instead. See: https://fb.me/react-legacyfactory', + ); + }); + + it('replaceState and callback works', () => { + var ops = []; + var Component = createReactClass({ + getInitialState() { + return {step: 0}; + }, + render() { + ops.push('Render: ' + this.state.step); + return
    ; + }, + }); + + var instance = ReactTestUtils.renderIntoDocument(); + instance.replaceState({step: 1}, () => { + ops.push('Callback: ' + instance.state.step); + }); + expect(ops).toEqual(['Render: 0', 'Render: 1', 'Callback: 1']); + }); + + it('isMounted works', () => { + spyOn(console, 'error'); + + var ops = []; + var instance; + var Component = createReactClass({ + displayName: 'MyComponent', + log(name) { + ops.push(`${name}: ${this.isMounted()}`); + }, + getInitialState() { + this.log('getInitialState'); + return {}; + }, + componentWillMount() { + this.log('componentWillMount'); + }, + componentDidMount() { + this.log('componentDidMount'); + }, + componentWillUpdate() { + this.log('componentWillUpdate'); + }, + componentDidUpdate() { + this.log('componentDidUpdate'); + }, + componentWillUnmount() { + this.log('componentWillUnmount'); + }, + render() { + instance = this; + this.log('render'); + return
    ; + }, + }); + + var container = document.createElement('div'); + ReactDOM.render(, container); + ReactDOM.render(, container); + ReactDOM.unmountComponentAtNode(container); + instance.log('after unmount'); + expect(ops).toEqual([ + 'getInitialState: false', + 'componentWillMount: false', + 'render: false', + 'componentDidMount: true', + 'componentWillUpdate: true', + 'render: true', + 'componentDidUpdate: true', + 'componentWillUnmount: false', + 'after unmount: false', + ]); + + expect(console.error.calls.count()).toBe(1); + expect(console.error.calls.argsFor(0)[0]).toEqual( + 'Warning: MyComponent: isMounted is deprecated. Instead, make sure to ' + + 'clean up subscriptions and pending requests in componentWillUnmount ' + + 'to prevent memory leaks.', + ); + }); +}); diff --git a/src/isomorphic/classic/class/createClass.js b/src/isomorphic/classic/class/createClass.js new file mode 100644 index 0000000000..3791e5392d --- /dev/null +++ b/src/isomorphic/classic/class/createClass.js @@ -0,0 +1,19 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule createClass + */ + +'use strict'; + +var {Component} = require('ReactBaseClasses'); +var {isValidElement} = require('ReactElement'); +var ReactNoopUpdateQueue = require('ReactNoopUpdateQueue'); +var factory = require('create-react-class/factory'); + +module.exports = factory(Component, isValidElement, ReactNoopUpdateQueue); diff --git a/src/isomorphic/classic/element/ReactCurrentOwner.js b/src/isomorphic/classic/element/ReactCurrentOwner.js index da5786e875..a095fe0097 100644 --- a/src/isomorphic/classic/element/ReactCurrentOwner.js +++ b/src/isomorphic/classic/element/ReactCurrentOwner.js @@ -12,7 +12,7 @@ 'use strict'; -import type { ReactInstance } from 'ReactInstanceType'; +import type {ReactInstance} from 'ReactInstanceType'; /** * Keeps track of the current owner. @@ -21,13 +21,11 @@ import type { ReactInstance } from 'ReactInstanceType'; * currently being constructed. */ var ReactCurrentOwner = { - /** * @internal * @type {ReactComponent} */ current: (null: null | ReactInstance), - }; module.exports = ReactCurrentOwner; diff --git a/src/isomorphic/classic/element/ReactElement.js b/src/isomorphic/classic/element/ReactElement.js index 90080c7ff8..15ff177641 100644 --- a/src/isomorphic/classic/element/ReactElement.js +++ b/src/isomorphic/classic/element/ReactElement.js @@ -59,10 +59,10 @@ function defineKeyPropWarningGetter(props, displayName) { warning( false, '%s: `key` is not a prop. Trying to access it will result ' + - 'in `undefined` being returned. If you need to access the same ' + - 'value within the child component, you should pass it as a different ' + - 'prop. (https://fb.me/react-special-props)', - displayName + 'in `undefined` being returned. If you need to access the same ' + + 'value within the child component, you should pass it as a different ' + + 'prop. (https://fb.me/react-special-props)', + displayName, ); } }; @@ -80,10 +80,10 @@ function defineRefPropWarningGetter(props, displayName) { warning( false, '%s: `ref` is not a prop. Trying to access it will result ' + - 'in `undefined` being returned. If you need to access the same ' + - 'value within the child component, you should pass it as a different ' + - 'prop. (https://fb.me/react-special-props)', - displayName + 'in `undefined` being returned. If you need to access the same ' + + 'value within the child component, you should pass it as a different ' + + 'prop. (https://fb.me/react-special-props)', + displayName, ); } }; @@ -203,8 +203,10 @@ ReactElement.createElement = function(type, config, children) { source = config.__source === undefined ? null : config.__source; // Remaining properties are added to a new props object for (propName in config) { - if (hasOwnProperty.call(config, propName) && - !RESERVED_PROPS.hasOwnProperty(propName)) { + if ( + hasOwnProperty.call(config, propName) && + !RESERVED_PROPS.hasOwnProperty(propName) + ) { props[propName] = config[propName]; } } @@ -239,11 +241,13 @@ ReactElement.createElement = function(type, config, children) { } if (__DEV__) { if (key || ref) { - if (typeof props.$$typeof === 'undefined' || - props.$$typeof !== REACT_ELEMENT_TYPE) { - var displayName = typeof type === 'function' ? - (type.displayName || type.name || 'Unknown') : - type; + if ( + typeof props.$$typeof === 'undefined' || + props.$$typeof !== REACT_ELEMENT_TYPE + ) { + var displayName = typeof type === 'function' + ? type.displayName || type.name || 'Unknown' + : type; if (key) { defineKeyPropWarningGetter(props, displayName); } @@ -260,7 +264,7 @@ ReactElement.createElement = function(type, config, children) { self, source, ReactCurrentOwner.current, - props + props, ); }; @@ -287,7 +291,7 @@ ReactElement.cloneAndReplaceKey = function(oldElement, newKey) { oldElement._self, oldElement._source, oldElement._owner, - oldElement.props + oldElement.props, ); return newElement; @@ -332,8 +336,10 @@ ReactElement.cloneElement = function(element, config, children) { defaultProps = element.type.defaultProps; } for (propName in config) { - if (hasOwnProperty.call(config, propName) && - !RESERVED_PROPS.hasOwnProperty(propName)) { + if ( + hasOwnProperty.call(config, propName) && + !RESERVED_PROPS.hasOwnProperty(propName) + ) { if (config[propName] === undefined && defaultProps !== undefined) { // Resolve default props props[propName] = defaultProps[propName]; @@ -357,15 +363,7 @@ ReactElement.cloneElement = function(element, config, children) { props.children = childArray; } - return ReactElement( - element.type, - key, - ref, - self, - source, - owner, - props - ); + return ReactElement(element.type, key, ref, self, source, owner, props); }; /** diff --git a/src/isomorphic/classic/element/ReactElementType.js b/src/isomorphic/classic/element/ReactElementType.js index 9702b0f77a..a0d8427b19 100644 --- a/src/isomorphic/classic/element/ReactElementType.js +++ b/src/isomorphic/classic/element/ReactElementType.js @@ -12,7 +12,7 @@ 'use strict'; -import type { ReactInstance } from 'ReactInstanceType'; +import type {ReactInstance} from 'ReactInstanceType'; export type Source = { fileName: string, diff --git a/src/isomorphic/classic/element/ReactElementValidator.js b/src/isomorphic/classic/element/ReactElementValidator.js index 6613aeeb61..17f0332f8d 100644 --- a/src/isomorphic/classic/element/ReactElementValidator.js +++ b/src/isomorphic/classic/element/ReactElementValidator.js @@ -27,6 +27,7 @@ var checkReactTypeSpec = require('checkReactTypeSpec'); var canDefineProperty = require('canDefineProperty'); var getIteratorFn = require('getIteratorFn'); var warning = require('warning'); +var lowPriorityWarning = require('lowPriorityWarning'); function getDeclarationErrorAddendum() { if (ReactCurrentOwner.current) { @@ -63,8 +64,9 @@ function getCurrentComponentErrorInfo(parentType) { var info = getDeclarationErrorAddendum(); if (!info) { - var parentName = typeof parentType === 'string' ? - parentType : parentType.displayName || parentType.name; + var parentName = typeof parentType === 'string' + ? parentType + : parentType.displayName || parentType.name; if (parentName) { info = ` Check the top-level render call using <${parentName}>.`; } @@ -89,9 +91,8 @@ function validateExplicitKey(element, parentType) { } element._store.validated = true; - var memoizer = ownerHasKeyUseWarning.uniqueKey || ( - ownerHasKeyUseWarning.uniqueKey = {} - ); + var memoizer = + ownerHasKeyUseWarning.uniqueKey || (ownerHasKeyUseWarning.uniqueKey = {}); var currentComponentErrorInfo = getCurrentComponentErrorInfo(parentType); if (memoizer[currentComponentErrorInfo]) { @@ -103,21 +104,22 @@ function validateExplicitKey(element, parentType) { // property, it may be the creator of the child that's responsible for // assigning it a key. var childOwner = ''; - if (element && - element._owner && - element._owner !== ReactCurrentOwner.current) { + if ( + element && + element._owner && + element._owner !== ReactCurrentOwner.current + ) { // Give the component that originally created this child. - childOwner = - ` It was passed a child from ${element._owner.getName()}.`; + childOwner = ` It was passed a child from ${element._owner.getName()}.`; } warning( false, 'Each child in an array or iterator should have a unique "key" prop.' + - '%s%s See https://fb.me/react-warning-keys for more information.%s', + '%s%s See https://fb.me/react-warning-keys for more information.%s', currentComponentErrorInfo, childOwner, - ReactComponentTreeHook.getCurrentStackAddendum(element) + ReactComponentTreeHook.getCurrentStackAddendum(element), ); } @@ -182,39 +184,35 @@ function validatePropTypes(element) { 'prop', name, element, - null + null, ); } if (typeof componentClass.getDefaultProps === 'function') { warning( componentClass.getDefaultProps.isReactClassApproved, 'getDefaultProps is only used on classic React.createClass ' + - 'definitions. Use a static property named `defaultProps` instead.' + 'definitions. Use a static property named `defaultProps` instead.', ); } } var ReactElementValidator = { - createElement: function(type, props, children) { var validType = typeof type === 'string' || typeof type === 'function'; // We warn in this case but don't throw. We expect the element creation to // succeed and there will likely be errors in render. if (!validType) { - if ( - typeof type !== 'function' && - typeof type !== 'string' - ) { + if (typeof type !== 'function' && typeof type !== 'string') { var info = ''; if ( type === undefined || - typeof type === 'object' && - type !== null && - Object.keys(type).length === 0 + (typeof type === 'object' && + type !== null && + Object.keys(type).length === 0) ) { info += ' You likely forgot to export your component from the file ' + - 'it\'s defined in.'; + "it's defined in."; } var sourceInfo = getSourceInfoErrorAddendum(props); @@ -226,14 +224,21 @@ var ReactElementValidator = { info += ReactComponentTreeHook.getCurrentStackAddendum(); + var currentSource = props !== null && + props !== undefined && + props.__source !== undefined + ? props.__source + : null; + ReactComponentTreeHook.pushNonStandardWarningStack(true, currentSource); warning( false, 'React.createElement: type is invalid -- expected a string (for ' + - 'built-in components) or a class/function (for composite ' + - 'components) but got: %s.%s', + 'built-in components) or a class/function (for composite ' + + 'components) but got: %s.%s', type == null ? type : typeof type, info, ); + ReactComponentTreeHook.popNonStandardWarningStack(); } } @@ -262,37 +267,29 @@ var ReactElementValidator = { }, createFactory: function(type) { - var validatedFactory = ReactElementValidator.createElement.bind( - null, - type - ); + var validatedFactory = ReactElementValidator.createElement.bind(null, type); // Legacy hook TODO: Warn if this is accessed validatedFactory.type = type; if (__DEV__) { if (canDefineProperty) { - Object.defineProperty( - validatedFactory, - 'type', - { - enumerable: false, - get: function() { - warning( - false, - 'Factory.type is deprecated. Access the class directly ' + - 'before passing it to createFactory.' - ); - Object.defineProperty(this, 'type', { - value: type, - }); - return type; - }, - } - ); + Object.defineProperty(validatedFactory, 'type', { + enumerable: false, + get: function() { + lowPriorityWarning( + false, + 'Factory.type is deprecated. Access the class directly ' + + 'before passing it to createFactory.', + ); + Object.defineProperty(this, 'type', { + value: type, + }); + return type; + }, + }); } } - return validatedFactory; }, @@ -304,7 +301,6 @@ var ReactElementValidator = { validatePropTypes(newElement); return newElement; }, - }; module.exports = ReactElementValidator; diff --git a/src/isomorphic/classic/element/__tests__/ReactElement-test.js b/src/isomorphic/classic/element/__tests__/ReactElement-test.js index cf82a95309..de83b4e912 100644 --- a/src/isomorphic/classic/element/__tests__/ReactElement-test.js +++ b/src/isomorphic/classic/element/__tests__/ReactElement-test.js @@ -81,9 +81,9 @@ describe('ReactElement', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( 'Child: `key` is not a prop. Trying to access it will result ' + - 'in `undefined` being returned. If you need to access the same ' + - 'value within the child component, you should pass it as a different ' + - 'prop. (https://fb.me/react-special-props)' + 'in `undefined` being returned. If you need to access the same ' + + 'value within the child component, you should pass it as a different ' + + 'prop. (https://fb.me/react-special-props)', ); }); @@ -95,9 +95,9 @@ describe('ReactElement', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( 'div: `key` is not a prop. Trying to access it will result ' + - 'in `undefined` being returned. If you need to access the same ' + - 'value within the child component, you should pass it as a different ' + - 'prop. (https://fb.me/react-special-props)' + 'in `undefined` being returned. If you need to access the same ' + + 'value within the child component, you should pass it as a different ' + + 'prop. (https://fb.me/react-special-props)', ); }); @@ -123,9 +123,9 @@ describe('ReactElement', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( 'Child: `ref` is not a prop. Trying to access it will result ' + - 'in `undefined` being returned. If you need to access the same ' + - 'value within the child component, you should pass it as a different ' + - 'prop. (https://fb.me/react-special-props)' + 'in `undefined` being returned. If you need to access the same ' + + 'value within the child component, you should pass it as a different ' + + 'prop. (https://fb.me/react-special-props)', ); }); @@ -141,7 +141,7 @@ describe('ReactElement', () => { it('returns an immutable element', () => { var element = React.createFactory(ComponentClass)(); - expect(() => element.type = 'div').toThrow(); + expect(() => (element.type = 'div')).toThrow(); }); it('does not reuse the original config object', () => { @@ -233,7 +233,7 @@ describe('ReactElement', () => { } var instance = ReactTestUtils.renderIntoDocument( - React.createElement(Wrapper) + React.createElement(Wrapper), ); expect(element._owner.getPublicInstance()).toBe(instance); @@ -242,9 +242,12 @@ describe('ReactElement', () => { it('merges an additional argument onto the children prop', () => { spyOn(console, 'error'); var a = 1; - var element = React.createFactory(ComponentClass)({ - children: 'text', - }, a); + var element = React.createFactory(ComponentClass)( + { + children: 'text', + }, + a, + ); expect(element.props.children).toBe(a); expect(console.error.calls.count()).toBe(0); }); @@ -260,9 +263,12 @@ describe('ReactElement', () => { it('overrides children if null is provided as an argument', () => { spyOn(console, 'error'); - var element = React.createFactory(ComponentClass)({ - children: 'text', - }, null); + var element = React.createFactory(ComponentClass)( + { + children: 'text', + }, + null, + ); expect(element.props.children).toBe(null); expect(console.error.calls.count()).toBe(0); }); @@ -303,18 +309,16 @@ describe('ReactElement', () => { } } - expect(React.isValidElement(React.createElement('div'))) - .toEqual(true); - expect(React.isValidElement(React.createElement(Component))) - .toEqual(true); + expect(React.isValidElement(React.createElement('div'))).toEqual(true); + expect(React.isValidElement(React.createElement(Component))).toEqual(true); expect(React.isValidElement(null)).toEqual(false); expect(React.isValidElement(true)).toEqual(false); expect(React.isValidElement({})).toEqual(false); expect(React.isValidElement('string')).toEqual(false); - expect(React.isValidElement(React.DOM.div)).toEqual(false); + expect(React.isValidElement(React.createFactory('div'))).toEqual(false); expect(React.isValidElement(Component)).toEqual(false); - expect(React.isValidElement({ type: 'div', props: {} })).toEqual(false); + expect(React.isValidElement({type: 'div', props: {}})).toEqual(false); var jsonElement = JSON.stringify(React.createElement('div')); expect(React.isValidElement(JSON.parse(jsonElement))).toBe(true); @@ -341,7 +345,7 @@ describe('ReactElement', () => { var container = document.createElement('div'); var instance = ReactDOM.render( React.createElement(Component, {fruit: 'mango'}), - container + container, ); expect(instance.props.fruit).toBe('mango'); @@ -360,12 +364,12 @@ describe('ReactElement', () => { Component.defaultProps = {prop: 'testKey'}; var instance = ReactTestUtils.renderIntoDocument( - React.createElement(Component) + React.createElement(Component), ); expect(instance.props.prop).toBe('testKey'); var inst2 = ReactTestUtils.renderIntoDocument( - React.createElement(Component, {prop: null}) + React.createElement(Component, {prop: null}), ); expect(inst2.props.prop).toBe(null); }); @@ -448,23 +452,20 @@ describe('ReactElement', () => { } } - expect(React.isValidElement(React.createElement('div'))) - .toEqual(true); - expect(React.isValidElement(React.createElement(Component))) - .toEqual(true); + expect(React.isValidElement(React.createElement('div'))).toEqual(true); + expect(React.isValidElement(React.createElement(Component))).toEqual(true); expect(React.isValidElement(null)).toEqual(false); expect(React.isValidElement(true)).toEqual(false); expect(React.isValidElement({})).toEqual(false); expect(React.isValidElement('string')).toEqual(false); - expect(React.isValidElement(React.DOM.div)).toEqual(false); + expect(React.isValidElement(React.createFactory('div'))).toEqual(false); expect(React.isValidElement(Component)).toEqual(false); - expect(React.isValidElement({ type: 'div', props: {} })).toEqual(false); + expect(React.isValidElement({type: 'div', props: {}})).toEqual(false); var jsonElement = JSON.stringify(React.createElement('div')); expect(React.isValidElement(JSON.parse(jsonElement))).toBe(false); }); - }); describe('comparing jsx vs .createFactory() vs .createElement()', () => { @@ -478,7 +479,6 @@ describe('comparing jsx vs .createFactory() vs .createElement()', () => { Child = jest.genMockFromModule('ReactElementTestChild'); }); - describe('when using jsx only', () => { var Parent, instance; beforeEach(() => { @@ -491,11 +491,14 @@ describe('comparing jsx vs .createFactory() vs .createElement()', () => { ); } }; - instance = ReactTestUtils.renderIntoDocument(); + instance = ReactTestUtils.renderIntoDocument(); }); it('should scry children but cannot', () => { - var children = ReactTestUtils.scryRenderedComponentsWithType(instance, Child); + var children = ReactTestUtils.scryRenderedComponentsWithType( + instance, + Child, + ); expect(children.length).toBe(1); }); @@ -504,7 +507,10 @@ describe('comparing jsx vs .createFactory() vs .createElement()', () => { }); it('can capture Child instantiation calls', () => { - expect(Child.mock.calls[0][0]).toEqual({ foo: 'foo value', children: 'children value' }); + expect(Child.mock.calls[0][0]).toEqual({ + foo: 'foo value', + children: 'children value', + }); }); }); @@ -514,7 +520,11 @@ describe('comparing jsx vs .createFactory() vs .createElement()', () => { var childFactory = React.createFactory(Child); class Parent extends React.Component { render() { - return React.DOM.div({}, childFactory({ ref: 'child', foo: 'foo value' }, 'children value')); + return React.createElement( + 'div', + {}, + childFactory({ref: 'child', foo: 'foo value'}, 'children value'), + ); } } factory = React.createFactory(Parent); @@ -522,7 +532,10 @@ describe('comparing jsx vs .createFactory() vs .createElement()', () => { }); it('can properly scry children', () => { - var children = ReactTestUtils.scryRenderedComponentsWithType(instance, Child); + var children = ReactTestUtils.scryRenderedComponentsWithType( + instance, + Child, + ); expect(children.length).toBe(1); }); @@ -531,7 +544,10 @@ describe('comparing jsx vs .createFactory() vs .createElement()', () => { }); it('can capture Child instantiation calls', () => { - expect(Child.mock.calls[0][0]).toEqual({ foo: 'foo value', children: 'children value' }); + expect(Child.mock.calls[0][0]).toEqual({ + foo: 'foo value', + children: 'children value', + }); }); }); @@ -540,7 +556,15 @@ describe('comparing jsx vs .createFactory() vs .createElement()', () => { beforeEach(() => { class Parent extends React.Component { render() { - return React.DOM.div({}, React.createElement(Child, { ref: 'child', foo: 'foo value' }, 'children value')); + return React.createElement( + 'div', + {}, + React.createElement( + Child, + {ref: 'child', foo: 'foo value'}, + 'children value', + ), + ); } } factory = React.createFactory(Parent); @@ -548,7 +572,10 @@ describe('comparing jsx vs .createFactory() vs .createElement()', () => { }); it('should scry children but cannot', () => { - var children = ReactTestUtils.scryRenderedComponentsWithType(instance, Child); + var children = ReactTestUtils.scryRenderedComponentsWithType( + instance, + Child, + ); expect(children.length).toBe(1); }); @@ -557,8 +584,10 @@ describe('comparing jsx vs .createFactory() vs .createElement()', () => { }); it('can capture Child instantiation calls', () => { - expect(Child.mock.calls[0][0]).toEqual({ foo: 'foo value', children: 'children value' }); + expect(Child.mock.calls[0][0]).toEqual({ + foo: 'foo value', + children: 'children value', + }); }); }); - }); diff --git a/src/isomorphic/classic/element/__tests__/ReactElementClone-test.js b/src/isomorphic/classic/element/__tests__/ReactElementClone-test.js index 69e8984ca2..a29bdff981 100644 --- a/src/isomorphic/classic/element/__tests__/ReactElementClone-test.js +++ b/src/isomorphic/classic/element/__tests__/ReactElementClone-test.js @@ -11,6 +11,7 @@ 'use strict'; +var PropTypes; var React; var ReactDOM; var ReactTestUtils; @@ -22,6 +23,7 @@ describe('ReactElementClone', () => { React = require('React'); ReactDOM = require('ReactDOM'); ReactTestUtils = require('ReactTestUtils'); + PropTypes = require('prop-types'); // NOTE: We're explicitly not using JSX here. This is intended to test // classic JS without JSX. @@ -42,7 +44,7 @@ describe('ReactElementClone', () => { render() { return (
    - {React.cloneElement(this.props.child, { className: 'xyz' })} + {React.cloneElement(this.props.child, {className: 'xyz'})}
    ); } @@ -66,7 +68,7 @@ describe('ReactElementClone', () => { render() { return (
    - {React.cloneElement(this.props.child, { className: 'xyz' })} + {React.cloneElement(this.props.child, {className: 'xyz'})}
    ); } @@ -91,7 +93,7 @@ describe('ReactElementClone', () => { render() { return (
    - {React.cloneElement(this.props.child, { className: 'xyz' })} + {React.cloneElement(this.props.child, {className: 'xyz'})}
    ); } @@ -120,7 +122,7 @@ describe('ReactElementClone', () => { } ReactTestUtils.renderIntoDocument( - React.cloneElement(, {children: 'xyz'}) + React.cloneElement(, {children: 'xyz'}), ); }); @@ -133,7 +135,7 @@ describe('ReactElementClone', () => { } ReactTestUtils.renderIntoDocument( - React.cloneElement(xyz, {}) + React.cloneElement(xyz, {}), ); }); @@ -146,34 +148,41 @@ describe('ReactElementClone', () => { var clone = React.cloneElement( xyz, - { children: }, -
    , - - ); - - expect(clone.props.children).toEqual([ + {children: },
    , , - ]); + ); + + expect(clone.props.children).toEqual([
    , ]); }); it('should override children if undefined is provided as an argument', () => { - var element = React.createElement(ComponentClass, { - children: 'text', - }, undefined); + var element = React.createElement( + ComponentClass, + { + children: 'text', + }, + undefined, + ); expect(element.props.children).toBe(undefined); - var element2 = React.cloneElement(React.createElement(ComponentClass, { - children: 'text', - }), {}, undefined); + var element2 = React.cloneElement( + React.createElement(ComponentClass, { + children: 'text', + }), + {}, + undefined, + ); expect(element2.props.children).toBe(undefined); }); it('should support keys and refs', () => { class Parent extends React.Component { render() { - var clone = - React.cloneElement(this.props.children, {key: 'xyz', ref: 'xyz'}); + var clone = React.cloneElement(this.props.children, { + key: 'xyz', + ref: 'xyz', + }); expect(clone.key).toBe('xyz'); expect(clone.ref).toBe('xyz'); return
    {clone}
    ; @@ -218,7 +227,7 @@ describe('ReactElementClone', () => { } ReactTestUtils.renderIntoDocument( - React.cloneElement(, {myprop: 'xyz'}) + React.cloneElement(, {myprop: 'xyz'}), ); }); @@ -250,7 +259,7 @@ describe('ReactElementClone', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( - 'Each child in an array or iterator should have a unique "key" prop.' + 'Each child in an array or iterator should have a unique "key" prop.', ); }); @@ -286,7 +295,7 @@ describe('ReactElementClone', () => { } } Component.propTypes = { - color: React.PropTypes.string.isRequired, + color: PropTypes.string.isRequired, }; class Parent extends React.Component { render() { @@ -295,21 +304,20 @@ describe('ReactElementClone', () => { } class GrandParent extends React.Component { render() { - return React.createElement( - Parent, - { child: React.createElement(Component, {color: 'red'}) } - ); + return React.createElement(Parent, { + child: React.createElement(Component, {color: 'red'}), + }); } } ReactTestUtils.renderIntoDocument(React.createElement(GrandParent)); expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: Failed prop type: ' + - 'Invalid prop `color` of type `number` supplied to `Component`, ' + - 'expected `string`.\n' + - ' in Component (created by GrandParent)\n' + - ' in Parent (created by GrandParent)\n' + - ' in GrandParent' + 'Invalid prop `color` of type `number` supplied to `Component`, ' + + 'expected `string`.\n' + + ' in Component (created by GrandParent)\n' + + ' in Parent (created by GrandParent)\n' + + ' in GrandParent', ); }); @@ -359,5 +367,4 @@ describe('ReactElementClone', () => { expect(Object.isFrozen(element.props)).toBe(true); expect(clone.props).toEqual({foo: 'ef'}); }); - }); diff --git a/src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js b/src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js index f2e403c5a4..a772395aa6 100644 --- a/src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js +++ b/src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js @@ -14,6 +14,7 @@ // NOTE: We're explicitly not using JSX in this file. This is intended to test // classic JS without JSX. +var PropTypes; var React; var ReactDOM; var ReactTestUtils; @@ -31,6 +32,7 @@ describe('ReactElementValidator', () => { React = require('React'); ReactDOM = require('ReactDOM'); ReactTestUtils = require('ReactTestUtils'); + PropTypes = require('prop-types'); ComponentClass = class extends React.Component { render() { return React.createElement('div'); @@ -46,7 +48,7 @@ describe('ReactElementValidator', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( - 'Each child in an array or iterator should have a unique "key" prop.' + 'Each child in an array or iterator should have a unique "key" prop.', ); }); @@ -64,19 +66,17 @@ describe('ReactElementValidator', () => { class ComponentWrapper extends React.Component { render() { - return InnerComponent({childSet: [Component(), Component()] }); + return InnerComponent({childSet: [Component(), Component()]}); } } - ReactTestUtils.renderIntoDocument( - React.createElement(ComponentWrapper) - ); + ReactTestUtils.renderIntoDocument(React.createElement(ComponentWrapper)); expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( 'Each child in an array or iterator should have a unique "key" prop. ' + - 'Check the render method of `InnerClass`. ' + - 'It was passed a child from ComponentWrapper. ' + 'Check the render method of `InnerClass`. ' + + 'It was passed a child from ComponentWrapper. ', ); }); @@ -86,37 +86,31 @@ describe('ReactElementValidator', () => { function Anonymous() { return
    ; } - Object.defineProperty(Anonymous, 'name', { value: undefined }); + Object.defineProperty(Anonymous, 'name', {value: undefined}); - var divs = [ -
    , -
    , - ]; + var divs = [
    ,
    ]; ReactTestUtils.renderIntoDocument({divs}); expect(console.error.calls.count()).toBe(1); expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( 'Warning: Each child in an array or iterator should have a unique ' + - '"key" prop. See https://fb.me/react-warning-keys for more information.\n' + - ' in div (at **)' + '"key" prop. See https://fb.me/react-warning-keys for more information.\n' + + ' in div (at **)', ); }); it('warns for keys for arrays of elements with no owner info', () => { spyOn(console, 'error'); - var divs = [ -
    , -
    , - ]; + var divs = [
    ,
    ]; ReactTestUtils.renderIntoDocument(
    {divs}
    ); expect(console.error.calls.count()).toBe(1); expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( 'Warning: Each child in an array or iterator should have a unique ' + - '"key" prop. Check the top-level render call using
    . See ' + - 'https://fb.me/react-warning-keys for more information.\n' + - ' in div (at **)' + '"key" prop. Check the top-level render call using
    . See ' + + 'https://fb.me/react-warning-keys for more information.\n' + + ' in div (at **)', ); }); @@ -140,12 +134,12 @@ describe('ReactElementValidator', () => { expect(console.error.calls.count()).toBe(1); expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( 'Warning: Each child in an array or iterator should have a unique ' + - '"key" prop. Check the render method of `Component`. See ' + - 'https://fb.me/react-warning-keys for more information.\n' + - ' in div (at **)\n' + - ' in Component (at **)\n' + - ' in Parent (at **)\n' + - ' in GrandParent (at **)' + '"key" prop. Check the render method of `Component`. See ' + + 'https://fb.me/react-warning-keys for more information.\n' + + ' in div (at **)\n' + + ' in Component (at **)\n' + + ' in Parent (at **)\n' + + ' in GrandParent (at **)', ); }); @@ -165,7 +159,7 @@ describe('ReactElementValidator', () => { - + , ); expect(console.error.calls.count()).toBe(0); @@ -191,7 +185,7 @@ describe('ReactElementValidator', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( - 'Each child in an array or iterator should have a unique "key" prop.' + 'Each child in an array or iterator should have a unique "key" prop.', ); }); @@ -258,7 +252,7 @@ describe('ReactElementValidator', () => { return React.createElement('div', null, 'My color is ' + props.color); } MyComp.propTypes = { - color: React.PropTypes.string, + color: PropTypes.string, }; function ParentComp() { return React.createElement(MyComp, {color: 123}); @@ -266,10 +260,10 @@ describe('ReactElementValidator', () => { ReactTestUtils.renderIntoDocument(React.createElement(ParentComp)); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: Failed prop type: ' + - 'Invalid prop `color` of type `number` supplied to `MyComp`, ' + - 'expected `string`.\n' + - ' in MyComp (created by ParentComp)\n' + - ' in ParentComp' + 'Invalid prop `color` of type `number` supplied to `MyComp`, ' + + 'expected `string`.\n' + + ' in MyComp (created by ParentComp)\n' + + ' in ParentComp', ); }); @@ -284,35 +278,35 @@ describe('ReactElementValidator', () => { expect(console.error.calls.count()).toBe(6); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: React.createElement: type is invalid -- expected a string ' + - '(for built-in components) or a class/function (for composite ' + - 'components) but got: undefined. You likely forgot to export your ' + - 'component from the file it\'s defined in.' + '(for built-in components) or a class/function (for composite ' + + 'components) but got: undefined. You likely forgot to export your ' + + "component from the file it's defined in.", ); expect(console.error.calls.argsFor(1)[0]).toBe( 'Warning: React.createElement: type is invalid -- expected a string ' + - '(for built-in components) or a class/function (for composite ' + - 'components) but got: null.' + '(for built-in components) or a class/function (for composite ' + + 'components) but got: null.', ); expect(console.error.calls.argsFor(2)[0]).toBe( 'Warning: React.createElement: type is invalid -- expected a string ' + - '(for built-in components) or a class/function (for composite ' + - 'components) but got: boolean.' + '(for built-in components) or a class/function (for composite ' + + 'components) but got: boolean.', ); expect(console.error.calls.argsFor(3)[0]).toBe( 'Warning: React.createElement: type is invalid -- expected a string ' + - '(for built-in components) or a class/function (for composite ' + - 'components) but got: number.' + '(for built-in components) or a class/function (for composite ' + + 'components) but got: number.', ); expect(console.error.calls.argsFor(4)[0]).toBe( 'Warning: React.createElement: type is invalid -- expected a string ' + - '(for built-in components) or a class/function (for composite ' + - 'components) but got: object.' + '(for built-in components) or a class/function (for composite ' + + 'components) but got: object.', ); expect(console.error.calls.argsFor(5)[0]).toBe( 'Warning: React.createElement: type is invalid -- expected a string ' + - '(for built-in components) or a class/function (for composite ' + - 'components) but got: object. You likely forgot to export your ' + - 'component from the file it\'s defined in.' + '(for built-in components) or a class/function (for composite ' + + 'components) but got: object. You likely forgot to export your ' + + "component from the file it's defined in.", ); React.createElement('div'); expect(console.error.calls.count()).toBe(6); @@ -327,15 +321,15 @@ describe('ReactElementValidator', () => { ReactTestUtils.renderIntoDocument(React.createElement(ParentComp)); }).toThrowError( 'Element type is invalid: expected a string (for built-in components) ' + - 'or a class/function (for composite components) but got: null. Check ' + - 'the render method of `ParentComp`.' + 'or a class/function (for composite components) but got: null. Check ' + + 'the render method of `ParentComp`.', ); expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: React.createElement: type is invalid -- expected a string ' + - '(for built-in components) or a class/function (for composite ' + - 'components) but got: null. Check the render method of `ParentComp`.' + - '\n in ParentComp' + '(for built-in components) or a class/function (for composite ' + + 'components) but got: null. Check the render method of `ParentComp`.' + + '\n in ParentComp', ); }); @@ -347,7 +341,7 @@ describe('ReactElementValidator', () => { return React.createElement('span', null, this.props.prop); } } - Component.propTypes = {prop: React.PropTypes.string.isRequired}; + Component.propTypes = {prop: PropTypes.string.isRequired}; Component.defaultProps = {prop: null}; ReactTestUtils.renderIntoDocument(React.createElement(Component)); @@ -355,8 +349,8 @@ describe('ReactElementValidator', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: Failed prop type: The prop `prop` is marked as required in ' + - '`Component`, but its value is `null`.\n' + - ' in Component' + '`Component`, but its value is `null`.\n' + + ' in Component', ); }); @@ -368,18 +362,18 @@ describe('ReactElementValidator', () => { return React.createElement('span', null, this.props.prop); } } - Component.propTypes = {prop: React.PropTypes.string.isRequired}; + Component.propTypes = {prop: PropTypes.string.isRequired}; Component.defaultProps = {prop: 'text'}; ReactTestUtils.renderIntoDocument( - React.createElement(Component, {prop:null}) + React.createElement(Component, {prop: null}), ); expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: Failed prop type: The prop `prop` is marked as required in ' + - '`Component`, but its value is `null`.\n' + - ' in Component' + '`Component`, but its value is `null`.\n' + + ' in Component', ); }); @@ -392,33 +386,31 @@ describe('ReactElementValidator', () => { } } Component.propTypes = { - prop: React.PropTypes.string.isRequired, + prop: PropTypes.string.isRequired, }; + ReactTestUtils.renderIntoDocument(React.createElement(Component)); ReactTestUtils.renderIntoDocument( - React.createElement(Component) - ); - ReactTestUtils.renderIntoDocument( - React.createElement(Component, {prop: 42}) + React.createElement(Component, {prop: 42}), ); expect(console.error.calls.count()).toBe(2); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: Failed prop type: ' + - 'The prop `prop` is marked as required in `Component`, but its value ' + - 'is `undefined`.\n' + - ' in Component' + 'The prop `prop` is marked as required in `Component`, but its value ' + + 'is `undefined`.\n' + + ' in Component', ); expect(console.error.calls.argsFor(1)[0]).toBe( 'Warning: Failed prop type: ' + - 'Invalid prop `prop` of type `number` supplied to ' + - '`Component`, expected `string`.\n' + - ' in Component' + 'Invalid prop `prop` of type `number` supplied to ' + + '`Component`, expected `string`.\n' + + ' in Component', ); ReactTestUtils.renderIntoDocument( - React.createElement(Component, {prop: 'string'}) + React.createElement(Component, {prop: 'string'}), ); // Should not error for strings @@ -431,42 +423,41 @@ describe('ReactElementValidator', () => { class Component extends React.Component { render() { return React.createElement('span', null, this.props.myProp.value); - } } Component.propTypes = { - myProp: React.PropTypes.shape, + myProp: PropTypes.shape, }; ReactTestUtils.renderIntoDocument( - React.createElement(Component, {myProp: {value: 'hi'}}) + React.createElement(Component, {myProp: {value: 'hi'}}), ); expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: Component: type specification of prop `myProp` is invalid; ' + - 'the type checker function must return `null` or an `Error` but ' + - 'returned a function. You may have forgotten to pass an argument to ' + - 'the type checker creator (arrayOf, instanceOf, objectOf, oneOf, ' + - 'oneOfType, and shape all require an argument).' + 'the type checker function must return `null` or an `Error` but ' + + 'returned a function. You may have forgotten to pass an argument to ' + + 'the type checker creator (arrayOf, instanceOf, objectOf, oneOf, ' + + 'oneOfType, and shape all require an argument).', ); }); it('should warn when accessing .type on an element factory', () => { - spyOn(console, 'error'); + spyOn(console, 'warn'); function TestComponent() { return
    ; } var TestFactory = React.createFactory(TestComponent); expect(TestFactory.type).toBe(TestComponent); - expect(console.error.calls.count()).toBe(1); - expect(console.error.calls.argsFor(0)[0]).toBe( + expect(console.warn.calls.count()).toBe(1); + expect(console.warn.calls.argsFor(0)[0]).toBe( 'Warning: Factory.type is deprecated. Access the class directly before ' + - 'passing it to createFactory.' + 'passing it to createFactory.', ); // Warn once, not again expect(TestFactory.type).toBe(TestComponent); - expect(console.error.calls.count()).toBe(1); + expect(console.warn.calls.count()).toBe(1); }); it('does not warn when using DOM node as children', () => { @@ -511,7 +502,7 @@ describe('ReactElementValidator', () => { // shouldn't blow up either. var child = { - $$typeof: (
    ).$$typeof, + $$typeof:
    .$$typeof, type: 'span', key: null, ref: null, @@ -529,10 +520,60 @@ describe('ReactElementValidator', () => { expect(console.error.calls.count()).toBe(1); expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( 'Warning: React.createElement: type is invalid -- expected a string ' + - '(for built-in components) or a class/function (for composite ' + - 'components) but got: undefined. You likely forgot to export your ' + - 'component from the file it\'s defined in. Check your code at **.' + '(for built-in components) or a class/function (for composite ' + + 'components) but got: undefined. You likely forgot to export your ' + + "component from the file it's defined in. Check your code at **.", ); }); + it('provides stack via non-standard console.reactStack for invalid types', () => { + spyOn(console, 'error'); + + function Foo() { + var Bad = undefined; + return React.createElement(Bad); + } + + function App() { + return React.createElement('div', null, React.createElement(Foo)); + } + + try { + console.reactStack = jest.fn(); + console.reactStackEnd = jest.fn(); + + expect(() => { + ReactTestUtils.renderIntoDocument(React.createElement(App)); + }).toThrow( + 'Element type is invalid: expected a string (for built-in components) ' + + 'or a class/function (for composite components) but got: undefined. ' + + "You likely forgot to export your component from the file it's " + + 'defined in. Check the render method of `Foo`.', + ); + + expect(console.reactStack.mock.calls.length).toBe(1); + expect(console.reactStackEnd.mock.calls.length).toBe(1); + + var stack = console.reactStack.mock.calls[0][0]; + expect(Array.isArray(stack)).toBe(true); + expect(stack.map(frame => frame.name)).toEqual([ + 'Foo', // is inside Foo + 'App', // is inside App + 'App', //
    is inside App + null, // is outside a component + ]); + expect( + stack.map(frame => frame.fileName && frame.fileName.slice(-8)), + ).toEqual([null, null, null, null]); + expect(stack.map(frame => frame.lineNumber)).toEqual([ + null, + null, + null, + null, + ]); + } finally { + delete console.reactStack; + delete console.reactStackEnd; + } + }); }); diff --git a/src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js b/src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js index 26cb0fa205..ab9f56bbc8 100644 --- a/src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js +++ b/src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js @@ -28,16 +28,18 @@ function typeCheckFail(declaration, value, message) { 'testComponent', 'prop', null, - ReactPropTypesSecret + ReactPropTypesSecret, ); expect(error instanceof Error).toBe(true); expect(error.message).toBe(message); } function typeCheckFailRequiredValues(declaration) { - var specifiedButIsNullMsg = 'The prop `testProp` is marked as required in ' + + var specifiedButIsNullMsg = + 'The prop `testProp` is marked as required in ' + '`testComponent`, but its value is `null`.'; - var unspecifiedMsg = 'The prop `testProp` is marked as required in ' + + var unspecifiedMsg = + 'The prop `testProp` is marked as required in ' + '`testComponent`, but its value is \`undefined\`.'; var props1 = {testProp: null}; var error1 = declaration( @@ -46,7 +48,7 @@ function typeCheckFailRequiredValues(declaration) { 'testComponent', 'prop', null, - ReactPropTypesSecret + ReactPropTypesSecret, ); expect(error1 instanceof Error).toBe(true); expect(error1.message).toBe(specifiedButIsNullMsg); @@ -57,7 +59,7 @@ function typeCheckFailRequiredValues(declaration) { 'testComponent', 'prop', null, - ReactPropTypesSecret + ReactPropTypesSecret, ); expect(error2 instanceof Error).toBe(true); expect(error2.message).toBe(unspecifiedMsg); @@ -68,7 +70,7 @@ function typeCheckFailRequiredValues(declaration) { 'testComponent', 'prop', null, - ReactPropTypesSecret + ReactPropTypesSecret, ); expect(error3 instanceof Error).toBe(true); expect(error3.message).toBe(unspecifiedMsg); @@ -82,7 +84,7 @@ function typeCheckPass(declaration, value) { 'testComponent', 'prop', null, - ReactPropTypesSecret + ReactPropTypesSecret, ); expect(error).toBe(null); } @@ -92,16 +94,11 @@ function expectWarningInDevelopment(declaration, value) { var propName = 'testProp' + Math.random().toString(); var componentName = 'testComponent' + Math.random().toString(); for (var i = 0; i < 3; i++) { - declaration( - props, - propName, - componentName, - 'prop' - ); + declaration(props, propName, componentName, 'prop'); } expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( - 'You are manually calling a React.PropTypes validation ' + 'You are manually calling a React.PropTypes validation ', ); console.error.calls.reset(); } @@ -121,31 +118,31 @@ describe('ReactPropTypes', () => { PropTypes.string, [], 'Invalid prop `testProp` of type `array` supplied to ' + - '`testComponent`, expected `string`.' + '`testComponent`, expected `string`.', ); typeCheckFail( PropTypes.string, false, 'Invalid prop `testProp` of type `boolean` supplied to ' + - '`testComponent`, expected `string`.' + '`testComponent`, expected `string`.', ); typeCheckFail( PropTypes.string, 0, 'Invalid prop `testProp` of type `number` supplied to ' + - '`testComponent`, expected `string`.' + '`testComponent`, expected `string`.', ); typeCheckFail( PropTypes.string, {}, 'Invalid prop `testProp` of type `object` supplied to ' + - '`testComponent`, expected `string`.' + '`testComponent`, expected `string`.', ); typeCheckFail( PropTypes.string, Symbol(), 'Invalid prop `testProp` of type `symbol` supplied to ' + - '`testComponent`, expected `string`.' + '`testComponent`, expected `string`.', ); }); @@ -154,13 +151,13 @@ describe('ReactPropTypes', () => { PropTypes.string, new Date(), 'Invalid prop `testProp` of type `date` supplied to ' + - '`testComponent`, expected `string`.' + '`testComponent`, expected `string`.', ); typeCheckFail( PropTypes.string, /please/, 'Invalid prop `testProp` of type `regexp` supplied to ' + - '`testComponent`, expected `string`.' + '`testComponent`, expected `string`.', ); }); @@ -260,9 +257,9 @@ describe('ReactPropTypes', () => { describe('ArrayOf Type', () => { it('should fail for invalid argument', () => { typeCheckFail( - PropTypes.arrayOf({ foo: PropTypes.string }), - { foo: 'bar' }, - 'Property `testProp` of component `testComponent` has invalid PropType notation inside arrayOf.' + PropTypes.arrayOf({foo: PropTypes.string}), + {foo: 'bar'}, + 'Property `testProp` of component `testComponent` has invalid PropType notation inside arrayOf.', ); }); @@ -276,14 +273,14 @@ describe('ReactPropTypes', () => { it('should support arrayOf with complex types', () => { typeCheckPass( PropTypes.arrayOf(PropTypes.shape({a: PropTypes.number.isRequired})), - [{a: 1}, {a: 2}] + [{a: 1}, {a: 2}], ); function Thing() {} - typeCheckPass( - PropTypes.arrayOf(PropTypes.instanceOf(Thing)), - [new Thing(), new Thing()] - ); + typeCheckPass(PropTypes.arrayOf(PropTypes.instanceOf(Thing)), [ + new Thing(), + new Thing(), + ]); }); it('should warn with invalid items in the array', () => { @@ -291,7 +288,7 @@ describe('ReactPropTypes', () => { PropTypes.arrayOf(PropTypes.number), [1, 2, 'b'], 'Invalid prop `testProp[2]` of type `string` supplied to ' + - '`testComponent`, expected `number`.' + '`testComponent`, expected `number`.', ); }); @@ -303,7 +300,9 @@ describe('ReactPropTypes', () => { PropTypes.arrayOf(PropTypes.instanceOf(Thing)), [new Thing(), 'xyz'], 'Invalid prop `testProp[1]` of type `String` supplied to ' + - '`testComponent`, expected instance of `' + name + '`.' + '`testComponent`, expected instance of `' + + name + + '`.', ); }); @@ -312,19 +311,19 @@ describe('ReactPropTypes', () => { PropTypes.arrayOf(PropTypes.number), {'0': 'maybe-array', length: 1}, 'Invalid prop `testProp` of type `object` supplied to ' + - '`testComponent`, expected an array.' + '`testComponent`, expected an array.', ); typeCheckFail( PropTypes.arrayOf(PropTypes.number), 123, 'Invalid prop `testProp` of type `number` supplied to ' + - '`testComponent`, expected an array.' + '`testComponent`, expected an array.', ); typeCheckFail( PropTypes.arrayOf(PropTypes.number), 'string', 'Invalid prop `testProp` of type `string` supplied to ' + - '`testComponent`, expected an array.' + '`testComponent`, expected an array.', ); }); @@ -339,26 +338,32 @@ describe('ReactPropTypes', () => { it('should warn for missing required values', () => { typeCheckFailRequiredValues( - PropTypes.arrayOf(PropTypes.number).isRequired + PropTypes.arrayOf(PropTypes.number).isRequired, ); }); it('should warn if called manually in development', () => { spyOn(console, 'error'); + expectWarningInDevelopment(PropTypes.arrayOf({foo: PropTypes.string}), { + foo: 'bar', + }); + expectWarningInDevelopment(PropTypes.arrayOf(PropTypes.number), [ + 1, + 2, + 'b', + ]); + expectWarningInDevelopment(PropTypes.arrayOf(PropTypes.number), { + '0': 'maybe-array', + length: 1, + }); expectWarningInDevelopment( - PropTypes.arrayOf({ foo: PropTypes.string }), - { foo: 'bar' } + PropTypes.arrayOf(PropTypes.number).isRequired, + null, ); expectWarningInDevelopment( - PropTypes.arrayOf(PropTypes.number), - [1, 2, 'b'] + PropTypes.arrayOf(PropTypes.number).isRequired, + undefined, ); - expectWarningInDevelopment( - PropTypes.arrayOf(PropTypes.number), - {'0': 'maybe-array', length: 1} - ); - expectWarningInDevelopment(PropTypes.arrayOf(PropTypes.number).isRequired, null); - expectWarningInDevelopment(PropTypes.arrayOf(PropTypes.number).isRequired, undefined); }); }); @@ -384,25 +389,25 @@ describe('ReactPropTypes', () => { PropTypes.element, [
    ,
    ], 'Invalid prop `testProp` of type `array` supplied to `testComponent`, ' + - 'expected a single ReactElement.' + 'expected a single ReactElement.', ); typeCheckFail( PropTypes.element, 123, 'Invalid prop `testProp` of type `number` supplied to `testComponent`, ' + - 'expected a single ReactElement.' + 'expected a single ReactElement.', ); typeCheckFail( PropTypes.element, 'foo', 'Invalid prop `testProp` of type `string` supplied to `testComponent`, ' + - 'expected a single ReactElement.' + 'expected a single ReactElement.', ); typeCheckFail( PropTypes.element, false, 'Invalid prop `testProp` of type `boolean` supplied to `testComponent`, ' + - 'expected a single ReactElement.' + 'expected a single ReactElement.', ); }); @@ -443,7 +448,6 @@ describe('ReactPropTypes', () => { expectWarningInDevelopment(PropTypes.element.isRequired, null); expectWarningInDevelopment(PropTypes.element.isRequired, undefined); }); - }); describe('Instance Types', () => { @@ -458,43 +462,57 @@ describe('ReactPropTypes', () => { PropTypes.instanceOf(Person), false, 'Invalid prop `testProp` of type `Boolean` supplied to ' + - '`testComponent`, expected instance of `' + personName + '`.' + '`testComponent`, expected instance of `' + + personName + + '`.', ); typeCheckFail( PropTypes.instanceOf(Person), {}, 'Invalid prop `testProp` of type `Object` supplied to ' + - '`testComponent`, expected instance of `' + personName + '`.' + '`testComponent`, expected instance of `' + + personName + + '`.', ); typeCheckFail( PropTypes.instanceOf(Person), '', 'Invalid prop `testProp` of type `String` supplied to ' + - '`testComponent`, expected instance of `' + personName + '`.' + '`testComponent`, expected instance of `' + + personName + + '`.', ); typeCheckFail( PropTypes.instanceOf(Date), {}, 'Invalid prop `testProp` of type `Object` supplied to ' + - '`testComponent`, expected instance of `' + dateName + '`.' + '`testComponent`, expected instance of `' + + dateName + + '`.', ); typeCheckFail( PropTypes.instanceOf(RegExp), {}, 'Invalid prop `testProp` of type `Object` supplied to ' + - '`testComponent`, expected instance of `' + regExpName + '`.' + '`testComponent`, expected instance of `' + + regExpName + + '`.', ); typeCheckFail( PropTypes.instanceOf(Person), new Cat(), 'Invalid prop `testProp` of type `Cat` supplied to ' + - '`testComponent`, expected instance of `' + personName + '`.' + '`testComponent`, expected instance of `' + + personName + + '`.', ); typeCheckFail( PropTypes.instanceOf(Person), Object.create(null), 'Invalid prop `testProp` of type `<>` supplied to ' + - '`testComponent`, expected instance of `' + personName + '`.' + '`testComponent`, expected instance of `' + + personName + + '`.', ); }); @@ -524,9 +542,11 @@ describe('ReactPropTypes', () => { expectWarningInDevelopment(PropTypes.instanceOf(Date), {}); expectWarningInDevelopment(PropTypes.instanceOf(Date), new Date()); expectWarningInDevelopment(PropTypes.instanceOf(Date).isRequired, {}); - expectWarningInDevelopment(PropTypes.instanceOf(Date).isRequired, new Date()); + expectWarningInDevelopment( + PropTypes.instanceOf(Date).isRequired, + new Date(), + ); }); - }); describe('React Component Types', () => { @@ -539,7 +559,8 @@ describe('ReactPropTypes', () => { }); it('should warn for invalid values', () => { - var failMessage = 'Invalid prop `testProp` supplied to ' + + var failMessage = + 'Invalid prop `testProp` supplied to ' + '`testComponent`, expected a ReactNode.'; typeCheckFail(PropTypes.node, true, failMessage); typeCheckFail(PropTypes.node, function() {}, failMessage); @@ -565,18 +586,21 @@ describe('ReactPropTypes', () => { // Object of renderable things var frag = ReactFragment.create; - typeCheckPass(PropTypes.node, frag({ - k0: 123, - k1: 'Some string', - k2:
    , - k3: frag({ - k30: , - k31: frag({k310: }), - k32: 'Another string', + typeCheckPass( + PropTypes.node, + frag({ + k0: 123, + k1: 'Some string', + k2:
    , + k3: frag({ + k30: , + k31: frag({k310: }), + k32: 'Another string', + }), + k4: null, + k5: undefined, }), - k4: null, - k5: undefined, - })); + ); expect(console.error.calls.count()).toBe(0); }); @@ -603,7 +627,10 @@ describe('ReactPropTypes', () => { return { next: function() { var done = ++i > 2; - return {value: done ? undefined : ['#' + i, ], done: done}; + return { + value: done ? undefined : ['#' + i, ], + done: done, + }; }, }; }, @@ -634,45 +661,46 @@ describe('ReactPropTypes', () => { expectWarningInDevelopment(PropTypes.node.isRequired, undefined); expectWarningInDevelopment(PropTypes.node.isRequired, undefined); }); - }); describe('ObjectOf Type', () => { it('should fail for invalid argument', () => { typeCheckFail( - PropTypes.objectOf({ foo: PropTypes.string }), - { foo: 'bar' }, - 'Property `testProp` of component `testComponent` has invalid PropType notation inside objectOf.' + PropTypes.objectOf({foo: PropTypes.string}), + {foo: 'bar'}, + 'Property `testProp` of component `testComponent` has invalid PropType notation inside objectOf.', ); }); it('should support the objectOf propTypes', () => { typeCheckPass(PropTypes.objectOf(PropTypes.number), {a: 1, b: 2, c: 3}); - typeCheckPass( - PropTypes.objectOf(PropTypes.string), - {a: 'a', b: 'b', c: 'c'} - ); - typeCheckPass( - PropTypes.objectOf(PropTypes.oneOf(['a', 'b'])), - {a: 'a', b: 'b'} - ); - typeCheckPass( - PropTypes.objectOf(PropTypes.symbol), - {a: Symbol(), b: Symbol(), c: Symbol()} - ); + typeCheckPass(PropTypes.objectOf(PropTypes.string), { + a: 'a', + b: 'b', + c: 'c', + }); + typeCheckPass(PropTypes.objectOf(PropTypes.oneOf(['a', 'b'])), { + a: 'a', + b: 'b', + }); + typeCheckPass(PropTypes.objectOf(PropTypes.symbol), { + a: Symbol(), + b: Symbol(), + c: Symbol(), + }); }); it('should support objectOf with complex types', () => { typeCheckPass( PropTypes.objectOf(PropTypes.shape({a: PropTypes.number.isRequired})), - {a: {a: 1}, b: {a: 2}} + {a: {a: 1}, b: {a: 2}}, ); function Thing() {} - typeCheckPass( - PropTypes.objectOf(PropTypes.instanceOf(Thing)), - {a: new Thing(), b: new Thing()} - ); + typeCheckPass(PropTypes.objectOf(PropTypes.instanceOf(Thing)), { + a: new Thing(), + b: new Thing(), + }); }); it('should warn with invalid items in the object', () => { @@ -680,7 +708,7 @@ describe('ReactPropTypes', () => { PropTypes.objectOf(PropTypes.number), {a: 1, b: 2, c: 'b'}, 'Invalid prop `testProp.c` of type `string` supplied to `testComponent`, ' + - 'expected `number`.' + 'expected `number`.', ); }); @@ -692,7 +720,9 @@ describe('ReactPropTypes', () => { PropTypes.objectOf(PropTypes.instanceOf(Thing)), {a: new Thing(), b: 'xyz'}, 'Invalid prop `testProp.b` of type `String` supplied to ' + - '`testComponent`, expected instance of `' + name + '`.' + '`testComponent`, expected instance of `' + + name + + '`.', ); }); @@ -701,25 +731,25 @@ describe('ReactPropTypes', () => { PropTypes.objectOf(PropTypes.number), [1, 2], 'Invalid prop `testProp` of type `array` supplied to ' + - '`testComponent`, expected an object.' + '`testComponent`, expected an object.', ); typeCheckFail( PropTypes.objectOf(PropTypes.number), 123, 'Invalid prop `testProp` of type `number` supplied to ' + - '`testComponent`, expected an object.' + '`testComponent`, expected an object.', ); typeCheckFail( PropTypes.objectOf(PropTypes.number), 'string', 'Invalid prop `testProp` of type `string` supplied to ' + - '`testComponent`, expected an object.' + '`testComponent`, expected an object.', ); typeCheckFail( PropTypes.objectOf(PropTypes.symbol), Symbol(), 'Invalid prop `testProp` of type `symbol` supplied to ' + - '`testComponent`, expected an object.' + '`testComponent`, expected an object.', ); }); @@ -734,23 +764,26 @@ describe('ReactPropTypes', () => { it('should warn for missing required values', () => { typeCheckFailRequiredValues( - PropTypes.objectOf(PropTypes.number).isRequired + PropTypes.objectOf(PropTypes.number).isRequired, ); }); it('should warn if called manually in development', () => { spyOn(console, 'error'); - expectWarningInDevelopment( - PropTypes.objectOf({ foo: PropTypes.string }), - { foo: 'bar' } - ); - expectWarningInDevelopment( - PropTypes.objectOf(PropTypes.number), - {a: 1, b: 2, c: 'b'} - ); + expectWarningInDevelopment(PropTypes.objectOf({foo: PropTypes.string}), { + foo: 'bar', + }); + expectWarningInDevelopment(PropTypes.objectOf(PropTypes.number), { + a: 1, + b: 2, + c: 'b', + }); expectWarningInDevelopment(PropTypes.objectOf(PropTypes.number), [1, 2]); expectWarningInDevelopment(PropTypes.objectOf(PropTypes.number), null); - expectWarningInDevelopment(PropTypes.objectOf(PropTypes.number), undefined); + expectWarningInDevelopment( + PropTypes.objectOf(PropTypes.number), + undefined, + ); }); }); @@ -761,8 +794,9 @@ describe('ReactPropTypes', () => { PropTypes.oneOf('red', 'blue'); expect(console.error).toHaveBeenCalled(); - expect(console.error.calls.argsFor(0)[0]) - .toContain('Invalid argument supplied to oneOf, expected an instance of array.'); + expect(console.error.calls.argsFor(0)[0]).toContain( + 'Invalid argument supplied to oneOf, expected an instance of array.', + ); typeCheckPass(PropTypes.oneOf('red', 'blue'), 'red'); }); @@ -772,25 +806,25 @@ describe('ReactPropTypes', () => { PropTypes.oneOf(['red', 'blue']), true, 'Invalid prop `testProp` of value `true` supplied to ' + - '`testComponent`, expected one of ["red","blue"].' + '`testComponent`, expected one of ["red","blue"].', ); typeCheckFail( PropTypes.oneOf(['red', 'blue']), [], 'Invalid prop `testProp` of value `` supplied to `testComponent`, ' + - 'expected one of ["red","blue"].' + 'expected one of ["red","blue"].', ); typeCheckFail( PropTypes.oneOf(['red', 'blue']), '', 'Invalid prop `testProp` of value `` supplied to `testComponent`, ' + - 'expected one of ["red","blue"].' + 'expected one of ["red","blue"].', ); typeCheckFail( PropTypes.oneOf([0, 'false']), false, 'Invalid prop `testProp` of value `false` supplied to ' + - '`testComponent`, expected one of [0,"false"].' + '`testComponent`, expected one of [0,"false"].', ); }); @@ -824,8 +858,9 @@ describe('ReactPropTypes', () => { PropTypes.oneOfType(PropTypes.string, PropTypes.number); expect(console.error).toHaveBeenCalled(); - expect(console.error.calls.argsFor(0)[0]) - .toContain('Invalid argument supplied to oneOfType, expected an instance of array.'); + expect(console.error.calls.argsFor(0)[0]).toContain( + 'Invalid argument supplied to oneOfType, expected an instance of array.', + ); typeCheckPass(PropTypes.oneOf(PropTypes.string, PropTypes.number), []); }); @@ -834,7 +869,7 @@ describe('ReactPropTypes', () => { typeCheckFail( PropTypes.oneOfType([PropTypes.string, PropTypes.number]), [], - 'Invalid prop `testProp` supplied to `testComponent`.' + 'Invalid prop `testProp` supplied to `testComponent`.', ); var checker = PropTypes.oneOfType([ @@ -844,15 +879,12 @@ describe('ReactPropTypes', () => { typeCheckFail( checker, {c: 1}, - 'Invalid prop `testProp` supplied to `testComponent`.' + 'Invalid prop `testProp` supplied to `testComponent`.', ); }); it('should not warn if one of the types are valid', () => { - var checker = PropTypes.oneOfType([ - PropTypes.string, - PropTypes.number, - ]); + var checker = PropTypes.oneOfType([PropTypes.string, PropTypes.number]); typeCheckPass(checker, null); typeCheckPass(checker, 'foo'); typeCheckPass(checker, 123); @@ -867,16 +899,18 @@ describe('ReactPropTypes', () => { it('should be implicitly optional and not warn without values', () => { typeCheckPass( - PropTypes.oneOfType([PropTypes.string, PropTypes.number]), null + PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + null, ); typeCheckPass( - PropTypes.oneOfType([PropTypes.string, PropTypes.number]), undefined + PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + undefined, ); }); it('should warn for missing required values', () => { typeCheckFailRequiredValues( - PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired + PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, ); }); @@ -884,18 +918,17 @@ describe('ReactPropTypes', () => { spyOn(console, 'error'); expectWarningInDevelopment( PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - [] + [], ); expectWarningInDevelopment( PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - null + null, ); expectWarningInDevelopment( PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - undefined + undefined, ); }); - }); describe('Shape Types', () => { @@ -904,13 +937,13 @@ describe('ReactPropTypes', () => { PropTypes.shape({}), 'some string', 'Invalid prop `testProp` of type `string` supplied to ' + - '`testComponent`, expected `object`.' + '`testComponent`, expected `object`.', ); typeCheckFail( PropTypes.shape({}), ['array'], 'Invalid prop `testProp` of type `array` supplied to ' + - '`testComponent`, expected `object`.' + '`testComponent`, expected `object`.', ); }); @@ -937,7 +970,7 @@ describe('ReactPropTypes', () => { PropTypes.shape({key: PropTypes.number.isRequired}), {}, 'The prop `testProp.key` is marked as required in `testComponent`, ' + - 'but its value is `undefined`.' + 'but its value is `undefined`.', ); }); @@ -949,44 +982,49 @@ describe('ReactPropTypes', () => { }), {}, 'The prop `testProp.key` is marked as required in `testComponent`, ' + - 'but its value is `undefined`.' + 'but its value is `undefined`.', ); }); it('should warn for invalid key types', () => { - typeCheckFail(PropTypes.shape({key: PropTypes.number}), + typeCheckFail( + PropTypes.shape({key: PropTypes.number}), {key: 'abc'}, 'Invalid prop `testProp.key` of type `string` supplied to `testComponent`, ' + - 'expected `number`.' + 'expected `number`.', ); }); it('should be implicitly optional and not warn without values', () => { typeCheckPass( - PropTypes.shape(PropTypes.shape({key: PropTypes.number})), null + PropTypes.shape(PropTypes.shape({key: PropTypes.number})), + null, ); typeCheckPass( - PropTypes.shape(PropTypes.shape({key: PropTypes.number})), undefined + PropTypes.shape(PropTypes.shape({key: PropTypes.number})), + undefined, ); }); it('should warn for missing required values', () => { typeCheckFailRequiredValues( - PropTypes.shape({key: PropTypes.number}).isRequired + PropTypes.shape({key: PropTypes.number}).isRequired, ); }); it('should warn if called manually in development', () => { spyOn(console, 'error'); expectWarningInDevelopment(PropTypes.shape({}), 'some string'); - expectWarningInDevelopment(PropTypes.shape({ foo: PropTypes.number }), { foo: 42 }); + expectWarningInDevelopment(PropTypes.shape({foo: PropTypes.number}), { + foo: 42, + }); expectWarningInDevelopment( PropTypes.shape({key: PropTypes.number}).isRequired, - null + null, ); expectWarningInDevelopment( PropTypes.shape({key: PropTypes.number}).isRequired, - undefined + undefined, ); expectWarningInDevelopment(PropTypes.element,
    ); }); @@ -998,13 +1036,13 @@ describe('ReactPropTypes', () => { PropTypes.symbol, 'hello', 'Invalid prop `testProp` of type `string` supplied to ' + - '`testComponent`, expected `symbol`.' + '`testComponent`, expected `symbol`.', ); typeCheckFail( PropTypes.symbol, - function() { }, + function() {}, 'Invalid prop `testProp` of type `function` supplied to ' + - '`testComponent`, expected `symbol`.' + '`testComponent`, expected `symbol`.', ); typeCheckFail( PropTypes.symbol, @@ -1012,7 +1050,7 @@ describe('ReactPropTypes', () => { '@@toStringTag': 'Katana', }, 'Invalid prop `testProp` of type `object` supplied to ' + - '`testComponent`, expected `symbol`.' + '`testComponent`, expected `symbol`.', ); }); @@ -1061,15 +1099,15 @@ describe('ReactPropTypes', () => { expect(spy.calls.argsFor(0)[1]).toBe('num'); }); - it('should have received the validator\'s return value', () => { + it("should have received the validator's return value", () => { spyOn(console, 'error'); - var spy = jasmine.createSpy().and.callFake( - function(props, propName, componentName) { + var spy = jasmine + .createSpy() + .and.callFake(function(props, propName, componentName) { if (props[propName] !== 5) { return new Error('num must be 5!'); } - } - ); + }); Component = class extends React.Component { static propTypes = {num: spy}; @@ -1082,33 +1120,31 @@ describe('ReactPropTypes', () => { ReactTestUtils.renderIntoDocument(instance); expect(console.error.calls.count()).toBe(1); expect( - console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)') + console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)'), ).toBe( 'Warning: Failed prop type: num must be 5!\n' + - ' in Component (at **)' + ' in Component (at **)', ); }); - it('should not warn if the validator returned null', - () => { - spyOn(console, 'error'); - var spy = jasmine.createSpy().and.callFake( - function(props, propName, componentName) { - return null; - } - ); - Component = class extends React.Component { - static propTypes = {num: spy}; + it('should not warn if the validator returned null', () => { + spyOn(console, 'error'); + var spy = jasmine + .createSpy() + .and.callFake(function(props, propName, componentName) { + return null; + }); + Component = class extends React.Component { + static propTypes = {num: spy}; - render() { - return
    ; - } - }; + render() { + return
    ; + } + }; - var instance = ; - ReactTestUtils.renderIntoDocument(instance); - expect(console.error.calls.count()).toBe(0); - } - ); + var instance = ; + ReactTestUtils.renderIntoDocument(instance); + expect(console.error.calls.count()).toBe(0); + }); }); }); diff --git a/src/isomorphic/hooks/ReactComponentTreeHook.js b/src/isomorphic/hooks/ReactComponentTreeHook.js index fd778f7536..6ff41657ab 100644 --- a/src/isomorphic/hooks/ReactComponentTreeHook.js +++ b/src/isomorphic/hooks/ReactComponentTreeHook.js @@ -17,23 +17,26 @@ var ReactCurrentOwner = require('ReactCurrentOwner'); var invariant = require('invariant'); var warning = require('warning'); -import type { ReactElement, Source } from 'ReactElementType'; -import type { DebugID } from 'ReactInstanceType'; +import type {ReactElement, Source} from 'ReactElementType'; +import type {DebugID} from 'ReactInstanceType'; function isNative(fn) { // Based on isNative() from Lodash var funcToString = Function.prototype.toString; var hasOwnProperty = Object.prototype.hasOwnProperty; - var reIsNative = RegExp('^' + funcToString - // Take an example native function source for comparison - .call(hasOwnProperty) - // Strip regex characters so we can use it for regex - .replace(/[\\^$.*+?()[\]{}|]/g, '\\$&') - // Remove hasOwnProperty from the template to make it generic - .replace( - /hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, - '$1.*?' - ) + '$' + var reIsNative = RegExp( + '^' + + funcToString + // Take an example native function source for comparison + .call(hasOwnProperty) + // Strip regex characters so we can use it for regex + .replace(/[\\^$.*+?()[\]{}|]/g, '\\$&') + // Remove hasOwnProperty from the template to make it generic + .replace( + /hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, + '$1.*?', + ) + + '$', ); try { var source = funcToString.call(fn); @@ -43,7 +46,7 @@ function isNative(fn) { } } -var canUseCollections = ( +var canUseCollections = // Array.from typeof Array.from === 'function' && // Map @@ -59,8 +62,7 @@ var canUseCollections = ( // Set.prototype.keys Set.prototype != null && typeof Set.prototype.keys === 'function' && - isNative(Set.prototype.keys) -); + isNative(Set.prototype.keys); var setItem; var getItem; @@ -96,7 +98,6 @@ if (canUseCollections) { getRootIDs = function() { return Array.from(rootIDSet.keys()); }; - } else { var itemByKey = {}; var rootByKey = {}; @@ -151,13 +152,16 @@ function purgeDeep(id) { } function describeComponentFrame(name, source, ownerName) { - return '\n in ' + (name || 'Unknown') + ( - source ? - ' (at ' + source.fileName.replace(/^.*[\\\/]/, '') + ':' + - source.lineNumber + ')' : - ownerName ? - ' (created by ' + ownerName + ')' : - '' + return ( + '\n in ' + + (name || 'Unknown') + + (source + ? ' (at ' + + source.fileName.replace(/^.*[\\\/]/, '') + + ':' + + source.lineNumber + + ')' + : ownerName ? ' (created by ' + ownerName + ')' : '') ); } @@ -184,8 +188,8 @@ function describeID(id: DebugID): string { warning( element, 'ReactComponentTreeHook: Missing React element for debugID %s when ' + - 'building stack', - id + 'building stack', + id, ); return describeComponentFrame(name, element && element._source, ownerName); } @@ -202,19 +206,19 @@ var ReactComponentTreeHook = { invariant( nextChild, 'Expected hook events to fire for the child ' + - 'before its parent includes it in onSetChildren().' + 'before its parent includes it in onSetChildren().', ); invariant( nextChild.childIDs != null || - typeof nextChild.element !== 'object' || - nextChild.element == null, + typeof nextChild.element !== 'object' || + nextChild.element == null, 'Expected onSetChildren() to fire for a container child ' + - 'before its parent includes it in onSetChildren().' + 'before its parent includes it in onSetChildren().', ); invariant( nextChild.isMounted, 'Expected onMountComponent() to fire for the child ' + - 'before its parent includes it in onSetChildren().' + 'before its parent includes it in onSetChildren().', ); if (nextChild.parentID == null) { nextChild.parentID = id; @@ -225,15 +229,19 @@ var ReactComponentTreeHook = { invariant( nextChild.parentID === id, 'Expected onBeforeMountComponent() parent and onSetChildren() to ' + - 'be consistent (%s has parents %s and %s).', + 'be consistent (%s has parents %s and %s).', nextChildID, nextChild.parentID, - id + id, ); } }, - onBeforeMountComponent(id: DebugID, element: ReactElement, parentID: DebugID): void { + onBeforeMountComponent( + id: DebugID, + element: ReactElement, + parentID: DebugID, + ): void { var item = { element, parentID, @@ -318,7 +326,7 @@ var ReactComponentTreeHook = { info += describeComponentFrame( name, topElement._source, - owner && owner.getName() + owner && owner.getName(), ); } @@ -394,6 +402,57 @@ var ReactComponentTreeHook = { getRootIDs, getRegisteredIDs: getItemIDs, + + pushNonStandardWarningStack( + isCreatingElement: boolean, + currentSource: ?Source, + ) { + if (typeof console.reactStack !== 'function') { + return; + } + + var stack = []; + var currentOwner = ReactCurrentOwner.current; + var id = currentOwner && currentOwner._debugID; + + try { + if (isCreatingElement) { + stack.push({ + name: id ? ReactComponentTreeHook.getDisplayName(id) : null, + fileName: currentSource ? currentSource.fileName : null, + lineNumber: currentSource ? currentSource.lineNumber : null, + }); + } + + while (id) { + var element = ReactComponentTreeHook.getElement(id); + var parentID = ReactComponentTreeHook.getParentID(id); + var ownerID = ReactComponentTreeHook.getOwnerID(id); + var ownerName = ownerID + ? ReactComponentTreeHook.getDisplayName(ownerID) + : null; + var source = element && element._source; + stack.push({ + name: ownerName, + fileName: source ? source.fileName : null, + lineNumber: source ? source.lineNumber : null, + }); + id = parentID; + } + } catch (err) { + // Internal state is messed up. + // Stop building the stack (it's just a nice to have). + } + + console.reactStack(stack); + }, + + popNonStandardWarningStack() { + if (typeof console.reactStackEnd !== 'function') { + return; + } + console.reactStackEnd(); + }, }; module.exports = ReactComponentTreeHook; diff --git a/src/isomorphic/modern/class/PropTypes.d.ts b/src/isomorphic/modern/class/PropTypes.d.ts new file mode 100644 index 0000000000..a8802e66fe --- /dev/null +++ b/src/isomorphic/modern/class/PropTypes.d.ts @@ -0,0 +1,19 @@ +/*! + * Copyright 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +/** + * TypeScript Definition File for React. + * + * Full type definitions are not yet officially supported. These are mostly + * just helpers for the unit test. + */ + +declare module 'prop-types' { + export var string : any; +} diff --git a/src/isomorphic/modern/class/ReactComponent.js b/src/isomorphic/modern/class/ReactBaseClasses.js similarity index 76% rename from src/isomorphic/modern/class/ReactComponent.js rename to src/isomorphic/modern/class/ReactBaseClasses.js index b93da63e94..ed8d31e9bc 100644 --- a/src/isomorphic/modern/class/ReactComponent.js +++ b/src/isomorphic/modern/class/ReactBaseClasses.js @@ -6,7 +6,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @providesModule ReactComponent + * @providesModule ReactBaseClasses */ 'use strict'; @@ -16,7 +16,7 @@ var ReactNoopUpdateQueue = require('ReactNoopUpdateQueue'); var canDefineProperty = require('canDefineProperty'); var emptyObject = require('emptyObject'); var invariant = require('invariant'); -var warning = require('warning'); +var lowPriorityWarning = require('lowPriorityWarning'); /** * Base class helpers for the updating state of a component. @@ -60,10 +60,10 @@ ReactComponent.prototype.isReactComponent = {}; ReactComponent.prototype.setState = function(partialState, callback) { invariant( typeof partialState === 'object' || - typeof partialState === 'function' || - partialState == null, + typeof partialState === 'function' || + partialState == null, 'setState(...): takes an object of state variables to update or a ' + - 'function which returns an object of state variables.' + 'function which returns an object of state variables.', ); this.updater.enqueueSetState(this, partialState); if (callback) { @@ -102,23 +102,23 @@ if (__DEV__) { isMounted: [ 'isMounted', 'Instead, make sure to clean up subscriptions and pending requests in ' + - 'componentWillUnmount to prevent memory leaks.', + 'componentWillUnmount to prevent memory leaks.', ], replaceState: [ 'replaceState', 'Refactor your code to use setState instead (see ' + - 'https://github.com/facebook/react/issues/3236).', + 'https://github.com/facebook/react/issues/3236).', ], }; var defineDeprecationWarning = function(methodName, info) { if (canDefineProperty) { Object.defineProperty(ReactComponent.prototype, methodName, { get: function() { - warning( + lowPriorityWarning( false, '%s(...) is deprecated in plain JavaScript React classes. %s', info[0], - info[1] + info[1], ); return undefined; }, @@ -132,4 +132,28 @@ if (__DEV__) { } } -module.exports = ReactComponent; +/** + * Base class helpers for the updating state of a component. + */ +function ReactPureComponent(props, context, updater) { + // Duplicated from ReactComponent. + this.props = props; + this.context = context; + this.refs = emptyObject; + // We initialize the default updater but the real one gets injected by the + // renderer. + this.updater = updater || ReactNoopUpdateQueue; +} + +function ComponentDummy() {} +ComponentDummy.prototype = ReactComponent.prototype; +ReactPureComponent.prototype = new ComponentDummy(); +ReactPureComponent.prototype.constructor = ReactPureComponent; +// Avoid an extra prototype jump for these methods. +Object.assign(ReactPureComponent.prototype, ReactComponent.prototype); +ReactPureComponent.prototype.isPureReactComponent = true; + +module.exports = { + Component: ReactComponent, + PureComponent: ReactPureComponent, +}; diff --git a/src/isomorphic/modern/class/ReactNoopUpdateQueue.js b/src/isomorphic/modern/class/ReactNoopUpdateQueue.js index 49d4c72660..c9e03b723e 100644 --- a/src/isomorphic/modern/class/ReactNoopUpdateQueue.js +++ b/src/isomorphic/modern/class/ReactNoopUpdateQueue.js @@ -19,11 +19,12 @@ function warnNoop(publicInstance, callerName) { warning( false, '%s(...): Can only update a mounted or mounting component. ' + - 'This usually means you called %s() on an unmounted component. ' + - 'This is a no-op. Please check the code for the %s component.', + 'This usually means you called %s() on an unmounted component. ' + + 'This is a no-op. Please check the code for the %s component.', callerName, callerName, - constructor && (constructor.displayName || constructor.name) || 'ReactClass' + (constructor && (constructor.displayName || constructor.name)) || + 'ReactClass', ); } } @@ -32,7 +33,6 @@ function warnNoop(publicInstance, callerName) { * This is the abstract API for an update queue. */ var ReactNoopUpdateQueue = { - /** * Checks whether or not this composite component is mounted. * @param {ReactClass} publicInstance The instance we want to test. @@ -52,7 +52,7 @@ var ReactNoopUpdateQueue = { * @param {?function} callback Called after state is updated. * @internal */ - enqueueCallback: function(publicInstance, callback) { }, + enqueueCallback: function(publicInstance, callback) {}, /** * Forces an update. This should only be invoked when it is known with diff --git a/src/isomorphic/modern/class/ReactPureComponent.js b/src/isomorphic/modern/class/ReactPureComponent.js deleted file mode 100644 index b4a808864b..0000000000 --- a/src/isomorphic/modern/class/ReactPureComponent.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule ReactPureComponent - */ - -'use strict'; - -var ReactComponent = require('ReactComponent'); -var ReactNoopUpdateQueue = require('ReactNoopUpdateQueue'); - -var emptyObject = require('emptyObject'); - -/** - * Base class helpers for the updating state of a component. - */ -function ReactPureComponent(props, context, updater) { - // Duplicated from ReactComponent. - this.props = props; - this.context = context; - this.refs = emptyObject; - // We initialize the default updater but the real one gets injected by the - // renderer. - this.updater = updater || ReactNoopUpdateQueue; -} - -function ComponentDummy() {} -ComponentDummy.prototype = ReactComponent.prototype; -ReactPureComponent.prototype = new ComponentDummy(); -ReactPureComponent.prototype.constructor = ReactPureComponent; -// Avoid an extra prototype jump for these methods. -Object.assign(ReactPureComponent.prototype, ReactComponent.prototype); -ReactPureComponent.prototype.isPureReactComponent = true; - -module.exports = ReactPureComponent; diff --git a/src/isomorphic/modern/class/__tests__/ReactClassEquivalence-test.js b/src/isomorphic/modern/class/__tests__/ReactClassEquivalence-test.js index b78a2edab1..bfa6c3b214 100644 --- a/src/isomorphic/modern/class/__tests__/ReactClassEquivalence-test.js +++ b/src/isomorphic/modern/class/__tests__/ReactClassEquivalence-test.js @@ -26,7 +26,6 @@ describe('ReactClassEquivalence', () => { var result2 = runJest('ReactES6Class-test.js'); compareResults(result1, result2); }); - }); function runJest(testFile) { @@ -35,14 +34,13 @@ function runJest(testFile) { var setupFile = path.resolve( 'scripts', 'jest', - 'setupSpecEquivalenceReporter.js' + 'setupSpecEquivalenceReporter.js', + ); + var result = spawnSync( + 'node', + [jestBin, testFile, '--setupTestFrameworkScriptFile', setupFile], + {cwd}, ); - var result = spawnSync('node', [ - jestBin, - testFile, - '--setupTestFrameworkScriptFile', - setupFile, - ], {cwd}); if (result.error) { throw result.error; @@ -51,12 +49,12 @@ function runJest(testFile) { if (result.status !== 0) { throw new Error( 'jest process exited with: ' + - result.status + - '\n' + - 'stdout: ' + - result.stdout.toString() + - 'stderr: ' + - result.stderr.toString() + result.status + + '\n' + + 'stdout: ' + + result.stdout.toString() + + 'stderr: ' + + result.stderr.toString(), ); } diff --git a/src/isomorphic/modern/class/__tests__/ReactCoffeeScriptClass-test.coffee b/src/isomorphic/modern/class/__tests__/ReactCoffeeScriptClass-test.coffee index de03d875b0..23661be18e 100644 --- a/src/isomorphic/modern/class/__tests__/ReactCoffeeScriptClass-test.coffee +++ b/src/isomorphic/modern/class/__tests__/ReactCoffeeScriptClass-test.coffee @@ -7,6 +7,7 @@ LICENSE file in the root directory of this source tree. An additional grant of patent rights can be found in the PATENTS file in the same directory. ### +PropTypes = null React = null ReactDOM = null @@ -21,6 +22,7 @@ describe 'ReactCoffeeScriptClass', -> beforeEach -> React = require 'React' ReactDOM = require 'ReactDOM' + PropTypes = require 'prop-types' container = document.createElement 'div' attachedListener = null renderedName = null @@ -102,8 +104,8 @@ describe 'ReactCoffeeScriptClass', -> it 'renders based on context in the constructor', -> class Foo extends React.Component @contextTypes: - tag: React.PropTypes.string - className: React.PropTypes.string + tag: PropTypes.string + className: PropTypes.string constructor: (props, context) -> super props, context @@ -118,8 +120,8 @@ describe 'ReactCoffeeScriptClass', -> class Outer extends React.Component @childContextTypes: - tag: React.PropTypes.string - className: React.PropTypes.string + tag: PropTypes.string + className: PropTypes.string getChildContext: -> tag: 'span' @@ -376,16 +378,16 @@ describe 'ReactCoffeeScriptClass', -> undefined it 'should throw AND warn when trying to access classic APIs', -> - spyOn console, 'error' + spyOn console, 'warn' instance = test Inner(name: 'foo'), 'DIV', 'foo' expect(-> instance.replaceState {}).toThrow() expect(-> instance.isMounted()).toThrow() - expect(console.error.calls.count()).toBe 2 - expect(console.error.calls.argsFor(0)[0]).toContain( + expect(console.warn.calls.count()).toBe 2 + expect(console.warn.calls.argsFor(0)[0]).toContain( 'replaceState(...) is deprecated in plain JavaScript React classes' ) - expect(console.error.calls.argsFor(1)[0]).toContain( + expect(console.warn.calls.argsFor(1)[0]).toContain( 'isMounted(...) is deprecated in plain JavaScript React classes' ) undefined @@ -393,13 +395,13 @@ describe 'ReactCoffeeScriptClass', -> it 'supports this.context passed via getChildContext', -> class Bar extends React.Component @contextTypes: - bar: React.PropTypes.string + bar: PropTypes.string render: -> div className: @context.bar class Foo extends React.Component @childContextTypes: - bar: React.PropTypes.string + bar: PropTypes.string getChildContext: -> bar: 'bar-through-context' render: -> diff --git a/src/isomorphic/modern/class/__tests__/ReactES6Class-test.js b/src/isomorphic/modern/class/__tests__/ReactES6Class-test.js index ca34089157..ee63a7af70 100644 --- a/src/isomorphic/modern/class/__tests__/ReactES6Class-test.js +++ b/src/isomorphic/modern/class/__tests__/ReactES6Class-test.js @@ -11,11 +11,11 @@ 'use strict'; +var PropTypes; var React; var ReactDOM; describe('ReactES6Class', () => { - var container; var freeze = function(expectation) { Object.freeze(expectation); @@ -28,6 +28,7 @@ describe('ReactES6Class', () => { beforeEach(() => { React = require('React'); ReactDOM = require('ReactDOM'); + PropTypes = require('prop-types'); container = document.createElement('div'); attachedListener = null; renderedName = null; @@ -52,19 +53,19 @@ describe('ReactES6Class', () => { } it('preserves the name of the class for use in error messages', () => { - class Foo extends React.Component { } + class Foo extends React.Component {} expect(Foo.name).toBe('Foo'); }); it('throws if no render function is defined', () => { spyOn(console, 'error'); - class Foo extends React.Component { } + class Foo extends React.Component {} expect(() => ReactDOM.render(, container)).toThrow(); expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: Foo(...): No `render` method found on the returned component ' + - 'instance: you may have forgotten to define `render`.' + 'instance: you may have forgotten to define `render`.', ); }); @@ -124,8 +125,8 @@ describe('ReactES6Class', () => { } } Foo.contextTypes = { - tag: React.PropTypes.string, - className: React.PropTypes.string, + tag: PropTypes.string, + className: PropTypes.string, }; class Outer extends React.Component { @@ -137,8 +138,8 @@ describe('ReactES6Class', () => { } } Outer.childContextTypes = { - tag: React.PropTypes.string, - className: React.PropTypes.string, + tag: PropTypes.string, + className: PropTypes.string, }; test(, 'SPAN', 'foo'); }); @@ -174,7 +175,7 @@ describe('ReactES6Class', () => { } } expect(() => test(, 'span', '')).toThrowError( - 'Foo.state: must be set to an object or null' + 'Foo.state: must be set to an object or null', ); }); }); @@ -203,10 +204,7 @@ describe('ReactES6Class', () => { } render() { return ( - + ); } } @@ -225,12 +223,7 @@ describe('ReactES6Class', () => { this.setState({bar: 'bar'}); } render() { - return ( - - ); + return ; } } test(, 'DIV', 'foo'); @@ -295,23 +288,25 @@ describe('ReactES6Class', () => { } } test(, 'SPAN', 'foo'); - expect(lifeCycles).toEqual([ - 'will-mount', - 'did-mount', - ]); + expect(lifeCycles).toEqual(['will-mount', 'did-mount']); lifeCycles = []; // reset test(, 'SPAN', 'bar'); expect(lifeCycles).toEqual([ - 'receive-props', freeze({value: 'bar'}), - 'should-update', freeze({value: 'bar'}), {}, - 'will-update', freeze({value: 'bar'}), {}, - 'did-update', freeze({value: 'foo'}), {}, + 'receive-props', + freeze({value: 'bar'}), + 'should-update', + freeze({value: 'bar'}), + {}, + 'will-update', + freeze({value: 'bar'}), + {}, + 'did-update', + freeze({value: 'foo'}), + {}, ]); lifeCycles = []; // reset ReactDOM.unmountComponentAtNode(container); - expect(lifeCycles).toEqual([ - 'will-unmount', - ]); + expect(lifeCycles).toEqual(['will-unmount']); }); it('warns when classic properties are defined on the instance, but does not invoke them.', () => { @@ -341,16 +336,16 @@ describe('ReactES6Class', () => { expect(getDefaultPropsWasCalled).toBe(false); expect(console.error.calls.count()).toBe(4); expect(console.error.calls.argsFor(0)[0]).toContain( - 'getInitialState was defined on Foo, a plain JavaScript class.' + 'getInitialState was defined on Foo, a plain JavaScript class.', ); expect(console.error.calls.argsFor(1)[0]).toContain( - 'getDefaultProps was defined on Foo, a plain JavaScript class.' + 'getDefaultProps was defined on Foo, a plain JavaScript class.', ); expect(console.error.calls.argsFor(2)[0]).toContain( - 'propTypes was defined as an instance property on Foo.' + 'propTypes was defined as an instance property on Foo.', ); expect(console.error.calls.argsFor(3)[0]).toContain( - 'contextTypes was defined as an instance property on Foo.' + 'contextTypes was defined as an instance property on Foo.', ); }); @@ -385,9 +380,9 @@ describe('ReactES6Class', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: ' + - 'NamedComponent has a method called componentShouldUpdate(). Did you ' + - 'mean shouldComponentUpdate()? The name is phrased as a question ' + - 'because the function is expected to return a value.' + 'NamedComponent has a method called componentShouldUpdate(). Did you ' + + 'mean shouldComponentUpdate()? The name is phrased as a question ' + + 'because the function is expected to return a value.', ); }); @@ -407,22 +402,22 @@ describe('ReactES6Class', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: ' + - 'NamedComponent has a method called componentWillRecieveProps(). Did ' + - 'you mean componentWillReceiveProps()?' + 'NamedComponent has a method called componentWillRecieveProps(). Did ' + + 'you mean componentWillReceiveProps()?', ); }); it('should throw AND warn when trying to access classic APIs', () => { - spyOn(console, 'error'); + spyOn(console, 'warn'); var instance = test(, 'DIV', 'foo'); expect(() => instance.replaceState({})).toThrow(); expect(() => instance.isMounted()).toThrow(); - expect(console.error.calls.count()).toBe(2); - expect(console.error.calls.argsFor(0)[0]).toContain( - 'replaceState(...) is deprecated in plain JavaScript React classes' + expect(console.warn.calls.count()).toBe(2); + expect(console.warn.calls.argsFor(0)[0]).toContain( + 'replaceState(...) is deprecated in plain JavaScript React classes', ); - expect(console.error.calls.argsFor(1)[0]).toContain( - 'isMounted(...) is deprecated in plain JavaScript React classes' + expect(console.warn.calls.argsFor(1)[0]).toContain( + 'isMounted(...) is deprecated in plain JavaScript React classes', ); }); @@ -432,7 +427,7 @@ describe('ReactES6Class', () => { return
    ; } } - Bar.contextTypes = {bar: React.PropTypes.string}; + Bar.contextTypes = {bar: PropTypes.string}; class Foo extends React.Component { getChildContext() { return {bar: 'bar-through-context'}; @@ -441,7 +436,7 @@ describe('ReactES6Class', () => { return ; } } - Foo.childContextTypes = {bar: React.PropTypes.string}; + Foo.childContextTypes = {bar: PropTypes.string}; test(, 'DIV', 'bar-through-context'); }); @@ -460,5 +455,4 @@ describe('ReactES6Class', () => { var node = ReactDOM.findDOMNode(instance); expect(node).toBe(container.firstChild); }); - }); diff --git a/src/isomorphic/modern/class/__tests__/ReactPureComponent-test.js b/src/isomorphic/modern/class/__tests__/ReactPureComponent-test.js index 1235a1ea82..0cf7f751a7 100644 --- a/src/isomorphic/modern/class/__tests__/ReactPureComponent-test.js +++ b/src/isomorphic/modern/class/__tests__/ReactPureComponent-test.js @@ -93,5 +93,4 @@ describe('ReactPureComponent', () => { ReactDOM.render(, document.createElement('div')); expect(renders).toBe(1); }); - }); diff --git a/src/isomorphic/modern/class/__tests__/ReactTypeScriptClass-test.ts b/src/isomorphic/modern/class/__tests__/ReactTypeScriptClass-test.ts index 6ef95ccaa3..d7de7469d3 100644 --- a/src/isomorphic/modern/class/__tests__/ReactTypeScriptClass-test.ts +++ b/src/isomorphic/modern/class/__tests__/ReactTypeScriptClass-test.ts @@ -1,3 +1,4 @@ +/// /// /// @@ -12,6 +13,7 @@ import React = require('React'); import ReactDOM = require('ReactDOM'); +import PropTypes = require('prop-types'); // Before Each @@ -85,8 +87,8 @@ class StateBasedOnProps extends React.Component { // it renders based on context in the constructor class StateBasedOnContext extends React.Component { static contextTypes = { - tag: React.PropTypes.string, - className: React.PropTypes.string + tag: PropTypes.string, + className: PropTypes.string }; state = { tag: this.context.tag, @@ -100,8 +102,8 @@ class StateBasedOnContext extends React.Component { class ProvideChildContextTypes extends React.Component { static childContextTypes = { - tag: React.PropTypes.string, - className: React.PropTypes.string + tag: PropTypes.string, + className: PropTypes.string }; getChildContext() { return { tag: 'span', className: 'foo' }; @@ -278,13 +280,13 @@ class MisspelledComponent2 extends React.Component { // it supports this.context passed via getChildContext class ReadContext extends React.Component { - static contextTypes = { bar: React.PropTypes.string }; + static contextTypes = { bar: PropTypes.string }; render() { return React.createElement('div', { className: this.context.bar }); } } class ProvideContext extends React.Component { - static childContextTypes = { bar: React.PropTypes.string }; + static childContextTypes = { bar: PropTypes.string }; getChildContext() { return { bar: 'bar-through-context' }; } @@ -500,18 +502,18 @@ describe('ReactTypeScriptClass', function() { }); it('should throw AND warn when trying to access classic APIs', function() { - spyOn(console, 'error'); + spyOn(console, 'warn'); var instance = test( React.createElement(Inner, {name: 'foo'}), 'DIV','foo' ); expect(() => instance.replaceState({})).toThrow(); expect(() => instance.isMounted()).toThrow(); - expect((console.error).calls.count()).toBe(2); - expect((console.error).calls.argsFor(0)[0]).toContain( + expect((console.warn).calls.count()).toBe(2); + expect((console.warn).calls.argsFor(0)[0]).toContain( 'replaceState(...) is deprecated in plain JavaScript React classes' ); - expect((console.error).calls.argsFor(1)[0]).toContain( + expect((console.warn).calls.argsFor(1)[0]).toContain( 'isMounted(...) is deprecated in plain JavaScript React classes' ); }); diff --git a/src/isomorphic/modern/element/__tests__/ReactJSXElement-test.js b/src/isomorphic/modern/element/__tests__/ReactJSXElement-test.js index 1b897494f6..fc494b436c 100644 --- a/src/isomorphic/modern/element/__tests__/ReactJSXElement-test.js +++ b/src/isomorphic/modern/element/__tests__/ReactJSXElement-test.js @@ -64,7 +64,7 @@ describe('ReactJSXElement', () => { it('returns an immutable element', () => { var element = ; - expect(() => element.type = 'div').toThrow(); + expect(() => (element.type = 'div')).toThrow(); }); it('does not reuse the object that is spread into props', () => { @@ -80,7 +80,7 @@ describe('ReactJSXElement', () => { expect(element.type).toBe(Component); expect(element.key).toBe('12'); expect(element.ref).toBe('34'); - var expectation = {foo:'56'}; + var expectation = {foo: '56'}; Object.freeze(expectation); expect(element.props).toEqual(expectation); }); @@ -90,7 +90,7 @@ describe('ReactJSXElement', () => { expect(element.type).toBe(Component); expect(element.key).toBe('12'); expect(element.ref).toBe(null); - var expectation = {foo:'56'}; + var expectation = {foo: '56'}; Object.freeze(expectation); expect(element.props).toEqual(expectation); }); @@ -124,7 +124,7 @@ describe('ReactJSXElement', () => { var element2 = React.cloneElement( , {}, - undefined + undefined, ); expect(element2.props.children).toBe(undefined); }); @@ -165,7 +165,7 @@ describe('ReactJSXElement', () => { expect(React.isValidElement({})).toEqual(false); expect(React.isValidElement('string')).toEqual(false); expect(React.isValidElement(Component)).toEqual(false); - expect(React.isValidElement({ type: 'div', props: {} })).toEqual(false); + expect(React.isValidElement({type: 'div', props: {}})).toEqual(false); }); it('is indistinguishable from a plain object', () => { @@ -178,10 +178,7 @@ describe('ReactJSXElement', () => { Component.defaultProps = {fruit: 'persimmon'}; var container = document.createElement('div'); - var instance = ReactDOM.render( - , - container - ); + var instance = ReactDOM.render(, container); expect(instance.props.fruit).toBe('mango'); ReactDOM.render(, container); @@ -199,9 +196,9 @@ describe('ReactJSXElement', () => { var instance = ReactTestUtils.renderIntoDocument(); expect(instance.props.prop).toBe('testKey'); - var inst2 = - ReactTestUtils.renderIntoDocument(); + var inst2 = ReactTestUtils.renderIntoDocument( + , + ); expect(inst2.props.prop).toBe(null); }); - }); diff --git a/src/isomorphic/modern/element/__tests__/ReactJSXElementValidator-test.js b/src/isomorphic/modern/element/__tests__/ReactJSXElementValidator-test.js index 5bb44c6943..0502161aad 100644 --- a/src/isomorphic/modern/element/__tests__/ReactJSXElementValidator-test.js +++ b/src/isomorphic/modern/element/__tests__/ReactJSXElementValidator-test.js @@ -14,6 +14,7 @@ // TODO: All these warnings should become static errors using Flow instead // of dynamic errors when using JSX with Flow. +var PropTypes; var React; var ReactDOM; var ReactTestUtils; @@ -32,6 +33,7 @@ describe('ReactJSXElementValidator', () => { React = require('React'); ReactDOM = require('ReactDOM'); ReactTestUtils = require('ReactTestUtils'); + PropTypes = require('prop-types'); Component = class extends React.Component { render() { @@ -45,7 +47,7 @@ describe('ReactJSXElementValidator', () => { } }; RequiredPropComponent.displayName = 'RequiredPropComponent'; - RequiredPropComponent.propTypes = {prop: React.PropTypes.string.isRequired}; + RequiredPropComponent.propTypes = {prop: PropTypes.string.isRequired}; }); it('warns for keys for arrays of elements in children position', () => { @@ -55,7 +57,7 @@ describe('ReactJSXElementValidator', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( - 'Each child in an array or iterator should have a unique "key" prop.' + 'Each child in an array or iterator should have a unique "key" prop.', ); }); @@ -70,11 +72,7 @@ describe('ReactJSXElementValidator', () => { class ComponentWrapper extends React.Component { render() { - return ( - , ]} - /> - ); + return , ]} />; } } @@ -83,8 +81,8 @@ describe('ReactJSXElementValidator', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( 'Each child in an array or iterator should have a unique "key" prop. ' + - 'Check the render method of `InnerComponent`. ' + - 'It was passed a child from ComponentWrapper. ' + 'Check the render method of `InnerComponent`. ' + + 'It was passed a child from ComponentWrapper. ', ); }); @@ -107,14 +105,16 @@ describe('ReactJSXElementValidator', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( - 'Each child in an array or iterator should have a unique "key" prop.' + 'Each child in an array or iterator should have a unique "key" prop.', ); }); it('does not warns for arrays of elements with keys', () => { spyOn(console, 'error'); - void {[, ]}; + void ( + {[, ]} + ); expect(console.error.calls.count()).toBe(0); }); @@ -193,7 +193,7 @@ describe('ReactJSXElementValidator', () => { } } MyComp.propTypes = { - color: React.PropTypes.string, + color: PropTypes.string, }; class ParentComp extends React.Component { render() { @@ -203,10 +203,10 @@ describe('ReactJSXElementValidator', () => { ReactTestUtils.renderIntoDocument(); expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( 'Warning: Failed prop type: ' + - 'Invalid prop `color` of type `number` supplied to `MyComp`, ' + - 'expected `string`.\n' + - ' in MyComp (at **)\n' + - ' in ParentComp (at **)' + 'Invalid prop `color` of type `number` supplied to `MyComp`, ' + + 'expected `string`.\n' + + ' in MyComp (at **)\n' + + ' in ParentComp (at **)', ); }); @@ -216,7 +216,7 @@ describe('ReactJSXElementValidator', () => { return null; } MyComp.propTypes = { - color: React.PropTypes.string, + color: PropTypes.string, }; function MiddleComp(props) { return ; @@ -239,11 +239,11 @@ describe('ReactJSXElementValidator', () => { // If it doesn't, it means we're using information from the old element. expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( 'Warning: Failed prop type: ' + - 'Invalid prop `color` of type `number` supplied to `MyComp`, ' + - 'expected `string`.\n' + - ' in MyComp (at **)\n' + - ' in MiddleComp (at **)\n' + - ' in ParentComp (at **)' + 'Invalid prop `color` of type `number` supplied to `MyComp`, ' + + 'expected `string`.\n' + + ' in MyComp (at **)\n' + + ' in MiddleComp (at **)\n' + + ' in ParentComp (at **)', ); }); @@ -261,28 +261,28 @@ describe('ReactJSXElementValidator', () => { expect(console.error.calls.count()).toBe(4); expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( 'Warning: React.createElement: type is invalid -- expected a string ' + - '(for built-in components) or a class/function (for composite ' + - 'components) but got: undefined. You likely forgot to export your ' + - 'component from the file it\'s defined in. ' + - 'Check your code at **.' + '(for built-in components) or a class/function (for composite ' + + 'components) but got: undefined. You likely forgot to export your ' + + "component from the file it's defined in. " + + 'Check your code at **.', ); expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe( 'Warning: React.createElement: type is invalid -- expected a string ' + - '(for built-in components) or a class/function (for composite ' + - 'components) but got: null. ' + - 'Check your code at **.' + '(for built-in components) or a class/function (for composite ' + + 'components) but got: null. ' + + 'Check your code at **.', ); expect(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe( 'Warning: React.createElement: type is invalid -- expected a string ' + - '(for built-in components) or a class/function (for composite ' + - 'components) but got: boolean. ' + - 'Check your code at **.' + '(for built-in components) or a class/function (for composite ' + + 'components) but got: boolean. ' + + 'Check your code at **.', ); expect(normalizeCodeLocInfo(console.error.calls.argsFor(3)[0])).toBe( 'Warning: React.createElement: type is invalid -- expected a string ' + - '(for built-in components) or a class/function (for composite ' + - 'components) but got: number. ' + - 'Check your code at **.' + '(for built-in components) or a class/function (for composite ' + + 'components) but got: number. ' + + 'Check your code at **.', ); void
    ; expect(console.error.calls.count()).toBe(4); @@ -298,8 +298,8 @@ describe('ReactJSXElementValidator', () => { expect(console.error.calls.count()).toBe(1); expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( 'Warning: Failed prop type: The prop `prop` is marked as required in ' + - '`RequiredPropComponent`, but its value is `null`.\n' + - ' in RequiredPropComponent (at **)' + '`RequiredPropComponent`, but its value is `null`.\n' + + ' in RequiredPropComponent (at **)', ); }); @@ -311,8 +311,8 @@ describe('ReactJSXElementValidator', () => { expect(console.error.calls.count()).toBe(1); expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( 'Warning: Failed prop type: The prop `prop` is marked as required in ' + - '`RequiredPropComponent`, but its value is `null`.\n' + - ' in RequiredPropComponent (at **)' + '`RequiredPropComponent`, but its value is `null`.\n' + + ' in RequiredPropComponent (at **)', ); }); @@ -325,16 +325,16 @@ describe('ReactJSXElementValidator', () => { expect(console.error.calls.count()).toBe(2); expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( 'Warning: Failed prop type: ' + - 'The prop `prop` is marked as required in `RequiredPropComponent`, but ' + - 'its value is `undefined`.\n' + - ' in RequiredPropComponent (at **)' + 'The prop `prop` is marked as required in `RequiredPropComponent`, but ' + + 'its value is `undefined`.\n' + + ' in RequiredPropComponent (at **)', ); expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe( 'Warning: Failed prop type: ' + - 'Invalid prop `prop` of type `number` supplied to ' + - '`RequiredPropComponent`, expected `string`.\n' + - ' in RequiredPropComponent (at **)' + 'Invalid prop `prop` of type `number` supplied to ' + + '`RequiredPropComponent`, expected `string`.\n' + + ' in RequiredPropComponent (at **)', ); ReactTestUtils.renderIntoDocument(); @@ -361,7 +361,7 @@ describe('ReactJSXElementValidator', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( 'NullPropTypeComponent: prop type `prop` is invalid; it must be a ' + - 'function, usually from React.PropTypes.' + 'function, usually from React.PropTypes.', ); }); @@ -379,7 +379,7 @@ describe('ReactJSXElementValidator', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( 'NullContextTypeComponent: context type `prop` is invalid; it must ' + - 'be a function, usually from React.PropTypes.' + 'be a function, usually from React.PropTypes.', ); }); @@ -397,8 +397,58 @@ describe('ReactJSXElementValidator', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( 'getDefaultProps is only used on classic React.createClass definitions.' + - ' Use a static property named `defaultProps` instead.' + ' Use a static property named `defaultProps` instead.', ); }); + it('provides stack via non-standard console.reactStack for invalid types', () => { + spyOn(console, 'error'); + + function Foo() { + var Bad = undefined; + return ; + } + + function App() { + return
    ; + } + + try { + console.reactStack = jest.fn(); + console.reactStackEnd = jest.fn(); + + expect(() => { + ReactTestUtils.renderIntoDocument(); + }).toThrow( + 'Element type is invalid: expected a string (for built-in components) ' + + 'or a class/function (for composite components) but got: undefined. ' + + "You likely forgot to export your component from the file it's " + + 'defined in. Check the render method of `Foo`.', + ); + + expect(console.reactStack.mock.calls.length).toBe(1); + expect(console.reactStackEnd.mock.calls.length).toBe(1); + + var stack = console.reactStack.mock.calls[0][0]; + expect(Array.isArray(stack)).toBe(true); + expect(stack.map(frame => frame.name)).toEqual([ + 'Foo', // is inside Foo + 'App', // is inside App + 'App', //
    is inside App + null, // is outside a component + ]); + expect( + stack.map(frame => frame.fileName && frame.fileName.slice(-8)), + ).toEqual(['-test.js', '-test.js', '-test.js', '-test.js']); + expect(stack.map(frame => typeof frame.lineNumber)).toEqual([ + 'number', + 'number', + 'number', + 'number', + ]); + } finally { + delete console.reactStack; + delete console.reactStackEnd; + } + }); }); diff --git a/src/renderers/art/ReactART.js b/src/renderers/art/ReactART.js index be85e1f238..7550e330d2 100644 --- a/src/renderers/art/ReactART.js +++ b/src/renderers/art/ReactART.js @@ -12,7 +12,7 @@ 'use strict'; require('art/modes/current').setCurrent( - require('art/modes/fast-noSideEffects') // Flip this to DOM mode for debugging + require('art/modes/fast-noSideEffects'), // Flip this to DOM mode for debugging ); const Transform = require('art/core/transform'); @@ -24,6 +24,7 @@ const ReactInstanceMap = require('ReactInstanceMap'); const ReactMultiChild = require('ReactMultiChild'); const ReactUpdates = require('ReactUpdates'); +const createReactClass = require('createClass'); const emptyObject = require('emptyObject'); const invariant = require('invariant'); @@ -67,8 +68,10 @@ function createComponent(name) { */ function injectAfter(parentNode, referenceNode, node) { let beforeNode; - if (node.parentNode === parentNode && - node.previousSibling === referenceNode) { + if ( + node.parentNode === parentNode && + node.previousSibling === referenceNode + ) { return; } if (referenceNode == null) { @@ -83,7 +86,7 @@ function injectAfter(parentNode, referenceNode, node) { // checks and the behavior isn't well-defined. invariant( node !== beforeNode, - 'ReactART: Can not insert node before itself' + 'ReactART: Can not insert node before itself', ); node.injectBefore(beforeNode); } else if (node.parentNode !== parentNode) { @@ -94,7 +97,6 @@ function injectAfter(parentNode, referenceNode, node) { // ContainerMixin for components that can hold ART nodes const ContainerMixin = assign({}, ReactMultiChild.Mixin, { - /** * Moves a child component to the supplied index. * @@ -153,11 +155,7 @@ const ContainerMixin = assign({}, ReactMultiChild.Mixin, { // Shorthands mountAndInjectChildren: function(children, transaction, context) { - const mountedImages = this.mountChildren( - children, - transaction, - context - ); + const mountedImages = this.mountChildren(children, transaction, context); // Each mount image corresponds to one of the flattened children let i = 0; for (let key in this._renderedChildren) { @@ -168,15 +166,13 @@ const ContainerMixin = assign({}, ReactMultiChild.Mixin, { i++; } } - } - + }, }); // Surface is a React DOM Component, not an ART component. It serves as the // entry point into the ART reconciler. -const Surface = React.createClass({ - +const Surface = createReactClass({ displayName: 'Surface', mixins: [ContainerMixin], @@ -192,15 +188,17 @@ const Surface = React.createClass({ this, this.props.children, transaction, - ReactInstanceMap.get(this)._context + ReactInstanceMap.get(this)._context, ); ReactUpdates.ReactReconcileTransaction.release(transaction); }, componentDidUpdate: function(oldProps) { const node = this.node; - if (this.props.width != oldProps.width || - this.props.height != oldProps.height) { + if ( + this.props.width != oldProps.width || + this.props.height != oldProps.height + ) { node.resize(+this.props.width, +this.props.height); } @@ -210,7 +208,7 @@ const Surface = React.createClass({ this, this.props.children, transaction, - ReactInstanceMap.get(this)._context + ReactInstanceMap.get(this)._context, ); ReactUpdates.ReactReconcileTransaction.release(transaction); @@ -241,8 +239,7 @@ const Surface = React.createClass({ title={props.title} /> ); - } - + }, }); // Various nodes that can go into a surface @@ -253,11 +250,10 @@ const EventTypes = { onMouseOut: 'mouseout', onMouseUp: 'mouseup', onMouseDown: 'mousedown', - onClick: 'click' + onClick: 'click', }; const NodeMixin = { - construct: function(element) { this._currentElement = element; }, @@ -312,10 +308,12 @@ const NodeMixin = { applyNodeProps: function(oldProps, props) { const node = this.node; - const scaleX = props.scaleX != null ? props.scaleX : - props.scale != null ? props.scale : 1; - const scaleY = props.scaleY != null ? props.scaleY : - props.scale != null ? props.scale : 1; + const scaleX = props.scaleX != null + ? props.scaleX + : props.scale != null ? props.scale : 1; + const scaleY = props.scaleY != null + ? props.scaleY + : props.scale != null ? props.scale : 1; pooledTransform .transformTo(1, 0, 0, 1, 0, 0) @@ -327,9 +325,14 @@ const NodeMixin = { pooledTransform.transform(props.transform); } - if (node.xx !== pooledTransform.xx || node.yx !== pooledTransform.yx || - node.xy !== pooledTransform.xy || node.yy !== pooledTransform.yy || - node.x !== pooledTransform.x || node.y !== pooledTransform.y) { + if ( + node.xx !== pooledTransform.xx || + node.yx !== pooledTransform.yx || + node.xy !== pooledTransform.xy || + node.yy !== pooledTransform.yy || + node.x !== pooledTransform.x || + node.y !== pooledTransform.y + ) { node.transformTo(pooledTransform); } @@ -357,21 +360,19 @@ const NodeMixin = { mountComponentIntoNode: function(rootID, container) { throw new Error( 'You cannot render an ART component standalone. ' + - 'You need to wrap it in a Surface.' + 'You need to wrap it in a Surface.', ); - } - + }, }; // Group const Group = createComponent('Group', NodeMixin, ContainerMixin, { - mountComponent: function( transaction, nativeParent, nativeContainerInfo, - context + context, ) { this.node = Mode.Group(); const props = this._currentElement.props; @@ -397,55 +398,54 @@ const Group = createComponent('Group', NodeMixin, ContainerMixin, { unmountComponent: function() { this.destroyEventListeners(); this.unmountChildren(); - } - + }, }); // ClippingRectangle const ClippingRectangle = createComponent( - 'ClippingRectangle', NodeMixin, ContainerMixin, { + 'ClippingRectangle', + NodeMixin, + ContainerMixin, + { + mountComponent: function( + transaction, + nativeParent, + nativeContainerInfo, + context, + ) { + this.node = Mode.ClippingRectangle(); + const props = this._currentElement.props; + this.applyClippingProps(emptyObject, props); + this.mountAndInjectChildren(props.children, transaction, context); + return this.node; + }, - mountComponent: function( - transaction, - nativeParent, - nativeContainerInfo, - context - ) { - this.node = Mode.ClippingRectangle(); - const props = this._currentElement.props; - this.applyClippingProps(emptyObject, props); - this.mountAndInjectChildren(props.children, transaction, context); - return this.node; + receiveComponent: function(nextComponent, transaction, context) { + const props = nextComponent.props; + const oldProps = this._currentElement.props; + this.applyClippingProps(oldProps, props); + this.updateChildren(props.children, transaction, context); + this._currentElement = nextComponent; + }, + + applyClippingProps: function(oldProps, props) { + this.node.width = props.width; + this.node.height = props.height; + this.node.x = props.x; + this.node.y = props.y; + this.applyNodeProps(oldProps, props); + }, + + unmountComponent: function() { + this.destroyEventListeners(); + this.unmountChildren(); + }, }, - - receiveComponent: function(nextComponent, transaction, context) { - const props = nextComponent.props; - const oldProps = this._currentElement.props; - this.applyClippingProps(oldProps, props); - this.updateChildren(props.children, transaction, context); - this._currentElement = nextComponent; - }, - - applyClippingProps: function(oldProps, props) { - this.node.width = props.width; - this.node.height = props.height; - this.node.x = props.x; - this.node.y = props.y; - this.applyNodeProps(oldProps, props); - }, - - unmountComponent: function() { - this.destroyEventListeners(); - this.unmountChildren(); - } - -}); - +); // Renderables const RenderableMixin = assign({}, NodeMixin, { - applyRenderableProps: function(oldProps, props) { if (oldProps.fill !== props.fill) { if (props.fill && props.fill.applyFill) { @@ -468,7 +468,7 @@ const RenderableMixin = assign({}, NodeMixin, { props.strokeWidth, props.strokeCap, props.strokeJoin, - props.strokeDash + props.strokeDash, ); } this.applyNodeProps(oldProps, props); @@ -476,14 +476,12 @@ const RenderableMixin = assign({}, NodeMixin, { unmountComponent: function() { this.destroyEventListeners(); - } - + }, }); // Shape const Shape = createComponent('Shape', RenderableMixin, { - construct: function(element) { this._currentElement = element; this._oldDelta = null; @@ -494,7 +492,7 @@ const Shape = createComponent('Shape', RenderableMixin, { transaction, nativeParent, nativeContainerInfo, - context + context, ) { this.node = Mode.Shape(); const props = this._currentElement.props; @@ -514,30 +512,25 @@ const Shape = createComponent('Shape', RenderableMixin, { const oldPath = this._oldPath; const path = props.d || childrenAsString(props.children); - if (path.delta !== oldDelta || - path !== oldPath || - oldProps.width !== props.width || - oldProps.height !== props.height) { - - this.node.draw( - path, - props.width, - props.height - ); + if ( + path.delta !== oldDelta || + path !== oldPath || + oldProps.width !== props.width || + oldProps.height !== props.height + ) { + this.node.draw(path, props.width, props.height); this._oldPath = path; this._oldDelta = path.delta; } this.applyRenderableProps(oldProps, props); - } - + }, }); // Text const Text = createComponent('Text', RenderableMixin, { - construct: function(element) { this._currentElement = element; this._oldString = null; @@ -547,7 +540,7 @@ const Text = createComponent('Text', RenderableMixin, { transaction, nativeParent, nativeContainerInfo, - context + context, ) { const props = this._currentElement.props; const newString = childrenAsString(props.children); @@ -580,23 +573,19 @@ const Text = createComponent('Text', RenderableMixin, { const oldString = this._oldString; const newString = childrenAsString(props.children); - if (oldString !== newString || - !this.isSameFont(oldProps.font, props.font) || - oldProps.alignment !== props.alignment || - oldProps.path !== props.path) { - this.node.draw( - newString, - props.font, - props.alignment, - props.path - ); + if ( + oldString !== newString || + !this.isSameFont(oldProps.font, props.font) || + oldProps.alignment !== props.alignment || + oldProps.path !== props.path + ) { + this.node.draw(newString, props.font, props.alignment, props.path); this._oldString = newString; } this.applyRenderableProps(oldProps, props); this._currentElement = nextComponent; - } - + }, }); // Declarative fill type objects - API design not finalized diff --git a/src/renderers/art/__tests__/ReactART-test.js b/src/renderers/art/__tests__/ReactART-test.js index b8cfea1c24..7d9ce37ffe 100644 --- a/src/renderers/art/__tests__/ReactART-test.js +++ b/src/renderers/art/__tests__/ReactART-test.js @@ -49,7 +49,6 @@ function testDOMNodeStructure(domNode, expectedStructure) { } describe('ReactART', () => { - beforeEach(() => { ARTCurrentMode.setCurrent(ARTSVGMode); @@ -59,29 +58,33 @@ describe('ReactART', () => { TestComponent = class extends React.Component { render() { - - var a = + var a = ( ; + /> + ); - var b = + var b = ( M64.564,38.583H54l0.008-5.834c0-3.035,0.293-4.666,4.657-4.666 h5.833V16.429h-9.33c-11.213,0-15.159,5.654-15.159,15.16v6.994 h-6.99v11.652h6.99v33.815H54V50.235h9.331L64.564,38.583z - ; + + ); var c = ; @@ -113,22 +116,20 @@ describe('ReactART', () => { width: '150', height: '200', children: [ - { nodeName: 'defs' }, + {nodeName: 'defs'}, { nodeName: 'g', children: [ { nodeName: 'defs', - children: [ - { nodeName: 'linearGradient' } - ] + children: [{nodeName: 'linearGradient'}], }, - { nodeName: 'path' }, - { nodeName: 'path' }, - { nodeName: 'g' } - ] - } - ] + {nodeName: 'path'}, + {nodeName: 'path'}, + {nodeName: 'g'}, + ], + }, + ], }; var realNode = ReactDOM.findDOMNode(instance); @@ -137,22 +138,25 @@ describe('ReactART', () => { it('should be able to reorder components', () => { var container = document.createElement('div'); - var instance = ReactDOM.render(, container); + var instance = ReactDOM.render( + , + container, + ); var expectedStructure = { nodeName: 'svg', children: [ - { nodeName: 'defs' }, + {nodeName: 'defs'}, { nodeName: 'g', children: [ - { nodeName: 'defs' }, - { nodeName: 'path', opacity: '0.1' }, - { nodeName: 'path', opacity: Missing }, - { nodeName: 'g' } - ] - } - ] + {nodeName: 'defs'}, + {nodeName: 'path', opacity: '0.1'}, + {nodeName: 'path', opacity: Missing}, + {nodeName: 'g'}, + ], + }, + ], }; var realNode = ReactDOM.findDOMNode(instance); @@ -163,17 +167,17 @@ describe('ReactART', () => { var expectedNewStructure = { nodeName: 'svg', children: [ - { nodeName: 'defs' }, + {nodeName: 'defs'}, { nodeName: 'g', children: [ - { nodeName: 'defs' }, - { nodeName: 'path', opacity: Missing }, - { nodeName: 'path', opacity: '0.1' }, - { nodeName: 'g' } - ] - } - ] + {nodeName: 'defs'}, + {nodeName: 'path', opacity: Missing}, + {nodeName: 'path', opacity: '0.1'}, + {nodeName: 'g'}, + ], + }, + ], }; testDOMNodeStructure(realNode, expectedNewStructure); @@ -187,7 +191,7 @@ describe('ReactART', () => { var chars = this.props.chars.split(''); return ( - {chars.map((text) => )} + {chars.map(text => )} ); } @@ -225,7 +229,7 @@ describe('ReactART', () => { - + , ); expect(mounted).toBe(true); }); @@ -294,5 +298,4 @@ describe('ReactART', () => { ReactDOM.render(, container); expect(ref.constructor).toBe(CustomShape); }); - }); diff --git a/src/renderers/dom/ReactDOM.js b/src/renderers/dom/ReactDOM.js index ff39dc5aa4..d313e001cd 100644 --- a/src/renderers/dom/ReactDOM.js +++ b/src/renderers/dom/ReactDOM.js @@ -43,11 +43,11 @@ var ReactDOM = { // Allows for debugging when the hook is injected on the page. if ( typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' && - typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject === 'function') { + typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject === 'function' +) { __REACT_DEVTOOLS_GLOBAL_HOOK__.inject({ ComponentTree: { - getClosestInstanceFromNode: - ReactDOMComponentTree.getClosestInstanceFromNode, + getClosestInstanceFromNode: ReactDOMComponentTree.getClosestInstanceFromNode, getNodeFromInstance: function(inst) { // inst is an internal instance (but could be a composite) if (inst._renderedComponent) { @@ -68,21 +68,25 @@ if ( if (__DEV__) { var ExecutionEnvironment = require('ExecutionEnvironment'); if (ExecutionEnvironment.canUseDOM && window.top === window.self) { - // First check if devtools is not installed if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') { // If we're in Chrome or Firefox, provide a download link if not installed. - if ((navigator.userAgent.indexOf('Chrome') > -1 && + if ( + (navigator.userAgent.indexOf('Chrome') > -1 && navigator.userAgent.indexOf('Edge') === -1) || - navigator.userAgent.indexOf('Firefox') > -1) { + navigator.userAgent.indexOf('Firefox') > -1 + ) { // Firefox does not have the issue with devtools loaded over file:// - var showFileUrlMessage = window.location.protocol.indexOf('http') === -1 && + var showFileUrlMessage = + window.location.protocol.indexOf('http') === -1 && navigator.userAgent.indexOf('Firefox') === -1; console.debug( 'Download the React DevTools ' + - (showFileUrlMessage ? 'and use an HTTP server (instead of a file: URL) ' : '') + - 'for a better development experience: ' + - 'https://fb.me/react-devtools' + (showFileUrlMessage + ? 'and use an HTTP server (instead of a file: URL) ' + : '') + + 'for a better development experience: ' + + 'https://fb.me/react-devtools', ); } } @@ -90,10 +94,10 @@ if (__DEV__) { var testFunc = function testFn() {}; warning( (testFunc.name || testFunc.toString()).indexOf('testFn') !== -1, - 'It looks like you\'re using a minified copy of the development build ' + - 'of React. When deploying React apps to production, make sure to use ' + - 'the production build which skips development warnings and is faster. ' + - 'See https://fb.me/react-minification for more details.' + "It looks like you're using a minified copy of the development build " + + 'of React. When deploying React apps to production, make sure to use ' + + 'the production build which skips development warnings and is faster. ' + + 'See https://fb.me/react-minification for more details.', ); // If we're in IE8, check to see if we are in compatibility mode and provide @@ -104,8 +108,8 @@ if (__DEV__) { warning( !ieCompatibilityMode, 'Internet Explorer is running in compatibility mode; please add the ' + - 'following tag to your HTML to prevent this from happening: ' + - '' + 'following tag to your HTML to prevent this from happening: ' + + '', ); var expectedFeatures = [ @@ -126,7 +130,7 @@ if (__DEV__) { warning( false, 'One or more ES5 shims expected by React are not available: ' + - 'https://fb.me/react-warning-polyfills' + 'https://fb.me/react-warning-polyfills', ); break; } diff --git a/src/renderers/dom/__mocks__/ReactDOM.js b/src/renderers/dom/__mocks__/ReactDOM.js index 00d14eb7f1..c5965c9ec9 100644 --- a/src/renderers/dom/__mocks__/ReactDOM.js +++ b/src/renderers/dom/__mocks__/ReactDOM.js @@ -13,5 +13,6 @@ var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); var useFiber = ReactDOMFeatureFlags.useFiber; -module.exports = - useFiber ? require('ReactDOMFiber') : require.requireActual('ReactDOM'); +module.exports = useFiber + ? require('ReactDOMFiber') + : require.requireActual('ReactDOM'); diff --git a/src/renderers/dom/__tests__/ReactDOMProduction-test.js b/src/renderers/dom/__tests__/ReactDOMProduction-test.js index c25d5e7595..f2b4a7fc12 100644 --- a/src/renderers/dom/__tests__/ReactDOMProduction-test.js +++ b/src/renderers/dom/__tests__/ReactDOMProduction-test.js @@ -62,7 +62,7 @@ describe('ReactDOMProduction', () => { B C
    , - container + container, ); expect(container.firstChild).toBe(inst); @@ -75,7 +75,7 @@ describe('ReactDOMProduction', () => { A C
    , - container + container, ); expect(inst.className).toBe('red'); @@ -119,10 +119,7 @@ describe('ReactDOMProduction', () => { } var container = document.createElement('div'); - var inst = ReactDOM.render( - , - container - ); + var inst = ReactDOM.render(, container); expect(log).toEqual([ ['componentWillMount'], ['render'], @@ -140,15 +137,10 @@ describe('ReactDOMProduction', () => { log = []; inst.setState({y: 2}); - expect(log).toEqual([ - ['shouldComponentUpdate', {x: 1}, {y: 2}], - ]); + expect(log).toEqual([['shouldComponentUpdate', {x: 1}, {y: 2}]]); log = []; - ReactDOM.render( - , - container - ); + ReactDOM.render(, container); expect(log).toEqual([ ['componentWillReceiveProps', {x: 2}], ['shouldComponentUpdate', {x: 2}, {y: 2}], @@ -158,10 +150,7 @@ describe('ReactDOMProduction', () => { ]); log = []; - ReactDOM.render( - , - container - ); + ReactDOM.render(, container); expect(log).toEqual([ ['componentWillReceiveProps', {x: 2}], ['shouldComponentUpdate', {x: 2}, {y: 2}], @@ -169,9 +158,7 @@ describe('ReactDOMProduction', () => { log = []; ReactDOM.unmountComponentAtNode(container); - expect(log).toEqual([ - ['componentWillUnmount'], - ]); + expect(log).toEqual([['componentWillUnmount']]); }); it('should throw with an error code in production', () => { @@ -186,9 +173,9 @@ describe('ReactDOMProduction', () => { ReactDOM.render(, container); }).toThrowError( 'Minified React error #109; visit ' + - 'http://facebook.github.io/react/docs/error-decoder.html?invariant=109&args[]=Component' + - ' for the full message or use the non-minified dev environment' + - ' for full errors and additional helpful warnings.' + 'http://facebook.github.io/react/docs/error-decoder.html?invariant=109&args[]=Component' + + ' for the full message or use the non-minified dev environment' + + ' for full errors and additional helpful warnings.', ); }); }); diff --git a/src/renderers/dom/client/ReactBrowserEventEmitter.js b/src/renderers/dom/client/ReactBrowserEventEmitter.js index 0d6a3b7a46..abdd55a894 100644 --- a/src/renderers/dom/client/ReactBrowserEventEmitter.js +++ b/src/renderers/dom/client/ReactBrowserEventEmitter.js @@ -84,8 +84,10 @@ var reactTopListenersCounter = 0; var topEventMapping = { topAbort: 'abort', topAnimationEnd: getVendorPrefixedEventName('animationend') || 'animationend', - topAnimationIteration: getVendorPrefixedEventName('animationiteration') || 'animationiteration', - topAnimationStart: getVendorPrefixedEventName('animationstart') || 'animationstart', + topAnimationIteration: getVendorPrefixedEventName('animationiteration') || + 'animationiteration', + topAnimationStart: getVendorPrefixedEventName('animationstart') || + 'animationstart', topBlur: 'blur', topCanPlay: 'canplay', topCanPlayThrough: 'canplaythrough', @@ -142,7 +144,8 @@ var topEventMapping = { topTouchEnd: 'touchend', topTouchMove: 'touchmove', topTouchStart: 'touchstart', - topTransitionEnd: getVendorPrefixedEventName('transitionend') || 'transitionend', + topTransitionEnd: getVendorPrefixedEventName('transitionend') || + 'transitionend', topVolumeChange: 'volumechange', topWaiting: 'waiting', topWheel: 'wheel', @@ -174,7 +177,6 @@ function getListeningForDocument(mountAt) { * @internal */ var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, { - /** * Injectable event backend */ @@ -186,7 +188,7 @@ var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, { */ injectReactEventListener: function(ReactEventListener) { ReactEventListener.setHandleTopLevel( - ReactBrowserEventEmitter.handleTopLevel + ReactBrowserEventEmitter.handleTopLevel, ); ReactBrowserEventEmitter.ReactEventListener = ReactEventListener; }, @@ -207,10 +209,8 @@ var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, { * @return {boolean} True if callbacks are enabled. */ isEnabled: function() { - return !!( - ReactBrowserEventEmitter.ReactEventListener && - ReactBrowserEventEmitter.ReactEventListener.isEnabled() - ); + return !!(ReactBrowserEventEmitter.ReactEventListener && + ReactBrowserEventEmitter.ReactEventListener.isEnabled()); }, /** @@ -242,22 +242,21 @@ var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, { for (var i = 0; i < dependencies.length; i++) { var dependency = dependencies[i]; - if (!( - isListening.hasOwnProperty(dependency) && - isListening[dependency] - )) { + if ( + !(isListening.hasOwnProperty(dependency) && isListening[dependency]) + ) { if (dependency === 'topWheel') { if (isEventSupported('wheel')) { ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( 'topWheel', 'wheel', - mountAt + mountAt, ); } else if (isEventSupported('mousewheel')) { ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( 'topWheel', 'mousewheel', - mountAt + mountAt, ); } else { // Firefox needs to capture a different mouse scroll event. @@ -265,37 +264,34 @@ var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, { ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( 'topWheel', 'DOMMouseScroll', - mountAt + mountAt, ); } } else if (dependency === 'topScroll') { - if (isEventSupported('scroll', true)) { ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent( 'topScroll', 'scroll', - mountAt + mountAt, ); } else { ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( 'topScroll', 'scroll', - ReactBrowserEventEmitter.ReactEventListener.WINDOW_HANDLE + ReactBrowserEventEmitter.ReactEventListener.WINDOW_HANDLE, ); } - } else if (dependency === 'topFocus' || - dependency === 'topBlur') { - + } else if (dependency === 'topFocus' || dependency === 'topBlur') { if (isEventSupported('focus', true)) { ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent( 'topFocus', 'focus', - mountAt + mountAt, ); ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent( 'topBlur', 'blur', - mountAt + mountAt, ); } else if (isEventSupported('focusin')) { // IE has `focusin` and `focusout` events which bubble. @@ -303,12 +299,12 @@ var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, { ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( 'topFocus', 'focusin', - mountAt + mountAt, ); ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( 'topBlur', 'focusout', - mountAt + mountAt, ); } @@ -319,7 +315,7 @@ var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, { ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( dependency, topEventMapping[dependency], - mountAt + mountAt, ); } @@ -332,7 +328,7 @@ var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, { return ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( topLevelType, handlerBaseName, - handle + handle, ); }, @@ -340,7 +336,7 @@ var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, { return ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent( topLevelType, handlerBaseName, - handle + handle, ); }, @@ -378,7 +374,6 @@ var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, { isMonitoringScrollValue = true; } }, - }); module.exports = ReactBrowserEventEmitter; diff --git a/src/renderers/dom/client/ReactDOMComponentTree.js b/src/renderers/dom/client/ReactDOMComponentTree.js index d440446ace..88e2b4bf15 100644 --- a/src/renderers/dom/client/ReactDOMComponentTree.js +++ b/src/renderers/dom/client/ReactDOMComponentTree.js @@ -26,12 +26,12 @@ var internalInstanceKey = * Check if a given node should be cached. */ function shouldPrecacheNode(node, nodeID) { - return (node.nodeType === 1 && - node.getAttribute(ATTR_NAME) === String(nodeID)) || - (node.nodeType === 8 && - node.nodeValue === ' react-text: ' + nodeID + ' ') || - (node.nodeType === 8 && - node.nodeValue === ' react-empty: ' + nodeID + ' '); + return ( + (node.nodeType === 1 && node.getAttribute(ATTR_NAME) === String(nodeID)) || + (node.nodeType === 8 && + node.nodeValue === ' react-text: ' + nodeID + ' ') || + (node.nodeType === 8 && node.nodeValue === ' react-empty: ' + nodeID + ' ') + ); } /** @@ -166,7 +166,7 @@ function getNodeFromInstance(inst) { // invariant for a missing parent, which is super confusing. invariant( inst._hostNode !== undefined, - 'getNodeFromInstance: Invalid argument.' + 'getNodeFromInstance: Invalid argument.', ); if (inst._hostNode) { @@ -179,7 +179,7 @@ function getNodeFromInstance(inst) { parents.push(inst); invariant( inst._hostParent, - 'React DOM tree root should always have a node reference.' + 'React DOM tree root should always have a node reference.', ); inst = inst._hostParent; } diff --git a/src/renderers/dom/client/ReactDOMIDOperations.js b/src/renderers/dom/client/ReactDOMIDOperations.js index fd8c16797b..e24335341d 100644 --- a/src/renderers/dom/client/ReactDOMIDOperations.js +++ b/src/renderers/dom/client/ReactDOMIDOperations.js @@ -18,7 +18,6 @@ var ReactDOMComponentTree = require('ReactDOMComponentTree'); * Operations used to process updates to DOM nodes. */ var ReactDOMIDOperations = { - /** * Updates a component's children by processing a series of updates. * diff --git a/src/renderers/dom/client/ReactDOMSelection.js b/src/renderers/dom/client/ReactDOMSelection.js index aff9e894bb..307d5c340a 100644 --- a/src/renderers/dom/client/ReactDOMSelection.js +++ b/src/renderers/dom/client/ReactDOMSelection.js @@ -99,7 +99,7 @@ function getModernOffsets(node) { selection.anchorNode, selection.anchorOffset, selection.focusNode, - selection.focusOffset + selection.focusOffset, ); var rangeLength = isSelectionCollapsed ? 0 : currentRange.toString().length; @@ -112,7 +112,7 @@ function getModernOffsets(node) { tempRange.startContainer, tempRange.startOffset, tempRange.endContainer, - tempRange.endOffset + tempRange.endOffset, ); var start = isTempRangeCollapsed ? 0 : tempRange.toString().length; @@ -176,8 +176,7 @@ function setModernOffsets(node, offsets) { var selection = window.getSelection(); var length = node[getTextContentAccessor()].length; var start = Math.min(offsets.start, length); - var end = offsets.end === undefined ? - start : Math.min(offsets.end, length); + var end = offsets.end === undefined ? start : Math.min(offsets.end, length); // IE 11 uses modern selection, but doesn't support the extend method. // Flip backward selections, so we can set with a single range. @@ -205,11 +204,10 @@ function setModernOffsets(node, offsets) { } } -var useIEOffsets = ( +var useIEOffsets = ExecutionEnvironment.canUseDOM && 'selection' in document && - !('getSelection' in window) -); + !('getSelection' in window); var ReactDOMSelection = { /** diff --git a/src/renderers/dom/client/ReactDOMTreeTraversal.js b/src/renderers/dom/client/ReactDOMTreeTraversal.js index 72cb838035..4d809cfdbe 100644 --- a/src/renderers/dom/client/ReactDOMTreeTraversal.js +++ b/src/renderers/dom/client/ReactDOMTreeTraversal.js @@ -89,7 +89,7 @@ function traverseTwoPhase(inst, fn, arg) { inst = inst._hostParent; } var i; - for (i = path.length; i-- > 0;) { + for (i = path.length; i-- > 0; ) { fn(path[i], 'captured', arg); } for (i = 0; i < path.length; i++) { @@ -120,7 +120,7 @@ function traverseEnterLeave(from, to, fn, argFrom, argTo) { for (i = 0; i < pathFrom.length; i++) { fn(pathFrom[i], 'bubbled', argFrom); } - for (i = pathTo.length; i-- > 0;) { + for (i = pathTo.length; i-- > 0; ) { fn(pathTo[i], 'captured', argTo); } } diff --git a/src/renderers/dom/client/ReactEventListener.js b/src/renderers/dom/client/ReactEventListener.js index 8a329e9599..a885cb9b9c 100644 --- a/src/renderers/dom/client/ReactEventListener.js +++ b/src/renderers/dom/client/ReactEventListener.js @@ -52,13 +52,13 @@ Object.assign(TopLevelCallbackBookKeeping.prototype, { }); PooledClass.addPoolingTo( TopLevelCallbackBookKeeping, - PooledClass.twoArgumentPooler + PooledClass.twoArgumentPooler, ); function handleTopLevelImpl(bookKeeping) { var nativeEventTarget = getEventTarget(bookKeeping.nativeEvent); var targetInst = ReactDOMComponentTree.getClosestInstanceFromNode( - nativeEventTarget + nativeEventTarget, ); // Loop through the hierarchy, in case there's any nested components. @@ -77,7 +77,7 @@ function handleTopLevelImpl(bookKeeping) { bookKeeping.topLevelType, targetInst, bookKeeping.nativeEvent, - getEventTarget(bookKeeping.nativeEvent) + getEventTarget(bookKeeping.nativeEvent), ); } } @@ -105,7 +105,6 @@ var ReactEventListener = { return ReactEventListener._enabled; }, - /** * Traps top-level events by using event bubbling. * @@ -123,7 +122,7 @@ var ReactEventListener = { return EventListener.listen( element, handlerBaseName, - ReactEventListener.dispatchEvent.bind(null, topLevelType) + ReactEventListener.dispatchEvent.bind(null, topLevelType), ); }, @@ -144,7 +143,7 @@ var ReactEventListener = { return EventListener.capture( element, handlerBaseName, - ReactEventListener.dispatchEvent.bind(null, topLevelType) + ReactEventListener.dispatchEvent.bind(null, topLevelType), ); }, @@ -160,7 +159,7 @@ var ReactEventListener = { var bookKeeping = TopLevelCallbackBookKeeping.getPooled( topLevelType, - nativeEvent + nativeEvent, ); try { // Event queue being processed in the same cycle allows diff --git a/src/renderers/dom/client/ReactInputSelection.js b/src/renderers/dom/client/ReactInputSelection.js index 46e6931a6d..ee9d33ddce 100644 --- a/src/renderers/dom/client/ReactInputSelection.js +++ b/src/renderers/dom/client/ReactInputSelection.js @@ -28,13 +28,13 @@ function isInDocument(node) { * Input selection module for React. */ var ReactInputSelection = { - hasSelectionCapabilities: function(elem) { var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase(); - return nodeName && ( - (nodeName === 'input' && elem.type === 'text') || - nodeName === 'textarea' || - elem.contentEditable === 'true' + return ( + nodeName && + ((nodeName === 'input' && elem.type === 'text') || + nodeName === 'textarea' || + elem.contentEditable === 'true') ); }, @@ -42,10 +42,9 @@ var ReactInputSelection = { var focusedElem = getActiveElement(); return { focusedElem: focusedElem, - selectionRange: - ReactInputSelection.hasSelectionCapabilities(focusedElem) ? - ReactInputSelection.getSelection(focusedElem) : - null, + selectionRange: ReactInputSelection.hasSelectionCapabilities(focusedElem) + ? ReactInputSelection.getSelection(focusedElem) + : null, }; }, @@ -58,13 +57,9 @@ var ReactInputSelection = { var curFocusedElem = getActiveElement(); var priorFocusedElem = priorSelectionInformation.focusedElem; var priorSelectionRange = priorSelectionInformation.selectionRange; - if (curFocusedElem !== priorFocusedElem && - isInDocument(priorFocusedElem)) { + if (curFocusedElem !== priorFocusedElem && isInDocument(priorFocusedElem)) { if (ReactInputSelection.hasSelectionCapabilities(priorFocusedElem)) { - ReactInputSelection.setSelection( - priorFocusedElem, - priorSelectionRange - ); + ReactInputSelection.setSelection(priorFocusedElem, priorSelectionRange); } focusNode(priorFocusedElem); } @@ -85,8 +80,10 @@ var ReactInputSelection = { start: input.selectionStart, end: input.selectionEnd, }; - } else if (document.selection && - (input.nodeName && input.nodeName.toLowerCase() === 'input')) { + } else if ( + document.selection && + (input.nodeName && input.nodeName.toLowerCase() === 'input') + ) { // IE8 input. var range = document.selection.createRange(); // There can only be one selection per document in IE, so it must @@ -121,8 +118,10 @@ var ReactInputSelection = { if ('selectionStart' in input) { input.selectionStart = start; input.selectionEnd = Math.min(end, input.value.length); - } else if (document.selection && - (input.nodeName && input.nodeName.toLowerCase() === 'input')) { + } else if ( + document.selection && + (input.nodeName && input.nodeName.toLowerCase() === 'input') + ) { var range = input.createTextRange(); range.collapse(true); range.moveStart('character', start); diff --git a/src/renderers/dom/client/ReactMount.js b/src/renderers/dom/client/ReactMount.js index c0e3c9ec00..d68760d573 100644 --- a/src/renderers/dom/client/ReactMount.js +++ b/src/renderers/dom/client/ReactMount.js @@ -80,7 +80,7 @@ function internalGetID(node) { // If node is something like a window, document, or text node, none of // which support attributes or a .getAttribute method, gracefully return // the empty string, as if the attribute were missing. - return node.getAttribute && node.getAttribute(ATTR_NAME) || ''; + return (node.getAttribute && node.getAttribute(ATTR_NAME)) || ''; } /** @@ -96,16 +96,15 @@ function mountComponentIntoNode( container, transaction, shouldReuseMarkup, - context + context, ) { var markerName; if (ReactFeatureFlags.logTopLevelRenders) { var wrappedElement = wrapperInstance._currentElement.props.child; var type = wrappedElement.type; - markerName = 'React mount: ' + ( - typeof type === 'string' ? type : - type.displayName || type.name - ); + markerName = + 'React mount: ' + + (typeof type === 'string' ? type : type.displayName || type.name); console.time(markerName); } @@ -115,7 +114,7 @@ function mountComponentIntoNode( null, ReactDOMContainerInfo(wrapperInstance, container), context, - 0 /* parentDebugID */ + 0 /* parentDebugID */, ); if (markerName) { @@ -128,7 +127,7 @@ function mountComponentIntoNode( container, wrapperInstance, shouldReuseMarkup, - transaction + transaction, ); } @@ -143,11 +142,11 @@ function batchedMountComponentIntoNode( componentInstance, container, shouldReuseMarkup, - context + context, ) { var transaction = ReactUpdates.ReactReconcileTransaction.getPooled( /* useCreateElement */ - !shouldReuseMarkup && ReactDOMFeatureFlags.useCreateElement + !shouldReuseMarkup && ReactDOMFeatureFlags.useCreateElement, ); transaction.perform( mountComponentIntoNode, @@ -156,7 +155,7 @@ function batchedMountComponentIntoNode( container, transaction, shouldReuseMarkup, - context + context, ); ReactUpdates.ReactReconcileTransaction.release(transaction); } @@ -217,7 +216,9 @@ function hasNonRootReactChild(container) { */ function nodeIsRenderedByOtherInstance(container) { var rootEl = getReactRootElementInContainer(container); - return !!(rootEl && isReactNode(rootEl) && !ReactDOMComponentTree.getInstanceFromNode(rootEl)); + return !!(rootEl && + isReactNode(rootEl) && + !ReactDOMComponentTree.getInstanceFromNode(rootEl)); } /** @@ -228,11 +229,10 @@ function nodeIsRenderedByOtherInstance(container) { * @internal */ function isValidContainer(node) { - return !!(node && ( - node.nodeType === ELEMENT_NODE_TYPE || - node.nodeType === DOC_NODE_TYPE || - node.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE - )); + return !!(node && + (node.nodeType === ELEMENT_NODE_TYPE || + node.nodeType === DOC_NODE_TYPE || + node.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE)); } /** @@ -243,17 +243,19 @@ function isValidContainer(node) { * @internal */ function isReactNode(node) { - return isValidContainer(node) && (node.hasAttribute(ROOT_ATTR_NAME) || node.hasAttribute(ATTR_NAME)); + return ( + isValidContainer(node) && + (node.hasAttribute(ROOT_ATTR_NAME) || node.hasAttribute(ATTR_NAME)) + ); } function getHostRootInstanceInContainer(container) { var rootEl = getReactRootElementInContainer(container); var prevHostInstance = rootEl && ReactDOMComponentTree.getInstanceFromNode(rootEl); - return ( - prevHostInstance && !prevHostInstance._hostParent ? - prevHostInstance : null - ); + return prevHostInstance && !prevHostInstance._hostParent + ? prevHostInstance + : null; } function getTopLevelWrapperInContainer(container) { @@ -298,7 +300,6 @@ TopLevelWrapper.isReactTopLevelWrapper = true; * Inside of `container`, the first element rendered is the "reactRoot". */ var ReactMount = { - TopLevelWrapper: TopLevelWrapper, /** @@ -326,13 +327,18 @@ var ReactMount = { * @param {?function} callback function triggered on completion */ _updateRootComponent: function( - prevComponent, - nextElement, - nextContext, - container, - callback) { + prevComponent, + nextElement, + nextContext, + container, + callback, + ) { ReactMount.scrollMonitor(container, function() { - ReactUpdateQueue.enqueueElementInternal(prevComponent, nextElement, nextContext); + ReactUpdateQueue.enqueueElementInternal( + prevComponent, + nextElement, + nextContext, + ); if (callback) { ReactUpdateQueue.enqueueCallbackInternal(prevComponent, callback); } @@ -353,7 +359,7 @@ var ReactMount = { nextElement, container, shouldReuseMarkup, - context + context, ) { // Various parts of our code (such as ReactCompositeComponent's // _renderValidatedComponent) assume that calls to render aren't nested; @@ -361,16 +367,16 @@ var ReactMount = { warning( ReactCurrentOwner.current == null, '_renderNewRootComponent(): Render methods should be a pure function ' + - 'of props and state; triggering nested component updates from ' + - 'render is not allowed. If necessary, trigger nested updates in ' + - 'componentDidUpdate. Check the render method of %s.', - ReactCurrentOwner.current && ReactCurrentOwner.current.getName() || - 'ReactCompositeComponent' + 'of props and state; triggering nested component updates from ' + + 'render is not allowed. If necessary, trigger nested updates in ' + + 'componentDidUpdate. Check the render method of %s.', + (ReactCurrentOwner.current && ReactCurrentOwner.current.getName()) || + 'ReactCompositeComponent', ); invariant( isValidContainer(container), - '_registerComponent(...): Target container is not a DOM element.' + '_registerComponent(...): Target container is not a DOM element.', ); ReactBrowserEventEmitter.ensureScrollValueMonitoring(); @@ -385,7 +391,7 @@ var ReactMount = { componentInstance, container, shouldReuseMarkup, - context + context, ); var wrapperID = componentInstance._instance.rootID; @@ -407,53 +413,61 @@ var ReactMount = { * @param {?function} callback function triggered on completion * @return {ReactComponent} Component instance rendered in `container`. */ - renderSubtreeIntoContainer: function(parentComponent, nextElement, container, callback) { + renderSubtreeIntoContainer: function( + parentComponent, + nextElement, + container, + callback, + ) { invariant( parentComponent != null && ReactInstanceMap.has(parentComponent), - 'parentComponent must be a valid React Component' + 'parentComponent must be a valid React Component', ); return ReactMount._renderSubtreeIntoContainer( parentComponent, nextElement, container, - callback + callback, ); }, - _renderSubtreeIntoContainer: function(parentComponent, nextElement, container, callback) { + _renderSubtreeIntoContainer: function( + parentComponent, + nextElement, + container, + callback, + ) { ReactUpdateQueue.validateCallback(callback, 'ReactDOM.render'); invariant( React.isValidElement(nextElement), 'ReactDOM.render(): Invalid component element.%s', - ( - typeof nextElement === 'string' ? - ' Instead of passing a string like \'div\', pass ' + - 'React.createElement(\'div\') or
    .' : - typeof nextElement === 'function' ? - ' Instead of passing a class like Foo, pass ' + - 'React.createElement(Foo) or .' : - // Check if it quacks like an element - nextElement != null && nextElement.props !== undefined ? - ' This may be caused by unintentionally loading two independent ' + - 'copies of React.' : - '' - ) + typeof nextElement === 'string' + ? " Instead of passing a string like 'div', pass " + + "React.createElement('div') or
    ." + : typeof nextElement === 'function' + ? ' Instead of passing a class like Foo, pass ' + + 'React.createElement(Foo) or .' + : // Check if it quacks like an element + nextElement != null && nextElement.props !== undefined + ? ' This may be caused by unintentionally loading two independent ' + + 'copies of React.' + : '', ); warning( - !container || !container.tagName || - container.tagName.toUpperCase() !== 'BODY', + !container || + !container.tagName || + container.tagName.toUpperCase() !== 'BODY', 'render(): Rendering components directly into document.body is ' + - 'discouraged, since its children are often manipulated by third-party ' + - 'scripts and browser extensions. This may lead to subtle ' + - 'reconciliation issues. Try rendering into a container element created ' + - 'for your app.' + 'discouraged, since its children are often manipulated by third-party ' + + 'scripts and browser extensions. This may lead to subtle ' + + 'reconciliation issues. Try rendering into a container element created ' + + 'for your app.', ); - var nextWrappedElement = React.createElement( - TopLevelWrapper, - { child: nextElement } - ); + var nextWrappedElement = React.createElement(TopLevelWrapper, { + child: nextElement, + }); var nextContext; if (parentComponent) { @@ -470,15 +484,17 @@ var ReactMount = { var prevElement = prevWrappedElement.props.child; if (shouldUpdateReactComponent(prevElement, nextElement)) { var publicInst = prevComponent._renderedComponent.getPublicInstance(); - var updatedCallback = callback && function() { - callback.call(publicInst); - }; + var updatedCallback = + callback && + function() { + callback.call(publicInst); + }; ReactMount._updateRootComponent( prevComponent, nextWrappedElement, nextContext, container, - updatedCallback + updatedCallback, ); return publicInst; } else { @@ -495,9 +511,9 @@ var ReactMount = { warning( !containerHasNonRootReactChild, 'render(...): Replacing React-rendered children with a new root ' + - 'component. If you intended to update the children of this node, ' + - 'you should instead have the existing children update their state ' + - 'and render the new components instead of calling ReactDOM.render.' + 'component. If you intended to update the children of this node, ' + + 'you should instead have the existing children update their state ' + + 'and render the new components instead of calling ReactDOM.render.', ); if (!containerHasReactMarkup || reactRootElement.nextSibling) { @@ -507,8 +523,8 @@ var ReactMount = { warning( false, 'render(): Target node has markup rendered by React, but there ' + - 'are unrelated nodes as well. This is most commonly caused by ' + - 'white-space inserted around server-rendered markup.' + 'are unrelated nodes as well. This is most commonly caused by ' + + 'white-space inserted around server-rendered markup.', ); break; } @@ -525,7 +541,7 @@ var ReactMount = { nextWrappedElement, container, shouldReuseMarkup, - nextContext + nextContext, )._renderedComponent.getPublicInstance(); if (callback) { callback.call(component); @@ -533,7 +549,6 @@ var ReactMount = { return component; }, - /** * Renders a React component into the DOM in the supplied `container`. * See https://facebook.github.io/react/docs/top-level-api.html#reactdom.render @@ -548,7 +563,12 @@ var ReactMount = { * @return {ReactComponent} Component instance rendered in `container`. */ render: function(nextElement, container, callback) { - return ReactMount._renderSubtreeIntoContainer(null, nextElement, container, callback); + return ReactMount._renderSubtreeIntoContainer( + null, + nextElement, + container, + callback, + ); }, /** @@ -567,23 +587,23 @@ var ReactMount = { warning( ReactCurrentOwner.current == null, 'unmountComponentAtNode(): Render methods should be a pure function ' + - 'of props and state; triggering nested component updates from render ' + - 'is not allowed. If necessary, trigger nested updates in ' + - 'componentDidUpdate. Check the render method of %s.', - ReactCurrentOwner.current && ReactCurrentOwner.current.getName() || - 'ReactCompositeComponent' + 'of props and state; triggering nested component updates from render ' + + 'is not allowed. If necessary, trigger nested updates in ' + + 'componentDidUpdate. Check the render method of %s.', + (ReactCurrentOwner.current && ReactCurrentOwner.current.getName()) || + 'ReactCompositeComponent', ); invariant( isValidContainer(container), - 'unmountComponentAtNode(...): Target container is not a DOM element.' + 'unmountComponentAtNode(...): Target container is not a DOM element.', ); if (__DEV__) { warning( !nodeIsRenderedByOtherInstance(container), - 'unmountComponentAtNode(): The node you\'re attempting to unmount ' + - 'was rendered by another copy of React.' + "unmountComponentAtNode(): The node you're attempting to unmount " + + 'was rendered by another copy of React.', ); } @@ -600,15 +620,13 @@ var ReactMount = { if (__DEV__) { warning( !containerHasNonRootReactChild, - 'unmountComponentAtNode(): The node you\'re attempting to unmount ' + - 'was rendered by React and is not a top-level container. %s', - ( - isContainerReactRoot ? - 'You may have accidentally passed in a React root node instead ' + - 'of its container.' : - 'Instead, have the parent component update its state and ' + - 'rerender in order to remove this component.' - ) + "unmountComponentAtNode(): The node you're attempting to unmount " + + 'was rendered by React and is not a top-level container. %s', + isContainerReactRoot + ? 'You may have accidentally passed in a React root node instead ' + + 'of its container.' + : 'Instead, have the parent component update its state and ' + + 'rerender in order to remove this component.', ); } @@ -619,7 +637,7 @@ var ReactMount = { unmountComponentFromNode, prevComponent, container, - false + false, ); return true; }, @@ -629,11 +647,11 @@ var ReactMount = { container, instance, shouldReuseMarkup, - transaction + transaction, ) { invariant( isValidContainer(container), - 'mountComponentIntoNode(...): Target container is not valid.' + 'mountComponentIntoNode(...): Target container is not valid.', ); if (shouldReuseMarkup) { @@ -643,14 +661,14 @@ var ReactMount = { return; } else { var checksum = rootElement.getAttribute( - ReactMarkupChecksum.CHECKSUM_ATTR_NAME + ReactMarkupChecksum.CHECKSUM_ATTR_NAME, ); rootElement.removeAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME); var rootMarkup = rootElement.outerHTML; rootElement.setAttribute( ReactMarkupChecksum.CHECKSUM_ATTR_NAME, - checksum + checksum, ); var normalizedMarkup = markup; @@ -668,41 +686,44 @@ var ReactMount = { normalizer = document.createElement('iframe'); document.body.appendChild(normalizer); normalizer.contentDocument.write(markup); - normalizedMarkup = normalizer.contentDocument.documentElement.outerHTML; + normalizedMarkup = + normalizer.contentDocument.documentElement.outerHTML; document.body.removeChild(normalizer); } } var diffIndex = firstDifferenceIndex(normalizedMarkup, rootMarkup); - var difference = ' (client) ' + + var difference = + ' (client) ' + normalizedMarkup.substring(diffIndex - 20, diffIndex + 20) + - '\n (server) ' + rootMarkup.substring(diffIndex - 20, diffIndex + 20); + '\n (server) ' + + rootMarkup.substring(diffIndex - 20, diffIndex + 20); invariant( container.nodeType !== DOC_NODE_TYPE, - 'You\'re trying to render a component to the document using ' + - 'server rendering but the checksum was invalid. This usually ' + - 'means you rendered a different component type or props on ' + - 'the client from the one on the server, or your render() ' + - 'methods are impure. React cannot handle this case due to ' + - 'cross-browser quirks by rendering at the document root. You ' + - 'should look for environment dependent code in your components ' + - 'and ensure the props are the same client and server side:\n%s', - difference + "You're trying to render a component to the document using " + + 'server rendering but the checksum was invalid. This usually ' + + 'means you rendered a different component type or props on ' + + 'the client from the one on the server, or your render() ' + + 'methods are impure. React cannot handle this case due to ' + + 'cross-browser quirks by rendering at the document root. You ' + + 'should look for environment dependent code in your components ' + + 'and ensure the props are the same client and server side:\n%s', + difference, ); if (__DEV__) { warning( false, 'React attempted to reuse markup in a container but the ' + - 'checksum was invalid. This generally means that you are ' + - 'using server rendering and the markup generated on the ' + - 'server was not what the client was expecting. React injected ' + - 'new markup to compensate which works but you have lost many ' + - 'of the benefits of server rendering. Instead, figure out ' + - 'why the markup being generated is different on the client ' + - 'or server:\n%s', - difference + 'checksum was invalid. This generally means that you are ' + + 'using server rendering and the markup generated on the ' + + 'server was not what the client was expecting. React injected ' + + 'new markup to compensate which works but you have lost many ' + + 'of the benefits of server rendering. Instead, figure out ' + + 'why the markup being generated is different on the client ' + + 'or server:\n%s', + difference, ); } } @@ -710,10 +731,10 @@ var ReactMount = { invariant( container.nodeType !== DOC_NODE_TYPE, - 'You\'re trying to render a component to the document but ' + - 'you didn\'t use server rendering. We can\'t do this ' + + "You're trying to render a component to the document but " + + "you didn't use server rendering. We can't do this " + 'without using server rendering due to cross-browser quirks. ' + - 'See ReactDOMServer.renderToString() for server rendering.' + 'See ReactDOMServer.renderToString() for server rendering.', ); if (transaction.useCreateElement) { @@ -727,7 +748,9 @@ var ReactMount = { } if (__DEV__) { - var hostNode = ReactDOMComponentTree.getInstanceFromNode(container.firstChild); + var hostNode = ReactDOMComponentTree.getInstanceFromNode( + container.firstChild, + ); if (hostNode._debugID !== 0) { ReactInstrumentation.debugTool.onHostOperation({ instanceID: hostNode._debugID, diff --git a/src/renderers/dom/client/ReactReconcileTransaction.js b/src/renderers/dom/client/ReactReconcileTransaction.js index 8888dcb51a..4b3950bbf9 100644 --- a/src/renderers/dom/client/ReactReconcileTransaction.js +++ b/src/renderers/dom/client/ReactReconcileTransaction.js @@ -19,7 +19,6 @@ var ReactInstrumentation = require('ReactInstrumentation'); var Transaction = require('Transaction'); var ReactUpdateQueue = require('ReactUpdateQueue'); - /** * Ensures that, when possible, the selection range (currently selected text * input) is not disturbed by performing the transaction. @@ -174,7 +173,6 @@ var Mixin = { }, }; - Object.assign(ReactReconcileTransaction.prototype, Transaction, Mixin); PooledClass.addPoolingTo(ReactReconcileTransaction); diff --git a/src/renderers/dom/client/__tests__/ReactBrowserEventEmitter-test.js b/src/renderers/dom/client/__tests__/ReactBrowserEventEmitter-test.js index d76d4868a3..fb0f0619bc 100644 --- a/src/renderers/dom/client/__tests__/ReactBrowserEventEmitter-test.js +++ b/src/renderers/dom/client/__tests__/ReactBrowserEventEmitter-test.js @@ -54,7 +54,6 @@ function getInternal(node) { return ReactDOMComponentTree.getInstanceFromNode(node); } - describe('ReactBrowserEventEmitter', () => { beforeEach(() => { jest.resetModuleRegistry(); @@ -69,11 +68,11 @@ describe('ReactBrowserEventEmitter', () => { TapEventPlugin = require('TapEventPlugin'); ReactTestUtils.renderIntoDocument( -
    GRANDPARENT = c}> -
    PARENT = c}> -
    CHILD = c} /> +
    (GRANDPARENT = c)}> +
    (PARENT = c)}> +
    (CHILD = c)} />
    -
    +
    , ); idCallOrder = []; @@ -108,34 +107,31 @@ describe('ReactBrowserEventEmitter', () => { expect(LISTENER.mock.calls.length).toBe(1); }); - it( - 'should not invoke handlers if ReactBrowserEventEmitter is disabled', - () => { - registerSimpleTestHandler(); - ReactBrowserEventEmitter.setEnabled(false); - ReactTestUtils.SimulateNative.click(CHILD); - expect(LISTENER.mock.calls.length).toBe(0); - ReactBrowserEventEmitter.setEnabled(true); - ReactTestUtils.SimulateNative.click(CHILD); - expect(LISTENER.mock.calls.length).toBe(1); - } - ); + it('should not invoke handlers if ReactBrowserEventEmitter is disabled', () => { + registerSimpleTestHandler(); + ReactBrowserEventEmitter.setEnabled(false); + ReactTestUtils.SimulateNative.click(CHILD); + expect(LISTENER.mock.calls.length).toBe(0); + ReactBrowserEventEmitter.setEnabled(true); + ReactTestUtils.SimulateNative.click(CHILD); + expect(LISTENER.mock.calls.length).toBe(1); + }); it('should bubble simply', () => { EventPluginHub.putListener( getInternal(CHILD), ON_CLICK_KEY, - recordID.bind(null, getInternal(CHILD)) + recordID.bind(null, getInternal(CHILD)), ); EventPluginHub.putListener( getInternal(PARENT), ON_CLICK_KEY, - recordID.bind(null, getInternal(PARENT)) + recordID.bind(null, getInternal(PARENT)), ); EventPluginHub.putListener( getInternal(GRANDPARENT), ON_CLICK_KEY, - recordID.bind(null, getInternal(GRANDPARENT)) + recordID.bind(null, getInternal(GRANDPARENT)), ); ReactTestUtils.Simulate.click(CHILD); expect(idCallOrder.length).toBe(3); @@ -148,20 +144,16 @@ describe('ReactBrowserEventEmitter', () => { EventPluginHub.putListener( getInternal(CHILD), ON_CLICK_KEY, - recordID.bind(null, getInternal(CHILD)) - ); - EventPluginHub.putListener( - getInternal(PARENT), - ON_CLICK_KEY, - function() { - recordID(getInternal(PARENT)); - throw new Error('Handler interrupted'); - } + recordID.bind(null, getInternal(CHILD)), ); + EventPluginHub.putListener(getInternal(PARENT), ON_CLICK_KEY, function() { + recordID(getInternal(PARENT)); + throw new Error('Handler interrupted'); + }); EventPluginHub.putListener( getInternal(GRANDPARENT), ON_CLICK_KEY, - recordID.bind(null, getInternal(GRANDPARENT)) + recordID.bind(null, getInternal(GRANDPARENT)), ); expect(function() { ReactTestUtils.Simulate.click(CHILD); @@ -173,30 +165,24 @@ describe('ReactBrowserEventEmitter', () => { }); it('should set currentTarget', () => { - EventPluginHub.putListener( - getInternal(CHILD), - ON_CLICK_KEY, - function(event) { - recordID(getInternal(CHILD)); - expect(event.currentTarget).toBe(CHILD); - } - ); - EventPluginHub.putListener( - getInternal(PARENT), - ON_CLICK_KEY, - function(event) { - recordID(getInternal(PARENT)); - expect(event.currentTarget).toBe(PARENT); - } - ); - EventPluginHub.putListener( - getInternal(GRANDPARENT), - ON_CLICK_KEY, - function(event) { - recordID(getInternal(GRANDPARENT)); - expect(event.currentTarget).toBe(GRANDPARENT); - } - ); + EventPluginHub.putListener(getInternal(CHILD), ON_CLICK_KEY, function( + event, + ) { + recordID(getInternal(CHILD)); + expect(event.currentTarget).toBe(CHILD); + }); + EventPluginHub.putListener(getInternal(PARENT), ON_CLICK_KEY, function( + event, + ) { + recordID(getInternal(PARENT)); + expect(event.currentTarget).toBe(PARENT); + }); + EventPluginHub.putListener(getInternal(GRANDPARENT), ON_CLICK_KEY, function( + event, + ) { + recordID(getInternal(GRANDPARENT)); + expect(event.currentTarget).toBe(GRANDPARENT); + }); ReactTestUtils.Simulate.click(CHILD); expect(idCallOrder.length).toBe(3); expect(idCallOrder[0]).toBe(getInternal(CHILD)); @@ -208,17 +194,17 @@ describe('ReactBrowserEventEmitter', () => { EventPluginHub.putListener( getInternal(CHILD), ON_CLICK_KEY, - recordID.bind(null, getInternal(CHILD)) + recordID.bind(null, getInternal(CHILD)), ); EventPluginHub.putListener( getInternal(PARENT), ON_CLICK_KEY, - recordIDAndStopPropagation.bind(null, getInternal(PARENT)) + recordIDAndStopPropagation.bind(null, getInternal(PARENT)), ); EventPluginHub.putListener( getInternal(GRANDPARENT), ON_CLICK_KEY, - recordID.bind(null, getInternal(GRANDPARENT)) + recordID.bind(null, getInternal(GRANDPARENT)), ); ReactTestUtils.Simulate.click(CHILD); expect(idCallOrder.length).toBe(2); @@ -230,17 +216,17 @@ describe('ReactBrowserEventEmitter', () => { EventPluginHub.putListener( getInternal(CHILD), ON_CLICK_KEY, - recordIDAndStopPropagation.bind(null, getInternal(CHILD)) + recordIDAndStopPropagation.bind(null, getInternal(CHILD)), ); EventPluginHub.putListener( getInternal(PARENT), ON_CLICK_KEY, - recordID.bind(null, getInternal(PARENT)) + recordID.bind(null, getInternal(PARENT)), ); EventPluginHub.putListener( getInternal(GRANDPARENT), ON_CLICK_KEY, - recordID.bind(null, getInternal(GRANDPARENT)) + recordID.bind(null, getInternal(GRANDPARENT)), ); ReactTestUtils.Simulate.click(CHILD); expect(idCallOrder.length).toBe(1); @@ -251,17 +237,17 @@ describe('ReactBrowserEventEmitter', () => { EventPluginHub.putListener( getInternal(CHILD), ON_CLICK_KEY, - recordIDAndReturnFalse.bind(null, getInternal(CHILD)) + recordIDAndReturnFalse.bind(null, getInternal(CHILD)), ); EventPluginHub.putListener( getInternal(PARENT), ON_CLICK_KEY, - recordID.bind(null, getInternal(PARENT)) + recordID.bind(null, getInternal(PARENT)), ); EventPluginHub.putListener( getInternal(GRANDPARENT), ON_CLICK_KEY, - recordID.bind(null, getInternal(GRANDPARENT)) + recordID.bind(null, getInternal(GRANDPARENT)), ); spyOn(console, 'error'); ReactTestUtils.Simulate.click(CHILD); @@ -289,12 +275,12 @@ describe('ReactBrowserEventEmitter', () => { EventPluginHub.putListener( getInternal(CHILD), ON_CLICK_KEY, - handleChildClick + handleChildClick, ); EventPluginHub.putListener( getInternal(PARENT), ON_CLICK_KEY, - handleParentClick + handleParentClick, ); ReactTestUtils.Simulate.click(CHILD); expect(handleParentClick.mock.calls.length).toBe(1); @@ -306,13 +292,13 @@ describe('ReactBrowserEventEmitter', () => { EventPluginHub.putListener( getInternal(PARENT), ON_CLICK_KEY, - handleParentClick + handleParentClick, ); }; EventPluginHub.putListener( getInternal(CHILD), ON_CLICK_KEY, - handleChildClick + handleChildClick, ); ReactTestUtils.Simulate.click(CHILD); expect(handleParentClick.mock.calls.length).toBe(0); @@ -322,7 +308,7 @@ describe('ReactBrowserEventEmitter', () => { EventPluginHub.putListener( getInternal(CHILD), ON_MOUSE_ENTER_KEY, - recordID.bind(null, getInternal(CHILD)) + recordID.bind(null, getInternal(CHILD)), ); ReactTestUtils.Simulate.mouseEnter(CHILD); expect(idCallOrder.length).toBe(1); @@ -333,15 +319,15 @@ describe('ReactBrowserEventEmitter', () => { EventPluginHub.putListener( getInternal(CHILD), ON_TOUCH_TAP_KEY, - recordID.bind(null, getInternal(CHILD)) + recordID.bind(null, getInternal(CHILD)), ); ReactTestUtils.SimulateNative.touchStart( CHILD, - ReactTestUtils.nativeTouchData(0, 0) + ReactTestUtils.nativeTouchData(0, 0), ); ReactTestUtils.SimulateNative.touchEnd( CHILD, - ReactTestUtils.nativeTouchData(0, 0) + ReactTestUtils.nativeTouchData(0, 0), ); expect(idCallOrder.length).toBe(1); expect(idCallOrder[0]).toBe(getInternal(CHILD)); @@ -351,15 +337,15 @@ describe('ReactBrowserEventEmitter', () => { EventPluginHub.putListener( getInternal(CHILD), ON_TOUCH_TAP_KEY, - recordID.bind(null, getInternal(CHILD)) + recordID.bind(null, getInternal(CHILD)), ); ReactTestUtils.SimulateNative.touchStart( CHILD, - ReactTestUtils.nativeTouchData(0, 0) + ReactTestUtils.nativeTouchData(0, 0), ); ReactTestUtils.SimulateNative.touchEnd( CHILD, - ReactTestUtils.nativeTouchData(0, tapMoveThreshold - 1) + ReactTestUtils.nativeTouchData(0, tapMoveThreshold - 1), ); expect(idCallOrder.length).toBe(1); expect(idCallOrder[0]).toBe(getInternal(CHILD)); @@ -369,15 +355,15 @@ describe('ReactBrowserEventEmitter', () => { EventPluginHub.putListener( getInternal(CHILD), ON_TOUCH_TAP_KEY, - recordID.bind(null, getInternal(CHILD)) + recordID.bind(null, getInternal(CHILD)), ); ReactTestUtils.SimulateNative.touchStart( CHILD, - ReactTestUtils.nativeTouchData(0, 0) + ReactTestUtils.nativeTouchData(0, 0), ); ReactTestUtils.SimulateNative.touchEnd( CHILD, - ReactTestUtils.nativeTouchData(0, tapMoveThreshold + 1) + ReactTestUtils.nativeTouchData(0, tapMoveThreshold + 1), ); expect(idCallOrder.length).toBe(0); }); @@ -426,25 +412,25 @@ describe('ReactBrowserEventEmitter', () => { EventPluginHub.putListener( getInternal(CHILD), ON_TOUCH_TAP_KEY, - recordID.bind(null, getInternal(CHILD)) + recordID.bind(null, getInternal(CHILD)), ); EventPluginHub.putListener( getInternal(PARENT), ON_TOUCH_TAP_KEY, - recordID.bind(null, getInternal(PARENT)) + recordID.bind(null, getInternal(PARENT)), ); EventPluginHub.putListener( getInternal(GRANDPARENT), ON_TOUCH_TAP_KEY, - recordID.bind(null, getInternal(GRANDPARENT)) + recordID.bind(null, getInternal(GRANDPARENT)), ); ReactTestUtils.SimulateNative.touchStart( CHILD, - ReactTestUtils.nativeTouchData(0, 0) + ReactTestUtils.nativeTouchData(0, 0), ); ReactTestUtils.SimulateNative.touchEnd( CHILD, - ReactTestUtils.nativeTouchData(0, 0) + ReactTestUtils.nativeTouchData(0, 0), ); expect(idCallOrder.length).toBe(3); expect(idCallOrder[0]).toBe(getInternal(CHILD)); @@ -467,5 +453,4 @@ describe('ReactBrowserEventEmitter', () => { document.createEvent = originalCreateEvent; } }); - }); diff --git a/src/renderers/dom/client/__tests__/ReactDOM-test.js b/src/renderers/dom/client/__tests__/ReactDOM-test.js index 76bc51e85e..b55e913ffb 100644 --- a/src/renderers/dom/client/__tests__/ReactDOM-test.js +++ b/src/renderers/dom/client/__tests__/ReactDOM-test.js @@ -53,16 +53,14 @@ describe('ReactDOM', () => { }); it('should allow children to be passed as an argument', () => { - var argDiv = ReactTestUtils.renderIntoDocument( - div(null, 'child') - ); + var argDiv = ReactTestUtils.renderIntoDocument(div(null, 'child')); var argNode = ReactDOM.findDOMNode(argDiv); expect(argNode.innerHTML).toBe('child'); }); it('should overwrite props.children with children argument', () => { var conflictDiv = ReactTestUtils.renderIntoDocument( - div({children: 'fakechild'}, 'child') + div({children: 'fakechild'}, 'child'), ); var conflictNode = ReactDOM.findDOMNode(conflictDiv); expect(conflictNode.innerHTML).toBe('child'); @@ -77,45 +75,45 @@ describe('ReactDOM', () => {
    ,
    -
    +
    , ); // Warm the cache with theDog myDiv = ReactTestUtils.renderIntoDocument(
    ,
    , -
    +
    , ); // Remove theDog - this should purge the cache myDiv = ReactTestUtils.renderIntoDocument(
    , -
    +
    , ); // Now, put theDog back. It's now a different DOM node. myDiv = ReactTestUtils.renderIntoDocument(
    ,
    , -
    +
    , ); // Change the className of theDog. It will use the same element myDiv = ReactTestUtils.renderIntoDocument(
    ,
    , -
    +
    , ); var root = ReactDOM.findDOMNode(myDiv); var dog = root.childNodes[0]; expect(dog.className).toBe('bigdog'); }); - it('allow React.DOM factories to be called without warnings', () => { - spyOn(console, 'error'); + it('throws warning when React.DOM factories are called', () => { + spyOn(console, 'warn'); var element = React.DOM.div(); expect(element.type).toBe('div'); - expect(console.error.calls.count()).toBe(0); + expect(console.warn.calls.count()).toBe(1); }); it('throws in render() if the mount callback is not a function', () => { @@ -135,15 +133,15 @@ describe('ReactDOM', () => { var myDiv = document.createElement('div'); expect(() => ReactDOM.render(, myDiv, 'no')).toThrowError( 'ReactDOM.render(...): Expected the last optional `callback` argument ' + - 'to be a function. Instead received: string.' + 'to be a function. Instead received: string.', ); expect(() => ReactDOM.render(, myDiv, {})).toThrowError( 'ReactDOM.render(...): Expected the last optional `callback` argument ' + - 'to be a function. Instead received: Object.' + 'to be a function. Instead received: Object.', ); expect(() => ReactDOM.render(, myDiv, new Foo())).toThrowError( 'ReactDOM.render(...): Expected the last optional `callback` argument ' + - 'to be a function. Instead received: Foo (keys: a, b).' + 'to be a function. Instead received: Foo (keys: a, b).', ); }); @@ -166,15 +164,15 @@ describe('ReactDOM', () => { expect(() => ReactDOM.render(, myDiv, 'no')).toThrowError( 'ReactDOM.render(...): Expected the last optional `callback` argument ' + - 'to be a function. Instead received: string.' + 'to be a function. Instead received: string.', ); expect(() => ReactDOM.render(, myDiv, {})).toThrowError( 'ReactDOM.render(...): Expected the last optional `callback` argument ' + - 'to be a function. Instead received: Object.' + 'to be a function. Instead received: Object.', ); expect(() => ReactDOM.render(, myDiv, new Foo())).toThrowError( 'ReactDOM.render(...): Expected the last optional `callback` argument ' + - 'to be a function. Instead received: Foo (keys: a, b).' + 'to be a function. Instead received: Foo (keys: a, b).', ); }); }); diff --git a/src/renderers/dom/client/__tests__/ReactDOMComponentTree-test.js b/src/renderers/dom/client/__tests__/ReactDOMComponentTree-test.js index 71733578e0..f6ce6f4286 100644 --- a/src/renderers/dom/client/__tests__/ReactDOMComponentTree-test.js +++ b/src/renderers/dom/client/__tests__/ReactDOMComponentTree-test.js @@ -88,7 +88,7 @@ describe('ReactDOMComponentTree', () => { function renderAndGetClosest(sel) { return ReactDOMComponentTree.getClosestInstanceFromNode( - renderAndQuery(sel) + renderAndQuery(sel), ); } @@ -101,11 +101,12 @@ describe('ReactDOMComponentTree', () => { // This one's a text component! var root = renderAndQuery(null); - var inst = ReactDOMComponentTree.getInstanceFromNode(root.children[0].childNodes[2]); + var inst = ReactDOMComponentTree.getInstanceFromNode( + root.children[0].childNodes[2], + ); expect(inst._stringText).toBe('goodbye.'); expect(renderAndGetClosest('b')._currentElement.type).toBe('main'); expect(renderAndGetClosest('img')._currentElement.type).toBe('main'); }); - }); diff --git a/src/renderers/dom/client/__tests__/ReactDOMIDOperations-test.js b/src/renderers/dom/client/__tests__/ReactDOMIDOperations-test.js index dffd62dae4..1187c9bf5f 100644 --- a/src/renderers/dom/client/__tests__/ReactDOMIDOperations-test.js +++ b/src/renderers/dom/client/__tests__/ReactDOMIDOperations-test.js @@ -23,13 +23,15 @@ describe('ReactDOMIDOperations', () => { var html = '\n \t \n testContent \t \n \t'; ReactDOMIDOperations.dangerouslyProcessChildrenUpdates( stubInstance, - [{ - type: 'SET_MARKUP', - content: html, - fromIndex: null, - toIndex: null, - }], - [] + [ + { + type: 'SET_MARKUP', + content: html, + fromIndex: null, + toIndex: null, + }, + ], + [], ); expect(stubNode.innerHTML).toBe(html); diff --git a/src/renderers/dom/client/__tests__/ReactDOMSVG-test.js b/src/renderers/dom/client/__tests__/ReactDOMSVG-test.js index 30760d3ae8..d7904b0030 100644 --- a/src/renderers/dom/client/__tests__/ReactDOMSVG-test.js +++ b/src/renderers/dom/client/__tests__/ReactDOMSVG-test.js @@ -15,7 +15,6 @@ var React; var ReactDOMServer; describe('ReactDOMSVG', () => { - beforeEach(() => { React = require('React'); ReactDOMServer = require('ReactDOMServer'); @@ -25,9 +24,8 @@ describe('ReactDOMSVG', () => { var markup = ReactDOMServer.renderToString( - + , ); expect(markup).toContain('xlink:href="http://i.imgur.com/w7GCRPb.png"'); }); - }); diff --git a/src/renderers/dom/client/__tests__/ReactDOMTreeTraversal-test.js b/src/renderers/dom/client/__tests__/ReactDOMTreeTraversal-test.js index d68855d30c..ed40c81121 100644 --- a/src/renderers/dom/client/__tests__/ReactDOMTreeTraversal-test.js +++ b/src/renderers/dom/client/__tests__/ReactDOMTreeTraversal-test.js @@ -113,7 +113,11 @@ describe('ReactDOMTreeTraversal', () => { var target = null; var expectedAggregation = []; ReactDOMTreeTraversal.traverseEnterLeave( - target, target, argAggregator, ARG, ARG2 + target, + target, + argAggregator, + ARG, + ARG2, ); expect(aggregatedArgs).toEqual(expectedAggregation); }); @@ -124,7 +128,11 @@ describe('ReactDOMTreeTraversal', () => { var enter = getInst(parent.refs.P_P1_C1.refs.DIV_1); var expectedAggregation = []; ReactDOMTreeTraversal.traverseEnterLeave( - leave, enter, argAggregator, ARG, ARG2 + leave, + enter, + argAggregator, + ARG, + ARG2, ); expect(aggregatedArgs).toEqual(expectedAggregation); }); @@ -139,7 +147,11 @@ describe('ReactDOMTreeTraversal', () => { {node: parent.refs.P_P1_C1.refs.DIV_2, phase: 'captured', arg: ARG2}, ]; ReactDOMTreeTraversal.traverseEnterLeave( - leave, enter, argAggregator, ARG, ARG2 + leave, + enter, + argAggregator, + ARG, + ARG2, ); expect(aggregatedArgs).toEqual(expectedAggregation); }); @@ -152,7 +164,11 @@ describe('ReactDOMTreeTraversal', () => { {node: parent.refs.P_P1_C1.refs.DIV_1, phase: 'bubbled', arg: ARG}, ]; ReactDOMTreeTraversal.traverseEnterLeave( - leave, enter, argAggregator, ARG, ARG2 + leave, + enter, + argAggregator, + ARG, + ARG2, ); expect(aggregatedArgs).toEqual(expectedAggregation); }); @@ -167,7 +183,11 @@ describe('ReactDOMTreeTraversal', () => { {node: parent.refs.P_P1_C1.refs.DIV, phase: 'captured', arg: ARG2}, ]; ReactDOMTreeTraversal.traverseEnterLeave( - leave, enter, argAggregator, ARG, ARG2 + leave, + enter, + argAggregator, + ARG, + ARG2, ); expect(aggregatedArgs).toEqual(expectedAggregation); }); @@ -180,7 +200,11 @@ describe('ReactDOMTreeTraversal', () => { {node: parent.refs.P, phase: 'captured', arg: ARG2}, ]; ReactDOMTreeTraversal.traverseEnterLeave( - leave, enter, argAggregator, ARG, ARG2 + leave, + enter, + argAggregator, + ARG, + ARG2, ); expect(aggregatedArgs).toEqual(expectedAggregation); }); @@ -195,7 +219,11 @@ describe('ReactDOMTreeTraversal', () => { {node: parent.refs.P, phase: 'bubbled', arg: ARG}, ]; ReactDOMTreeTraversal.traverseEnterLeave( - leave, enter, argAggregator, ARG, ARG2 + leave, + enter, + argAggregator, + ARG, + ARG2, ); expect(aggregatedArgs).toEqual(expectedAggregation); }); @@ -210,7 +238,11 @@ describe('ReactDOMTreeTraversal', () => { {node: parent.refs.P, phase: 'bubbled', arg: ARG}, ]; ReactDOMTreeTraversal.traverseEnterLeave( - leave, enter, argAggregator, ARG, ARG2 + leave, + enter, + argAggregator, + ARG, + ARG2, ); expect(aggregatedArgs).toEqual(expectedAggregation); }); @@ -221,7 +253,8 @@ describe('ReactDOMTreeTraversal', () => { var parent = renderParentIntoDocument(); var ancestors = [ // Common ancestor with self is self. - {one: parent.refs.P_P1_C1.refs.DIV_1, + { + one: parent.refs.P_P1_C1.refs.DIV_1, two: parent.refs.P_P1_C1.refs.DIV_1, com: parent.refs.P_P1_C1.refs.DIV_1, }, @@ -263,11 +296,10 @@ describe('ReactDOMTreeTraversal', () => { var plan = ancestors[i]; var firstCommon = ReactDOMTreeTraversal.getLowestCommonAncestor( getInst(plan.one), - getInst(plan.two) + getInst(plan.two), ); expect(firstCommon).toBe(getInst(plan.com)); } }); }); - }); diff --git a/src/renderers/dom/client/__tests__/ReactEventIndependence-test.js b/src/renderers/dom/client/__tests__/ReactEventIndependence-test.js index 695e1927b1..ed5ce813f7 100644 --- a/src/renderers/dom/client/__tests__/ReactEventIndependence-test.js +++ b/src/renderers/dom/client/__tests__/ReactEventIndependence-test.js @@ -32,7 +32,7 @@ describe('ReactEventIndependence', () => { dangerouslySetInnerHTML={{ __html: '
    ', }} - /> + />, ); ReactTestUtils.SimulateNative.click(div.firstChild); expect(clicks).toBe(1); @@ -44,7 +44,7 @@ describe('ReactEventIndependence', () => { outer.setAttribute('data-reactid', '.z'); var inner = ReactDOM.render( , - outer + outer, ); ReactTestUtils.SimulateNative.click(inner); expect(clicks).toBe(1); @@ -55,7 +55,7 @@ describe('ReactEventIndependence', () => { var container = document.createElement('div'); var button = ReactDOM.render( , - container + container, ); // Now we unmount the component, as if caused by a non-React event handler @@ -66,5 +66,4 @@ describe('ReactEventIndependence', () => { // Since the tree is unmounted, we don't dispatch the click event. expect(clicks).toBe(0); }); - }); diff --git a/src/renderers/dom/client/__tests__/ReactEventListener-test.js b/src/renderers/dom/client/__tests__/ReactEventListener-test.js index f0fc24b9bc..598da2c6f1 100644 --- a/src/renderers/dom/client/__tests__/ReactEventListener-test.js +++ b/src/renderers/dom/client/__tests__/ReactEventListener-test.js @@ -11,7 +11,6 @@ 'use strict'; - var EVENT_TARGET_PARAM = 1; describe('ReactEventListener', () => { @@ -38,19 +37,16 @@ describe('ReactEventListener', () => { var otherNode = document.createElement('h1'); var component = ReactDOM.render(
    , document.createElement('div')); expect(handleTopLevel.mock.calls.length).toBe(0); - ReactEventListener.dispatchEvent( - 'topMouseOut', - { - type: 'mouseout', - fromElement: otherNode, - target: otherNode, - srcElement: otherNode, - toElement: ReactDOM.findDOMNode(component), - relatedTarget: ReactDOM.findDOMNode(component), - view: window, - path: [otherNode, otherNode], - }, - ); + ReactEventListener.dispatchEvent('topMouseOut', { + type: 'mouseout', + fromElement: otherNode, + target: otherNode, + srcElement: otherNode, + toElement: ReactDOM.findDOMNode(component), + relatedTarget: ReactDOM.findDOMNode(component), + view: window, + path: [otherNode, otherNode], + }); expect(handleTopLevel.mock.calls.length).toBe(1); }); @@ -61,8 +57,7 @@ describe('ReactEventListener', () => { var parentContainer = document.createElement('div'); var parentControl =
    Parent
    ; childControl = ReactDOM.render(childControl, childContainer); - parentControl = - ReactDOM.render(parentControl, parentContainer); + parentControl = ReactDOM.render(parentControl, parentContainer); ReactDOM.findDOMNode(parentControl).appendChild(childContainer); var callback = ReactEventListener.dispatchEvent.bind(null, 'test'); @@ -72,10 +67,12 @@ describe('ReactEventListener', () => { var calls = handleTopLevel.mock.calls; expect(calls.length).toBe(2); - expect(calls[0][EVENT_TARGET_PARAM]) - .toBe(ReactDOMComponentTree.getInstanceFromNode(childControl)); - expect(calls[1][EVENT_TARGET_PARAM]) - .toBe(ReactDOMComponentTree.getInstanceFromNode(parentControl)); + expect(calls[0][EVENT_TARGET_PARAM]).toBe( + ReactDOMComponentTree.getInstanceFromNode(childControl), + ); + expect(calls[1][EVENT_TARGET_PARAM]).toBe( + ReactDOMComponentTree.getInstanceFromNode(parentControl), + ); }); it('should propagate events two levels down', () => { @@ -86,10 +83,11 @@ describe('ReactEventListener', () => { var grandParentContainer = document.createElement('div'); var grandParentControl =
    Parent
    ; childControl = ReactDOM.render(childControl, childContainer); - parentControl = - ReactDOM.render(parentControl, parentContainer); - grandParentControl = - ReactDOM.render(grandParentControl, grandParentContainer); + parentControl = ReactDOM.render(parentControl, parentContainer); + grandParentControl = ReactDOM.render( + grandParentControl, + grandParentContainer, + ); ReactDOM.findDOMNode(parentControl).appendChild(childContainer); ReactDOM.findDOMNode(grandParentControl).appendChild(parentContainer); @@ -100,12 +98,15 @@ describe('ReactEventListener', () => { var calls = handleTopLevel.mock.calls; expect(calls.length).toBe(3); - expect(calls[0][EVENT_TARGET_PARAM]) - .toBe(ReactDOMComponentTree.getInstanceFromNode(childControl)); - expect(calls[1][EVENT_TARGET_PARAM]) - .toBe(ReactDOMComponentTree.getInstanceFromNode(parentControl)); - expect(calls[2][EVENT_TARGET_PARAM]) - .toBe(ReactDOMComponentTree.getInstanceFromNode(grandParentControl)); + expect(calls[0][EVENT_TARGET_PARAM]).toBe( + ReactDOMComponentTree.getInstanceFromNode(childControl), + ); + expect(calls[1][EVENT_TARGET_PARAM]).toBe( + ReactDOMComponentTree.getInstanceFromNode(parentControl), + ); + expect(calls[2][EVENT_TARGET_PARAM]).toBe( + ReactDOMComponentTree.getInstanceFromNode(grandParentControl), + ); }); it('should not get confused by disappearing elements', () => { @@ -114,8 +115,7 @@ describe('ReactEventListener', () => { var parentContainer = document.createElement('div'); var parentControl =
    Parent
    ; childControl = ReactDOM.render(childControl, childContainer); - parentControl = - ReactDOM.render(parentControl, parentContainer); + parentControl = ReactDOM.render(parentControl, parentContainer); ReactDOM.findDOMNode(parentControl).appendChild(childContainer); // ReactBrowserEventEmitter.handleTopLevel might remove the @@ -123,13 +123,16 @@ describe('ReactEventListener', () => { // node when the first event handlers are called; we'll still // expect to receive a second call for the parent control. var childNode = ReactDOM.findDOMNode(childControl); - handleTopLevel.mockImplementation( - function(topLevelType, topLevelTarget, topLevelTargetID, nativeEvent) { - if (topLevelTarget === childNode) { - ReactDOM.unmountComponentAtNode(childContainer); - } + handleTopLevel.mockImplementation(function( + topLevelType, + topLevelTarget, + topLevelTargetID, + nativeEvent, + ) { + if (topLevelTarget === childNode) { + ReactDOM.unmountComponentAtNode(childContainer); } - ); + }); var callback = ReactEventListener.dispatchEvent.bind(null, 'test'); callback({ @@ -138,41 +141,42 @@ describe('ReactEventListener', () => { var calls = handleTopLevel.mock.calls; expect(calls.length).toBe(2); - expect(calls[0][EVENT_TARGET_PARAM]) - .toBe(ReactDOMComponentTree.getInstanceFromNode(childNode)); - expect(calls[1][EVENT_TARGET_PARAM]) - .toBe(ReactDOMComponentTree.getInstanceFromNode(parentControl)); + expect(calls[0][EVENT_TARGET_PARAM]).toBe( + ReactDOMComponentTree.getInstanceFromNode(childNode), + ); + expect(calls[1][EVENT_TARGET_PARAM]).toBe( + ReactDOMComponentTree.getInstanceFromNode(parentControl), + ); }); it('should batch between handlers from different roots', () => { var childContainer = document.createElement('div'); var parentContainer = document.createElement('div'); - var childControl = ReactDOM.render( -
    Child
    , - childContainer - ); - var parentControl = ReactDOM.render( -
    Parent
    , - parentContainer - ); + var childControl = ReactDOM.render(
    Child
    , childContainer); + var parentControl = ReactDOM.render(
    Parent
    , parentContainer); ReactDOM.findDOMNode(parentControl).appendChild(childContainer); // Suppose an event handler in each root enqueues an update to the // childControl element -- the two updates should get batched together. var childNode = ReactDOM.findDOMNode(childControl); - handleTopLevel.mockImplementation( - function(topLevelType, topLevelTarget, topLevelTargetID, nativeEvent) { - ReactDOM.render( -
    {topLevelTarget === childNode ? '1' : '2'}
    , - childContainer - ); - // Since we're batching, neither update should yet have gone through. - expect(childNode.textContent).toBe('Child'); - } - ); + handleTopLevel.mockImplementation(function( + topLevelType, + topLevelTarget, + topLevelTargetID, + nativeEvent, + ) { + ReactDOM.render( +
    {topLevelTarget === childNode ? '1' : '2'}
    , + childContainer, + ); + // Since we're batching, neither update should yet have gone through. + expect(childNode.textContent).toBe('Child'); + }); - var callback = - ReactEventListener.dispatchEvent.bind(ReactEventListener, 'test'); + var callback = ReactEventListener.dispatchEvent.bind( + ReactEventListener, + 'test', + ); callback({ target: childNode, }); @@ -204,7 +208,8 @@ describe('ReactEventListener', () => { var calls = handleTopLevel.mock.calls; expect(calls.length).toBe(1); - expect(calls[0][EVENT_TARGET_PARAM]) - .toBe(ReactDOMComponentTree.getInstanceFromNode(instance.getInner())); + expect(calls[0][EVENT_TARGET_PARAM]).toBe( + ReactDOMComponentTree.getInstanceFromNode(instance.getInner()), + ); }); }); diff --git a/src/renderers/dom/client/__tests__/ReactMount-test.js b/src/renderers/dom/client/__tests__/ReactMount-test.js index d9d9e5eff1..d83bd1108b 100644 --- a/src/renderers/dom/client/__tests__/ReactMount-test.js +++ b/src/renderers/dom/client/__tests__/ReactMount-test.js @@ -45,7 +45,7 @@ describe('ReactMount', () => { expect(function() { ReactDOM.unmountComponentAtNode(nodeArray); }).toThrowError( - 'unmountComponentAtNode(...): Target container is not a DOM element.' + 'unmountComponentAtNode(...): Target container is not a DOM element.', ); }); }); @@ -55,7 +55,7 @@ describe('ReactMount', () => { ReactTestUtils.renderIntoDocument('div'); }).toThrowError( 'ReactDOM.render(): Invalid component element. Instead of passing a ' + - 'string like \'div\', pass React.createElement(\'div\') or
    .' + "string like 'div', pass React.createElement('div') or
    .", ); }); @@ -70,7 +70,7 @@ describe('ReactMount', () => { ReactTestUtils.renderIntoDocument(Component); }).toThrowError( 'ReactDOM.render(): Invalid component element. Instead of passing a ' + - 'class like Foo, pass React.createElement(Foo) or .' + 'class like Foo, pass React.createElement(Foo) or .', ); }); @@ -160,25 +160,26 @@ describe('ReactMount', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( - 'Rendering components directly into document.body is discouraged' + 'Rendering components directly into document.body is discouraged', ); }); it('should account for escaping on a checksum mismatch', () => { var div = document.createElement('div'); var markup = ReactDOMServer.renderToString( -
    This markup contains an nbsp entity:   server text
    ); +
    This markup contains an nbsp entity:   server text
    , + ); div.innerHTML = markup; spyOn(console, 'error'); ReactDOM.render(
    This markup contains an nbsp entity:   client text
    , - div + div, ); expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( ' (client) nbsp entity:   client text
    \n' + - ' (server) nbsp entity:   server text
    ' + ' (server) nbsp entity:   server text
    ', ); }); @@ -223,9 +224,9 @@ describe('ReactMount', () => { expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: render(...): Replacing React-rendered children with a new ' + - 'root component. If you intended to update the children of this node, ' + - 'you should instead have the existing children update their state and ' + - 'render the new components instead of calling ReactDOM.render.' + 'root component. If you intended to update the children of this node, ' + + 'you should instead have the existing children update their state and ' + + 'render the new components instead of calling ReactDOM.render.', ); }); @@ -248,8 +249,8 @@ describe('ReactMount', () => { ReactDOMOther.unmountComponentAtNode(container); expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( - 'Warning: unmountComponentAtNode(): The node you\'re attempting to unmount ' + - 'was rendered by another copy of React.' + "Warning: unmountComponentAtNode(): The node you're attempting to unmount " + + 'was rendered by another copy of React.', ); // Don't throw a warning if the correct React copy unmounts the node diff --git a/src/renderers/dom/client/__tests__/ReactMountDestruction-test.js b/src/renderers/dom/client/__tests__/ReactMountDestruction-test.js index 6068d99339..37cc4f5b9d 100644 --- a/src/renderers/dom/client/__tests__/ReactMountDestruction-test.js +++ b/src/renderers/dom/client/__tests__/ReactMountDestruction-test.js @@ -43,10 +43,11 @@ describe('ReactMount', () => { it('should warn when unmounting a non-container root node', () => { var mainContainerDiv = document.createElement('div'); - var component = + var component = (
    -
    ; +
    + ); ReactDOM.render(component, mainContainerDiv); // Test that unmounting at a root node gives a helpful warning @@ -55,22 +56,23 @@ describe('ReactMount', () => { ReactDOM.unmountComponentAtNode(rootDiv); expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( - 'Warning: unmountComponentAtNode(): The node you\'re attempting to ' + - 'unmount was rendered by React and is not a top-level container. You ' + - 'may have accidentally passed in a React root node instead of its ' + - 'container.' + "Warning: unmountComponentAtNode(): The node you're attempting to " + + 'unmount was rendered by React and is not a top-level container. You ' + + 'may have accidentally passed in a React root node instead of its ' + + 'container.', ); }); it('should warn when unmounting a non-container, non-root node', () => { var mainContainerDiv = document.createElement('div'); - var component = + var component = (
    -
    ; +
    + ); ReactDOM.render(component, mainContainerDiv); // Test that unmounting at a non-root node gives a different warning @@ -79,10 +81,10 @@ describe('ReactMount', () => { ReactDOM.unmountComponentAtNode(nonRootDiv); expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( - 'Warning: unmountComponentAtNode(): The node you\'re attempting to ' + - 'unmount was rendered by React and is not a top-level container. ' + - 'Instead, have the parent component update its state and rerender in ' + - 'order to remove this component.' + "Warning: unmountComponentAtNode(): The node you're attempting to " + + 'unmount was rendered by React and is not a top-level container. ' + + 'Instead, have the parent component update its state and rerender in ' + + 'order to remove this component.', ); }); }); diff --git a/src/renderers/dom/client/__tests__/ReactRenderDocument-test.js b/src/renderers/dom/client/__tests__/ReactRenderDocument-test.js index 473bf9c76b..8150a17f70 100644 --- a/src/renderers/dom/client/__tests__/ReactRenderDocument-test.js +++ b/src/renderers/dom/client/__tests__/ReactRenderDocument-test.js @@ -166,7 +166,7 @@ describe('rendering React components at document', () => { } var markup = ReactDOMServer.renderToString( - + , ); testDocument = getTestDocument(markup); @@ -194,7 +194,7 @@ describe('rendering React components at document', () => { } var markup = ReactDOMServer.renderToString( - + , ); testDocument = getTestDocument(markup); @@ -202,16 +202,16 @@ describe('rendering React components at document', () => { // Notice the text is different! ReactDOM.render(, testDocument); }).toThrowError( - 'You\'re trying to render a component to the document using ' + - 'server rendering but the checksum was invalid. This usually ' + - 'means you rendered a different component type or props on ' + - 'the client from the one on the server, or your render() methods ' + - 'are impure. React cannot handle this case due to cross-browser ' + - 'quirks by rendering at the document root. You should look for ' + - 'environment dependent code in your components and ensure ' + - 'the props are the same client and server side:\n' + - ' (client) dy data-reactid="4">Hello worldGoodbye world' + "You're trying to render a component to the document using " + + 'server rendering but the checksum was invalid. This usually ' + + 'means you rendered a different component type or props on ' + + 'the client from the one on the server, or your render() methods ' + + 'are impure. React cannot handle this case due to cross-browser ' + + 'quirks by rendering at the document root. You should look for ' + + 'environment dependent code in your components and ensure ' + + 'the props are the same client and server side:\n' + + ' (client) dy data-reactid="4">Hello worldGoodbye world', ); }); @@ -238,15 +238,15 @@ describe('rendering React components at document', () => { expect(function() { ReactDOM.render(, container); }).toThrowError( - 'You\'re trying to render a component to the document but you didn\'t ' + - 'use server rendering. We can\'t do this without using server ' + - 'rendering due to cross-browser quirks. See ' + - 'ReactDOMServer.renderToString() for server rendering.' + "You're trying to render a component to the document but you didn't " + + "use server rendering. We can't do this without using server " + + 'rendering due to cross-browser quirks. See ' + + 'ReactDOMServer.renderToString() for server rendering.', ); }); it('supports findDOMNode on full-page components', () => { - var tree = + var tree = ( Hello World @@ -254,7 +254,8 @@ describe('rendering React components at document', () => { Hello world - ; + + ); var markup = ReactDOMServer.renderToString(tree); testDocument = getTestDocument(markup); diff --git a/src/renderers/dom/client/__tests__/findDOMNode-test.js b/src/renderers/dom/client/__tests__/findDOMNode-test.js index 05b76d4fba..502e24fe70 100644 --- a/src/renderers/dom/client/__tests__/findDOMNode-test.js +++ b/src/renderers/dom/client/__tests__/findDOMNode-test.js @@ -38,7 +38,7 @@ describe('findDOMNode', () => { expect(function() { ReactDOM.findDOMNode({foo: 'bar'}); }).toThrowError( - 'Element appears to be neither ReactComponent nor DOMNode (keys: foo)' + 'Element appears to be neither ReactComponent nor DOMNode (keys: foo)', ); }); @@ -54,7 +54,7 @@ describe('findDOMNode', () => { ReactDOM.unmountComponentAtNode(container); expect(() => ReactDOM.findDOMNode(inst)).toThrowError( - 'findDOMNode was called on an unmounted component.' + 'findDOMNode was called on an unmounted component.', ); }); @@ -65,11 +65,10 @@ describe('findDOMNode', () => { } render() { - return
    ; + return
    ; } } - expect(() => ReactTestUtils.renderIntoDocument()).not.toThrow(); + expect(() => ReactTestUtils.renderIntoDocument()).not.toThrow(); }); - }); diff --git a/src/renderers/dom/client/__tests__/inputValueTracking-test.js b/src/renderers/dom/client/__tests__/inputValueTracking-test.js new file mode 100644 index 0000000000..103964cc1d --- /dev/null +++ b/src/renderers/dom/client/__tests__/inputValueTracking-test.js @@ -0,0 +1,158 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @emails react-core + */ +'use strict'; + +var React = require('React'); +var ReactTestUtils = require('ReactTestUtils'); +var inputValueTracking = require('inputValueTracking'); + +describe('inputValueTracking', function() { + var input, checkbox, mockComponent; + + beforeEach(function() { + input = document.createElement('input'); + input.type = 'text'; + checkbox = document.createElement('input'); + checkbox.type = 'checkbox'; + mockComponent = {_hostNode: input, _wrapperState: {}}; + }); + + it('should attach tracker to wrapper state', function() { + inputValueTracking.track(mockComponent); + + expect(mockComponent._wrapperState.hasOwnProperty('valueTracker')).toBe( + true, + ); + }); + + it('should define `value` on the instance node', function() { + inputValueTracking.track(mockComponent); + + expect(input.hasOwnProperty('value')).toBe(true); + }); + + it('should define `checked` on the instance node', function() { + mockComponent._hostNode = checkbox; + inputValueTracking.track(mockComponent); + + expect(checkbox.hasOwnProperty('checked')).toBe(true); + }); + + it('should initialize with the current value', function() { + input.value = 'foo'; + + inputValueTracking.track(mockComponent); + + var tracker = mockComponent._wrapperState.valueTracker; + + expect(tracker.getValue()).toEqual('foo'); + }); + + it('should initialize with the current `checked`', function() { + mockComponent._hostNode = checkbox; + checkbox.checked = true; + inputValueTracking.track(mockComponent); + + var tracker = mockComponent._wrapperState.valueTracker; + + expect(tracker.getValue()).toEqual('true'); + }); + + it('should track value changes', function() { + input.value = 'foo'; + + inputValueTracking.track(mockComponent); + + var tracker = mockComponent._wrapperState.valueTracker; + + input.value = 'bar'; + expect(tracker.getValue()).toEqual('bar'); + }); + + it('should tracked`checked` changes', function() { + mockComponent._hostNode = checkbox; + checkbox.checked = true; + inputValueTracking.track(mockComponent); + + var tracker = mockComponent._wrapperState.valueTracker; + + checkbox.checked = false; + expect(tracker.getValue()).toEqual('false'); + }); + + it('should update value manually', function() { + input.value = 'foo'; + inputValueTracking.track(mockComponent); + + var tracker = mockComponent._wrapperState.valueTracker; + + tracker.setValue('bar'); + expect(tracker.getValue()).toEqual('bar'); + }); + + it('should coerce value to a string', function() { + input.value = 'foo'; + inputValueTracking.track(mockComponent); + + var tracker = mockComponent._wrapperState.valueTracker; + + tracker.setValue(500); + expect(tracker.getValue()).toEqual('500'); + }); + + it('should update value if it changed and return result', function() { + inputValueTracking.track(mockComponent); + input.value = 'foo'; + + var tracker = mockComponent._wrapperState.valueTracker; + + expect(inputValueTracking.updateValueIfChanged(mockComponent)).toBe(false); + + tracker.setValue('bar'); + + expect(inputValueTracking.updateValueIfChanged(mockComponent)).toBe(true); + + expect(tracker.getValue()).toEqual('foo'); + }); + + it('should track value and return true when updating untracked instance', function() { + input.value = 'foo'; + + expect(inputValueTracking.updateValueIfChanged(mockComponent)).toBe(true); + + var tracker = mockComponent._wrapperState.valueTracker; + expect(tracker.getValue()).toEqual('foo'); + }); + + it('should return tracker from node', function() { + var node = ReactTestUtils.renderIntoDocument( + , + ); + var tracker = inputValueTracking._getTrackerFromNode(node); + expect(tracker.getValue()).toEqual('foo'); + }); + + it('should stop tracking', function() { + inputValueTracking.track(mockComponent); + + expect(mockComponent._wrapperState.hasOwnProperty('valueTracker')).toBe( + true, + ); + + inputValueTracking.stopTracking(mockComponent); + + expect(mockComponent._wrapperState.hasOwnProperty('valueTracker')).toBe( + false, + ); + + expect(input.hasOwnProperty('value')).toBe(false); + }); +}); diff --git a/src/renderers/dom/client/__tests__/validateDOMNesting-test.js b/src/renderers/dom/client/__tests__/validateDOMNesting-test.js index 5f0afa5cb9..97aac29a5f 100644 --- a/src/renderers/dom/client/__tests__/validateDOMNesting-test.js +++ b/src/renderers/dom/client/__tests__/validateDOMNesting-test.js @@ -15,22 +15,107 @@ var validateDOMNesting; // https://html.spec.whatwg.org/multipage/syntax.html#special var specialTags = [ - 'address', 'applet', 'area', 'article', 'aside', 'base', 'basefont', - 'bgsound', 'blockquote', 'body', 'br', 'button', 'caption', 'center', 'col', - 'colgroup', 'dd', 'details', 'dir', 'div', 'dl', 'dt', 'embed', 'fieldset', - 'figcaption', 'figure', 'footer', 'form', 'frame', 'frameset', 'h1', 'h2', - 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'iframe', - 'img', 'input', 'isindex', 'li', 'link', 'listing', 'main', 'marquee', 'menu', - 'menuitem', 'meta', 'nav', 'noembed', 'noframes', 'noscript', 'object', 'ol', - 'p', 'param', 'plaintext', 'pre', 'script', 'section', 'select', 'source', - 'style', 'summary', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', - 'th', 'thead', 'title', 'tr', 'track', 'ul', 'wbr', 'xmp', + 'address', + 'applet', + 'area', + 'article', + 'aside', + 'base', + 'basefont', + 'bgsound', + 'blockquote', + 'body', + 'br', + 'button', + 'caption', + 'center', + 'col', + 'colgroup', + 'dd', + 'details', + 'dir', + 'div', + 'dl', + 'dt', + 'embed', + 'fieldset', + 'figcaption', + 'figure', + 'footer', + 'form', + 'frame', + 'frameset', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'head', + 'header', + 'hgroup', + 'hr', + 'html', + 'iframe', + 'img', + 'input', + 'isindex', + 'li', + 'link', + 'listing', + 'main', + 'marquee', + 'menu', + 'menuitem', + 'meta', + 'nav', + 'noembed', + 'noframes', + 'noscript', + 'object', + 'ol', + 'p', + 'param', + 'plaintext', + 'pre', + 'script', + 'section', + 'select', + 'source', + 'style', + 'summary', + 'table', + 'tbody', + 'td', + 'template', + 'textarea', + 'tfoot', + 'th', + 'thead', + 'title', + 'tr', + 'track', + 'ul', + 'wbr', + 'xmp', ]; // https://html.spec.whatwg.org/multipage/syntax.html#formatting var formattingTags = [ - 'a', 'b', 'big', 'code', 'em', 'font', 'i', 'nobr', 's', 'small', 'strike', - 'strong', 'tt', 'u', + 'a', + 'b', + 'big', + 'code', + 'em', + 'font', + 'i', + 'nobr', + 's', + 'small', + 'strike', + 'strong', + 'tt', + 'u', ]; function isTagStackValid(stack) { @@ -39,8 +124,11 @@ function isTagStackValid(stack) { if (!validateDOMNesting.isTagValidInContext(stack[i], ancestorInfo)) { return false; } - ancestorInfo = - validateDOMNesting.updatedAncestorInfo(ancestorInfo, stack[i], null); + ancestorInfo = validateDOMNesting.updatedAncestorInfo( + ancestorInfo, + stack[i], + null, + ); } return true; } diff --git a/src/renderers/dom/client/eventPlugins/BeforeInputEventPlugin.js b/src/renderers/dom/client/eventPlugins/BeforeInputEventPlugin.js index e1163835fe..5f0df7e470 100644 --- a/src/renderers/dom/client/eventPlugins/BeforeInputEventPlugin.js +++ b/src/renderers/dom/client/eventPlugins/BeforeInputEventPlugin.js @@ -17,15 +17,13 @@ var FallbackCompositionState = require('FallbackCompositionState'); var SyntheticCompositionEvent = require('SyntheticCompositionEvent'); var SyntheticInputEvent = require('SyntheticInputEvent'); -import type { TopLevelTypes } from 'EventConstants'; +import type {TopLevelTypes} from 'EventConstants'; var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space var START_KEYCODE = 229; -var canUseCompositionEvent = ( - ExecutionEnvironment.canUseDOM && - 'CompositionEvent' in window -); +var canUseCompositionEvent = + ExecutionEnvironment.canUseDOM && 'CompositionEvent' in window; var documentMode = null; if (ExecutionEnvironment.canUseDOM && 'documentMode' in document) { @@ -35,23 +33,19 @@ if (ExecutionEnvironment.canUseDOM && 'documentMode' in document) { // Webkit offers a very useful `textInput` event that can be used to // directly represent `beforeInput`. The IE `textinput` event is not as // useful, so we don't use it. -var canUseTextInputEvent = ( +var canUseTextInputEvent = ExecutionEnvironment.canUseDOM && 'TextEvent' in window && !documentMode && - !isPresto() -); + !isPresto(); // In IE9+, we have access to composition events, but the data supplied // by the native compositionend event may be incorrect. Japanese ideographic // spaces, for instance (\u3000) are not recorded correctly. -var useFallbackCompositionData = ( +var useFallbackCompositionData = ExecutionEnvironment.canUseDOM && - ( - !canUseCompositionEvent || - (documentMode && documentMode > 8 && documentMode <= 11) - ) -); + (!canUseCompositionEvent || + (documentMode && documentMode > 8 && documentMode <= 11)); /** * Opera <= 12 includes TextEvent in window, but does not fire @@ -143,7 +137,6 @@ function isKeypressCommand(nativeEvent) { ); } - /** * Translate native top level events into event types. * @@ -170,10 +163,7 @@ function getCompositionEventType(topLevelType) { * @return {boolean} */ function isFallbackCompositionStart(topLevelType, nativeEvent) { - return ( - topLevelType === 'topKeyDown' && - nativeEvent.keyCode === START_KEYCODE - ); + return topLevelType === 'topKeyDown' && nativeEvent.keyCode === START_KEYCODE; } /** @@ -187,11 +177,11 @@ function isFallbackCompositionEnd(topLevelType, nativeEvent) { switch (topLevelType) { case 'topKeyUp': // Command keys insert or clear IME input. - return (END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1); + return END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1; case 'topKeyDown': // Expect IME keyCode on each keydown. If we get any other // code we must have exited earlier. - return (nativeEvent.keyCode !== START_KEYCODE); + return nativeEvent.keyCode !== START_KEYCODE; case 'topKeyPress': case 'topMouseDown': case 'topBlur': @@ -229,7 +219,7 @@ function extractCompositionEvent( topLevelType, targetInst, nativeEvent, - nativeEventTarget + nativeEventTarget, ) { var eventType; var fallbackData; @@ -252,8 +242,9 @@ function extractCompositionEvent( // The current composition is stored statically and must not be // overwritten while composition continues. if (!currentComposition && eventType === eventTypes.compositionStart) { - currentComposition = - FallbackCompositionState.getPooled(nativeEventTarget); + currentComposition = FallbackCompositionState.getPooled( + nativeEventTarget, + ); } else if (eventType === eventTypes.compositionEnd) { if (currentComposition) { fallbackData = currentComposition.getData(); @@ -265,7 +256,7 @@ function extractCompositionEvent( eventType, targetInst, nativeEvent, - nativeEventTarget + nativeEventTarget, ); if (fallbackData) { @@ -348,9 +339,11 @@ function getFallbackBeforeInputChars(topLevelType: TopLevelTypes, nativeEvent) { // If composition event is available, we extract a string only at // compositionevent, otherwise extract it at fallback events. if (currentComposition) { - if (topLevelType === 'topCompositionEnd' - || (!canUseCompositionEvent - && isFallbackCompositionEnd(topLevelType, nativeEvent))) { + if ( + topLevelType === 'topCompositionEnd' || + (!canUseCompositionEvent && + isFallbackCompositionEnd(topLevelType, nativeEvent)) + ) { var chars = currentComposition.getData(); FallbackCompositionState.release(currentComposition); currentComposition = null; @@ -402,7 +395,7 @@ function extractBeforeInputEvent( topLevelType, targetInst, nativeEvent, - nativeEventTarget + nativeEventTarget, ) { var chars; @@ -422,7 +415,7 @@ function extractBeforeInputEvent( eventTypes.beforeInput, targetInst, nativeEvent, - nativeEventTarget + nativeEventTarget, ); event.data = chars; @@ -449,27 +442,26 @@ function extractBeforeInputEvent( * `composition` event types. */ var BeforeInputEventPlugin = { - eventTypes: eventTypes, extractEvents: function( topLevelType, targetInst, nativeEvent, - nativeEventTarget + nativeEventTarget, ) { return [ extractCompositionEvent( topLevelType, targetInst, nativeEvent, - nativeEventTarget + nativeEventTarget, ), extractBeforeInputEvent( topLevelType, targetInst, nativeEvent, - nativeEventTarget + nativeEventTarget, ), ]; }, diff --git a/src/renderers/dom/client/eventPlugins/ChangeEventPlugin.js b/src/renderers/dom/client/eventPlugins/ChangeEventPlugin.js index e4a1f9b910..473aef5153 100644 --- a/src/renderers/dom/client/eventPlugins/ChangeEventPlugin.js +++ b/src/renderers/dom/client/eventPlugins/ChangeEventPlugin.js @@ -18,6 +18,7 @@ var ReactDOMComponentTree = require('ReactDOMComponentTree'); var ReactUpdates = require('ReactUpdates'); var SyntheticEvent = require('SyntheticEvent'); +var inputValueTracking = require('inputValueTracking'); var getEventTarget = require('getEventTarget'); var isEventSupported = require('isEventSupported'); var isTextInputElement = require('isTextInputElement'); @@ -41,13 +42,22 @@ var eventTypes = { }, }; +function createAndAccumulateChangeEvent(inst, nativeEvent, target) { + var event = SyntheticEvent.getPooled( + eventTypes.change, + inst, + nativeEvent, + target, + ); + event.type = 'change'; + EventPropagators.accumulateTwoPhaseDispatches(event); + return event; +} /** * For IE shims */ var activeElement = null; var activeElementInst = null; -var activeElementValue = null; -var activeElementValueProp = null; /** * SECTION: handle `change` event @@ -55,27 +65,24 @@ var activeElementValueProp = null; function shouldUseChangeEvent(elem) { var nodeName = elem.nodeName && elem.nodeName.toLowerCase(); return ( - nodeName === 'select' || - (nodeName === 'input' && elem.type === 'file') + nodeName === 'select' || (nodeName === 'input' && elem.type === 'file') ); } var doesChangeEventBubble = false; if (ExecutionEnvironment.canUseDOM) { // See `handleChange` comment below - doesChangeEventBubble = isEventSupported('change') && ( - !document.documentMode || document.documentMode > 8 - ); + doesChangeEventBubble = + isEventSupported('change') && + (!document.documentMode || document.documentMode > 8); } function manualDispatchChangeEvent(nativeEvent) { - var event = SyntheticEvent.getPooled( - eventTypes.change, + var event = createAndAccumulateChangeEvent( activeElementInst, nativeEvent, - getEventTarget(nativeEvent) + getEventTarget(nativeEvent), ); - EventPropagators.accumulateTwoPhaseDispatches(event); // If change and propertychange bubbled, we'd just bind to it like all the // other events and have it go through ReactBrowserEventEmitter. Since it @@ -111,19 +118,24 @@ function stopWatchingForChangeEventIE8() { activeElementInst = null; } -function getTargetInstForChangeEvent( - topLevelType, - targetInst -) { +function getInstIfValueChanged(targetInst, nativeEvent) { + var updated = inputValueTracking.updateValueIfChanged(targetInst); + var simulated = + nativeEvent.simulated === true && + ChangeEventPlugin._allowSimulatedPassThrough; + + if (updated || simulated) { + return targetInst; + } +} + +function getTargetInstForChangeEvent(topLevelType, targetInst) { if (topLevelType === 'topChange') { return targetInst; } } -function handleEventsForChangeEventIE8( - topLevelType, - target, - targetInst -) { + +function handleEventsForChangeEventIE8(topLevelType, target, targetInst) { if (topLevelType === 'topFocus') { // stopWatching() should be a noop here but we call it just in case we // missed a blur event somehow. @@ -134,7 +146,6 @@ function handleEventsForChangeEventIE8( } } - /** * SECTION: handle `input` event */ @@ -142,117 +153,56 @@ var isInputEventSupported = false; if (ExecutionEnvironment.canUseDOM) { // IE9 claims to support the input event but fails to trigger it when // deleting text, so we ignore its input events. - // IE10+ fire input events to often, such when a placeholder - // changes or when an input with a placeholder is focused. - isInputEventSupported = isEventSupported('input') && ( - !document.documentMode || document.documentMode > 11 - ); + + isInputEventSupported = + isEventSupported('input') && + (!('documentMode' in document) || document.documentMode > 9); } /** - * (For IE <=11) Replacement getter/setter for the `value` property that gets - * set on the active element. - */ -var newValueProp = { - get: function() { - return activeElementValueProp.get.call(this); - }, - set: function(val) { - // Cast to a string so we can do equality checks. - activeElementValue = '' + val; - activeElementValueProp.set.call(this, val); - }, -}; - -/** - * (For IE <=11) Starts tracking propertychange events on the passed-in element + * (For IE <=9) Starts tracking propertychange events on the passed-in element * and override the value property so that we can distinguish user events from * value changes in JS. */ function startWatchingForValueChange(target, targetInst) { activeElement = target; activeElementInst = targetInst; - activeElementValue = target.value; - activeElementValueProp = Object.getOwnPropertyDescriptor( - target.constructor.prototype, - 'value' - ); - - // Not guarded in a canDefineProperty check: IE8 supports defineProperty only - // on DOM elements - Object.defineProperty(activeElement, 'value', newValueProp); - if (activeElement.attachEvent) { - activeElement.attachEvent('onpropertychange', handlePropertyChange); - } else { - activeElement.addEventListener('propertychange', handlePropertyChange, false); - } + activeElement.attachEvent('onpropertychange', handlePropertyChange); } /** - * (For IE <=11) Removes the event listeners from the currently-tracked element, + * (For IE <=9) Removes the event listeners from the currently-tracked element, * if any exists. */ function stopWatchingForValueChange() { if (!activeElement) { return; } - - // delete restores the original property definition - delete activeElement.value; - - if (activeElement.detachEvent) { - activeElement.detachEvent('onpropertychange', handlePropertyChange); - } else { - activeElement.removeEventListener('propertychange', handlePropertyChange, false); - } + activeElement.detachEvent('onpropertychange', handlePropertyChange); activeElement = null; activeElementInst = null; - activeElementValue = null; - activeElementValueProp = null; } /** - * (For IE <=11) Handles a propertychange event, sending a `change` event if + * (For IE <=9) Handles a propertychange event, sending a `change` event if * the value of the active element has changed. */ function handlePropertyChange(nativeEvent) { if (nativeEvent.propertyName !== 'value') { return; } - var value = nativeEvent.srcElement.value; - if (value === activeElementValue) { - return; - } - activeElementValue = value; - - manualDispatchChangeEvent(nativeEvent); -} - -/** - * If a `change` event should be fired, returns the target's ID. - */ -function getTargetInstForInputEvent( - topLevelType, - targetInst -) { - if (topLevelType === 'topInput') { - // In modern browsers (i.e., not IE8 or IE9), the input event is exactly - // what we want so fall through here and trigger an abstract event - return targetInst; + if (getInstIfValueChanged(activeElementInst, nativeEvent)) { + manualDispatchChangeEvent(nativeEvent); } } -function handleEventsForInputEventIE( - topLevelType, - target, - targetInst -) { +function handleEventsForInputEventPolyfill(topLevelType, target, targetInst) { if (topLevelType === 'topFocus') { // In IE8, we can capture almost all .value changes by adding a // propertychange handler and looking for events with propertyName // equal to 'value' - // In IE9-11, propertychange fires for most input events but is buggy and + // In IE9, propertychange fires for most input events but is buggy and // doesn't fire when text is deleted, but conveniently, selectionchange // appears to fire in all of the remaining cases so we catch those and // forward the event if the value has changed @@ -270,13 +220,16 @@ function handleEventsForInputEventIE( } // For IE8 and IE9. -function getTargetInstForInputEventIE( +function getTargetInstForInputEventPolyfill( topLevelType, - targetInst + targetInst, + nativeEvent, ) { - if (topLevelType === 'topSelectionChange' || - topLevelType === 'topKeyUp' || - topLevelType === 'topKeyDown') { + if ( + topLevelType === 'topSelectionChange' || + topLevelType === 'topKeyUp' || + topLevelType === 'topKeyDown' + ) { // On the selectionchange event, the target is just document which isn't // helpful for us so just check activeElement instead. // @@ -287,14 +240,10 @@ function getTargetInstForInputEventIE( // keystroke if user does a key repeat (it'll be a little delayed: right // before the second keystroke). Other input methods (e.g., paste) seem to // fire selectionchange normally. - if (activeElement && activeElement.value !== activeElementValue) { - activeElementValue = activeElement.value; - return activeElementInst; - } + return getInstIfValueChanged(activeElementInst, nativeEvent); } } - /** * SECTION: handle `click` event */ @@ -302,18 +251,27 @@ function shouldUseClickEvent(elem) { // Use the `click` event to detect changes to checkbox and radio inputs. // This approach works across all browsers, whereas `change` does not fire // until `blur` in IE8. + var nodeName = elem.nodeName; return ( - (elem.nodeName && elem.nodeName.toLowerCase() === 'input') && + nodeName && + nodeName.toLowerCase() === 'input' && (elem.type === 'checkbox' || elem.type === 'radio') ); } -function getTargetInstForClickEvent( - topLevelType, - targetInst -) { +function getTargetInstForClickEvent(topLevelType, targetInst, nativeEvent) { if (topLevelType === 'topClick') { - return targetInst; + return getInstIfValueChanged(targetInst, nativeEvent); + } +} + +function getTargetInstForInputOrChangeEvent( + topLevelType, + targetInst, + nativeEvent, +) { + if (topLevelType === 'topInput' || topLevelType === 'topChange') { + return getInstIfValueChanged(targetInst, nativeEvent); } } @@ -348,17 +306,20 @@ function handleControlledInputBlur(inst, node) { * - select */ var ChangeEventPlugin = { - eventTypes: eventTypes, + _allowSimulatedPassThrough: true, + _isInputEventSupported: isInputEventSupported, + extractEvents: function( topLevelType, targetInst, nativeEvent, - nativeEventTarget + nativeEventTarget, ) { - var targetNode = targetInst ? - ReactDOMComponentTree.getNodeFromInstance(targetInst) : window; + var targetNode = targetInst + ? ReactDOMComponentTree.getNodeFromInstance(targetInst) + : window; var getTargetInstFunc, handleEventFunc; if (shouldUseChangeEvent(targetNode)) { @@ -369,36 +330,29 @@ var ChangeEventPlugin = { } } else if (isTextInputElement(targetNode)) { if (isInputEventSupported) { - getTargetInstFunc = getTargetInstForInputEvent; + getTargetInstFunc = getTargetInstForInputOrChangeEvent; } else { - getTargetInstFunc = getTargetInstForInputEventIE; - handleEventFunc = handleEventsForInputEventIE; + getTargetInstFunc = getTargetInstForInputEventPolyfill; + handleEventFunc = handleEventsForInputEventPolyfill; } } else if (shouldUseClickEvent(targetNode)) { getTargetInstFunc = getTargetInstForClickEvent; } if (getTargetInstFunc) { - var inst = getTargetInstFunc(topLevelType, targetInst); + var inst = getTargetInstFunc(topLevelType, targetInst, nativeEvent); if (inst) { - var event = SyntheticEvent.getPooled( - eventTypes.change, + var event = createAndAccumulateChangeEvent( inst, nativeEvent, - nativeEventTarget + nativeEventTarget, ); - event.type = 'change'; - EventPropagators.accumulateTwoPhaseDispatches(event); return event; } } if (handleEventFunc) { - handleEventFunc( - topLevelType, - targetNode, - targetInst - ); + handleEventFunc(topLevelType, targetNode, targetInst); } // When blurring, set the value attribute for number inputs @@ -406,7 +360,6 @@ var ChangeEventPlugin = { handleControlledInputBlur(targetInst, targetNode); } }, - }; module.exports = ChangeEventPlugin; diff --git a/src/renderers/dom/client/eventPlugins/EnterLeaveEventPlugin.js b/src/renderers/dom/client/eventPlugins/EnterLeaveEventPlugin.js index 9c0ebc6420..fa9925f144 100644 --- a/src/renderers/dom/client/eventPlugins/EnterLeaveEventPlugin.js +++ b/src/renderers/dom/client/eventPlugins/EnterLeaveEventPlugin.js @@ -18,22 +18,15 @@ var SyntheticMouseEvent = require('SyntheticMouseEvent'); var eventTypes = { mouseEnter: { registrationName: 'onMouseEnter', - dependencies: [ - 'topMouseOut', - 'topMouseOver', - ], + dependencies: ['topMouseOut', 'topMouseOver'], }, mouseLeave: { registrationName: 'onMouseLeave', - dependencies: [ - 'topMouseOut', - 'topMouseOver', - ], + dependencies: ['topMouseOut', 'topMouseOver'], }, }; var EnterLeaveEventPlugin = { - eventTypes: eventTypes, /** @@ -47,14 +40,15 @@ var EnterLeaveEventPlugin = { topLevelType, targetInst, nativeEvent, - nativeEventTarget + nativeEventTarget, ) { - if (topLevelType === 'topMouseOver' && - (nativeEvent.relatedTarget || nativeEvent.fromElement)) { + if ( + topLevelType === 'topMouseOver' && + (nativeEvent.relatedTarget || nativeEvent.fromElement) + ) { return null; } - if (topLevelType !== 'topMouseOut' && - topLevelType !== 'topMouseOver') { + if (topLevelType !== 'topMouseOut' && topLevelType !== 'topMouseOver') { // Must not be a mouse in or mouse out - ignoring. return null; } @@ -78,8 +72,9 @@ var EnterLeaveEventPlugin = { if (topLevelType === 'topMouseOut') { from = targetInst; var related = nativeEvent.relatedTarget || nativeEvent.toElement; - to = related ? - ReactDOMComponentTree.getClosestInstanceFromNode(related) : null; + to = related + ? ReactDOMComponentTree.getClosestInstanceFromNode(related) + : null; } else { // Moving to a node from outside the window. from = null; @@ -91,16 +86,18 @@ var EnterLeaveEventPlugin = { return null; } - var fromNode = - from == null ? win : ReactDOMComponentTree.getNodeFromInstance(from); - var toNode = - to == null ? win : ReactDOMComponentTree.getNodeFromInstance(to); + var fromNode = from == null + ? win + : ReactDOMComponentTree.getNodeFromInstance(from); + var toNode = to == null + ? win + : ReactDOMComponentTree.getNodeFromInstance(to); var leave = SyntheticMouseEvent.getPooled( eventTypes.mouseLeave, from, nativeEvent, - nativeEventTarget + nativeEventTarget, ); leave.type = 'mouseleave'; leave.target = fromNode; @@ -110,7 +107,7 @@ var EnterLeaveEventPlugin = { eventTypes.mouseEnter, to, nativeEvent, - nativeEventTarget + nativeEventTarget, ); enter.type = 'mouseenter'; enter.target = toNode; @@ -120,7 +117,6 @@ var EnterLeaveEventPlugin = { return [leave, enter]; }, - }; module.exports = EnterLeaveEventPlugin; diff --git a/src/renderers/dom/client/eventPlugins/SelectEventPlugin.js b/src/renderers/dom/client/eventPlugins/SelectEventPlugin.js index a4467c72a2..8b89dc377a 100644 --- a/src/renderers/dom/client/eventPlugins/SelectEventPlugin.js +++ b/src/renderers/dom/client/eventPlugins/SelectEventPlugin.js @@ -21,11 +21,10 @@ var getActiveElement = require('getActiveElement'); var isTextInputElement = require('isTextInputElement'); var shallowEqual = require('shallowEqual'); -var skipSelectionChangeEvent = ( +var skipSelectionChangeEvent = ExecutionEnvironment.canUseDOM && 'documentMode' in document && - document.documentMode <= 11 -); + document.documentMode <= 11; var eventTypes = { select: { @@ -65,8 +64,10 @@ var hasListener = false; * @return {object} */ function getSelection(node) { - if ('selectionStart' in node && - ReactInputSelection.hasSelectionCapabilities(node)) { + if ( + 'selectionStart' in node && + ReactInputSelection.hasSelectionCapabilities(node) + ) { return { start: node.selectionStart, end: node.selectionEnd, @@ -101,9 +102,11 @@ function constructSelectEvent(nativeEvent, nativeEventTarget) { // selection (this matches native `select` event behavior). In HTML5, select // fires only on input and textarea thus if there's no focused element we // won't dispatch. - if (mouseDown || - activeElement == null || - activeElement !== getActiveElement()) { + if ( + mouseDown || + activeElement == null || + activeElement !== getActiveElement() + ) { return null; } @@ -116,7 +119,7 @@ function constructSelectEvent(nativeEvent, nativeEventTarget) { eventTypes.select, activeElementInst, nativeEvent, - nativeEventTarget + nativeEventTarget, ); syntheticEvent.type = 'select'; @@ -145,27 +148,29 @@ function constructSelectEvent(nativeEvent, nativeEventTarget) { * - Fires after user input. */ var SelectEventPlugin = { - eventTypes: eventTypes, extractEvents: function( topLevelType, targetInst, nativeEvent, - nativeEventTarget + nativeEventTarget, ) { if (!hasListener) { return null; } - var targetNode = targetInst ? - ReactDOMComponentTree.getNodeFromInstance(targetInst) : window; + var targetNode = targetInst + ? ReactDOMComponentTree.getNodeFromInstance(targetInst) + : window; switch (topLevelType) { // Track the input node that has focus. case 'topFocus': - if (isTextInputElement(targetNode) || - targetNode.contentEditable === 'true') { + if ( + isTextInputElement(targetNode) || + targetNode.contentEditable === 'true' + ) { activeElement = targetNode; activeElementInst = targetInst; lastSelection = null; @@ -176,7 +181,6 @@ var SelectEventPlugin = { activeElementInst = null; lastSelection = null; break; - // Don't fire the event while the user is dragging. This matches the // semantics of the native select event. case 'topMouseDown': @@ -186,7 +190,6 @@ var SelectEventPlugin = { case 'topMouseUp': mouseDown = false; return constructSelectEvent(nativeEvent, nativeEventTarget); - // Chrome and IE fire non-standard event when selection is changed (and // sometimes when it hasn't). IE's event fires out of order with respect // to key and input events on deletion, so we discard it. @@ -200,7 +203,7 @@ var SelectEventPlugin = { if (skipSelectionChangeEvent) { break; } - // falls through + // falls through case 'topKeyDown': case 'topKeyUp': return constructSelectEvent(nativeEvent, nativeEventTarget); diff --git a/src/renderers/dom/client/eventPlugins/SimpleEventPlugin.js b/src/renderers/dom/client/eventPlugins/SimpleEventPlugin.js index 4479b44762..6acaf74a5a 100644 --- a/src/renderers/dom/client/eventPlugins/SimpleEventPlugin.js +++ b/src/renderers/dom/client/eventPlugins/SimpleEventPlugin.js @@ -37,10 +37,7 @@ import type { ReactSyntheticEvent, } from 'ReactSyntheticEventType'; import type {ReactInstance} from 'ReactInstanceType'; -import type { - EventTypes, - PluginModule, -} from 'PluginModuleType'; +import type {EventTypes, PluginModule} from 'PluginModuleType'; /** * Turns @@ -151,13 +148,14 @@ function getDictionaryKey(inst: ReactInstance): string { function isInteractive(tag) { return ( - tag === 'button' || tag === 'input' || - tag === 'select' || tag === 'textarea' + tag === 'button' || + tag === 'input' || + tag === 'select' || + tag === 'textarea' ); } var SimpleEventPlugin: PluginModule = { - eventTypes: eventTypes, extractEvents: function( @@ -211,7 +209,7 @@ var SimpleEventPlugin: PluginModule = { if (getEventCharCode(nativeEvent) === 0) { return null; } - /* falls through */ + /* falls through */ case 'topKeyDown': case 'topKeyUp': EventConstructor = SyntheticKeyboardEvent; @@ -226,13 +224,13 @@ var SimpleEventPlugin: PluginModule = { if (nativeEvent.button === 2) { return null; } - /* falls through */ + /* falls through */ case 'topDoubleClick': case 'topMouseDown': case 'topMouseMove': case 'topMouseUp': - // TODO: Disabled elements should not respond to mouse events - /* falls through */ + // TODO: Disabled elements should not respond to mouse events + /* falls through */ case 'topMouseOut': case 'topMouseOver': case 'topContextMenu': @@ -277,13 +275,13 @@ var SimpleEventPlugin: PluginModule = { invariant( EventConstructor, 'SimpleEventPlugin: Unhandled event type, `%s`.', - topLevelType + topLevelType, ); var event = EventConstructor.getPooled( dispatchConfig, targetInst, nativeEvent, - nativeEventTarget + nativeEventTarget, ); EventPropagators.accumulateTwoPhaseDispatches(event); return event; @@ -306,7 +304,7 @@ var SimpleEventPlugin: PluginModule = { onClickListeners[key] = EventListener.listen( node, 'click', - emptyFunction + emptyFunction, ); } } @@ -322,7 +320,6 @@ var SimpleEventPlugin: PluginModule = { delete onClickListeners[key]; } }, - }; module.exports = SimpleEventPlugin; diff --git a/src/renderers/dom/client/eventPlugins/TapEventPlugin.js b/src/renderers/dom/client/eventPlugins/TapEventPlugin.js index 2bf0c2fa3d..a5b46a07eb 100644 --- a/src/renderers/dom/client/eventPlugins/TapEventPlugin.js +++ b/src/renderers/dom/client/eventPlugins/TapEventPlugin.js @@ -21,10 +21,7 @@ var ViewportMetrics = require('ViewportMetrics'); var isStartish = EventPluginUtils.isStartish; var isEndish = EventPluginUtils.isEndish; -import type { - EventTypes, - PluginModule, -} from 'PluginModuleType'; +import type {EventTypes, PluginModule} from 'PluginModuleType'; import type {ReactInstance} from 'ReactInstanceType'; import type {TopLevelTypes} from 'EventConstants'; @@ -35,14 +32,10 @@ import type {TopLevelTypes} from 'EventConstants'; * "Indexable signature not found in Touch". * See https://github.com/facebook/flow/issues/1323 */ -type TouchPropertyKey = - 'clientX' | - 'clientY' | - 'pageX' | - 'pageY'; +type TouchPropertyKey = 'clientX' | 'clientY' | 'pageX' | 'pageY'; declare class _Touch extends Touch { - [key: TouchPropertyKey]: number; + [key: TouchPropertyKey]: number, } type AxisCoordinateData = { @@ -81,20 +74,17 @@ function getAxisCoordOfEvent( if (singleTouch) { return singleTouch[axis.page]; } - return axis.page in nativeEvent ? - nativeEvent[axis.page] : - nativeEvent[axis.client] + ViewportMetrics[axis.envScroll]; + return axis.page in nativeEvent + ? nativeEvent[axis.page] + : nativeEvent[axis.client] + ViewportMetrics[axis.envScroll]; } -function getDistance( - coords: CoordinatesType, - nativeEvent: _Touch, -): number { +function getDistance(coords: CoordinatesType, nativeEvent: _Touch): number { var pageX = getAxisCoordOfEvent(Axis.x, nativeEvent); var pageY = getAxisCoordOfEvent(Axis.y, nativeEvent); return Math.pow( Math.pow(pageX - coords.x, 2) + Math.pow(pageY - coords.y, 2), - 0.5 + 0.5, ); } @@ -105,11 +95,9 @@ var touchEvents = [ 'topTouchMove', ]; -var dependencies = [ - 'topMouseDown', - 'topMouseMove', - 'topMouseUp', -].concat(touchEvents); +var dependencies = ['topMouseDown', 'topMouseMove', 'topMouseUp'].concat( + touchEvents, +); var eventTypes: EventTypes = { touchTap: { @@ -126,7 +114,6 @@ var usedTouchTime = 0; var TOUCH_DELAY = 1000; var TapEventPlugin: PluginModule<_Touch> = { - tapMoveThreshold: tapMoveThreshold, eventTypes: eventTypes, @@ -147,7 +134,7 @@ var TapEventPlugin: PluginModule<_Touch> = { usedTouch = true; usedTouchTime = Date.now(); } else { - if (usedTouch && (Date.now() - usedTouchTime < TOUCH_DELAY)) { + if (usedTouch && Date.now() - usedTouchTime < TOUCH_DELAY) { return null; } } @@ -158,7 +145,7 @@ var TapEventPlugin: PluginModule<_Touch> = { eventTypes.touchTap, targetInst, nativeEvent, - nativeEventTarget + nativeEventTarget, ); } if (isStartish(topLevelType)) { @@ -171,7 +158,6 @@ var TapEventPlugin: PluginModule<_Touch> = { EventPropagators.accumulateTwoPhaseDispatches(event); return event; }, - }; module.exports = TapEventPlugin; diff --git a/src/renderers/dom/client/eventPlugins/__tests__/BeforeInputEventPlugin-test.js b/src/renderers/dom/client/eventPlugins/__tests__/BeforeInputEventPlugin-test.js index 5d2ec7ab88..265ca820bc 100644 --- a/src/renderers/dom/client/eventPlugins/__tests__/BeforeInputEventPlugin-test.js +++ b/src/renderers/dom/client/eventPlugins/__tests__/BeforeInputEventPlugin-test.js @@ -15,12 +15,12 @@ var React = require('React'); var ReactTestUtils = require('ReactTestUtils'); var EventMapping = { - compositionstart : 'topCompositionStart', - compositionend : 'topCompositionEnd', - keyup : 'topKeyUp', - keydown : 'topKeyDown', - textInput : 'topTextInput', - textinput : null, // Not defined now + compositionstart: 'topCompositionStart', + compositionend: 'topCompositionEnd', + keyup: 'topKeyUp', + keydown: 'topKeyDown', + textInput: 'topTextInput', + textinput: null, // Not defined now }; describe('BeforeInputEventPlugin', function() { @@ -62,12 +62,12 @@ describe('BeforeInputEventPlugin', function() { EventMapping[eventType], ModuleCache.ReactDOMComponentTree.getInstanceFromNode(node), evt, - node + node, ); } function setElementText(node) { - return (args) => node.innerHTML = args; + return args => (node.innerHTML = args); } function accumulateEvents(node, events) { @@ -94,8 +94,10 @@ describe('BeforeInputEventPlugin', function() { // Both are null. Expected. } else if (actual === null) { throw new EventMismatchError(idx, 'Expected not to be null'); - } else if (expected.type === null - || !(actual instanceof expected.type)) { + } else if ( + expected.type === null || + !(actual instanceof expected.type) + ) { throw new EventMismatchError(idx, 'Unexpected type: ' + actual); } else { // Type match. @@ -103,8 +105,10 @@ describe('BeforeInputEventPlugin', function() { if (!(expectedKey in actual)) { throw new EventMismatchError(idx, 'KeyNotFound: ' + expectedKey); } else if (actual[expectedKey] !== expected.data[expectedKey]) { - throw new EventMismatchError(idx, - 'ValueMismatch: ' + actual[expectedKey]); + throw new EventMismatchError( + idx, + 'ValueMismatch: ' + actual[expectedKey], + ); } }); } @@ -142,16 +146,26 @@ describe('BeforeInputEventPlugin', function() { // textInput, SyntheticCompositionEvent at composition, and nothing from // keyUp. var Expected_Webkit = () => [ - {type: ModuleCache.SyntheticCompositionEvent, data: {}}, {type: null}, - {type: null}, {type: ModuleCache.SyntheticInputEvent, data: {data: 'A'}}, - {type: null}, {type: null}, // textinput of A - {type: null}, {type: null}, // keyUp of 65 - {type: null}, {type: ModuleCache.SyntheticInputEvent, data: {data: 'abc'}}, - {type: null}, {type: null}, // textinput of abc - {type: null}, {type: null}, // keyUp of 32 - {type: null}, {type: ModuleCache.SyntheticInputEvent, data: {data: 'xyz'}}, - {type: null}, {type: null}, // textinput of xyz - {type: null}, {type: null}, // keyUp of 32 + {type: ModuleCache.SyntheticCompositionEvent, data: {}}, + {type: null}, + {type: null}, + {type: ModuleCache.SyntheticInputEvent, data: {data: 'A'}}, + {type: null}, + {type: null}, // textinput of A + {type: null}, + {type: null}, // keyUp of 65 + {type: null}, + {type: ModuleCache.SyntheticInputEvent, data: {data: 'abc'}}, + {type: null}, + {type: null}, // textinput of abc + {type: null}, + {type: null}, // keyUp of 32 + {type: null}, + {type: ModuleCache.SyntheticInputEvent, data: {data: 'xyz'}}, + {type: null}, + {type: null}, // textinput of xyz + {type: null}, + {type: null}, // keyUp of 32 {type: ModuleCache.SyntheticCompositionEvent, data: {data: 'Hello'}}, {type: null}, ]; @@ -161,19 +175,29 @@ describe('BeforeInputEventPlugin', function() { // expected to be triggered at compositionend with a text of the target // element, not event data. var Expected_IE11 = () => [ - {type: ModuleCache.SyntheticCompositionEvent, data: {}}, {type: null}, - {type: null}, {type: null}, // textInput of A - {type: null}, {type: null}, // textinput of A - {type: null}, {type: null}, // keyUp of 65 - {type: null}, {type: null}, // textInput of abc - {type: null}, {type: null}, // textinput of abc + {type: ModuleCache.SyntheticCompositionEvent, data: {}}, + {type: null}, + {type: null}, + {type: null}, // textInput of A + {type: null}, + {type: null}, // textinput of A + {type: null}, + {type: null}, // keyUp of 65 + {type: null}, + {type: null}, // textInput of abc + {type: null}, + {type: null}, // textinput of abc // fallbackData should NOT be set at keyUp with any of END_KEYCODES - {type: null}, {type: null}, // keyUp of 32 + {type: null}, + {type: null}, // keyUp of 32 - {type: null}, {type: null}, // textInput of xyz - {type: null}, {type: null}, // textinput of xyz - {type: null}, {type: null}, // keyUp of 32 + {type: null}, + {type: null}, // textInput of xyz + {type: null}, + {type: null}, // textinput of xyz + {type: null}, + {type: null}, // keyUp of 32 // fallbackData is retrieved from the element, which is XYZ, // at a time of compositionend @@ -194,18 +218,23 @@ describe('BeforeInputEventPlugin', function() { var node = ModuleCache.ReactDOM.findDOMNode(rendered); var events = []; - Scenario.forEach((el) => - el.run.call(this, node, events).apply(this, el.arg)); + Scenario.forEach(el => el.run.call(this, node, events).apply(this, el.arg)); verifyEvents(events, ExpectedResult()); } it('extract onBeforeInput from native textinput events', function() { TestEditableReactComponent( - simulateWebkit, Scenario_Composition, Expected_Webkit); + simulateWebkit, + Scenario_Composition, + Expected_Webkit, + ); }); it('extract onBeforeInput from fallback objects', function() { TestEditableReactComponent( - simulateIE11, Scenario_Composition, Expected_IE11); + simulateIE11, + Scenario_Composition, + Expected_IE11, + ); }); }); diff --git a/src/renderers/dom/client/eventPlugins/__tests__/ChangeEventPlugin-test.js b/src/renderers/dom/client/eventPlugins/__tests__/ChangeEventPlugin-test.js index bd6a904e61..d85970cec5 100644 --- a/src/renderers/dom/client/eventPlugins/__tests__/ChangeEventPlugin-test.js +++ b/src/renderers/dom/client/eventPlugins/__tests__/ChangeEventPlugin-test.js @@ -12,9 +12,42 @@ 'use strict'; var React = require('React'); +var ReactDOM = require('ReactDOM'); var ReactTestUtils = require('ReactTestUtils'); +var ChangeEventPlugin = require('ChangeEventPlugin'); +var inputValueTracking = require('inputValueTracking'); + +function getTrackedValue(elem) { + var tracker = inputValueTracking._getTrackerFromNode(elem); + return tracker.getValue(); +} + +function setTrackedValue(elem, value) { + var tracker = inputValueTracking._getTrackerFromNode(elem); + tracker.setValue(value); +} + +function setUntrackedValue(elem, value) { + var tracker = inputValueTracking._getTrackerFromNode(elem); + var current = tracker.getValue(); + + if (elem.type === 'checkbox' || elem.type === 'radio') { + elem.checked = value; + } else { + elem.value = value; + } + tracker.setValue(current); +} describe('ChangeEventPlugin', () => { + beforeEach(() => { + ChangeEventPlugin._allowSimulatedPassThrough = false; + }); + + afterEach(() => { + ChangeEventPlugin._allowSimulatedPassThrough = true; + }); + it('should fire change for checkbox input', () => { var called = 0; @@ -23,8 +56,176 @@ describe('ChangeEventPlugin', () => { expect(e.type).toBe('change'); } - var input = ReactTestUtils.renderIntoDocument(); + var input = ReactTestUtils.renderIntoDocument( + , + ); + + setUntrackedValue(input, true); + ReactTestUtils.SimulateNative.click(input); + + expect(called).toBe(1); + }); + + it('should catch setting the value programmatically', function() { + var input = ReactTestUtils.renderIntoDocument( + , + ); + + input.value = 'bar'; + expect(getTrackedValue(input)).toBe('bar'); + }); + + it('should not fire change when setting the value programmatically', function() { + var called = 0; + + function cb(e) { + called += 1; + expect(e.type).toBe('change'); + } + + var input = ReactTestUtils.renderIntoDocument( + , + ); + + input.value = 'bar'; + ReactTestUtils.SimulateNative.change(input); + expect(called).toBe(0); + + setUntrackedValue(input, 'foo'); + ReactTestUtils.SimulateNative.change(input); + + expect(called).toBe(1); + }); + + it('should not fire change when setting checked programmatically', function() { + var called = 0; + + function cb(e) { + called += 1; + expect(e.type).toBe('change'); + } + + var input = ReactTestUtils.renderIntoDocument( + , + ); + + input.checked = true; + ReactTestUtils.SimulateNative.click(input); + expect(called).toBe(0); + + input.checked = false; + setTrackedValue(input, undefined); + ReactTestUtils.SimulateNative.click(input); + + expect(called).toBe(1); + }); + + it('should unmount', function() { + var container = document.createElement('div'); + var input = ReactDOM.render(, container); + + ReactDOM.unmountComponentAtNode(container); + }); + + it('should only fire change for checked radio button once', function() { + var called = 0; + + function cb(e) { + called += 1; + } + + var input = ReactTestUtils.renderIntoDocument( + , + ); + setUntrackedValue(input, true); + ReactTestUtils.SimulateNative.click(input); ReactTestUtils.SimulateNative.click(input); expect(called).toBe(1); }); + + it('should deduplicate input value change events', function() { + var input; + var called = 0; + + function cb(e) { + called += 1; + expect(e.type).toBe('change'); + } + + [ + , + , + , + ].forEach(function(element) { + called = 0; + input = ReactTestUtils.renderIntoDocument(element); + + setUntrackedValue(input, '40'); + ReactTestUtils.SimulateNative.change(input); + ReactTestUtils.SimulateNative.change(input); + expect(called).toBe(1); + + called = 0; + input = ReactTestUtils.renderIntoDocument(element); + setUntrackedValue(input, '40'); + ReactTestUtils.SimulateNative.input(input); + ReactTestUtils.SimulateNative.input(input); + expect(called).toBe(1); + + called = 0; + input = ReactTestUtils.renderIntoDocument(element); + setUntrackedValue(input, '40'); + ReactTestUtils.SimulateNative.input(input); + ReactTestUtils.SimulateNative.change(input); + expect(called).toBe(1); + }); + }); + + it('should listen for both change and input events when supported', function() { + var called = 0; + + function cb(e) { + called += 1; + expect(e.type).toBe('change'); + } + + if (!ChangeEventPlugin._isInputEventSupported) { + return; + } + + var input = ReactTestUtils.renderIntoDocument( + , + ); + setUntrackedValue(input, 'bar'); + + ReactTestUtils.SimulateNative.input(input); + + setUntrackedValue(input, 'foo'); + + ReactTestUtils.SimulateNative.change(input); + + expect(called).toBe(2); + }); + + it('should only fire events when the value changes for range inputs', function() { + var called = 0; + + function cb(e) { + called += 1; + expect(e.type).toBe('change'); + } + + var input = ReactTestUtils.renderIntoDocument( + , + ); + setUntrackedValue(input, '40'); + ReactTestUtils.SimulateNative.input(input); + ReactTestUtils.SimulateNative.change(input); + + setUntrackedValue(input, 'foo'); + + ReactTestUtils.SimulateNative.input(input); + ReactTestUtils.SimulateNative.change(input); + expect(called).toBe(2); + }); }); diff --git a/src/renderers/dom/client/eventPlugins/__tests__/EnterLeaveEventPlugin-test.js b/src/renderers/dom/client/eventPlugins/__tests__/EnterLeaveEventPlugin-test.js index f856cf320f..5fbf068934 100644 --- a/src/renderers/dom/client/eventPlugins/__tests__/EnterLeaveEventPlugin-test.js +++ b/src/renderers/dom/client/eventPlugins/__tests__/EnterLeaveEventPlugin-test.js @@ -33,18 +33,21 @@ describe('EnterLeaveEventPlugin', () => { var iframeDocument = iframe.contentDocument; iframeDocument.write( - '
    ' + '
    ', ); iframeDocument.close(); - var component = ReactDOM.render(
    , iframeDocument.body.getElementsByTagName('div')[0]); + var component = ReactDOM.render( +
    , + iframeDocument.body.getElementsByTagName('div')[0], + ); var div = ReactDOM.findDOMNode(component); var extracted = EnterLeaveEventPlugin.extractEvents( 'topMouseOver', ReactDOMComponentTree.getInstanceFromNode(div), {target: div}, - div + div, ); expect(extracted.length).toBe(2); diff --git a/src/renderers/dom/client/eventPlugins/__tests__/SelectEventPlugin-test.js b/src/renderers/dom/client/eventPlugins/__tests__/SelectEventPlugin-test.js index d8d79f7082..41707da4f6 100644 --- a/src/renderers/dom/client/eventPlugins/__tests__/SelectEventPlugin-test.js +++ b/src/renderers/dom/client/eventPlugins/__tests__/SelectEventPlugin-test.js @@ -23,7 +23,7 @@ describe('SelectEventPlugin', () => { topLevelEvent, ReactDOMComponentTree.getInstanceFromNode(node), {target: node}, - node + node, ); } @@ -63,7 +63,7 @@ describe('SelectEventPlugin', () => { var cb = jest.fn(); var rendered = ReactTestUtils.renderIntoDocument( - + , ); var node = ReactDOM.findDOMNode(rendered); diff --git a/src/renderers/dom/client/eventPlugins/__tests__/SimpleEventPlugin-test.js b/src/renderers/dom/client/eventPlugins/__tests__/SimpleEventPlugin-test.js index 5f4a533ea4..09dbea8665 100644 --- a/src/renderers/dom/client/eventPlugins/__tests__/SimpleEventPlugin-test.js +++ b/src/renderers/dom/client/eventPlugins/__tests__/SimpleEventPlugin-test.js @@ -11,7 +11,6 @@ 'use strict'; - describe('SimpleEventPlugin', function() { var React; var ReactDOM; @@ -43,13 +42,13 @@ describe('SimpleEventPlugin', function() { }); it('A non-interactive tags click when disabled', function() { - var element = (
    ); + var element =
    ; expectClickThru(mounted(element)); }); it('A non-interactive tags clicks bubble when disabled', function() { var element = ReactTestUtils.renderIntoDocument( -
    +
    , ); var child = ReactDOM.findDOMNode(element).firstChild; @@ -59,7 +58,7 @@ describe('SimpleEventPlugin', function() { it('does not register a click when clicking a child of a disabled element', function() { var element = ReactTestUtils.renderIntoDocument( - + , ); var child = ReactDOM.findDOMNode(element).querySelector('span'); @@ -69,7 +68,7 @@ describe('SimpleEventPlugin', function() { it('triggers click events for children of disabled elements', function() { var element = ReactTestUtils.renderIntoDocument( - + , ); var child = ReactDOM.findDOMNode(element).querySelector('span'); @@ -81,7 +80,7 @@ describe('SimpleEventPlugin', function() { var element = ReactTestUtils.renderIntoDocument(
    -
    +
    , ); var child = ReactDOM.findDOMNode(element).querySelector('span'); @@ -91,7 +90,7 @@ describe('SimpleEventPlugin', function() { it('triggers captured click events for children of disabled elements', function() { var element = ReactTestUtils.renderIntoDocument( - + , ); var child = ReactDOM.findDOMNode(element).querySelector('span'); @@ -100,9 +99,7 @@ describe('SimpleEventPlugin', function() { }); ['button', 'input', 'select', 'textarea'].forEach(function(tagName) { - describe(tagName, function() { - it('should forward clicks when it starts out not disabled', () => { var element = React.createElement(tagName, { onClick: onClick, @@ -123,12 +120,12 @@ describe('SimpleEventPlugin', function() { it('should forward clicks when it becomes not disabled', () => { var container = document.createElement('div'); var element = ReactDOM.render( - React.createElement(tagName, { onClick: onClick, disabled: true }), - container + React.createElement(tagName, {onClick: onClick, disabled: true}), + container, ); element = ReactDOM.render( - React.createElement(tagName, { onClick: onClick }), - container + React.createElement(tagName, {onClick: onClick}), + container, ); expectClickThru(element); }); @@ -136,12 +133,12 @@ describe('SimpleEventPlugin', function() { it('should not forward clicks when it becomes disabled', () => { var container = document.createElement('div'); var element = ReactDOM.render( - React.createElement(tagName, { onClick: onClick }), - container + React.createElement(tagName, {onClick: onClick}), + container, ); element = ReactDOM.render( - React.createElement(tagName, { onClick: onClick, disabled: true }), - container + React.createElement(tagName, {onClick: onClick, disabled: true}), + container, ); expectNoClickThru(element); }); @@ -149,26 +146,25 @@ describe('SimpleEventPlugin', function() { it('should work correctly if the listener is changed', () => { var container = document.createElement('div'); var element = ReactDOM.render( - React.createElement(tagName, { onClick: onClick, disabled: true }), - container + React.createElement(tagName, {onClick: onClick, disabled: true}), + container, ); element = ReactDOM.render( - React.createElement(tagName, { onClick: onClick, disabled: false }), - container + React.createElement(tagName, {onClick: onClick, disabled: false}), + container, ); expectClickThru(element); }); }); }); - describe('iOS bubbling click fix', function() { // See http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html it('does not add a local click to interactive elements', function() { var container = document.createElement('div'); - ReactDOM.render(