From fae88534becd30fc1765dbf1235b344f46ab9e85 Mon Sep 17 00:00:00 2001 From: Josh Story Date: Tue, 9 Apr 2024 12:00:31 -0700 Subject: [PATCH] Removes the react-interactions project which is unused --- packages/react-interactions/README.md | 4 - packages/react-interactions/events/focus.js | 10 - .../src/dom/create-event-handle/Focus.js | 400 ----------- .../__tests__/useFocus-test.internal.js | 335 ---------- .../__tests__/useFocusWithin-test.internal.js | 626 ------------------ .../src/dom/create-event-handle/useEvent.js | 72 -- packages/react-interactions/npm/drag.js | 7 - packages/react-interactions/npm/focus.js | 7 - packages/react-interactions/npm/hover.js | 7 - packages/react-interactions/npm/input.js | 7 - .../react-interactions/npm/press-legacy.js | 7 - packages/react-interactions/npm/press.js | 7 - packages/react-interactions/npm/scroll.js | 7 - packages/react-interactions/npm/swipe.js | 7 - packages/react-interactions/npm/tap.js | 7 - packages/react-interactions/package.json | 39 -- 16 files changed, 1549 deletions(-) delete mode 100644 packages/react-interactions/README.md delete mode 100644 packages/react-interactions/events/focus.js delete mode 100644 packages/react-interactions/events/src/dom/create-event-handle/Focus.js delete mode 100644 packages/react-interactions/events/src/dom/create-event-handle/__tests__/useFocus-test.internal.js delete mode 100644 packages/react-interactions/events/src/dom/create-event-handle/__tests__/useFocusWithin-test.internal.js delete mode 100644 packages/react-interactions/events/src/dom/create-event-handle/useEvent.js delete mode 100644 packages/react-interactions/npm/drag.js delete mode 100644 packages/react-interactions/npm/focus.js delete mode 100644 packages/react-interactions/npm/hover.js delete mode 100644 packages/react-interactions/npm/input.js delete mode 100644 packages/react-interactions/npm/press-legacy.js delete mode 100644 packages/react-interactions/npm/press.js delete mode 100644 packages/react-interactions/npm/scroll.js delete mode 100644 packages/react-interactions/npm/swipe.js delete mode 100644 packages/react-interactions/npm/tap.js delete mode 100644 packages/react-interactions/package.json diff --git a/packages/react-interactions/README.md b/packages/react-interactions/README.md deleted file mode 100644 index 2326ed26c5acc..0000000000000 --- a/packages/react-interactions/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# `react-interactions` - -This package is experimental. It is intended for use with the experimental React -flags for internal testing. \ No newline at end of file diff --git a/packages/react-interactions/events/focus.js b/packages/react-interactions/events/focus.js deleted file mode 100644 index c4421b2eda17c..0000000000000 --- a/packages/react-interactions/events/focus.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -export * from './src/dom/create-event-handle/Focus'; diff --git a/packages/react-interactions/events/src/dom/create-event-handle/Focus.js b/packages/react-interactions/events/src/dom/create-event-handle/Focus.js deleted file mode 100644 index e6cb7302e7520..0000000000000 --- a/packages/react-interactions/events/src/dom/create-event-handle/Focus.js +++ /dev/null @@ -1,400 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -import * as React from 'react'; -import useEvent from './useEvent'; - -const {useCallback, useEffect, useLayoutEffect, useRef} = React; - -type FocusEvent = SyntheticEvent; - -type UseFocusOptions = { - disabled?: boolean, - onBlur?: ?(FocusEvent) => void, - onFocus?: ?(FocusEvent) => void, - onFocusChange?: ?(boolean) => void, - onFocusVisibleChange?: ?(boolean) => void, -}; - -type UseFocusWithinOptions = { - disabled?: boolean, - onAfterBlurWithin?: FocusEvent => void, - onBeforeBlurWithin?: FocusEvent => void, - onBlurWithin?: FocusEvent => void, - onFocusWithin?: FocusEvent => void, - onFocusWithinChange?: boolean => void, - onFocusWithinVisibleChange?: boolean => void, -}; - -const isMac = - typeof window !== 'undefined' && window.navigator != null - ? /^Mac/.test(window.navigator.platform) - : false; - -const hasPointerEvents = - typeof window !== 'undefined' && window.PointerEvent != null; - -const globalFocusVisibleEvents = hasPointerEvents - ? ['keydown', 'pointermove', 'pointerdown', 'pointerup'] - : [ - 'keydown', - 'mousedown', - 'mousemove', - 'mouseup', - 'touchmove', - 'touchstart', - 'touchend', - ]; - -// Global state for tracking focus visible and emulation of mouse -let isGlobalFocusVisible = true; -let hasTrackedGlobalFocusVisible = false; - -function trackGlobalFocusVisible() { - globalFocusVisibleEvents.forEach(type => { - window.addEventListener(type, handleGlobalFocusVisibleEvent, true); - }); -} - -function isValidKey(nativeEvent: KeyboardEvent): boolean { - const {metaKey, altKey, ctrlKey} = nativeEvent; - return !(metaKey || (!isMac && altKey) || ctrlKey); -} - -function isTextInput(nativeEvent: KeyboardEvent): boolean { - const {key, target} = nativeEvent; - if (key === 'Tab' || key === 'Escape') { - return false; - } - const {isContentEditable, tagName} = (target: any); - return tagName === 'INPUT' || tagName === 'TEXTAREA' || isContentEditable; -} - -function handleGlobalFocusVisibleEvent( - nativeEvent: MouseEvent | TouchEvent | KeyboardEvent, -): void { - if (nativeEvent.type === 'keydown') { - if (isValidKey(((nativeEvent: any): KeyboardEvent))) { - isGlobalFocusVisible = true; - } - } else { - const nodeName = (nativeEvent.target: any).nodeName; - // Safari calls mousemove/pointermove events when you tab out of the active - // Safari frame. - if (nodeName === 'HTML') { - return; - } - // Handle all the other mouse/touch/pointer events - isGlobalFocusVisible = false; - } -} - -function handleFocusVisibleTargetEvents( - event: SyntheticEvent, - callback: boolean => void, -): void { - if (event.type === 'keydown') { - const {nativeEvent} = (event: any); - if (isValidKey(nativeEvent) && !isTextInput(nativeEvent)) { - callback(true); - } - } else { - callback(false); - } -} - -function isRelatedTargetWithin( - focusWithinTarget: Object, - relatedTarget: null | EventTarget, -): boolean { - if (relatedTarget == null) { - return false; - } - // As the focusWithinTarget can be a Scope Instance (experimental API), - // we need to use the containsNode() method. Otherwise, focusWithinTarget - // must be a Node, which means we can use the contains() method. - return typeof focusWithinTarget.containsNode === 'function' - ? focusWithinTarget.containsNode(relatedTarget) - : focusWithinTarget.contains(relatedTarget); -} - -function setFocusVisibleListeners( - // $FlowFixMe[missing-local-annot] - focusVisibleHandles, - focusTarget: EventTarget, - callback: boolean => void, -) { - focusVisibleHandles.forEach(focusVisibleHandle => { - focusVisibleHandle.setListener(focusTarget, event => - handleFocusVisibleTargetEvents(event, callback), - ); - }); -} - -function useFocusVisibleInputHandles() { - return [ - useEvent('mousedown'), - useEvent(hasPointerEvents ? 'pointerdown' : 'touchstart'), - useEvent('keydown'), - ]; -} - -function useFocusLifecycles() { - useEffect(() => { - if (!hasTrackedGlobalFocusVisible) { - hasTrackedGlobalFocusVisible = true; - trackGlobalFocusVisible(); - } - }, []); -} - -export function useFocus( - focusTargetRef: {current: null | Node}, - { - disabled, - onBlur, - onFocus, - onFocusChange, - onFocusVisibleChange, - }: UseFocusOptions, -): void { - // Setup controlled state for this useFocus hook - const stateRef = useRef({isFocused: false, isFocusVisible: false}); - const focusHandle = useEvent('focusin'); - const blurHandle = useEvent('focusout'); - const focusVisibleHandles = useFocusVisibleInputHandles(); - - useLayoutEffect(() => { - const focusTarget = focusTargetRef.current; - const state = stateRef.current; - - if (focusTarget !== null && state !== null && focusTarget.nodeType === 1) { - // Handle focus visible - setFocusVisibleListeners( - focusVisibleHandles, - focusTarget, - isFocusVisible => { - if (state.isFocused && state.isFocusVisible !== isFocusVisible) { - state.isFocusVisible = isFocusVisible; - if (onFocusVisibleChange) { - onFocusVisibleChange(isFocusVisible); - } - } - }, - ); - - // Handle focus - focusHandle.setListener(focusTarget, (event: FocusEvent) => { - if (disabled === true) { - return; - } - if (!state.isFocused && focusTarget === event.target) { - state.isFocused = true; - state.isFocusVisible = isGlobalFocusVisible; - if (onFocus) { - onFocus(event); - } - if (onFocusChange) { - onFocusChange(true); - } - if (state.isFocusVisible && onFocusVisibleChange) { - onFocusVisibleChange(true); - } - } - }); - - // Handle blur - blurHandle.setListener(focusTarget, (event: FocusEvent) => { - if (disabled === true) { - return; - } - if (state.isFocused) { - state.isFocused = false; - state.isFocusVisible = isGlobalFocusVisible; - if (onBlur) { - onBlur(event); - } - if (onFocusChange) { - onFocusChange(false); - } - if (state.isFocusVisible && onFocusVisibleChange) { - onFocusVisibleChange(false); - } - } - }); - } - }, [ - blurHandle, - disabled, - focusHandle, - focusTargetRef, - focusVisibleHandles, - onBlur, - onFocus, - onFocusChange, - onFocusVisibleChange, - ]); - - // Mount/Unmount logic - useFocusLifecycles(); -} - -export function useFocusWithin( - focusWithinTargetRef: - | {current: null | T} - | ((focusWithinTarget: null | T) => void), - { - disabled, - onAfterBlurWithin, - onBeforeBlurWithin, - onBlurWithin, - onFocusWithin, - onFocusWithinChange, - onFocusWithinVisibleChange, - }: UseFocusWithinOptions, -): (focusWithinTarget: null | T) => void { - // Setup controlled state for this useFocus hook - const stateRef = useRef({isFocused: false, isFocusVisible: false}); - const focusHandle = useEvent('focusin'); - const blurHandle = useEvent('focusout'); - const afterBlurHandle = useEvent('afterblur'); - const beforeBlurHandle = useEvent('beforeblur'); - const focusVisibleHandles = useFocusVisibleInputHandles(); - - const useFocusWithinRef = useCallback( - (focusWithinTarget: null | T) => { - // Handle the incoming focusTargetRef. It can be either a function ref - // or an object ref. - if (typeof focusWithinTargetRef === 'function') { - focusWithinTargetRef(focusWithinTarget); - } else { - focusWithinTargetRef.current = focusWithinTarget; - } - const state = stateRef.current; - - if (focusWithinTarget !== null && state !== null) { - // Handle focus visible - setFocusVisibleListeners( - focusVisibleHandles, - // $FlowFixMe[incompatible-call] focusWithinTarget is not null here - focusWithinTarget, - isFocusVisible => { - if (state.isFocused && state.isFocusVisible !== isFocusVisible) { - state.isFocusVisible = isFocusVisible; - if (onFocusWithinVisibleChange) { - onFocusWithinVisibleChange(isFocusVisible); - } - } - }, - ); - - // Handle focus - // $FlowFixMe[incompatible-call] focusWithinTarget is not null here - focusHandle.setListener(focusWithinTarget, (event: FocusEvent) => { - if (disabled) { - return; - } - if (!state.isFocused) { - state.isFocused = true; - state.isFocusVisible = isGlobalFocusVisible; - if (onFocusWithinChange) { - onFocusWithinChange(true); - } - if (state.isFocusVisible && onFocusWithinVisibleChange) { - onFocusWithinVisibleChange(true); - } - } - if (!state.isFocusVisible && isGlobalFocusVisible) { - state.isFocusVisible = isGlobalFocusVisible; - if (onFocusWithinVisibleChange) { - onFocusWithinVisibleChange(true); - } - } - if (onFocusWithin) { - onFocusWithin(event); - } - }); - - // Handle blur - // $FlowFixMe[incompatible-call] focusWithinTarget is not null here - blurHandle.setListener(focusWithinTarget, (event: FocusEvent) => { - if (disabled) { - return; - } - const {relatedTarget} = (event.nativeEvent: any); - - if ( - state.isFocused && - !isRelatedTargetWithin(focusWithinTarget, relatedTarget) - ) { - state.isFocused = false; - if (onFocusWithinChange) { - onFocusWithinChange(false); - } - if (state.isFocusVisible && onFocusWithinVisibleChange) { - onFocusWithinVisibleChange(false); - } - if (onBlurWithin) { - onBlurWithin(event); - } - } - }); - - // Handle before blur. This is a special - // React provided event. - // $FlowFixMe[incompatible-call] focusWithinTarget is not null here - beforeBlurHandle.setListener(focusWithinTarget, (event: FocusEvent) => { - if (disabled) { - return; - } - if (onBeforeBlurWithin) { - onBeforeBlurWithin(event); - // Add an "afterblur" listener on document. This is a special - // React provided event. - afterBlurHandle.setListener( - document, - (afterBlurEvent: FocusEvent) => { - if (onAfterBlurWithin) { - onAfterBlurWithin(afterBlurEvent); - } - // Clear listener on document - afterBlurHandle.setListener(document, null); - }, - ); - } - }); - } - }, - [ - afterBlurHandle, - beforeBlurHandle, - blurHandle, - disabled, - focusHandle, - focusWithinTargetRef, - onAfterBlurWithin, - onBeforeBlurWithin, - onBlurWithin, - onFocusWithin, - onFocusWithinChange, - onFocusWithinVisibleChange, - ], - ); - - // Mount/Unmount logic - useFocusLifecycles(); - - return useFocusWithinRef; -} diff --git a/packages/react-interactions/events/src/dom/create-event-handle/__tests__/useFocus-test.internal.js b/packages/react-interactions/events/src/dom/create-event-handle/__tests__/useFocus-test.internal.js deleted file mode 100644 index cc456434f975c..0000000000000 --- a/packages/react-interactions/events/src/dom/create-event-handle/__tests__/useFocus-test.internal.js +++ /dev/null @@ -1,335 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @emails react-core - */ - -'use strict'; - -import {createEventTarget, setPointerEvent} from 'dom-event-testing-library'; - -let React; -let ReactFeatureFlags; -let ReactDOMClient; -let useFocus; -let act; - -function initializeModules(hasPointerEvents) { - setPointerEvent(hasPointerEvents); - jest.resetModules(); - ReactFeatureFlags = require('shared/ReactFeatureFlags'); - ReactFeatureFlags.enableCreateEventHandleAPI = true; - React = require('react'); - ReactDOMClient = require('react-dom/client'); - act = require('internal-test-utils').act; - // TODO: This import throws outside of experimental mode. Figure out better - // strategy for gated imports. - if (__EXPERIMENTAL__ || global.__WWW__) { - useFocus = require('react-interactions/events/focus').useFocus; - } -} - -const forcePointerEvents = true; -const table = [[forcePointerEvents], [!forcePointerEvents]]; - -describe.each(table)(`useFocus hasPointerEvents=%s`, hasPointerEvents => { - let container; - - beforeEach(() => { - initializeModules(hasPointerEvents); - container = document.createElement('div'); - document.body.appendChild(container); - }); - - afterEach(() => { - document.body.removeChild(container); - container = null; - }); - - describe('disabled', () => { - let onBlur, onFocus, ref; - - const componentInit = async () => { - onBlur = jest.fn(); - onFocus = jest.fn(); - ref = React.createRef(); - const Component = () => { - useFocus(ref, { - disabled: true, - onBlur, - onFocus, - }); - return
; - }; - const root = ReactDOMClient.createRoot(container); - await act(() => { - root.render(); - }); - }; - - // @gate www - it('does not call callbacks', async () => { - await componentInit(); - const target = createEventTarget(ref.current); - target.focus(); - target.blur(); - expect(onFocus).not.toBeCalled(); - expect(onBlur).not.toBeCalled(); - }); - }); - - describe('onBlur', () => { - let onBlur, ref; - - const componentInit = async () => { - onBlur = jest.fn(); - ref = React.createRef(); - const Component = () => { - useFocus(ref, { - onBlur, - }); - return
; - }; - const root = ReactDOMClient.createRoot(container); - await act(() => { - root.render(); - }); - }; - - // @gate www - it('is called after "blur" event', async () => { - await componentInit(); - const target = createEventTarget(ref.current); - target.focus(); - target.blur(); - expect(onBlur).toHaveBeenCalledTimes(1); - }); - }); - - describe('onFocus', () => { - let onFocus, ref, innerRef; - - const componentInit = async () => { - onFocus = jest.fn(); - ref = React.createRef(); - innerRef = React.createRef(); - const Component = () => { - useFocus(ref, { - onFocus, - }); - return ( -
- -
- ); - }; - const root = ReactDOMClient.createRoot(container); - await act(() => { - root.render(); - }); - }; - - // @gate www - it('is called after "focus" event', async () => { - await componentInit(); - const target = createEventTarget(ref.current); - target.focus(); - expect(onFocus).toHaveBeenCalledTimes(1); - }); - - // @gate www - it('is not called if descendants of target receive focus', async () => { - await componentInit(); - const target = createEventTarget(innerRef.current); - target.focus(); - expect(onFocus).not.toBeCalled(); - }); - }); - - describe('onFocusChange', () => { - let onFocusChange, ref, innerRef; - - const componentInit = async () => { - onFocusChange = jest.fn(); - ref = React.createRef(); - innerRef = React.createRef(); - const Component = () => { - useFocus(ref, { - onFocusChange, - }); - return ( -
-
-
- ); - }; - const root = ReactDOMClient.createRoot(container); - await act(() => { - root.render(); - }); - }; - - // @gate www - it('is called after "blur" and "focus" events', async () => { - await componentInit(); - const target = createEventTarget(ref.current); - target.focus(); - expect(onFocusChange).toHaveBeenCalledTimes(1); - expect(onFocusChange).toHaveBeenCalledWith(true); - target.blur(); - expect(onFocusChange).toHaveBeenCalledTimes(2); - expect(onFocusChange).toHaveBeenCalledWith(false); - }); - - // @gate www - it('is not called after "blur" and "focus" events on descendants', async () => { - await componentInit(); - const target = createEventTarget(innerRef.current); - target.focus(); - expect(onFocusChange).toHaveBeenCalledTimes(0); - target.blur(); - expect(onFocusChange).toHaveBeenCalledTimes(0); - }); - }); - - describe('onFocusVisibleChange', () => { - let onFocusVisibleChange, ref, innerRef; - - const componentInit = async () => { - onFocusVisibleChange = jest.fn(); - ref = React.createRef(); - innerRef = React.createRef(); - const Component = () => { - useFocus(ref, { - onFocusVisibleChange, - }); - return ( -
-
-
- ); - }; - const root = ReactDOMClient.createRoot(container); - await act(() => { - root.render(); - }); - }; - - // @gate www - it('is called after "focus" and "blur" if keyboard navigation is active', async () => { - await componentInit(); - const target = createEventTarget(ref.current); - const containerTarget = createEventTarget(container); - // use keyboard first - containerTarget.keydown({key: 'Tab'}); - target.focus(); - expect(onFocusVisibleChange).toHaveBeenCalledTimes(1); - expect(onFocusVisibleChange).toHaveBeenCalledWith(true); - target.blur({relatedTarget: container}); - expect(onFocusVisibleChange).toHaveBeenCalledTimes(2); - expect(onFocusVisibleChange).toHaveBeenCalledWith(false); - }); - - // @gate www - it('is called if non-keyboard event is dispatched on target previously focused with keyboard', async () => { - await componentInit(); - const target = createEventTarget(ref.current); - const containerTarget = createEventTarget(container); - // use keyboard first - containerTarget.keydown({key: 'Tab'}); - target.focus(); - expect(onFocusVisibleChange).toHaveBeenCalledTimes(1); - expect(onFocusVisibleChange).toHaveBeenCalledWith(true); - // then use pointer on the target, focus should no longer be visible - target.pointerdown(); - expect(onFocusVisibleChange).toHaveBeenCalledTimes(2); - expect(onFocusVisibleChange).toHaveBeenCalledWith(false); - // onFocusVisibleChange should not be called again - target.blur({relatedTarget: container}); - expect(onFocusVisibleChange).toHaveBeenCalledTimes(2); - }); - - // @gate www - it('is not called after "focus" and "blur" events without keyboard', async () => { - await componentInit(); - const target = createEventTarget(ref.current); - const containerTarget = createEventTarget(container); - target.pointerdown(); - target.pointerup(); - containerTarget.pointerdown(); - target.blur({relatedTarget: container}); - expect(onFocusVisibleChange).toHaveBeenCalledTimes(0); - }); - - // @gate www - it('is not called after "blur" and "focus" events on descendants', async () => { - await componentInit(); - const innerTarget = createEventTarget(innerRef.current); - const containerTarget = createEventTarget(container); - containerTarget.keydown({key: 'Tab'}); - innerTarget.focus(); - expect(onFocusVisibleChange).toHaveBeenCalledTimes(0); - innerTarget.blur({relatedTarget: container}); - expect(onFocusVisibleChange).toHaveBeenCalledTimes(0); - }); - }); - - describe('nested Focus components', () => { - // @gate www - it('propagates events in the correct order', async () => { - const events = []; - const innerRef = React.createRef(); - const outerRef = React.createRef(); - const createEventHandler = msg => () => { - events.push(msg); - }; - - const Inner = () => { - useFocus(innerRef, { - onBlur: createEventHandler('inner: onBlur'), - onFocus: createEventHandler('inner: onFocus'), - onFocusChange: createEventHandler('inner: onFocusChange'), - }); - return
; - }; - - const Outer = () => { - useFocus(outerRef, { - onBlur: createEventHandler('outer: onBlur'), - onFocus: createEventHandler('outer: onFocus'), - onFocusChange: createEventHandler('outer: onFocusChange'), - }); - return ( -
- -
- ); - }; - - const root = ReactDOMClient.createRoot(container); - await act(() => { - root.render(); - }); - const innerTarget = createEventTarget(innerRef.current); - const outerTarget = createEventTarget(outerRef.current); - - outerTarget.focus(); - outerTarget.blur(); - innerTarget.focus(); - innerTarget.blur(); - expect(events).toEqual([ - 'outer: onFocus', - 'outer: onFocusChange', - 'outer: onBlur', - 'outer: onFocusChange', - 'inner: onFocus', - 'inner: onFocusChange', - 'inner: onBlur', - 'inner: onFocusChange', - ]); - }); - }); -}); diff --git a/packages/react-interactions/events/src/dom/create-event-handle/__tests__/useFocusWithin-test.internal.js b/packages/react-interactions/events/src/dom/create-event-handle/__tests__/useFocusWithin-test.internal.js deleted file mode 100644 index f77dd5b779355..0000000000000 --- a/packages/react-interactions/events/src/dom/create-event-handle/__tests__/useFocusWithin-test.internal.js +++ /dev/null @@ -1,626 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @emails react-core - */ - -'use strict'; - -import {createEventTarget, setPointerEvent} from 'dom-event-testing-library'; - -let React; -let ReactFeatureFlags; -let ReactDOMClient; -let useFocusWithin; -let act; - -function initializeModules(hasPointerEvents) { - setPointerEvent(hasPointerEvents); - jest.resetModules(); - ReactFeatureFlags = require('shared/ReactFeatureFlags'); - ReactFeatureFlags.enableScopeAPI = true; - ReactFeatureFlags.enableCreateEventHandleAPI = true; - React = require('react'); - ReactDOMClient = require('react-dom/client'); - act = require('internal-test-utils').act; - - // TODO: This import throws outside of experimental mode. Figure out better - // strategy for gated imports. - if (__EXPERIMENTAL__ || global.__WWW__) { - useFocusWithin = require('react-interactions/events/focus').useFocusWithin; - } -} - -const forcePointerEvents = true; -const table = [[forcePointerEvents], [!forcePointerEvents]]; - -describe.each(table)(`useFocus`, hasPointerEvents => { - let container; - let container2; - let root; - - beforeEach(() => { - initializeModules(hasPointerEvents); - container = document.createElement('div'); - document.body.appendChild(container); - container2 = document.createElement('div'); - document.body.appendChild(container2); - root = ReactDOMClient.createRoot(container); - }); - - afterEach(async () => { - await act(() => { - root.render(null); - }); - - document.body.removeChild(container); - document.body.removeChild(container2); - container = null; - container2 = null; - }); - - describe('disabled', () => { - let onFocusWithinChange, onFocusWithinVisibleChange, ref; - - const componentInit = async () => { - onFocusWithinChange = jest.fn(); - onFocusWithinVisibleChange = jest.fn(); - ref = React.createRef(); - const Component = () => { - const focusWithinRef = useFocusWithin(ref, { - disabled: true, - onFocusWithinChange, - onFocusWithinVisibleChange, - }); - return
; - }; - await act(() => { - root.render(); - }); - }; - - // @gate www - it('prevents custom events being dispatched', async () => { - await componentInit(); - const target = createEventTarget(ref.current); - target.focus(); - target.blur(); - expect(onFocusWithinChange).not.toBeCalled(); - expect(onFocusWithinVisibleChange).not.toBeCalled(); - }); - }); - - describe('onFocusWithinChange', () => { - let onFocusWithinChange, ref, innerRef, innerRef2; - - const Component = ({show}) => { - const focusWithinRef = useFocusWithin(ref, { - onFocusWithinChange, - }); - return ( -
- {show && } -
-
- ); - }; - - const componentInit = async () => { - onFocusWithinChange = jest.fn(); - ref = React.createRef(); - innerRef = React.createRef(); - innerRef2 = React.createRef(); - await act(() => { - root.render(); - }); - }; - - // @gate www - it('is called after "blur" and "focus" events on focus target', async () => { - await componentInit(); - const target = createEventTarget(ref.current); - target.focus(); - expect(onFocusWithinChange).toHaveBeenCalledTimes(1); - expect(onFocusWithinChange).toHaveBeenCalledWith(true); - target.blur({relatedTarget: container}); - expect(onFocusWithinChange).toHaveBeenCalledTimes(2); - expect(onFocusWithinChange).toHaveBeenCalledWith(false); - }); - - // @gate www - it('is called after "blur" and "focus" events on descendants', async () => { - await componentInit(); - const target = createEventTarget(innerRef.current); - target.focus(); - expect(onFocusWithinChange).toHaveBeenCalledTimes(1); - expect(onFocusWithinChange).toHaveBeenCalledWith(true); - target.blur({relatedTarget: container}); - expect(onFocusWithinChange).toHaveBeenCalledTimes(2); - expect(onFocusWithinChange).toHaveBeenCalledWith(false); - }); - - // @gate www - it('is only called once when focus moves within and outside the subtree', async () => { - await componentInit(); - const node = ref.current; - const innerNode1 = innerRef.current; - const innerNode2 = innerRef.current; - const target = createEventTarget(node); - const innerTarget1 = createEventTarget(innerNode1); - const innerTarget2 = createEventTarget(innerNode2); - - // focus shifts into subtree - innerTarget1.focus(); - expect(onFocusWithinChange).toHaveBeenCalledTimes(1); - expect(onFocusWithinChange).toHaveBeenCalledWith(true); - // focus moves around subtree - innerTarget1.blur({relatedTarget: innerNode2}); - innerTarget2.focus(); - innerTarget2.blur({relatedTarget: node}); - target.focus(); - target.blur({relatedTarget: innerNode1}); - expect(onFocusWithinChange).toHaveBeenCalledTimes(1); - // focus shifts outside subtree - innerTarget1.blur({relatedTarget: container}); - expect(onFocusWithinChange).toHaveBeenCalledTimes(2); - expect(onFocusWithinChange).toHaveBeenCalledWith(false); - }); - }); - - describe('onFocusWithinVisibleChange', () => { - let onFocusWithinVisibleChange, ref, innerRef, innerRef2; - - const Component = ({show}) => { - const focusWithinRef = useFocusWithin(ref, { - onFocusWithinVisibleChange, - }); - return ( -
- {show && } -
-
- ); - }; - - const componentInit = async () => { - onFocusWithinVisibleChange = jest.fn(); - ref = React.createRef(); - innerRef = React.createRef(); - innerRef2 = React.createRef(); - await act(() => { - root.render(); - }); - }; - - // @gate www - it('is called after "focus" and "blur" on focus target if keyboard was used', async () => { - await componentInit(); - const target = createEventTarget(ref.current); - const containerTarget = createEventTarget(container); - // use keyboard first - containerTarget.keydown({key: 'Tab'}); - target.focus(); - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1); - expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true); - target.blur({relatedTarget: container}); - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2); - expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false); - }); - - // @gate www - it('is called after "focus" and "blur" on descendants if keyboard was used', async () => { - await componentInit(); - const innerTarget = createEventTarget(innerRef.current); - const containerTarget = createEventTarget(container); - // use keyboard first - containerTarget.keydown({key: 'Tab'}); - innerTarget.focus(); - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1); - expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true); - innerTarget.blur({relatedTarget: container}); - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2); - expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false); - }); - - // @gate www - it('is called if non-keyboard event is dispatched on target previously focused with keyboard', async () => { - await componentInit(); - const node = ref.current; - const innerNode1 = innerRef.current; - const innerNode2 = innerRef2.current; - - const target = createEventTarget(node); - const innerTarget1 = createEventTarget(innerNode1); - const innerTarget2 = createEventTarget(innerNode2); - // use keyboard first - target.focus(); - target.keydown({key: 'Tab'}); - target.blur({relatedTarget: innerNode1}); - innerTarget1.focus(); - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1); - expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true); - // then use pointer on the next target, focus should no longer be visible - innerTarget2.pointerdown(); - innerTarget1.blur({relatedTarget: innerNode2}); - innerTarget2.focus(); - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2); - expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false); - // then use keyboard again - innerTarget2.keydown({key: 'Tab', shiftKey: true}); - innerTarget2.blur({relatedTarget: innerNode1}); - innerTarget1.focus(); - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(3); - expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true); - // then use pointer on the target, focus should no longer be visible - innerTarget1.pointerdown(); - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(4); - expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false); - // onFocusVisibleChange should not be called again - innerTarget1.blur({relatedTarget: container}); - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(4); - }); - - // @gate www - it('is not called after "focus" and "blur" events without keyboard', async () => { - await componentInit(); - const innerTarget = createEventTarget(innerRef.current); - innerTarget.pointerdown(); - innerTarget.pointerup(); - innerTarget.blur({relatedTarget: container}); - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(0); - }); - - // @gate www - it('is only called once when focus moves within and outside the subtree', async () => { - await componentInit(); - const node = ref.current; - const innerNode1 = innerRef.current; - const innerNode2 = innerRef2.current; - const target = createEventTarget(node); - const innerTarget1 = createEventTarget(innerNode1); - const innerTarget2 = createEventTarget(innerNode2); - - // focus shifts into subtree - innerTarget1.focus(); - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1); - expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true); - // focus moves around subtree - innerTarget1.blur({relatedTarget: innerNode2}); - innerTarget2.focus(); - innerTarget2.blur({relatedTarget: node}); - target.focus(); - target.blur({relatedTarget: innerNode1}); - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1); - // focus shifts outside subtree - innerTarget1.blur({relatedTarget: container}); - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2); - expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false); - }); - }); - - // @gate www - it('should correctly handle focus visibility when typing into an input', async () => { - const onFocusWithinVisibleChange = jest.fn(); - const ref = React.createRef(); - const inputRef = React.createRef(); - const Component = () => { - const focusWithinRef = useFocusWithin(ref, { - onFocusWithinVisibleChange, - }); - return ( -
- -
- ); - }; - await act(() => { - root.render(); - }); - - const target = createEventTarget(inputRef.current); - // focus the target - target.pointerdown(); - target.focus(); - target.keydown({key: 'a'}); - expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(0); - }); - - describe('onBeforeBlurWithin', () => { - let onBeforeBlurWithin, onAfterBlurWithin, ref, innerRef, innerRef2; - - beforeEach(() => { - onBeforeBlurWithin = jest.fn(); - onAfterBlurWithin = jest.fn(e => { - e.persist(); - }); - ref = React.createRef(); - innerRef = React.createRef(); - innerRef2 = React.createRef(); - }); - - // @gate www - it('is called after a focused element is unmounted', async () => { - const Component = ({show}) => { - const focusWithinRef = useFocusWithin(ref, { - onBeforeBlurWithin, - onAfterBlurWithin, - }); - return ( -
- {show && } -
-
- ); - }; - - await act(() => { - root.render(); - }); - - const inner = innerRef.current; - const target = createEventTarget(inner); - target.keydown({key: 'Tab'}); - target.focus(); - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0); - expect(onAfterBlurWithin).toHaveBeenCalledTimes(0); - await act(() => { - root.render(); - }); - - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1); - expect(onAfterBlurWithin).toHaveBeenCalledTimes(1); - expect(onAfterBlurWithin).toHaveBeenCalledWith( - expect.objectContaining({relatedTarget: inner}), - ); - }); - - // @gate www - it('is called after a nested focused element is unmounted', async () => { - const Component = ({show}) => { - const focusWithinRef = useFocusWithin(ref, { - onBeforeBlurWithin, - onAfterBlurWithin, - }); - return ( -
- {show && ( -
- -
- )} -
-
- ); - }; - - await act(() => { - root.render(); - }); - - const inner = innerRef.current; - const target = createEventTarget(inner); - target.keydown({key: 'Tab'}); - target.focus(); - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0); - expect(onAfterBlurWithin).toHaveBeenCalledTimes(0); - - await act(() => { - root.render(); - }); - - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1); - expect(onAfterBlurWithin).toHaveBeenCalledTimes(1); - expect(onAfterBlurWithin).toHaveBeenCalledWith( - expect.objectContaining({relatedTarget: inner}), - ); - }); - - // @gate www - it('is called after many elements are unmounted', async () => { - const buttonRef = React.createRef(); - const inputRef = React.createRef(); - - const Component = ({show}) => { - const focusWithinRef = useFocusWithin(ref, { - onBeforeBlurWithin, - onAfterBlurWithin, - }); - return ( -
- {show && } - {show && } - {show && } - {show && } - {!show && } - {show && } - - -
- ); - }; - - await act(() => { - root.render(); - }); - - inputRef.current.focus(); - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0); - expect(onAfterBlurWithin).toHaveBeenCalledTimes(0); - await act(() => { - root.render(); - }); - - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1); - expect(onAfterBlurWithin).toHaveBeenCalledTimes(1); - }); - - // @gate www - it('is called after a nested focused element is unmounted (with scope query)', async () => { - const TestScope = React.unstable_Scope; - const testScopeQuery = (type, props) => true; - let targetNodes; - let targetNode; - - const Component = ({show}) => { - const scopeRef = React.useRef(null); - const focusWithinRef = useFocusWithin(scopeRef, { - onBeforeBlurWithin(event) { - const scope = scopeRef.current; - targetNode = innerRef.current; - targetNodes = scope.DO_NOT_USE_queryAllNodes(testScopeQuery); - }, - }); - - return ( - - {show && } - - ); - }; - - await act(() => { - root.render(); - }); - - const inner = innerRef.current; - const target = createEventTarget(inner); - target.keydown({key: 'Tab'}); - target.focus(); - await act(() => { - root.render(); - }); - expect(targetNodes).toEqual([targetNode]); - }); - - // @gate www - it('is called after a focused suspended element is hidden', async () => { - const Suspense = React.Suspense; - let suspend = false; - let resolve; - const promise = new Promise(resolvePromise => (resolve = resolvePromise)); - - function Child() { - if (suspend) { - throw promise; - } else { - return ; - } - } - - const Component = ({show}) => { - const focusWithinRef = useFocusWithin(ref, { - onBeforeBlurWithin, - onAfterBlurWithin, - }); - - return ( -
- - - -
- ); - }; - - const root2 = ReactDOMClient.createRoot(container2); - - await act(() => { - root2.render(); - }); - expect(container2.innerHTML).toBe('
'); - - const inner = innerRef.current; - const target = createEventTarget(inner); - target.keydown({key: 'Tab'}); - target.focus(); - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0); - expect(onAfterBlurWithin).toHaveBeenCalledTimes(0); - - suspend = true; - await act(() => { - root2.render(); - }); - expect(container2.innerHTML).toBe( - '
Loading...
', - ); - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1); - expect(onAfterBlurWithin).toHaveBeenCalledTimes(1); - await act(() => { - suspend = false; - resolve(); - }); - expect(container2.innerHTML).toBe('
'); - }); - - // @gate www - it('is called after a focused suspended element is hidden then shown', async () => { - const Suspense = React.Suspense; - let suspend = false; - let resolve; - const promise = new Promise(resolvePromise => (resolve = resolvePromise)); - const buttonRef = React.createRef(); - - function Child() { - if (suspend) { - throw promise; - } else { - return ; - } - } - - const Component = ({show}) => { - const focusWithinRef = useFocusWithin(ref, { - onBeforeBlurWithin, - onAfterBlurWithin, - }); - - return ( -
- Loading...}> - - -
- ); - }; - - const root2 = ReactDOMClient.createRoot(container2); - - await act(() => { - root2.render(); - }); - - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0); - expect(onAfterBlurWithin).toHaveBeenCalledTimes(0); - - suspend = true; - await act(() => { - root2.render(); - }); - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0); - expect(onAfterBlurWithin).toHaveBeenCalledTimes(0); - - await act(() => { - root2.render(); - }); - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0); - expect(onAfterBlurWithin).toHaveBeenCalledTimes(0); - - buttonRef.current.focus(); - suspend = false; - await act(() => { - root2.render(); - }); - expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1); - expect(onAfterBlurWithin).toHaveBeenCalledTimes(1); - - await act(() => { - suspend = false; - resolve(); - }); - expect(container2.innerHTML).toBe('
'); - }); - }); -}); diff --git a/packages/react-interactions/events/src/dom/create-event-handle/useEvent.js b/packages/react-interactions/events/src/dom/create-event-handle/useEvent.js deleted file mode 100644 index 6092e858ff7ad..0000000000000 --- a/packages/react-interactions/events/src/dom/create-event-handle/useEvent.js +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -import * as React from 'react'; -import * as ReactDOM from 'react-dom'; - -const {useLayoutEffect, useRef} = React; -const {unstable_createEventHandle} = ReactDOM; - -type UseEventHandle = { - setListener: ( - target: EventTarget, - null | ((SyntheticEvent) => void), - ) => void, - clear: () => void, -}; - -export default function useEvent( - event: string, - options?: { - capture?: boolean, - }, -): UseEventHandle { - const handleRef = useRef(null); - let useEventHandle = handleRef.current; - - if (useEventHandle === null) { - const setEventHandle = unstable_createEventHandle(event, options); - const clears = new Map void>(); - useEventHandle = { - setListener( - target: EventTarget, - callback: null | ((SyntheticEvent) => void), - ): void { - let clear = clears.get(target); - if (clear !== undefined) { - clear(); - } - if (callback === null) { - clears.delete(target); - return; - } - clear = setEventHandle(target, callback); - clears.set(target, clear); - }, - clear(): void { - clears.forEach(c => { - c(); - }); - clears.clear(); - }, - }; - handleRef.current = useEventHandle; - } - - useLayoutEffect(() => { - return () => { - if (useEventHandle !== null) { - useEventHandle.clear(); - } - handleRef.current = null; - }; - }, [useEventHandle]); - - return useEventHandle; -} diff --git a/packages/react-interactions/npm/drag.js b/packages/react-interactions/npm/drag.js deleted file mode 100644 index fa844823e8ad7..0000000000000 --- a/packages/react-interactions/npm/drag.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -if (process.env.NODE_ENV === 'production') { - module.exports = require('./cjs/react-interactions-events/drag.production.min.js'); -} else { - module.exports = require('./cjs/react-interactions-events/drag.development.js'); -} diff --git a/packages/react-interactions/npm/focus.js b/packages/react-interactions/npm/focus.js deleted file mode 100644 index 9a3a56e9a26d1..0000000000000 --- a/packages/react-interactions/npm/focus.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -if (process.env.NODE_ENV === 'production') { - module.exports = require('./cjs/react-interactions-events/focus.production.min.js'); -} else { - module.exports = require('./cjs/react-interactions-events/focus.development.js'); -} diff --git a/packages/react-interactions/npm/hover.js b/packages/react-interactions/npm/hover.js deleted file mode 100644 index 6ac666f7e0c6e..0000000000000 --- a/packages/react-interactions/npm/hover.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -if (process.env.NODE_ENV === 'production') { - module.exports = require('./cjs/react-interactions-events/hover.production.min.js'); -} else { - module.exports = require('./cjs/react-interactions-events/hover.development.js'); -} diff --git a/packages/react-interactions/npm/input.js b/packages/react-interactions/npm/input.js deleted file mode 100644 index 82c5aadf6e29b..0000000000000 --- a/packages/react-interactions/npm/input.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -if (process.env.NODE_ENV === 'production') { - module.exports = require('./cjs/react-interactions-events/input.production.min.js'); -} else { - module.exports = require('./cjs/react-interactions-events/input.development.js'); -} diff --git a/packages/react-interactions/npm/press-legacy.js b/packages/react-interactions/npm/press-legacy.js deleted file mode 100644 index f98feaa1f1f31..0000000000000 --- a/packages/react-interactions/npm/press-legacy.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -if (process.env.NODE_ENV === 'production') { - module.exports = require('./cjs/react-interactions-events/press-legacy.production.min.js'); -} else { - module.exports = require('./cjs/react-interactions-events/press-legacy.development.js'); -} diff --git a/packages/react-interactions/npm/press.js b/packages/react-interactions/npm/press.js deleted file mode 100644 index 9476dfceac6e9..0000000000000 --- a/packages/react-interactions/npm/press.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -if (process.env.NODE_ENV === 'production') { - module.exports = require('./cjs/react-interactions-events/press.production.min.js'); -} else { - module.exports = require('./cjs/react-interactions-events/press.development.js'); -} diff --git a/packages/react-interactions/npm/scroll.js b/packages/react-interactions/npm/scroll.js deleted file mode 100644 index 0cb8db03587ae..0000000000000 --- a/packages/react-interactions/npm/scroll.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -if (process.env.NODE_ENV === 'production') { - module.exports = require('./cjs/react-interactions-events/scroll.production.min.js'); -} else { - module.exports = require('./cjs/react-interactions-events/scroll.development.js'); -} diff --git a/packages/react-interactions/npm/swipe.js b/packages/react-interactions/npm/swipe.js deleted file mode 100644 index acd514d10c319..0000000000000 --- a/packages/react-interactions/npm/swipe.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -if (process.env.NODE_ENV === 'production') { - module.exports = require('./cjs/react-interactions-events/swipe.production.min.js'); -} else { - module.exports = require('./cjs/react-interactions-events/swipe.development.js'); -} diff --git a/packages/react-interactions/npm/tap.js b/packages/react-interactions/npm/tap.js deleted file mode 100644 index 0a096a2fa6057..0000000000000 --- a/packages/react-interactions/npm/tap.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -if (process.env.NODE_ENV === 'production') { - module.exports = require('./cjs/react-interactions-events/tap.production.min.js'); -} else { - module.exports = require('./cjs/react-interactions-events/tap.development.js'); -} diff --git a/packages/react-interactions/package.json b/packages/react-interactions/package.json deleted file mode 100644 index effcf05151834..0000000000000 --- a/packages/react-interactions/package.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "react-interactions", - "private": true, - "description": "React is a JavaScript library for building user interfaces.", - "keywords": [ - "react" - ], - "version": "0.1.0", - "homepage": "https://react.dev/", - "bugs": "https://github.com/facebook/react/issues", - "license": "MIT", - "files": [ - "LICENSE", - "README.md", - "events/README.md", - "events/context-menu.js", - "events/focus.js", - "events/hover.js", - "events/input.js", - "events/keyboard.js", - "events/press.js", - "events/press-legacy.js", - "events/tap.js", - "cjs/", - "umd/" - ], - "main": "index.js", - "repository": { - "type": "git", - "url": "https://github.com/facebook/react.git", - "directory": "packages/react" - }, - "engines": { - "node": ">=0.10.0" - }, - "peerDependencies": { - "react": "^17.0.0" - } -}