Compare commits
79 Commits
v16.4.0
...
is-rendera
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
efe8e670af | ||
|
|
3297102de6 | ||
|
|
f4b6a9f8ee | ||
|
|
3eedcb1fda | ||
|
|
6d6de6011c | ||
|
|
71a60ddb16 | ||
|
|
9e6c99ca2e | ||
|
|
baff5cc2f6 | ||
|
|
6a530e3baa | ||
|
|
c35a1e7483 | ||
|
|
076bbeace7 | ||
|
|
da5c87bdfa | ||
|
|
a960d18bc7 | ||
|
|
5b3d17a5f7 | ||
|
|
b0f60895f7 | ||
|
|
ae8c6dd534 | ||
|
|
0fcf92d06d | ||
|
|
97af3e1f3a | ||
|
|
4fe6eec15b | ||
|
|
8e87c139b4 | ||
|
|
aeda7b745d | ||
|
|
b1b3acbd6b | ||
|
|
ae14317d68 | ||
|
|
72434a7686 | ||
|
|
64c54edea4 | ||
|
|
9bd4d1fae2 | ||
|
|
9bda7b28f3 | ||
|
|
2e75779075 | ||
|
|
bc963f353d | ||
|
|
051637da61 | ||
|
|
2a8085980f | ||
|
|
e0c78344e2 | ||
|
|
74b1723df1 | ||
|
|
9725065eb4 | ||
|
|
a5957bf296 | ||
|
|
0b87b27906 | ||
|
|
65eb6b94ac | ||
|
|
c469e3b422 | ||
|
|
036ae3c6e2 | ||
|
|
945fc1bfce | ||
|
|
392530104c | ||
|
|
1594409fab | ||
|
|
d5c11193e2 | ||
|
|
ec60457bcd | ||
|
|
30bc8ef792 | ||
|
|
d480782c41 | ||
|
|
4ac6f133af | ||
|
|
23be4102df | ||
|
|
d0d4280640 | ||
|
|
c78957eac8 | ||
|
|
bfb12ebb52 | ||
|
|
394b17eede | ||
|
|
188c4252a2 | ||
|
|
d3e0a3aaf3 | ||
|
|
9cf3733a9a | ||
|
|
52fbe7612e | ||
|
|
c5a733e1e3 | ||
|
|
36546b5137 | ||
|
|
65ab53694f | ||
|
|
15767a8f8f | ||
|
|
3118ed9d64 | ||
|
|
524a743313 | ||
|
|
ae57b125c7 | ||
|
|
e0a03c1b4d | ||
|
|
79a740c6e3 | ||
|
|
ff724d3c28 | ||
|
|
001f9ef471 | ||
|
|
83f76e4db9 | ||
|
|
4f1f909b5b | ||
|
|
8aeea5afa2 | ||
|
|
aa85b0fd5f | ||
|
|
a32f857ac7 | ||
|
|
61777a78f6 | ||
|
|
e7bd3d59a9 | ||
|
|
f35d989bea | ||
|
|
5578700671 | ||
|
|
76e07071a1 | ||
|
|
345e0a71ac | ||
|
|
fa7fa812c7 |
@@ -29,7 +29,7 @@ jobs:
|
||||
|
||||
- run:
|
||||
name: Install Packages
|
||||
command: yarn install
|
||||
command: yarn install --frozen-lockfile
|
||||
|
||||
- run:
|
||||
name: Test Packages
|
||||
|
||||
@@ -81,5 +81,6 @@ module.exports = {
|
||||
spyOnDev: true,
|
||||
spyOnDevAndProd: true,
|
||||
spyOnProd: true,
|
||||
__PROFILE__: true,
|
||||
},
|
||||
};
|
||||
|
||||
30
CHANGELOG.md
30
CHANGELOG.md
@@ -5,6 +5,34 @@
|
||||
Click to see more.
|
||||
</summary>
|
||||
|
||||
</details>
|
||||
|
||||
## 16.4.1 (June 13, 2018)
|
||||
|
||||
### React
|
||||
|
||||
* You can now assign `propTypes` to components returned by `React.ForwardRef`. ([@bvaughn](https://github.com/bvaughn) in [#12911](https://github.com/facebook/react/pull/12911))
|
||||
|
||||
### React DOM
|
||||
|
||||
* Fix a crash when the input `type` changes from some other types to `text`. ([@spirosikmd](https://github.com/spirosikmd) in [#12135](https://github.com/facebook/react/pull/12135))
|
||||
* Fix a crash in IE11 when restoring focus to an SVG element. ([@ThaddeusJiang](https://github.com/ThaddeusJiang) in [#12996](https://github.com/facebook/react/pull/12996))
|
||||
* Fix a range input not updating in some cases. ([@Illu](https://github.com/Illu) in [#12939](https://github.com/facebook/react/pull/12939))
|
||||
* Fix input validation triggering unnecessarily in Firefox. ([@nhunzaker](https://github.com/nhunzaker) in [#12925](https://github.com/facebook/react/pull/12925))
|
||||
* Fix an incorrect `event.target` value for the `onChange` event in IE9. ([@nhunzaker](https://github.com/nhunzaker) in [#12976](https://github.com/facebook/react/pull/12976))
|
||||
* Fix a false positive error when returning an empty `<React.Fragment />` from a component. ([@philipp-spiess](https://github.com/philipp-spiess) in [#12966](https://github.com/facebook/react/pull/12966))
|
||||
|
||||
### React DOM Server
|
||||
|
||||
* Fix an incorrect value being provided by new context API. ([@ericsoderberghp](https://github.com/ericsoderberghp) in [#12985](https://github.com/facebook/react/pull/12985), [@gaearon](https://github.com/gaearon) in [#13019](https://github.com/facebook/react/pull/13019))
|
||||
|
||||
### React Test Renderer
|
||||
|
||||
* Allow multiple root children in test renderer traversal API. ([@gaearon](https://github.com/gaearon) in [#13017](https://github.com/facebook/react/pull/13017))
|
||||
* Fix `getDerivedStateFromProps()` in the shallow renderer to not discard the pending state. ([@fatfisz](https://github.com/fatfisz) in [#13030](https://github.com/facebook/react/pull/13030))
|
||||
|
||||
## 16.4.0 (May 23, 2018)
|
||||
|
||||
### React
|
||||
|
||||
* Add a new [experimental](https://github.com/reactjs/rfcs/pull/51) `React.unstable_Profiler` component for measuring performance. ([@bvaughn](https://github.com/bvaughn) in [#12745](https://github.com/facebook/react/pull/12745))
|
||||
@@ -42,8 +70,6 @@
|
||||
|
||||
* The [new host config shape](https://github.com/facebook/react/blob/c601f7a64640290af85c9f0e33c78480656b46bc/packages/react-noop-renderer/src/createReactNoop.js#L82-L285) is flat and doesn't use nested objects. ([@gaearon](https://github.com/gaearon) in [#12792](https://github.com/facebook/react/pull/12792))
|
||||
|
||||
</details>
|
||||
|
||||
## 16.3.2 (April 16, 2018)
|
||||
|
||||
### React
|
||||
|
||||
37
README.md
37
README.md
@@ -8,15 +8,28 @@ React is a JavaScript library for building user interfaces.
|
||||
|
||||
[Learn how to use React in your own project](https://reactjs.org/docs/getting-started.html).
|
||||
|
||||
## Installation
|
||||
|
||||
React has been designed for gradual adoption from the start, and **you can use as little or as much React as you need**:
|
||||
|
||||
* Use [Online Playgrounds](https://reactjs.org/docs/getting-started.html#online-playgrounds) to get a taste of React.
|
||||
* [Add React to a Website](https://reactjs.org/docs/add-react-to-a-website.html) as a `<script>` tag in one minute.
|
||||
* [Create a New React App](https://reactjs.org/docs/create-a-new-react-app.html) if you're looking for a powerful JavaScript toolchain.
|
||||
|
||||
You can use React as a `<script>` tag from a [CDN](https://reactjs.org/docs/cdn-links.html), or as a `react` package on [npm](https://www.npmjs.com/).
|
||||
|
||||
## Documentation
|
||||
|
||||
You can find the React documentation [on the website](https://reactjs.org/docs).
|
||||
It is divided into several sections:
|
||||
|
||||
* [Quick Start](https://reactjs.org/docs/hello-world.html)
|
||||
Check out the [Getting Started](https://reactjs.org/docs/getting-started.html) page for a quick overview.
|
||||
|
||||
The documentation is divided into several sections:
|
||||
|
||||
* [Tutorial](https://reactjs.org/tutorial/tutorial.html)
|
||||
* [Main Concepts](https://reactjs.org/docs/hello-world.html)
|
||||
* [Advanced Guides](https://reactjs.org/docs/jsx-in-depth.html)
|
||||
* [API Reference](https://reactjs.org/docs/react-api.html)
|
||||
* [Tutorial](https://reactjs.org/tutorial/tutorial.html)
|
||||
* [Where to Get Support](https://reactjs.org/community/support.html)
|
||||
* [Contributing Guide](https://reactjs.org/docs/how-to-contribute.html)
|
||||
|
||||
@@ -34,26 +47,14 @@ class HelloMessage extends React.Component {
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
<HelloMessage name="John" />,
|
||||
<HelloMessage name="Taylor" />,
|
||||
document.getElementById('container')
|
||||
);
|
||||
```
|
||||
|
||||
This example will render "Hello John" into a container on the page.
|
||||
This example will render "Hello Taylor" into a container on the page.
|
||||
|
||||
You'll notice that we used an HTML-like syntax; [we call it JSX](https://reactjs.org/docs/introducing-jsx.html). JSX is not required to use React, but it makes code more readable, and writing it feels like writing HTML. We recommend using [Babel](https://babeljs.io/) with a [React preset](https://babeljs.io/docs/plugins/preset-react/) to convert JSX into native JavaScript for browsers to digest.
|
||||
|
||||
## Installation
|
||||
|
||||
React is available as the `react` package on [npm](https://www.npmjs.com/). It is also available on a [CDN](https://reactjs.org/docs/cdn-links.html).
|
||||
|
||||
React is flexible and can be used in a variety of projects. You can create new apps with it, but you can also gradually introduce it into an existing codebase without doing a rewrite.
|
||||
|
||||
The recommended way to install React depends on your project. Here you can find short guides for the most common scenarios:
|
||||
|
||||
* [Trying Out React](https://reactjs.org/docs/try-react.html)
|
||||
* [Creating a New Application](https://reactjs.org/docs/add-react-to-a-new-app.html)
|
||||
* [Adding React to an Existing Application](https://reactjs.org/docs/add-react-to-an-existing-app.html)
|
||||
You'll notice that we used an HTML-like syntax; [we call it JSX](https://reactjs.org/docs/introducing-jsx.html). JSX is not required to use React, but it makes code more readable, and writing it feels like writing HTML.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
@@ -25,12 +25,12 @@ deploy: off
|
||||
|
||||
install:
|
||||
- ps: Install-Product node $env:nodejs_version $env:platform
|
||||
- yarn install
|
||||
- yarn install --frozen-lockfile
|
||||
|
||||
test_script:
|
||||
- node --version
|
||||
- yarn lint
|
||||
- yarn flow-ci
|
||||
# - yarn flow-ci
|
||||
- yarn build
|
||||
- yarn test
|
||||
- yarn prettier
|
||||
|
||||
@@ -65,6 +65,7 @@ class Header extends React.Component {
|
||||
<option value="/custom-elements">Custom Elements</option>
|
||||
<option value="/media-events">Media Events</option>
|
||||
<option value="/pointer-events">Pointer Events</option>
|
||||
<option value="/mouse-events">Mouse Events</option>
|
||||
</select>
|
||||
</label>
|
||||
<label htmlFor="react_version">
|
||||
|
||||
@@ -12,6 +12,7 @@ import EventPooling from './event-pooling';
|
||||
import CustomElementFixtures from './custom-elements';
|
||||
import MediaEventsFixtures from './media-events';
|
||||
import PointerEventsFixtures from './pointer-events';
|
||||
import MouseEventsFixtures from './mouse-events';
|
||||
|
||||
const React = window.React;
|
||||
|
||||
@@ -49,6 +50,8 @@ function FixturesPage() {
|
||||
return <MediaEventsFixtures />;
|
||||
case '/pointer-events':
|
||||
return <PointerEventsFixtures />;
|
||||
case '/mouse-events':
|
||||
return <MouseEventsFixtures />;
|
||||
default:
|
||||
return <p>Please select a test fixture.</p>;
|
||||
}
|
||||
|
||||
16
fixtures/dom/src/components/fixtures/mouse-events/index.js
Normal file
16
fixtures/dom/src/components/fixtures/mouse-events/index.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import FixtureSet from '../../FixtureSet';
|
||||
import MouseMovement from './mouse-movement';
|
||||
|
||||
const React = window.React;
|
||||
|
||||
class MouseEvents extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<FixtureSet title="Mouse Events" description="">
|
||||
<MouseMovement />
|
||||
</FixtureSet>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MouseEvents;
|
||||
@@ -0,0 +1,48 @@
|
||||
import TestCase from '../../TestCase';
|
||||
|
||||
const React = window.React;
|
||||
|
||||
class MouseMovement extends React.Component {
|
||||
state = {
|
||||
movement: {x: 0, y: 0},
|
||||
};
|
||||
|
||||
onMove = event => {
|
||||
this.setState({x: event.movementX, y: event.movementY});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {x, y} = this.state;
|
||||
|
||||
const boxStyle = {
|
||||
padding: '10px 20px',
|
||||
border: '1px solid #d9d9d9',
|
||||
margin: '10px 0 20px',
|
||||
};
|
||||
|
||||
return (
|
||||
<TestCase
|
||||
title="Mouse Movement"
|
||||
description="We polyfill the movementX and movementY fields."
|
||||
affectedBrowsers="IE, Safari">
|
||||
<TestCase.Steps>
|
||||
<li>Mouse over the box below</li>
|
||||
</TestCase.Steps>
|
||||
|
||||
<TestCase.ExpectedResult>
|
||||
The reported values should equal the pixel (integer) difference
|
||||
between mouse movements positions.
|
||||
</TestCase.ExpectedResult>
|
||||
|
||||
<div style={boxStyle} onMouseMove={this.onMove}>
|
||||
<p>Trace your mouse over this box.</p>
|
||||
<p>
|
||||
Last movement: {x},{y}
|
||||
</p>
|
||||
</div>
|
||||
</TestCase>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MouseMovement;
|
||||
@@ -0,0 +1,40 @@
|
||||
import Fixture from '../../Fixture';
|
||||
|
||||
const React = window.React;
|
||||
|
||||
class ReplaceEmailInput extends React.Component {
|
||||
state = {
|
||||
formSubmitted: false,
|
||||
};
|
||||
|
||||
onReset = () => {
|
||||
this.setState({formSubmitted: false});
|
||||
};
|
||||
|
||||
onSubmit = event => {
|
||||
event.preventDefault();
|
||||
this.setState({formSubmitted: true});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Fixture>
|
||||
<form className="control-box" onSubmit={this.onSubmit}>
|
||||
<fieldset>
|
||||
<legend>Email</legend>
|
||||
{!this.state.formSubmitted ? (
|
||||
<input type="email" />
|
||||
) : (
|
||||
<input type="text" disabled={true} />
|
||||
)}
|
||||
</fieldset>
|
||||
</form>
|
||||
<button type="button" onClick={this.onReset}>
|
||||
Reset
|
||||
</button>
|
||||
</Fixture>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ReplaceEmailInput;
|
||||
@@ -2,6 +2,7 @@ import Fixture from '../../Fixture';
|
||||
import FixtureSet from '../../FixtureSet';
|
||||
import TestCase from '../../TestCase';
|
||||
import InputTestCase from './InputTestCase';
|
||||
import ReplaceEmailInput from './ReplaceEmailInput';
|
||||
|
||||
const React = window.React;
|
||||
|
||||
@@ -64,21 +65,36 @@ class TextInputFixtures extends React.Component {
|
||||
<Fixture>
|
||||
<form className="control-box">
|
||||
<fieldset>
|
||||
<legend>Text</legend>
|
||||
<legend>Empty value prop string</legend>
|
||||
<input value="" required={true} />
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>No value prop</legend>
|
||||
<input required={true} />
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Date</legend>
|
||||
<legend>Empty defaultValue prop string</legend>
|
||||
<input required={true} defaultValue="" />
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>No value prop date input</legend>
|
||||
<input type="date" required={true} />
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Empty value prop date input</legend>
|
||||
<input type="date" value="" required={true} />
|
||||
</fieldset>
|
||||
</form>
|
||||
</Fixture>
|
||||
|
||||
<p className="footnote">
|
||||
Checking the date type is also important because of a prior fix for
|
||||
iOS Safari that involved assigning over value/defaultValue
|
||||
properties of the input to prevent a display bug. This also
|
||||
triggered input validation.
|
||||
properties of the input to prevent a display bug. This also triggers
|
||||
input validation.
|
||||
</p>
|
||||
<p className="footnote">
|
||||
The date inputs should be blank in iOS. This is not a bug.
|
||||
</p>
|
||||
</TestCase>
|
||||
|
||||
@@ -110,6 +126,21 @@ class TextInputFixtures extends React.Component {
|
||||
<InputTestCase type="url" defaultValue="" />
|
||||
</TestCase>
|
||||
|
||||
<TestCase
|
||||
title="Replacing email input with text disabled input"
|
||||
relatedIssues="12062">
|
||||
<TestCase.Steps>
|
||||
<li>Type "test@test.com"</li>
|
||||
<li>Press enter</li>
|
||||
</TestCase.Steps>
|
||||
|
||||
<TestCase.ExpectedResult>
|
||||
There should be no selection-related error in the console.
|
||||
</TestCase.ExpectedResult>
|
||||
|
||||
<ReplaceEmailInput />
|
||||
</TestCase>
|
||||
|
||||
<TestCase title="All inputs" description="General test of all inputs">
|
||||
<InputTestCase type="text" defaultValue="Text" />
|
||||
<InputTestCase type="email" defaultValue="user@example.com" />
|
||||
|
||||
494
fixtures/schedule/index.html
Normal file
494
fixtures/schedule/index.html
Normal file
@@ -0,0 +1,494 @@
|
||||
<!DOCTYPE html>
|
||||
<html style="width: 100%; height: 100%;">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Schedule Test Page</title>
|
||||
<style>
|
||||
.correct {
|
||||
border: solid green 2px;
|
||||
}
|
||||
.incorrect {
|
||||
border: dashed red 2px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Schedule Fixture</h1>
|
||||
<p>
|
||||
This fixture is for manual testing purposes, and the patterns used in
|
||||
implementing it should not be used as a model. This is mainly for anyone
|
||||
working on making changes to the `schedule` module.
|
||||
</p>
|
||||
<h2>Tests:</h2>
|
||||
<ol>
|
||||
<li>
|
||||
<button onClick="runTestOne()">Run Test 1</button>
|
||||
<p>Calls the callback within the frame when not blocked:</p>
|
||||
<div><b>Expected:</b></div>
|
||||
<div id="test-1-expected">
|
||||
</div>
|
||||
<div> -------------------------------------------------</div>
|
||||
<div> If you see the same above and below it's correct.
|
||||
<div> -------------------------------------------------</div>
|
||||
<div><b>Actual:</b></div>
|
||||
<div id="test-1"></div>
|
||||
</li>
|
||||
<li>
|
||||
<p>Accepts multiple callbacks and calls within frame when not blocked</p>
|
||||
<button onClick="runTestTwo()">Run Test 2</button>
|
||||
<div><b>Expected:</b></div>
|
||||
<div id="test-2-expected">
|
||||
</div>
|
||||
<div> -------------------------------------------------</div>
|
||||
<div> If you see the same above and below it's correct.
|
||||
<div> -------------------------------------------------</div>
|
||||
<div><b>Actual:</b></div>
|
||||
<div id="test-2"></div>
|
||||
</li>
|
||||
<li>
|
||||
<p>Schedules callbacks in correct order when they use scheduleWork to schedule themselves</p>
|
||||
<button onClick="runTestThree()">Run Test 3</button>
|
||||
<div><b>Expected:</b></div>
|
||||
<div id="test-3-expected">
|
||||
</div>
|
||||
<div> -------------------------------------------------</div>
|
||||
<div> If you see the same above and below it's correct.
|
||||
<div> -------------------------------------------------</div>
|
||||
<div><b>Actual:</b></div>
|
||||
<div id="test-3"></div>
|
||||
</li>
|
||||
<li>
|
||||
<p>Calls timed out callbacks and then any more pending callbacks, defers others if time runs out</p>
|
||||
<button onClick="runTestFour()">Run Test 4</button>
|
||||
<div><b>Expected:</b></div>
|
||||
<div id="test-4-expected">
|
||||
</div>
|
||||
<div> -------------------------------------------------</div>
|
||||
<div> If you see the same above and below it's correct.
|
||||
<div> -------------------------------------------------</div>
|
||||
<div><b>Actual:</b></div>
|
||||
<div id="test-4"></div>
|
||||
</li>
|
||||
<li>
|
||||
<p>When some callbacks throw errors, still calls them all within the same frame</p>
|
||||
<p><b>IMPORTANT:</b> Open the console when you run this! Inspect the logs there!</p>
|
||||
<button onClick="runTestFive()">Run Test 5</button>
|
||||
</li>
|
||||
<li>
|
||||
<p>When some callbacks throw errors <b> and some also time out</b>, still calls them all within the same frame</p>
|
||||
<p><b>IMPORTANT:</b> Open the console when you run this! Inspect the logs there!</p>
|
||||
<button onClick="runTestSix()">Run Test 6</button>
|
||||
</li>
|
||||
<li>
|
||||
<p>Continues calling callbacks even when user switches away from this tab</p>
|
||||
<button onClick="runTestSeven()">Run Test 7</button>
|
||||
<div><b>Click the button above, observe the counter, then switch to
|
||||
another tab and switch back:</b></div>
|
||||
<div id="test-7">
|
||||
</div>
|
||||
<div> If the counter advanced while you were away from this tab, it's correct.</div>
|
||||
</li>
|
||||
</ol>
|
||||
<script src="../../build/dist/react-scheduler.development.js"></script>
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.js"></script>
|
||||
<script type="text/babel">
|
||||
const {scheduleWork, cancelWork, now} = ReactScheduler;
|
||||
function displayTestResult(testNumber) {
|
||||
const expectationNode = document.getElementById('test-' + testNumber + '-expected');
|
||||
const resultNode = document.getElementById('test-' + testNumber);
|
||||
resultNode.innerHTML = latestResults[testNumber - 1].join('<br />');
|
||||
expectationNode.innerHTML = expectedResults[testNumber - 1].join('<br />');
|
||||
}
|
||||
function clearTestResult(testNumber) {
|
||||
const resultNode = document.getElementById('test-' + testNumber);
|
||||
resultNode.innerHTML = '';
|
||||
latestResults[testNumber - 1] = [];
|
||||
}
|
||||
function updateTestResult(testNumber, textToAddToResult) {
|
||||
latestResults[testNumber - 1].push(textToAddToResult);
|
||||
};
|
||||
function checkTestResult(testNumber) {
|
||||
|
||||
let correct = true;
|
||||
const expected = expectedResults[testNumber - 1]; // zero indexing
|
||||
const result = latestResults[testNumber - 1]; // zero indexing
|
||||
if (expected.length !== result.length) {
|
||||
correct = false;
|
||||
} else {
|
||||
for (let i = 0, len = expected.length; i < len; i++) {
|
||||
if (expected[i] !== result[i]) {
|
||||
correct = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const currentClass = correct ? 'correct' : 'incorrect';
|
||||
const previousClass = correct ? 'incorrect' : 'correct';
|
||||
document.getElementById('test-' + testNumber).classList.remove(previousClass);
|
||||
document.getElementById('test-' + testNumber).classList.add(currentClass);
|
||||
}
|
||||
function logWhenFramesStart(testNumber, cb) {
|
||||
requestAnimationFrame(() => {
|
||||
updateTestResult(testNumber, 'frame 1 started');
|
||||
requestAnimationFrame(() => {
|
||||
updateTestResult(testNumber, 'frame 2 started');
|
||||
requestAnimationFrame(() => {
|
||||
updateTestResult(testNumber, 'frame 3 started... we stop counting now.');
|
||||
cb();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
// push in results when we run the test
|
||||
const latestResults = [
|
||||
// test 1
|
||||
[
|
||||
],
|
||||
// test 2
|
||||
[
|
||||
],
|
||||
// test 3
|
||||
[
|
||||
],
|
||||
// test 4
|
||||
[
|
||||
],
|
||||
// test 5
|
||||
[
|
||||
],
|
||||
];
|
||||
|
||||
const expectedResults = [
|
||||
// test 1
|
||||
[
|
||||
'scheduled Cb1',
|
||||
'frame 1 started',
|
||||
'cb1 called with argument of {"didTimeout":false}',
|
||||
'frame 2 started',
|
||||
'frame 3 started... we stop counting now.',
|
||||
],
|
||||
// test 2
|
||||
[
|
||||
'scheduled CbA',
|
||||
'scheduled CbB',
|
||||
'frame 1 started',
|
||||
'cbA called with argument of {"didTimeout":false}',
|
||||
'cbB called with argument of {"didTimeout":false}',
|
||||
'frame 2 started',
|
||||
'frame 3 started... we stop counting now.',
|
||||
],
|
||||
// test 3
|
||||
[
|
||||
'scheduled CbA',
|
||||
'scheduled CbB',
|
||||
'frame 1 started',
|
||||
'scheduled CbA again',
|
||||
'cbA0 called with argument of {"didTimeout":false}',
|
||||
'cbB called with argument of {"didTimeout":false}',
|
||||
'cbA1 called with argument of {"didTimeout":false}',
|
||||
'frame 2 started',
|
||||
'frame 3 started... we stop counting now.',
|
||||
],
|
||||
// test 4
|
||||
[
|
||||
'scheduled cbA',
|
||||
'scheduled cbB',
|
||||
'scheduled cbC',
|
||||
'scheduled cbD',
|
||||
'frame 1 started',
|
||||
'cbC called with argument of {"didTimeout":true}',
|
||||
'cbA called with argument of {"didTimeout":false}',
|
||||
'cbA running and taking some time',
|
||||
'frame 2 started',
|
||||
'cbB called with argument of {"didTimeout":false}',
|
||||
'cbD called with argument of {"didTimeout":false}',
|
||||
'frame 3 started... we stop counting now.',
|
||||
],
|
||||
// test 5
|
||||
[
|
||||
// ... TODO
|
||||
],
|
||||
];
|
||||
function runTestOne() {
|
||||
// Test 1
|
||||
// Calls the callback with the frame when not blocked
|
||||
clearTestResult(1);
|
||||
const test1Log = [];
|
||||
const cb1Arguments = [];
|
||||
const cb1 = (x) => {
|
||||
updateTestResult(1, 'cb1 called with argument of ' + JSON.stringify(x));
|
||||
}
|
||||
scheduleWork(cb1);
|
||||
updateTestResult(1, 'scheduled Cb1');
|
||||
logWhenFramesStart(1, () => {
|
||||
displayTestResult(1);
|
||||
checkTestResult(1);
|
||||
});
|
||||
};
|
||||
|
||||
function runTestTwo() {
|
||||
// Test 2
|
||||
// accepts multiple callbacks and calls within frame when not blocked
|
||||
clearTestResult(2);
|
||||
const cbA = (x) => {
|
||||
updateTestResult(2, 'cbA called with argument of ' + JSON.stringify(x));
|
||||
}
|
||||
const cbB = (x) => {
|
||||
updateTestResult(2, 'cbB called with argument of ' + JSON.stringify(x));
|
||||
}
|
||||
scheduleWork(cbA);
|
||||
updateTestResult(2, 'scheduled CbA');
|
||||
scheduleWork(cbB);
|
||||
updateTestResult(2, 'scheduled CbB');
|
||||
logWhenFramesStart(2, () => {
|
||||
displayTestResult(2);
|
||||
checkTestResult(2);
|
||||
});
|
||||
}
|
||||
|
||||
function runTestThree() {
|
||||
// Test 3
|
||||
// Schedules callbacks in correct order when they use scheduleWork to schedule themselves
|
||||
clearTestResult(3);
|
||||
let callbackAIterations = 0;
|
||||
const cbA = (x) => {
|
||||
if (callbackAIterations < 1) {
|
||||
scheduleWork(cbA);
|
||||
updateTestResult(3, 'scheduled CbA again');
|
||||
}
|
||||
updateTestResult(3, 'cbA' + callbackAIterations + ' called with argument of ' + JSON.stringify(x));
|
||||
callbackAIterations++;
|
||||
}
|
||||
const cbB = (x) => {
|
||||
updateTestResult(3, 'cbB called with argument of ' + JSON.stringify(x));
|
||||
}
|
||||
scheduleWork(cbA);
|
||||
updateTestResult(3, 'scheduled CbA');
|
||||
scheduleWork(cbB);
|
||||
updateTestResult(3, 'scheduled CbB');
|
||||
logWhenFramesStart(3, () => {
|
||||
displayTestResult(3);
|
||||
checkTestResult(3);
|
||||
});
|
||||
}
|
||||
|
||||
function waitForTimeToPass(timeInMs) {
|
||||
const startTime = Date.now();
|
||||
const endTime = startTime + timeInMs;
|
||||
while (Date.now() < endTime) {
|
||||
// wait...
|
||||
}
|
||||
}
|
||||
|
||||
function runTestFour() {
|
||||
// Test 4
|
||||
// Calls timed out callbacks and then any more pending callbacks, defers others if time runs out
|
||||
clearTestResult(4);
|
||||
const cbA = (x) => {
|
||||
updateTestResult(4, 'cbA called with argument of ' + JSON.stringify(x));
|
||||
updateTestResult(4, 'cbA running and taking some time');
|
||||
waitForTimeToPass(35);
|
||||
}
|
||||
const cbB = (x) => {
|
||||
updateTestResult(4, 'cbB called with argument of ' + JSON.stringify(x));
|
||||
}
|
||||
const cbC = (x) => {
|
||||
updateTestResult(4, 'cbC called with argument of ' + JSON.stringify(x));
|
||||
}
|
||||
const cbD = (x) => {
|
||||
updateTestResult(4, 'cbD called with argument of ' + JSON.stringify(x));
|
||||
}
|
||||
scheduleWork(cbA); // won't time out
|
||||
updateTestResult(4, 'scheduled cbA');
|
||||
scheduleWork(cbB, {timeout: 100}); // times out later
|
||||
updateTestResult(4, 'scheduled cbB');
|
||||
scheduleWork(cbC, {timeout: 1}); // will time out fast
|
||||
updateTestResult(4, 'scheduled cbC');
|
||||
scheduleWork(cbD); // won't time out
|
||||
updateTestResult(4, 'scheduled cbD');
|
||||
|
||||
// should have run in order of C, A, B, D
|
||||
|
||||
logWhenFramesStart(4, () => {
|
||||
displayTestResult(4);
|
||||
checkTestResult(4);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Error handling
|
||||
|
||||
function runTestFive() {
|
||||
// Test 5
|
||||
// When some callbacks throw errors, still calls them all within the same frame
|
||||
const cbA = (x) => {
|
||||
console.log('cbA called with argument of ' + JSON.stringify(x));
|
||||
}
|
||||
const cbB = (x) => {
|
||||
console.log('cbB called with argument of ' + JSON.stringify(x));
|
||||
console.log('cbB is about to throw an error!');
|
||||
throw new Error('error B');
|
||||
}
|
||||
const cbC = (x) => {
|
||||
console.log('cbC called with argument of ' + JSON.stringify(x));
|
||||
}
|
||||
const cbD = (x) => {
|
||||
console.log('cbD called with argument of ' + JSON.stringify(x));
|
||||
console.log('cbD is about to throw an error!');
|
||||
throw new Error('error D');
|
||||
}
|
||||
const cbE = (x) => {
|
||||
console.log('cbE called with argument of ' + JSON.stringify(x));
|
||||
console.log('This was the last callback! ------------------');
|
||||
}
|
||||
|
||||
console.log('We are aiming to roughly emulate the way ' +
|
||||
'`requestAnimationFrame` handles errors from callbacks.');
|
||||
|
||||
console.log('about to run the simulation of what it should look like...:');
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
console.log('frame 1 started');
|
||||
requestAnimationFrame(() => {
|
||||
console.log('frame 2 started');
|
||||
requestAnimationFrame(() => {
|
||||
console.log('frame 3 started... we stop counting now.');
|
||||
console.log('about to wait a moment and start this again but ' +
|
||||
'with the scheduler instead of requestAnimationFrame');
|
||||
setTimeout(runSchedulerCode, 1000);
|
||||
});
|
||||
});
|
||||
});
|
||||
requestAnimationFrame(cbA);
|
||||
console.log('scheduled cbA');
|
||||
requestAnimationFrame(cbB); // will throw error
|
||||
console.log('scheduled cbB');
|
||||
requestAnimationFrame(cbC);
|
||||
console.log('scheduled cbC');
|
||||
requestAnimationFrame(cbD); // will throw error
|
||||
console.log('scheduled cbD');
|
||||
requestAnimationFrame(cbE);
|
||||
console.log('scheduled cbE');
|
||||
|
||||
|
||||
function runSchedulerCode() {
|
||||
console.log('-------------------------------------------------------------');
|
||||
console.log('now lets see what it looks like using the scheduler...:');
|
||||
requestAnimationFrame(() => {
|
||||
console.log('frame 1 started');
|
||||
requestAnimationFrame(() => {
|
||||
console.log('frame 2 started');
|
||||
requestAnimationFrame(() => {
|
||||
console.log('frame 3 started... we stop counting now.');
|
||||
});
|
||||
});
|
||||
});
|
||||
scheduleWork(cbA);
|
||||
console.log('scheduled cbA');
|
||||
scheduleWork(cbB); // will throw error
|
||||
console.log('scheduled cbB');
|
||||
scheduleWork(cbC);
|
||||
console.log('scheduled cbC');
|
||||
scheduleWork(cbD); // will throw error
|
||||
console.log('scheduled cbD');
|
||||
scheduleWork(cbE);
|
||||
console.log('scheduled cbE');
|
||||
};
|
||||
}
|
||||
|
||||
function runTestSix() {
|
||||
// Test 6
|
||||
// When some callbacks throw errors, still calls them all within the same frame
|
||||
const cbA = (x) => {
|
||||
console.log('cbA called with argument of ' + JSON.stringify(x));
|
||||
console.log('cbA is about to throw an error!');
|
||||
throw new Error('error A');
|
||||
}
|
||||
const cbB = (x) => {
|
||||
console.log('cbB called with argument of ' + JSON.stringify(x));
|
||||
}
|
||||
const cbC = (x) => {
|
||||
console.log('cbC called with argument of ' + JSON.stringify(x));
|
||||
}
|
||||
const cbD = (x) => {
|
||||
console.log('cbD called with argument of ' + JSON.stringify(x));
|
||||
console.log('cbD is about to throw an error!');
|
||||
throw new Error('error D');
|
||||
}
|
||||
const cbE = (x) => {
|
||||
console.log('cbE called with argument of ' + JSON.stringify(x));
|
||||
console.log('This was the last callback! ------------------');
|
||||
}
|
||||
|
||||
console.log('We are aiming to roughly emulate the way ' +
|
||||
'`requestAnimationFrame` handles errors from callbacks.');
|
||||
|
||||
console.log('about to run the simulation of what it should look like...:');
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
console.log('frame 1 started');
|
||||
requestAnimationFrame(() => {
|
||||
console.log('frame 2 started');
|
||||
requestAnimationFrame(() => {
|
||||
console.log('frame 3 started... we stop counting now.');
|
||||
console.log('about to wait a moment and start this again but ' +
|
||||
'with the scheduler instead of requestAnimationFrame');
|
||||
setTimeout(runSchedulerCode, 1000);
|
||||
});
|
||||
});
|
||||
});
|
||||
requestAnimationFrame(cbC);
|
||||
console.log('scheduled cbC first; simulating timing out');
|
||||
requestAnimationFrame(cbD); // will throw error
|
||||
console.log('scheduled cbD first; simulating timing out');
|
||||
requestAnimationFrame(cbE);
|
||||
console.log('scheduled cbE first; simulating timing out');
|
||||
requestAnimationFrame(cbA);
|
||||
console.log('scheduled cbA'); // will throw error
|
||||
requestAnimationFrame(cbB);
|
||||
console.log('scheduled cbB');
|
||||
|
||||
|
||||
function runSchedulerCode() {
|
||||
console.log('-------------------------------------------------------------');
|
||||
console.log('now lets see what it looks like using the scheduler...:');
|
||||
requestAnimationFrame(() => {
|
||||
console.log('frame 1 started');
|
||||
requestAnimationFrame(() => {
|
||||
console.log('frame 2 started');
|
||||
requestAnimationFrame(() => {
|
||||
console.log('frame 3 started... we stop counting now.');
|
||||
});
|
||||
});
|
||||
});
|
||||
scheduleWork(cbA);
|
||||
console.log('scheduled cbA');
|
||||
scheduleWork(cbB); // will throw error
|
||||
console.log('scheduled cbB');
|
||||
scheduleWork(cbC, {timeout: 1});
|
||||
console.log('scheduled cbC');
|
||||
scheduleWork(cbD, {timeout: 1}); // will throw error
|
||||
console.log('scheduled cbD');
|
||||
scheduleWork(cbE, {timeout: 1});
|
||||
console.log('scheduled cbE');
|
||||
};
|
||||
}
|
||||
|
||||
function runTestSeven() {
|
||||
// Test 7
|
||||
// Calls callbacks, continues calling them even when this tab is in the
|
||||
// background
|
||||
clearTestResult(7);
|
||||
let counter = -1;
|
||||
function incrementCounterAndScheduleNextCallback() {
|
||||
const counterNode = document.getElementById('test-7');
|
||||
counter++;
|
||||
counterNode.innerHTML = counter;
|
||||
waitForTimeToPass(100);
|
||||
scheduleWork(incrementCounterAndScheduleNextCallback);
|
||||
}
|
||||
scheduleWork(incrementCounterAndScheduleNextCallback);
|
||||
}
|
||||
</script type="text/babel">
|
||||
</body>
|
||||
</html>
|
||||
11
package.json
11
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"version": "16.4.0",
|
||||
"version": "16.4.1",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
@@ -11,7 +11,7 @@
|
||||
"babel-code-frame": "^6.26.0",
|
||||
"babel-core": "^6.0.0",
|
||||
"babel-eslint": "^8.0.0",
|
||||
"babel-jest": "^22.4.4",
|
||||
"babel-jest": "^23.0.1",
|
||||
"babel-plugin-check-es2015-constants": "^6.5.0",
|
||||
"babel-plugin-external-helpers": "^6.22.0",
|
||||
"babel-plugin-syntax-trailing-function-commas": "^6.5.0",
|
||||
@@ -57,7 +57,6 @@
|
||||
"eslint-plugin-no-for-of-loops": "^1.0.0",
|
||||
"eslint-plugin-react": "^6.7.1",
|
||||
"eslint-plugin-react-internal": "link:./scripts/eslint-rules/",
|
||||
"fbjs": "^0.8.16",
|
||||
"fbjs-scripts": "^0.6.0",
|
||||
"filesize": "^3.5.6",
|
||||
"flow-bin": "^0.72.0",
|
||||
@@ -69,8 +68,8 @@
|
||||
"gzip-js": "~0.3.2",
|
||||
"gzip-size": "^3.0.0",
|
||||
"jasmine-check": "^1.0.0-rc.0",
|
||||
"jest": "^22.4.4",
|
||||
"jest-diff": "^22.4.3",
|
||||
"jest": "^23.1.0",
|
||||
"jest-diff": "^23.0.1",
|
||||
"merge-stream": "^1.0.0",
|
||||
"minimatch": "^3.0.4",
|
||||
"minimist": "^1.2.0",
|
||||
@@ -79,7 +78,7 @@
|
||||
"object-assign": "^4.1.1",
|
||||
"platform": "^1.1.0",
|
||||
"prettier": "1.11.1",
|
||||
"prop-types": "^15.6.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"random-seed": "^0.3.0",
|
||||
"react-lifecycles-compat": "^3.0.2",
|
||||
"rimraf": "^2.6.1",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "create-subscription",
|
||||
"description": "utility for subscribing to external data sources inside React components",
|
||||
"version": "16.4.0",
|
||||
"version": "16.4.1",
|
||||
"repository": "facebook/react",
|
||||
"files": [
|
||||
"LICENSE",
|
||||
@@ -9,9 +9,6 @@
|
||||
"index.js",
|
||||
"cjs/"
|
||||
],
|
||||
"dependencies": {
|
||||
"fbjs": "^0.8.16"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.3.0"
|
||||
},
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import warning from 'fbjs/lib/warning';
|
||||
import invariant from 'shared/invariant';
|
||||
import warning from 'shared/warning';
|
||||
|
||||
type Unsubscribe = () => void;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import ReactErrorUtils from 'shared/ReactErrorUtils';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import invariant from 'shared/invariant';
|
||||
|
||||
import {
|
||||
injectEventPluginOrder,
|
||||
|
||||
@@ -14,7 +14,7 @@ import type {
|
||||
PluginModule,
|
||||
} from './PluginModuleType';
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import invariant from 'shared/invariant';
|
||||
|
||||
type NamesToPlugins = {[key: PluginName]: PluginModule<AnyNativeEvent>};
|
||||
type EventPluginOrder = null | Array<PluginName>;
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
*/
|
||||
|
||||
import ReactErrorUtils from 'shared/ReactErrorUtils';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import warning from 'fbjs/lib/warning';
|
||||
import invariant from 'shared/invariant';
|
||||
import warning from 'shared/warning';
|
||||
|
||||
export let getFiberCurrentPropsFromNode = null;
|
||||
export let getInstanceFromNode = null;
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
traverseTwoPhase,
|
||||
traverseEnterLeave,
|
||||
} from 'shared/ReactTreeTraversal';
|
||||
import warning from 'fbjs/lib/warning';
|
||||
import warning from 'shared/warning';
|
||||
|
||||
import {getListener} from './EventPluginHub';
|
||||
import accumulateInto from './accumulateInto';
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import invariant from 'shared/invariant';
|
||||
|
||||
import {
|
||||
getInstanceFromNode,
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import warning from 'fbjs/lib/warning';
|
||||
import invariant from 'shared/invariant';
|
||||
import warning from 'shared/warning';
|
||||
|
||||
import {isStartish, isMoveish, isEndish} from './ResponderTopLevelEventTypes';
|
||||
|
||||
|
||||
@@ -7,9 +7,8 @@
|
||||
|
||||
/* eslint valid-typeof: 0 */
|
||||
|
||||
import emptyFunction from 'fbjs/lib/emptyFunction';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import warning from 'fbjs/lib/warning';
|
||||
import invariant from 'shared/invariant';
|
||||
import warning from 'shared/warning';
|
||||
|
||||
let didWarnForAddedNewProperty = false;
|
||||
const EVENT_POOL_SIZE = 10;
|
||||
@@ -32,7 +31,9 @@ const EventInterface = {
|
||||
type: null,
|
||||
target: null,
|
||||
// currentTarget is set when dispatching; no use in copying it here
|
||||
currentTarget: emptyFunction.thatReturnsNull,
|
||||
currentTarget: function() {
|
||||
return null;
|
||||
},
|
||||
eventPhase: null,
|
||||
bubbles: null,
|
||||
cancelable: null,
|
||||
@@ -43,6 +44,14 @@ const EventInterface = {
|
||||
isTrusted: null,
|
||||
};
|
||||
|
||||
function functionThatReturnsTrue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function functionThatReturnsFalse() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthetic events are dispatched by event plugins, typically in response to a
|
||||
* top-level event delegation handler.
|
||||
@@ -103,11 +112,11 @@ function SyntheticEvent(
|
||||
? nativeEvent.defaultPrevented
|
||||
: nativeEvent.returnValue === false;
|
||||
if (defaultPrevented) {
|
||||
this.isDefaultPrevented = emptyFunction.thatReturnsTrue;
|
||||
this.isDefaultPrevented = functionThatReturnsTrue;
|
||||
} else {
|
||||
this.isDefaultPrevented = emptyFunction.thatReturnsFalse;
|
||||
this.isDefaultPrevented = functionThatReturnsFalse;
|
||||
}
|
||||
this.isPropagationStopped = emptyFunction.thatReturnsFalse;
|
||||
this.isPropagationStopped = functionThatReturnsFalse;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -124,7 +133,7 @@ Object.assign(SyntheticEvent.prototype, {
|
||||
} else if (typeof event.returnValue !== 'unknown') {
|
||||
event.returnValue = false;
|
||||
}
|
||||
this.isDefaultPrevented = emptyFunction.thatReturnsTrue;
|
||||
this.isDefaultPrevented = functionThatReturnsTrue;
|
||||
},
|
||||
|
||||
stopPropagation: function() {
|
||||
@@ -144,7 +153,7 @@ Object.assign(SyntheticEvent.prototype, {
|
||||
event.cancelBubble = true;
|
||||
}
|
||||
|
||||
this.isPropagationStopped = emptyFunction.thatReturnsTrue;
|
||||
this.isPropagationStopped = functionThatReturnsTrue;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -153,7 +162,7 @@ Object.assign(SyntheticEvent.prototype, {
|
||||
* won't be added back into the pool.
|
||||
*/
|
||||
persist: function() {
|
||||
this.isPersistent = emptyFunction.thatReturnsTrue;
|
||||
this.isPersistent = functionThatReturnsTrue;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -161,7 +170,7 @@ Object.assign(SyntheticEvent.prototype, {
|
||||
*
|
||||
* @return {boolean} True if this should not be released, false otherwise.
|
||||
*/
|
||||
isPersistent: emptyFunction.thatReturnsFalse,
|
||||
isPersistent: functionThatReturnsFalse,
|
||||
|
||||
/**
|
||||
* `PooledClass` looks for `destructor` on each instance it releases.
|
||||
@@ -191,12 +200,12 @@ Object.assign(SyntheticEvent.prototype, {
|
||||
Object.defineProperty(
|
||||
this,
|
||||
'preventDefault',
|
||||
getPooledWarningPropertyDefinition('preventDefault', emptyFunction),
|
||||
getPooledWarningPropertyDefinition('preventDefault', () => {}),
|
||||
);
|
||||
Object.defineProperty(
|
||||
this,
|
||||
'stopPropagation',
|
||||
getPooledWarningPropertyDefinition('stopPropagation', emptyFunction),
|
||||
getPooledWarningPropertyDefinition('stopPropagation', () => {}),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import invariant from 'shared/invariant';
|
||||
|
||||
/**
|
||||
* Accumulates items that must not be null or undefined.
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import invariant from 'shared/invariant';
|
||||
|
||||
/**
|
||||
* Accumulates items that must not be null or undefined into the first one. This
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "react-art",
|
||||
"description": "React ART is a JavaScript library for drawing vector graphics using React. It provides declarative and reactive bindings to the ART library. Using the same declarative API you can render the output to either Canvas, SVG or VML (IE8).",
|
||||
"version": "16.4.0",
|
||||
"version": "16.4.1",
|
||||
"main": "index.js",
|
||||
"repository": "facebook/react",
|
||||
"keywords": [
|
||||
@@ -20,10 +20,9 @@
|
||||
"dependencies": {
|
||||
"art": "^0.10.1",
|
||||
"create-react-class": "^15.6.2",
|
||||
"fbjs": "^0.8.16",
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"prop-types": "^15.6.0"
|
||||
"prop-types": "^15.6.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.0.0"
|
||||
|
||||
14
packages/react-art/src/ReactARTHostConfig.js
vendored
14
packages/react-art/src/ReactARTHostConfig.js
vendored
@@ -5,17 +5,21 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import * as ReactScheduler from 'react-scheduler';
|
||||
import * as ReactScheduler from 'shared/ReactScheduler';
|
||||
import Transform from 'art/core/transform';
|
||||
import Mode from 'art/modes/current';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import emptyObject from 'fbjs/lib/emptyObject';
|
||||
import invariant from 'shared/invariant';
|
||||
|
||||
import {TYPES, EVENT_TYPES, childrenAsString} from './ReactARTInternals';
|
||||
|
||||
const pooledTransform = new Transform();
|
||||
|
||||
const NO_CONTEXT = {};
|
||||
const UPDATE_SIGNAL = {};
|
||||
if (__DEV__) {
|
||||
Object.freeze(NO_CONTEXT);
|
||||
Object.freeze(UPDATE_SIGNAL);
|
||||
}
|
||||
|
||||
/** Helper Methods */
|
||||
|
||||
@@ -318,11 +322,11 @@ export function shouldDeprioritizeSubtree(type, props) {
|
||||
}
|
||||
|
||||
export function getRootHostContext() {
|
||||
return emptyObject;
|
||||
return NO_CONTEXT;
|
||||
}
|
||||
|
||||
export function getChildHostContext() {
|
||||
return emptyObject;
|
||||
return NO_CONTEXT;
|
||||
}
|
||||
|
||||
export const scheduleDeferredCallback = ReactScheduler.scheduleWork;
|
||||
|
||||
@@ -71,6 +71,8 @@ describe('ReactART', () => {
|
||||
Surface = ReactART.Surface;
|
||||
|
||||
TestComponent = class extends React.Component {
|
||||
group = React.createRef();
|
||||
|
||||
render() {
|
||||
const a = (
|
||||
<Shape
|
||||
@@ -104,7 +106,7 @@ describe('ReactART', () => {
|
||||
|
||||
return (
|
||||
<Surface width={150} height={200}>
|
||||
<Group ref="group">
|
||||
<Group ref={this.group}>
|
||||
{this.props.flipped ? [b, a, c] : [a, b, c]}
|
||||
</Group>
|
||||
</Surface>
|
||||
@@ -121,7 +123,7 @@ describe('ReactART', () => {
|
||||
it('should have the correct lifecycle state', () => {
|
||||
let instance = <TestComponent />;
|
||||
instance = ReactTestUtils.renderIntoDocument(instance);
|
||||
const group = instance.refs.group;
|
||||
const group = instance.group.current;
|
||||
// Duck type test for an ART group
|
||||
expect(typeof group.indicate).toBe('function');
|
||||
});
|
||||
@@ -260,15 +262,17 @@ describe('ReactART', () => {
|
||||
let ref = null;
|
||||
|
||||
class Outer extends React.Component {
|
||||
test = React.createRef();
|
||||
|
||||
componentDidMount() {
|
||||
ref = this.refs.test;
|
||||
ref = this.test.current;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Surface>
|
||||
<Group>
|
||||
<CustomShape ref="test" />
|
||||
<CustomShape ref={this.test} />
|
||||
</Group>
|
||||
</Surface>
|
||||
);
|
||||
@@ -289,26 +293,28 @@ describe('ReactART', () => {
|
||||
let ref = {};
|
||||
|
||||
class Outer extends React.Component {
|
||||
test = React.createRef();
|
||||
|
||||
componentDidMount() {
|
||||
ref = this.refs.test;
|
||||
ref = this.test.current;
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
ref = this.refs.test;
|
||||
ref = this.test.current;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Surface>
|
||||
<Group>
|
||||
{this.props.mountCustomShape && <CustomShape ref="test" />}
|
||||
{this.props.mountCustomShape && <CustomShape ref={this.test} />}
|
||||
</Group>
|
||||
</Surface>
|
||||
);
|
||||
}
|
||||
}
|
||||
ReactDOM.render(<Outer />, container);
|
||||
expect(ref).not.toBeDefined();
|
||||
expect(ref).toBe(null);
|
||||
ReactDOM.render(<Outer mountCustomShape={true} />, container);
|
||||
expect(ref.constructor).toBe(CustomShape);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-dom",
|
||||
"version": "16.4.0",
|
||||
"version": "16.4.1",
|
||||
"description": "React package for working with the DOM.",
|
||||
"main": "index.js",
|
||||
"repository": "facebook/react",
|
||||
@@ -13,10 +13,9 @@
|
||||
},
|
||||
"homepage": "https://reactjs.org/",
|
||||
"dependencies": {
|
||||
"fbjs": "^0.8.16",
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"prop-types": "^15.6.0"
|
||||
"prop-types": "^15.6.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.0.0"
|
||||
|
||||
@@ -123,7 +123,7 @@ describe('DOMPropertyOperations', () => {
|
||||
spyOnDevAndProd(container.firstChild, 'setAttribute');
|
||||
ReactDOM.render(<progress value={30} />, container);
|
||||
ReactDOM.render(<progress value="30" />, container);
|
||||
expect(container.firstChild.setAttribute.calls.count()).toBe(2);
|
||||
expect(container.firstChild.setAttribute).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -42,6 +42,8 @@ let getListener;
|
||||
let putListener;
|
||||
let deleteAllListeners;
|
||||
|
||||
let container;
|
||||
|
||||
function registerSimpleTestHandler() {
|
||||
putListener(CHILD, ON_CLICK_KEY, LISTENER);
|
||||
const listener = getListener(CHILD, ON_CLICK_KEY);
|
||||
@@ -63,7 +65,8 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
ReactBrowserEventEmitter = require('../events/ReactBrowserEventEmitter');
|
||||
ReactTestUtils = require('react-dom/test-utils');
|
||||
|
||||
const container = document.createElement('div');
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
|
||||
let GRANDPARENT_PROPS = {};
|
||||
let PARENT_PROPS = {};
|
||||
@@ -129,6 +132,11 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
idCallOrder = [];
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
document.body.removeChild(container);
|
||||
container = null;
|
||||
});
|
||||
|
||||
it('should store a listener correctly', () => {
|
||||
registerSimpleTestHandler();
|
||||
const listener = getListener(CHILD, ON_CLICK_KEY);
|
||||
@@ -150,25 +158,25 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
|
||||
it('should invoke a simple handler registered on a node', () => {
|
||||
registerSimpleTestHandler();
|
||||
ReactTestUtils.Simulate.click(CHILD);
|
||||
expect(LISTENER.mock.calls.length).toBe(1);
|
||||
CHILD.click();
|
||||
expect(LISTENER).toHaveBeenCalledTimes(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);
|
||||
CHILD.click();
|
||||
expect(LISTENER).toHaveBeenCalledTimes(0);
|
||||
ReactBrowserEventEmitter.setEnabled(true);
|
||||
ReactTestUtils.SimulateNative.click(CHILD);
|
||||
expect(LISTENER.mock.calls.length).toBe(1);
|
||||
CHILD.click();
|
||||
expect(LISTENER).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should bubble simply', () => {
|
||||
putListener(CHILD, ON_CLICK_KEY, recordID.bind(null, CHILD));
|
||||
putListener(PARENT, ON_CLICK_KEY, recordID.bind(null, PARENT));
|
||||
putListener(GRANDPARENT, ON_CLICK_KEY, recordID.bind(null, GRANDPARENT));
|
||||
ReactTestUtils.Simulate.click(CHILD);
|
||||
CHILD.click();
|
||||
expect(idCallOrder.length).toBe(3);
|
||||
expect(idCallOrder[0]).toBe(CHILD);
|
||||
expect(idCallOrder[1]).toBe(PARENT);
|
||||
@@ -179,7 +187,7 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
putListener(GRANDPARENT, ON_CLICK_KEY, recordID.bind(null, 'GRANDPARENT'));
|
||||
putListener(PARENT, ON_CLICK_KEY, recordID.bind(null, 'PARENT'));
|
||||
putListener(CHILD, ON_CLICK_KEY, recordID.bind(null, 'CHILD'));
|
||||
ReactTestUtils.Simulate.click(CHILD);
|
||||
CHILD.click();
|
||||
expect(idCallOrder).toEqual(['CHILD', 'PARENT', 'GRANDPARENT']);
|
||||
|
||||
idCallOrder = [];
|
||||
@@ -191,7 +199,7 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
recordID.bind(null, 'UPDATED_GRANDPARENT'),
|
||||
);
|
||||
|
||||
ReactTestUtils.Simulate.click(CHILD);
|
||||
CHILD.click();
|
||||
expect(idCallOrder).toEqual(['CHILD', 'PARENT', 'UPDATED_GRANDPARENT']);
|
||||
});
|
||||
|
||||
@@ -224,7 +232,7 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
recordID(GRANDPARENT);
|
||||
expect(event.currentTarget).toBe(GRANDPARENT);
|
||||
});
|
||||
ReactTestUtils.Simulate.click(CHILD);
|
||||
CHILD.click();
|
||||
expect(idCallOrder.length).toBe(3);
|
||||
expect(idCallOrder[0]).toBe(CHILD);
|
||||
expect(idCallOrder[1]).toBe(PARENT);
|
||||
@@ -239,7 +247,7 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
recordIDAndStopPropagation.bind(null, PARENT),
|
||||
);
|
||||
putListener(GRANDPARENT, ON_CLICK_KEY, recordID.bind(null, GRANDPARENT));
|
||||
ReactTestUtils.Simulate.click(CHILD);
|
||||
CHILD.click();
|
||||
expect(idCallOrder.length).toBe(2);
|
||||
expect(idCallOrder[0]).toBe(CHILD);
|
||||
expect(idCallOrder[1]).toBe(PARENT);
|
||||
@@ -254,7 +262,7 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
e.isPropagationStopped = () => true;
|
||||
});
|
||||
putListener(GRANDPARENT, ON_CLICK_KEY, recordID.bind(null, GRANDPARENT));
|
||||
ReactTestUtils.Simulate.click(CHILD);
|
||||
CHILD.click();
|
||||
expect(idCallOrder.length).toBe(2);
|
||||
expect(idCallOrder[0]).toBe(CHILD);
|
||||
expect(idCallOrder[1]).toBe(PARENT);
|
||||
@@ -268,7 +276,7 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
);
|
||||
putListener(PARENT, ON_CLICK_KEY, recordID.bind(null, PARENT));
|
||||
putListener(GRANDPARENT, ON_CLICK_KEY, recordID.bind(null, GRANDPARENT));
|
||||
ReactTestUtils.Simulate.click(CHILD);
|
||||
CHILD.click();
|
||||
expect(idCallOrder.length).toBe(1);
|
||||
expect(idCallOrder[0]).toBe(CHILD);
|
||||
});
|
||||
@@ -277,7 +285,7 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
putListener(CHILD, ON_CLICK_KEY, recordIDAndReturnFalse.bind(null, CHILD));
|
||||
putListener(PARENT, ON_CLICK_KEY, recordID.bind(null, PARENT));
|
||||
putListener(GRANDPARENT, ON_CLICK_KEY, recordID.bind(null, GRANDPARENT));
|
||||
ReactTestUtils.Simulate.click(CHILD);
|
||||
CHILD.click();
|
||||
expect(idCallOrder.length).toBe(3);
|
||||
expect(idCallOrder[0]).toBe(CHILD);
|
||||
expect(idCallOrder[1]).toBe(PARENT);
|
||||
@@ -300,8 +308,8 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
};
|
||||
putListener(CHILD, ON_CLICK_KEY, handleChildClick);
|
||||
putListener(PARENT, ON_CLICK_KEY, handleParentClick);
|
||||
ReactTestUtils.Simulate.click(CHILD);
|
||||
expect(handleParentClick.mock.calls.length).toBe(1);
|
||||
CHILD.click();
|
||||
expect(handleParentClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should not invoke newly inserted handlers while bubbling', () => {
|
||||
@@ -310,8 +318,8 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
putListener(PARENT, ON_CLICK_KEY, handleParentClick);
|
||||
};
|
||||
putListener(CHILD, ON_CLICK_KEY, handleChildClick);
|
||||
ReactTestUtils.Simulate.click(CHILD);
|
||||
expect(handleParentClick.mock.calls.length).toBe(0);
|
||||
CHILD.click();
|
||||
expect(handleParentClick).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('should have mouse enter simulated by test utils', () => {
|
||||
@@ -325,7 +333,7 @@ describe('ReactBrowserEventEmitter', () => {
|
||||
spyOnDevAndProd(EventTarget.prototype, 'addEventListener');
|
||||
ReactBrowserEventEmitter.listenTo(ON_CLICK_KEY, document);
|
||||
ReactBrowserEventEmitter.listenTo(ON_CLICK_KEY, document);
|
||||
expect(EventTarget.prototype.addEventListener.calls.count()).toBe(1);
|
||||
expect(EventTarget.prototype.addEventListener).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should work with event plugins without dependencies', () => {
|
||||
|
||||
@@ -278,7 +278,7 @@ describe('ReactComponent', () => {
|
||||
componentDidMount() {
|
||||
// Check .props.title to make sure we got the right elements back
|
||||
expect(this.wrapperRef.getTitle()).toBe('wrapper');
|
||||
expect(ReactDOM.findDOMNode(this.innerRef).className).toBe('inner');
|
||||
expect(this.innerRef.className).toBe('inner');
|
||||
mounted = true;
|
||||
}
|
||||
}
|
||||
@@ -387,11 +387,11 @@ describe('ReactComponent', () => {
|
||||
const callback = jest.fn();
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<div />, container, callback);
|
||||
expect(callback.mock.calls.length).toBe(1);
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
ReactDOM.render(<div className="foo" />, container, callback);
|
||||
expect(callback.mock.calls.length).toBe(2);
|
||||
expect(callback).toHaveBeenCalledTimes(2);
|
||||
ReactDOM.render(<span />, container, callback);
|
||||
expect(callback.mock.calls.length).toBe(3);
|
||||
expect(callback).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it('throws usefully when rendering badly-typed elements', () => {
|
||||
|
||||
@@ -1065,16 +1065,22 @@ describe('ReactComponentLifeCycle', () => {
|
||||
}
|
||||
}
|
||||
|
||||
ReactTestUtils.renderIntoDocument(<Parent />);
|
||||
expect(divRef.current.textContent).toBe('remote:0, local:0');
|
||||
const container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
try {
|
||||
ReactDOM.render(<Parent />, container);
|
||||
expect(divRef.current.textContent).toBe('remote:0, local:0');
|
||||
|
||||
// Trigger setState() calls
|
||||
childInstance.updateState();
|
||||
expect(divRef.current.textContent).toBe('remote:1, local:1');
|
||||
// Trigger setState() calls
|
||||
childInstance.updateState();
|
||||
expect(divRef.current.textContent).toBe('remote:1, local:1');
|
||||
|
||||
// Trigger batched setState() calls
|
||||
ReactTestUtils.Simulate.click(divRef.current);
|
||||
expect(divRef.current.textContent).toBe('remote:2, local:2');
|
||||
// Trigger batched setState() calls
|
||||
divRef.current.click();
|
||||
expect(divRef.current.textContent).toBe('remote:2, local:2');
|
||||
} finally {
|
||||
document.body.removeChild(container);
|
||||
}
|
||||
});
|
||||
|
||||
it('should pass the return value from getSnapshotBeforeUpdate to componentDidUpdate', () => {
|
||||
|
||||
@@ -17,10 +17,50 @@ let ReactDOMServer;
|
||||
let ReactCurrentOwner;
|
||||
let ReactTestUtils;
|
||||
let PropTypes;
|
||||
let shallowEqual;
|
||||
let shallowCompare;
|
||||
|
||||
describe('ReactCompositeComponent', () => {
|
||||
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
/**
|
||||
* Performs equality by iterating through keys on an object and returning false
|
||||
* when any key has values which are not strictly equal between the arguments.
|
||||
* Returns true when the values of all keys are strictly equal.
|
||||
*/
|
||||
function shallowEqual(objA: mixed, objB: mixed): boolean {
|
||||
if (Object.is(objA, objB)) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
typeof objA !== 'object' ||
|
||||
objA === null ||
|
||||
typeof objB !== 'object' ||
|
||||
objB === null
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const keysA = Object.keys(objA);
|
||||
const keysB = Object.keys(objB);
|
||||
if (keysA.length !== keysB.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < keysA.length; i++) {
|
||||
if (
|
||||
!hasOwnProperty.call(objB, keysA[i]) ||
|
||||
!Object.is(objA[keysA[i]], objB[keysA[i]])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function shallowCompare(instance, nextProps, nextState) {
|
||||
return (
|
||||
!shallowEqual(instance.props, nextProps) ||
|
||||
!shallowEqual(instance.state, nextState)
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
React = require('react');
|
||||
@@ -30,14 +70,6 @@ describe('ReactCompositeComponent', () => {
|
||||
.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner;
|
||||
ReactTestUtils = require('react-dom/test-utils');
|
||||
PropTypes = require('prop-types');
|
||||
shallowEqual = require('fbjs/lib/shallowEqual');
|
||||
|
||||
shallowCompare = function(instance, nextProps, nextState) {
|
||||
return (
|
||||
!shallowEqual(instance.props, nextProps) ||
|
||||
!shallowEqual(instance.state, nextState)
|
||||
);
|
||||
};
|
||||
|
||||
MorphingComponent = class extends React.Component {
|
||||
state = {activated: false};
|
||||
@@ -140,23 +172,28 @@ describe('ReactCompositeComponent', () => {
|
||||
});
|
||||
|
||||
it('should react to state changes from callbacks', () => {
|
||||
const instance = ReactTestUtils.renderIntoDocument(<MorphingComponent />);
|
||||
let el = ReactDOM.findDOMNode(instance);
|
||||
expect(el.tagName).toBe('A');
|
||||
|
||||
ReactTestUtils.Simulate.click(el);
|
||||
el = ReactDOM.findDOMNode(instance);
|
||||
expect(el.tagName).toBe('B');
|
||||
const container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
try {
|
||||
const instance = ReactDOM.render(<MorphingComponent />, container);
|
||||
let el = ReactDOM.findDOMNode(instance);
|
||||
expect(el.tagName).toBe('A');
|
||||
el.click();
|
||||
el = ReactDOM.findDOMNode(instance);
|
||||
expect(el.tagName).toBe('B');
|
||||
} finally {
|
||||
document.body.removeChild(container);
|
||||
}
|
||||
});
|
||||
|
||||
it('should rewire refs when rendering to different child types', () => {
|
||||
const instance = ReactTestUtils.renderIntoDocument(<MorphingComponent />);
|
||||
|
||||
expect(ReactDOM.findDOMNode(instance.refs.x).tagName).toBe('A');
|
||||
expect(instance.refs.x.tagName).toBe('A');
|
||||
instance._toggleActivatedState();
|
||||
expect(ReactDOM.findDOMNode(instance.refs.x).tagName).toBe('B');
|
||||
expect(instance.refs.x.tagName).toBe('B');
|
||||
instance._toggleActivatedState();
|
||||
expect(ReactDOM.findDOMNode(instance.refs.x).tagName).toBe('A');
|
||||
expect(instance.refs.x.tagName).toBe('A');
|
||||
});
|
||||
|
||||
it('should not cache old DOM nodes when switching constructors', () => {
|
||||
|
||||
@@ -11,13 +11,11 @@
|
||||
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactTestUtils;
|
||||
|
||||
describe('ReactCompositeComponentNestedState-state', () => {
|
||||
beforeEach(() => {
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactTestUtils = require('react-dom/test-utils');
|
||||
});
|
||||
|
||||
it('should provide up to date values for props', () => {
|
||||
@@ -102,7 +100,7 @@ describe('ReactCompositeComponentNestedState-state', () => {
|
||||
void ReactDOM.render(<ParentComponent logger={logger} />, container);
|
||||
|
||||
// click "light green"
|
||||
ReactTestUtils.Simulate.click(container.childNodes[0].childNodes[3]);
|
||||
container.childNodes[0].childNodes[3].click();
|
||||
|
||||
expect(logger.mock.calls).toEqual([
|
||||
['parent-render', 'blue'],
|
||||
|
||||
@@ -45,23 +45,21 @@ describe('ReactDOM', () => {
|
||||
|
||||
it('allows a DOM element to be used with a string', () => {
|
||||
const element = React.createElement('div', {className: 'foo'});
|
||||
const instance = ReactTestUtils.renderIntoDocument(element);
|
||||
expect(ReactDOM.findDOMNode(instance).tagName).toBe('DIV');
|
||||
const node = ReactTestUtils.renderIntoDocument(element);
|
||||
expect(node.tagName).toBe('DIV');
|
||||
});
|
||||
|
||||
it('should allow children to be passed as an argument', () => {
|
||||
const argDiv = ReactTestUtils.renderIntoDocument(
|
||||
const argNode = ReactTestUtils.renderIntoDocument(
|
||||
React.createElement('div', null, 'child'),
|
||||
);
|
||||
const argNode = ReactDOM.findDOMNode(argDiv);
|
||||
expect(argNode.innerHTML).toBe('child');
|
||||
});
|
||||
|
||||
it('should overwrite props.children with children argument', () => {
|
||||
const conflictDiv = ReactTestUtils.renderIntoDocument(
|
||||
const conflictNode = ReactTestUtils.renderIntoDocument(
|
||||
React.createElement('div', {children: 'fakechild'}, 'child'),
|
||||
);
|
||||
const conflictNode = ReactDOM.findDOMNode(conflictDiv);
|
||||
expect(conflictNode.innerHTML).toBe('child');
|
||||
});
|
||||
|
||||
@@ -103,8 +101,7 @@ describe('ReactDOM', () => {
|
||||
<div key="theBird" className="bird" />,
|
||||
</div>,
|
||||
);
|
||||
const root = ReactDOM.findDOMNode(myDiv);
|
||||
const dog = root.childNodes[0];
|
||||
const dog = myDiv.childNodes[0];
|
||||
expect(dog.className).toBe('bigdog');
|
||||
});
|
||||
|
||||
@@ -242,34 +239,37 @@ describe('ReactDOM', () => {
|
||||
const log = [];
|
||||
const container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
ReactDOM.render(<A showTwo={false} />, container);
|
||||
input.focus();
|
||||
try {
|
||||
ReactDOM.render(<A showTwo={false} />, container);
|
||||
input.focus();
|
||||
|
||||
// When the second input is added, let's simulate losing focus, which is
|
||||
// something that could happen when manipulating DOM nodes (but is hard to
|
||||
// deterministically force without relying intensely on React DOM
|
||||
// implementation details)
|
||||
const div = container.firstChild;
|
||||
['appendChild', 'insertBefore'].forEach(name => {
|
||||
const mutator = div[name];
|
||||
div[name] = function() {
|
||||
if (input) {
|
||||
input.blur();
|
||||
expect(document.activeElement.tagName).toBe('BODY');
|
||||
log.push('input2 inserted');
|
||||
}
|
||||
return mutator.apply(this, arguments);
|
||||
};
|
||||
});
|
||||
// When the second input is added, let's simulate losing focus, which is
|
||||
// something that could happen when manipulating DOM nodes (but is hard to
|
||||
// deterministically force without relying intensely on React DOM
|
||||
// implementation details)
|
||||
const div = container.firstChild;
|
||||
['appendChild', 'insertBefore'].forEach(name => {
|
||||
const mutator = div[name];
|
||||
div[name] = function() {
|
||||
if (input) {
|
||||
input.blur();
|
||||
expect(document.activeElement.tagName).toBe('BODY');
|
||||
log.push('input2 inserted');
|
||||
}
|
||||
return mutator.apply(this, arguments);
|
||||
};
|
||||
});
|
||||
|
||||
expect(document.activeElement.id).toBe('one');
|
||||
ReactDOM.render(<A showTwo={true} />, container);
|
||||
// input2 gets added, which causes input to get blurred. Then
|
||||
// componentDidUpdate focuses input2 and that should make it down to here,
|
||||
// not get overwritten by focus restoration.
|
||||
expect(document.activeElement.id).toBe('two');
|
||||
expect(log).toEqual(['input2 inserted', 'input2 focused']);
|
||||
document.body.removeChild(container);
|
||||
expect(document.activeElement.id).toBe('one');
|
||||
ReactDOM.render(<A showTwo={true} />, container);
|
||||
// input2 gets added, which causes input to get blurred. Then
|
||||
// componentDidUpdate focuses input2 and that should make it down to here,
|
||||
// not get overwritten by focus restoration.
|
||||
expect(document.activeElement.id).toBe('two');
|
||||
expect(log).toEqual(['input2 inserted', 'input2 focused']);
|
||||
} finally {
|
||||
document.body.removeChild(container);
|
||||
}
|
||||
});
|
||||
|
||||
it('calls focus() on autoFocus elements after they have been mounted to the DOM', () => {
|
||||
@@ -308,15 +308,9 @@ describe('ReactDOM', () => {
|
||||
it("shouldn't fire duplicate event handler while handling other nested dispatch", () => {
|
||||
const actual = [];
|
||||
|
||||
function click(node) {
|
||||
ReactTestUtils.Simulate.click(node, {
|
||||
path: [node, container],
|
||||
});
|
||||
}
|
||||
|
||||
class Wrapper extends React.Component {
|
||||
componentDidMount() {
|
||||
click(this.ref1);
|
||||
this.ref1.click();
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -325,7 +319,7 @@ describe('ReactDOM', () => {
|
||||
<div
|
||||
onClick={() => {
|
||||
actual.push('1st node clicked');
|
||||
click(this.ref2);
|
||||
this.ref2.click();
|
||||
}}
|
||||
ref={ref => (this.ref1 = ref)}
|
||||
/>
|
||||
@@ -341,13 +335,18 @@ describe('ReactDOM', () => {
|
||||
}
|
||||
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<Wrapper />, container);
|
||||
document.body.appendChild(container);
|
||||
try {
|
||||
ReactDOM.render(<Wrapper />, container);
|
||||
|
||||
const expected = [
|
||||
'1st node clicked',
|
||||
"2nd node clicked imperatively from 1st's handler",
|
||||
];
|
||||
expect(actual).toEqual(expected);
|
||||
const expected = [
|
||||
'1st node clicked',
|
||||
"2nd node clicked imperatively from 1st's handler",
|
||||
];
|
||||
expect(actual).toEqual(expected);
|
||||
} finally {
|
||||
document.body.removeChild(container);
|
||||
}
|
||||
});
|
||||
|
||||
it('should not crash with devtools installed', () => {
|
||||
@@ -441,7 +440,7 @@ describe('ReactDOM', () => {
|
||||
global.requestAnimationFrame = undefined;
|
||||
jest.resetModules();
|
||||
expect(() => require('react-dom')).toWarnDev(
|
||||
'React depends on requestAnimationFrame.',
|
||||
"This browser doesn't support requestAnimationFrame.",
|
||||
);
|
||||
} finally {
|
||||
global.requestAnimationFrame = previousRAF;
|
||||
|
||||
@@ -738,28 +738,28 @@ describe('ReactDOMComponent', () => {
|
||||
node.removeAttribute.mockImplementation(nodeRemoveAttribute);
|
||||
|
||||
ReactDOM.render(<div id="" />, container);
|
||||
expect(node.setAttribute.mock.calls.length).toBe(0);
|
||||
expect(node.removeAttribute.mock.calls.length).toBe(0);
|
||||
expect(node.setAttribute).toHaveBeenCalledTimes(0);
|
||||
expect(node.removeAttribute).toHaveBeenCalledTimes(0);
|
||||
|
||||
ReactDOM.render(<div id="foo" />, container);
|
||||
expect(node.setAttribute.mock.calls.length).toBe(1);
|
||||
expect(node.removeAttribute.mock.calls.length).toBe(0);
|
||||
expect(node.setAttribute).toHaveBeenCalledTimes(1);
|
||||
expect(node.removeAttribute).toHaveBeenCalledTimes(0);
|
||||
|
||||
ReactDOM.render(<div id="foo" />, container);
|
||||
expect(node.setAttribute.mock.calls.length).toBe(1);
|
||||
expect(node.removeAttribute.mock.calls.length).toBe(0);
|
||||
expect(node.setAttribute).toHaveBeenCalledTimes(1);
|
||||
expect(node.removeAttribute).toHaveBeenCalledTimes(0);
|
||||
|
||||
ReactDOM.render(<div />, container);
|
||||
expect(node.setAttribute.mock.calls.length).toBe(1);
|
||||
expect(node.removeAttribute.mock.calls.length).toBe(1);
|
||||
expect(node.setAttribute).toHaveBeenCalledTimes(1);
|
||||
expect(node.removeAttribute).toHaveBeenCalledTimes(1);
|
||||
|
||||
ReactDOM.render(<div id="" />, container);
|
||||
expect(node.setAttribute.mock.calls.length).toBe(2);
|
||||
expect(node.removeAttribute.mock.calls.length).toBe(1);
|
||||
expect(node.setAttribute).toHaveBeenCalledTimes(2);
|
||||
expect(node.removeAttribute).toHaveBeenCalledTimes(1);
|
||||
|
||||
ReactDOM.render(<div />, container);
|
||||
expect(node.setAttribute.mock.calls.length).toBe(2);
|
||||
expect(node.removeAttribute.mock.calls.length).toBe(2);
|
||||
expect(node.setAttribute).toHaveBeenCalledTimes(2);
|
||||
expect(node.removeAttribute).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should not incur unnecessary DOM mutations for string properties', () => {
|
||||
@@ -768,7 +768,7 @@ describe('ReactDOMComponent', () => {
|
||||
|
||||
const node = container.firstChild;
|
||||
|
||||
const nodeValueSetter = jest.genMockFn();
|
||||
const nodeValueSetter = jest.fn();
|
||||
|
||||
const oldSetAttribute = node.setAttribute.bind(node);
|
||||
node.setAttribute = function(key, value) {
|
||||
@@ -777,22 +777,22 @@ describe('ReactDOMComponent', () => {
|
||||
};
|
||||
|
||||
ReactDOM.render(<div value="foo" />, container);
|
||||
expect(nodeValueSetter.mock.calls.length).toBe(1);
|
||||
expect(nodeValueSetter).toHaveBeenCalledTimes(1);
|
||||
|
||||
ReactDOM.render(<div value="foo" />, container);
|
||||
expect(nodeValueSetter.mock.calls.length).toBe(1);
|
||||
expect(nodeValueSetter).toHaveBeenCalledTimes(1);
|
||||
|
||||
ReactDOM.render(<div />, container);
|
||||
expect(nodeValueSetter.mock.calls.length).toBe(1);
|
||||
expect(nodeValueSetter).toHaveBeenCalledTimes(1);
|
||||
|
||||
ReactDOM.render(<div value={null} />, container);
|
||||
expect(nodeValueSetter.mock.calls.length).toBe(1);
|
||||
expect(nodeValueSetter).toHaveBeenCalledTimes(1);
|
||||
|
||||
ReactDOM.render(<div value="" />, container);
|
||||
expect(nodeValueSetter.mock.calls.length).toBe(2);
|
||||
expect(nodeValueSetter).toHaveBeenCalledTimes(2);
|
||||
|
||||
ReactDOM.render(<div />, container);
|
||||
expect(nodeValueSetter.mock.calls.length).toBe(2);
|
||||
expect(nodeValueSetter).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should not incur unnecessary DOM mutations for boolean properties', () => {
|
||||
@@ -812,16 +812,16 @@ describe('ReactDOMComponent', () => {
|
||||
});
|
||||
|
||||
ReactDOM.render(<div checked={true} />, container);
|
||||
expect(nodeValueSetter.mock.calls.length).toBe(0);
|
||||
expect(nodeValueSetter).toHaveBeenCalledTimes(0);
|
||||
|
||||
ReactDOM.render(<div />, container);
|
||||
expect(nodeValueSetter.mock.calls.length).toBe(1);
|
||||
expect(nodeValueSetter).toHaveBeenCalledTimes(1);
|
||||
|
||||
ReactDOM.render(<div checked={false} />, container);
|
||||
expect(nodeValueSetter.mock.calls.length).toBe(2);
|
||||
expect(nodeValueSetter).toHaveBeenCalledTimes(2);
|
||||
|
||||
ReactDOM.render(<div checked={true} />, container);
|
||||
expect(nodeValueSetter.mock.calls.length).toBe(3);
|
||||
expect(nodeValueSetter).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it('should ignore attribute whitelist for elements with the "is" attribute', () => {
|
||||
@@ -850,9 +850,9 @@ describe('ReactDOMComponent', () => {
|
||||
ReactDOM.render(<div dir={null} />, container);
|
||||
ReactDOM.render(<div dir={undefined} />, container);
|
||||
ReactDOM.render(<div />, container);
|
||||
expect(setter.mock.calls.length).toBe(0);
|
||||
expect(setter).toHaveBeenCalledTimes(0);
|
||||
ReactDOM.render(<div dir="ltr" />, container);
|
||||
expect(setter.mock.calls.length).toBe(1);
|
||||
expect(setter).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('handles multiple child updates without interference', () => {
|
||||
@@ -984,7 +984,7 @@ describe('ReactDOMComponent', () => {
|
||||
container.getElementsByTagName('source')[0].dispatchEvent(errorEvent);
|
||||
|
||||
if (__DEV__) {
|
||||
expect(console.log.calls.count()).toBe(1);
|
||||
expect(console.log).toHaveBeenCalledTimes(1);
|
||||
expect(console.log.calls.argsFor(0)[0]).toContain('onError called');
|
||||
}
|
||||
});
|
||||
@@ -1315,7 +1315,7 @@ describe('ReactDOMComponent', () => {
|
||||
container.getElementsByTagName('image')[0].dispatchEvent(loadEvent);
|
||||
|
||||
if (__DEV__) {
|
||||
expect(console.log.calls.count()).toBe(2);
|
||||
expect(console.log).toHaveBeenCalledTimes(2);
|
||||
expect(console.log.calls.argsFor(0)[0]).toContain('onError called');
|
||||
expect(console.log.calls.argsFor(1)[0]).toContain('onLoad called');
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ describe('ReactDOMEventListener', () => {
|
||||
childNode.dispatchEvent(nativeEvent);
|
||||
|
||||
expect(mouseOut).toBeCalled();
|
||||
expect(mouseOut.mock.calls.length).toBe(2);
|
||||
expect(mouseOut).toHaveBeenCalledTimes(2);
|
||||
expect(mouseOut.mock.calls[0][0]).toEqual(childNode);
|
||||
expect(mouseOut.mock.calls[1][0]).toEqual(parentNode);
|
||||
|
||||
@@ -97,7 +97,7 @@ describe('ReactDOMEventListener', () => {
|
||||
childNode.dispatchEvent(nativeEvent);
|
||||
|
||||
expect(mouseOut).toBeCalled();
|
||||
expect(mouseOut.mock.calls.length).toBe(3);
|
||||
expect(mouseOut).toHaveBeenCalledTimes(3);
|
||||
expect(mouseOut.mock.calls[0][0]).toEqual(childNode);
|
||||
expect(mouseOut.mock.calls[1][0]).toEqual(parentNode);
|
||||
expect(mouseOut.mock.calls[2][0]).toEqual(grandParentNode);
|
||||
@@ -169,7 +169,7 @@ describe('ReactDOMEventListener', () => {
|
||||
childNode.dispatchEvent(nativeEvent);
|
||||
|
||||
// Child and parent should both call from event handlers.
|
||||
expect(mock.mock.calls.length).toBe(2);
|
||||
expect(mock).toHaveBeenCalledTimes(2);
|
||||
// The first call schedules a render of '1' into the 'Child'.
|
||||
// However, we're batching so it isn't flushed yet.
|
||||
expect(mock.mock.calls[0][0]).toBe('Child');
|
||||
@@ -213,7 +213,7 @@ describe('ReactDOMEventListener', () => {
|
||||
instance.getInner().dispatchEvent(nativeEvent);
|
||||
|
||||
expect(mouseOut).toBeCalled();
|
||||
expect(mouseOut.mock.calls.length).toBe(1);
|
||||
expect(mouseOut).toHaveBeenCalledTimes(1);
|
||||
expect(mouseOut.mock.calls[0][0]).toEqual(instance.getInner());
|
||||
document.body.removeChild(container);
|
||||
});
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
const React = require('react');
|
||||
const ReactDOM = require('react-dom');
|
||||
const ReactTestUtils = require('react-dom/test-utils');
|
||||
const PropTypes = require('prop-types');
|
||||
|
||||
describe('ReactDOMFiber', () => {
|
||||
@@ -171,6 +170,31 @@ describe('ReactDOMFiber', () => {
|
||||
expect(firstNode.tagName).toBe('DIV');
|
||||
});
|
||||
|
||||
it('renders an empty fragment', () => {
|
||||
const Div = () => <div />;
|
||||
const EmptyFragment = () => <React.Fragment />;
|
||||
const NonEmptyFragment = () => (
|
||||
<React.Fragment>
|
||||
<Div />
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
ReactDOM.render(<EmptyFragment />, container);
|
||||
expect(container.firstChild).toBe(null);
|
||||
|
||||
ReactDOM.render(<NonEmptyFragment />, container);
|
||||
expect(container.firstChild.tagName).toBe('DIV');
|
||||
|
||||
ReactDOM.render(<EmptyFragment />, container);
|
||||
expect(container.firstChild).toBe(null);
|
||||
|
||||
ReactDOM.render(<Div />, container);
|
||||
expect(container.firstChild.tagName).toBe('DIV');
|
||||
|
||||
ReactDOM.render(<EmptyFragment />, container);
|
||||
expect(container.firstChild).toBe(null);
|
||||
});
|
||||
|
||||
let svgEls, htmlEls, mathEls;
|
||||
const expectSVG = {ref: el => svgEls.push(el)};
|
||||
const expectHTML = {ref: el => htmlEls.push(el)};
|
||||
@@ -818,33 +842,39 @@ describe('ReactDOMFiber', () => {
|
||||
|
||||
it('should bubble events from the portal to the parent', () => {
|
||||
const portalContainer = document.createElement('div');
|
||||
document.body.appendChild(portalContainer);
|
||||
try {
|
||||
const ops = [];
|
||||
let portal = null;
|
||||
|
||||
const ops = [];
|
||||
let portal = null;
|
||||
ReactDOM.render(
|
||||
<div onClick={() => ops.push('parent clicked')}>
|
||||
{ReactDOM.createPortal(
|
||||
<div
|
||||
onClick={() => ops.push('portal clicked')}
|
||||
ref={n => (portal = n)}>
|
||||
portal
|
||||
</div>,
|
||||
portalContainer,
|
||||
)}
|
||||
</div>,
|
||||
container,
|
||||
);
|
||||
|
||||
ReactDOM.render(
|
||||
<div onClick={() => ops.push('parent clicked')}>
|
||||
{ReactDOM.createPortal(
|
||||
<div
|
||||
onClick={() => ops.push('portal clicked')}
|
||||
ref={n => (portal = n)}>
|
||||
portal
|
||||
</div>,
|
||||
portalContainer,
|
||||
)}
|
||||
</div>,
|
||||
container,
|
||||
);
|
||||
expect(portal.tagName).toBe('DIV');
|
||||
|
||||
expect(portal.tagName).toBe('DIV');
|
||||
portal.click();
|
||||
|
||||
ReactTestUtils.Simulate.click(portal);
|
||||
|
||||
expect(ops).toEqual(['portal clicked', 'parent clicked']);
|
||||
expect(ops).toEqual(['portal clicked', 'parent clicked']);
|
||||
} finally {
|
||||
document.body.removeChild(portalContainer);
|
||||
}
|
||||
});
|
||||
|
||||
it('should not onMouseLeave when staying in the portal', () => {
|
||||
const portalContainer = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
document.body.appendChild(portalContainer);
|
||||
|
||||
let ops = [];
|
||||
let firstTarget = null;
|
||||
@@ -853,56 +883,69 @@ describe('ReactDOMFiber', () => {
|
||||
|
||||
function simulateMouseMove(from, to) {
|
||||
if (from) {
|
||||
ReactTestUtils.SimulateNative.mouseOut(from, {
|
||||
relatedTarget: to,
|
||||
});
|
||||
from.dispatchEvent(
|
||||
new MouseEvent('mouseout', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
relatedTarget: to,
|
||||
}),
|
||||
);
|
||||
}
|
||||
if (to) {
|
||||
ReactTestUtils.SimulateNative.mouseOver(to, {
|
||||
relatedTarget: from,
|
||||
});
|
||||
to.dispatchEvent(
|
||||
new MouseEvent('mouseover', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
relatedTarget: from,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
<div
|
||||
onMouseEnter={() => ops.push('enter parent')}
|
||||
onMouseLeave={() => ops.push('leave parent')}>
|
||||
<div ref={n => (firstTarget = n)} />
|
||||
{ReactDOM.createPortal(
|
||||
<div
|
||||
onMouseEnter={() => ops.push('enter portal')}
|
||||
onMouseLeave={() => ops.push('leave portal')}
|
||||
ref={n => (secondTarget = n)}>
|
||||
portal
|
||||
</div>,
|
||||
portalContainer,
|
||||
)}
|
||||
</div>
|
||||
<div ref={n => (thirdTarget = n)} />
|
||||
</div>,
|
||||
container,
|
||||
);
|
||||
try {
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
<div
|
||||
onMouseEnter={() => ops.push('enter parent')}
|
||||
onMouseLeave={() => ops.push('leave parent')}>
|
||||
<div ref={n => (firstTarget = n)} />
|
||||
{ReactDOM.createPortal(
|
||||
<div
|
||||
onMouseEnter={() => ops.push('enter portal')}
|
||||
onMouseLeave={() => ops.push('leave portal')}
|
||||
ref={n => (secondTarget = n)}>
|
||||
portal
|
||||
</div>,
|
||||
portalContainer,
|
||||
)}
|
||||
</div>
|
||||
<div ref={n => (thirdTarget = n)} />
|
||||
</div>,
|
||||
container,
|
||||
);
|
||||
|
||||
simulateMouseMove(null, firstTarget);
|
||||
expect(ops).toEqual(['enter parent']);
|
||||
simulateMouseMove(null, firstTarget);
|
||||
expect(ops).toEqual(['enter parent']);
|
||||
|
||||
ops = [];
|
||||
ops = [];
|
||||
|
||||
simulateMouseMove(firstTarget, secondTarget);
|
||||
expect(ops).toEqual([
|
||||
// Parent did not invoke leave because we're still inside the portal.
|
||||
'enter portal',
|
||||
]);
|
||||
simulateMouseMove(firstTarget, secondTarget);
|
||||
expect(ops).toEqual([
|
||||
// Parent did not invoke leave because we're still inside the portal.
|
||||
'enter portal',
|
||||
]);
|
||||
|
||||
ops = [];
|
||||
ops = [];
|
||||
|
||||
simulateMouseMove(secondTarget, thirdTarget);
|
||||
expect(ops).toEqual([
|
||||
'leave portal',
|
||||
'leave parent', // Only when we leave the portal does onMouseLeave fire.
|
||||
]);
|
||||
simulateMouseMove(secondTarget, thirdTarget);
|
||||
expect(ops).toEqual([
|
||||
'leave portal',
|
||||
'leave parent', // Only when we leave the portal does onMouseLeave fire.
|
||||
]);
|
||||
} finally {
|
||||
document.body.removeChild(container);
|
||||
document.body.removeChild(portalContainer);
|
||||
}
|
||||
});
|
||||
|
||||
it('should throw on bad createPortal argument', () => {
|
||||
@@ -942,81 +985,82 @@ describe('ReactDOMFiber', () => {
|
||||
});
|
||||
|
||||
it('should not update event handlers until commit', () => {
|
||||
let ops = [];
|
||||
const handlerA = () => ops.push('A');
|
||||
const handlerB = () => ops.push('B');
|
||||
document.body.appendChild(container);
|
||||
try {
|
||||
let ops = [];
|
||||
const handlerA = () => ops.push('A');
|
||||
const handlerB = () => ops.push('B');
|
||||
|
||||
class Example extends React.Component {
|
||||
state = {flip: false, count: 0};
|
||||
flip() {
|
||||
this.setState({flip: true, count: this.state.count + 1});
|
||||
class Example extends React.Component {
|
||||
state = {flip: false, count: 0};
|
||||
flip() {
|
||||
this.setState({flip: true, count: this.state.count + 1});
|
||||
}
|
||||
tick() {
|
||||
this.setState({count: this.state.count + 1});
|
||||
}
|
||||
render() {
|
||||
const useB = !this.props.forceA && this.state.flip;
|
||||
return <div onClick={useB ? handlerB : handlerA} />;
|
||||
}
|
||||
}
|
||||
tick() {
|
||||
this.setState({count: this.state.count + 1});
|
||||
}
|
||||
render() {
|
||||
const useB = !this.props.forceA && this.state.flip;
|
||||
return <div onClick={useB ? handlerB : handlerA} />;
|
||||
|
||||
class Click extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
node.click();
|
||||
}
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
let inst;
|
||||
ReactDOM.render([<Example key="a" ref={n => (inst = n)} />], container);
|
||||
const node = container.firstChild;
|
||||
expect(node.tagName).toEqual('DIV');
|
||||
|
||||
node.click();
|
||||
|
||||
expect(ops).toEqual(['A']);
|
||||
ops = [];
|
||||
|
||||
// Render with the other event handler.
|
||||
inst.flip();
|
||||
|
||||
node.click();
|
||||
|
||||
expect(ops).toEqual(['B']);
|
||||
ops = [];
|
||||
|
||||
// Rerender without changing any props.
|
||||
inst.tick();
|
||||
|
||||
node.click();
|
||||
|
||||
expect(ops).toEqual(['B']);
|
||||
ops = [];
|
||||
|
||||
// Render a flip back to the A handler. The second component invokes the
|
||||
// click handler during render to simulate a click during an aborted
|
||||
// render. I use this hack because at current time we don't have a way to
|
||||
// test aborted ReactDOM renders.
|
||||
ReactDOM.render(
|
||||
[<Example key="a" forceA={true} />, <Click key="b" />],
|
||||
container,
|
||||
);
|
||||
|
||||
// Because the new click handler has not yet committed, we should still
|
||||
// invoke B.
|
||||
expect(ops).toEqual(['B']);
|
||||
ops = [];
|
||||
|
||||
// Any click that happens after commit, should invoke A.
|
||||
node.click();
|
||||
expect(ops).toEqual(['A']);
|
||||
} finally {
|
||||
document.body.removeChild(container);
|
||||
}
|
||||
|
||||
class Click extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
click(node);
|
||||
}
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
let inst;
|
||||
ReactDOM.render([<Example key="a" ref={n => (inst = n)} />], container);
|
||||
const node = container.firstChild;
|
||||
expect(node.tagName).toEqual('DIV');
|
||||
|
||||
function click(target) {
|
||||
ReactTestUtils.Simulate.click(target);
|
||||
}
|
||||
|
||||
click(node);
|
||||
|
||||
expect(ops).toEqual(['A']);
|
||||
ops = [];
|
||||
|
||||
// Render with the other event handler.
|
||||
inst.flip();
|
||||
|
||||
click(node);
|
||||
|
||||
expect(ops).toEqual(['B']);
|
||||
ops = [];
|
||||
|
||||
// Rerender without changing any props.
|
||||
inst.tick();
|
||||
|
||||
click(node);
|
||||
|
||||
expect(ops).toEqual(['B']);
|
||||
ops = [];
|
||||
|
||||
// Render a flip back to the A handler. The second component invokes the
|
||||
// click handler during render to simulate a click during an aborted
|
||||
// render. I use this hack because at current time we don't have a way to
|
||||
// test aborted ReactDOM renders.
|
||||
ReactDOM.render(
|
||||
[<Example key="a" forceA={true} />, <Click key="b" />],
|
||||
container,
|
||||
);
|
||||
|
||||
// Because the new click handler has not yet committed, we should still
|
||||
// invoke B.
|
||||
expect(ops).toEqual(['B']);
|
||||
ops = [];
|
||||
|
||||
// Any click that happens after commit, should invoke A.
|
||||
click(node);
|
||||
expect(ops).toEqual(['A']);
|
||||
});
|
||||
|
||||
it('should not crash encountering low-priority tree', () => {
|
||||
|
||||
@@ -11,24 +11,22 @@
|
||||
|
||||
describe('ReactDOMIframe', () => {
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactTestUtils;
|
||||
|
||||
beforeEach(() => {
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactTestUtils = require('react-dom/test-utils');
|
||||
});
|
||||
|
||||
it('should trigger load events', () => {
|
||||
const onLoadSpy = jasmine.createSpy();
|
||||
const onLoadSpy = jest.fn();
|
||||
let iframe = React.createElement('iframe', {onLoad: onLoadSpy});
|
||||
iframe = ReactTestUtils.renderIntoDocument(iframe);
|
||||
|
||||
const loadEvent = document.createEvent('Event');
|
||||
loadEvent.initEvent('load', false, false);
|
||||
|
||||
ReactDOM.findDOMNode(iframe).dispatchEvent(loadEvent);
|
||||
iframe.dispatchEvent(loadEvent);
|
||||
|
||||
expect(onLoadSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
const emptyFunction = require('fbjs/lib/emptyFunction');
|
||||
function emptyFunction() {}
|
||||
|
||||
describe('ReactDOMInput', () => {
|
||||
let React;
|
||||
@@ -38,27 +38,27 @@ describe('ReactDOMInput', () => {
|
||||
|
||||
it('should properly control a value even if no event listener exists', () => {
|
||||
const container = document.createElement('div');
|
||||
let stub;
|
||||
let node;
|
||||
|
||||
expect(() => {
|
||||
stub = ReactDOM.render(<input type="text" value="lion" />, container);
|
||||
node = ReactDOM.render(<input type="text" value="lion" />, container);
|
||||
}).toWarnDev(
|
||||
'Failed prop type: You provided a `value` prop to a form field without an `onChange` handler.',
|
||||
);
|
||||
|
||||
document.body.appendChild(container);
|
||||
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
try {
|
||||
setUntrackedValue.call(node, 'giraffe');
|
||||
|
||||
setUntrackedValue.call(node, 'giraffe');
|
||||
// This must use the native event dispatching. If we simulate, we will
|
||||
// bypass the lazy event attachment system so we won't actually test this.
|
||||
dispatchEventOnNode(node, 'change');
|
||||
|
||||
// This must use the native event dispatching. If we simulate, we will
|
||||
// bypass the lazy event attachment system so we won't actually test this.
|
||||
dispatchEventOnNode(node, 'change');
|
||||
|
||||
expect(node.value).toBe('lion');
|
||||
|
||||
document.body.removeChild(container);
|
||||
expect(node.value).toBe('lion');
|
||||
} finally {
|
||||
document.body.removeChild(container);
|
||||
}
|
||||
});
|
||||
|
||||
it('should control a value in reentrant events', () => {
|
||||
@@ -169,26 +169,24 @@ describe('ReactDOMInput', () => {
|
||||
|
||||
describe('switching text inputs between numeric and string numbers', () => {
|
||||
it('does change the number 2 to "2.0" with no change handler', () => {
|
||||
let stub = <input type="text" value={2} onChange={jest.fn()} />;
|
||||
stub = ReactTestUtils.renderIntoDocument(stub);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const stub = <input type="text" value={2} onChange={jest.fn()} />;
|
||||
const node = ReactTestUtils.renderIntoDocument(stub);
|
||||
|
||||
node.value = '2.0';
|
||||
|
||||
ReactTestUtils.Simulate.change(stub);
|
||||
ReactTestUtils.Simulate.change(node);
|
||||
|
||||
expect(node.getAttribute('value')).toBe('2');
|
||||
expect(node.value).toBe('2');
|
||||
});
|
||||
|
||||
it('does change the string "2" to "2.0" with no change handler', () => {
|
||||
let stub = <input type="text" value={'2'} onChange={jest.fn()} />;
|
||||
stub = ReactTestUtils.renderIntoDocument(stub);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const stub = <input type="text" value={'2'} onChange={jest.fn()} />;
|
||||
const node = ReactTestUtils.renderIntoDocument(stub);
|
||||
|
||||
node.value = '2.0';
|
||||
|
||||
ReactTestUtils.Simulate.change(stub);
|
||||
ReactTestUtils.Simulate.change(node);
|
||||
|
||||
expect(node.getAttribute('value')).toBe('2');
|
||||
expect(node.value).toBe('2');
|
||||
@@ -315,8 +313,7 @@ describe('ReactDOMInput', () => {
|
||||
|
||||
it('should display `defaultValue` of number 0', () => {
|
||||
let stub = <input type="text" defaultValue={0} />;
|
||||
stub = ReactTestUtils.renderIntoDocument(stub);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const node = ReactTestUtils.renderIntoDocument(stub);
|
||||
|
||||
expect(node.getAttribute('value')).toBe('0');
|
||||
expect(node.value).toBe('0');
|
||||
@@ -348,16 +345,14 @@ describe('ReactDOMInput', () => {
|
||||
|
||||
it('should display "true" for `defaultValue` of `true`', () => {
|
||||
let stub = <input type="text" defaultValue={true} />;
|
||||
stub = ReactTestUtils.renderIntoDocument(stub);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const node = ReactTestUtils.renderIntoDocument(stub);
|
||||
|
||||
expect(node.value).toBe('true');
|
||||
});
|
||||
|
||||
it('should display "false" for `defaultValue` of `false`', () => {
|
||||
let stub = <input type="text" defaultValue={false} />;
|
||||
stub = ReactTestUtils.renderIntoDocument(stub);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const node = ReactTestUtils.renderIntoDocument(stub);
|
||||
|
||||
expect(node.value).toBe('false');
|
||||
});
|
||||
@@ -467,17 +462,15 @@ describe('ReactDOMInput', () => {
|
||||
},
|
||||
};
|
||||
|
||||
let stub = <input type="text" defaultValue={objToString} />;
|
||||
stub = ReactTestUtils.renderIntoDocument(stub);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const stub = <input type="text" defaultValue={objToString} />;
|
||||
const node = ReactTestUtils.renderIntoDocument(stub);
|
||||
|
||||
expect(node.value).toBe('foobar');
|
||||
});
|
||||
|
||||
it('should display `value` of number 0', () => {
|
||||
let stub = <input type="text" value={0} />;
|
||||
stub = ReactTestUtils.renderIntoDocument(stub);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const stub = <input type="text" value={0} />;
|
||||
const node = ReactTestUtils.renderIntoDocument(stub);
|
||||
|
||||
expect(node.value).toBe('0');
|
||||
});
|
||||
@@ -535,7 +528,7 @@ describe('ReactDOMInput', () => {
|
||||
|
||||
const node = container.firstChild;
|
||||
let nodeValue = 'a';
|
||||
const nodeValueSetter = jest.genMockFn();
|
||||
const nodeValueSetter = jest.fn();
|
||||
Object.defineProperty(node, 'value', {
|
||||
get: function() {
|
||||
return nodeValue;
|
||||
@@ -546,10 +539,10 @@ describe('ReactDOMInput', () => {
|
||||
});
|
||||
|
||||
ReactDOM.render(<input value="a" onChange={() => {}} />, container);
|
||||
expect(nodeValueSetter.mock.calls.length).toBe(0);
|
||||
expect(nodeValueSetter).toHaveBeenCalledTimes(0);
|
||||
|
||||
ReactDOM.render(<input value="b" onChange={() => {}} />, container);
|
||||
expect(nodeValueSetter.mock.calls.length).toBe(1);
|
||||
expect(nodeValueSetter).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should not incur unnecessary DOM mutations for numeric type conversion', () => {
|
||||
@@ -558,7 +551,7 @@ describe('ReactDOMInput', () => {
|
||||
|
||||
const node = container.firstChild;
|
||||
let nodeValue = '0';
|
||||
const nodeValueSetter = jest.genMockFn();
|
||||
const nodeValueSetter = jest.fn();
|
||||
Object.defineProperty(node, 'value', {
|
||||
get: function() {
|
||||
return nodeValue;
|
||||
@@ -569,7 +562,7 @@ describe('ReactDOMInput', () => {
|
||||
});
|
||||
|
||||
ReactDOM.render(<input value={0} onChange={() => {}} />, container);
|
||||
expect(nodeValueSetter.mock.calls.length).toBe(0);
|
||||
expect(nodeValueSetter).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('should not incur unnecessary DOM mutations for the boolean type conversion', () => {
|
||||
@@ -578,7 +571,7 @@ describe('ReactDOMInput', () => {
|
||||
|
||||
const node = container.firstChild;
|
||||
let nodeValue = 'true';
|
||||
const nodeValueSetter = jest.genMockFn();
|
||||
const nodeValueSetter = jest.fn();
|
||||
Object.defineProperty(node, 'value', {
|
||||
get: function() {
|
||||
return nodeValue;
|
||||
@@ -589,13 +582,12 @@ describe('ReactDOMInput', () => {
|
||||
});
|
||||
|
||||
ReactDOM.render(<input value={true} onChange={() => {}} />, container);
|
||||
expect(nodeValueSetter.mock.calls.length).toBe(0);
|
||||
expect(nodeValueSetter).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('should properly control a value of number `0`', () => {
|
||||
let stub = <input type="text" value={0} onChange={emptyFunction} />;
|
||||
stub = ReactTestUtils.renderIntoDocument(stub);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const stub = <input type="text" value={0} onChange={emptyFunction} />;
|
||||
const node = ReactTestUtils.renderIntoDocument(stub);
|
||||
|
||||
node.value = 'giraffe';
|
||||
ReactTestUtils.Simulate.change(node);
|
||||
@@ -603,9 +595,8 @@ describe('ReactDOMInput', () => {
|
||||
});
|
||||
|
||||
it('should properly control 0.0 for a text input', () => {
|
||||
let stub = <input type="text" value={0} onChange={emptyFunction} />;
|
||||
stub = ReactTestUtils.renderIntoDocument(stub);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const stub = <input type="text" value={0} onChange={emptyFunction} />;
|
||||
const node = ReactTestUtils.renderIntoDocument(stub);
|
||||
|
||||
node.value = '0.0';
|
||||
ReactTestUtils.Simulate.change(node, {target: {value: '0.0'}});
|
||||
@@ -613,9 +604,8 @@ describe('ReactDOMInput', () => {
|
||||
});
|
||||
|
||||
it('should properly control 0.0 for a number input', () => {
|
||||
let stub = <input type="number" value={0} onChange={emptyFunction} />;
|
||||
stub = ReactTestUtils.renderIntoDocument(stub);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const stub = <input type="number" value={0} onChange={emptyFunction} />;
|
||||
const node = ReactTestUtils.renderIntoDocument(stub);
|
||||
|
||||
node.value = '0.0';
|
||||
ReactTestUtils.Simulate.change(node, {target: {value: '0.0'}});
|
||||
@@ -702,9 +692,8 @@ describe('ReactDOMInput', () => {
|
||||
});
|
||||
|
||||
it('should not set a value for submit buttons unnecessarily', () => {
|
||||
let stub = <input type="submit" />;
|
||||
stub = ReactTestUtils.renderIntoDocument(stub);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const stub = <input type="submit" />;
|
||||
const node = ReactTestUtils.renderIntoDocument(stub);
|
||||
|
||||
// The value shouldn't be '', or else the button will have no text; it
|
||||
// should have the default "Submit" or "Submit Query" label. Most browsers
|
||||
@@ -1460,10 +1449,9 @@ describe('ReactDOMInput', () => {
|
||||
});
|
||||
|
||||
it('an uncontrolled number input will not update the value attribute on blur', () => {
|
||||
const stub = ReactTestUtils.renderIntoDocument(
|
||||
const node = ReactTestUtils.renderIntoDocument(
|
||||
<input type="number" defaultValue="1" />,
|
||||
);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
|
||||
node.value = 4;
|
||||
|
||||
@@ -1473,10 +1461,9 @@ describe('ReactDOMInput', () => {
|
||||
});
|
||||
|
||||
it('an uncontrolled text input will not update the value attribute on blur', () => {
|
||||
const stub = ReactTestUtils.renderIntoDocument(
|
||||
const node = ReactTestUtils.renderIntoDocument(
|
||||
<input type="text" defaultValue="1" />,
|
||||
);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
|
||||
node.value = 4;
|
||||
|
||||
|
||||
@@ -26,8 +26,7 @@ describe('ReactDOMOption', () => {
|
||||
{1} {'foo'}
|
||||
</option>
|
||||
);
|
||||
stub = ReactTestUtils.renderIntoDocument(stub);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const node = ReactTestUtils.renderIntoDocument(stub);
|
||||
|
||||
expect(node.innerHTML).toBe('1 foo');
|
||||
});
|
||||
@@ -59,17 +58,15 @@ describe('ReactDOMOption', () => {
|
||||
{undefined} {2}
|
||||
</option>
|
||||
);
|
||||
stub = ReactTestUtils.renderIntoDocument(stub);
|
||||
const node = ReactTestUtils.renderIntoDocument(stub);
|
||||
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
expect(node.innerHTML).toBe('1 2');
|
||||
});
|
||||
|
||||
it('should be able to use dangerouslySetInnerHTML on option', () => {
|
||||
let stub = <option dangerouslySetInnerHTML={{__html: 'foobar'}} />;
|
||||
stub = ReactTestUtils.renderIntoDocument(stub);
|
||||
const node = ReactTestUtils.renderIntoDocument(stub);
|
||||
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
expect(node.innerHTML).toBe('foobar');
|
||||
});
|
||||
|
||||
@@ -95,8 +92,7 @@ describe('ReactDOMOption', () => {
|
||||
);
|
||||
const options = stub.props.children;
|
||||
const container = document.createElement('div');
|
||||
stub = ReactDOM.render(stub, container);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const node = ReactDOM.render(stub, container);
|
||||
|
||||
expect(node.selectedIndex).toBe(1);
|
||||
|
||||
|
||||
@@ -35,8 +35,7 @@ describe('ReactDOMSelect', () => {
|
||||
);
|
||||
const options = stub.props.children;
|
||||
const container = document.createElement('div');
|
||||
stub = ReactDOM.render(stub, container);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const node = ReactDOM.render(stub, container);
|
||||
|
||||
expect(node.value).toBe('giraffe');
|
||||
|
||||
@@ -65,8 +64,7 @@ describe('ReactDOMSelect', () => {
|
||||
</select>
|
||||
);
|
||||
const container = document.createElement('div');
|
||||
const stub = ReactDOM.render(el, container);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const node = ReactDOM.render(el, container);
|
||||
|
||||
expect(node.value).toBe('giraffe');
|
||||
|
||||
@@ -86,8 +84,7 @@ describe('ReactDOMSelect', () => {
|
||||
);
|
||||
const options = stub.props.children;
|
||||
const container = document.createElement('div');
|
||||
stub = ReactDOM.render(stub, container);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const node = ReactDOM.render(stub, container);
|
||||
|
||||
expect(node.options[0].selected).toBe(false); // monkey
|
||||
expect(node.options[1].selected).toBe(true); // giraffe
|
||||
@@ -116,8 +113,7 @@ describe('ReactDOMSelect', () => {
|
||||
);
|
||||
const options = stub.props.children;
|
||||
const container = document.createElement('div');
|
||||
stub = ReactDOM.render(stub, container);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const node = ReactDOM.render(stub, container);
|
||||
|
||||
expect(node.value).toBe('giraffe');
|
||||
|
||||
@@ -141,8 +137,7 @@ describe('ReactDOMSelect', () => {
|
||||
</select>
|
||||
);
|
||||
const container = document.createElement('div');
|
||||
stub = ReactDOM.render(stub, container);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const node = ReactDOM.render(stub, container);
|
||||
expect(node.options[0].selected).toBe(false);
|
||||
expect(node.options[2].selected).toBe(true);
|
||||
});
|
||||
@@ -157,8 +152,7 @@ describe('ReactDOMSelect', () => {
|
||||
);
|
||||
const options = stub.props.children;
|
||||
const container = document.createElement('div');
|
||||
stub = ReactDOM.render(stub, container);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const node = ReactDOM.render(stub, container);
|
||||
|
||||
expect(node.value).toBe('__proto__');
|
||||
|
||||
@@ -190,8 +184,7 @@ describe('ReactDOMSelect', () => {
|
||||
);
|
||||
const options = stub.props.children;
|
||||
const container = document.createElement('div');
|
||||
stub = ReactDOM.render(stub, container);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const node = ReactDOM.render(stub, container);
|
||||
|
||||
expect(node.options[0].selected).toBe(false); // monkey
|
||||
expect(node.options[1].selected).toBe(true); // giraffe
|
||||
@@ -220,8 +213,7 @@ describe('ReactDOMSelect', () => {
|
||||
);
|
||||
const options = stub.props.children;
|
||||
const container = document.createElement('div');
|
||||
stub = ReactDOM.render(stub, container);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const node = ReactDOM.render(stub, container);
|
||||
|
||||
expect(node.options[0].selected).toBe(false); // monkey
|
||||
expect(node.options[1].selected).toBe(true); // __proto__
|
||||
@@ -248,8 +240,7 @@ describe('ReactDOMSelect', () => {
|
||||
<option value="12">twelve</option>
|
||||
</select>
|
||||
);
|
||||
stub = ReactTestUtils.renderIntoDocument(stub);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const node = ReactTestUtils.renderIntoDocument(stub);
|
||||
|
||||
expect(node.options[0].selected).toBe(false); // one
|
||||
expect(node.options[1].selected).toBe(false); // two
|
||||
@@ -259,7 +250,7 @@ describe('ReactDOMSelect', () => {
|
||||
it('should reset child options selected when they are changed and `value` is set', () => {
|
||||
let stub = <select multiple={true} value={['a', 'b']} onChange={noop} />;
|
||||
const container = document.createElement('div');
|
||||
stub = ReactDOM.render(stub, container);
|
||||
const node = ReactDOM.render(stub, container);
|
||||
|
||||
ReactDOM.render(
|
||||
<select multiple={true} value={['a', 'b']} onChange={noop}>
|
||||
@@ -270,8 +261,6 @@ describe('ReactDOMSelect', () => {
|
||||
container,
|
||||
);
|
||||
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
|
||||
expect(node.options[0].selected).toBe(true); // a
|
||||
expect(node.options[1].selected).toBe(true); // b
|
||||
expect(node.options[2].selected).toBe(false); // c
|
||||
@@ -293,8 +282,7 @@ describe('ReactDOMSelect', () => {
|
||||
</select>
|
||||
);
|
||||
const container = document.createElement('div');
|
||||
const stub = ReactDOM.render(el, container);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const node = ReactDOM.render(el, container);
|
||||
|
||||
expect(node.options[0].selected).toBe(false); // monkey
|
||||
expect(node.options[1].selected).toBe(true); // giraffe
|
||||
@@ -327,8 +315,7 @@ describe('ReactDOMSelect', () => {
|
||||
);
|
||||
const options = stub.props.children;
|
||||
const container = document.createElement('div');
|
||||
stub = ReactDOM.render(stub, container);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const node = ReactDOM.render(stub, container);
|
||||
|
||||
expect(node.options[0].selected).toBe(false); // monkey
|
||||
expect(node.options[1].selected).toBe(true); // giraffe
|
||||
@@ -357,8 +344,7 @@ describe('ReactDOMSelect', () => {
|
||||
);
|
||||
const options = stub.props.children;
|
||||
const container = document.createElement('div');
|
||||
stub = ReactDOM.render(stub, container);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const node = ReactDOM.render(stub, container);
|
||||
|
||||
expect(node.options[0].selected).toBe(false); // monkey
|
||||
expect(node.options[1].selected).toBe(true); // giraffe
|
||||
@@ -386,8 +372,7 @@ describe('ReactDOMSelect', () => {
|
||||
);
|
||||
const options = stub.props.children;
|
||||
const container = document.createElement('div');
|
||||
stub = ReactDOM.render(stub, container);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const node = ReactDOM.render(stub, container);
|
||||
|
||||
expect(node.options[0].selected).toBe(false); // monkey
|
||||
expect(node.options[1].selected).toBe(true); // giraffe
|
||||
@@ -410,8 +395,7 @@ describe('ReactDOMSelect', () => {
|
||||
);
|
||||
const options = stub.props.children;
|
||||
const container = document.createElement('div');
|
||||
stub = ReactDOM.render(stub, container);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const node = ReactDOM.render(stub, container);
|
||||
|
||||
ReactDOM.render(
|
||||
<select value="gorilla" onChange={noop}>
|
||||
@@ -459,6 +443,37 @@ describe('ReactDOMSelect', () => {
|
||||
expect(markup).not.toContain('<option selected="" value="gorilla"');
|
||||
});
|
||||
|
||||
it('should support server-side rendering with dangerouslySetInnerHTML', () => {
|
||||
const stub = (
|
||||
<select defaultValue="giraffe">
|
||||
<option
|
||||
value="monkey"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: 'A monkey!',
|
||||
}}>
|
||||
{undefined}
|
||||
</option>
|
||||
<option
|
||||
value="giraffe"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: 'A giraffe!',
|
||||
}}>
|
||||
{null}
|
||||
</option>
|
||||
<option
|
||||
value="gorilla"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: 'A gorilla!',
|
||||
}}
|
||||
/>
|
||||
</select>
|
||||
);
|
||||
const markup = ReactDOMServer.renderToString(stub);
|
||||
expect(markup).toContain('<option selected="" value="giraffe"');
|
||||
expect(markup).not.toContain('<option selected="" value="monkey"');
|
||||
expect(markup).not.toContain('<option selected="" value="gorilla"');
|
||||
});
|
||||
|
||||
it('should support server-side rendering with multiple', () => {
|
||||
const stub = (
|
||||
<select multiple={true} value={['giraffe', 'gorilla']} onChange={noop}>
|
||||
@@ -476,7 +491,7 @@ describe('ReactDOMSelect', () => {
|
||||
it('should not control defaultValue if readding options', () => {
|
||||
const container = document.createElement('div');
|
||||
|
||||
const select = ReactDOM.render(
|
||||
const node = ReactDOM.render(
|
||||
<select multiple={true} defaultValue={['giraffe']}>
|
||||
<option key="monkey" value="monkey">
|
||||
A monkey!
|
||||
@@ -490,7 +505,6 @@ describe('ReactDOMSelect', () => {
|
||||
</select>,
|
||||
container,
|
||||
);
|
||||
const node = ReactDOM.findDOMNode(select);
|
||||
|
||||
expect(node.options[0].selected).toBe(false); // monkey
|
||||
expect(node.options[1].selected).toBe(true); // giraffe
|
||||
@@ -601,8 +615,7 @@ describe('ReactDOMSelect', () => {
|
||||
<option value="gorilla">A gorilla!</option>
|
||||
</select>
|
||||
);
|
||||
stub = ReactTestUtils.renderIntoDocument(stub);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const node = ReactTestUtils.renderIntoDocument(stub);
|
||||
|
||||
ReactTestUtils.Simulate.change(node);
|
||||
|
||||
@@ -648,8 +661,7 @@ describe('ReactDOMSelect', () => {
|
||||
<option value="gorilla">A gorilla!</option>
|
||||
</select>
|
||||
);
|
||||
stub = ReactDOM.render(stub, container);
|
||||
const node = ReactDOM.findDOMNode(stub);
|
||||
const node = ReactDOM.render(stub, container);
|
||||
|
||||
expect(() => ReactTestUtils.Simulate.change(node)).not.toThrow();
|
||||
});
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactDOMSelection;
|
||||
let invariant;
|
||||
|
||||
let getModernOffsetsFromPoints;
|
||||
|
||||
@@ -21,7 +20,6 @@ describe('ReactDOMSelection', () => {
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactDOMSelection = require('../client/ReactDOMSelection');
|
||||
invariant = require('fbjs/lib/invariant');
|
||||
|
||||
({getModernOffsetsFromPoints} = ReactDOMSelection);
|
||||
});
|
||||
@@ -68,10 +66,9 @@ describe('ReactDOMSelection', () => {
|
||||
}
|
||||
traverse(outerNode);
|
||||
|
||||
invariant(
|
||||
start !== null && end !== null,
|
||||
'Provided anchor/focus nodes were outside of root.',
|
||||
);
|
||||
if (start === null || end === null) {
|
||||
throw new Error('Provided anchor/focus nodes were outside of root.');
|
||||
}
|
||||
return {start, end};
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ const {
|
||||
resetModules,
|
||||
itRenders,
|
||||
itClientRenders,
|
||||
itThrowsWhenRendering,
|
||||
renderIntoDom,
|
||||
serverRender,
|
||||
} = ReactDOMServerIntegrationUtils(initModules);
|
||||
@@ -327,6 +328,81 @@ describe('ReactDOMServerIntegration', () => {
|
||||
expectSelectValue(e, 'bar');
|
||||
});
|
||||
|
||||
itRenders(
|
||||
'a select with options that use dangerouslySetInnerHTML',
|
||||
async render => {
|
||||
const e = await render(
|
||||
<select defaultValue="baz" value="bar" readOnly={true}>
|
||||
<option
|
||||
id="foo"
|
||||
value="foo"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: 'Foo',
|
||||
}}>
|
||||
{undefined}
|
||||
</option>
|
||||
<option
|
||||
id="bar"
|
||||
value="bar"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: 'Bar',
|
||||
}}>
|
||||
{null}
|
||||
</option>
|
||||
<option
|
||||
id="baz"
|
||||
value="baz"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: 'Baz',
|
||||
}}
|
||||
/>
|
||||
</select>,
|
||||
1,
|
||||
);
|
||||
expectSelectValue(e, 'bar');
|
||||
},
|
||||
);
|
||||
|
||||
itThrowsWhenRendering(
|
||||
'a select with option that uses dangerouslySetInnerHTML and 0 as child',
|
||||
async render => {
|
||||
await render(
|
||||
<select defaultValue="baz" value="foo" readOnly={true}>
|
||||
<option
|
||||
id="foo"
|
||||
value="foo"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: 'Foo',
|
||||
}}>
|
||||
{0}
|
||||
</option>
|
||||
</select>,
|
||||
1,
|
||||
);
|
||||
},
|
||||
'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
|
||||
);
|
||||
|
||||
itThrowsWhenRendering(
|
||||
'a select with option that uses dangerouslySetInnerHTML and empty string as child',
|
||||
async render => {
|
||||
await render(
|
||||
<select defaultValue="baz" value="foo" readOnly={true}>
|
||||
<option
|
||||
id="foo"
|
||||
value="foo"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: 'Foo',
|
||||
}}>
|
||||
{''}
|
||||
</option>
|
||||
</select>,
|
||||
1,
|
||||
);
|
||||
},
|
||||
'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
|
||||
);
|
||||
|
||||
itRenders(
|
||||
'a select value overriding defaultValue no matter the prop order',
|
||||
async render => {
|
||||
@@ -348,9 +424,13 @@ describe('ReactDOMServerIntegration', () => {
|
||||
ControlledSelect;
|
||||
beforeEach(() => {
|
||||
ControlledInput = class extends React.Component {
|
||||
static defaultProps = {
|
||||
type: 'text',
|
||||
initialValue: 'Hello',
|
||||
};
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {value: 'Hello'};
|
||||
super(...arguments);
|
||||
this.state = {value: this.props.initialValue};
|
||||
}
|
||||
handleChange(event) {
|
||||
if (this.props.onChange) {
|
||||
@@ -361,6 +441,7 @@ describe('ReactDOMServerIntegration', () => {
|
||||
render() {
|
||||
return (
|
||||
<input
|
||||
type={this.props.type}
|
||||
value={this.state.value}
|
||||
onChange={this.handleChange.bind(this)}
|
||||
/>
|
||||
@@ -551,6 +632,27 @@ describe('ReactDOMServerIntegration', () => {
|
||||
expect(changeCount).toBe(0);
|
||||
});
|
||||
|
||||
it('should not blow away user-interaction on successful reconnect to an uncontrolled range input', () =>
|
||||
testUserInteractionBeforeClientRender(
|
||||
<input type="text" defaultValue="0.5" />,
|
||||
'0.5',
|
||||
'1',
|
||||
));
|
||||
|
||||
it('should not blow away user-interaction on successful reconnect to a controlled range input', async () => {
|
||||
let changeCount = 0;
|
||||
await testUserInteractionBeforeClientRender(
|
||||
<ControlledInput
|
||||
type="range"
|
||||
initialValue="0.25"
|
||||
onChange={() => changeCount++}
|
||||
/>,
|
||||
'0.25',
|
||||
'1',
|
||||
);
|
||||
expect(changeCount).toBe(0);
|
||||
});
|
||||
|
||||
it('should not blow away user-entered text on successful reconnect to an uncontrolled checkbox', () =>
|
||||
testUserInteractionBeforeClientRender(
|
||||
<input type="checkbox" defaultChecked={true} />,
|
||||
|
||||
@@ -191,5 +191,63 @@ describe('ReactDOMServerIntegration', () => {
|
||||
expect(e.querySelector('#theme').textContent).toBe('light');
|
||||
expect(e.querySelector('#language').textContent).toBe('english');
|
||||
});
|
||||
|
||||
itRenders('nested context unwinding', async render => {
|
||||
const Theme = React.createContext('dark');
|
||||
const Language = React.createContext('french');
|
||||
|
||||
const App = () => (
|
||||
<div>
|
||||
<Theme.Provider value="light">
|
||||
<Language.Provider value="english">
|
||||
<Theme.Provider value="dark">
|
||||
<Theme.Consumer>
|
||||
{theme => <div id="theme1">{theme}</div>}
|
||||
</Theme.Consumer>
|
||||
</Theme.Provider>
|
||||
<Theme.Consumer>
|
||||
{theme => <div id="theme2">{theme}</div>}
|
||||
</Theme.Consumer>
|
||||
<Language.Provider value="sanskrit">
|
||||
<Theme.Provider value="blue">
|
||||
<Theme.Provider value="red">
|
||||
<Language.Consumer>
|
||||
{() => (
|
||||
<Language.Provider value="chinese">
|
||||
<Language.Provider value="hungarian" />
|
||||
<Language.Consumer>
|
||||
{language => <div id="language1">{language}</div>}
|
||||
</Language.Consumer>
|
||||
</Language.Provider>
|
||||
)}
|
||||
</Language.Consumer>
|
||||
</Theme.Provider>
|
||||
<Language.Consumer>
|
||||
{language => (
|
||||
<React.Fragment>
|
||||
<Theme.Consumer>
|
||||
{theme => <div id="theme3">{theme}</div>}
|
||||
</Theme.Consumer>
|
||||
<div id="language2">{language}</div>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Language.Consumer>
|
||||
</Theme.Provider>
|
||||
</Language.Provider>
|
||||
</Language.Provider>
|
||||
</Theme.Provider>
|
||||
<Language.Consumer>
|
||||
{language => <div id="language3">{language}</div>}
|
||||
</Language.Consumer>
|
||||
</div>
|
||||
);
|
||||
let e = await render(<App />);
|
||||
expect(e.querySelector('#theme1').textContent).toBe('dark');
|
||||
expect(e.querySelector('#theme2').textContent).toBe('light');
|
||||
expect(e.querySelector('#theme3').textContent).toBe('blue');
|
||||
expect(e.querySelector('#language1').textContent).toBe('chinese');
|
||||
expect(e.querySelector('#language2').textContent).toBe('sanskrit');
|
||||
expect(e.querySelector('#language3').textContent).toBe('french');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,7 +39,7 @@ describe('ReactDOMTextComponent', () => {
|
||||
</div>,
|
||||
el,
|
||||
);
|
||||
let nodes = filterOutComments(ReactDOM.findDOMNode(inst).childNodes);
|
||||
let nodes = filterOutComments(inst.childNodes);
|
||||
|
||||
let foo = nodes[1];
|
||||
let bar = nodes[2];
|
||||
@@ -56,7 +56,7 @@ describe('ReactDOMTextComponent', () => {
|
||||
);
|
||||
// After the update, the text nodes should have stayed in place (as opposed
|
||||
// to getting unmounted and remounted)
|
||||
nodes = filterOutComments(ReactDOM.findDOMNode(inst).childNodes);
|
||||
nodes = filterOutComments(inst.childNodes);
|
||||
expect(nodes[1]).toBe(foo);
|
||||
expect(nodes[2]).toBe(bar);
|
||||
expect(foo.data).toBe('baz');
|
||||
@@ -74,8 +74,7 @@ describe('ReactDOMTextComponent', () => {
|
||||
el,
|
||||
);
|
||||
|
||||
let container = ReactDOM.findDOMNode(inst);
|
||||
let childNodes = filterOutComments(container.childNodes);
|
||||
let childNodes = filterOutComments(inst.childNodes);
|
||||
let childDiv = childNodes[1];
|
||||
|
||||
inst = ReactDOM.render(
|
||||
@@ -86,8 +85,7 @@ describe('ReactDOMTextComponent', () => {
|
||||
</div>,
|
||||
el,
|
||||
);
|
||||
container = ReactDOM.findDOMNode(inst);
|
||||
childNodes = filterOutComments(container.childNodes);
|
||||
childNodes = filterOutComments(inst.childNodes);
|
||||
expect(childNodes.length).toBe(1);
|
||||
expect(childNodes[0]).toBe(childDiv);
|
||||
|
||||
@@ -99,8 +97,7 @@ describe('ReactDOMTextComponent', () => {
|
||||
</div>,
|
||||
el,
|
||||
);
|
||||
container = ReactDOM.findDOMNode(inst);
|
||||
childNodes = filterOutComments(container.childNodes);
|
||||
childNodes = filterOutComments(inst.childNodes);
|
||||
expect(childNodes.length).toBe(3);
|
||||
expect(childNodes[0].data).toBe('foo');
|
||||
expect(childNodes[1]).toBe(childDiv);
|
||||
@@ -125,8 +122,7 @@ describe('ReactDOMTextComponent', () => {
|
||||
el,
|
||||
);
|
||||
|
||||
let container = ReactDOM.findDOMNode(inst);
|
||||
container.normalize();
|
||||
inst.normalize();
|
||||
|
||||
inst = ReactDOM.render(
|
||||
<div>
|
||||
@@ -138,8 +134,7 @@ describe('ReactDOMTextComponent', () => {
|
||||
</div>,
|
||||
el,
|
||||
);
|
||||
container = ReactDOM.findDOMNode(inst);
|
||||
expect(container.textContent).toBe('barbazquxfoo');
|
||||
expect(inst.textContent).toBe('barbazquxfoo');
|
||||
});
|
||||
|
||||
xit('can reconcile text merged by Node.normalize()', () => {
|
||||
@@ -153,8 +148,7 @@ describe('ReactDOMTextComponent', () => {
|
||||
el,
|
||||
);
|
||||
|
||||
let container = ReactDOM.findDOMNode(inst);
|
||||
container.normalize();
|
||||
inst.normalize();
|
||||
|
||||
inst = ReactDOM.render(
|
||||
<div>
|
||||
@@ -164,8 +158,7 @@ describe('ReactDOMTextComponent', () => {
|
||||
</div>,
|
||||
el,
|
||||
);
|
||||
container = ReactDOM.findDOMNode(inst);
|
||||
expect(container.textContent).toBe('barbazqux');
|
||||
expect(inst.textContent).toBe('barbazqux');
|
||||
});
|
||||
|
||||
it('can reconcile text from pre-rendered markup', () => {
|
||||
@@ -207,15 +200,14 @@ describe('ReactDOMTextComponent', () => {
|
||||
el,
|
||||
);
|
||||
|
||||
let container = ReactDOM.findDOMNode(inst);
|
||||
let childNodes = filterOutComments(ReactDOM.findDOMNode(inst).childNodes);
|
||||
let childNodes = filterOutComments(inst.childNodes);
|
||||
let textNode = childNodes[1];
|
||||
textNode.textContent = 'foo';
|
||||
container.insertBefore(
|
||||
inst.insertBefore(
|
||||
document.createTextNode('bar'),
|
||||
childNodes[1].nextSibling,
|
||||
);
|
||||
container.insertBefore(
|
||||
inst.insertBefore(
|
||||
document.createTextNode('baz'),
|
||||
childNodes[1].nextSibling,
|
||||
);
|
||||
@@ -227,8 +219,7 @@ describe('ReactDOMTextComponent', () => {
|
||||
</div>,
|
||||
el,
|
||||
);
|
||||
container = ReactDOM.findDOMNode(inst);
|
||||
expect(container.textContent).toBe('barbazqux');
|
||||
expect(inst.textContent).toBe('barbazqux');
|
||||
});
|
||||
|
||||
xit('can reconcile text arbitrarily split into multiple nodes on some substitutions only', () => {
|
||||
@@ -246,21 +237,20 @@ describe('ReactDOMTextComponent', () => {
|
||||
el,
|
||||
);
|
||||
|
||||
let container = ReactDOM.findDOMNode(inst);
|
||||
let childNodes = filterOutComments(ReactDOM.findDOMNode(inst).childNodes);
|
||||
let childNodes = filterOutComments(inst.childNodes);
|
||||
let textNode = childNodes[3];
|
||||
textNode.textContent = 'foo';
|
||||
container.insertBefore(
|
||||
inst.insertBefore(
|
||||
document.createTextNode('bar'),
|
||||
childNodes[3].nextSibling,
|
||||
);
|
||||
container.insertBefore(
|
||||
inst.insertBefore(
|
||||
document.createTextNode('baz'),
|
||||
childNodes[3].nextSibling,
|
||||
);
|
||||
let secondTextNode = childNodes[5];
|
||||
secondTextNode.textContent = 'bar';
|
||||
container.insertBefore(
|
||||
inst.insertBefore(
|
||||
document.createTextNode('foo'),
|
||||
childNodes[5].nextSibling,
|
||||
);
|
||||
@@ -277,8 +267,7 @@ describe('ReactDOMTextComponent', () => {
|
||||
</div>,
|
||||
el,
|
||||
);
|
||||
container = ReactDOM.findDOMNode(inst);
|
||||
expect(container.textContent).toBe('bazbarbazquxbarbazbar');
|
||||
expect(inst.textContent).toBe('bazbarbazquxbarbazbar');
|
||||
});
|
||||
|
||||
xit('can unmount normalized text nodes', () => {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
const emptyFunction = require('fbjs/lib/emptyFunction');
|
||||
function emptyFunction() {}
|
||||
|
||||
describe('ReactDOMTextarea', () => {
|
||||
let React;
|
||||
@@ -218,7 +218,7 @@ describe('ReactDOMTextarea', () => {
|
||||
|
||||
const node = container.firstChild;
|
||||
let nodeValue = 'a';
|
||||
const nodeValueSetter = jest.genMockFn();
|
||||
const nodeValueSetter = jest.fn();
|
||||
Object.defineProperty(node, 'value', {
|
||||
get: function() {
|
||||
return nodeValue;
|
||||
@@ -229,10 +229,10 @@ describe('ReactDOMTextarea', () => {
|
||||
});
|
||||
|
||||
ReactDOM.render(<textarea value="a" onChange={emptyFunction} />, container);
|
||||
expect(nodeValueSetter.mock.calls.length).toBe(0);
|
||||
expect(nodeValueSetter).toHaveBeenCalledTimes(0);
|
||||
|
||||
ReactDOM.render(<textarea value="b" onChange={emptyFunction} />, container);
|
||||
expect(nodeValueSetter.mock.calls.length).toBe(1);
|
||||
expect(nodeValueSetter).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should properly control a value of number `0`', () => {
|
||||
|
||||
@@ -24,7 +24,7 @@ describe('ReactEmptyComponent', () => {
|
||||
ReactDOM = require('react-dom');
|
||||
ReactTestUtils = require('react-dom/test-utils');
|
||||
|
||||
log = jasmine.createSpy();
|
||||
log = jest.fn();
|
||||
|
||||
TogglingComponent = class extends React.Component {
|
||||
state = {component: this.props.firstComponent};
|
||||
@@ -91,11 +91,17 @@ describe('ReactEmptyComponent', () => {
|
||||
ReactTestUtils.renderIntoDocument(instance1);
|
||||
ReactTestUtils.renderIntoDocument(instance2);
|
||||
|
||||
expect(log.calls.count()).toBe(4);
|
||||
expect(log.calls.argsFor(0)[0]).toBe(null);
|
||||
expect(log.calls.argsFor(1)[0].tagName).toBe('DIV');
|
||||
expect(log.calls.argsFor(2)[0].tagName).toBe('DIV');
|
||||
expect(log.calls.argsFor(3)[0]).toBe(null);
|
||||
expect(log).toHaveBeenCalledTimes(4);
|
||||
expect(log).toHaveBeenNthCalledWith(1, null);
|
||||
expect(log).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expect.objectContaining({tagName: 'DIV'}),
|
||||
);
|
||||
expect(log).toHaveBeenNthCalledWith(
|
||||
3,
|
||||
expect.objectContaining({tagName: 'DIV'}),
|
||||
);
|
||||
expect(log).toHaveBeenNthCalledWith(4, null);
|
||||
});
|
||||
|
||||
it('should be able to switch in a list of children', () => {
|
||||
@@ -111,13 +117,22 @@ describe('ReactEmptyComponent', () => {
|
||||
</div>,
|
||||
);
|
||||
|
||||
expect(log.calls.count()).toBe(6);
|
||||
expect(log.calls.argsFor(0)[0]).toBe(null);
|
||||
expect(log.calls.argsFor(1)[0]).toBe(null);
|
||||
expect(log.calls.argsFor(2)[0]).toBe(null);
|
||||
expect(log.calls.argsFor(3)[0].tagName).toBe('DIV');
|
||||
expect(log.calls.argsFor(4)[0].tagName).toBe('DIV');
|
||||
expect(log.calls.argsFor(5)[0].tagName).toBe('DIV');
|
||||
expect(log).toHaveBeenCalledTimes(6);
|
||||
expect(log).toHaveBeenNthCalledWith(1, null);
|
||||
expect(log).toHaveBeenNthCalledWith(2, null);
|
||||
expect(log).toHaveBeenNthCalledWith(3, null);
|
||||
expect(log).toHaveBeenNthCalledWith(
|
||||
4,
|
||||
expect.objectContaining({tagName: 'DIV'}),
|
||||
);
|
||||
expect(log).toHaveBeenNthCalledWith(
|
||||
5,
|
||||
expect.objectContaining({tagName: 'DIV'}),
|
||||
);
|
||||
expect(log).toHaveBeenNthCalledWith(
|
||||
6,
|
||||
expect.objectContaining({tagName: 'DIV'}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should distinguish between a script placeholder and an actual script tag', () => {
|
||||
@@ -135,11 +150,17 @@ describe('ReactEmptyComponent', () => {
|
||||
ReactTestUtils.renderIntoDocument(instance2);
|
||||
}).not.toThrow();
|
||||
|
||||
expect(log.calls.count()).toBe(4);
|
||||
expect(log.calls.argsFor(0)[0]).toBe(null);
|
||||
expect(log.calls.argsFor(1)[0].tagName).toBe('SCRIPT');
|
||||
expect(log.calls.argsFor(2)[0].tagName).toBe('SCRIPT');
|
||||
expect(log.calls.argsFor(3)[0]).toBe(null);
|
||||
expect(log).toHaveBeenCalledTimes(4);
|
||||
expect(log).toHaveBeenNthCalledWith(1, null);
|
||||
expect(log).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expect.objectContaining({tagName: 'SCRIPT'}),
|
||||
);
|
||||
expect(log).toHaveBeenNthCalledWith(
|
||||
3,
|
||||
expect.objectContaining({tagName: 'SCRIPT'}),
|
||||
);
|
||||
expect(log).toHaveBeenNthCalledWith(4, null);
|
||||
});
|
||||
|
||||
it(
|
||||
@@ -172,11 +193,17 @@ describe('ReactEmptyComponent', () => {
|
||||
ReactTestUtils.renderIntoDocument(instance2);
|
||||
}).not.toThrow();
|
||||
|
||||
expect(log.calls.count()).toBe(4);
|
||||
expect(log.calls.argsFor(0)[0].tagName).toBe('DIV');
|
||||
expect(log.calls.argsFor(1)[0]).toBe(null);
|
||||
expect(log.calls.argsFor(2)[0]).toBe(null);
|
||||
expect(log.calls.argsFor(3)[0].tagName).toBe('DIV');
|
||||
expect(log).toHaveBeenCalledTimes(4);
|
||||
expect(log).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expect.objectContaining({tagName: 'DIV'}),
|
||||
);
|
||||
expect(log).toHaveBeenNthCalledWith(2, null);
|
||||
expect(log).toHaveBeenNthCalledWith(3, null);
|
||||
expect(log).toHaveBeenNthCalledWith(
|
||||
4,
|
||||
expect.objectContaining({tagName: 'DIV'}),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactTestUtils;
|
||||
|
||||
describe('ReactEventIndependence', () => {
|
||||
beforeEach(() => {
|
||||
@@ -19,49 +18,66 @@ describe('ReactEventIndependence', () => {
|
||||
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactTestUtils = require('react-dom/test-utils');
|
||||
});
|
||||
|
||||
it('does not crash with other react inside', () => {
|
||||
let clicks = 0;
|
||||
const div = ReactTestUtils.renderIntoDocument(
|
||||
<div
|
||||
onClick={() => clicks++}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: '<button data-reactid=".z">click me</div>',
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
ReactTestUtils.SimulateNative.click(div.firstChild);
|
||||
expect(clicks).toBe(1);
|
||||
const container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
try {
|
||||
const div = ReactDOM.render(
|
||||
<div
|
||||
onClick={() => clicks++}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: '<button data-reactid=".z">click me</div>',
|
||||
}}
|
||||
/>,
|
||||
container,
|
||||
);
|
||||
|
||||
div.firstChild.click();
|
||||
expect(clicks).toBe(1);
|
||||
} finally {
|
||||
document.body.removeChild(container);
|
||||
}
|
||||
});
|
||||
|
||||
it('does not crash with other react outside', () => {
|
||||
let clicks = 0;
|
||||
const outer = document.createElement('div');
|
||||
outer.setAttribute('data-reactid', '.z');
|
||||
const inner = ReactDOM.render(
|
||||
<button onClick={() => clicks++}>click me</button>,
|
||||
outer,
|
||||
);
|
||||
ReactTestUtils.SimulateNative.click(inner);
|
||||
expect(clicks).toBe(1);
|
||||
document.body.appendChild(outer);
|
||||
try {
|
||||
outer.setAttribute('data-reactid', '.z');
|
||||
const inner = ReactDOM.render(
|
||||
<button onClick={() => clicks++}>click me</button>,
|
||||
outer,
|
||||
);
|
||||
inner.click();
|
||||
expect(clicks).toBe(1);
|
||||
} finally {
|
||||
document.body.removeChild(outer);
|
||||
}
|
||||
});
|
||||
|
||||
it('does not when event fired on unmounted tree', () => {
|
||||
let clicks = 0;
|
||||
const container = document.createElement('div');
|
||||
const button = ReactDOM.render(
|
||||
<button onClick={() => clicks++}>click me</button>,
|
||||
container,
|
||||
);
|
||||
document.body.appendChild(container);
|
||||
try {
|
||||
const button = ReactDOM.render(
|
||||
<button onClick={() => clicks++}>click me</button>,
|
||||
container,
|
||||
);
|
||||
|
||||
// Now we unmount the component, as if caused by a non-React event handler
|
||||
// for the same click we're about to simulate, like closing a layer:
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
ReactTestUtils.SimulateNative.click(button);
|
||||
// Now we unmount the component, as if caused by a non-React event handler
|
||||
// for the same click we're about to simulate, like closing a layer:
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
button.click();
|
||||
|
||||
// Since the tree is unmounted, we don't dispatch the click event.
|
||||
expect(clicks).toBe(0);
|
||||
// Since the tree is unmounted, we don't dispatch the click event.
|
||||
expect(clicks).toBe(0);
|
||||
} finally {
|
||||
document.body.removeChild(container);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -78,7 +78,7 @@ describe('ReactIdentity', () => {
|
||||
|
||||
const instance = ReactDOM.render(<Wrapper />, container);
|
||||
const span = instance.refs.span;
|
||||
expect(ReactDOM.findDOMNode(span)).not.toBe(null);
|
||||
expect(span).not.toBe(null);
|
||||
}
|
||||
|
||||
it('should allow any character as a key, in a detached parent', () => {
|
||||
|
||||
@@ -91,25 +91,25 @@ describe('ReactMount', () => {
|
||||
}
|
||||
}
|
||||
|
||||
expect(mockMount.mock.calls.length).toBe(0);
|
||||
expect(mockUnmount.mock.calls.length).toBe(0);
|
||||
expect(mockMount).toHaveBeenCalledTimes(0);
|
||||
expect(mockUnmount).toHaveBeenCalledTimes(0);
|
||||
|
||||
ReactDOM.render(<Component text="orange" key="A" />, container);
|
||||
expect(container.firstChild.innerHTML).toBe('orange');
|
||||
expect(mockMount.mock.calls.length).toBe(1);
|
||||
expect(mockUnmount.mock.calls.length).toBe(0);
|
||||
expect(mockMount).toHaveBeenCalledTimes(1);
|
||||
expect(mockUnmount).toHaveBeenCalledTimes(0);
|
||||
|
||||
// If we change the key, the component is unmounted and remounted
|
||||
ReactDOM.render(<Component text="green" key="B" />, container);
|
||||
expect(container.firstChild.innerHTML).toBe('green');
|
||||
expect(mockMount.mock.calls.length).toBe(2);
|
||||
expect(mockUnmount.mock.calls.length).toBe(1);
|
||||
expect(mockMount).toHaveBeenCalledTimes(2);
|
||||
expect(mockUnmount).toHaveBeenCalledTimes(1);
|
||||
|
||||
// But if we don't change the key, the component instance is reused
|
||||
ReactDOM.render(<Component text="blue" key="B" />, container);
|
||||
expect(container.firstChild.innerHTML).toBe('blue');
|
||||
expect(mockMount.mock.calls.length).toBe(2);
|
||||
expect(mockUnmount.mock.calls.length).toBe(1);
|
||||
expect(mockMount).toHaveBeenCalledTimes(2);
|
||||
expect(mockUnmount).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should reuse markup if rendering to the same target twice', () => {
|
||||
|
||||
@@ -36,9 +36,9 @@ describe('ReactMultiChild', () => {
|
||||
}
|
||||
}
|
||||
|
||||
expect(mockMount.mock.calls.length).toBe(0);
|
||||
expect(mockUpdate.mock.calls.length).toBe(0);
|
||||
expect(mockUnmount.mock.calls.length).toBe(0);
|
||||
expect(mockMount).toHaveBeenCalledTimes(0);
|
||||
expect(mockUpdate).toHaveBeenCalledTimes(0);
|
||||
expect(mockUnmount).toHaveBeenCalledTimes(0);
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
@@ -47,9 +47,9 @@ describe('ReactMultiChild', () => {
|
||||
container,
|
||||
);
|
||||
|
||||
expect(mockMount.mock.calls.length).toBe(1);
|
||||
expect(mockUpdate.mock.calls.length).toBe(0);
|
||||
expect(mockUnmount.mock.calls.length).toBe(0);
|
||||
expect(mockMount).toHaveBeenCalledTimes(1);
|
||||
expect(mockUpdate).toHaveBeenCalledTimes(0);
|
||||
expect(mockUnmount).toHaveBeenCalledTimes(0);
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
@@ -58,9 +58,9 @@ describe('ReactMultiChild', () => {
|
||||
container,
|
||||
);
|
||||
|
||||
expect(mockMount.mock.calls.length).toBe(1);
|
||||
expect(mockUpdate.mock.calls.length).toBe(1);
|
||||
expect(mockUnmount.mock.calls.length).toBe(0);
|
||||
expect(mockMount).toHaveBeenCalledTimes(1);
|
||||
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
||||
expect(mockUnmount).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('should replace children with different constructors', () => {
|
||||
@@ -77,8 +77,8 @@ describe('ReactMultiChild', () => {
|
||||
}
|
||||
}
|
||||
|
||||
expect(mockMount.mock.calls.length).toBe(0);
|
||||
expect(mockUnmount.mock.calls.length).toBe(0);
|
||||
expect(mockMount).toHaveBeenCalledTimes(0);
|
||||
expect(mockUnmount).toHaveBeenCalledTimes(0);
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
@@ -87,8 +87,8 @@ describe('ReactMultiChild', () => {
|
||||
container,
|
||||
);
|
||||
|
||||
expect(mockMount.mock.calls.length).toBe(1);
|
||||
expect(mockUnmount.mock.calls.length).toBe(0);
|
||||
expect(mockMount).toHaveBeenCalledTimes(1);
|
||||
expect(mockUnmount).toHaveBeenCalledTimes(0);
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
@@ -97,8 +97,8 @@ describe('ReactMultiChild', () => {
|
||||
container,
|
||||
);
|
||||
|
||||
expect(mockMount.mock.calls.length).toBe(1);
|
||||
expect(mockUnmount.mock.calls.length).toBe(1);
|
||||
expect(mockMount).toHaveBeenCalledTimes(1);
|
||||
expect(mockUnmount).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should NOT replace children with different owners', () => {
|
||||
@@ -121,13 +121,13 @@ describe('ReactMultiChild', () => {
|
||||
}
|
||||
}
|
||||
|
||||
expect(mockMount.mock.calls.length).toBe(0);
|
||||
expect(mockUnmount.mock.calls.length).toBe(0);
|
||||
expect(mockMount).toHaveBeenCalledTimes(0);
|
||||
expect(mockUnmount).toHaveBeenCalledTimes(0);
|
||||
|
||||
ReactDOM.render(<WrapperComponent />, container);
|
||||
|
||||
expect(mockMount.mock.calls.length).toBe(1);
|
||||
expect(mockUnmount.mock.calls.length).toBe(0);
|
||||
expect(mockMount).toHaveBeenCalledTimes(1);
|
||||
expect(mockUnmount).toHaveBeenCalledTimes(0);
|
||||
|
||||
ReactDOM.render(
|
||||
<WrapperComponent>
|
||||
@@ -136,8 +136,8 @@ describe('ReactMultiChild', () => {
|
||||
container,
|
||||
);
|
||||
|
||||
expect(mockMount.mock.calls.length).toBe(1);
|
||||
expect(mockUnmount.mock.calls.length).toBe(0);
|
||||
expect(mockMount).toHaveBeenCalledTimes(1);
|
||||
expect(mockUnmount).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('should replace children with different keys', () => {
|
||||
@@ -154,8 +154,8 @@ describe('ReactMultiChild', () => {
|
||||
}
|
||||
}
|
||||
|
||||
expect(mockMount.mock.calls.length).toBe(0);
|
||||
expect(mockUnmount.mock.calls.length).toBe(0);
|
||||
expect(mockMount).toHaveBeenCalledTimes(0);
|
||||
expect(mockUnmount).toHaveBeenCalledTimes(0);
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
@@ -164,8 +164,8 @@ describe('ReactMultiChild', () => {
|
||||
container,
|
||||
);
|
||||
|
||||
expect(mockMount.mock.calls.length).toBe(1);
|
||||
expect(mockUnmount.mock.calls.length).toBe(0);
|
||||
expect(mockMount).toHaveBeenCalledTimes(1);
|
||||
expect(mockUnmount).toHaveBeenCalledTimes(0);
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
@@ -174,8 +174,8 @@ describe('ReactMultiChild', () => {
|
||||
container,
|
||||
);
|
||||
|
||||
expect(mockMount.mock.calls.length).toBe(2);
|
||||
expect(mockUnmount.mock.calls.length).toBe(1);
|
||||
expect(mockMount).toHaveBeenCalledTimes(2);
|
||||
expect(mockUnmount).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should warn for duplicated array keys with component stack info', () => {
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactDOMServer;
|
||||
let ReactTestUtils;
|
||||
|
||||
// These tests rely both on ReactDOMServer and ReactDOM.
|
||||
// If a test only needs ReactDOMServer, put it in ReactServerRendering-test instead.
|
||||
@@ -21,7 +20,6 @@ describe('ReactDOMServerHydration', () => {
|
||||
jest.resetModules();
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactTestUtils = require('react-dom/test-utils');
|
||||
ReactDOMServer = require('react-dom/server');
|
||||
});
|
||||
|
||||
@@ -48,67 +46,73 @@ describe('ReactDOMServerHydration', () => {
|
||||
}
|
||||
|
||||
const element = document.createElement('div');
|
||||
ReactDOM.render(<TestComponent />, element);
|
||||
document.body.appendChild(element);
|
||||
try {
|
||||
ReactDOM.render(<TestComponent />, element);
|
||||
|
||||
let lastMarkup = element.innerHTML;
|
||||
let lastMarkup = element.innerHTML;
|
||||
|
||||
// Exercise the update path. Markup should not change,
|
||||
// but some lifecycle methods should be run again.
|
||||
ReactDOM.render(<TestComponent name="x" />, element);
|
||||
expect(mountCount).toEqual(1);
|
||||
// Exercise the update path. Markup should not change,
|
||||
// but some lifecycle methods should be run again.
|
||||
ReactDOM.render(<TestComponent name="x" />, element);
|
||||
expect(mountCount).toEqual(1);
|
||||
|
||||
// Unmount and remount. We should get another mount event and
|
||||
// we should get different markup, as the IDs are unique each time.
|
||||
ReactDOM.unmountComponentAtNode(element);
|
||||
expect(element.innerHTML).toEqual('');
|
||||
ReactDOM.render(<TestComponent name="x" />, element);
|
||||
expect(mountCount).toEqual(2);
|
||||
expect(element.innerHTML).not.toEqual(lastMarkup);
|
||||
// Unmount and remount. We should get another mount event and
|
||||
// we should get different markup, as the IDs are unique each time.
|
||||
ReactDOM.unmountComponentAtNode(element);
|
||||
expect(element.innerHTML).toEqual('');
|
||||
ReactDOM.render(<TestComponent name="x" />, element);
|
||||
expect(mountCount).toEqual(2);
|
||||
expect(element.innerHTML).not.toEqual(lastMarkup);
|
||||
|
||||
// Now kill the node and render it on top of server-rendered markup, as if
|
||||
// we used server rendering. We should mount again, but the markup should
|
||||
// be unchanged. We will append a sentinel at the end of innerHTML to be
|
||||
// sure that innerHTML was not changed.
|
||||
ReactDOM.unmountComponentAtNode(element);
|
||||
expect(element.innerHTML).toEqual('');
|
||||
// Now kill the node and render it on top of server-rendered markup, as if
|
||||
// we used server rendering. We should mount again, but the markup should
|
||||
// be unchanged. We will append a sentinel at the end of innerHTML to be
|
||||
// sure that innerHTML was not changed.
|
||||
ReactDOM.unmountComponentAtNode(element);
|
||||
expect(element.innerHTML).toEqual('');
|
||||
|
||||
lastMarkup = ReactDOMServer.renderToString(<TestComponent name="x" />);
|
||||
element.innerHTML = lastMarkup;
|
||||
lastMarkup = ReactDOMServer.renderToString(<TestComponent name="x" />);
|
||||
element.innerHTML = lastMarkup;
|
||||
|
||||
let instance;
|
||||
let instance;
|
||||
|
||||
expect(() => {
|
||||
instance = ReactDOM.render(<TestComponent name="x" />, element);
|
||||
}).toLowPriorityWarnDev(
|
||||
'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
|
||||
'will stop working in React v17. Replace the ReactDOM.render() call ' +
|
||||
'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
|
||||
);
|
||||
expect(mountCount).toEqual(3);
|
||||
expect(element.innerHTML).toBe(lastMarkup);
|
||||
expect(() => {
|
||||
instance = ReactDOM.render(<TestComponent name="x" />, element);
|
||||
}).toLowPriorityWarnDev(
|
||||
'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
|
||||
'will stop working in React v17. Replace the ReactDOM.render() call ' +
|
||||
'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
|
||||
);
|
||||
expect(mountCount).toEqual(3);
|
||||
expect(element.innerHTML).toBe(lastMarkup);
|
||||
|
||||
// Ensure the events system works after mount into server markup
|
||||
expect(numClicks).toEqual(0);
|
||||
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance.refs.span));
|
||||
expect(numClicks).toEqual(1);
|
||||
// Ensure the events system works after mount into server markup
|
||||
expect(numClicks).toEqual(0);
|
||||
|
||||
ReactDOM.unmountComponentAtNode(element);
|
||||
expect(element.innerHTML).toEqual('');
|
||||
instance.refs.span.click();
|
||||
expect(numClicks).toEqual(1);
|
||||
|
||||
// Now simulate a situation where the app is not idempotent. React should
|
||||
// warn but do the right thing.
|
||||
element.innerHTML = lastMarkup;
|
||||
expect(() => {
|
||||
instance = ReactDOM.render(<TestComponent name="y" />, element);
|
||||
}).toWarnDev('Text content did not match. Server: "x" Client: "y"');
|
||||
expect(mountCount).toEqual(4);
|
||||
expect(element.innerHTML.length > 0).toBe(true);
|
||||
expect(element.innerHTML).not.toEqual(lastMarkup);
|
||||
ReactDOM.unmountComponentAtNode(element);
|
||||
expect(element.innerHTML).toEqual('');
|
||||
|
||||
// Ensure the events system works after markup mismatch.
|
||||
expect(numClicks).toEqual(1);
|
||||
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance.refs.span));
|
||||
expect(numClicks).toEqual(2);
|
||||
// Now simulate a situation where the app is not idempotent. React should
|
||||
// warn but do the right thing.
|
||||
element.innerHTML = lastMarkup;
|
||||
expect(() => {
|
||||
instance = ReactDOM.render(<TestComponent name="y" />, element);
|
||||
}).toWarnDev('Text content did not match. Server: "x" Client: "y"');
|
||||
expect(mountCount).toEqual(4);
|
||||
expect(element.innerHTML.length > 0).toBe(true);
|
||||
expect(element.innerHTML).not.toEqual(lastMarkup);
|
||||
|
||||
// Ensure the events system works after markup mismatch.
|
||||
expect(numClicks).toEqual(1);
|
||||
instance.refs.span.click();
|
||||
expect(numClicks).toEqual(2);
|
||||
} finally {
|
||||
document.body.removeChild(element);
|
||||
}
|
||||
});
|
||||
|
||||
it('should have the correct mounting behavior (new hydrate API)', () => {
|
||||
@@ -134,59 +138,64 @@ describe('ReactDOMServerHydration', () => {
|
||||
}
|
||||
|
||||
const element = document.createElement('div');
|
||||
ReactDOM.render(<TestComponent />, element);
|
||||
document.body.appendChild(element);
|
||||
try {
|
||||
ReactDOM.render(<TestComponent />, element);
|
||||
|
||||
let lastMarkup = element.innerHTML;
|
||||
let lastMarkup = element.innerHTML;
|
||||
|
||||
// Exercise the update path. Markup should not change,
|
||||
// but some lifecycle methods should be run again.
|
||||
ReactDOM.render(<TestComponent name="x" />, element);
|
||||
expect(mountCount).toEqual(1);
|
||||
// Exercise the update path. Markup should not change,
|
||||
// but some lifecycle methods should be run again.
|
||||
ReactDOM.render(<TestComponent name="x" />, element);
|
||||
expect(mountCount).toEqual(1);
|
||||
|
||||
// Unmount and remount. We should get another mount event and
|
||||
// we should get different markup, as the IDs are unique each time.
|
||||
ReactDOM.unmountComponentAtNode(element);
|
||||
expect(element.innerHTML).toEqual('');
|
||||
ReactDOM.render(<TestComponent name="x" />, element);
|
||||
expect(mountCount).toEqual(2);
|
||||
expect(element.innerHTML).not.toEqual(lastMarkup);
|
||||
// Unmount and remount. We should get another mount event and
|
||||
// we should get different markup, as the IDs are unique each time.
|
||||
ReactDOM.unmountComponentAtNode(element);
|
||||
expect(element.innerHTML).toEqual('');
|
||||
ReactDOM.render(<TestComponent name="x" />, element);
|
||||
expect(mountCount).toEqual(2);
|
||||
expect(element.innerHTML).not.toEqual(lastMarkup);
|
||||
|
||||
// Now kill the node and render it on top of server-rendered markup, as if
|
||||
// we used server rendering. We should mount again, but the markup should
|
||||
// be unchanged. We will append a sentinel at the end of innerHTML to be
|
||||
// sure that innerHTML was not changed.
|
||||
ReactDOM.unmountComponentAtNode(element);
|
||||
expect(element.innerHTML).toEqual('');
|
||||
// Now kill the node and render it on top of server-rendered markup, as if
|
||||
// we used server rendering. We should mount again, but the markup should
|
||||
// be unchanged. We will append a sentinel at the end of innerHTML to be
|
||||
// sure that innerHTML was not changed.
|
||||
ReactDOM.unmountComponentAtNode(element);
|
||||
expect(element.innerHTML).toEqual('');
|
||||
|
||||
lastMarkup = ReactDOMServer.renderToString(<TestComponent name="x" />);
|
||||
element.innerHTML = lastMarkup;
|
||||
lastMarkup = ReactDOMServer.renderToString(<TestComponent name="x" />);
|
||||
element.innerHTML = lastMarkup;
|
||||
|
||||
let instance = ReactDOM.hydrate(<TestComponent name="x" />, element);
|
||||
expect(mountCount).toEqual(3);
|
||||
expect(element.innerHTML).toBe(lastMarkup);
|
||||
let instance = ReactDOM.hydrate(<TestComponent name="x" />, element);
|
||||
expect(mountCount).toEqual(3);
|
||||
expect(element.innerHTML).toBe(lastMarkup);
|
||||
|
||||
// Ensure the events system works after mount into server markup
|
||||
expect(numClicks).toEqual(0);
|
||||
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance.refs.span));
|
||||
expect(numClicks).toEqual(1);
|
||||
// Ensure the events system works after mount into server markup
|
||||
expect(numClicks).toEqual(0);
|
||||
instance.refs.span.click();
|
||||
expect(numClicks).toEqual(1);
|
||||
|
||||
ReactDOM.unmountComponentAtNode(element);
|
||||
expect(element.innerHTML).toEqual('');
|
||||
ReactDOM.unmountComponentAtNode(element);
|
||||
expect(element.innerHTML).toEqual('');
|
||||
|
||||
// Now simulate a situation where the app is not idempotent. React should
|
||||
// warn but do the right thing.
|
||||
element.innerHTML = lastMarkup;
|
||||
expect(() => {
|
||||
instance = ReactDOM.hydrate(<TestComponent name="y" />, element);
|
||||
}).toWarnDev('Text content did not match. Server: "x" Client: "y"');
|
||||
expect(mountCount).toEqual(4);
|
||||
expect(element.innerHTML.length > 0).toBe(true);
|
||||
expect(element.innerHTML).not.toEqual(lastMarkup);
|
||||
// Now simulate a situation where the app is not idempotent. React should
|
||||
// warn but do the right thing.
|
||||
element.innerHTML = lastMarkup;
|
||||
expect(() => {
|
||||
instance = ReactDOM.hydrate(<TestComponent name="y" />, element);
|
||||
}).toWarnDev('Text content did not match. Server: "x" Client: "y"');
|
||||
expect(mountCount).toEqual(4);
|
||||
expect(element.innerHTML.length > 0).toBe(true);
|
||||
expect(element.innerHTML).not.toEqual(lastMarkup);
|
||||
|
||||
// Ensure the events system works after markup mismatch.
|
||||
expect(numClicks).toEqual(1);
|
||||
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance.refs.span));
|
||||
expect(numClicks).toEqual(2);
|
||||
// Ensure the events system works after markup mismatch.
|
||||
expect(numClicks).toEqual(1);
|
||||
instance.refs.span.click();
|
||||
expect(numClicks).toEqual(2);
|
||||
} finally {
|
||||
document.body.removeChild(element);
|
||||
}
|
||||
});
|
||||
|
||||
// We have a polyfill for autoFocus on the client, but we intentionally don't
|
||||
|
||||
@@ -280,17 +280,16 @@ describe('ReactTestUtils', () => {
|
||||
};
|
||||
spyOnDevAndProd(obj, 'handler').and.callThrough();
|
||||
const container = document.createElement('div');
|
||||
const instance = ReactDOM.render(
|
||||
const node = ReactDOM.render(
|
||||
<input type="text" onChange={obj.handler} />,
|
||||
container,
|
||||
);
|
||||
|
||||
const node = ReactDOM.findDOMNode(instance);
|
||||
node.value = 'giraffe';
|
||||
ReactTestUtils.Simulate.change(node);
|
||||
|
||||
expect(obj.handler).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({target: node}),
|
||||
expect.objectContaining({target: node}),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -321,12 +320,12 @@ describe('ReactTestUtils', () => {
|
||||
container,
|
||||
);
|
||||
|
||||
const node = ReactDOM.findDOMNode(instance.refs.input);
|
||||
const node = instance.refs.input;
|
||||
node.value = 'zebra';
|
||||
ReactTestUtils.Simulate.change(node);
|
||||
|
||||
expect(obj.handler).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({target: node}),
|
||||
expect.objectContaining({target: node}),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -337,7 +336,7 @@ describe('ReactTestUtils', () => {
|
||||
}
|
||||
}
|
||||
|
||||
const handler = jasmine.createSpy('spy');
|
||||
const handler = jest.fn().mockName('spy');
|
||||
const shallowRenderer = createRenderer();
|
||||
const result = shallowRenderer.render(
|
||||
<SomeComponent handleClick={handler} />,
|
||||
@@ -358,7 +357,7 @@ describe('ReactTestUtils', () => {
|
||||
}
|
||||
}
|
||||
|
||||
const handler = jasmine.createSpy('spy');
|
||||
const handler = jest.fn().mockName('spy');
|
||||
const container = document.createElement('div');
|
||||
const instance = ReactDOM.render(
|
||||
<SomeComponent handleClick={handler} />,
|
||||
@@ -394,7 +393,7 @@ describe('ReactTestUtils', () => {
|
||||
|
||||
it('should set the type of the event', () => {
|
||||
let event;
|
||||
const stub = jest.genMockFn().mockImplementation(e => {
|
||||
const stub = jest.fn().mockImplementation(e => {
|
||||
e.persist();
|
||||
event = e;
|
||||
});
|
||||
@@ -431,7 +430,7 @@ describe('ReactTestUtils', () => {
|
||||
ReactTestUtils.Simulate.change(input);
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({target: input}),
|
||||
expect.objectContaining({target: input}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
100
packages/react-dom/src/__tests__/refs-test.js
vendored
100
packages/react-dom/src/__tests__/refs-test.js
vendored
@@ -10,6 +10,7 @@
|
||||
'use strict';
|
||||
|
||||
let React = require('react');
|
||||
let ReactDOM = require('react-dom');
|
||||
let ReactTestUtils = require('react-dom/test-utils');
|
||||
|
||||
/**
|
||||
@@ -81,24 +82,6 @@ class TestRefsComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a TestRefsComponent and ensure that the main refs are wired up.
|
||||
*/
|
||||
const renderTestRefsComponent = function() {
|
||||
const testRefsComponent = ReactTestUtils.renderIntoDocument(
|
||||
<TestRefsComponent />,
|
||||
);
|
||||
expect(testRefsComponent instanceof TestRefsComponent).toBe(true);
|
||||
|
||||
const generalContainer = testRefsComponent.refs.myContainer;
|
||||
expect(generalContainer instanceof GeneralContainerComponent).toBe(true);
|
||||
|
||||
const counter = testRefsComponent.refs.myCounter;
|
||||
expect(counter instanceof ClickCounter).toBe(true);
|
||||
|
||||
return testRefsComponent;
|
||||
};
|
||||
|
||||
const expectClickLogsLengthToBe = function(instance, length) {
|
||||
const clickLogs = ReactTestUtils.scryRenderedDOMComponentsWithClass(
|
||||
instance,
|
||||
@@ -109,12 +92,40 @@ const expectClickLogsLengthToBe = function(instance, length) {
|
||||
};
|
||||
|
||||
describe('reactiverefs', () => {
|
||||
let container;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactTestUtils = require('react-dom/test-utils');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (container) {
|
||||
document.body.removeChild(container);
|
||||
container = null;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Render a TestRefsComponent and ensure that the main refs are wired up.
|
||||
*/
|
||||
const renderTestRefsComponent = function() {
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
const testRefsComponent = ReactDOM.render(<TestRefsComponent />, container);
|
||||
expect(testRefsComponent instanceof TestRefsComponent).toBe(true);
|
||||
|
||||
const generalContainer = testRefsComponent.refs.myContainer;
|
||||
expect(generalContainer instanceof GeneralContainerComponent).toBe(true);
|
||||
|
||||
const counter = testRefsComponent.refs.myCounter;
|
||||
expect(counter instanceof ClickCounter).toBe(true);
|
||||
|
||||
return testRefsComponent;
|
||||
};
|
||||
|
||||
/**
|
||||
* Ensure that for every click log there is a corresponding ref (from the
|
||||
* perspective of the injected ClickCounter component.
|
||||
@@ -129,18 +140,18 @@ describe('reactiverefs', () => {
|
||||
expectClickLogsLengthToBe(testRefsComponent, 1);
|
||||
|
||||
// After clicking the reset, there should still only be one click log ref.
|
||||
ReactTestUtils.Simulate.click(testRefsComponent.refs.resetDiv);
|
||||
testRefsComponent.refs.resetDiv.click();
|
||||
expectClickLogsLengthToBe(testRefsComponent, 1);
|
||||
|
||||
// Begin incrementing clicks (and therefore refs).
|
||||
ReactTestUtils.Simulate.click(clickIncrementer);
|
||||
clickIncrementer.click();
|
||||
expectClickLogsLengthToBe(testRefsComponent, 2);
|
||||
|
||||
ReactTestUtils.Simulate.click(clickIncrementer);
|
||||
clickIncrementer.click();
|
||||
expectClickLogsLengthToBe(testRefsComponent, 3);
|
||||
|
||||
// Now reset again
|
||||
ReactTestUtils.Simulate.click(testRefsComponent.refs.resetDiv);
|
||||
testRefsComponent.refs.resetDiv.click();
|
||||
expectClickLogsLengthToBe(testRefsComponent, 1);
|
||||
});
|
||||
});
|
||||
@@ -168,6 +179,7 @@ describe('ref swapping', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactTestUtils = require('react-dom/test-utils');
|
||||
|
||||
RefHopsAround = class extends React.Component {
|
||||
@@ -293,7 +305,6 @@ describe('ref swapping', () => {
|
||||
|
||||
describe('root level refs', () => {
|
||||
it('attaches and detaches root refs', () => {
|
||||
const ReactDOM = require('react-dom');
|
||||
let inst = null;
|
||||
|
||||
// host node
|
||||
@@ -401,3 +412,46 @@ describe('creating element with ref in constructor', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('strings refs across renderers', () => {
|
||||
it('does not break', () => {
|
||||
class Parent extends React.Component {
|
||||
render() {
|
||||
// This component owns both refs.
|
||||
return (
|
||||
<Indirection
|
||||
child1={<div ref="child1" />}
|
||||
child2={<div ref="child2" />}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Indirection extends React.Component {
|
||||
componentDidUpdate() {
|
||||
// One ref is being rendered later using another renderer copy.
|
||||
jest.resetModules();
|
||||
const AnotherCopyOfReactDOM = require('react-dom');
|
||||
AnotherCopyOfReactDOM.render(this.props.child2, div2);
|
||||
}
|
||||
render() {
|
||||
// The other one is being rendered directly.
|
||||
return this.props.child1;
|
||||
}
|
||||
}
|
||||
|
||||
const div1 = document.createElement('div');
|
||||
const div2 = document.createElement('div');
|
||||
const inst = ReactDOM.render(<Parent />, div1);
|
||||
// Only the first ref has rendered yet.
|
||||
expect(inst.refs.child1.tagName).toBe('DIV');
|
||||
expect(inst.refs.child1).toBe(div1.firstChild);
|
||||
|
||||
// Now both refs should be rendered.
|
||||
ReactDOM.render(<Parent />, div1);
|
||||
expect(inst.refs.child1.tagName).toBe('DIV');
|
||||
expect(inst.refs.child1).toBe(div1.firstChild);
|
||||
expect(inst.refs.child2.tagName).toBe('DIV');
|
||||
expect(inst.refs.child2).toBe(div2.firstChild);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -63,7 +63,7 @@ module.exports = function(initModules) {
|
||||
}
|
||||
}
|
||||
if (__DEV__) {
|
||||
expect(console.error.calls.count()).toBe(count);
|
||||
expect(console.error).toHaveBeenCalledTimes(count);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
14
packages/react-dom/src/client/ReactDOM.js
vendored
14
packages/react-dom/src/client/ReactDOM.js
vendored
@@ -21,7 +21,7 @@ import './ReactDOMClientInjection';
|
||||
|
||||
import * as DOMRenderer from 'react-reconciler/inline.dom';
|
||||
import * as ReactPortal from 'shared/ReactPortal';
|
||||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
|
||||
import {canUseDOM} from 'shared/ExecutionEnvironment';
|
||||
import * as ReactGenericBatching from 'events/ReactGenericBatching';
|
||||
import * as ReactControlledComponent from 'events/ReactControlledComponent';
|
||||
import * as EventPluginHub from 'events/EventPluginHub';
|
||||
@@ -31,9 +31,9 @@ import * as ReactInstanceMap from 'shared/ReactInstanceMap';
|
||||
import ReactVersion from 'shared/ReactVersion';
|
||||
import {ReactCurrentOwner} from 'shared/ReactGlobalSharedState';
|
||||
import getComponentName from 'shared/getComponentName';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import invariant from 'shared/invariant';
|
||||
import lowPriorityWarning from 'shared/lowPriorityWarning';
|
||||
import warning from 'fbjs/lib/warning';
|
||||
import warning from 'shared/warning';
|
||||
|
||||
import * as ReactDOMComponentTree from './ReactDOMComponentTree';
|
||||
import * as ReactDOMFiberComponent from './ReactDOMFiberComponent';
|
||||
@@ -730,6 +730,8 @@ const ReactDOM: Object = {
|
||||
|
||||
unstable_deferredUpdates: DOMRenderer.deferredUpdates,
|
||||
|
||||
unstable_interactiveUpdates: DOMRenderer.interactiveUpdates,
|
||||
|
||||
flushSync: DOMRenderer.flushSync,
|
||||
|
||||
unstable_flushControlled: DOMRenderer.flushControlled,
|
||||
@@ -766,11 +768,7 @@ const foundDevTools = DOMRenderer.injectIntoDevTools({
|
||||
});
|
||||
|
||||
if (__DEV__) {
|
||||
if (
|
||||
!foundDevTools &&
|
||||
ExecutionEnvironment.canUseDOM &&
|
||||
window.top === window.self
|
||||
) {
|
||||
if (!foundDevTools && canUseDOM && window.top === window.self) {
|
||||
// If we're in Chrome or Firefox, provide a download link if not installed.
|
||||
if (
|
||||
(navigator.userAgent.indexOf('Chrome') > -1 &&
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import {HostComponent, HostText} from 'shared/ReactTypeOfWork';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import invariant from 'shared/invariant';
|
||||
|
||||
const randomKey = Math.random()
|
||||
.toString(36)
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
// TODO: direct imports like some-package/src/* are bad. Fix me.
|
||||
import ReactDebugCurrentFiber from 'react-reconciler/src/ReactDebugCurrentFiber';
|
||||
import {registrationNameModules} from 'events/EventPluginRegistry';
|
||||
import emptyFunction from 'fbjs/lib/emptyFunction';
|
||||
import warning from 'fbjs/lib/warning';
|
||||
import warning from 'shared/warning';
|
||||
|
||||
import * as DOMPropertyOperations from './DOMPropertyOperations';
|
||||
import * as ReactDOMFiberInput from './ReactDOMFiberInput';
|
||||
@@ -63,7 +62,7 @@ const HTML = '__html';
|
||||
|
||||
const {html: HTML_NAMESPACE} = Namespaces;
|
||||
|
||||
let getStack = emptyFunction.thatReturns('');
|
||||
let getStack = () => '';
|
||||
|
||||
let warnedUnknownTags;
|
||||
let suppressHydrationWarning;
|
||||
@@ -232,6 +231,8 @@ function getOwnerDocumentFromRootContainer(
|
||||
: rootContainerElement.ownerDocument;
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
|
||||
function trapClickOnNonInteractiveElement(node: HTMLElement) {
|
||||
// Mobile Safari does not fire properly bubble click events on
|
||||
// non-interactive elements, which means delegated click listeners do not
|
||||
@@ -242,7 +243,7 @@ function trapClickOnNonInteractiveElement(node: HTMLElement) {
|
||||
// bookkeeping for it. Not sure if we need to clear it when the listener is
|
||||
// removed.
|
||||
// TODO: Only do this for the relevant Safaris maybe?
|
||||
node.onclick = emptyFunction;
|
||||
node.onclick = noop;
|
||||
}
|
||||
|
||||
function setInitialDOMProperties(
|
||||
@@ -530,7 +531,7 @@ export function setInitialProperties(
|
||||
// TODO: Make sure we check if this is still unmounted or do any clean
|
||||
// up necessary since we never stop tracking anymore.
|
||||
inputValueTracking.track((domElement: any));
|
||||
ReactDOMFiberInput.postMountWrapper(domElement, rawProps);
|
||||
ReactDOMFiberInput.postMountWrapper(domElement, rawProps, false);
|
||||
break;
|
||||
case 'textarea':
|
||||
// TODO: Make sure we check if this is still unmounted or do any clean
|
||||
@@ -1077,7 +1078,7 @@ export function diffHydratedProperties(
|
||||
// TODO: Make sure we check if this is still unmounted or do any clean
|
||||
// up necessary since we never stop tracking anymore.
|
||||
inputValueTracking.track((domElement: any));
|
||||
ReactDOMFiberInput.postMountWrapper(domElement, rawProps);
|
||||
ReactDOMFiberInput.postMountWrapper(domElement, rawProps, true);
|
||||
break;
|
||||
case 'textarea':
|
||||
// TODO: Make sure we check if this is still unmounted or do any clean
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
|
||||
// TODO: direct imports like some-package/src/* are bad. Fix me.
|
||||
import ReactDebugCurrentFiber from 'react-reconciler/src/ReactDebugCurrentFiber';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import warning from 'fbjs/lib/warning';
|
||||
import invariant from 'shared/invariant';
|
||||
import warning from 'shared/warning';
|
||||
|
||||
import * as DOMPropertyOperations from './DOMPropertyOperations';
|
||||
import {getFiberCurrentPropsFromNode} from './ReactDOMComponentTree';
|
||||
@@ -205,20 +205,32 @@ export function updateWrapper(element: Element, props: Object) {
|
||||
}
|
||||
}
|
||||
|
||||
export function postMountWrapper(element: Element, props: Object) {
|
||||
export function postMountWrapper(
|
||||
element: Element,
|
||||
props: Object,
|
||||
isHydrating: boolean,
|
||||
) {
|
||||
const node = ((element: any): InputWithWrapperState);
|
||||
|
||||
if (props.hasOwnProperty('value') || props.hasOwnProperty('defaultValue')) {
|
||||
const initialValue = '' + node._wrapperState.initialValue;
|
||||
const currentValue = node.value;
|
||||
|
||||
// Do not assign value if it is already set. This prevents user text input
|
||||
// from being lost during SSR hydration.
|
||||
if (node.value === '') {
|
||||
node.value = '' + node._wrapperState.initialValue;
|
||||
if (!isHydrating) {
|
||||
// Do not re-assign the value property if there is no change. This
|
||||
// potentially avoids a DOM write and prevents Firefox (~60.0.1) from
|
||||
// prematurely marking required inputs as invalid
|
||||
if (initialValue !== currentValue) {
|
||||
node.value = initialValue;
|
||||
}
|
||||
}
|
||||
|
||||
// value must be assigned before defaultValue. This fixes an issue where the
|
||||
// visually displayed value of date inputs disappears on mobile Safari and Chrome:
|
||||
// https://github.com/facebook/react/issues/7233
|
||||
node.defaultValue = '' + node._wrapperState.initialValue;
|
||||
node.defaultValue = initialValue;
|
||||
}
|
||||
|
||||
// Normally, we'd just do `node.checked = node.checked` upon initial mount, less this bug
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import warning from 'fbjs/lib/warning';
|
||||
import warning from 'shared/warning';
|
||||
|
||||
let didWarnSelectedSetOnOption = false;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
// TODO: direct imports like some-package/src/* are bad. Fix me.
|
||||
import ReactDebugCurrentFiber from 'react-reconciler/src/ReactDebugCurrentFiber';
|
||||
import warning from 'fbjs/lib/warning';
|
||||
import warning from 'shared/warning';
|
||||
|
||||
import ReactControlledValuePropTypes from '../shared/ReactControlledValuePropTypes';
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import warning from 'fbjs/lib/warning';
|
||||
import invariant from 'shared/invariant';
|
||||
import warning from 'shared/warning';
|
||||
// TODO: direct imports like some-package/src/* are bad. Fix me.
|
||||
import ReactDebugCurrentFiber from 'react-reconciler/src/ReactDebugCurrentFiber';
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as ReactScheduler from 'react-scheduler';
|
||||
import * as ReactScheduler from 'shared/ReactScheduler';
|
||||
|
||||
import * as ReactDOMComponentTree from './ReactDOMComponentTree';
|
||||
import * as ReactDOMFiberComponent from './ReactDOMFiberComponent';
|
||||
|
||||
@@ -5,11 +5,48 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import containsNode from 'fbjs/lib/containsNode';
|
||||
import getActiveElement from 'fbjs/lib/getActiveElement';
|
||||
import getActiveElement from './getActiveElement';
|
||||
|
||||
import * as ReactDOMSelection from './ReactDOMSelection';
|
||||
import {ELEMENT_NODE} from '../shared/HTMLNodeType';
|
||||
import {ELEMENT_NODE, TEXT_NODE} from '../shared/HTMLNodeType';
|
||||
|
||||
// TODO: this code is originally inlined from fbjs.
|
||||
// It is likely that we don't actually need all these checks
|
||||
// for the particular use case in this file.
|
||||
function isNode(object) {
|
||||
const doc = object ? object.ownerDocument || object : document;
|
||||
const defaultView = doc.defaultView || window;
|
||||
return !!(
|
||||
object &&
|
||||
(typeof defaultView.Node === 'function'
|
||||
? object instanceof defaultView.Node
|
||||
: typeof object === 'object' &&
|
||||
typeof object.nodeType === 'number' &&
|
||||
typeof object.nodeName === 'string')
|
||||
);
|
||||
}
|
||||
|
||||
function isTextNode(object) {
|
||||
return isNode(object) && object.nodeType === TEXT_NODE;
|
||||
}
|
||||
|
||||
function containsNode(outerNode, innerNode) {
|
||||
if (!outerNode || !innerNode) {
|
||||
return false;
|
||||
} else if (outerNode === innerNode) {
|
||||
return true;
|
||||
} else if (isTextNode(outerNode)) {
|
||||
return false;
|
||||
} else if (isTextNode(innerNode)) {
|
||||
return containsNode(outerNode, innerNode.parentNode);
|
||||
} else if ('contains' in outerNode) {
|
||||
return outerNode.contains(innerNode);
|
||||
} else if (outerNode.compareDocumentPosition) {
|
||||
return !!(outerNode.compareDocumentPosition(innerNode) & 16);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isInDocument(node) {
|
||||
return containsNode(document.documentElement, node);
|
||||
@@ -22,11 +59,21 @@ function isInDocument(node) {
|
||||
* Input selection module for React.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @hasSelectionCapabilities: we get the element types that support selection
|
||||
* from https://html.spec.whatwg.org/#do-not-apply, looking at `selectionStart`
|
||||
* and `selectionEnd` rows.
|
||||
*/
|
||||
export function hasSelectionCapabilities(elem) {
|
||||
const nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();
|
||||
return (
|
||||
nodeName &&
|
||||
((nodeName === 'input' && elem.type === 'text') ||
|
||||
((nodeName === 'input' &&
|
||||
(elem.type === 'text' ||
|
||||
elem.type === 'search' ||
|
||||
elem.type === 'tel' ||
|
||||
elem.type === 'url' ||
|
||||
elem.type === 'password')) ||
|
||||
nodeName === 'textarea' ||
|
||||
elem.contentEditable === 'true')
|
||||
);
|
||||
@@ -52,7 +99,10 @@ export function restoreSelection(priorSelectionInformation) {
|
||||
const priorFocusedElem = priorSelectionInformation.focusedElem;
|
||||
const priorSelectionRange = priorSelectionInformation.selectionRange;
|
||||
if (curFocusedElem !== priorFocusedElem && isInDocument(priorFocusedElem)) {
|
||||
if (hasSelectionCapabilities(priorFocusedElem)) {
|
||||
if (
|
||||
priorSelectionRange !== null &&
|
||||
hasSelectionCapabilities(priorFocusedElem)
|
||||
) {
|
||||
setSelection(priorFocusedElem, priorSelectionRange);
|
||||
}
|
||||
|
||||
@@ -69,7 +119,9 @@ export function restoreSelection(priorSelectionInformation) {
|
||||
}
|
||||
}
|
||||
|
||||
priorFocusedElem.focus();
|
||||
if (typeof priorFocusedElem.focus === 'function') {
|
||||
priorFocusedElem.focus();
|
||||
}
|
||||
|
||||
for (let i = 0; i < ancestors.length; i++) {
|
||||
const info = ancestors[i];
|
||||
|
||||
20
packages/react-dom/src/client/getActiveElement.js
vendored
Normal file
20
packages/react-dom/src/client/getActiveElement.js
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
export default function getActiveElement(doc: ?Document): ?Element {
|
||||
doc = doc || (typeof document !== 'undefined' ? document : undefined);
|
||||
if (typeof doc === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return doc.activeElement || doc.body;
|
||||
} catch (e) {
|
||||
return doc.body;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
|
||||
import {canUseDOM} from 'shared/ExecutionEnvironment';
|
||||
|
||||
let contentKey = null;
|
||||
|
||||
@@ -16,7 +16,7 @@ let contentKey = null;
|
||||
* @internal
|
||||
*/
|
||||
function getTextContentAccessor() {
|
||||
if (!contentKey && ExecutionEnvironment.canUseDOM) {
|
||||
if (!contentKey && canUseDOM) {
|
||||
// Prefer textContent to innerText because many browsers support both but
|
||||
// SVG <text> elements don't support innerText even when <div> does.
|
||||
contentKey =
|
||||
|
||||
@@ -5,13 +5,12 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import emptyFunction from 'fbjs/lib/emptyFunction';
|
||||
import warning from 'fbjs/lib/warning';
|
||||
import warning from 'shared/warning';
|
||||
// TODO: direct imports like some-package/src/* are bad. Fix me.
|
||||
import ReactDebugCurrentFiber from 'react-reconciler/src/ReactDebugCurrentFiber';
|
||||
|
||||
const {getCurrentFiberStackAddendum} = ReactDebugCurrentFiber;
|
||||
let validateDOMNesting = emptyFunction;
|
||||
let validateDOMNesting = () => {};
|
||||
|
||||
if (__DEV__) {
|
||||
// This validation code was written based on the HTML5 parsing spec:
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import type {TopLevelType} from 'events/TopLevelEventTypes';
|
||||
|
||||
import {accumulateTwoPhaseDispatches} from 'events/EventPropagators';
|
||||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
|
||||
import {canUseDOM} from 'shared/ExecutionEnvironment';
|
||||
|
||||
import {
|
||||
TOP_BLUR,
|
||||
@@ -29,11 +29,10 @@ import SyntheticInputEvent from './SyntheticInputEvent';
|
||||
const END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
|
||||
const START_KEYCODE = 229;
|
||||
|
||||
const canUseCompositionEvent =
|
||||
ExecutionEnvironment.canUseDOM && 'CompositionEvent' in window;
|
||||
const canUseCompositionEvent = canUseDOM && 'CompositionEvent' in window;
|
||||
|
||||
let documentMode = null;
|
||||
if (ExecutionEnvironment.canUseDOM && 'documentMode' in document) {
|
||||
if (canUseDOM && 'documentMode' in document) {
|
||||
documentMode = document.documentMode;
|
||||
}
|
||||
|
||||
@@ -41,13 +40,13 @@ if (ExecutionEnvironment.canUseDOM && 'documentMode' in document) {
|
||||
// directly represent `beforeInput`. The IE `textinput` event is not as
|
||||
// useful, so we don't use it.
|
||||
const canUseTextInputEvent =
|
||||
ExecutionEnvironment.canUseDOM && 'TextEvent' in window && !documentMode;
|
||||
canUseDOM && 'TextEvent' in window && !documentMode;
|
||||
|
||||
// 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.
|
||||
const useFallbackCompositionData =
|
||||
ExecutionEnvironment.canUseDOM &&
|
||||
canUseDOM &&
|
||||
(!canUseCompositionEvent ||
|
||||
(documentMode && documentMode > 8 && documentMode <= 11));
|
||||
|
||||
@@ -200,6 +199,20 @@ function getDataFromCustomEvent(nativeEvent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a composition event was triggered by Korean IME.
|
||||
* Our fallback mode does not work well with IE's Korean IME,
|
||||
* so just use native composition events when Korean IME is used.
|
||||
* Although CompositionEvent.locale property is deprecated,
|
||||
* it is available in IE, where our fallback mode is enabled.
|
||||
*
|
||||
* @param {object} nativeEvent
|
||||
* @return {boolean}
|
||||
*/
|
||||
function isUsingKoreanIME(nativeEvent) {
|
||||
return nativeEvent.locale === 'ko';
|
||||
}
|
||||
|
||||
// Track the current IME composition status, if any.
|
||||
let isComposing = false;
|
||||
|
||||
@@ -229,7 +242,7 @@ function extractCompositionEvent(
|
||||
return null;
|
||||
}
|
||||
|
||||
if (useFallbackCompositionData) {
|
||||
if (useFallbackCompositionData && !isUsingKoreanIME(nativeEvent)) {
|
||||
// The current composition is stored statically and must not be
|
||||
// overwritten while composition continues.
|
||||
if (!isComposing && eventType === eventTypes.compositionStart) {
|
||||
@@ -378,7 +391,9 @@ function getFallbackBeforeInputChars(topLevelType: TopLevelType, nativeEvent) {
|
||||
}
|
||||
return null;
|
||||
case TOP_COMPOSITION_END:
|
||||
return useFallbackCompositionData ? null : nativeEvent.data;
|
||||
return useFallbackCompositionData && !isUsingKoreanIME(nativeEvent)
|
||||
? null
|
||||
: nativeEvent.data;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {enqueueStateRestore} from 'events/ReactControlledComponent';
|
||||
import {batchedUpdates} from 'events/ReactGenericBatching';
|
||||
import SyntheticEvent from 'events/SyntheticEvent';
|
||||
import isTextInputElement from 'shared/isTextInputElement';
|
||||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
|
||||
import {canUseDOM} from 'shared/ExecutionEnvironment';
|
||||
|
||||
import {
|
||||
TOP_BLUR,
|
||||
@@ -119,7 +119,7 @@ function getTargetInstForChangeEvent(topLevelType, targetInst) {
|
||||
* SECTION: handle `input` event
|
||||
*/
|
||||
let isInputEventSupported = false;
|
||||
if (ExecutionEnvironment.canUseDOM) {
|
||||
if (canUseDOM) {
|
||||
// IE9 claims to support the input event but fails to trigger it when
|
||||
// deleting text, so we ignore its input events.
|
||||
isInputEventSupported =
|
||||
@@ -231,14 +231,8 @@ function getTargetInstForInputOrChangeEvent(topLevelType, targetInst) {
|
||||
}
|
||||
}
|
||||
|
||||
function handleControlledInputBlur(inst, node) {
|
||||
// TODO: In IE, inst is occasionally null. Why?
|
||||
if (inst == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fiber and ReactDOM keep wrapper state in separate places
|
||||
let state = inst._wrapperState || node._wrapperState;
|
||||
function handleControlledInputBlur(node) {
|
||||
let state = node._wrapperState;
|
||||
|
||||
if (!state || !state.controlled || node.type !== 'number') {
|
||||
return;
|
||||
@@ -303,7 +297,7 @@ const ChangeEventPlugin = {
|
||||
|
||||
// When blurring, set the value attribute for number inputs
|
||||
if (topLevelType === TOP_BLUR) {
|
||||
handleControlledInputBlur(targetInst, targetNode);
|
||||
handleControlledInputBlur(targetNode);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -6,11 +6,10 @@
|
||||
*/
|
||||
|
||||
import {accumulateTwoPhaseDispatches} from 'events/EventPropagators';
|
||||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
|
||||
import {canUseDOM} from 'shared/ExecutionEnvironment';
|
||||
import SyntheticEvent from 'events/SyntheticEvent';
|
||||
import isTextInputElement from 'shared/isTextInputElement';
|
||||
import getActiveElement from 'fbjs/lib/getActiveElement';
|
||||
import shallowEqual from 'fbjs/lib/shallowEqual';
|
||||
import shallowEqual from 'shared/shallowEqual';
|
||||
|
||||
import {
|
||||
TOP_BLUR,
|
||||
@@ -23,14 +22,13 @@ import {
|
||||
TOP_SELECTION_CHANGE,
|
||||
} from './DOMTopLevelEventTypes';
|
||||
import {isListeningToAllDependencies} from './ReactBrowserEventEmitter';
|
||||
import getActiveElement from '../client/getActiveElement';
|
||||
import {getNodeFromInstance} from '../client/ReactDOMComponentTree';
|
||||
import * as ReactInputSelection from '../client/ReactInputSelection';
|
||||
import {DOCUMENT_NODE} from '../shared/HTMLNodeType';
|
||||
|
||||
const skipSelectionChangeEvent =
|
||||
ExecutionEnvironment.canUseDOM &&
|
||||
'documentMode' in document &&
|
||||
document.documentMode <= 11;
|
||||
canUseDOM && 'documentMode' in document && document.documentMode <= 11;
|
||||
|
||||
const eventTypes = {
|
||||
select: {
|
||||
|
||||
@@ -22,7 +22,7 @@ import {accumulateTwoPhaseDispatches} from 'events/EventPropagators';
|
||||
import SyntheticEvent from 'events/SyntheticEvent';
|
||||
|
||||
import * as DOMTopLevelEventTypes from './DOMTopLevelEventTypes';
|
||||
import warning from 'fbjs/lib/warning';
|
||||
import warning from 'shared/warning';
|
||||
|
||||
import SyntheticAnimationEvent from './SyntheticAnimationEvent';
|
||||
import SyntheticClipboardEvent from './SyntheticClipboardEvent';
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
import SyntheticUIEvent from './SyntheticUIEvent';
|
||||
import getEventModifierState from './getEventModifierState';
|
||||
|
||||
let previousScreenX = 0;
|
||||
let previousScreenY = 0;
|
||||
// Use flags to signal movementX/Y has already been set
|
||||
let isMovementXSet = false;
|
||||
let isMovementYSet = false;
|
||||
|
||||
/**
|
||||
* @interface MouseEvent
|
||||
* @see http://www.w3.org/TR/DOM-Level-3-Events/
|
||||
@@ -34,6 +40,36 @@ const SyntheticMouseEvent = SyntheticUIEvent.extend({
|
||||
: event.fromElement)
|
||||
);
|
||||
},
|
||||
movementX: function(event) {
|
||||
if ('movementX' in event) {
|
||||
return event.movementX;
|
||||
}
|
||||
|
||||
const screenX = previousScreenX;
|
||||
previousScreenX = event.screenX;
|
||||
|
||||
if (!isMovementXSet) {
|
||||
isMovementXSet = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return event.type === 'mousemove' ? event.screenX - screenX : 0;
|
||||
},
|
||||
movementY: function(event) {
|
||||
if ('movementY' in event) {
|
||||
return event.movementY;
|
||||
}
|
||||
|
||||
const screenY = previousScreenY;
|
||||
previousScreenY = event.screenY;
|
||||
|
||||
if (!isMovementYSet) {
|
||||
isMovementYSet = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return event.type === 'mousemove' ? event.screenY - screenY : 0;
|
||||
},
|
||||
});
|
||||
|
||||
export default SyntheticMouseEvent;
|
||||
|
||||
16
packages/react-dom/src/events/TapEventPlugin.js
vendored
16
packages/react-dom/src/events/TapEventPlugin.js
vendored
@@ -8,7 +8,6 @@
|
||||
*/
|
||||
|
||||
import {accumulateTwoPhaseDispatches} from 'events/EventPropagators';
|
||||
import TouchEventUtils from 'fbjs/lib/TouchEventUtils';
|
||||
import type {TopLevelType} from 'events/TopLevelEventTypes';
|
||||
|
||||
import {
|
||||
@@ -85,11 +84,24 @@ const Axis: AxisType = {
|
||||
y: {page: 'pageY', client: 'clientY', envScroll: 'currentPageScrollTop'},
|
||||
};
|
||||
|
||||
function extractSingleTouch(nativeEvent) {
|
||||
// $FlowFixMe: figure out why this is missing
|
||||
const touches = nativeEvent.touches;
|
||||
// $FlowFixMe: figure out why this is missing
|
||||
const changedTouches = nativeEvent.changedTouches;
|
||||
const hasTouches = touches && touches.length > 0;
|
||||
const hasChangedTouches = changedTouches && changedTouches.length > 0;
|
||||
|
||||
return !hasTouches && hasChangedTouches
|
||||
? changedTouches[0]
|
||||
: hasTouches ? touches[0] : nativeEvent;
|
||||
}
|
||||
|
||||
function getAxisCoordOfEvent(
|
||||
axis: AxisCoordinateData,
|
||||
nativeEvent: _Touch,
|
||||
): number {
|
||||
const singleTouch = TouchEventUtils.extractSingleTouch(nativeEvent);
|
||||
const singleTouch = extractSingleTouch(nativeEvent);
|
||||
if (singleTouch) {
|
||||
return singleTouch[axis.page];
|
||||
}
|
||||
|
||||
@@ -200,9 +200,9 @@ describe('BeforeInputEventPlugin', () => {
|
||||
spyOnBeforeInput,
|
||||
spyOnCompositionStart,
|
||||
}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
expect(spyOnCompositionStart.mock.calls.length).toBe(1);
|
||||
expect(spyOnCompositionStart).toHaveBeenCalledTimes(1);
|
||||
expect(compositionStartEvent.type).toBe('compositionstart');
|
||||
expect(compositionStartEvent.data).toBe('test');
|
||||
},
|
||||
@@ -214,116 +214,116 @@ describe('BeforeInputEventPlugin', () => {
|
||||
spyOnBeforeInput,
|
||||
spyOnCompositionUpdate,
|
||||
}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
expect(spyOnCompositionUpdate.mock.calls.length).toBe(1);
|
||||
expect(spyOnCompositionUpdate).toHaveBeenCalledTimes(1);
|
||||
expect(compositionUpdateEvent.type).toBe('compositionupdate');
|
||||
expect(compositionUpdateEvent.data).toBe('test string');
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(1);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
|
||||
expect(beforeInputEvent.type).toBe('compositionend');
|
||||
expect(beforeInputEvent.data).toBe('test string 3');
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(1);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
|
||||
expect(beforeInputEvent.type).toBe('textInput');
|
||||
expect(beforeInputEvent.data).toBe('abcß');
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(1);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
|
||||
expect(beforeInputEvent.type).toBe('keypress');
|
||||
expect(beforeInputEvent.data).toBe(' ');
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(1);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
|
||||
expect(beforeInputEvent.type).toBe('textInput');
|
||||
expect(beforeInputEvent.data).toBe('\uD83D\uDE0A');
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
@@ -334,119 +334,119 @@ describe('BeforeInputEventPlugin', () => {
|
||||
assertions: [
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(1);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
|
||||
expect(beforeInputEvent.type).toBe('keypress');
|
||||
expect(beforeInputEvent.data).toBe('a');
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(1);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
|
||||
expect(beforeInputEvent.type).toBe('keypress');
|
||||
expect(beforeInputEvent.data).toBe(' ');
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(1);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
|
||||
expect(beforeInputEvent.type).toBe('keypress');
|
||||
expect(beforeInputEvent.data).toBe('c');
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(1);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
|
||||
expect(beforeInputEvent.type).toBe('keypress');
|
||||
expect(beforeInputEvent.data).toBe('\uD83D\uDE0A');
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
@@ -457,122 +457,122 @@ describe('BeforeInputEventPlugin', () => {
|
||||
assertions: [
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(1);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
|
||||
expect(beforeInputEvent.type).toBe('keypress');
|
||||
expect(beforeInputEvent.data).toBe('a');
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(1);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
|
||||
expect(beforeInputEvent.type).toBe('keypress');
|
||||
expect(beforeInputEvent.data).toBe(' ');
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(1);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
|
||||
expect(beforeInputEvent.type).toBe('keypress');
|
||||
expect(beforeInputEvent.data).toBe('c');
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(1);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
|
||||
expect(beforeInputEvent.type).toBe('keypress');
|
||||
expect(beforeInputEvent.data).toBe('\uD83D\uDE0A');
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(1);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
|
||||
expect(beforeInputEvent.type).toBe('keydown');
|
||||
expect(beforeInputEvent.data).toBe('bar');
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(1);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
|
||||
expect(beforeInputEvent.type).toBe('keyup');
|
||||
expect(beforeInputEvent.data).toBe('BAR');
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(1);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
|
||||
expect(beforeInputEvent.type).toBe('keypress');
|
||||
expect(beforeInputEvent.data).toBe('Bar');
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
@@ -583,120 +583,120 @@ describe('BeforeInputEventPlugin', () => {
|
||||
assertions: [
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(1);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
|
||||
expect(beforeInputEvent.type).toBe('compositionend');
|
||||
expect(beforeInputEvent.data).toBe('test string 3');
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(1);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
|
||||
expect(beforeInputEvent.type).toBe('keypress');
|
||||
expect(beforeInputEvent.data).toBe('a');
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(1);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
|
||||
expect(beforeInputEvent.type).toBe('keypress');
|
||||
expect(beforeInputEvent.data).toBe(' ');
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(1);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
|
||||
expect(beforeInputEvent.type).toBe('keypress');
|
||||
expect(beforeInputEvent.data).toBe('c');
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(1);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
|
||||
expect(beforeInputEvent.type).toBe('keypress');
|
||||
expect(beforeInputEvent.data).toBe('\uD83D\uDE0A');
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
{
|
||||
run: ({beforeInputEvent, spyOnBeforeInput}) => {
|
||||
expect(spyOnBeforeInput.mock.calls.length).toBe(0);
|
||||
expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
|
||||
expect(beforeInputEvent).toBeNull();
|
||||
},
|
||||
},
|
||||
|
||||
@@ -72,7 +72,7 @@ describe('SelectEventPlugin', () => {
|
||||
// Verify that it doesn't get "stuck" waiting for
|
||||
// a `mouseup` event that it has "missed" because
|
||||
// a top-level listener didn't exist yet.
|
||||
expect(select.mock.calls.length).toBe(1);
|
||||
expect(select).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should fire `onSelect` when a listener is present', () => {
|
||||
@@ -95,17 +95,17 @@ describe('SelectEventPlugin', () => {
|
||||
cancelable: true,
|
||||
});
|
||||
node.dispatchEvent(nativeEvent);
|
||||
expect(select.mock.calls.length).toBe(0);
|
||||
expect(select).toHaveBeenCalledTimes(0);
|
||||
|
||||
nativeEvent = new MouseEvent('mousedown', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
node.dispatchEvent(nativeEvent);
|
||||
expect(select.mock.calls.length).toBe(0);
|
||||
expect(select).toHaveBeenCalledTimes(0);
|
||||
|
||||
nativeEvent = new MouseEvent('mouseup', {bubbles: true, cancelable: true});
|
||||
node.dispatchEvent(nativeEvent);
|
||||
expect(select.mock.calls.length).toBe(1);
|
||||
expect(select).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,23 +12,25 @@
|
||||
describe('SimpleEventPlugin', function() {
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactTestUtils;
|
||||
let ReactFeatureFlags;
|
||||
|
||||
let onClick;
|
||||
let container;
|
||||
|
||||
function expectClickThru(element) {
|
||||
ReactTestUtils.SimulateNative.click(ReactDOM.findDOMNode(element));
|
||||
expect(onClick.mock.calls.length).toBe(1);
|
||||
element.click();
|
||||
expect(onClick).toHaveBeenCalledTimes(1);
|
||||
}
|
||||
|
||||
function expectNoClickThru(element) {
|
||||
ReactTestUtils.SimulateNative.click(ReactDOM.findDOMNode(element));
|
||||
expect(onClick.mock.calls.length).toBe(0);
|
||||
element.click();
|
||||
expect(onClick).toHaveBeenCalledTimes(0);
|
||||
}
|
||||
|
||||
function mounted(element) {
|
||||
element = ReactTestUtils.renderIntoDocument(element);
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
element = ReactDOM.render(element, container);
|
||||
return element;
|
||||
}
|
||||
|
||||
@@ -60,76 +62,81 @@ describe('SimpleEventPlugin', function() {
|
||||
jest.resetModules();
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactTestUtils = require('react-dom/test-utils');
|
||||
|
||||
onClick = jest.fn();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (container && document.body.contains(container)) {
|
||||
document.body.removeChild(container);
|
||||
container = null;
|
||||
}
|
||||
});
|
||||
|
||||
it('A non-interactive tags click when disabled', function() {
|
||||
const element = <div onClick={onClick} />;
|
||||
expectClickThru(mounted(element));
|
||||
});
|
||||
|
||||
it('A non-interactive tags clicks bubble when disabled', function() {
|
||||
const element = ReactTestUtils.renderIntoDocument(
|
||||
const element = mounted(
|
||||
<div onClick={onClick}>
|
||||
<div />
|
||||
</div>,
|
||||
);
|
||||
const child = ReactDOM.findDOMNode(element).firstChild;
|
||||
|
||||
ReactTestUtils.SimulateNative.click(child);
|
||||
expect(onClick.mock.calls.length).toBe(1);
|
||||
const child = element.firstChild;
|
||||
child.click();
|
||||
expect(onClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('does not register a click when clicking a child of a disabled element', function() {
|
||||
const element = ReactTestUtils.renderIntoDocument(
|
||||
const element = mounted(
|
||||
<button onClick={onClick} disabled={true}>
|
||||
<span />
|
||||
</button>,
|
||||
);
|
||||
const child = ReactDOM.findDOMNode(element).querySelector('span');
|
||||
const child = element.querySelector('span');
|
||||
|
||||
ReactTestUtils.SimulateNative.click(child);
|
||||
expect(onClick.mock.calls.length).toBe(0);
|
||||
child.click();
|
||||
expect(onClick).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('triggers click events for children of disabled elements', function() {
|
||||
const element = ReactTestUtils.renderIntoDocument(
|
||||
const element = mounted(
|
||||
<button disabled={true}>
|
||||
<span onClick={onClick} />
|
||||
</button>,
|
||||
);
|
||||
const child = ReactDOM.findDOMNode(element).querySelector('span');
|
||||
const child = element.querySelector('span');
|
||||
|
||||
ReactTestUtils.SimulateNative.click(child);
|
||||
expect(onClick.mock.calls.length).toBe(1);
|
||||
child.click();
|
||||
expect(onClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('triggers parent captured click events when target is a child of a disabled elements', function() {
|
||||
const element = ReactTestUtils.renderIntoDocument(
|
||||
const element = mounted(
|
||||
<div onClickCapture={onClick}>
|
||||
<button disabled={true}>
|
||||
<span />
|
||||
</button>
|
||||
</div>,
|
||||
);
|
||||
const child = ReactDOM.findDOMNode(element).querySelector('span');
|
||||
const child = element.querySelector('span');
|
||||
|
||||
ReactTestUtils.SimulateNative.click(child);
|
||||
expect(onClick.mock.calls.length).toBe(1);
|
||||
child.click();
|
||||
expect(onClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('triggers captured click events for children of disabled elements', function() {
|
||||
const element = ReactTestUtils.renderIntoDocument(
|
||||
const element = mounted(
|
||||
<button disabled={true}>
|
||||
<span onClickCapture={onClick} />
|
||||
</button>,
|
||||
);
|
||||
const child = ReactDOM.findDOMNode(element).querySelector('span');
|
||||
const child = element.querySelector('span');
|
||||
|
||||
ReactTestUtils.SimulateNative.click(child);
|
||||
expect(onClick.mock.calls.length).toBe(1);
|
||||
child.click();
|
||||
expect(onClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
['button', 'input', 'select', 'textarea'].forEach(function(tagName) {
|
||||
@@ -152,7 +159,8 @@ describe('SimpleEventPlugin', function() {
|
||||
});
|
||||
|
||||
it('should forward clicks when it becomes not disabled', () => {
|
||||
const container = document.createElement('div');
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
let element = ReactDOM.render(
|
||||
React.createElement(tagName, {onClick: onClick, disabled: true}),
|
||||
container,
|
||||
@@ -165,7 +173,8 @@ describe('SimpleEventPlugin', function() {
|
||||
});
|
||||
|
||||
it('should not forward clicks when it becomes disabled', () => {
|
||||
const container = document.createElement('div');
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
let element = ReactDOM.render(
|
||||
React.createElement(tagName, {onClick: onClick}),
|
||||
container,
|
||||
@@ -178,7 +187,8 @@ describe('SimpleEventPlugin', function() {
|
||||
});
|
||||
|
||||
it('should work correctly if the listener is changed', () => {
|
||||
const container = document.createElement('div');
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
let element = ReactDOM.render(
|
||||
React.createElement(tagName, {onClick: onClick, disabled: true}),
|
||||
container,
|
||||
@@ -193,7 +203,7 @@ describe('SimpleEventPlugin', function() {
|
||||
});
|
||||
|
||||
it('batches updates that occur as a result of a nested event dispatch', () => {
|
||||
const container = document.createElement('div');
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
|
||||
let ops = [];
|
||||
@@ -251,7 +261,7 @@ describe('SimpleEventPlugin', function() {
|
||||
});
|
||||
|
||||
it('flushes pending interactive work before extracting event handler', () => {
|
||||
const container = document.createElement('div');
|
||||
container = document.createElement('div');
|
||||
const root = ReactDOM.unstable_createRoot(container);
|
||||
document.body.appendChild(container);
|
||||
|
||||
@@ -331,7 +341,7 @@ describe('SimpleEventPlugin', function() {
|
||||
});
|
||||
|
||||
it('end result of many interactive updates is deterministic', () => {
|
||||
const container = document.createElement('div');
|
||||
container = document.createElement('div');
|
||||
const root = ReactDOM.unstable_createRoot(container);
|
||||
document.body.appendChild(container);
|
||||
|
||||
@@ -386,7 +396,7 @@ describe('SimpleEventPlugin', function() {
|
||||
});
|
||||
|
||||
it('flushes lowest pending interactive priority', () => {
|
||||
const container = document.createElement('div');
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
|
||||
let button;
|
||||
@@ -459,7 +469,7 @@ describe('SimpleEventPlugin', 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() {
|
||||
const container = document.createElement('div');
|
||||
container = document.createElement('div');
|
||||
|
||||
ReactDOM.render(<button onClick={onClick} />, container);
|
||||
|
||||
@@ -467,11 +477,11 @@ describe('SimpleEventPlugin', function() {
|
||||
|
||||
node.dispatchEvent(new MouseEvent('click'));
|
||||
|
||||
expect(onClick.mock.calls.length).toBe(0);
|
||||
expect(onClick).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('adds a local click listener to non-interactive elements', function() {
|
||||
const container = document.createElement('div');
|
||||
container = document.createElement('div');
|
||||
|
||||
ReactDOM.render(<div onClick={onClick} />, container);
|
||||
|
||||
@@ -479,7 +489,7 @@ describe('SimpleEventPlugin', function() {
|
||||
|
||||
node.dispatchEvent(new MouseEvent('click'));
|
||||
|
||||
expect(onClick.mock.calls.length).toBe(0);
|
||||
expect(onClick).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactTestUtils;
|
||||
|
||||
describe('SyntheticEvent', () => {
|
||||
let container;
|
||||
@@ -19,7 +18,6 @@ describe('SyntheticEvent', () => {
|
||||
beforeEach(() => {
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactTestUtils = require('react-dom/test-utils');
|
||||
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
@@ -245,17 +243,13 @@ describe('SyntheticEvent', () => {
|
||||
expect(expectedCount).toBe(1);
|
||||
});
|
||||
|
||||
// TODO: reenable this test. We are currently silencing these warnings when
|
||||
// using TestUtils.Simulate to avoid spurious warnings that result from the
|
||||
// way we simulate events.
|
||||
xit('should properly log warnings when events simulated with rendered components', () => {
|
||||
it('should properly log warnings when events simulated with rendered components', () => {
|
||||
let event;
|
||||
const element = document.createElement('div');
|
||||
function assignEvent(e) {
|
||||
event = e;
|
||||
}
|
||||
const node = ReactDOM.render(<div onClick={assignEvent} />, element);
|
||||
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(node));
|
||||
const node = ReactDOM.render(<div onClick={assignEvent} />, container);
|
||||
node.click();
|
||||
|
||||
// access a property to cause the warning
|
||||
expect(() => {
|
||||
|
||||
80
packages/react-dom/src/events/__tests__/SyntheticMouseEvent-test.js
vendored
Normal file
80
packages/react-dom/src/events/__tests__/SyntheticMouseEvent-test.js
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
let React;
|
||||
let ReactDOM;
|
||||
|
||||
describe('SyntheticMouseEvent', () => {
|
||||
let container;
|
||||
|
||||
beforeEach(() => {
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
|
||||
// The container has to be attached for events to fire.
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
document.body.removeChild(container);
|
||||
container = null;
|
||||
});
|
||||
|
||||
it('should only use values from movementX/Y when event type is mousemove', () => {
|
||||
const events = [];
|
||||
const onMouseMove = event => {
|
||||
events.push(event.movementX);
|
||||
};
|
||||
|
||||
const onMouseDown = event => {
|
||||
events.push(event.movementX);
|
||||
};
|
||||
|
||||
const node = ReactDOM.render(
|
||||
<div onMouseMove={onMouseMove} onMouseDown={onMouseDown} />,
|
||||
container,
|
||||
);
|
||||
|
||||
let event = new MouseEvent('mousemove', {
|
||||
relatedTarget: null,
|
||||
bubbles: true,
|
||||
screenX: 2,
|
||||
screenY: 2,
|
||||
});
|
||||
|
||||
node.dispatchEvent(event);
|
||||
|
||||
event = new MouseEvent('mousemove', {
|
||||
relatedTarget: null,
|
||||
bubbles: true,
|
||||
screenX: 8,
|
||||
screenY: 8,
|
||||
});
|
||||
|
||||
node.dispatchEvent(event);
|
||||
|
||||
// Now trigger a mousedown event to see if movementX has changed back to 0
|
||||
event = new MouseEvent('mousedown', {
|
||||
relatedTarget: null,
|
||||
bubbles: true,
|
||||
screenX: 25,
|
||||
screenY: 65,
|
||||
});
|
||||
|
||||
node.dispatchEvent(event);
|
||||
|
||||
expect(events.length).toBe(3);
|
||||
expect(events[0]).toBe(0);
|
||||
expect(events[1]).toBe(6);
|
||||
expect(events[2]).toBe(0); // mousedown event should have movementX at 0
|
||||
});
|
||||
});
|
||||
@@ -28,6 +28,27 @@ let PARENT;
|
||||
let CHILD;
|
||||
|
||||
let putListener;
|
||||
let container;
|
||||
|
||||
function touchStart(element, touchEventInit) {
|
||||
element.dispatchEvent(
|
||||
new TouchEvent('touchstart', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
...touchEventInit,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function touchEnd(element, touchEventInit) {
|
||||
element.dispatchEvent(
|
||||
new TouchEvent('touchend', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
...touchEventInit,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
describe('TapEventPlugin', () => {
|
||||
beforeEach(() => {
|
||||
@@ -40,7 +61,8 @@ describe('TapEventPlugin', () => {
|
||||
ReactTestUtils = require('react-dom/test-utils');
|
||||
TapEventPlugin = require('react-dom/src/events/TapEventPlugin').default;
|
||||
|
||||
const container = document.createElement('div');
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
|
||||
const GRANDPARENT_PROPS = {};
|
||||
const PARENT_PROPS = {};
|
||||
@@ -91,6 +113,11 @@ describe('TapEventPlugin', () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
document.body.removeChild(container);
|
||||
container = null;
|
||||
});
|
||||
|
||||
/**
|
||||
* The onTouchTap inject is ignore future,
|
||||
* we should always test the deprecated message correct.
|
||||
@@ -99,42 +126,24 @@ describe('TapEventPlugin', () => {
|
||||
|
||||
it('should infer onTouchTap from a touchStart/End', () => {
|
||||
putListener(CHILD, ON_TOUCH_TAP_KEY, recordID.bind(null, CHILD));
|
||||
ReactTestUtils.SimulateNative.touchStart(
|
||||
CHILD,
|
||||
ReactTestUtils.nativeTouchData(0, 0),
|
||||
);
|
||||
ReactTestUtils.SimulateNative.touchEnd(
|
||||
CHILD,
|
||||
ReactTestUtils.nativeTouchData(0, 0),
|
||||
);
|
||||
touchStart(CHILD, ReactTestUtils.nativeTouchData(0, 0));
|
||||
touchEnd(CHILD, ReactTestUtils.nativeTouchData(0, 0));
|
||||
expect(idCallOrder.length).toBe(1);
|
||||
expect(idCallOrder[0]).toBe(CHILD);
|
||||
});
|
||||
|
||||
it('should infer onTouchTap from when dragging below threshold', () => {
|
||||
putListener(CHILD, ON_TOUCH_TAP_KEY, recordID.bind(null, CHILD));
|
||||
ReactTestUtils.SimulateNative.touchStart(
|
||||
CHILD,
|
||||
ReactTestUtils.nativeTouchData(0, 0),
|
||||
);
|
||||
ReactTestUtils.SimulateNative.touchEnd(
|
||||
CHILD,
|
||||
ReactTestUtils.nativeTouchData(0, tapMoveThreshold - 1),
|
||||
);
|
||||
touchStart(CHILD, ReactTestUtils.nativeTouchData(0, 0));
|
||||
touchEnd(CHILD, ReactTestUtils.nativeTouchData(0, tapMoveThreshold - 1));
|
||||
expect(idCallOrder.length).toBe(1);
|
||||
expect(idCallOrder[0]).toBe(CHILD);
|
||||
});
|
||||
|
||||
it('should not onTouchTap from when dragging beyond threshold', () => {
|
||||
putListener(CHILD, ON_TOUCH_TAP_KEY, recordID.bind(null, CHILD));
|
||||
ReactTestUtils.SimulateNative.touchStart(
|
||||
CHILD,
|
||||
ReactTestUtils.nativeTouchData(0, 0),
|
||||
);
|
||||
ReactTestUtils.SimulateNative.touchEnd(
|
||||
CHILD,
|
||||
ReactTestUtils.nativeTouchData(0, tapMoveThreshold + 1),
|
||||
);
|
||||
touchStart(CHILD, ReactTestUtils.nativeTouchData(0, 0));
|
||||
touchEnd(CHILD, ReactTestUtils.nativeTouchData(0, tapMoveThreshold + 1));
|
||||
expect(idCallOrder.length).toBe(0);
|
||||
});
|
||||
|
||||
@@ -146,14 +155,8 @@ describe('TapEventPlugin', () => {
|
||||
ON_TOUCH_TAP_KEY,
|
||||
recordID.bind(null, GRANDPARENT),
|
||||
);
|
||||
ReactTestUtils.SimulateNative.touchStart(
|
||||
CHILD,
|
||||
ReactTestUtils.nativeTouchData(0, 0),
|
||||
);
|
||||
ReactTestUtils.SimulateNative.touchEnd(
|
||||
CHILD,
|
||||
ReactTestUtils.nativeTouchData(0, 0),
|
||||
);
|
||||
touchStart(CHILD, ReactTestUtils.nativeTouchData(0, 0));
|
||||
touchEnd(CHILD, ReactTestUtils.nativeTouchData(0, 0));
|
||||
expect(idCallOrder.length).toBe(3);
|
||||
expect(idCallOrder[0] === CHILD).toBe(true);
|
||||
expect(idCallOrder[1] === PARENT).toBe(true);
|
||||
|
||||
@@ -15,7 +15,9 @@ import {TEXT_NODE} from '../shared/HTMLNodeType';
|
||||
* @return {DOMEventTarget} Target node.
|
||||
*/
|
||||
function getEventTarget(nativeEvent) {
|
||||
let target = nativeEvent.target || window;
|
||||
// Fallback to nativeEvent.srcElement for IE9
|
||||
// https://github.com/facebook/react/issues/12506
|
||||
let target = nativeEvent.target || nativeEvent.srcElement || window;
|
||||
|
||||
// Normalize SVG <use> element events #4963
|
||||
if (target.correspondingUseElement) {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
|
||||
import {canUseDOM} from 'shared/ExecutionEnvironment';
|
||||
|
||||
/**
|
||||
* Generate a mapping of standard vendor prefixes using the defined style property and event name.
|
||||
@@ -49,7 +49,7 @@ let style = {};
|
||||
/**
|
||||
* Bootstrap if a DOM exists.
|
||||
*/
|
||||
if (ExecutionEnvironment.canUseDOM) {
|
||||
if (canUseDOM) {
|
||||
style = document.createElement('div').style;
|
||||
|
||||
// On some platforms, in particular some releases of Android 4.x,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
|
||||
import {canUseDOM} from 'shared/ExecutionEnvironment';
|
||||
|
||||
/**
|
||||
* Checks if an event is supported in the current execution environment.
|
||||
@@ -22,10 +22,7 @@ import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
|
||||
* @license Modernizr 3.0.0pre (Custom Build) | MIT
|
||||
*/
|
||||
function isEventSupported(eventNameSuffix, capture) {
|
||||
if (
|
||||
!ExecutionEnvironment.canUseDOM ||
|
||||
(capture && !('addEventListener' in document))
|
||||
) {
|
||||
if (!canUseDOM || (capture && !('addEventListener' in document))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import ReactVersion from 'shared/ReactVersion';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import invariant from 'shared/invariant';
|
||||
|
||||
import {renderToString, renderToStaticMarkup} from './ReactDOMStringRenderer';
|
||||
|
||||
|
||||
@@ -15,13 +15,9 @@ import type {
|
||||
} from 'shared/ReactTypes';
|
||||
|
||||
import React from 'react';
|
||||
import emptyFunction from 'fbjs/lib/emptyFunction';
|
||||
import emptyObject from 'fbjs/lib/emptyObject';
|
||||
import hyphenateStyleName from 'fbjs/lib/hyphenateStyleName';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import invariant from 'shared/invariant';
|
||||
import lowPriorityWarning from 'shared/lowPriorityWarning';
|
||||
import memoizeStringOnly from 'fbjs/lib/memoizeStringOnly';
|
||||
import warning from 'fbjs/lib/warning';
|
||||
import warning from 'shared/warning';
|
||||
import checkPropTypes from 'prop-types/checkPropTypes';
|
||||
import describeComponentFrame from 'shared/describeComponentFrame';
|
||||
import {ReactDebugCurrentFrame} from 'shared/ReactGlobalSharedState';
|
||||
@@ -51,6 +47,7 @@ import {
|
||||
import ReactControlledValuePropTypes from '../shared/ReactControlledValuePropTypes';
|
||||
import assertValidProps from '../shared/assertValidProps';
|
||||
import dangerousStyleValue from '../shared/dangerousStyleValue';
|
||||
import hyphenateStyleName from '../shared/hyphenateStyleName';
|
||||
import isCustomComponent from '../shared/isCustomComponent';
|
||||
import omittedCloseTags from '../shared/omittedCloseTags';
|
||||
import warnValidStyle from '../shared/warnValidStyle';
|
||||
@@ -67,8 +64,8 @@ const toArray = ((React.Children.toArray: any): toArrayType);
|
||||
let currentDebugStack;
|
||||
let currentDebugElementStack;
|
||||
|
||||
let getStackAddendum = emptyFunction.thatReturns('');
|
||||
let describeStackFrame = emptyFunction.thatReturns('');
|
||||
let getStackAddendum = () => '';
|
||||
let describeStackFrame = element => '';
|
||||
|
||||
let validatePropertiesInDevelopment = (type, props) => {};
|
||||
let setCurrentDebugStack = (stack: Array<Frame>) => {};
|
||||
@@ -162,9 +159,15 @@ function validateDangerousTag(tag) {
|
||||
}
|
||||
}
|
||||
|
||||
const processStyleName = memoizeStringOnly(function(styleName) {
|
||||
return hyphenateStyleName(styleName);
|
||||
});
|
||||
const styleNameCache = {};
|
||||
const processStyleName = function(styleName) {
|
||||
if (styleNameCache.hasOwnProperty(styleName)) {
|
||||
return styleNameCache[styleName];
|
||||
}
|
||||
const result = hyphenateStyleName(styleName);
|
||||
styleNameCache[styleName] = result;
|
||||
return result;
|
||||
};
|
||||
|
||||
function createMarkupForStyles(styles): string | null {
|
||||
let serialized = '';
|
||||
@@ -255,7 +258,10 @@ function flattenTopLevelChildren(children: mixed): FlatReactChildren {
|
||||
return [fragmentChildElement];
|
||||
}
|
||||
|
||||
function flattenOptionChildren(children: mixed): string {
|
||||
function flattenOptionChildren(children: mixed): ?string {
|
||||
if (children === undefined || children === null) {
|
||||
return children;
|
||||
}
|
||||
let content = '';
|
||||
// Flatten children and warn if they aren't strings or numbers;
|
||||
// invalid types are ignored.
|
||||
@@ -280,6 +286,11 @@ function flattenOptionChildren(children: mixed): string {
|
||||
return content;
|
||||
}
|
||||
|
||||
const emptyObject = {};
|
||||
if (__DEV__) {
|
||||
Object.freeze(emptyObject);
|
||||
}
|
||||
|
||||
function maskContext(type, context) {
|
||||
const contextTypes = type.contextTypes;
|
||||
if (!contextTypes) {
|
||||
@@ -641,8 +652,10 @@ class ReactDOMServerRenderer {
|
||||
previousWasTextNode: boolean;
|
||||
makeStaticMarkup: boolean;
|
||||
|
||||
providerStack: Array<?ReactProvider<any>>;
|
||||
providerIndex: number;
|
||||
contextIndex: number;
|
||||
contextStack: Array<ReactContext<any>>;
|
||||
contextValueStack: Array<any>;
|
||||
contextProviderStack: ?Array<ReactProvider<any>>; // DEV-only
|
||||
|
||||
constructor(children: mixed, makeStaticMarkup: boolean) {
|
||||
const flatChildren = flattenTopLevelChildren(children);
|
||||
@@ -667,37 +680,65 @@ class ReactDOMServerRenderer {
|
||||
this.makeStaticMarkup = makeStaticMarkup;
|
||||
|
||||
// Context (new API)
|
||||
this.providerStack = []; // Stack of provider objects
|
||||
this.providerIndex = -1;
|
||||
this.contextIndex = -1;
|
||||
this.contextStack = [];
|
||||
this.contextValueStack = [];
|
||||
if (__DEV__) {
|
||||
this.contextProviderStack = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: We use just two stacks regardless of how many context providers you have.
|
||||
* Providers are always popped in the reverse order to how they were pushed
|
||||
* so we always know on the way down which provider you'll encounter next on the way up.
|
||||
* On the way down, we push the current provider, and its context value *before*
|
||||
* we mutated it, onto the stacks. Therefore, on the way up, we always know which
|
||||
* provider needs to be "restored" to which value.
|
||||
* https://github.com/facebook/react/pull/12985#issuecomment-396301248
|
||||
*/
|
||||
|
||||
pushProvider<T>(provider: ReactProvider<T>): void {
|
||||
this.providerIndex += 1;
|
||||
this.providerStack[this.providerIndex] = provider;
|
||||
const index = ++this.contextIndex;
|
||||
const context: ReactContext<any> = provider.type._context;
|
||||
const previousValue = context._currentValue;
|
||||
|
||||
// Remember which value to restore this context to on our way up.
|
||||
this.contextStack[index] = context;
|
||||
this.contextValueStack[index] = previousValue;
|
||||
if (__DEV__) {
|
||||
// Only used for push/pop mismatch warnings.
|
||||
(this.contextProviderStack: any)[index] = provider;
|
||||
}
|
||||
|
||||
// Mutate the current value.
|
||||
context._currentValue = provider.props.value;
|
||||
}
|
||||
|
||||
popProvider<T>(provider: ReactProvider<T>): void {
|
||||
const index = this.contextIndex;
|
||||
if (__DEV__) {
|
||||
warning(
|
||||
this.providerIndex > -1 &&
|
||||
provider === this.providerStack[this.providerIndex],
|
||||
index > -1 && provider === (this.contextProviderStack: any)[index],
|
||||
'Unexpected pop.',
|
||||
);
|
||||
}
|
||||
this.providerStack[this.providerIndex] = null;
|
||||
this.providerIndex -= 1;
|
||||
const context: ReactContext<any> = provider.type._context;
|
||||
if (this.providerIndex < 0) {
|
||||
context._currentValue = context._defaultValue;
|
||||
} else {
|
||||
// We assume this type is correct because of the index check above.
|
||||
const previousProvider: ReactProvider<any> = (this.providerStack[
|
||||
this.providerIndex
|
||||
]: any);
|
||||
context._currentValue = previousProvider.props.value;
|
||||
|
||||
const context: ReactContext<any> = this.contextStack[index];
|
||||
const previousValue = this.contextValueStack[index];
|
||||
|
||||
// "Hide" these null assignments from Flow by using `any`
|
||||
// because conceptually they are deletions--as long as we
|
||||
// promise to never access values beyond `this.contextIndex`.
|
||||
this.contextStack[index] = (null: any);
|
||||
this.contextValueStack[index] = (null: any);
|
||||
if (__DEV__) {
|
||||
(this.contextProviderStack: any)[index] = (null: any);
|
||||
}
|
||||
this.contextIndex--;
|
||||
|
||||
// Restore to the previous value we stored as we were walking down.
|
||||
context._currentValue = previousValue;
|
||||
}
|
||||
|
||||
read(bytes: number): string | null {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import dangerousStyleValue from './dangerousStyleValue';
|
||||
import hyphenateStyleName from 'fbjs/lib/hyphenateStyleName';
|
||||
import hyphenateStyleName from './hyphenateStyleName';
|
||||
import warnValidStyle from './warnValidStyle';
|
||||
|
||||
/**
|
||||
|
||||
2
packages/react-dom/src/shared/DOMProperty.js
vendored
2
packages/react-dom/src/shared/DOMProperty.js
vendored
@@ -7,7 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import warning from 'fbjs/lib/warning';
|
||||
import warning from 'shared/warning';
|
||||
|
||||
type PropertyType = 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import warning from 'fbjs/lib/warning';
|
||||
import warning from 'shared/warning';
|
||||
import {ReactDebugCurrentFrame} from 'shared/ReactGlobalSharedState';
|
||||
|
||||
import {ATTRIBUTE_NAME_CHAR} from './DOMProperty';
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import {ReactDebugCurrentFrame} from 'shared/ReactGlobalSharedState';
|
||||
import warning from 'fbjs/lib/warning';
|
||||
import warning from 'shared/warning';
|
||||
|
||||
let didWarnValueNull = false;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
possibleRegistrationNames,
|
||||
} from 'events/EventPluginRegistry';
|
||||
import {ReactDebugCurrentFrame} from 'shared/ReactGlobalSharedState';
|
||||
import warning from 'fbjs/lib/warning';
|
||||
import warning from 'shared/warning';
|
||||
|
||||
import {
|
||||
ATTRIBUTE_NAME_CHAR,
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import warning from 'fbjs/lib/warning';
|
||||
import invariant from 'shared/invariant';
|
||||
import warning from 'shared/warning';
|
||||
|
||||
import voidElementTags from './voidElementTags';
|
||||
|
||||
|
||||
2
packages/react-dom/src/shared/checkReact.js
vendored
2
packages/react-dom/src/shared/checkReact.js
vendored
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import invariant from 'shared/invariant';
|
||||
|
||||
invariant(
|
||||
React,
|
||||
|
||||
31
packages/react-dom/src/shared/hyphenateStyleName.js
vendored
Normal file
31
packages/react-dom/src/shared/hyphenateStyleName.js
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
const uppercasePattern = /([A-Z])/g;
|
||||
const msPattern = /^ms-/;
|
||||
|
||||
/**
|
||||
* Hyphenates a camelcased CSS property name, for example:
|
||||
*
|
||||
* > hyphenateStyleName('backgroundColor')
|
||||
* < "background-color"
|
||||
* > hyphenateStyleName('MozTransition')
|
||||
* < "-moz-transition"
|
||||
* > hyphenateStyleName('msTransition')
|
||||
* < "-ms-transition"
|
||||
*
|
||||
* As Modernizr suggests (http://modernizr.com/docs/#prefixed), an `ms` prefix
|
||||
* is converted to `-ms-`.
|
||||
*/
|
||||
export default function hyphenateStyleName(name: string): string {
|
||||
return name
|
||||
.replace(uppercasePattern, '-$1')
|
||||
.toLowerCase()
|
||||
.replace(msPattern, '-ms-');
|
||||
}
|
||||
19
packages/react-dom/src/shared/warnValidStyle.js
vendored
19
packages/react-dom/src/shared/warnValidStyle.js
vendored
@@ -5,15 +5,15 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import emptyFunction from 'fbjs/lib/emptyFunction';
|
||||
import camelizeStyleName from 'fbjs/lib/camelizeStyleName';
|
||||
import warning from 'fbjs/lib/warning';
|
||||
import warning from 'shared/warning';
|
||||
|
||||
let warnValidStyle = emptyFunction;
|
||||
let warnValidStyle = () => {};
|
||||
|
||||
if (__DEV__) {
|
||||
// 'msTransform' is correct, but the other prefixes should be capitalized
|
||||
const badVendoredStyleNamePattern = /^(?:webkit|moz|o)[A-Z]/;
|
||||
const msPattern = /^-ms-/;
|
||||
const hyphenPattern = /-(.)/g;
|
||||
|
||||
// style values shouldn't contain a semicolon
|
||||
const badStyleValueWithSemicolonPattern = /;\s*$/;
|
||||
@@ -23,6 +23,12 @@ if (__DEV__) {
|
||||
let warnedForNaNValue = false;
|
||||
let warnedForInfinityValue = false;
|
||||
|
||||
const camelize = function(string) {
|
||||
return string.replace(hyphenPattern, function(_, character) {
|
||||
return character.toUpperCase();
|
||||
});
|
||||
};
|
||||
|
||||
const warnHyphenatedStyleName = function(name, getStack) {
|
||||
if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
|
||||
return;
|
||||
@@ -33,7 +39,10 @@ if (__DEV__) {
|
||||
false,
|
||||
'Unsupported style property %s. Did you mean %s?%s',
|
||||
name,
|
||||
camelizeStyleName(name),
|
||||
// As Andi Smith suggests
|
||||
// (http://www.andismith.com/blog/2012/02/modernizr-prefixed/), an `-ms` prefix
|
||||
// is converted to lowercase `ms`.
|
||||
camelize(name.replace(msPattern, 'ms-')),
|
||||
getStack(),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
HostText,
|
||||
} from 'shared/ReactTypeOfWork';
|
||||
import SyntheticEvent from 'events/SyntheticEvent';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import invariant from 'shared/invariant';
|
||||
|
||||
import * as DOMTopLevelEventTypes from '../events/DOMTopLevelEventTypes';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-is",
|
||||
"version": "16.4.0",
|
||||
"version": "16.4.1",
|
||||
"description": "Brand checking of React Elements.",
|
||||
"main": "index.js",
|
||||
"repository": "facebook/react",
|
||||
|
||||
17
packages/react-is/src/ReactIs.js
vendored
17
packages/react-is/src/ReactIs.js
vendored
@@ -90,6 +90,23 @@ export function isForwardRef(object: any) {
|
||||
export function isFragment(object: any) {
|
||||
return typeOf(object) === REACT_FRAGMENT_TYPE;
|
||||
}
|
||||
export function isNode(object: any) {
|
||||
switch (typeof object) {
|
||||
case 'number':
|
||||
case 'string':
|
||||
case 'undefined':
|
||||
return true;
|
||||
case 'boolean':
|
||||
return !object;
|
||||
case 'object':
|
||||
if (Array.isArray(object)) {
|
||||
return object.every(isNode);
|
||||
}
|
||||
return object === null || isElement(object);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export function isProfiler(object: any) {
|
||||
return typeOf(object) === REACT_PROFILER_TYPE;
|
||||
}
|
||||
|
||||
20
packages/react-is/src/__tests__/ReactIs-test.js
vendored
20
packages/react-is/src/__tests__/ReactIs-test.js
vendored
@@ -85,6 +85,26 @@ describe('ReactIs', () => {
|
||||
expect(ReactIs.isContextConsumer(<div />)).toBe(false);
|
||||
});
|
||||
|
||||
it('should identify nodes', () => {
|
||||
expect(ReactIs.isNode(<div />)).toBe(true);
|
||||
expect(ReactIs.isNode('div')).toBe(true);
|
||||
expect(ReactIs.isNode(false)).toBe(true);
|
||||
expect(ReactIs.isNode(true)).toBe(false);
|
||||
expect(ReactIs.isNode(123)).toBe(true);
|
||||
expect(ReactIs.isNode(null)).toBe(true);
|
||||
expect(ReactIs.isNode(undefined)).toBe(true);
|
||||
expect(ReactIs.isNode([1, 'string', <div />])).toBe(true);
|
||||
expect(ReactIs.isNode({})).toBe(false);
|
||||
|
||||
// It should also identify more specific types as nodes
|
||||
const Context = React.createContext(false);
|
||||
expect(ReactIs.isNode(<Context.Provider />)).toBe(true);
|
||||
expect(ReactIs.isNode(<Context.Consumer />)).toBe(true);
|
||||
expect(ReactIs.isNode(<React.Fragment />)).toBe(true);
|
||||
expect(ReactIs.isNode(<React.unstable_AsyncMode />)).toBe(true);
|
||||
expect(ReactIs.isNode(<React.StrictMode />)).toBe(true);
|
||||
});
|
||||
|
||||
it('should identify context providers', () => {
|
||||
const Context = React.createContext(false);
|
||||
expect(ReactIs.typeOf(<Context.Provider />)).toBe(ReactIs.ContextProvider);
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
"private": true,
|
||||
"repository": "facebook/react",
|
||||
"dependencies": {
|
||||
"fbjs": "^0.8.16",
|
||||
"object-assign": "^4.1.1",
|
||||
"prop-types": "^15.6.0"
|
||||
"prop-types": "^15.6.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.0.0"
|
||||
|
||||
@@ -15,7 +15,7 @@ import type {
|
||||
ReactNativeBaseComponentViewConfig,
|
||||
} from './ReactNativeTypes';
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import invariant from 'shared/invariant';
|
||||
// Modules provided by RN:
|
||||
import TextInputState from 'TextInputState';
|
||||
import UIManager from 'UIManager';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user