Skip to content

Commit 7f696bd

Browse files
eps1longaearon
andauthored
fix: onFocus/onBlur/onBeforeInput have a matching event type (facebook#19561)
* test: Add current behavior for event types of onFocus/onBlur * fix: onFocus/onBlur have a matching event type * fix useFocus * fix: don't compare native event types with react event types * Add FocusIn/FocusOutEventInterface * A simpler alternative fix * Add regression tests * Always pass React event type and fix beforeinput Co-authored-by: Dan Abramov <dan.abramov@me.com>
1 parent 7c30fb3 commit 7f696bd

File tree

13 files changed

+241
-41
lines changed

13 files changed

+241
-41
lines changed

packages/react-dom/src/__tests__/ReactDOMEventPropagation-test.js

Lines changed: 93 additions & 0 deletions
Large diffs are not rendered by default.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,7 @@ export function accumulateSinglePhaseListeners(
709709

710710
let instance = targetFiber;
711711
let lastHostComponent = null;
712-
const targetType = event.type;
712+
const targetType = event.nativeEvent.type;
713713

714714
// Accumulate all instances and listeners via the target -> root path.
715715
while (instance !== null) {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export type ReactSyntheticEvent = {|
2929
_dispatchListeners?: null | Array<Function> | Function,
3030
_reactName: string,
3131
_targetInst: Fiber,
32+
nativeEvent: Event,
3233
type: string,
3334
currentTarget: null | EventTarget,
3435
|};

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import getEventCharCode from './getEventCharCode';
1414
* @see http://www.w3.org/TR/DOM-Level-3-Events/
1515
*/
1616
const EventInterface = {
17-
type: 0,
1817
eventPhase: 0,
1918
bubbles: 0,
2019
cancelable: 0,
@@ -48,13 +47,15 @@ function functionThatReturnsFalse() {
4847
*/
4948
export function SyntheticEvent(
5049
reactName,
50+
reactEventType,
5151
targetInst,
5252
nativeEvent,
5353
nativeEventTarget,
5454
Interface = EventInterface,
5555
) {
5656
this._reactName = reactName;
5757
this._targetInst = targetInst;
58+
this.type = reactEventType;
5859
this.nativeEvent = nativeEvent;
5960
this.target = nativeEventTarget;
6061
this.currentTarget = null;
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
'use strict';
9+
10+
describe('SyntheticFocusEvent', () => {
11+
let React;
12+
let ReactDOM;
13+
let container;
14+
15+
beforeEach(() => {
16+
jest.resetModules();
17+
React = require('react');
18+
ReactDOM = require('react-dom');
19+
20+
container = document.createElement('div');
21+
document.body.appendChild(container);
22+
});
23+
24+
afterEach(() => {
25+
document.body.removeChild(container);
26+
container = null;
27+
});
28+
29+
test('onFocus events have the focus type', () => {
30+
const log = [];
31+
ReactDOM.render(
32+
<button
33+
onFocus={event => log.push(`onFocus: ${event.type}`)}
34+
onFocusCapture={event => log.push(`onFocusCapture: ${event.type}`)}
35+
/>,
36+
container,
37+
);
38+
const button = container.querySelector('button');
39+
40+
button.dispatchEvent(
41+
new FocusEvent('focusin', {
42+
bubbles: true,
43+
cancelable: false,
44+
}),
45+
);
46+
47+
expect(log).toEqual(['onFocusCapture: focus', 'onFocus: focus']);
48+
});
49+
50+
test('onBlur events have the blur type', () => {
51+
const log = [];
52+
ReactDOM.render(
53+
<button
54+
onBlur={event => log.push(`onBlur: ${event.type}`)}
55+
onBlurCapture={event => log.push(`onBlurCapture: ${event.type}`)}
56+
/>,
57+
container,
58+
);
59+
const button = container.querySelector('button');
60+
61+
button.dispatchEvent(
62+
new FocusEvent('focusout', {
63+
bubbles: true,
64+
cancelable: false,
65+
}),
66+
);
67+
68+
expect(log).toEqual(['onBlurCapture: blur', 'onBlur: blur']);
69+
});
70+
});

packages/react-dom/src/events/plugins/BeforeInputEventPlugin.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ function extractCompositionEvent(
229229

230230
const event = new SyntheticEvent(
231231
eventType,
232+
domEventName,
232233
null,
233234
nativeEvent,
234235
nativeEventTarget,
@@ -397,6 +398,7 @@ function extractBeforeInputEvent(
397398

398399
const event = new SyntheticEvent(
399400
'onBeforeInput',
401+
'beforeinput',
400402
null,
401403
nativeEvent,
402404
nativeEventTarget,

packages/react-dom/src/events/plugins/ChangeEventPlugin.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,13 @@ function createAndAccumulateChangeEvent(
4949
nativeEvent,
5050
target,
5151
) {
52-
const event = new SyntheticEvent('onChange', null, nativeEvent, target);
53-
event.type = 'change';
52+
const event = new SyntheticEvent(
53+
'onChange',
54+
'change',
55+
null,
56+
nativeEvent,
57+
target,
58+
);
5459
// Flag this event loop as needing state restore.
5560
enqueueStateRestore(((target: any): Node));
5661
accumulateTwoPhaseListeners(inst, dispatchQueue, event);

packages/react-dom/src/events/plugins/EnterLeaveEventPlugin.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,23 +133,23 @@ function extractEvents(
133133

134134
const leave = new SyntheticEvent(
135135
leaveEventType,
136+
eventTypePrefix + 'leave',
136137
from,
137138
nativeEvent,
138139
nativeEventTarget,
139140
eventInterface,
140141
);
141-
leave.type = eventTypePrefix + 'leave';
142142
leave.target = fromNode;
143143
leave.relatedTarget = toNode;
144144

145145
let enter = new SyntheticEvent(
146146
enterEventType,
147+
eventTypePrefix + 'enter',
147148
to,
148149
nativeEvent,
149150
nativeEventTarget,
150151
eventInterface,
151152
);
152-
enter.type = eventTypePrefix + 'enter';
153153
enter.target = toNode;
154154
enter.relatedTarget = fromNode;
155155

packages/react-dom/src/events/plugins/SelectEventPlugin.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,11 @@ function constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget) {
114114

115115
const syntheticEvent = new SyntheticEvent(
116116
'onSelect',
117+
'select',
117118
null,
118119
nativeEvent,
119120
nativeEventTarget,
120121
);
121-
122-
syntheticEvent.type = 'select';
123122
syntheticEvent.target = activeElement;
124123

125124
accumulateTwoPhaseListeners(

packages/react-dom/src/events/plugins/SimpleEventPlugin.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ function extractEvents(
6363
return;
6464
}
6565
let EventInterface;
66+
let reactEventType = domEventName;
6667
switch (domEventName) {
6768
case 'keypress':
6869
// Firefox creates a keypress event for function keys too. This removes
@@ -77,7 +78,13 @@ function extractEvents(
7778
EventInterface = KeyboardEventInterface;
7879
break;
7980
case 'focusin':
81+
reactEventType = 'focus';
82+
EventInterface = FocusEventInterface;
83+
break;
8084
case 'focusout':
85+
reactEventType = 'blur';
86+
EventInterface = FocusEventInterface;
87+
break;
8188
case 'beforeblur':
8289
case 'afterblur':
8390
EventInterface = FocusEventInterface;
@@ -152,6 +159,7 @@ function extractEvents(
152159
}
153160
const event = new SyntheticEvent(
154161
reactName,
162+
reactEventType,
155163
null,
156164
nativeEvent,
157165
nativeEventTarget,

0 commit comments

Comments
 (0)