Unify Flare FocusWithin responder with useFocusWithin (#18636)
Dominic Gannaway committed
Apr 16, 2020 at 19:35 UTC
f24a9e70980c2b1d0eb8309eb751315ff39f96bd
3 files changed
+53
-32
packages/react-dom/src/client/ReactDOMHostConfig.js
+2
-3
@@ -555,11 +555,10 @@ function dispatchBeforeDetachedBlur(target: HTMLElement): void {
555
function dispatchAfterDetachedBlur(target: HTMLElement): void {
556
if (enableDeprecatedFlareAPI) {
557
DEPRECATED_dispatchEventForResponderEventSystem(
558
- 'blur',
558
+ 'afterblur',
559
null,
560
({
561
- isTargetAttached: false,
562
- target,
561
+ relatedTarget: target,
562
timeStamp: Date.now(),
563
}: any),
564
target,
packages/react-interactions/events/src/dom/DeprecatedFocus.js
+36
-14
@@ -22,7 +22,7 @@ import {DiscreteEvent} from 'shared/ReactTypes';
22
*/
23
24
type FocusEvent = {|
25
- isTargetAttached: boolean,
25
+ relatedTarget: null | Element | Document,
26
target: Element | Document,
27
type: FocusEventType | FocusWithinEventType,
28
pointerType: PointerType,
@@ -53,6 +53,7 @@ type FocusEventType = 'focus' | 'blur' | 'focuschange' | 'focusvisiblechange';
53
type FocusWithinProps = {
54
disabled?: boolean,
55
onFocusWithin?: (e: FocusEvent) => void,
56
+ onAfterBlurWithin?: (e: FocusEvent) => void,
57
onBeforeBlurWithin?: (e: FocusEvent) => void,
58
onBlurWithin?: (e: FocusEvent) => void,
59
onFocusWithinChange?: boolean => void,
@@ -65,7 +66,8 @@ type FocusWithinEventType =
66
| 'focuswithinchange'
67
| 'blurwithin'
68
| 'focuswithin'
68
- | 'beforeblurwithin';
69
+ | 'beforeblurwithin'
70
+ | 'afterblurwithin';
71
72
/**
73
* Shared between Focus and FocusWithin
@@ -116,8 +118,7 @@ const focusVisibleEvents = hasPointerEvents
118
119
const targetEventTypes = ['focus', 'blur', 'beforeblur', ...focusVisibleEvents];
120
119
-// Used only for the blur "detachedTarget" logic
120
-const rootEventTypes = ['blur'];
121
+const rootEventTypes = ['afterblur'];
122
123
function addWindowEventListener(types, callback, options) {
124
types.forEach(type => {
@@ -192,10 +193,10 @@ function createFocusEvent(
193
type: FocusEventType | FocusWithinEventType,
194
target: Element | Document,
195
pointerType: PointerType,
195
- isTargetAttached: boolean,
196
+ relatedTarget: null | Element | Document,
197
): FocusEvent {
198
return {
198
- isTargetAttached,
199
+ relatedTarget,
200
target,
201
type,
202
pointerType,
@@ -297,7 +298,7 @@ function dispatchFocusEvents(
298
'focus',
299
target,
300
pointerType,
300
- true,
301
+ null,
302
);
303
context.dispatchEvent(syntheticEvent, onFocus, DiscreteEvent);
304
}
@@ -321,7 +322,7 @@ function dispatchBlurEvents(
322
'blur',
323
target,
324
pointerType,
324
- true,
325
+ null,
326
);
327
context.dispatchEvent(syntheticEvent, onBlur, DiscreteEvent);
328
}
@@ -346,7 +347,7 @@ function dispatchFocusWithinEvents(
347
'focuswithin',
348
target,
349
pointerType,
349
- true,
350
+ null,
351
);
352
context.dispatchEvent(syntheticEvent, onFocusWithin, DiscreteEvent);
353
}
@@ -361,19 +362,40 @@ function dispatchBlurWithinEvents(
362
const pointerType = state.pointerType;
363
const target = ((state.focusTarget: any): Element | Document) || event.target;
364
const onBlurWithin = (props.onBlurWithin: any);
364
- const isTargetAttached = state.detachedTarget === null;
365
if (isFunction(onBlurWithin)) {
366
const syntheticEvent = createFocusEvent(
367
context,
368
'blurwithin',
369
target,
370
pointerType,
371
- isTargetAttached,
371
+ null,
372
);
373
context.dispatchEvent(syntheticEvent, onBlurWithin, DiscreteEvent);
374
}
375
}
376
377
+function dispatchAfterBlurWithinEvents(
378
+ context: ReactDOMResponderContext,
379
+ event: ReactDOMResponderEvent,
380
+ props: FocusWithinProps,
381
+ state: FocusState,
382
+) {
383
+ const pointerType = state.pointerType;
384
+ const target = ((state.focusTarget: any): Element | Document) || event.target;
385
+ const onAfterBlurWithin = (props.onAfterBlurWithin: any);
386
+ const relatedTarget = state.detachedTarget;
387
+ if (isFunction(onAfterBlurWithin)) {
388
+ const syntheticEvent = createFocusEvent(
389
+ context,
390
+ 'afterblurwithin',
391
+ target,
392
+ pointerType,
393
+ relatedTarget,
394
+ );
395
+ context.dispatchEvent(syntheticEvent, onAfterBlurWithin, DiscreteEvent);
396
+ }
397
+}
398
+
399
function dispatchFocusChange(
400
context: ReactDOMResponderContext,
401
props: FocusProps,
@@ -616,7 +638,7 @@ const focusWithinResponderImpl = {
638
'beforeblurwithin',
639
event.target,
640
state.pointerType,
619
- true,
641
+ null,
642
);
643
state.detachedTarget = event.target;
644
context.dispatchEvent(
@@ -660,10 +682,10 @@ const focusWithinResponderImpl = {
682
props: FocusWithinProps,
683
state: FocusState,
684
): void {
663
- if (event.type === 'blur') {
685
+ if (event.type === 'afterblur') {
686
const detachedTarget = state.detachedTarget;
687
if (detachedTarget !== null && detachedTarget === event.target) {
666
- dispatchBlurWithinEvents(context, event, props, state);
688
+ dispatchAfterBlurWithinEvents(context, event, props, state);
689
state.detachedTarget = null;
690
if (state.addedRootEvents) {
691
state.addedRootEvents = false;
packages/react-interactions/events/src/dom/__tests__/FocusWithin-test.internal.js
+15
-15
@@ -290,11 +290,11 @@ describe.each(table)('FocusWithin responder', hasPointerEvents => {
290
});
291
292
describe('onBeforeBlurWithin', () => {
293
- let onBeforeBlurWithin, onBlurWithin, ref, innerRef, innerRef2;
293
+ let onBeforeBlurWithin, onAfterBlurWithin, ref, innerRef, innerRef2;
294
295
beforeEach(() => {
296
onBeforeBlurWithin = jest.fn();
297
- onBlurWithin = jest.fn();
297
+ onAfterBlurWithin = jest.fn();
298
ref = React.createRef();
299
innerRef = React.createRef();
300
innerRef2 = React.createRef();
@@ -305,7 +305,7 @@ describe.each(table)('FocusWithin responder', hasPointerEvents => {
305
const Component = ({show}) => {
306
const listener = useFocusWithin({
307
onBeforeBlurWithin,
308
- onBlurWithin,
308
+ onAfterBlurWithin,
309
});
310
return (
311
<div ref={ref} DEPRECATED_flareListeners={listener}>
@@ -322,12 +322,12 @@ describe.each(table)('FocusWithin responder', hasPointerEvents => {
322
target.keydown({key: 'Tab'});
323
target.focus();
324
expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
325
- expect(onBlurWithin).toHaveBeenCalledTimes(0);
325
+ expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
326
ReactDOM.render(<Component show={false} />, container);
327
expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
328
- expect(onBlurWithin).toHaveBeenCalledTimes(1);
329
- expect(onBlurWithin).toHaveBeenCalledWith(
330
- expect.objectContaining({isTargetAttached: false}),
328
+ expect(onAfterBlurWithin).toHaveBeenCalledTimes(1);
329
+ expect(onAfterBlurWithin).toHaveBeenCalledWith(
330
+ expect.objectContaining({relatedTarget: inner}),
331
);
332
});
333
@@ -336,7 +336,7 @@ describe.each(table)('FocusWithin responder', hasPointerEvents => {
336
const Component = ({show}) => {
337
const listener = useFocusWithin({
338
onBeforeBlurWithin,
339
- onBlurWithin,
339
+ onAfterBlurWithin,
340
});
341
return (
342
<div ref={ref} DEPRECATED_flareListeners={listener}>
@@ -357,12 +357,12 @@ describe.each(table)('FocusWithin responder', hasPointerEvents => {
357
target.keydown({key: 'Tab'});
358
target.focus();
359
expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
360
- expect(onBlurWithin).toHaveBeenCalledTimes(0);
360
+ expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
361
ReactDOM.render(<Component show={false} />, container);
362
expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
363
- expect(onBlurWithin).toHaveBeenCalledTimes(1);
364
- expect(onBlurWithin).toHaveBeenCalledWith(
365
- expect.objectContaining({isTargetAttached: false}),
363
+ expect(onAfterBlurWithin).toHaveBeenCalledTimes(1);
364
+ expect(onAfterBlurWithin).toHaveBeenCalledWith(
365
+ expect.objectContaining({relatedTarget: inner}),
366
);
367
});
368
@@ -418,7 +418,7 @@ describe.each(table)('FocusWithin responder', hasPointerEvents => {
418
const Component = ({show}) => {
419
const listener = useFocusWithin({
420
onBeforeBlurWithin,
421
- onBlurWithin,
421
+ onAfterBlurWithin,
422
});
423
424
return (
@@ -444,7 +444,7 @@ describe.each(table)('FocusWithin responder', hasPointerEvents => {
444
target.keydown({key: 'Tab'});
445
target.focus();
446
expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
447
- expect(onBlurWithin).toHaveBeenCalledTimes(0);
447
+ expect(onAfterBlurWithin).toHaveBeenCalledTimes(0);
448
449
suspend = true;
450
root.render(<Component />);
@@ -454,7 +454,7 @@ describe.each(table)('FocusWithin responder', hasPointerEvents => {
454
'<div><input style="display: none;">Loading...</div>',
455
);
456
expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
457
- expect(onBlurWithin).toHaveBeenCalledTimes(1);
457
+ expect(onAfterBlurWithin).toHaveBeenCalledTimes(1);
458
resolve();
459
460
document.body.removeChild(container2);