Skip to content

Commit dff3e52

Browse files
author
Brandon Dail
committed
Implement selection event fixtures
1 parent 2c3c59e commit dff3e52

File tree

10 files changed

+189
-1
lines changed

10 files changed

+189
-1
lines changed

fixtures/dom/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"dependencies": {
99
"classnames": "^2.2.5",
1010
"core-js": "^2.4.1",
11+
"draft-js": "^0.10.5",
1112
"prop-types": "^15.6.0",
1213
"query-string": "^4.2.3",
1314
"react": "^15.4.1",

fixtures/dom/public/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<title>React App</title>
1717
<script src="https://unpkg.com/[email protected]/prop-types.js"></script>
1818
<script src="https://unpkg.com/[email protected]/umd/expect.min.js"></script>
19+
<script src="https://unpkg.com/[email protected]/dist/immutable.js"></script>
1920
</head>
2021
<body>
2122
<div id="root"></div>

fixtures/dom/src/components/Header.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ class Header extends React.Component {
6464
<option value="/event-pooling">Event Pooling</option>
6565
<option value="/custom-elements">Custom Elements</option>
6666
<option value="/media-events">Media Events</option>
67+
<option value="/selection-events">Selection Events</option>
6768
</select>
6869
</label>
6970
<label htmlFor="react_version">
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
const React = window.React;
2+
const ReactDOM = window.ReactDOM;
3+
4+
class IframePortal extends React.Component {
5+
state = {ref: null};
6+
7+
handleRef = (ref) => {
8+
if (ref !== this.state.ref) {
9+
this.setState({ref});
10+
if (ref && ref.contentDocument && this.props.head) {
11+
ref.contentDocument.head.innerHTML = this.props.head;
12+
}
13+
}
14+
};
15+
16+
render() {
17+
const {ref} = this.state;
18+
let portal = null;
19+
if (ref && ref.contentDocument) {
20+
portal = ReactDOM.createPortal(
21+
this.props.children,
22+
ref.contentDocument.body,
23+
);
24+
}
25+
26+
return (
27+
<div>
28+
<iframe
29+
style={{ border: 'none', height: this.props.height }}
30+
ref={this.handleRef} />
31+
{portal}
32+
</div>
33+
);
34+
}
35+
}
36+
37+
class IframeSubtree extends React.Component {
38+
warned = false;
39+
render() {
40+
if (!this.warned) {
41+
console.error(`IFrame has not yet been implemented for React v${React.version}`);
42+
this.warned = true;
43+
}
44+
return (
45+
<div>
46+
{this.props.children}
47+
</div>
48+
)
49+
}
50+
}
51+
52+
53+
export default ReactDOM.createPortal ? IframePortal : IframeSubtree;

fixtures/dom/src/components/fixtures/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import ErrorHandling from './error-handling';
1111
import EventPooling from './event-pooling';
1212
import CustomElementFixtures from './custom-elements';
1313
import MediaEventsFixtures from './media-events';
14+
import SelectionEventsFixtures from './selection-events';
1415

1516
const React = window.React;
1617

@@ -46,6 +47,8 @@ function FixturesPage() {
4647
return <CustomElementFixtures />;
4748
case '/media-events':
4849
return <MediaEventsFixtures />;
50+
case '/selection-events':
51+
return <SelectionEventsFixtures />;
4952
default:
5053
return <p>Please select a test fixture.</p>;
5154
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import TestCase from '../../TestCase';
2+
import Iframe from '../../Iframe';
3+
const React = window.React;
4+
const {EditorState, Editor} = window.Draft;
5+
6+
7+
export default class DraftJsEditorTestCase extends React.Component {
8+
constructor(props) {
9+
super(props);
10+
this.state = {editorState: EditorState.createEmpty()};
11+
this.onChange = (editorState) => this.setState({editorState});
12+
}
13+
render() {
14+
return (
15+
<TestCase
16+
title="Cursor Position in a Draft.js Editor"
17+
description="Draft.js is a rich text editor system for React.
18+
This verifies that the selection restoration functionality it depends on
19+
works in an iframe.">
20+
<TestCase.Steps>
21+
<li>Enter some text into the Draft.js editor (grey outlined box)</li>
22+
<li>Change your cursor position to somewhere in the middle of the text</li>
23+
<li>Enter a new character</li>
24+
</TestCase.Steps>
25+
<TestCase.ExpectedResult>
26+
The cursor should not jump positions
27+
</TestCase.ExpectedResult>
28+
<Iframe height={60}>
29+
<div style={{ border: '1px solid grey' }}>
30+
<Editor editorState={this.state.editorState} onChange={this.onChange} />
31+
</div>
32+
</Iframe>
33+
</TestCase>
34+
);
35+
}
36+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import TestCase from '../../TestCase';
2+
import Iframe from '../../Iframe';
3+
const React = window.React;
4+
5+
export default class ReorderedInputsTestCase extends React.Component {
6+
7+
state = { count: 0 };
8+
9+
componentDidMount() {
10+
this.interval = setInterval(() => {
11+
this.setState({ count: this.state.count + 1 });
12+
}, 2000);
13+
}
14+
15+
componentWillUnmount() {
16+
clearInterval(this.interval);
17+
}
18+
19+
renderInputs() {
20+
const inputs = [
21+
<input key={1} defaultValue="Foo" />,
22+
<input key={2} defaultValue="Bar" />,
23+
];
24+
if (this.state.count % 2 === 0) {
25+
inputs.reverse();
26+
}
27+
return inputs;
28+
}
29+
30+
render() {
31+
return (
32+
<TestCase
33+
title="Reordered input elements in iframes"
34+
description="">
35+
<TestCase.Steps>
36+
<li>The two inputs below swap positions every two seconds</li>
37+
<li>Select the text in either of them</li>
38+
<li>Wait for the swap to occur</li>
39+
</TestCase.Steps>
40+
<TestCase.ExpectedResult>
41+
The selection you made should be maintained
42+
</TestCase.ExpectedResult>
43+
<Iframe height={50}>
44+
{this.renderInputs()}
45+
</Iframe>
46+
</TestCase>
47+
)
48+
}
49+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import FixtureSet from '../../FixtureSet';
2+
import TestCase from '../../TestCase';
3+
import Iframe from '../../Iframe';
4+
import ReorderedInputsTestCase from './ReorderedInputsTestCase';
5+
import DraftJsEditorTestCase from './DraftJsEditorTestCase';
6+
const React = window.React;
7+
8+
9+
export default function SelectionEvents() {
10+
return (
11+
<FixtureSet
12+
title="Selection Restoration in iframes"
13+
description="
14+
When React commits changes it may perform operations which cause existing
15+
selection state to be lost. This is manually managed by reading the
16+
selection state before commits and then restoring it afterwards.
17+
This selection restoration process should work for elements rendered in
18+
iframes.
19+
">
20+
<ReorderedInputsTestCase />
21+
<DraftJsEditorTestCase />
22+
</FixtureSet>
23+
);
24+
};

fixtures/dom/src/react-loader.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ function loadScript(src) {
3535
}
3636

3737
export default function loadReact() {
38+
// const DRAFT_JS_PATH = 'https://unpkg.com/[email protected]/dist/Draft.js';
39+
// @TODO We're using a build of draft-js provided by @acusti in https://github.com/facebook/react/pull/12037
40+
// This is because currently draft doesn't support iframes. This should be changed back to the official
41+
// release on unpkg once the draft fixes are merged.
42+
const DRAFT_JS_PATH = 'https://cdn.rawgit.com/brandcast/draft-js-built/d6c2ffc64914b001411356c0bfab24e831bc5429/dist/Draft.js';
3843
let REACT_PATH = 'react.development.js';
3944
let DOM_PATH = 'react-dom.development.js';
4045

@@ -70,6 +75,9 @@ export default function loadReact() {
7075
window.ReactDOM = window.React;
7176
});
7277
}
78+
request = request.then(() => loadScript(DRAFT_JS_PATH));
79+
// We need Draft.js for the selection events fixture. It has to load
80+
// after ReactDOM.
7381

7482
return request;
7583
}

fixtures/dom/yarn.lock

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2153,6 +2153,14 @@ [email protected]:
21532153
version "4.0.0"
21542154
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-4.0.0.tgz#864ef1379aced55ce6f95debecdce179f7a0cd1d"
21552155

2156+
draft-js@^0.10.5:
2157+
version "0.10.5"
2158+
resolved "https://registry.yarnpkg.com/draft-js/-/draft-js-0.10.5.tgz#bfa9beb018fe0533dbb08d6675c371a6b08fa742"
2159+
dependencies:
2160+
fbjs "^0.8.15"
2161+
immutable "~3.7.4"
2162+
object-assign "^4.1.0"
2163+
21562164
duplexer2@^0.1.4:
21572165
version "0.1.4"
21582166
resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"
@@ -2672,7 +2680,7 @@ fbjs@^0.8.1, fbjs@^0.8.4:
26722680
setimmediate "^1.0.5"
26732681
ua-parser-js "^0.7.9"
26742682

2675-
fbjs@^0.8.16:
2683+
fbjs@^0.8.15, fbjs@^0.8.16:
26762684
version "0.8.16"
26772685
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
26782686
dependencies:
@@ -3302,6 +3310,10 @@ ignore@^3.3.3:
33023310
version "3.3.3"
33033311
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d"
33043312

3313+
immutable@~3.7.4:
3314+
version "3.7.6"
3315+
resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b"
3316+
33053317
imurmurhash@^0.1.4:
33063318
version "0.1.4"
33073319
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"

0 commit comments

Comments
 (0)