mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 12:20:20 +01:00
Remove old IE polyfill code (#10238)
* Upgrade DOM Fixtures Upgrade to react-scripts v1 and include required polyfills for older browsers * Remove ChangeEvent polyfills for unsupported browsers
This commit is contained in:
parent
c1833b4b7e
commit
0b220d0f04
|
|
@ -3,12 +3,13 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"react-scripts": "0.9.5"
|
"react-scripts": "1.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"classnames": "^2.2.5",
|
"classnames": "^2.2.5",
|
||||||
"query-string": "^4.2.3",
|
"core-js": "^2.4.1",
|
||||||
"prop-types": "^15.5.6",
|
"prop-types": "^15.5.6",
|
||||||
|
"query-string": "^4.2.3",
|
||||||
"react": "^15.4.1",
|
"react": "^15.4.1",
|
||||||
"react-dom": "^15.4.1",
|
"react-dom": "^15.4.1",
|
||||||
"semver": "^5.3.0"
|
"semver": "^5.3.0"
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
-->
|
-->
|
||||||
<title>React App</title>
|
<title>React App</title>
|
||||||
<script src="https://unpkg.com/prop-types@15.5.6/prop-types.js"></script>
|
<script src="https://unpkg.com/prop-types@15.5.6/prop-types.js"></script>
|
||||||
<script src="react-loader.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|
|
||||||
43
fixtures/dom/public/react-loader.js
vendored
43
fixtures/dom/public/react-loader.js
vendored
|
|
@ -1,43 +0,0 @@
|
||||||
/**
|
|
||||||
* Take a version from the window query string and load a specific
|
|
||||||
* version of React.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* http://localhost:3000?version=15.4.1
|
|
||||||
* (Loads React 15.4.1)
|
|
||||||
*/
|
|
||||||
|
|
||||||
var REACT_PATH = 'react.development.js';
|
|
||||||
var DOM_PATH = 'react-dom.development.js';
|
|
||||||
|
|
||||||
function parseQuery(qstr) {
|
|
||||||
var query = {};
|
|
||||||
|
|
||||||
var a = qstr.substr(1).split('&');
|
|
||||||
|
|
||||||
for (var i = 0; i < a.length; i++) {
|
|
||||||
var b = a[i].split('=');
|
|
||||||
|
|
||||||
query[decodeURIComponent(b[0])] = decodeURIComponent(b[1] || '');
|
|
||||||
}
|
|
||||||
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
var query = parseQuery(window.location.search);
|
|
||||||
var version = query.version || 'local';
|
|
||||||
|
|
||||||
if (version !== 'local') {
|
|
||||||
REACT_PATH = 'https://unpkg.com/react@' + version + '/dist/react.js';
|
|
||||||
DOM_PATH = 'https://unpkg.com/react-dom@' + version + '/dist/react-dom.js';
|
|
||||||
}
|
|
||||||
|
|
||||||
document.write('<script src="' + REACT_PATH + '"></script>');
|
|
||||||
|
|
||||||
// Versions earlier than 14 do not use ReactDOM
|
|
||||||
if (version === 'local' || parseFloat(version, 10) > 0.13) {
|
|
||||||
document.write('<script src="' + DOM_PATH + '"></script>');
|
|
||||||
} else {
|
|
||||||
// Aliasing React to ReactDOM for compatibility.
|
|
||||||
document.write('<script>ReactDOM = React</script>');
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
const React = window.React;
|
|
||||||
import Header from './Header';
|
import Header from './Header';
|
||||||
import Fixtures from './fixtures';
|
import Fixtures from './fixtures';
|
||||||
|
|
||||||
import '../style.css';
|
import '../style.css';
|
||||||
|
|
||||||
|
const React = window.React;
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
const React = window.React;
|
|
||||||
|
|
||||||
import FixtureSet from '../../FixtureSet';
|
import FixtureSet from '../../FixtureSet';
|
||||||
import TestCase from '../../TestCase';
|
import TestCase from '../../TestCase';
|
||||||
|
|
||||||
|
const React = window.React;
|
||||||
|
|
||||||
function onButtonClick() {
|
function onButtonClick() {
|
||||||
window.alert(`This shouldn't have happened!`);
|
window.alert(`This shouldn't have happened!`);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
const React = window.React;
|
|
||||||
|
|
||||||
import FixtureSet from '../../FixtureSet';
|
import FixtureSet from '../../FixtureSet';
|
||||||
import TestCase from '../../TestCase';
|
import TestCase from '../../TestCase';
|
||||||
|
|
||||||
|
const React = window.React;
|
||||||
|
|
||||||
function BadRender(props) {
|
function BadRender(props) {
|
||||||
throw props.error;
|
throw props.error;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
const React = window.React;
|
|
||||||
import RangeInputFixtures from './range-inputs';
|
import RangeInputFixtures from './range-inputs';
|
||||||
import TextInputFixtures from './text-inputs';
|
import TextInputFixtures from './text-inputs';
|
||||||
import SelectFixtures from './selects';
|
import SelectFixtures from './selects';
|
||||||
|
|
@ -10,6 +9,8 @@ import ButtonFixtures from './buttons';
|
||||||
import DateInputFixtures from './date-inputs';
|
import DateInputFixtures from './date-inputs';
|
||||||
import ErrorHandling from './error-handling';
|
import ErrorHandling from './error-handling';
|
||||||
|
|
||||||
|
const React = window.React;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple routing component that renders the appropriate
|
* A simple routing component that renders the appropriate
|
||||||
* fixture based on the location pathname.
|
* fixture based on the location pathname.
|
||||||
|
|
@ -41,4 +42,4 @@ function FixturesPage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = FixturesPage;
|
export default FixturesPage;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
const React = window.React;
|
|
||||||
|
|
||||||
import Fixture from '../../Fixture';
|
import Fixture from '../../Fixture';
|
||||||
|
|
||||||
|
const React = window.React;
|
||||||
|
|
||||||
class NumberInputDecimal extends React.Component {
|
class NumberInputDecimal extends React.Component {
|
||||||
state = {value: '.98'};
|
state = {value: '.98'};
|
||||||
changeValue = () => {
|
changeValue = () => {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
const React = window.React;
|
|
||||||
|
|
||||||
import Fixture from '../../Fixture';
|
import Fixture from '../../Fixture';
|
||||||
|
|
||||||
|
const React = window.React;
|
||||||
|
|
||||||
class NumberInputExtraZeroes extends React.Component {
|
class NumberInputExtraZeroes extends React.Component {
|
||||||
state = {value: '3.0000'};
|
state = {value: '3.0000'};
|
||||||
changeValue = () => {
|
changeValue = () => {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
const React = window.React;
|
|
||||||
|
|
||||||
import Fixture from '../../Fixture';
|
import Fixture from '../../Fixture';
|
||||||
|
|
||||||
|
const React = window.React;
|
||||||
|
|
||||||
class NumberTestCase extends React.Component {
|
class NumberTestCase extends React.Component {
|
||||||
state = {value: ''};
|
state = {value: ''};
|
||||||
onChange = event => {
|
onChange = event => {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
const React = window.React;
|
|
||||||
|
|
||||||
import FixtureSet from '../../FixtureSet';
|
import FixtureSet from '../../FixtureSet';
|
||||||
import TestCase from '../../TestCase';
|
import TestCase from '../../TestCase';
|
||||||
import NumberTestCase from './NumberTestCase';
|
import NumberTestCase from './NumberTestCase';
|
||||||
import NumberInputDecimal from './NumberInputDecimal';
|
import NumberInputDecimal from './NumberInputDecimal';
|
||||||
import NumberInputExtraZeroes from './NumberInputExtraZeroes';
|
import NumberInputExtraZeroes from './NumberInputExtraZeroes';
|
||||||
|
|
||||||
|
const React = window.React;
|
||||||
|
|
||||||
function NumberInputs() {
|
function NumberInputs() {
|
||||||
return (
|
return (
|
||||||
<FixtureSet
|
<FixtureSet
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
const React = window.React;
|
|
||||||
|
|
||||||
import Fixture from '../../Fixture';
|
import Fixture from '../../Fixture';
|
||||||
|
|
||||||
|
const React = window.React;
|
||||||
|
|
||||||
class PasswordTestCase extends React.Component {
|
class PasswordTestCase extends React.Component {
|
||||||
state = {value: ''};
|
state = {value: ''};
|
||||||
onChange = event => {
|
onChange = event => {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
const React = window.React;
|
|
||||||
|
|
||||||
import FixtureSet from '../../FixtureSet';
|
import FixtureSet from '../../FixtureSet';
|
||||||
import TestCase from '../../TestCase';
|
import TestCase from '../../TestCase';
|
||||||
import PasswordTestCase from './PasswordTestCase';
|
import PasswordTestCase from './PasswordTestCase';
|
||||||
|
|
||||||
|
const React = window.React;
|
||||||
|
|
||||||
function NumberInputs() {
|
function NumberInputs() {
|
||||||
return (
|
return (
|
||||||
<FixtureSet title="Password inputs" description="">
|
<FixtureSet title="Password inputs" description="">
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
const React = window.React;
|
|
||||||
|
|
||||||
import Fixture from '../../Fixture';
|
import Fixture from '../../Fixture';
|
||||||
|
const React = window.React;
|
||||||
|
|
||||||
class InputTestCase extends React.Component {
|
class InputTestCase extends React.Component {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
const React = window.React;
|
|
||||||
|
|
||||||
import Fixture from '../../Fixture';
|
import Fixture from '../../Fixture';
|
||||||
import FixtureSet from '../../FixtureSet';
|
import FixtureSet from '../../FixtureSet';
|
||||||
import TestCase from '../../TestCase';
|
import TestCase from '../../TestCase';
|
||||||
import InputTestCase from './InputTestCase';
|
import InputTestCase from './InputTestCase';
|
||||||
|
|
||||||
|
const React = window.React;
|
||||||
|
|
||||||
class TextInputFixtures extends React.Component {
|
class TextInputFixtures extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
|
|
@ -93,4 +93,4 @@ class TextInputFixtures extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = TextInputFixtures;
|
export default TextInputFixtures;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,11 @@
|
||||||
const React = window.React;
|
import './polyfills';
|
||||||
const ReactDOM = window.ReactDOM;
|
import loadReact from './react-loader';
|
||||||
import App from './components/App';
|
|
||||||
|
|
||||||
ReactDOM.render(<App />, document.getElementById('root'));
|
loadReact().then(() => import('./components/App')).then(App => {
|
||||||
|
const {React, ReactDOM} = window;
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
React.createElement(App.default),
|
||||||
|
document.getElementById('root')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
|
||||||
35
fixtures/dom/src/polyfills.js
Normal file
35
fixtures/dom/src/polyfills.js
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import 'core-js/es6/promise';
|
||||||
|
import 'core-js/es6/set';
|
||||||
|
import 'core-js/es6/map';
|
||||||
|
|
||||||
|
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
|
||||||
|
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
|
||||||
|
|
||||||
|
// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
|
||||||
|
// MIT license
|
||||||
|
(function() {
|
||||||
|
var lastTime = 0;
|
||||||
|
var vendors = ['ms', 'moz', 'webkit', 'o'];
|
||||||
|
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
|
||||||
|
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
|
||||||
|
window.cancelAnimationFrame =
|
||||||
|
window[vendors[x] + 'CancelAnimationFrame'] ||
|
||||||
|
window[vendors[x] + 'CancelRequestAnimationFrame'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!window.requestAnimationFrame)
|
||||||
|
window.requestAnimationFrame = function(callback, element) {
|
||||||
|
var currTime = new Date().getTime();
|
||||||
|
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
|
||||||
|
var id = window.setTimeout(function() {
|
||||||
|
callback(currTime + timeToCall);
|
||||||
|
}, timeToCall);
|
||||||
|
lastTime = currTime + timeToCall;
|
||||||
|
return id;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!window.cancelAnimationFrame)
|
||||||
|
window.cancelAnimationFrame = function(id) {
|
||||||
|
clearTimeout(id);
|
||||||
|
};
|
||||||
|
})();
|
||||||
63
fixtures/dom/src/react-loader.js
vendored
Normal file
63
fixtures/dom/src/react-loader.js
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
/**
|
||||||
|
* Take a version from the window query string and load a specific
|
||||||
|
* version of React.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* http://localhost:3000?version=15.4.1
|
||||||
|
* (Loads React 15.4.1)
|
||||||
|
*/
|
||||||
|
|
||||||
|
function parseQuery(qstr) {
|
||||||
|
var query = {};
|
||||||
|
var a = qstr.substr(1).split('&');
|
||||||
|
|
||||||
|
for (var i = 0; i < a.length; i++) {
|
||||||
|
var b = a[i].split('=');
|
||||||
|
query[decodeURIComponent(b[0])] = decodeURIComponent(b[1] || '');
|
||||||
|
}
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadScript(src) {
|
||||||
|
let firstScript = document.getElementsByTagName('script')[0];
|
||||||
|
let scriptNode;
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
scriptNode = document.createElement('script');
|
||||||
|
scriptNode.async = 1;
|
||||||
|
scriptNode.src = src;
|
||||||
|
|
||||||
|
scriptNode.onload = () => resolve();
|
||||||
|
scriptNode.onerror = () => reject(new Error(`failed to load: ${src}`));
|
||||||
|
|
||||||
|
firstScript.parentNode.insertBefore(scriptNode, firstScript);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function loadReact() {
|
||||||
|
let REACT_PATH = 'react.development.js';
|
||||||
|
let DOM_PATH = 'react-dom.development.js';
|
||||||
|
|
||||||
|
let query = parseQuery(window.location.search);
|
||||||
|
let version = query.version || 'local';
|
||||||
|
|
||||||
|
if (version !== 'local') {
|
||||||
|
REACT_PATH = 'https://unpkg.com/react@' + version + '/dist/react.js';
|
||||||
|
DOM_PATH = 'https://unpkg.com/react-dom@' + version + '/dist/react-dom.js';
|
||||||
|
}
|
||||||
|
|
||||||
|
const needsReactDOM = version === 'local' || parseFloat(version, 10) > 0.13;
|
||||||
|
|
||||||
|
let request = loadScript(REACT_PATH);
|
||||||
|
|
||||||
|
if (needsReactDOM) {
|
||||||
|
request = request.then(() => loadScript(DOM_PATH));
|
||||||
|
} else {
|
||||||
|
// Aliasing React to ReactDOM for compatibility.
|
||||||
|
request = request.then(() => {
|
||||||
|
window.ReactDOM = window.React;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
@ -1472,7 +1472,7 @@ core-js@^1.0.0:
|
||||||
version "1.2.7"
|
version "1.2.7"
|
||||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
|
||||||
|
|
||||||
core-js@^2.4.0:
|
core-js@^2.4.0, core-js@^2.4.1:
|
||||||
version "2.4.1"
|
version "2.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e"
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,17 +11,14 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var EventPluginHub = require('EventPluginHub');
|
|
||||||
var EventPropagators = require('EventPropagators');
|
var EventPropagators = require('EventPropagators');
|
||||||
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
|
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
|
||||||
var ReactControlledComponent = require('ReactControlledComponent');
|
var ReactControlledComponent = require('ReactControlledComponent');
|
||||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||||
var ReactGenericBatching = require('ReactGenericBatching');
|
|
||||||
var SyntheticEvent = require('SyntheticEvent');
|
var SyntheticEvent = require('SyntheticEvent');
|
||||||
|
var getActiveElement = require('fbjs/lib/getActiveElement');
|
||||||
|
|
||||||
var inputValueTracking = require('inputValueTracking');
|
var inputValueTracking = require('inputValueTracking');
|
||||||
var getEventTarget = require('getEventTarget');
|
|
||||||
var isEventSupported = require('isEventSupported');
|
|
||||||
var isTextInputElement = require('isTextInputElement');
|
var isTextInputElement = require('isTextInputElement');
|
||||||
|
|
||||||
var eventTypes = {
|
var eventTypes = {
|
||||||
|
|
@ -43,6 +40,13 @@ var eventTypes = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function shouldUseChangeEvent(elem) {
|
||||||
|
var nodeName = elem.nodeName && elem.nodeName.toLowerCase();
|
||||||
|
return (
|
||||||
|
nodeName === 'select' || (nodeName === 'input' && elem.type === 'file')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function createAndAccumulateChangeEvent(inst, nativeEvent, target) {
|
function createAndAccumulateChangeEvent(inst, nativeEvent, target) {
|
||||||
var event = SyntheticEvent.getPooled(
|
var event = SyntheticEvent.getPooled(
|
||||||
eventTypes.change,
|
eventTypes.change,
|
||||||
|
|
@ -56,174 +60,53 @@ function createAndAccumulateChangeEvent(inst, nativeEvent, target) {
|
||||||
EventPropagators.accumulateTwoPhaseDispatches(event);
|
EventPropagators.accumulateTwoPhaseDispatches(event);
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* For IE shims
|
|
||||||
*/
|
|
||||||
var activeElement = null;
|
|
||||||
var activeElementInst = null;
|
|
||||||
|
|
||||||
/**
|
function getInstIfValueChanged(targetInst, targetNode) {
|
||||||
* SECTION: handle `change` event
|
|
||||||
*/
|
|
||||||
function shouldUseChangeEvent(elem) {
|
|
||||||
var nodeName = elem.nodeName && elem.nodeName.toLowerCase();
|
|
||||||
return (
|
|
||||||
nodeName === 'select' || (nodeName === 'input' && elem.type === 'file')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function manualDispatchChangeEvent(nativeEvent) {
|
|
||||||
var event = createAndAccumulateChangeEvent(
|
|
||||||
activeElementInst,
|
|
||||||
nativeEvent,
|
|
||||||
getEventTarget(nativeEvent),
|
|
||||||
);
|
|
||||||
|
|
||||||
// If change and propertychange bubbled, we'd just bind to it like all the
|
|
||||||
// other events and have it go through ReactBrowserEventEmitter. Since it
|
|
||||||
// doesn't, we manually listen for the events and so we have to enqueue and
|
|
||||||
// process the abstract event manually.
|
|
||||||
//
|
|
||||||
// Batching is necessary here in order to ensure that all event handlers run
|
|
||||||
// before the next rerender (including event handlers attached to ancestor
|
|
||||||
// elements instead of directly on the input). Without this, controlled
|
|
||||||
// components don't work properly in conjunction with event bubbling because
|
|
||||||
// the component is rerendered and the value reverted before all the event
|
|
||||||
// handlers can run. See https://github.com/facebook/react/issues/708.
|
|
||||||
ReactGenericBatching.batchedUpdates(runEventInBatch, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
function runEventInBatch(event) {
|
|
||||||
EventPluginHub.enqueueEvents(event);
|
|
||||||
EventPluginHub.processEventQueue(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getInstIfValueChanged(targetInst) {
|
|
||||||
const targetNode = ReactDOMComponentTree.getNodeFromInstance(targetInst);
|
|
||||||
if (inputValueTracking.updateValueIfChanged(targetNode)) {
|
if (inputValueTracking.updateValueIfChanged(targetNode)) {
|
||||||
return targetInst;
|
return targetInst;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTargetInstForChangeEvent(topLevelType, targetInst) {
|
|
||||||
if (topLevelType === 'topChange') {
|
|
||||||
return targetInst;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SECTION: handle `input` event
|
* SECTION: handle `input` event
|
||||||
*/
|
*/
|
||||||
var isInputEventSupported = false;
|
|
||||||
|
var isTextInputEventSupported = false;
|
||||||
if (ExecutionEnvironment.canUseDOM) {
|
if (ExecutionEnvironment.canUseDOM) {
|
||||||
// IE9 claims to support the input event but fails to trigger it when
|
isTextInputEventSupported =
|
||||||
// deleting text, so we ignore its input events.
|
!document.documentMode || document.documentMode > 9;
|
||||||
isInputEventSupported =
|
|
||||||
isEventSupported('input') &&
|
|
||||||
(!document.documentMode || document.documentMode > 9);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function getTargetInstForInputEventPolyfill(
|
||||||
* (For IE <=9) Starts tracking propertychange events on the passed-in element
|
topLevelType,
|
||||||
* and override the value property so that we can distinguish user events from
|
targetInst,
|
||||||
* value changes in JS.
|
targetNode,
|
||||||
*/
|
) {
|
||||||
function startWatchingForValueChange(target, targetInst) {
|
|
||||||
activeElement = target;
|
|
||||||
activeElementInst = targetInst;
|
|
||||||
activeElement.attachEvent('onpropertychange', handlePropertyChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* (For IE <=9) Removes the event listeners from the currently-tracked element,
|
|
||||||
* if any exists.
|
|
||||||
*/
|
|
||||||
function stopWatchingForValueChange() {
|
|
||||||
if (!activeElement) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
activeElement.detachEvent('onpropertychange', handlePropertyChange);
|
|
||||||
activeElement = null;
|
|
||||||
activeElementInst = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* (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;
|
|
||||||
}
|
|
||||||
if (getInstIfValueChanged(activeElementInst)) {
|
|
||||||
manualDispatchChangeEvent(nativeEvent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleEventsForInputEventPolyfill(topLevelType, target, targetInst) {
|
|
||||||
if (topLevelType === 'topFocus') {
|
|
||||||
// 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
|
|
||||||
// In either case, we don't want to call the event handler if the value
|
|
||||||
// is changed from JS so we redefine a setter for `.value` that updates
|
|
||||||
// our activeElementValue variable, allowing us to ignore those changes
|
|
||||||
//
|
|
||||||
// stopWatching() should be a noop here but we call it just in case we
|
|
||||||
// missed a blur event somehow.
|
|
||||||
stopWatchingForValueChange();
|
|
||||||
startWatchingForValueChange(target, targetInst);
|
|
||||||
} else if (topLevelType === 'topBlur') {
|
|
||||||
stopWatchingForValueChange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// For IE8 and IE9.
|
|
||||||
function getTargetInstForInputEventPolyfill(topLevelType, targetInst) {
|
|
||||||
if (
|
if (
|
||||||
|
topLevelType === 'topInput' ||
|
||||||
|
topLevelType === 'topChange' ||
|
||||||
|
// These events catch anything the IE9 onInput misses
|
||||||
topLevelType === 'topSelectionChange' ||
|
topLevelType === 'topSelectionChange' ||
|
||||||
topLevelType === 'topKeyUp' ||
|
topLevelType === 'topKeyUp' ||
|
||||||
topLevelType === 'topKeyDown'
|
topLevelType === 'topKeyDown'
|
||||||
) {
|
) {
|
||||||
// On the selectionchange event, the target is just document which isn't
|
return getInstIfValueChanged(targetInst, targetNode);
|
||||||
// helpful for us so just check activeElement instead.
|
|
||||||
//
|
|
||||||
// 99% of the time, keydown and keyup aren't necessary. IE8 fails to fire
|
|
||||||
// propertychange on the first input event after setting `value` from a
|
|
||||||
// script and fires only keydown, keypress, keyup. Catching keyup usually
|
|
||||||
// gets it and catching keydown lets us fire an event for the first
|
|
||||||
// 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.
|
|
||||||
return getInstIfValueChanged(activeElementInst);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function getTargetInstForInputOrChangeEvent(
|
||||||
* SECTION: handle `click` event
|
topLevelType,
|
||||||
*/
|
targetInst,
|
||||||
function shouldUseClickEvent(elem) {
|
targetNode,
|
||||||
// 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 (
|
|
||||||
nodeName &&
|
|
||||||
nodeName.toLowerCase() === 'input' &&
|
|
||||||
(elem.type === 'checkbox' || elem.type === 'radio')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTargetInstForClickEvent(topLevelType, targetInst) {
|
|
||||||
if (topLevelType === 'topClick') {
|
|
||||||
return getInstIfValueChanged(targetInst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTargetInstForInputOrChangeEvent(topLevelType, targetInst) {
|
|
||||||
if (topLevelType === 'topInput' || topLevelType === 'topChange') {
|
if (topLevelType === 'topInput' || topLevelType === 'topChange') {
|
||||||
return getInstIfValueChanged(targetInst);
|
return getInstIfValueChanged(targetInst, targetNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTargetInstForChangeEvent(topLevelType, targetInst, targetNode) {
|
||||||
|
if (topLevelType === 'topChange') {
|
||||||
|
return getInstIfValueChanged(targetInst, targetNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -260,34 +143,33 @@ function handleControlledInputBlur(inst, node) {
|
||||||
var ChangeEventPlugin = {
|
var ChangeEventPlugin = {
|
||||||
eventTypes: eventTypes,
|
eventTypes: eventTypes,
|
||||||
|
|
||||||
_isInputEventSupported: isInputEventSupported,
|
extractEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget) {
|
||||||
|
|
||||||
extractEvents: function(
|
|
||||||
topLevelType,
|
|
||||||
targetInst,
|
|
||||||
nativeEvent,
|
|
||||||
nativeEventTarget,
|
|
||||||
) {
|
|
||||||
var targetNode = targetInst
|
var targetNode = targetInst
|
||||||
? ReactDOMComponentTree.getNodeFromInstance(targetInst)
|
? ReactDOMComponentTree.getNodeFromInstance(targetInst)
|
||||||
: window;
|
: window;
|
||||||
|
|
||||||
|
// On the selectionchange event, the target is the document which isn't
|
||||||
|
// helpful becasue we need the input, so we use the activeElement instead.
|
||||||
|
if (!isTextInputEventSupported && topLevelType === 'topSelectionChange') {
|
||||||
|
nativeEventTarget = targetNode = getActiveElement();
|
||||||
|
|
||||||
|
if (targetNode) {
|
||||||
|
targetInst = ReactDOMComponentTree.getInstanceFromNode(targetNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var getTargetInstFunc, handleEventFunc;
|
var getTargetInstFunc, handleEventFunc;
|
||||||
|
|
||||||
if (shouldUseChangeEvent(targetNode)) {
|
if (shouldUseChangeEvent(targetNode)) {
|
||||||
getTargetInstFunc = getTargetInstForChangeEvent;
|
getTargetInstFunc = getTargetInstForChangeEvent;
|
||||||
} else if (isTextInputElement(targetNode)) {
|
} else if (isTextInputElement(targetNode) && !isTextInputEventSupported) {
|
||||||
if (isInputEventSupported) {
|
|
||||||
getTargetInstFunc = getTargetInstForInputOrChangeEvent;
|
|
||||||
} else {
|
|
||||||
getTargetInstFunc = getTargetInstForInputEventPolyfill;
|
getTargetInstFunc = getTargetInstForInputEventPolyfill;
|
||||||
handleEventFunc = handleEventsForInputEventPolyfill;
|
} else {
|
||||||
}
|
getTargetInstFunc = getTargetInstForInputOrChangeEvent;
|
||||||
} else if (shouldUseClickEvent(targetNode)) {
|
|
||||||
getTargetInstFunc = getTargetInstForClickEvent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getTargetInstFunc) {
|
if (getTargetInstFunc) {
|
||||||
var inst = getTargetInstFunc(topLevelType, targetInst);
|
var inst = getTargetInstFunc(topLevelType, targetInst, targetNode);
|
||||||
if (inst) {
|
if (inst) {
|
||||||
var event = createAndAccumulateChangeEvent(
|
var event = createAndAccumulateChangeEvent(
|
||||||
inst,
|
inst,
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ var React = require('react');
|
||||||
var ReactDOM = require('react-dom');
|
var ReactDOM = require('react-dom');
|
||||||
var ReactTestUtils = require('react-dom/test-utils');
|
var ReactTestUtils = require('react-dom/test-utils');
|
||||||
// TODO: can we express this test with only public API?
|
// TODO: can we express this test with only public API?
|
||||||
var ChangeEventPlugin = require('ChangeEventPlugin');
|
|
||||||
var inputValueTracking = require('inputValueTracking');
|
var inputValueTracking = require('inputValueTracking');
|
||||||
|
|
||||||
function getTrackedValue(elem) {
|
function getTrackedValue(elem) {
|
||||||
|
|
@ -54,7 +53,7 @@ describe('ChangeEventPlugin', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
setUntrackedValue(input, true);
|
setUntrackedValue(input, true);
|
||||||
ReactTestUtils.SimulateNative.click(input);
|
ReactTestUtils.SimulateNative.change(input);
|
||||||
|
|
||||||
expect(called).toBe(1);
|
expect(called).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
@ -103,12 +102,12 @@ describe('ChangeEventPlugin', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
input.checked = true;
|
input.checked = true;
|
||||||
ReactTestUtils.SimulateNative.click(input);
|
ReactTestUtils.SimulateNative.change(input);
|
||||||
expect(called).toBe(0);
|
expect(called).toBe(0);
|
||||||
|
|
||||||
input.checked = false;
|
input.checked = false;
|
||||||
setTrackedValue(input, undefined);
|
setTrackedValue(input, undefined);
|
||||||
ReactTestUtils.SimulateNative.click(input);
|
ReactTestUtils.SimulateNative.change(input);
|
||||||
|
|
||||||
expect(called).toBe(1);
|
expect(called).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
@ -131,8 +130,8 @@ describe('ChangeEventPlugin', () => {
|
||||||
<input type="radio" onChange={cb} />,
|
<input type="radio" onChange={cb} />,
|
||||||
);
|
);
|
||||||
setUntrackedValue(input, true);
|
setUntrackedValue(input, true);
|
||||||
ReactTestUtils.SimulateNative.click(input);
|
ReactTestUtils.SimulateNative.change(input);
|
||||||
ReactTestUtils.SimulateNative.click(input);
|
ReactTestUtils.SimulateNative.input(input);
|
||||||
expect(called).toBe(1);
|
expect(called).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -182,10 +181,6 @@ describe('ChangeEventPlugin', () => {
|
||||||
expect(e.type).toBe('change');
|
expect(e.type).toBe('change');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ChangeEventPlugin._isInputEventSupported) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var input = ReactTestUtils.renderIntoDocument(
|
var input = ReactTestUtils.renderIntoDocument(
|
||||||
<input type="range" onChange={cb} />,
|
<input type="range" onChange={cb} />,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user