mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Merge branch 'master' into rollup
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
**What is the current behavior?**
|
||||
|
||||
**If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://jsfiddle.net or similar (template: https://jsfiddle.net/reactjs/69z2wepo/).**
|
||||
**If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://jsfiddle.net or similar (template: https://jsfiddle.net/84v837e9/).**
|
||||
|
||||
**What is the expected behavior?**
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@ The [`ReactTransitionGroup`](#low-level-api-reacttransitiongroup) add-on compone
|
||||
**Importing**
|
||||
|
||||
```javascript
|
||||
import ReactCSSTransitionGroup from 'react-addons-css-transition-group' // ES6
|
||||
var ReactCSSTransitionGroup = require('react-addons-css-transition-group') // ES5 with npm
|
||||
import ReactCSSTransitionGroup from 'react-addons-css-transition-group'; // ES6
|
||||
var ReactCSSTransitionGroup = require('react-addons-css-transition-group'); // ES5 with npm
|
||||
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup; // ES5 with react-with-addons.js
|
||||
```
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ next: perf.html
|
||||
**Importing**
|
||||
|
||||
```javascript
|
||||
import createFragment from 'react-addons-create-fragment' // ES6
|
||||
var createFragment = require('react-addons-create-fragment') // ES5 with npm
|
||||
import createFragment from 'react-addons-create-fragment'; // ES6
|
||||
var createFragment = require('react-addons-create-fragment'); // ES5 with npm
|
||||
var createFragment = React.addons.createFragment; // ES5 with react-with-addons.js
|
||||
```
|
||||
|
||||
@@ -42,8 +42,8 @@ To solve this problem, you can use the `createFragment` add-on to give keys to t
|
||||
|
||||
Instead of creating arrays, we write:
|
||||
|
||||
```js
|
||||
import createFragment from 'react-addons-create-fragment'
|
||||
```javascript
|
||||
import createFragment from 'react-addons-create-fragment';
|
||||
|
||||
function Swapper(props) {
|
||||
let children;
|
||||
|
||||
@@ -11,8 +11,8 @@ next: test-utils.html
|
||||
**Importing**
|
||||
|
||||
```javascript
|
||||
import Perf from 'react-addons-perf' // ES6
|
||||
var Perf = require('react-addons-perf') // ES5 with npm
|
||||
import Perf from 'react-addons-perf'; // ES6
|
||||
var Perf = require('react-addons-perf'); // ES5 with npm
|
||||
var Perf = React.addons.Perf; // ES5 with react-with-addons.js
|
||||
```
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ category: Add-Ons
|
||||
**Importing**
|
||||
|
||||
```javascript
|
||||
import PureRenderMixin from 'react-addons-pure-render-mixin' // ES6
|
||||
var PureRenderMixin = require('react-addons-pure-render-mixin') // ES5 with npm
|
||||
import PureRenderMixin from 'react-addons-pure-render-mixin'; // ES6
|
||||
var PureRenderMixin = require('react-addons-pure-render-mixin'); // ES5 with npm
|
||||
var PureRenderMixin = React.addons.PureRenderMixin; // ES5 with react-with-addons.js
|
||||
```
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ category: Reference
|
||||
**Importing**
|
||||
|
||||
```javascript
|
||||
import shallowCompare from 'react-addons-shallow-compare' // ES6
|
||||
var shallowCompare = require('react-addons-shallow-compare') // ES5 with npm
|
||||
import shallowCompare from 'react-addons-shallow-compare'; // ES6
|
||||
var shallowCompare = require('react-addons-shallow-compare'); // ES5 with npm
|
||||
var shallowCompare = React.addons.shallowCompare; // ES5 with react-with-addons.js
|
||||
```
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ prev: perf.html
|
||||
**Importing**
|
||||
|
||||
```javascript
|
||||
import ReactTestUtils from 'react-addons-test-utils' // ES6
|
||||
var ReactTestUtils = require('react-addons-test-utils') // ES5 with npm
|
||||
import ReactTestUtils from 'react-addons-test-utils'; // ES6
|
||||
var ReactTestUtils = require('react-addons-test-utils'); // ES5 with npm
|
||||
var ReactTestUtils = React.addons.TestUtils; // ES5 with react-with-addons.js
|
||||
```
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ next: update.html
|
||||
**Importing**
|
||||
|
||||
```javascript
|
||||
import LinkedStateMixin from 'react-addons-linked-state-mixin' // ES6
|
||||
var LinkedStateMixin = require('react-addons-linked-state-mixin') // ES5 with npm
|
||||
import LinkedStateMixin from 'react-addons-linked-state-mixin'; // ES6
|
||||
var LinkedStateMixin = require('react-addons-linked-state-mixin'); // ES5 with npm
|
||||
var LinkedStateMixin = React.addons.LinkedStateMixin; // ES5 with react-with-addons.js
|
||||
```
|
||||
|
||||
|
||||
@@ -45,9 +45,11 @@ const Header = React.createClass({
|
||||
<option value="/range-inputs">Range Inputs</option>
|
||||
<option value="/text-inputs">Text Inputs</option>
|
||||
<option value="/number-inputs">Number Input</option>
|
||||
<option value="/password-inputs">Password Input</option>
|
||||
<option value="/selects">Selects</option>
|
||||
<option value="/textareas">Textareas</option>
|
||||
<option value="/input-change-events">Input change events</option>
|
||||
<option value="/buttons">Buttons</option>
|
||||
</select>
|
||||
</label>
|
||||
<label htmlFor="react_version">
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
const React = window.React;
|
||||
|
||||
import FixtureSet from '../../FixtureSet';
|
||||
import TestCase from '../../TestCase';
|
||||
|
||||
function onButtonClick() {
|
||||
window.alert(`This shouldn't have happened!`);
|
||||
}
|
||||
|
||||
export default class ButtonTestCases extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<FixtureSet title="Buttons">
|
||||
<TestCase
|
||||
title="onClick with disabled buttons"
|
||||
description="The onClick event handler should not be invoked when clicking on a disabled buyaton">
|
||||
<TestCase.Steps>
|
||||
<li>Click on the disabled button</li>
|
||||
</TestCase.Steps>
|
||||
<TestCase.ExpectedResult>
|
||||
Nothing should happen
|
||||
</TestCase.ExpectedResult>
|
||||
<button disabled onClick={onButtonClick}>Click Me</button>
|
||||
</TestCase>
|
||||
<TestCase
|
||||
title="onClick with disabled buttons containing other elements"
|
||||
description="The onClick event handler should not be invoked when clicking on a disabled button that contains other elements">
|
||||
<TestCase.Steps>
|
||||
<li>Click on the disabled button, which contains a span</li>
|
||||
</TestCase.Steps>
|
||||
<TestCase.ExpectedResult>
|
||||
Nothing should happen
|
||||
</TestCase.ExpectedResult>
|
||||
<button disabled onClick={onButtonClick}>
|
||||
<span>Click Me</span>
|
||||
</button>
|
||||
</TestCase>
|
||||
</FixtureSet>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,9 @@ import TextInputFixtures from './text-inputs';
|
||||
import SelectFixtures from './selects';
|
||||
import TextAreaFixtures from './textareas';
|
||||
import InputChangeEvents from './input-change-events';
|
||||
import NumberInputFixtures from './number-inputs/';
|
||||
import NumberInputFixtures from './number-inputs';
|
||||
import PasswordInputFixtures from './password-inputs';
|
||||
import ButtonFixtures from './buttons';
|
||||
|
||||
/**
|
||||
* A simple routing component that renders the appropriate
|
||||
@@ -25,6 +27,10 @@ const FixturesPage = React.createClass({
|
||||
return <InputChangeEvents />;
|
||||
case '/number-inputs':
|
||||
return <NumberInputFixtures />;
|
||||
case '/password-inputs':
|
||||
return <PasswordInputFixtures />;
|
||||
case '/buttons':
|
||||
return <ButtonFixtures />
|
||||
default:
|
||||
return <p>Please select a test fixture.</p>;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
const React = window.React;
|
||||
|
||||
import Fixture from '../../Fixture';
|
||||
|
||||
const PasswordTestCase = React.createClass({
|
||||
getInitialState() {
|
||||
return { value: '' };
|
||||
},
|
||||
onChange(event) {
|
||||
this.setState({ value: event.target.value })
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<Fixture>
|
||||
<div>{this.props.children}</div>
|
||||
|
||||
<div className="control-box">
|
||||
<fieldset>
|
||||
<legend>Controlled</legend>
|
||||
<input type="password" value={this.state.value} onChange={this.onChange} />
|
||||
<span className="hint"> Value: {JSON.stringify(this.state.value)}</span>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Uncontrolled</legend>
|
||||
<input type="password" defaultValue="" />
|
||||
</fieldset>
|
||||
</div>
|
||||
</Fixture>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default PasswordTestCase;
|
||||
@@ -0,0 +1,33 @@
|
||||
const React = window.React;
|
||||
|
||||
import FixtureSet from '../../FixtureSet';
|
||||
import TestCase from '../../TestCase';
|
||||
import PasswordTestCase from './PasswordTestCase'
|
||||
|
||||
const NumberInputs = React.createClass({
|
||||
render() {
|
||||
return (
|
||||
<FixtureSet title="Password inputs" description="">
|
||||
<TestCase
|
||||
title="The show password icon"
|
||||
description={`
|
||||
Some browsers have an unmask password icon that React accidentally
|
||||
prevents the display of.
|
||||
`}
|
||||
affectedBrowsers="IE Edge, IE 11">
|
||||
<TestCase.Steps>
|
||||
<li>Type any string (not an actual password</li>
|
||||
</TestCase.Steps>
|
||||
|
||||
<TestCase.ExpectedResult>
|
||||
The field should include the "unmasking password" icon.
|
||||
</TestCase.ExpectedResult>
|
||||
|
||||
<PasswordTestCase />
|
||||
</TestCase>
|
||||
</FixtureSet>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default NumberInputs;
|
||||
@@ -18,6 +18,12 @@ select {
|
||||
width: 12rem;
|
||||
}
|
||||
|
||||
button {
|
||||
margin: 10px;
|
||||
font-size: 18px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
|
||||
.header {
|
||||
background: #222;
|
||||
|
||||
Vendored
+15
@@ -26,6 +26,12 @@ declare module 'TextInputState' {
|
||||
declare function blurTextInput(object : any) : void;
|
||||
declare function focusTextInput(object : any) : void;
|
||||
}
|
||||
declare module 'ExceptionsManager' {
|
||||
declare function handleException(
|
||||
error: Error,
|
||||
isFatal: boolean,
|
||||
) : void;
|
||||
}
|
||||
declare module 'UIManager' {
|
||||
declare var customBubblingEventTypes : Object;
|
||||
declare var customDirectEventTypes : Object;
|
||||
@@ -58,6 +64,15 @@ declare module 'UIManager' {
|
||||
viewName : string,
|
||||
props : ?Object,
|
||||
) : void;
|
||||
declare function __takeSnapshot(
|
||||
view ?: 'window' | Element<any> | number,
|
||||
options ?: {
|
||||
width ?: number,
|
||||
height ?: number,
|
||||
format ?: 'png' | 'jpeg',
|
||||
quality ?: number,
|
||||
},
|
||||
) : Promise<any>;
|
||||
}
|
||||
declare module 'View' {
|
||||
declare var exports : typeof ReactComponent;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
|
||||
* should not warn when server-side rendering `onScroll`
|
||||
* should warn about incorrect casing on properties (ssr)
|
||||
* should warn about incorrect casing on event handlers (ssr)
|
||||
* should warn about class (ssr)
|
||||
|
||||
@@ -868,7 +868,6 @@ src/renderers/dom/shared/__tests__/ReactBrowserEventEmitter-test.js
|
||||
* should work with event plugins without dependencies
|
||||
* should work with event plugins with dependencies
|
||||
* should bubble onTouchTap
|
||||
* should not crash ensureScrollValueMonitoring when createEvent returns null
|
||||
|
||||
src/renderers/dom/shared/__tests__/ReactDOM-test.js
|
||||
* allows a DOM element to be used with a string
|
||||
@@ -952,7 +951,6 @@ src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
|
||||
* should report component containing invalid styles
|
||||
* should properly escape text content and attributes values
|
||||
* unmounts children before unsetting DOM node info
|
||||
* should warn about the `onScroll` issue when unsupported (IE8)
|
||||
* should throw when an invalid tag name is used server-side
|
||||
* should throw when an attack vector is used server-side
|
||||
* should throw when an invalid tag name is used
|
||||
@@ -1596,6 +1594,9 @@ src/renderers/shared/fiber/__tests__/ReactCoroutine-test.js
|
||||
* should unmount a composite in a coroutine
|
||||
* should handle deep updates in coroutine
|
||||
|
||||
src/renderers/shared/fiber/__tests__/ReactFiberHostContext-test.js
|
||||
* works with null host context
|
||||
|
||||
src/renderers/shared/fiber/__tests__/ReactIncremental-test.js
|
||||
* should render a simple component
|
||||
* should render a simple component, in steps if needed
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Copyright 2013-2015, 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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
handleException: jest.fn(),
|
||||
};
|
||||
@@ -20,6 +20,7 @@ var RCTUIManager = {
|
||||
updateView: jest.fn(),
|
||||
removeSubviewsFromContainerWithID: jest.fn(),
|
||||
replaceExistingNonRootView: jest.fn(),
|
||||
__takeSnapshot: jest.fn(),
|
||||
customBubblingEventTypes: {
|
||||
topBlur: {
|
||||
phasedRegistrationNames: {
|
||||
|
||||
@@ -28,7 +28,6 @@ var {getCurrentFiberOwnerName} = require('ReactDebugCurrentFiber');
|
||||
|
||||
var emptyFunction = require('fbjs/lib/emptyFunction');
|
||||
var invariant = require('fbjs/lib/invariant');
|
||||
var isEventSupported = require('isEventSupported');
|
||||
var setInnerHTML = require('setInnerHTML');
|
||||
var setTextContent = require('setTextContent');
|
||||
var inputValueTracking = require('inputValueTracking');
|
||||
@@ -145,14 +144,6 @@ if (__DEV__) {
|
||||
}
|
||||
|
||||
function ensureListeningTo(rootContainerElement, registrationName) {
|
||||
if (__DEV__) {
|
||||
// IE8 has no API for event capturing and the `onScroll` event doesn't
|
||||
// bubble.
|
||||
warning(
|
||||
registrationName !== 'onScroll' || isEventSupported('scroll', true),
|
||||
"This browser doesn't support the `onScroll` event",
|
||||
);
|
||||
}
|
||||
var isDocumentFragment = rootContainerElement.nodeType === DOC_FRAGMENT_TYPE;
|
||||
var doc = isDocumentFragment
|
||||
? rootContainerElement
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
var EventPluginRegistry = require('EventPluginRegistry');
|
||||
var ReactEventEmitterMixin = require('ReactEventEmitterMixin');
|
||||
var ViewportMetrics = require('ViewportMetrics');
|
||||
|
||||
var getVendorPrefixedEventName = require('getVendorPrefixedEventName');
|
||||
var isEventSupported = require('isEventSupported');
|
||||
@@ -73,9 +72,7 @@ var isEventSupported = require('isEventSupported');
|
||||
* React Core . General Purpose Event Plugin System
|
||||
*/
|
||||
|
||||
var hasEventPageXY;
|
||||
var alreadyListeningTo = {};
|
||||
var isMonitoringScrollValue = false;
|
||||
var reactTopListenersCounter = 0;
|
||||
|
||||
// For events like 'submit' which don't consistently bubble (which we trap at a
|
||||
@@ -262,45 +259,22 @@ var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, {
|
||||
);
|
||||
}
|
||||
} else if (dependency === 'topScroll') {
|
||||
if (isEventSupported('scroll', true)) {
|
||||
ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(
|
||||
'topScroll',
|
||||
'scroll',
|
||||
mountAt,
|
||||
);
|
||||
} else {
|
||||
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
|
||||
'topScroll',
|
||||
'scroll',
|
||||
ReactBrowserEventEmitter.ReactEventListener.WINDOW_HANDLE,
|
||||
);
|
||||
}
|
||||
ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(
|
||||
'topScroll',
|
||||
'scroll',
|
||||
mountAt,
|
||||
);
|
||||
} else if (dependency === 'topFocus' || dependency === 'topBlur') {
|
||||
if (isEventSupported('focus', true)) {
|
||||
ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(
|
||||
'topFocus',
|
||||
'focus',
|
||||
mountAt,
|
||||
);
|
||||
ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(
|
||||
'topBlur',
|
||||
'blur',
|
||||
mountAt,
|
||||
);
|
||||
} else if (isEventSupported('focusin')) {
|
||||
// IE has `focusin` and `focusout` events which bubble.
|
||||
// @see http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html
|
||||
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
|
||||
'topFocus',
|
||||
'focusin',
|
||||
mountAt,
|
||||
);
|
||||
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
|
||||
'topBlur',
|
||||
'focusout',
|
||||
mountAt,
|
||||
);
|
||||
}
|
||||
ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(
|
||||
'topFocus',
|
||||
'focus',
|
||||
mountAt,
|
||||
);
|
||||
ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(
|
||||
'topBlur',
|
||||
'blur',
|
||||
mountAt,
|
||||
);
|
||||
|
||||
// to make sure blur and focus event listeners are only attached once
|
||||
isListening.topBlur = true;
|
||||
@@ -367,41 +341,6 @@ var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, {
|
||||
handle,
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Protect against document.createEvent() returning null
|
||||
* Some popup blocker extensions appear to do this:
|
||||
* https://github.com/facebook/react/issues/6887
|
||||
*/
|
||||
supportsEventPageXY: function() {
|
||||
if (!document.createEvent) {
|
||||
return false;
|
||||
}
|
||||
var ev = document.createEvent('MouseEvent');
|
||||
return ev != null && 'pageX' in ev;
|
||||
},
|
||||
|
||||
/**
|
||||
* Listens to window scroll and resize events. We cache scroll values so that
|
||||
* application code can access them without triggering reflows.
|
||||
*
|
||||
* ViewportMetrics is only used by SyntheticMouse/TouchEvent and only when
|
||||
* pageX/pageY isn't supported (legacy browsers).
|
||||
*
|
||||
* NOTE: Scroll events do not bubble.
|
||||
*
|
||||
* @see http://www.quirksmode.org/dom/events/scroll.html
|
||||
*/
|
||||
ensureScrollValueMonitoring: function() {
|
||||
if (hasEventPageXY === undefined) {
|
||||
hasEventPageXY = ReactBrowserEventEmitter.supportsEventPageXY();
|
||||
}
|
||||
if (!hasEventPageXY && !isMonitoringScrollValue) {
|
||||
var refresh = ViewportMetrics.refreshScrollValues;
|
||||
ReactBrowserEventEmitter.ReactEventListener.monitorScrollValue(refresh);
|
||||
isMonitoringScrollValue = true;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = ReactBrowserEventEmitter;
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
|
||||
|
||||
var getNodeForCharacterOffset = require('getNodeForCharacterOffset');
|
||||
var getTextContentAccessor = require('getTextContentAccessor');
|
||||
|
||||
@@ -25,39 +23,6 @@ function isCollapsed(anchorNode, anchorOffset, focusNode, focusOffset) {
|
||||
return anchorNode === focusNode && anchorOffset === focusOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the appropriate anchor and focus node/offset pairs for IE.
|
||||
*
|
||||
* The catch here is that IE's selection API doesn't provide information
|
||||
* about whether the selection is forward or backward, so we have to
|
||||
* behave as though it's always forward.
|
||||
*
|
||||
* IE text differs from modern selection in that it behaves as though
|
||||
* block elements end with a new line. This means character offsets will
|
||||
* differ between the two APIs.
|
||||
*
|
||||
* @param {DOMElement} node
|
||||
* @return {object}
|
||||
*/
|
||||
function getIEOffsets(node) {
|
||||
var selection = document.selection;
|
||||
var selectedRange = selection.createRange();
|
||||
var selectedLength = selectedRange.text.length;
|
||||
|
||||
// Duplicate selection so we can move range without breaking user selection.
|
||||
var fromStart = selectedRange.duplicate();
|
||||
fromStart.moveToElementText(node);
|
||||
fromStart.setEndPoint('EndToStart', selectedRange);
|
||||
|
||||
var startOffset = fromStart.text.length;
|
||||
var endOffset = startOffset + selectedLength;
|
||||
|
||||
return {
|
||||
start: startOffset,
|
||||
end: endOffset,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DOMElement} node
|
||||
* @return {?object}
|
||||
@@ -130,32 +95,6 @@ function getModernOffsets(node) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DOMElement|DOMTextNode} node
|
||||
* @param {object} offsets
|
||||
*/
|
||||
function setIEOffsets(node, offsets) {
|
||||
var range = document.selection.createRange().duplicate();
|
||||
var start, end;
|
||||
|
||||
if (offsets.end === undefined) {
|
||||
start = offsets.start;
|
||||
end = start;
|
||||
} else if (offsets.start > offsets.end) {
|
||||
start = offsets.end;
|
||||
end = offsets.start;
|
||||
} else {
|
||||
start = offsets.start;
|
||||
end = offsets.end;
|
||||
}
|
||||
|
||||
range.moveToElementText(node);
|
||||
range.moveStart('character', start);
|
||||
range.setEndPoint('EndToStart', range);
|
||||
range.moveEnd('character', end - start);
|
||||
range.select();
|
||||
}
|
||||
|
||||
/**
|
||||
* In modern non-IE browsers, we can support both forward and backward
|
||||
* selections.
|
||||
@@ -204,21 +143,17 @@ function setModernOffsets(node, offsets) {
|
||||
}
|
||||
}
|
||||
|
||||
var useIEOffsets = ExecutionEnvironment.canUseDOM &&
|
||||
'selection' in document &&
|
||||
!('getSelection' in window);
|
||||
|
||||
var ReactDOMSelection = {
|
||||
/**
|
||||
* @param {DOMElement} node
|
||||
*/
|
||||
getOffsets: useIEOffsets ? getIEOffsets : getModernOffsets,
|
||||
getOffsets: getModernOffsets,
|
||||
|
||||
/**
|
||||
* @param {DOMElement|DOMTextNode} node
|
||||
* @param {object} offsets
|
||||
*/
|
||||
setOffsets: useIEOffsets ? setIEOffsets : setModernOffsets,
|
||||
setOffsets: setModernOffsets,
|
||||
};
|
||||
|
||||
module.exports = ReactDOMSelection;
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
'use strict';
|
||||
|
||||
var EventListener = require('fbjs/lib/EventListener');
|
||||
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
|
||||
var PooledClass = require('PooledClass');
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
var ReactGenericBatching = require('ReactGenericBatching');
|
||||
@@ -111,8 +110,6 @@ var ReactEventListener = {
|
||||
_enabled: true,
|
||||
_handleTopLevel: null,
|
||||
|
||||
WINDOW_HANDLE: ExecutionEnvironment.canUseDOM ? window : null,
|
||||
|
||||
setHandleTopLevel: function(handleTopLevel) {
|
||||
ReactEventListener._handleTopLevel = handleTopLevel;
|
||||
},
|
||||
|
||||
@@ -98,20 +98,6 @@ var ReactInputSelection = {
|
||||
start: input.selectionStart,
|
||||
end: input.selectionEnd,
|
||||
};
|
||||
} 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
|
||||
// be in our element.
|
||||
if (range.parentElement() === input) {
|
||||
selection = {
|
||||
start: -range.moveStart('character', -input.value.length),
|
||||
end: -range.moveEnd('character', -input.value.length),
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// Content editable or old IE textarea.
|
||||
selection = ReactDOMSelection.getOffsets(input);
|
||||
@@ -136,15 +122,6 @@ 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')
|
||||
) {
|
||||
var range = input.createTextRange();
|
||||
range.collapse(true);
|
||||
range.moveStart('character', start);
|
||||
range.moveEnd('character', end - start);
|
||||
range.select();
|
||||
} else {
|
||||
ReactDOMSelection.setOffsets(input, offsets);
|
||||
}
|
||||
|
||||
@@ -435,20 +435,4 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
expect(idCallOrder[1] === PARENT).toBe(true);
|
||||
expect(idCallOrder[2] === GRANDPARENT).toBe(true);
|
||||
});
|
||||
|
||||
it('should not crash ensureScrollValueMonitoring when createEvent returns null', () => {
|
||||
var originalCreateEvent = document.createEvent;
|
||||
document.createEvent = function() {
|
||||
return null;
|
||||
};
|
||||
spyOn(document, 'createEvent');
|
||||
|
||||
try {
|
||||
var hasEventPageXY = ReactBrowserEventEmitter.supportsEventPageXY();
|
||||
expect(document.createEvent.calls.count()).toBe(1);
|
||||
expect(hasEventPageXY).toBe(false);
|
||||
} finally {
|
||||
document.createEvent = originalCreateEvent;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1233,30 +1233,6 @@ describe('ReactDOMComponent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('onScroll warning', () => {
|
||||
it('should warn about the `onScroll` issue when unsupported (IE8)', () => {
|
||||
// Mock this here so we can mimic IE8 support. We require isEventSupported
|
||||
// before React so it's pre-mocked before React would require it.
|
||||
jest.resetModules().mock('isEventSupported');
|
||||
var isEventSupported = require('isEventSupported');
|
||||
isEventSupported.mockReturnValueOnce(false);
|
||||
ReactTestUtils = require('ReactTestUtils');
|
||||
|
||||
spyOn(console, 'error');
|
||||
ReactTestUtils.renderIntoDocument(<div onScroll={function() {}} />);
|
||||
expectDev(console.error.calls.count()).toBe(1);
|
||||
expectDev(console.error.calls.argsFor(0)[0]).toBe(
|
||||
"Warning: This browser doesn't support the `onScroll` event",
|
||||
);
|
||||
});
|
||||
|
||||
it('should not warn when server-side rendering `onScroll`', () => {
|
||||
spyOn(console, 'error');
|
||||
ReactDOMServer.renderToString(<div onScroll={() => {}} />);
|
||||
expectDev(console.error).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('tag sanitization', () => {
|
||||
it('should throw when an invalid tag name is used server-side', () => {
|
||||
var hackzor = React.createElement('script tag');
|
||||
|
||||
@@ -71,13 +71,6 @@ function shouldUseChangeEvent(elem) {
|
||||
(nodeName === 'input' && elem.type === 'file');
|
||||
}
|
||||
|
||||
var doesChangeEventBubble = false;
|
||||
if (ExecutionEnvironment.canUseDOM) {
|
||||
// See `handleChange` comment below
|
||||
doesChangeEventBubble = isEventSupported('change') &&
|
||||
(!document.documentMode || document.documentMode > 8);
|
||||
}
|
||||
|
||||
function manualDispatchChangeEvent(nativeEvent) {
|
||||
var event = createAndAccumulateChangeEvent(
|
||||
activeElementInst,
|
||||
@@ -104,21 +97,6 @@ function runEventInBatch(event) {
|
||||
EventPluginHub.processEventQueue(false);
|
||||
}
|
||||
|
||||
function startWatchingForChangeEventIE8(target, targetInst) {
|
||||
activeElement = target;
|
||||
activeElementInst = targetInst;
|
||||
activeElement.attachEvent('onchange', manualDispatchChangeEvent);
|
||||
}
|
||||
|
||||
function stopWatchingForChangeEventIE8() {
|
||||
if (!activeElement) {
|
||||
return;
|
||||
}
|
||||
activeElement.detachEvent('onchange', manualDispatchChangeEvent);
|
||||
activeElement = null;
|
||||
activeElementInst = null;
|
||||
}
|
||||
|
||||
function getInstIfValueChanged(targetInst) {
|
||||
if (inputValueTracking.updateValueIfChanged(targetInst)) {
|
||||
return targetInst;
|
||||
@@ -131,17 +109,6 @@ function getTargetInstForChangeEvent(topLevelType, 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.
|
||||
stopWatchingForChangeEventIE8();
|
||||
startWatchingForChangeEventIE8(target, targetInst);
|
||||
} else if (topLevelType === 'topBlur') {
|
||||
stopWatchingForChangeEventIE8();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SECTION: handle `input` event
|
||||
*/
|
||||
@@ -192,9 +159,6 @@ function handlePropertyChange(nativeEvent) {
|
||||
|
||||
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, 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
|
||||
@@ -305,11 +269,7 @@ var ChangeEventPlugin = {
|
||||
|
||||
var getTargetInstFunc, handleEventFunc;
|
||||
if (shouldUseChangeEvent(targetNode)) {
|
||||
if (doesChangeEventBubble) {
|
||||
getTargetInstFunc = getTargetInstForChangeEvent;
|
||||
} else {
|
||||
handleEventFunc = handleEventsForChangeEventIE8;
|
||||
}
|
||||
getTargetInstFunc = getTargetInstForChangeEvent;
|
||||
} else if (isTextInputElement(targetNode)) {
|
||||
if (isInputEventSupported) {
|
||||
getTargetInstFunc = getTargetInstForInputOrChangeEvent;
|
||||
|
||||
@@ -80,14 +80,6 @@ function getSelection(node) {
|
||||
focusNode: selection.focusNode,
|
||||
focusOffset: selection.focusOffset,
|
||||
};
|
||||
} else if (document.selection) {
|
||||
var range = document.selection.createRange();
|
||||
return {
|
||||
parentElement: range.parentElement(),
|
||||
text: range.text,
|
||||
top: range.boundingTop,
|
||||
left: range.boundingLeft,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ var EventPluginUtils = require('EventPluginUtils');
|
||||
var EventPropagators = require('EventPropagators');
|
||||
var SyntheticUIEvent = require('SyntheticUIEvent');
|
||||
var TouchEventUtils = require('fbjs/lib/TouchEventUtils');
|
||||
var ViewportMetrics = require('ViewportMetrics');
|
||||
|
||||
var isStartish = EventPluginUtils.isStartish;
|
||||
var isEndish = EventPluginUtils.isEndish;
|
||||
@@ -70,9 +69,7 @@ function getAxisCoordOfEvent(
|
||||
if (singleTouch) {
|
||||
return singleTouch[axis.page];
|
||||
}
|
||||
return axis.page in nativeEvent
|
||||
? nativeEvent[axis.page]
|
||||
: nativeEvent[axis.client] + ViewportMetrics[axis.envScroll];
|
||||
return nativeEvent[axis.page];
|
||||
}
|
||||
|
||||
function getDistance(coords: CoordinatesType, nativeEvent: _Touch): number {
|
||||
|
||||
@@ -11,20 +11,14 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
|
||||
var DOMNamespaces = require('DOMNamespaces');
|
||||
|
||||
var WHITESPACE_TEST = /^[ \r\n\t\f]/;
|
||||
var NONVISIBLE_TEST = /<(!--|link|noscript|meta|script|style)[ \r\n\t\f\/>]/;
|
||||
|
||||
var createMicrosoftUnsafeLocalFunction = require('createMicrosoftUnsafeLocalFunction');
|
||||
|
||||
// SVG temp container for IE lacking innerHTML
|
||||
var reusableSVGContainer;
|
||||
|
||||
/**
|
||||
* Set the innerHTML property of a node, ensuring that whitespace is preserved
|
||||
* even in IE8.
|
||||
* Set the innerHTML property of a node
|
||||
*
|
||||
* @param {DOMElement} node
|
||||
* @param {string} html
|
||||
@@ -47,56 +41,4 @@ var setInnerHTML = createMicrosoftUnsafeLocalFunction(function(node, html) {
|
||||
}
|
||||
});
|
||||
|
||||
if (ExecutionEnvironment.canUseDOM) {
|
||||
// IE8: When updating a just created node with innerHTML only leading
|
||||
// whitespace is removed. When updating an existing node with innerHTML
|
||||
// whitespace in root TextNodes is also collapsed.
|
||||
// @see quirksmode.org/bugreports/archives/2004/11/innerhtml_and_t.html
|
||||
|
||||
// Feature detection; only IE8 is known to behave improperly like this.
|
||||
var testElement = document.createElement('div');
|
||||
testElement.innerHTML = ' ';
|
||||
if (testElement.innerHTML === '') {
|
||||
setInnerHTML = function(node, html) {
|
||||
// Magic theory: IE8 supposedly differentiates between added and updated
|
||||
// nodes when processing innerHTML, innerHTML on updated nodes suffers
|
||||
// from worse whitespace behavior. Re-adding a node like this triggers
|
||||
// the initial and more favorable whitespace behavior.
|
||||
// TODO: What to do on a detached node?
|
||||
if (node.parentNode) {
|
||||
node.parentNode.replaceChild(node, node);
|
||||
}
|
||||
|
||||
// We also implement a workaround for non-visible tags disappearing into
|
||||
// thin air on IE8, this only happens if there is no visible text
|
||||
// in-front of the non-visible tags. Piggyback on the whitespace fix
|
||||
// and simply check if any non-visible tags appear in the source.
|
||||
if (
|
||||
WHITESPACE_TEST.test(html) ||
|
||||
(html[0] === '<' && NONVISIBLE_TEST.test(html))
|
||||
) {
|
||||
// Recover leading whitespace by temporarily prepending any character.
|
||||
// \uFEFF has the potential advantage of being zero-width/invisible.
|
||||
// UglifyJS drops U+FEFF chars when parsing, so use String.fromCharCode
|
||||
// in hopes that this is preserved even if "\uFEFF" is transformed to
|
||||
// the actual Unicode character (by Babel, for example).
|
||||
// https://github.com/mishoo/UglifyJS2/blob/v2.4.20/lib/parse.js#L216
|
||||
node.innerHTML = String.fromCharCode(0xfeff) + html;
|
||||
|
||||
// deleteData leaves an empty `TextNode` which offsets the index of all
|
||||
// children. Definitely want to avoid this.
|
||||
var textNode = node.firstChild;
|
||||
if (textNode.data.length === 1) {
|
||||
node.removeChild(textNode);
|
||||
} else {
|
||||
textNode.deleteData(0, 1);
|
||||
}
|
||||
} else {
|
||||
node.innerHTML = html;
|
||||
}
|
||||
};
|
||||
}
|
||||
testElement = null;
|
||||
}
|
||||
|
||||
module.exports = setInnerHTML;
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
'use strict';
|
||||
|
||||
var SyntheticUIEvent = require('SyntheticUIEvent');
|
||||
var ViewportMetrics = require('ViewportMetrics');
|
||||
|
||||
var getEventModifierState = require('getEventModifierState');
|
||||
|
||||
@@ -25,6 +24,8 @@ var MouseEventInterface = {
|
||||
screenY: null,
|
||||
clientX: null,
|
||||
clientY: null,
|
||||
pageX: null,
|
||||
pageY: null,
|
||||
ctrlKey: null,
|
||||
shiftKey: null,
|
||||
altKey: null,
|
||||
@@ -51,17 +52,6 @@ var MouseEventInterface = {
|
||||
? event.toElement
|
||||
: event.fromElement);
|
||||
},
|
||||
// "Proprietary" Interface.
|
||||
pageX: function(event) {
|
||||
return 'pageX' in event
|
||||
? event.pageX
|
||||
: event.clientX + ViewportMetrics.currentScrollLeft;
|
||||
},
|
||||
pageY: function(event) {
|
||||
return 'pageY' in event
|
||||
? event.pageY
|
||||
: event.clientY + ViewportMetrics.currentScrollTop;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,25 +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 ViewportMetrics
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var ViewportMetrics = {
|
||||
currentScrollLeft: 0,
|
||||
|
||||
currentScrollTop: 0,
|
||||
|
||||
refreshScrollValues: function(scrollPosition) {
|
||||
ViewportMetrics.currentScrollLeft = scrollPosition.x;
|
||||
ViewportMetrics.currentScrollTop = scrollPosition.y;
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = ViewportMetrics;
|
||||
@@ -34,7 +34,6 @@ var ReactServerRenderingTransaction = require('ReactServerRenderingTransaction')
|
||||
var emptyFunction = require('fbjs/lib/emptyFunction');
|
||||
var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
|
||||
var invariant = require('fbjs/lib/invariant');
|
||||
var isEventSupported = require('isEventSupported');
|
||||
var inputValueTracking = require('inputValueTracking');
|
||||
var validateDOMNesting = require('validateDOMNesting');
|
||||
var warning = require('fbjs/lib/warning');
|
||||
@@ -138,14 +137,6 @@ function ensureListeningTo(inst, registrationName, transaction) {
|
||||
if (transaction instanceof ReactServerRenderingTransaction) {
|
||||
return;
|
||||
}
|
||||
if (__DEV__) {
|
||||
// IE8 has no API for event capturing and the `onScroll` event doesn't
|
||||
// bubble.
|
||||
warning(
|
||||
registrationName !== 'onScroll' || isEventSupported('scroll', true),
|
||||
"This browser doesn't support the `onScroll` event",
|
||||
);
|
||||
}
|
||||
var containerInfo = inst._hostContainerInfo;
|
||||
var isDocumentFragment = containerInfo._node &&
|
||||
containerInfo._node.nodeType === DOC_FRAGMENT_TYPE;
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
var DOMLazyTree = require('DOMLazyTree');
|
||||
var DOMProperty = require('DOMProperty');
|
||||
var React = require('react');
|
||||
var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
|
||||
var ReactCurrentOwner = require('react/lib/ReactCurrentOwner');
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
var ReactDOMContainerInfo = require('ReactDOMContainerInfo');
|
||||
@@ -382,7 +381,6 @@ var ReactMount = {
|
||||
'_registerComponent(...): Target container is not a DOM element.',
|
||||
);
|
||||
|
||||
ReactBrowserEventEmitter.ensureScrollValueMonitoring();
|
||||
var componentInstance = instantiateReactComponent(nextElement, false);
|
||||
|
||||
if (callback) {
|
||||
|
||||
@@ -12,10 +12,12 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
const ReactFiberErrorLogger = require('ReactFiberErrorLogger');
|
||||
const ReactFiberReconciler = require('ReactFiberReconciler');
|
||||
const ReactGenericBatching = require('ReactGenericBatching');
|
||||
const ReactNativeAttributePayload = require('ReactNativeAttributePayload');
|
||||
const ReactNativeComponentTree = require('ReactNativeComponentTree');
|
||||
const ReactNativeFiberErrorDialog = require('ReactNativeFiberErrorDialog');
|
||||
const ReactNativeFiberHostComponent = require('ReactNativeFiberHostComponent');
|
||||
const ReactNativeInjection = require('ReactNativeInjection');
|
||||
const ReactNativeTagHandles = require('ReactNativeTagHandles');
|
||||
@@ -27,6 +29,7 @@ const deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationI
|
||||
const emptyObject = require('fbjs/lib/emptyObject');
|
||||
const findNodeHandle = require('findNodeHandle');
|
||||
const invariant = require('fbjs/lib/invariant');
|
||||
const takeSnapshot = require('takeSnapshot');
|
||||
|
||||
const {injectInternals} = require('ReactFiberDevToolsHook');
|
||||
|
||||
@@ -376,6 +379,12 @@ findNodeHandle.injection.injectFindNode((fiber: Fiber) =>
|
||||
NativeRenderer.findHostInstance(fiber));
|
||||
findNodeHandle.injection.injectFindRootNodeID(instance => instance);
|
||||
|
||||
// Intercept lifecycle errors and ensure they are shown with the correct stack
|
||||
// trace within the native redbox component.
|
||||
ReactFiberErrorLogger.injection.injectDialog(
|
||||
ReactNativeFiberErrorDialog.showDialog,
|
||||
);
|
||||
|
||||
const ReactNative = {
|
||||
// External users of findNodeHandle() expect the host tag number return type.
|
||||
// The injected findNodeHandle() strategy returns the instance wrapper though.
|
||||
@@ -402,6 +411,8 @@ const ReactNative = {
|
||||
return NativeRenderer.getPublicRootInstance(root);
|
||||
},
|
||||
|
||||
takeSnapshot,
|
||||
|
||||
unmountComponentAtNode(containerTag: number) {
|
||||
const root = roots.get(containerTag);
|
||||
if (root) {
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 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 ReactNativeFiberErrorDialog
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const ExceptionsManager = require('ExceptionsManager');
|
||||
|
||||
import type {CapturedError} from 'ReactFiberScheduler';
|
||||
|
||||
/**
|
||||
* Intercept lifecycle errors and ensure they are shown with the correct stack
|
||||
* trace within the native redbox component.
|
||||
*/
|
||||
function ReactNativeFiberErrorDialog(capturedError: CapturedError): boolean {
|
||||
const {componentStack, error} = capturedError;
|
||||
|
||||
let errorMessage: string;
|
||||
let errorStack: string;
|
||||
let errorType: Class<Error>;
|
||||
|
||||
// Typically Errors are thrown but eg strings or null can be thrown as well.
|
||||
if (error && typeof error === 'object') {
|
||||
const {message, name} = error;
|
||||
|
||||
const summary = message ? `${name}: ${message}` : name;
|
||||
|
||||
errorMessage = `${summary}\n\nThis error is located at:${componentStack}`;
|
||||
errorStack = error.stack;
|
||||
errorType = error.constructor;
|
||||
} else {
|
||||
errorMessage = `Unspecified error at:${componentStack}`;
|
||||
errorStack = '';
|
||||
errorType = Error;
|
||||
}
|
||||
|
||||
const newError = new errorType(errorMessage);
|
||||
newError.stack = errorStack;
|
||||
|
||||
ExceptionsManager.handleException(newError, false);
|
||||
|
||||
// Return false here to prevent ReactFiberErrorLogger default behavior of
|
||||
// logging error details to console.error. Calls to console.error are
|
||||
// automatically routed to the native redbox controller, which we've already
|
||||
// done above by calling ExceptionsManager.
|
||||
return false;
|
||||
}
|
||||
|
||||
module.exports.showDialog = ReactNativeFiberErrorDialog;
|
||||
@@ -18,6 +18,7 @@ var ReactNativeStackInjection = require('ReactNativeStackInjection');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
|
||||
var findNodeHandle = require('findNodeHandle');
|
||||
var takeSnapshot = require('takeSnapshot');
|
||||
|
||||
ReactNativeInjection.inject();
|
||||
ReactNativeStackInjection.inject();
|
||||
@@ -45,6 +46,9 @@ var ReactNative = {
|
||||
},
|
||||
|
||||
render: render,
|
||||
|
||||
takeSnapshot,
|
||||
|
||||
unmountComponentAtNode: ReactNativeMount.unmountComponentAtNode,
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) 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.
|
||||
*
|
||||
* @providesModule takeSnapshot
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var ReactNative = require('ReactNative');
|
||||
var UIManager = require('UIManager');
|
||||
|
||||
import type {Element} from 'React';
|
||||
|
||||
/**
|
||||
* Capture an image of the screen, window or an individual view. The image
|
||||
* will be stored in a temporary file that will only exist for as long as the
|
||||
* app is running.
|
||||
*
|
||||
* The `view` argument can be the literal string `window` if you want to
|
||||
* capture the entire window, or it can be a reference to a specific
|
||||
* React Native component.
|
||||
*
|
||||
* The `options` argument may include:
|
||||
* - width/height (number) - the width and height of the image to capture.
|
||||
* - format (string) - either 'png' or 'jpeg'. Defaults to 'png'.
|
||||
* - quality (number) - the quality when using jpeg. 0.0 - 1.0 (default).
|
||||
*
|
||||
* Returns a Promise.
|
||||
* @platform ios
|
||||
*/
|
||||
function takeSnapshot(
|
||||
view?: 'window' | Element<any> | number,
|
||||
options?: {
|
||||
width?: number,
|
||||
height?: number,
|
||||
format?: 'png' | 'jpeg',
|
||||
quality?: number,
|
||||
},
|
||||
): Promise<any> {
|
||||
if (typeof view !== 'number' && view !== 'window') {
|
||||
view = ReactNative.findNodeHandle(view) || 'window';
|
||||
}
|
||||
|
||||
// Call the hidden '__takeSnapshot' method; the main one throws an error to
|
||||
// prevent accidental backwards-incompatible usage.
|
||||
return UIManager.__takeSnapshot(view, options);
|
||||
}
|
||||
|
||||
module.exports = takeSnapshot;
|
||||
@@ -12,14 +12,23 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
const emptyFunction = require('fbjs/lib/emptyFunction');
|
||||
const invariant = require('fbjs/lib/invariant');
|
||||
|
||||
import type {CapturedError} from 'ReactFiberScheduler';
|
||||
|
||||
let showDialog = emptyFunction;
|
||||
const defaultShowDialog = () => true;
|
||||
|
||||
let showDialog = defaultShowDialog;
|
||||
|
||||
function logCapturedError(capturedError: CapturedError): void {
|
||||
const logError = showDialog(capturedError);
|
||||
|
||||
// Allow injected showDialog() to prevent default console.error logging.
|
||||
// This enables renderers like ReactNative to better manage redbox behavior.
|
||||
if (logError === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
const {
|
||||
componentName,
|
||||
@@ -85,14 +94,16 @@ function logCapturedError(capturedError: CapturedError): void {
|
||||
`React caught an error thrown by one of your components.\n\n${error.stack}`,
|
||||
);
|
||||
}
|
||||
|
||||
showDialog(capturedError);
|
||||
}
|
||||
|
||||
exports.injection = {
|
||||
injectDialog(fn: (e: CapturedError) => void) {
|
||||
/**
|
||||
* Display custom dialog for lifecycle errors.
|
||||
* Return false to prevent default behavior of logging to console.error.
|
||||
*/
|
||||
injectDialog(fn: (e: CapturedError) => boolean) {
|
||||
invariant(
|
||||
showDialog === emptyFunction,
|
||||
showDialog === defaultShowDialog,
|
||||
'The custom dialog was already injected.',
|
||||
);
|
||||
invariant(
|
||||
|
||||
@@ -16,8 +16,6 @@ import type {Fiber} from 'ReactFiber';
|
||||
import type {HostConfig} from 'ReactFiberReconciler';
|
||||
import type {StackCursor} from 'ReactFiberStack';
|
||||
|
||||
const emptyObject = require('fbjs/lib/emptyObject');
|
||||
|
||||
const {
|
||||
createCursor,
|
||||
pop,
|
||||
@@ -26,6 +24,9 @@ const {
|
||||
|
||||
const invariant = require('fbjs/lib/invariant');
|
||||
|
||||
declare class NoContextT {}
|
||||
const NO_CONTEXT: NoContextT = ({}: any);
|
||||
|
||||
export type HostContext<C, CX> = {
|
||||
getHostContext(): CX,
|
||||
getRootHostContainer(): C,
|
||||
@@ -44,19 +45,27 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
|
||||
getRootHostContext,
|
||||
} = config;
|
||||
|
||||
let contextStackCursor: StackCursor<CX | null> = createCursor((null: ?CX));
|
||||
let contextFiberStackCursor: StackCursor<Fiber | null> = createCursor(
|
||||
(null: Fiber | null),
|
||||
let contextStackCursor: StackCursor<CX | NoContextT> = createCursor(
|
||||
NO_CONTEXT,
|
||||
);
|
||||
let rootInstanceStackCursor: StackCursor<C | null> = createCursor((null: ?C));
|
||||
let contextFiberStackCursor: StackCursor<Fiber | NoContextT> = createCursor(
|
||||
NO_CONTEXT,
|
||||
);
|
||||
let rootInstanceStackCursor: StackCursor<C | NoContextT> = createCursor(
|
||||
NO_CONTEXT,
|
||||
);
|
||||
|
||||
function requiredContext<Value>(c: Value | NoContextT): Value {
|
||||
invariant(
|
||||
c !== NO_CONTEXT,
|
||||
'Expected host context to exist. This error is likely caused by a bug ' +
|
||||
'in React. Please file an issue.',
|
||||
);
|
||||
return (c: any);
|
||||
}
|
||||
|
||||
function getRootHostContainer(): C {
|
||||
const rootInstance = rootInstanceStackCursor.current;
|
||||
invariant(
|
||||
rootInstance !== null,
|
||||
'Expected root container to exist. This error is likely caused by a ' +
|
||||
'bug in React. Please file an issue.',
|
||||
);
|
||||
const rootInstance = requiredContext(rootInstanceStackCursor.current);
|
||||
return rootInstance;
|
||||
}
|
||||
|
||||
@@ -80,26 +89,13 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
|
||||
}
|
||||
|
||||
function getHostContext(): CX {
|
||||
const context = contextStackCursor.current;
|
||||
invariant(
|
||||
context != null,
|
||||
'Expected host context to exist. This error is likely caused by a bug ' +
|
||||
'in React. Please file an issue.',
|
||||
);
|
||||
const context = requiredContext(contextStackCursor.current);
|
||||
return context;
|
||||
}
|
||||
|
||||
function pushHostContext(fiber: Fiber): void {
|
||||
const rootInstance = rootInstanceStackCursor.current;
|
||||
invariant(
|
||||
rootInstance != null,
|
||||
'Expected root host context to exist. This error is likely caused by ' +
|
||||
'a bug in React. Please file an issue.',
|
||||
);
|
||||
|
||||
const context = contextStackCursor.current !== null
|
||||
? contextStackCursor.current
|
||||
: emptyObject;
|
||||
const rootInstance = requiredContext(rootInstanceStackCursor.current);
|
||||
const context = requiredContext(contextStackCursor.current);
|
||||
const nextContext = getChildHostContext(context, fiber.type, rootInstance);
|
||||
|
||||
// Don't push this Fiber's context unless it's unique.
|
||||
@@ -125,8 +121,8 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
|
||||
}
|
||||
|
||||
function resetHostContainer() {
|
||||
contextStackCursor.current = null;
|
||||
rootInstanceStackCursor.current = null;
|
||||
contextStackCursor.current = NO_CONTEXT;
|
||||
rootInstanceStackCursor.current = NO_CONTEXT;
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -684,6 +684,11 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Without this explicit null return Flow complains of invalid return type
|
||||
// TODO Remove the above while(true) loop
|
||||
// eslint-disable-next-line no-unreachable
|
||||
return null;
|
||||
}
|
||||
|
||||
function performUnitOfWork(workInProgress: Fiber): Fiber | null {
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* 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 ReactFiberReconciler;
|
||||
|
||||
describe('ReactFiberHostContext', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
React = require('React');
|
||||
ReactFiberReconciler = require('ReactFiberReconciler');
|
||||
});
|
||||
|
||||
it('works with null host context', () => {
|
||||
var creates = 0;
|
||||
var Renderer = ReactFiberReconciler({
|
||||
prepareForCommit: function() {},
|
||||
resetAfterCommit: function() {},
|
||||
getRootHostContext: function() {
|
||||
return null;
|
||||
},
|
||||
getChildHostContext: function() {
|
||||
return null;
|
||||
},
|
||||
shouldSetTextContent: function() {
|
||||
return false;
|
||||
},
|
||||
createInstance: function() {
|
||||
creates++;
|
||||
},
|
||||
finalizeInitialChildren: function() {
|
||||
return null;
|
||||
},
|
||||
appendInitialChild: function() {
|
||||
return null;
|
||||
},
|
||||
appendChild: function() {
|
||||
return null;
|
||||
},
|
||||
useSyncScheduling: true,
|
||||
});
|
||||
|
||||
const container = Renderer.createContainer(/* root: */ null);
|
||||
Renderer.updateContainer(
|
||||
<a><b /></a>,
|
||||
container,
|
||||
/* parentComponent: */ null,
|
||||
/* callback: */ null,
|
||||
);
|
||||
expect(creates).toBe(2);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user