Skip to content

Commit 49979bb

Browse files
philipp-spiessgaearon
authored andcommitted
Support Pointer Events (#12507)
* Support Pointer Events * Add Pointer Events DOM Fixture
1 parent de84d5c commit 49979bb

File tree

13 files changed

+373
-17
lines changed

13 files changed

+373
-17
lines changed

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="/pointer-events">Pointer Events</option>
6768
</select>
6869
</label>
6970
<label htmlFor="react_version">

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 PointerEventsFixtures from './pointer-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 '/pointer-events':
51+
return <PointerEventsFixtures />;
4952
default:
5053
return <p>Please select a test fixture.</p>;
5154
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
const React = window.React;
2+
3+
const CIRCLE_SIZE = 80;
4+
5+
class DragBox extends React.Component {
6+
state = {
7+
hasCapture: false,
8+
circleLeft: 80,
9+
circleTop: 80,
10+
};
11+
isDragging = false;
12+
previousLeft = 0;
13+
previousTop = 0;
14+
15+
onDown = event => {
16+
this.isDragging = true;
17+
event.target.setPointerCapture(event.pointerId);
18+
19+
// We store the initial coordinates to be able to calculate the changes
20+
// later on.
21+
this.extractPositionDelta(event);
22+
};
23+
24+
onMove = event => {
25+
if (!this.isDragging) {
26+
return;
27+
}
28+
const {left, top} = this.extractPositionDelta(event);
29+
30+
this.setState(({circleLeft, circleTop}) => ({
31+
circleLeft: circleLeft + left,
32+
circleTop: circleTop + top,
33+
}));
34+
};
35+
36+
onUp = event => (this.isDragging = false);
37+
onGotCapture = event => this.setState({hasCapture: true});
38+
onLostCapture = event => this.setState({hasCapture: false});
39+
40+
extractPositionDelta = event => {
41+
const left = event.pageX;
42+
const top = event.pageY;
43+
const delta = {
44+
left: left - this.previousLeft,
45+
top: top - this.previousTop,
46+
};
47+
this.previousLeft = left;
48+
this.previousTop = top;
49+
return delta;
50+
};
51+
52+
render() {
53+
const {hasCapture, circleLeft, circleTop} = this.state;
54+
55+
const boxStyle = {
56+
border: '1px solid #d9d9d9',
57+
margin: '10px 0 20px',
58+
minHeight: 400,
59+
width: '100%',
60+
position: 'relative',
61+
};
62+
63+
const circleStyle = {
64+
width: CIRCLE_SIZE,
65+
height: CIRCLE_SIZE,
66+
borderRadius: CIRCLE_SIZE / 2,
67+
position: 'absolute',
68+
left: circleLeft,
69+
top: circleTop,
70+
backgroundColor: hasCapture ? 'blue' : 'green',
71+
touchAction: 'none',
72+
};
73+
74+
return (
75+
<div style={boxStyle}>
76+
<div
77+
style={circleStyle}
78+
onPointerDown={this.onDown}
79+
onPointerMove={this.onMove}
80+
onPointerUp={this.onUp}
81+
onPointerCancel={this.onUp}
82+
onGotPointerCapture={this.onGotCapture}
83+
onLostPointerCapture={this.onLostCapture}
84+
/>
85+
</div>
86+
);
87+
}
88+
}
89+
90+
export default DragBox;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import TestCase from '../../TestCase';
2+
import DragBox from './drag-box';
3+
4+
const React = window.React;
5+
6+
class Drag extends React.Component {
7+
render() {
8+
return (
9+
<TestCase title="Drag" description="">
10+
<TestCase.Steps>
11+
<li>Drag the circle below with any pointer tool</li>
12+
</TestCase.Steps>
13+
14+
<TestCase.ExpectedResult>
15+
While dragging, the circle must have turn blue to indicate that a
16+
pointer capture was received.
17+
</TestCase.ExpectedResult>
18+
19+
<DragBox />
20+
</TestCase>
21+
);
22+
}
23+
}
24+
25+
export default Drag;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const React = window.React;
2+
3+
class DrawBox extends React.Component {
4+
render() {
5+
const boxStyle = {
6+
border: '1px solid #d9d9d9',
7+
margin: '10px 0 20px',
8+
padding: '20px 20px',
9+
touchAction: 'none',
10+
};
11+
12+
const obstacleStyle = {
13+
border: '1px solid #d9d9d9',
14+
width: '25%',
15+
height: '200px',
16+
margin: '12.5%',
17+
display: 'inline-block',
18+
};
19+
20+
return (
21+
<div
22+
style={boxStyle}
23+
onPointerOver={this.props.onOver}
24+
onPointerOut={this.props.onOut}
25+
onPointerEnter={this.props.onEnter}
26+
onPointerLeave={this.props.onLeave}>
27+
<div style={obstacleStyle} />
28+
<div style={obstacleStyle} />
29+
</div>
30+
);
31+
}
32+
}
33+
34+
export default DrawBox;
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import TestCase from '../../TestCase';
2+
import HoverBox from './hover-box';
3+
4+
const React = window.React;
5+
6+
class Hover extends React.Component {
7+
state = {
8+
overs: 0,
9+
outs: 0,
10+
enters: 0,
11+
leaves: 0,
12+
};
13+
14+
onOver = () => this.setState({overs: this.state.overs + 1});
15+
onOut = () => this.setState({outs: this.state.outs + 1});
16+
onEnter = () => this.setState({enters: this.state.enters + 1});
17+
onLeave = () => this.setState({leaves: this.state.leaves + 1});
18+
19+
render() {
20+
const {overs, outs, enters, leaves} = this.state;
21+
22+
return (
23+
<TestCase title="Hover" description="">
24+
<TestCase.Steps>
25+
<li>Hover over the above box and the obstacles</li>
26+
</TestCase.Steps>
27+
28+
<TestCase.ExpectedResult>
29+
Overs and outs should increase when moving over the obstacles but
30+
enters and leaves should not.
31+
</TestCase.ExpectedResult>
32+
33+
<HoverBox
34+
onOver={this.onOver}
35+
onOut={this.onOut}
36+
onEnter={this.onEnter}
37+
onLeave={this.onLeave}
38+
/>
39+
40+
<p>
41+
Pointer Overs: <b>{overs}</b> <br />
42+
Pointer Outs: <b>{outs}</b> <br />
43+
Pointer Enters: <b>{enters}</b> <br />
44+
Pointer Leaves: <b>{leaves}</b> <br />
45+
</p>
46+
</TestCase>
47+
);
48+
}
49+
}
50+
51+
export default Hover;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import FixtureSet from '../../FixtureSet';
2+
import Drag from './drag';
3+
import Hover from './hover';
4+
5+
const React = window.React;
6+
7+
class PointerEvents extends React.Component {
8+
render() {
9+
return (
10+
<FixtureSet
11+
title="Pointer Events"
12+
description="Pointer Events are not supported in every browser. The examples below might not work in every browser. To test pointer events, make sure to use Google Chrome, Firefox, Internet Explorer, or Edge.">
13+
<Drag />
14+
<Hover />
15+
</FixtureSet>
16+
);
17+
}
18+
}
19+
20+
export default PointerEvents;

packages/react-dom/src/__tests__/__snapshots__/ReactTestUtils-test.js.snap

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Array [
3535
"ended",
3636
"error",
3737
"focus",
38+
"gotPointerCapture",
3839
"input",
3940
"invalid",
4041
"keyDown",
@@ -44,6 +45,7 @@ Array [
4445
"loadStart",
4546
"loadedData",
4647
"loadedMetadata",
48+
"lostPointerCapture",
4749
"mouseDown",
4850
"mouseEnter",
4951
"mouseLeave",
@@ -55,6 +57,14 @@ Array [
5557
"pause",
5658
"play",
5759
"playing",
60+
"pointerCancel",
61+
"pointerDown",
62+
"pointerEnter",
63+
"pointerLeave",
64+
"pointerMove",
65+
"pointerOut",
66+
"pointerOver",
67+
"pointerUp",
5868
"progress",
5969
"rateChange",
6070
"reset",

packages/react-dom/src/events/DOMTopLevelEventTypes.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ export const TOP_ENCRYPTED = unsafeCastStringToDOMTopLevelType('encrypted');
7272
export const TOP_ENDED = unsafeCastStringToDOMTopLevelType('ended');
7373
export const TOP_ERROR = unsafeCastStringToDOMTopLevelType('error');
7474
export const TOP_FOCUS = unsafeCastStringToDOMTopLevelType('focus');
75+
export const TOP_GOT_POINTER_CAPTURE = unsafeCastStringToDOMTopLevelType(
76+
'gotpointercapture',
77+
);
7578
export const TOP_INPUT = unsafeCastStringToDOMTopLevelType('input');
7679
export const TOP_INVALID = unsafeCastStringToDOMTopLevelType('invalid');
7780
export const TOP_KEY_DOWN = unsafeCastStringToDOMTopLevelType('keydown');
@@ -83,6 +86,9 @@ export const TOP_LOADED_DATA = unsafeCastStringToDOMTopLevelType('loadeddata');
8386
export const TOP_LOADED_METADATA = unsafeCastStringToDOMTopLevelType(
8487
'loadedmetadata',
8588
);
89+
export const TOP_LOST_POINTER_CAPTURE = unsafeCastStringToDOMTopLevelType(
90+
'lostpointercapture',
91+
);
8692
export const TOP_MOUSE_DOWN = unsafeCastStringToDOMTopLevelType('mousedown');
8793
export const TOP_MOUSE_MOVE = unsafeCastStringToDOMTopLevelType('mousemove');
8894
export const TOP_MOUSE_OUT = unsafeCastStringToDOMTopLevelType('mouseout');
@@ -92,6 +98,26 @@ export const TOP_PASTE = unsafeCastStringToDOMTopLevelType('paste');
9298
export const TOP_PAUSE = unsafeCastStringToDOMTopLevelType('pause');
9399
export const TOP_PLAY = unsafeCastStringToDOMTopLevelType('play');
94100
export const TOP_PLAYING = unsafeCastStringToDOMTopLevelType('playing');
101+
export const TOP_POINTER_CANCEL = unsafeCastStringToDOMTopLevelType(
102+
'pointercancel',
103+
);
104+
export const TOP_POINTER_DOWN = unsafeCastStringToDOMTopLevelType(
105+
'pointerdown',
106+
);
107+
export const TOP_POINTER_ENTER = unsafeCastStringToDOMTopLevelType(
108+
'pointerenter',
109+
);
110+
export const TOP_POINTER_LEAVE = unsafeCastStringToDOMTopLevelType(
111+
'pointerleave',
112+
);
113+
export const TOP_POINTER_MOVE = unsafeCastStringToDOMTopLevelType(
114+
'pointermove',
115+
);
116+
export const TOP_POINTER_OUT = unsafeCastStringToDOMTopLevelType('pointerout');
117+
export const TOP_POINTER_OVER = unsafeCastStringToDOMTopLevelType(
118+
'pointerover',
119+
);
120+
export const TOP_POINTER_UP = unsafeCastStringToDOMTopLevelType('pointerup');
95121
export const TOP_PROGRESS = unsafeCastStringToDOMTopLevelType('progress');
96122
export const TOP_RATE_CHANGE = unsafeCastStringToDOMTopLevelType('ratechange');
97123
export const TOP_RESET = unsafeCastStringToDOMTopLevelType('reset');

0 commit comments

Comments
 (0)