diff --git a/packages/react-events/docs/FocusScope.md b/packages/react-events/docs/FocusScope.md deleted file mode 100644 index 996a4e3de5e47..0000000000000 --- a/packages/react-events/docs/FocusScope.md +++ /dev/null @@ -1,37 +0,0 @@ -# FocusScope - -The `FocusScope` module can be used to manage focus within its subtree. - -```js -// Example -const Modal = () => ( - -

Focus contained within modal

- -
Focusable element
- - -
Close
-
-
-); -``` - -## Props - -### autoFocus: boolean = false - -Automatically moves focus to the first focusable element within scope. - -### contain: boolean = false - -Contain focus within the subtree of the `FocusScope` instance. - -### restoreFocus: boolean = false - -Automatically restores focus to element that was last focused before focus moved -within the scope. diff --git a/packages/react-events/focus-scope.js b/packages/react-events/focus-scope.js deleted file mode 100644 index 5748f99e7748d..0000000000000 --- a/packages/react-events/focus-scope.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -'use strict'; - -module.exports = require('./src/dom/FocusScope'); diff --git a/packages/react-events/npm/focus-scope.js b/packages/react-events/npm/focus-scope.js deleted file mode 100644 index 0608f1826aa62..0000000000000 --- a/packages/react-events/npm/focus-scope.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -if (process.env.NODE_ENV === 'production') { - module.exports = require('./cjs/react-events-focus-scope.production.min.js'); -} else { - module.exports = require('./cjs/react-events-focus-scope.development.js'); -} diff --git a/packages/react-events/package.json b/packages/react-events/package.json index fa12895c074f0..7135dc53b4ce6 100644 --- a/packages/react-events/package.json +++ b/packages/react-events/package.json @@ -18,7 +18,6 @@ "swipe.js", "drag.js", "scroll.js", - "focus-scope.js", "input.js", "keyboard.js", "build-info.json", diff --git a/packages/react-events/src/dom/FocusScope.js b/packages/react-events/src/dom/FocusScope.js deleted file mode 100644 index f19bdb5a0eb5e..0000000000000 --- a/packages/react-events/src/dom/FocusScope.js +++ /dev/null @@ -1,167 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its 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 type { - ReactDOMResponderEvent, - ReactDOMResponderContext, -} from 'shared/ReactDOMTypes'; - -import React from 'react'; - -type FocusScopeProps = { - autoFocus: Boolean, - contain: Boolean, - restoreFocus: Boolean, -}; - -type FocusScopeState = { - nodeToRestore: null | HTMLElement, - currentFocusedNode: null | HTMLElement, -}; - -const targetEventTypes = ['keydown_active']; -const rootEventTypes = ['focus']; - -function focusElement(element: ?HTMLElement) { - if (element != null) { - try { - element.focus(); - } catch (err) {} - } -} - -function getFirstFocusableElement( - context: ReactDOMResponderContext, - state: FocusScopeState, -): ?HTMLElement { - const elements = context.getFocusableElementsInScope(false); - if (elements.length > 0) { - return elements[0]; - } -} - -const focusScopeResponderImpl = { - targetEventTypes, - rootEventTypes, - getInitialState(): FocusScopeState { - return { - nodeToRestore: null, - currentFocusedNode: null, - }; - }, - onEvent( - event: ReactDOMResponderEvent, - context: ReactDOMResponderContext, - props: FocusScopeProps, - state: FocusScopeState, - ) { - const {type, nativeEvent} = event; - - if (type === 'keydown' && nativeEvent.key === 'Tab') { - const focusedElement = context.getActiveDocument().activeElement; - if ( - focusedElement !== null && - context.isTargetWithinResponder(focusedElement) - ) { - const {altkey, ctrlKey, metaKey, shiftKey} = (nativeEvent: any); - // Skip if any of these keys are being pressed - if (altkey || ctrlKey || metaKey) { - return; - } - const elements = context.getFocusableElementsInScope(false); - const position = elements.indexOf(focusedElement); - const lastPosition = elements.length - 1; - let nextElement = null; - - if (shiftKey) { - if (position === 0) { - if (props.contain) { - nextElement = elements[lastPosition]; - } else { - // Out of bounds - // TODO remove this, this is hacky - const allElements = context.getFocusableElementsInScope(true); - const prevPosition = allElements.indexOf(focusedElement) - 1; - nextElement = allElements[prevPosition] || null; - return; - } - } else { - nextElement = elements[position - 1]; - } - } else { - if (position === lastPosition) { - if (props.contain) { - nextElement = elements[0]; - } else { - // Out of bounds - // TODO remove this, this is hacky - const allElements = context.getFocusableElementsInScope(true); - const nextPosition = allElements.indexOf(focusedElement) + 1; - nextElement = allElements[nextPosition] || null; - } - } else { - nextElement = elements[position + 1]; - } - } - if (nextElement !== null) { - focusElement(nextElement); - state.currentFocusedNode = nextElement; - ((nativeEvent: any): KeyboardEvent).preventDefault(); - } - } - } - }, - onRootEvent( - event: ReactDOMResponderEvent, - context: ReactDOMResponderContext, - props: FocusScopeProps, - state: FocusScopeState, - ) { - const {target} = event; - - // Handle global focus containment - if (props.contain) { - if (!context.isTargetWithinResponder(target)) { - const currentFocusedNode = state.currentFocusedNode; - if (currentFocusedNode !== null) { - focusElement(currentFocusedNode); - } else if (props.autoFocus) { - const firstElement = getFirstFocusableElement(context, state); - focusElement(firstElement); - } - } - } - }, - onMount( - context: ReactDOMResponderContext, - props: FocusScopeProps, - state: FocusScopeState, - ): void { - if (props.restoreFocus) { - state.nodeToRestore = context.getActiveDocument().activeElement; - } - if (props.autoFocus) { - const firstElement = getFirstFocusableElement(context, state); - focusElement(firstElement); - } - }, - onUnmount( - context: ReactDOMResponderContext, - props: FocusScopeProps, - state: FocusScopeState, - ): void { - if (props.restoreFocus && state.nodeToRestore !== null) { - focusElement(state.nodeToRestore); - } - }, -}; - -export const FocusScopeResponder = React.unstable_createResponder( - 'FocusSocpe', - focusScopeResponderImpl, -); diff --git a/packages/react-events/src/dom/__tests__/FocusScope-test.internal.js b/packages/react-events/src/dom/__tests__/FocusScope-test.internal.js deleted file mode 100644 index 1261817e65b52..0000000000000 --- a/packages/react-events/src/dom/__tests__/FocusScope-test.internal.js +++ /dev/null @@ -1,234 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its 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'; - -let React; -let ReactFeatureFlags; -let ReactDOM; -let FocusScopeResponder; - -const createTabForward = type => { - const event = new KeyboardEvent('keydown', { - key: 'Tab', - bubbles: true, - cancelable: true, - }); - return event; -}; - -const createTabBackward = type => { - const event = new KeyboardEvent('keydown', { - key: 'Tab', - shiftKey: true, - bubbles: true, - cancelable: true, - }); - return event; -}; - -describe('FocusScope event responder', () => { - let container; - - beforeEach(() => { - jest.resetModules(); - ReactFeatureFlags = require('shared/ReactFeatureFlags'); - ReactFeatureFlags.enableFlareAPI = true; - React = require('react'); - ReactDOM = require('react-dom'); - FocusScopeResponder = require('react-events/focus-scope') - .FocusScopeResponder; - container = document.createElement('div'); - document.body.appendChild(container); - }); - - afterEach(() => { - ReactDOM.render(null, container); - document.body.removeChild(container); - container = null; - }); - - it('should work as expected with autofocus', () => { - const inputRef = React.createRef(); - const input2Ref = React.createRef(); - const buttonRef = React.createRef(); - const butto2nRef = React.createRef(); - const divRef = React.createRef(); - - const SimpleFocusScope = () => ( -
}> - -
- ); - - ReactDOM.render(, container); - expect(document.activeElement).toBe(inputRef.current); - document.activeElement.dispatchEvent(createTabForward()); - expect(document.activeElement).toBe(buttonRef.current); - document.activeElement.dispatchEvent(createTabForward()); - expect(document.activeElement).toBe(divRef.current); - document.activeElement.dispatchEvent(createTabForward()); - expect(document.activeElement).toBe(butto2nRef.current); - document.activeElement.dispatchEvent(createTabBackward()); - expect(document.activeElement).toBe(divRef.current); - }); - - it('should work as expected with autoFocus and contain', () => { - const inputRef = React.createRef(); - const input2Ref = React.createRef(); - const buttonRef = React.createRef(); - const button2Ref = React.createRef(); - - const SimpleFocusScope = () => ( -
}> - -
- ); - - ReactDOM.render(, container); - expect(document.activeElement).toBe(buttonRef.current); - document.activeElement.dispatchEvent(createTabForward()); - expect(document.activeElement).toBe(button2Ref.current); - document.activeElement.dispatchEvent(createTabForward()); - expect(document.activeElement).toBe(buttonRef.current); - document.activeElement.dispatchEvent(createTabForward()); - expect(document.activeElement).toBe(button2Ref.current); - document.activeElement.dispatchEvent(createTabBackward()); - expect(document.activeElement).toBe(buttonRef.current); - document.activeElement.dispatchEvent(createTabBackward()); - expect(document.activeElement).toBe(button2Ref.current); - }); - - it('should work as expected when nested', () => { - const inputRef = React.createRef(); - const input2Ref = React.createRef(); - const buttonRef = React.createRef(); - const button2Ref = React.createRef(); - const button3Ref = React.createRef(); - const button4Ref = React.createRef(); - - const SimpleFocusScope = () => ( -
}> - -
- -