@samitouri / QOS-React-2 / commits / d53988a9d1

ReactDOM.useEvent: add useEvent interaction hook (#18604)

Dominic Gannaway committed Apr 14, 2020 at 22:06 UTC d53988a9d108b9480ff161a6e85ca26760ada615
15 files changed +1256 -17
packages/react-debug-tools/src/ReactDebugHooks.js
+1 -1
@@ -52,7 +52,7 @@ type ReactDebugListenerMap = {|
52 clear: () => void,
53 setListener: (
54 target: EventTarget | ReactScopeMethods,
55 - callback: ?(Event) => void,
55 + callback: ?(SyntheticEvent<EventTarget>) => void,
56 ) => void,
57 |};
58
packages/react-dom/src/client/ReactDOMHostConfig.js
+1 -1
@@ -1216,7 +1216,7 @@ export function unmountEventListener(listener: ReactDOMListener): void {
1216
1217 export function validateEventListenerTarget(
1218 target: EventTarget | ReactScopeMethods,
1219 - listener: ?(Event) => void,
1219 + listener: ?(SyntheticEvent<EventTarget>) => void,
1220 ): boolean {
1221 if (enableUseEventAPI) {
1222 if (
packages/react-dom/src/client/ReactDOMUseEvent.js
+4 -3
@@ -42,9 +42,10 @@ function resolveDispatcher() {
42 }
43
44 export function useEvent(
45 - type: DOMTopLevelEventType,
45 + type: string,
46 options?: EventOptions,
47 ): ReactDOMListenerMap {
48 + const topLevelType = ((type: any): DOMTopLevelEventType);
49 const dispatcher = resolveDispatcher();
50 let capture = false;
51 let passive = undefined; // Undefined means to use the browser default
@@ -66,13 +67,13 @@ export function useEvent(
67 }
68 }
69 if (priority === undefined) {
69 - priority = getEventPriorityForListenerSystem(type);
70 + priority = getEventPriorityForListenerSystem(topLevelType);
71 }
72 const event: ReactDOMListenerEvent = {
73 capture,
74 passive,
75 priority,
75 - type,
76 + type: topLevelType,
77 };
78 return dispatcher.useEvent(event);
79 }
packages/react-dom/src/shared/ReactDOMTypes.js
+2 -2
@@ -89,12 +89,12 @@ export type ReactDOMListenerMap = {|
89 clear: () => void,
90 setListener: (
91 target: EventTarget | ReactScopeMethods,
92 - callback: ?(Event) => void,
92 + callback: ?(SyntheticEvent<EventTarget>) => void,
93 ) => void,
94 |};
95
96 export type ReactDOMListener = {|
97 - callback: Event => void,
97 + callback: (SyntheticEvent<EventTarget>) => void,
98 destroy: Node => void,
99 event: ReactDOMListenerEvent,
100 target: EventTarget | ReactScopeMethods,
packages/react-interactions/events/deprecated-focus.js new
+10
@@ -0,0 +1,10 @@
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 + * @flow
8 + */
9 +
10 +export * from './src/dom/DeprecatedFocus';
packages/react-interactions/events/focus.js
+1 -1
@@ -7,4 +7,4 @@
7 * @flow
8 */
9
10 -export * from './src/dom/Focus';
10 +export * from './src/dom/use-event/Focus';
packages/react-interactions/events/src/dom/DeprecatedFocus.js renamed
packages/react-interactions/events/src/dom/__tests__/Focus-test.internal.js
+3 -2
@@ -32,8 +32,9 @@ function initializeModules(hasPointerEvents) {
32 // TODO: This import throws outside of experimental mode. Figure out better
33 // strategy for gated imports.
34 if (__EXPERIMENTAL__) {
35 - FocusResponder = require('react-interactions/events/focus').FocusResponder;
36 - useFocus = require('react-interactions/events/focus').useFocus;
35 + FocusResponder = require('react-interactions/events/deprecated-focus')
36 + .FocusResponder;
37 + useFocus = require('react-interactions/events/deprecated-focus').useFocus;
38 }
39 }
40
packages/react-interactions/events/src/dom/__tests__/FocusWithin-test.internal.js
+3 -2
@@ -31,9 +31,10 @@ const initializeModules = hasPointerEvents => {
31 // TODO: This import throws outside of experimental mode. Figure out better
32 // strategy for gated imports.
33 if (__EXPERIMENTAL__) {
34 - FocusWithinResponder = require('react-interactions/events/focus')
34 + FocusWithinResponder = require('react-interactions/events/deprecated-focus')
35 .FocusWithinResponder;
36 - useFocusWithin = require('react-interactions/events/focus').useFocusWithin;
36 + useFocusWithin = require('react-interactions/events/deprecated-focus')
37 + .useFocusWithin;
38 }
39 };
40
packages/react-interactions/events/src/dom/use-event/Focus.js new
+440
@@ -0,0 +1,440 @@
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 + * @flow
8 + */
9 +
10 +import * as React from 'react';
11 +import * as ReactDOM from 'react-dom';
12 +
13 +const useEvent = ReactDOM.unstable_useEvent;
14 +const {useEffect, useRef} = React;
15 +
16 +type UseFocusOptions = {|
17 + disabled?: boolean,
18 + onBlur?: (SyntheticEvent<EventTarget>) => void,
19 + onFocus?: (SyntheticEvent<EventTarget>) => void,
20 + onFocusChange?: boolean => void,
21 + onFocusVisibleChange?: boolean => void,
22 +|};
23 +
24 +type UseFocusWithinOptions = {|
25 + disabled?: boolean,
26 + onAfterBlurWithin?: (SyntheticEvent<EventTarget>) => void,
27 + onBeforeBlurWithin?: (SyntheticEvent<EventTarget>) => void,
28 + onBlurWithin?: (SyntheticEvent<EventTarget>) => void,
29 + onFocusWithin?: (SyntheticEvent<EventTarget>) => void,
30 + onFocusWithinChange?: boolean => void,
31 + onFocusWithinVisibleChange?: boolean => void,
32 +|};
33 +
34 +const isMac =
35 + typeof window !== 'undefined' && window.navigator != null
36 + ? /^Mac/.test(window.navigator.platform)
37 + : false;
38 +
39 +const canUseDOM: boolean = !!(
40 + typeof window !== 'undefined' &&
41 + typeof window.document !== 'undefined' &&
42 + typeof window.document.createElement !== 'undefined'
43 +);
44 +
45 +let passiveBrowserEventsSupported = false;
46 +
47 +// Check if browser support events with passive listeners
48 +// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
49 +if (canUseDOM) {
50 + try {
51 + const options = {};
52 + // $FlowFixMe: Ignore Flow complaining about needing a value
53 + Object.defineProperty(options, 'passive', {
54 + get: function() {
55 + passiveBrowserEventsSupported = true;
56 + },
57 + });
58 + window.addEventListener('test', options, options);
59 + window.removeEventListener('test', options, options);
60 + } catch (e) {
61 + passiveBrowserEventsSupported = false;
62 + }
63 +}
64 +
65 +const hasPointerEvents =
66 + typeof window !== 'undefined' && window.PointerEvent != null;
67 +
68 +const globalFocusVisibleEvents = hasPointerEvents
69 + ? ['keydown', 'pointermove', 'pointerdown', 'pointerup']
70 + : ['keydown', 'mousedown', 'touchmove', 'touchstart', 'touchend'];
71 +
72 +// Global state for tracking focus visible and emulation of mouse
73 +let isGlobalFocusVisible = true;
74 +let hasTrackedGlobalFocusVisible = false;
75 +let isEmulatingMouseEvents = false;
76 +
77 +function trackGlobalFocusVisible() {
78 + globalFocusVisibleEvents.forEach(type => {
79 + window.addEventListener(
80 + type,
81 + handleGlobalFocusVisibleEvent,
82 + passiveBrowserEventsSupported ? {capture: true, passive: true} : true,
83 + );
84 + });
85 +}
86 +
87 +function handleGlobalFocusVisibleEvent(
88 + nativeEvent: MouseEvent | TouchEvent | KeyboardEvent,
89 +): void {
90 + const {type} = nativeEvent;
91 +
92 + switch (type) {
93 + case 'pointermove':
94 + case 'pointerdown':
95 + case 'pointerup': {
96 + isGlobalFocusVisible = false;
97 + break;
98 + }
99 +
100 + case 'keydown': {
101 + const {metaKey, altKey, ctrlKey} = nativeEvent;
102 + const validKey = !(metaKey || (!isMac && altKey) || ctrlKey);
103 +
104 + if (validKey) {
105 + isGlobalFocusVisible = true;
106 + }
107 + break;
108 + }
109 +
110 + // fallbacks for no PointerEvent support
111 + case 'touchmove':
112 + case 'touchstart':
113 + case 'touchend': {
114 + isEmulatingMouseEvents = true;
115 + isGlobalFocusVisible = false;
116 + break;
117 + }
118 + case 'mousedown': {
119 + if (!isEmulatingMouseEvents) {
120 + isGlobalFocusVisible = false;
121 + } else {
122 + isEmulatingMouseEvents = false;
123 + }
124 + break;
125 + }
126 + }
127 +}
128 +
129 +const passiveObject = {passive: true};
130 +
131 +function handleFocusVisibleTargetEvent(
132 + type: string,
133 + focusTarget: EventTarget,
134 + callback: boolean => void,
135 +): void {
136 + isGlobalFocusVisible = false;
137 +
138 + // Focus should stop being visible if a pointer is used on the element
139 + // after it was focused using a keyboard.
140 + if (
141 + focusTarget !== null &&
142 + (type === 'mousedown' || type === 'touchstart' || type === 'pointerdown')
143 + ) {
144 + callback(false);
145 + }
146 +}
147 +
148 +function handleFocusVisibleTargetEvents(
149 + event: SyntheticEvent<EventTarget>,
150 + focusTarget,
151 + callback,
152 +): void {
153 + const {type} = event;
154 +
155 + switch (type) {
156 + case 'pointermove':
157 + case 'pointerdown':
158 + case 'pointerup': {
159 + handleFocusVisibleTargetEvent(type, focusTarget, callback);
160 + break;
161 + }
162 +
163 + case 'keydown':
164 + case 'keyup': {
165 + const {metaKey, altKey, ctrlKey} = (event: any);
166 + const validKey = !(metaKey || (!isMac && altKey) || ctrlKey);
167 +
168 + if (validKey) {
169 + if (focusTarget !== null) {
170 + callback(true);
171 + }
172 + }
173 + break;
174 + }
175 +
176 + // fallbacks for no PointerEvent support
177 + case 'touchmove':
178 + case 'touchstart':
179 + case 'touchend': {
180 + handleFocusVisibleTargetEvent(type, focusTarget, callback);
181 + break;
182 + }
183 + case 'mousedown': {
184 + if (!isEmulatingMouseEvents) {
185 + handleFocusVisibleTargetEvent(type, focusTarget, callback);
186 + }
187 + break;
188 + }
189 + }
190 +}
191 +
192 +function isRelatedTargetWithin(
193 + focusWithinTarget: Node,
194 + relatedTarget: null | Node,
195 +): boolean {
196 + if (relatedTarget == null) {
197 + return false;
198 + }
199 + // To support experimental scopes, which can be the target:
200 + const containsNode = (focusWithinTarget: any).containsNode;
201 + if (typeof containsNode === 'function') {
202 + return containsNode(relatedTarget);
203 + }
204 + return focusWithinTarget.contains(relatedTarget);
205 +}
206 +
207 +function setFocusVisibleListeners(focusVisibleHandles, focusTarget, callback) {
208 + focusVisibleHandles.forEach(focusVisibleHandle => {
209 + focusVisibleHandle.setListener(focusTarget, event =>
210 + handleFocusVisibleTargetEvents(event, focusTarget, callback),
211 + );
212 + });
213 +}
214 +
215 +function useFocusVisibleInputHandles() {
216 + return [
217 + useEvent('mousedown', passiveObject),
218 + useEvent(hasPointerEvents ? 'pointerdown' : 'touchstart', passiveObject),
219 + useEvent(hasPointerEvents ? 'pointermove' : 'touchmove', passiveObject),
220 + useEvent(hasPointerEvents ? 'pointerup' : 'touchend', passiveObject),
221 + useEvent('keydown', passiveObject),
222 + ];
223 +}
224 +
225 +function useFocusLifecycles(stateRef) {
226 + useEffect(() => {
227 + if (!hasTrackedGlobalFocusVisible) {
228 + hasTrackedGlobalFocusVisible = true;
229 + trackGlobalFocusVisible();
230 + }
231 + }, []);
232 +}
233 +
234 +export function useFocus(
235 + focusTargetRef: {current: null | Node},
236 + {
237 + disabled,
238 + onBlur,
239 + onFocus,
240 + onFocusChange,
241 + onFocusVisibleChange,
242 + }: UseFocusOptions,
243 +): void {
244 + // Setup controlled state for this useFocus hook
245 + const stateRef = useRef({isFocused: false, isFocusVisible: false});
246 + const focusHandle = useEvent('focus', passiveObject);
247 + const blurHandle = useEvent('blur', passiveObject);
248 + const focusVisibleHandles = useFocusVisibleInputHandles();
249 +
250 + useEffect(() => {
251 + const focusTarget = focusTargetRef.current;
252 + const state = stateRef.current;
253 +
254 + if (focusTarget !== null && state !== null) {
255 + // Handle focus visible
256 + setFocusVisibleListeners(
257 + focusVisibleHandles,
258 + focusTarget,
259 + isFocusVisible => {
260 + if (state.isFocused && state.isFocusVisible !== isFocusVisible) {
261 + state.isFocusVisible = isFocusVisible;
262 + if (onFocusVisibleChange) {
263 + onFocusVisibleChange(isFocusVisible);
264 + }
265 + }
266 + },
267 + );
268 +
269 + // Handle focus
270 + focusHandle.setListener(focusTarget, event => {
271 + if (disabled) {
272 + return;
273 + }
274 + // Limit focus events to the direct child of the event component.
275 + // Browser focus is not expected to bubble.
276 + if (!state.isFocused && focusTarget === event.target) {
277 + state.isFocused = true;
278 + state.isFocusVisible = isGlobalFocusVisible;
279 + if (onFocus) {
280 + onFocus(event);
281 + }
282 + if (onFocusChange) {
283 + onFocusChange(true);
284 + }
285 + if (state.isFocusVisible && onFocusVisibleChange) {
286 + onFocusVisibleChange(true);
287 + }
288 + isEmulatingMouseEvents = false;
289 + }
290 + });
291 +
292 + // Handle blur
293 + blurHandle.setListener(focusTarget, event => {
294 + if (disabled) {
295 + return;
296 + }
297 + if (state.isFocused) {
298 + state.isFocused = false;
299 + state.isFocusVisible = isGlobalFocusVisible;
300 + if (onBlur) {
301 + onBlur(event);
302 + }
303 + if (onFocusChange) {
304 + onFocusChange(false);
305 + }
306 + if (state.isFocusVisible && onFocusVisibleChange) {
307 + onFocusVisibleChange(false);
308 + }
309 + }
310 + isEmulatingMouseEvents = false;
311 + });
312 + }
313 + }, [disabled, onBlur, onFocus, onFocusChange, onFocusVisibleChange]);
314 +
315 + // Mount/Unmount logic
316 + useFocusLifecycles(stateRef);
317 +}
318 +
319 +export function useFocusWithin(
320 + focusWithinTargetRef: {current: null | Node},
321 + {
322 + disabled,
323 + onAfterBlurWithin,
324 + onBeforeBlurWithin,
325 + onBlurWithin,
326 + onFocusWithin,
327 + onFocusWithinChange,
328 + onFocusWithinVisibleChange,
329 + }: UseFocusWithinOptions,
330 +) {
331 + // Setup controlled state for this useFocus hook
332 + const stateRef = useRef({isFocused: false, isFocusVisible: false});
333 + const focusHandle = useEvent('focus', passiveObject);
334 + const blurHandle = useEvent('blur', passiveObject);
335 + const afterBlurHandle = useEvent('afterblur', passiveObject);
336 + const beforeBlurHandle = useEvent('beforeblur', passiveObject);
337 + const focusVisibleHandles = useFocusVisibleInputHandles();
338 +
339 + useEffect(() => {
340 + const focusWithinTarget = focusWithinTargetRef.current;
341 + const state = stateRef.current;
342 +
343 + if (focusWithinTarget !== null && state !== null) {
344 + // Handle focus visible
345 + setFocusVisibleListeners(
346 + focusVisibleHandles,
347 + focusWithinTarget,
348 + isFocusVisible => {
349 + if (state.isFocused && state.isFocusVisible !== isFocusVisible) {
350 + state.isFocusVisible = isFocusVisible;
351 + if (onFocusWithinVisibleChange) {
352 + onFocusWithinVisibleChange(isFocusVisible);
353 + }
354 + }
355 + },
356 + );
357 +
358 + // Handle focus
359 + focusHandle.setListener(focusWithinTarget, event => {
360 + if (disabled) {
361 + return;
362 + }
363 + if (!state.isFocused) {
364 + state.isFocused = true;
365 + state.isFocusVisible = isGlobalFocusVisible;
366 + if (onFocusWithinChange) {
367 + onFocusWithinChange(true);
368 + }
369 + if (state.isFocusVisible && onFocusWithinVisibleChange) {
370 + onFocusWithinVisibleChange(true);
371 + }
372 + }
373 + if (!state.isFocusVisible && isGlobalFocusVisible) {
374 + state.isFocusVisible = isGlobalFocusVisible;
375 + if (onFocusWithinVisibleChange) {
376 + onFocusWithinVisibleChange(true);
377 + }
378 + }
379 + if (onFocusWithin) {
380 + onFocusWithin(event);
381 + }
382 + isEmulatingMouseEvents = false;
383 + });
384 +
385 + // Handle blur
386 + blurHandle.setListener(focusWithinTarget, event => {
387 + if (disabled) {
388 + return;
389 + }
390 + const {relatedTarget} = (event: any);
391 +
392 + if (
393 + state.isFocused &&
394 + !isRelatedTargetWithin(focusWithinTarget, relatedTarget)
395 + ) {
396 + state.isFocused = false;
397 + if (onFocusWithinChange) {
398 + onFocusWithinChange(false);
399 + }
400 + if (state.isFocusVisible && onFocusWithinVisibleChange) {
401 + onFocusWithinVisibleChange(false);
402 + }
403 + if (onBlurWithin) {
404 + onBlurWithin(event);
405 + }
406 + }
407 + isEmulatingMouseEvents = false;
408 + });
409 +
410 + // Handle before blur. This is a special
411 + // React provided event.
412 + beforeBlurHandle.setListener(focusWithinTarget, event => {
413 + if (disabled) {
414 + return;
415 + }
416 + if (onBeforeBlurWithin) {
417 + onBeforeBlurWithin(event);
418 + // Add an "afterblur" listener on document. This is a special
419 + // React provided event.
420 + afterBlurHandle.setListener(document, afterBlurEvent => {
421 + if (onAfterBlurWithin) {
422 + onAfterBlurWithin(afterBlurEvent);
423 + }
424 + // Clear listener on document
425 + afterBlurHandle.setListener(document, null);
426 + });
427 + }
428 + });
429 + }
430 + }, [
431 + disabled,
432 + onBlurWithin,
433 + onFocusWithin,
434 + onFocusWithinChange,
435 + onFocusWithinVisibleChange,
436 + ]);
437 +
438 + // Mount/Unmount logic
439 + useFocusLifecycles(stateRef);
440 +}
packages/react-interactions/events/src/dom/use-event/__tests__/useFocus-test.internal.js new
+319
@@ -0,0 +1,319 @@
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 + * @emails react-core
8 + */
9 +
10 +'use strict';
11 +
12 +import {createEventTarget, setPointerEvent} from 'dom-event-testing-library';
13 +
14 +let React;
15 +let ReactFeatureFlags;
16 +let ReactDOM;
17 +let useFocus;
18 +
19 +function initializeModules(hasPointerEvents) {
20 + setPointerEvent(hasPointerEvents);
21 + jest.resetModules();
22 + ReactFeatureFlags = require('shared/ReactFeatureFlags');
23 + ReactFeatureFlags.enableModernEventSystem = true;
24 + ReactFeatureFlags.enableUseEventAPI = true;
25 + React = require('react');
26 + ReactDOM = require('react-dom');
27 +
28 + // TODO: This import throws outside of experimental mode. Figure out better
29 + // strategy for gated imports.
30 + if (__EXPERIMENTAL__) {
31 + useFocus = require('react-interactions/events/focus').useFocus;
32 + }
33 +}
34 +
35 +const forcePointerEvents = true;
36 +const table = [[forcePointerEvents], [!forcePointerEvents]];
37 +
38 +describe.each(table)(`useFocus`, hasPointerEvents => {
39 + let container;
40 +
41 + beforeEach(() => {
42 + initializeModules(hasPointerEvents);
43 + container = document.createElement('div');
44 + document.body.appendChild(container);
45 + });
46 +
47 + afterEach(() => {
48 + ReactDOM.render(null, container);
49 + document.body.removeChild(container);
50 + container = null;
51 + });
52 +
53 + describe('disabled', () => {
54 + let onBlur, onFocus, ref;
55 +
56 + const componentInit = () => {
57 + onBlur = jest.fn();
58 + onFocus = jest.fn();
59 + ref = React.createRef();
60 + const Component = () => {
61 + useFocus(ref, {
62 + disabled: true,
63 + onBlur,
64 + onFocus,
65 + });
66 + return <div ref={ref} />;
67 + };
68 + ReactDOM.render(<Component />, container);
69 + };
70 +
71 + // @gate experimental
72 + it('does not call callbacks', () => {
73 + componentInit();
74 + const target = createEventTarget(ref.current);
75 + target.focus();
76 + target.blur();
77 + expect(onFocus).not.toBeCalled();
78 + expect(onBlur).not.toBeCalled();
79 + });
80 + });
81 +
82 + describe('onBlur', () => {
83 + let onBlur, ref;
84 +
85 + const componentInit = () => {
86 + onBlur = jest.fn();
87 + ref = React.createRef();
88 + const Component = () => {
89 + useFocus(ref, {
90 + onBlur,
91 + });
92 + return <div ref={ref} />;
93 + };
94 + ReactDOM.render(<Component />, container);
95 + };
96 +
97 + // @gate experimental
98 + it('is called after "blur" event', () => {
99 + componentInit();
100 + const target = createEventTarget(ref.current);
101 + target.focus();
102 + target.blur();
103 + expect(onBlur).toHaveBeenCalledTimes(1);
104 + });
105 + });
106 +
107 + describe('onFocus', () => {
108 + let onFocus, ref, innerRef;
109 +
110 + const componentInit = () => {
111 + onFocus = jest.fn();
112 + ref = React.createRef();
113 + innerRef = React.createRef();
114 + const Component = () => {
115 + useFocus(ref, {
116 + onFocus,
117 + });
118 + return (
119 + <div ref={ref}>
120 + <a ref={innerRef} />
121 + </div>
122 + );
123 + };
124 + ReactDOM.render(<Component />, container);
125 + };
126 +
127 + // @gate experimental
128 + it('is called after "focus" event', () => {
129 + componentInit();
130 + const target = createEventTarget(ref.current);
131 + target.focus();
132 + expect(onFocus).toHaveBeenCalledTimes(1);
133 + });
134 +
135 + // @gate experimental
136 + it('is not called if descendants of target receive focus', () => {
137 + componentInit();
138 + const target = createEventTarget(innerRef.current);
139 + target.focus();
140 + expect(onFocus).not.toBeCalled();
141 + });
142 + });
143 +
144 + describe('onFocusChange', () => {
145 + let onFocusChange, ref, innerRef;
146 +
147 + const componentInit = () => {
148 + onFocusChange = jest.fn();
149 + ref = React.createRef();
150 + innerRef = React.createRef();
151 + const Component = () => {
152 + useFocus(ref, {
153 + onFocusChange,
154 + });
155 + return (
156 + <div ref={ref}>
157 + <div ref={innerRef} />
158 + </div>
159 + );
160 + };
161 + ReactDOM.render(<Component />, container);
162 + };
163 +
164 + // @gate experimental
165 + it('is called after "blur" and "focus" events', () => {
166 + componentInit();
167 + const target = createEventTarget(ref.current);
168 + target.focus();
169 + expect(onFocusChange).toHaveBeenCalledTimes(1);
170 + expect(onFocusChange).toHaveBeenCalledWith(true);
171 + target.blur();
172 + expect(onFocusChange).toHaveBeenCalledTimes(2);
173 + expect(onFocusChange).toHaveBeenCalledWith(false);
174 + });
175 +
176 + // @gate experimental
177 + it('is not called after "blur" and "focus" events on descendants', () => {
178 + componentInit();
179 + const target = createEventTarget(innerRef.current);
180 + target.focus();
181 + expect(onFocusChange).toHaveBeenCalledTimes(0);
182 + target.blur();
183 + expect(onFocusChange).toHaveBeenCalledTimes(0);
184 + });
185 + });
186 +
187 + describe('onFocusVisibleChange', () => {
188 + let onFocusVisibleChange, ref, innerRef;
189 +
190 + const componentInit = () => {
191 + onFocusVisibleChange = jest.fn();
192 + ref = React.createRef();
193 + innerRef = React.createRef();
194 + const Component = () => {
195 + useFocus(ref, {
196 + onFocusVisibleChange,
197 + });
198 + return (
199 + <div ref={ref}>
200 + <div ref={innerRef} />
201 + </div>
202 + );
203 + };
204 + ReactDOM.render(<Component />, container);
205 + };
206 +
207 + // @gate experimental
208 + it('is called after "focus" and "blur" if keyboard navigation is active', () => {
209 + componentInit();
210 + const target = createEventTarget(ref.current);
211 + const containerTarget = createEventTarget(container);
212 + // use keyboard first
213 + containerTarget.keydown({key: 'Tab'});
214 + target.focus();
215 + expect(onFocusVisibleChange).toHaveBeenCalledTimes(1);
216 + expect(onFocusVisibleChange).toHaveBeenCalledWith(true);
217 + target.blur({relatedTarget: container});
218 + expect(onFocusVisibleChange).toHaveBeenCalledTimes(2);
219 + expect(onFocusVisibleChange).toHaveBeenCalledWith(false);
220 + });
221 +
222 + // @gate experimental
223 + it('is called if non-keyboard event is dispatched on target previously focused with keyboard', () => {
224 + componentInit();
225 + const target = createEventTarget(ref.current);
226 + const containerTarget = createEventTarget(container);
227 + // use keyboard first
228 + containerTarget.keydown({key: 'Tab'});
229 + target.focus();
230 + expect(onFocusVisibleChange).toHaveBeenCalledTimes(1);
231 + expect(onFocusVisibleChange).toHaveBeenCalledWith(true);
232 + // then use pointer on the target, focus should no longer be visible
233 + target.pointerdown();
234 + expect(onFocusVisibleChange).toHaveBeenCalledTimes(2);
235 + expect(onFocusVisibleChange).toHaveBeenCalledWith(false);
236 + // onFocusVisibleChange should not be called again
237 + target.blur({relatedTarget: container});
238 + expect(onFocusVisibleChange).toHaveBeenCalledTimes(2);
239 + });
240 +
241 + // @gate experimental
242 + it('is not called after "focus" and "blur" events without keyboard', () => {
243 + componentInit();
244 + const target = createEventTarget(ref.current);
245 + const containerTarget = createEventTarget(container);
246 + target.pointerdown();
247 + target.pointerup();
248 + containerTarget.pointerdown();
249 + target.blur({relatedTarget: container});
250 + expect(onFocusVisibleChange).toHaveBeenCalledTimes(0);
251 + });
252 +
253 + // @gate experimental
254 + it('is not called after "blur" and "focus" events on descendants', () => {
255 + componentInit();
256 + const innerTarget = createEventTarget(innerRef.current);
257 + const containerTarget = createEventTarget(container);
258 + containerTarget.keydown({key: 'Tab'});
259 + innerTarget.focus();
260 + expect(onFocusVisibleChange).toHaveBeenCalledTimes(0);
261 + innerTarget.blur({relatedTarget: container});
262 + expect(onFocusVisibleChange).toHaveBeenCalledTimes(0);
263 + });
264 + });
265 +
266 + describe('nested Focus components', () => {
267 + // @gate experimental
268 + it('propagates events in the correct order', () => {
269 + const events = [];
270 + const innerRef = React.createRef();
271 + const outerRef = React.createRef();
272 + const createEventHandler = msg => () => {
273 + events.push(msg);
274 + };
275 +
276 + const Inner = () => {
277 + useFocus(innerRef, {
278 + onBlur: createEventHandler('inner: onBlur'),
279 + onFocus: createEventHandler('inner: onFocus'),
280 + onFocusChange: createEventHandler('inner: onFocusChange'),
281 + });
282 + return <div ref={innerRef} />;
283 + };
284 +
285 + const Outer = () => {
286 + useFocus(outerRef, {
287 + onBlur: createEventHandler('outer: onBlur'),
288 + onFocus: createEventHandler('outer: onFocus'),
289 + onFocusChange: createEventHandler('outer: onFocusChange'),
290 + });
291 + return (
292 + <div ref={outerRef}>
293 + <Inner />
294 + </div>
295 + );
296 + };
297 +
298 + ReactDOM.render(<Outer />, container);
299 +
300 + const innerTarget = createEventTarget(innerRef.current);
301 + const outerTarget = createEventTarget(outerRef.current);
302 +
303 + outerTarget.focus();
304 + outerTarget.blur();
305 + innerTarget.focus();
306 + innerTarget.blur();
307 + expect(events).toEqual([
308 + 'outer: onFocus',
309 + 'outer: onFocusChange',
310 + 'outer: onBlur',
311 + 'outer: onFocusChange',
312 + 'inner: onFocus',
313 + 'inner: onFocusChange',
314 + 'inner: onBlur',
315 + 'inner: onFocusChange',
316 + ]);
317 + });
318 + });
319 +});
packages/react-interactions/events/src/dom/use-event/__tests__/useFocusWithin-test.internal.js new
+467
@@ -0,0 +1,467 @@
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 + * @emails react-core
8 + */
9 +
10 +'use strict';
11 +
12 +import {createEventTarget, setPointerEvent} from 'dom-event-testing-library';
13 +
14 +let React;
15 +let ReactFeatureFlags;
16 +let ReactDOM;
17 +let useFocusWithin;
18 +let ReactTestRenderer;
19 +let act;
20 +
21 +function initializeModules(hasPointerEvents) {
22 + setPointerEvent(hasPointerEvents);
23 + jest.resetModules();
24 + ReactFeatureFlags = require('shared/ReactFeatureFlags');
25 + ReactFeatureFlags.enableScopeAPI = true;
26 + ReactFeatureFlags.enableModernEventSystem = true;
27 + ReactFeatureFlags.enableUseEventAPI = true;
28 + React = require('react');
29 + ReactDOM = require('react-dom');
30 + ReactTestRenderer = require('react-test-renderer');
31 + act = ReactTestRenderer.act;
32 +
33 + // TODO: This import throws outside of experimental mode. Figure out better
34 + // strategy for gated imports.
35 + if (__EXPERIMENTAL__) {
36 + useFocusWithin = require('react-interactions/events/focus').useFocusWithin;
37 + }
38 +}
39 +
40 +const forcePointerEvents = true;
41 +const table = [[forcePointerEvents], [!forcePointerEvents]];
42 +
43 +describe.each(table)(`useFocus`, hasPointerEvents => {
44 + let container;
45 +
46 + beforeEach(() => {
47 + initializeModules(hasPointerEvents);
48 + container = document.createElement('div');
49 + document.body.appendChild(container);
50 + });
51 +
52 + afterEach(() => {
53 + ReactDOM.render(null, container);
54 + document.body.removeChild(container);
55 + container = null;
56 + });
57 +
58 + describe('disabled', () => {
59 + let onFocusWithinChange, onFocusWithinVisibleChange, ref;
60 +
61 + const componentInit = () => {
62 + onFocusWithinChange = jest.fn();
63 + onFocusWithinVisibleChange = jest.fn();
64 + ref = React.createRef();
65 + const Component = () => {
66 + useFocusWithin(ref, {
67 + disabled: true,
68 + onFocusWithinChange,
69 + onFocusWithinVisibleChange,
70 + });
71 + return <div ref={ref} />;
72 + };
73 + ReactDOM.render(<Component />, container);
74 + };
75 +
76 + // @gate experimental
77 + it('prevents custom events being dispatched', () => {
78 + componentInit();
79 + const target = createEventTarget(ref.current);
80 + target.focus();
81 + target.blur();
82 + expect(onFocusWithinChange).not.toBeCalled();
83 + expect(onFocusWithinVisibleChange).not.toBeCalled();
84 + });
85 + });
86 +
87 + describe('onFocusWithinChange', () => {
88 + let onFocusWithinChange, ref, innerRef, innerRef2;
89 +
90 + const Component = ({show}) => {
91 + useFocusWithin(ref, {
92 + onFocusWithinChange,
93 + });
94 + return (
95 + <div ref={ref}>
96 + {show && <input ref={innerRef} />}
97 + <div ref={innerRef2} />
98 + </div>
99 + );
100 + };
101 +
102 + const componentInit = () => {
103 + onFocusWithinChange = jest.fn();
104 + ref = React.createRef();
105 + innerRef = React.createRef();
106 + innerRef2 = React.createRef();
107 + ReactDOM.render(<Component show={true} />, container);
108 + };
109 +
110 + // @gate experimental
111 + it('is called after "blur" and "focus" events on focus target', () => {
112 + componentInit();
113 + const target = createEventTarget(ref.current);
114 + target.focus();
115 + expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
116 + expect(onFocusWithinChange).toHaveBeenCalledWith(true);
117 + target.blur({relatedTarget: container});
118 + expect(onFocusWithinChange).toHaveBeenCalledTimes(2);
119 + expect(onFocusWithinChange).toHaveBeenCalledWith(false);
120 + });
121 +
122 + // @gate experimental
123 + it('is called after "blur" and "focus" events on descendants', () => {
124 + componentInit();
125 + const target = createEventTarget(innerRef.current);
126 + target.focus();
127 + expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
128 + expect(onFocusWithinChange).toHaveBeenCalledWith(true);
129 + target.blur({relatedTarget: container});
130 + expect(onFocusWithinChange).toHaveBeenCalledTimes(2);
131 + expect(onFocusWithinChange).toHaveBeenCalledWith(false);
132 + });
133 +
134 + // @gate experimental
135 + it('is only called once when focus moves within and outside the subtree', () => {
136 + componentInit();
137 + const node = ref.current;
138 + const innerNode1 = innerRef.current;
139 + const innerNode2 = innerRef.current;
140 + const target = createEventTarget(node);
141 + const innerTarget1 = createEventTarget(innerNode1);
142 + const innerTarget2 = createEventTarget(innerNode2);
143 +
144 + // focus shifts into subtree
145 + innerTarget1.focus();
146 + expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
147 + expect(onFocusWithinChange).toHaveBeenCalledWith(true);
148 + // focus moves around subtree
149 + innerTarget1.blur({relatedTarget: innerNode2});
150 + innerTarget2.focus();
151 + innerTarget2.blur({relatedTarget: node});
152 + target.focus();
153 + target.blur({relatedTarget: innerNode1});
154 + expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
155 + // focus shifts outside subtree
156 + innerTarget1.blur({relatedTarget: container});
157 + expect(onFocusWithinChange).toHaveBeenCalledTimes(2);
158 + expect(onFocusWithinChange).toHaveBeenCalledWith(false);
159 + });
160 + });
161 +
162 + describe('onFocusWithinVisibleChange', () => {
163 + let onFocusWithinVisibleChange, ref, innerRef, innerRef2;
164 +
165 + const Component = ({show}) => {
166 + useFocusWithin(ref, {
167 + onFocusWithinVisibleChange,
168 + });
169 + return (
170 + <div ref={ref}>
171 + {show && <input ref={innerRef} />}
172 + <div ref={innerRef2} />
173 + </div>
174 + );
175 + };
176 +
177 + const componentInit = () => {
178 + onFocusWithinVisibleChange = jest.fn();
179 + ref = React.createRef();
180 + innerRef = React.createRef();
181 + innerRef2 = React.createRef();
182 + ReactDOM.render(<Component show={true} />, container);
183 + };
184 +
185 + // @gate experimental
186 + it('is called after "focus" and "blur" on focus target if keyboard was used', () => {
187 + componentInit();
188 + const target = createEventTarget(ref.current);
189 + const containerTarget = createEventTarget(container);
190 + // use keyboard first
191 + containerTarget.keydown({key: 'Tab'});
192 + target.focus();
193 + expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
194 + expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
195 + target.blur({relatedTarget: container});
196 + expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
197 + expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
198 + });
199 +
200 + // @gate experimental
201 + it('is called after "focus" and "blur" on descendants if keyboard was used', () => {
202 + componentInit();
203 + const innerTarget = createEventTarget(innerRef.current);
204 + const containerTarget = createEventTarget(container);
205 + // use keyboard first
206 + containerTarget.keydown({key: 'Tab'});
207 + innerTarget.focus();
208 + expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
209 + expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
210 + innerTarget.blur({relatedTarget: container});
211 + expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
212 + expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
213 + });
214 +
215 + // @gate experimental
216 + it('is called if non-keyboard event is dispatched on target previously focused with keyboard', () => {
217 + componentInit();
218 + const node = ref.current;
219 + const innerNode1 = innerRef.current;
220 + const innerNode2 = innerRef2.current;
221 +
222 + const target = createEventTarget(node);
223 + const innerTarget1 = createEventTarget(innerNode1);
224 + const innerTarget2 = createEventTarget(innerNode2);
225 + // use keyboard first
226 + target.focus();
227 + target.keydown({key: 'Tab'});
228 + target.blur({relatedTarget: innerNode1});
229 + innerTarget1.focus();
230 + expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
231 + expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
232 + // then use pointer on the next target, focus should no longer be visible
233 + innerTarget2.pointerdown();
234 + innerTarget1.blur({relatedTarget: innerNode2});
235 + innerTarget2.focus();
236 + expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
237 + expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
238 + // then use keyboard again
239 + innerTarget2.keydown({key: 'Tab', shiftKey: true});
240 + innerTarget2.blur({relatedTarget: innerNode1});
241 + innerTarget1.focus();
242 + expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(3);
243 + expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
244 + // then use pointer on the target, focus should no longer be visible
245 + innerTarget1.pointerdown();
246 + expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(4);
247 + expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
248 + // onFocusVisibleChange should not be called again
249 + innerTarget1.blur({relatedTarget: container});
250 + expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(4);
251 + });
252 +
253 + // @gate experimental
254 + it('is not called after "focus" and "blur" events without keyboard', () => {
255 + componentInit();
256 + const innerTarget = createEventTarget(innerRef.current);
257 + innerTarget.pointerdown();
258 + innerTarget.pointerup();
259 + innerTarget.blur({relatedTarget: container});
260 + expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(0);
261 + });
262 +
263 + // @gate experimental
264 + it('is only called once when focus moves within and outside the subtree', () => {
265 + componentInit();
266 + const node = ref.current;
267 + const innerNode1 = innerRef.current;
268 + const innerNode2 = innerRef2.current;
269 + const target = createEventTarget(node);
270 + const innerTarget1 = createEventTarget(innerNode1);
271 + const innerTarget2 = createEventTarget(innerNode2);
272 +
273 + // focus shifts into subtree
274 + innerTarget1.focus();
275 + expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
276 + expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
277 + // focus moves around subtree
278 + innerTarget1.blur({relatedTarget: innerNode2});
279 + innerTarget2.focus();
280 + innerTarget2.blur({relatedTarget: node});
281 + target.focus();
282 + target.blur({relatedTarget: innerNode1});
283 + expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
284 + // focus shifts outside subtree
285 + innerTarget1.blur({relatedTarget: container});
286 + expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
287 + expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
288 + });
289 + });
290 +
291 + describe('onBeforeBlurWithin', () => {
292 + let onBeforeBlurWithin, onAfterBlurWithin, ref, innerRef, innerRef2;
293 +
294 + beforeEach(() => {
295 + onBeforeBlurWithin = jest.fn();
296 + onAfterBlurWithin = jest.fn(e => {
297 + e.persist();
298 + });
299 + ref = React.createRef();
300 + innerRef = React.createRef();
301 + innerRef2 = React.createRef();
302 + });
303 +
304 + // @gate experimental
305 + it('is called after a focused element is unmounted', () => {
306 + const Component = ({show}) => {
307 + useFocusWithin(ref, {
308 + onBeforeBlurWithin,
309 + onAfterBlurWithin,
310 + });
311 + return (
312 + <div ref={ref}>
313 + {show && <input ref={innerRef} />}
314 + <div ref={innerRef2} />
315 + </div>
316 + );
317 + };
318 +
319 + ReactDOM.render(<Component show={true} />, container);
320 +
321 + const inner = innerRef.current;
322 + const target = createEventTarget(inner);
323 + target.keydown({key: 'Tab'});
324 + target.focus();
325 + expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
326 + expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
327 + ReactDOM.render(<Component show={false} />, container);
328 + expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
329 + expect(onAfterBlurWithin).toHaveBeenCalledTimes(1);
330 + expect(onAfterBlurWithin).toHaveBeenCalledWith(
331 + expect.objectContaining({relatedTarget: inner}),
332 + );
333 + });
334 +
335 + // @gate experimental
336 + it('is called after a nested focused element is unmounted', () => {
337 + const Component = ({show}) => {
338 + useFocusWithin(ref, {
339 + onBeforeBlurWithin,
340 + onAfterBlurWithin,
341 + });
342 + return (
343 + <div ref={ref}>
344 + {show && (
345 + <div>
346 + <input ref={innerRef} />
347 + </div>
348 + )}
349 + <div ref={innerRef2} />
350 + </div>
351 + );
352 + };
353 +
354 + ReactDOM.render(<Component show={true} />, container);
355 +
356 + const inner = innerRef.current;
357 + const target = createEventTarget(inner);
358 + target.keydown({key: 'Tab'});
359 + target.focus();
360 + expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
361 + expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
362 + ReactDOM.render(<Component show={false} />, container);
363 + expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
364 + expect(onAfterBlurWithin).toHaveBeenCalledTimes(1);
365 + expect(onAfterBlurWithin).toHaveBeenCalledWith(
366 + expect.objectContaining({relatedTarget: inner}),
367 + );
368 + });
369 +
370 + // @gate experimental
371 + it('is called after a nested focused element is unmounted (with scope query)', () => {
372 + const TestScope = React.unstable_createScope();
373 + const testScopeQuery = (type, props) => true;
374 + let targetNodes;
375 + let targetNode;
376 +
377 + const Component = ({show}) => {
378 + const scopeRef = React.useRef(null);
379 + useFocusWithin(scopeRef, {
380 + onBeforeBlurWithin(event) {
381 + const scope = scopeRef.current;
382 + targetNode = innerRef.current;
383 + targetNodes = scope.DO_NOT_USE_queryAllNodes(testScopeQuery);
384 + },
385 + });
386 +
387 + return (
388 + <TestScope ref={scopeRef}>
389 + {show && <input ref={innerRef} />}
390 + </TestScope>
391 + );
392 + };
393 +
394 + ReactDOM.render(<Component show={true} />, container);
395 +
396 + const inner = innerRef.current;
397 + const target = createEventTarget(inner);
398 + target.keydown({key: 'Tab'});
399 + target.focus();
400 + ReactDOM.render(<Component show={false} />, container);
401 + expect(targetNodes).toEqual([targetNode]);
402 + });
403 +
404 + // @gate experimental
405 + it('is called after a focused suspended element is hidden', () => {
406 + const Suspense = React.Suspense;
407 + let suspend = false;
408 + let resolve;
409 + const promise = new Promise(resolvePromise => (resolve = resolvePromise));
410 +
411 + function Child() {
412 + if (suspend) {
413 + throw promise;
414 + } else {
415 + return <input ref={innerRef} />;
416 + }
417 + }
418 +
419 + const Component = ({show}) => {
420 + useFocusWithin(ref, {
421 + onBeforeBlurWithin,
422 + onAfterBlurWithin,
423 + });
424 +
425 + return (
426 + <div ref={ref}>
427 + <Suspense fallback="Loading...">
428 + <Child />
429 + </Suspense>
430 + </div>
431 + );
432 + };
433 +
434 + const container2 = document.createElement('div');
435 + document.body.appendChild(container2);
436 +
437 + const root = ReactDOM.createRoot(container2);
438 +
439 + act(() => {
440 + root.render(<Component />);
441 + });
442 + jest.runAllTimers();
443 + expect(container2.innerHTML).toBe('<div><input></div>');
444 +
445 + const inner = innerRef.current;
446 + const target = createEventTarget(inner);
447 + target.keydown({key: 'Tab'});
448 + target.focus();
449 + expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
450 + expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
451 +
452 + suspend = true;
453 + act(() => {
454 + root.render(<Component />);
455 + });
456 + jest.runAllTimers();
457 + expect(container2.innerHTML).toBe(
458 + '<div><input style="display: none;">Loading...</div>',
459 + );
460 + expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
461 + expect(onAfterBlurWithin).toHaveBeenCalledTimes(1);
462 + resolve();
463 +
464 + document.body.removeChild(container2);
465 + });
466 + });
467 +});
packages/react-reconciler/src/ReactFiberHooks.new.js
+2 -2
@@ -1757,7 +1757,7 @@ function validateNotInFunctionRender(): boolean {
1757
1758 function createReactListener(
1759 event: ReactListenerEvent,
1760 - callback: Event => void,
1760 + callback: (SyntheticEvent<EventTarget>) => void,
1761 target: EventTarget | ReactScopeMethods,
1762 destroy: Node => void,
1763 ): ReactListener {
@@ -1812,7 +1812,7 @@ function mountEventListener(event: ReactListenerEvent): ReactListenerMap {
1812 clear,
1813 setListener(
1814 target: EventTarget | ReactScopeMethods,
1815 - callback: ?(Event) => void,
1815 + callback: ?(SyntheticEvent<EventTarget>) => void,
1816 ): void {
1817 if (
1818 validateNotInFunctionRender() &&
packages/react-reconciler/src/ReactFiberHooks.old.js
+2 -2
@@ -1752,7 +1752,7 @@ function validateNotInFunctionRender(): boolean {
1752
1753 function createReactListener(
1754 event: ReactListenerEvent,
1755 - callback: Event => void,
1755 + callback: (SyntheticEvent<EventTarget>) => void,
1756 target: EventTarget | ReactScopeMethods,
1757 destroy: Node => void,
1758 ): ReactListener {
@@ -1807,7 +1807,7 @@ function mountEventListener(event: ReactListenerEvent): ReactListenerMap {
1807 clear,
1808 setListener(
1809 target: EventTarget | ReactScopeMethods,
1810 - callback: ?(Event) => void,
1810 + callback: ?(SyntheticEvent<EventTarget>) => void,
1811 ): void {
1812 if (
1813 validateNotInFunctionRender() &&
scripts/rollup/bundles.js
+1 -1
@@ -689,7 +689,7 @@ const bundles = [
689 FB_WWW_PROD,
690 ],
691 moduleType: NON_FIBER_RENDERER,
692 - entry: 'react-interactions/events/focus',
692 + entry: 'react-interactions/events/deprecated-focus',
693 global: 'ReactEventsFocus',
694 externals: ['react'],
695 },