Remove FocusScope (#16267)
Dominic Gannaway committed
Jul 31, 2019 at 17:40 UTC
b5af4fe3c64e1ac254e8fd101fec2f8e414aabf6
7 files changed
-473
packages/react-events/docs/FocusScope.md
deleted
-37
@@ -1,37 +0,0 @@
1
-# FocusScope
2
-
3
-The `FocusScope` module can be used to manage focus within its subtree.
4
-
5
-```js
6
-// Example
7
-const Modal = () => (
8
- <FocusScope
9
- autoFocus={true}
10
- contain={true}
11
- restoreFocus={true}
12
- >
13
- <h1>Focus contained within modal</h1>
14
- <input placeholder="Focusable input" />
15
- <div role="button" tabIndex={0}>Focusable element</div>
16
- <input placeholder="Non-focusable input" tabIndex={-1} />
17
- <Press onPress={onPressClose}>
18
- <div role="button" tabIndex={0}>Close</div>
19
- </Press>
20
- </FocusScope>
21
-);
22
-```
23
-
24
-## Props
25
-
26
-### autoFocus: boolean = false
27
-
28
-Automatically moves focus to the first focusable element within scope.
29
-
30
-### contain: boolean = false
31
-
32
-Contain focus within the subtree of the `FocusScope` instance.
33
-
34
-### restoreFocus: boolean = false
35
-
36
-Automatically restores focus to element that was last focused before focus moved
37
-within the scope.
packages/react-events/focus-scope.js
deleted
-12
@@ -1,12 +0,0 @@
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
-'use strict';
11
-
12
-module.exports = require('./src/dom/FocusScope');
packages/react-events/npm/focus-scope.js
deleted
-7
@@ -1,7 +0,0 @@
1
-'use strict';
2
-
3
-if (process.env.NODE_ENV === 'production') {
4
- module.exports = require('./cjs/react-events-focus-scope.production.min.js');
5
-} else {
6
- module.exports = require('./cjs/react-events-focus-scope.development.js');
7
-}
packages/react-events/package.json
-1
@@ -18,7 +18,6 @@
18
"swipe.js",
19
"drag.js",
20
"scroll.js",
21
- "focus-scope.js",
21
"input.js",
22
"keyboard.js",
23
"build-info.json",
packages/react-events/src/dom/FocusScope.js
deleted
-167
@@ -1,167 +0,0 @@
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
-import type {
10
- ReactDOMResponderEvent,
11
- ReactDOMResponderContext,
12
-} from 'shared/ReactDOMTypes';
13
-
14
-import React from 'react';
15
-
16
-type FocusScopeProps = {
17
- autoFocus: Boolean,
18
- contain: Boolean,
19
- restoreFocus: Boolean,
20
-};
21
-
22
-type FocusScopeState = {
23
- nodeToRestore: null | HTMLElement,
24
- currentFocusedNode: null | HTMLElement,
25
-};
26
-
27
-const targetEventTypes = ['keydown_active'];
28
-const rootEventTypes = ['focus'];
29
-
30
-function focusElement(element: ?HTMLElement) {
31
- if (element != null) {
32
- try {
33
- element.focus();
34
- } catch (err) {}
35
- }
36
-}
37
-
38
-function getFirstFocusableElement(
39
- context: ReactDOMResponderContext,
40
- state: FocusScopeState,
41
-): ?HTMLElement {
42
- const elements = context.getFocusableElementsInScope(false);
43
- if (elements.length > 0) {
44
- return elements[0];
45
- }
46
-}
47
-
48
-const focusScopeResponderImpl = {
49
- targetEventTypes,
50
- rootEventTypes,
51
- getInitialState(): FocusScopeState {
52
- return {
53
- nodeToRestore: null,
54
- currentFocusedNode: null,
55
- };
56
- },
57
- onEvent(
58
- event: ReactDOMResponderEvent,
59
- context: ReactDOMResponderContext,
60
- props: FocusScopeProps,
61
- state: FocusScopeState,
62
- ) {
63
- const {type, nativeEvent} = event;
64
-
65
- if (type === 'keydown' && nativeEvent.key === 'Tab') {
66
- const focusedElement = context.getActiveDocument().activeElement;
67
- if (
68
- focusedElement !== null &&
69
- context.isTargetWithinResponder(focusedElement)
70
- ) {
71
- const {altkey, ctrlKey, metaKey, shiftKey} = (nativeEvent: any);
72
- // Skip if any of these keys are being pressed
73
- if (altkey || ctrlKey || metaKey) {
74
- return;
75
- }
76
- const elements = context.getFocusableElementsInScope(false);
77
- const position = elements.indexOf(focusedElement);
78
- const lastPosition = elements.length - 1;
79
- let nextElement = null;
80
-
81
- if (shiftKey) {
82
- if (position === 0) {
83
- if (props.contain) {
84
- nextElement = elements[lastPosition];
85
- } else {
86
- // Out of bounds
87
- // TODO remove this, this is hacky
88
- const allElements = context.getFocusableElementsInScope(true);
89
- const prevPosition = allElements.indexOf(focusedElement) - 1;
90
- nextElement = allElements[prevPosition] || null;
91
- return;
92
- }
93
- } else {
94
- nextElement = elements[position - 1];
95
- }
96
- } else {
97
- if (position === lastPosition) {
98
- if (props.contain) {
99
- nextElement = elements[0];
100
- } else {
101
- // Out of bounds
102
- // TODO remove this, this is hacky
103
- const allElements = context.getFocusableElementsInScope(true);
104
- const nextPosition = allElements.indexOf(focusedElement) + 1;
105
- nextElement = allElements[nextPosition] || null;
106
- }
107
- } else {
108
- nextElement = elements[position + 1];
109
- }
110
- }
111
- if (nextElement !== null) {
112
- focusElement(nextElement);
113
- state.currentFocusedNode = nextElement;
114
- ((nativeEvent: any): KeyboardEvent).preventDefault();
115
- }
116
- }
117
- }
118
- },
119
- onRootEvent(
120
- event: ReactDOMResponderEvent,
121
- context: ReactDOMResponderContext,
122
- props: FocusScopeProps,
123
- state: FocusScopeState,
124
- ) {
125
- const {target} = event;
126
-
127
- // Handle global focus containment
128
- if (props.contain) {
129
- if (!context.isTargetWithinResponder(target)) {
130
- const currentFocusedNode = state.currentFocusedNode;
131
- if (currentFocusedNode !== null) {
132
- focusElement(currentFocusedNode);
133
- } else if (props.autoFocus) {
134
- const firstElement = getFirstFocusableElement(context, state);
135
- focusElement(firstElement);
136
- }
137
- }
138
- }
139
- },
140
- onMount(
141
- context: ReactDOMResponderContext,
142
- props: FocusScopeProps,
143
- state: FocusScopeState,
144
- ): void {
145
- if (props.restoreFocus) {
146
- state.nodeToRestore = context.getActiveDocument().activeElement;
147
- }
148
- if (props.autoFocus) {
149
- const firstElement = getFirstFocusableElement(context, state);
150
- focusElement(firstElement);
151
- }
152
- },
153
- onUnmount(
154
- context: ReactDOMResponderContext,
155
- props: FocusScopeProps,
156
- state: FocusScopeState,
157
- ): void {
158
- if (props.restoreFocus && state.nodeToRestore !== null) {
159
- focusElement(state.nodeToRestore);
160
- }
161
- },
162
-};
163
-
164
-export const FocusScopeResponder = React.unstable_createResponder(
165
- 'FocusSocpe',
166
- focusScopeResponderImpl,
167
-);
packages/react-events/src/dom/__tests__/FocusScope-test.internal.js
deleted
-234
@@ -1,234 +0,0 @@
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
-let React;
13
-let ReactFeatureFlags;
14
-let ReactDOM;
15
-let FocusScopeResponder;
16
-
17
-const createTabForward = type => {
18
- const event = new KeyboardEvent('keydown', {
19
- key: 'Tab',
20
- bubbles: true,
21
- cancelable: true,
22
- });
23
- return event;
24
-};
25
-
26
-const createTabBackward = type => {
27
- const event = new KeyboardEvent('keydown', {
28
- key: 'Tab',
29
- shiftKey: true,
30
- bubbles: true,
31
- cancelable: true,
32
- });
33
- return event;
34
-};
35
-
36
-describe('FocusScope event responder', () => {
37
- let container;
38
-
39
- beforeEach(() => {
40
- jest.resetModules();
41
- ReactFeatureFlags = require('shared/ReactFeatureFlags');
42
- ReactFeatureFlags.enableFlareAPI = true;
43
- React = require('react');
44
- ReactDOM = require('react-dom');
45
- FocusScopeResponder = require('react-events/focus-scope')
46
- .FocusScopeResponder;
47
- container = document.createElement('div');
48
- document.body.appendChild(container);
49
- });
50
-
51
- afterEach(() => {
52
- ReactDOM.render(null, container);
53
- document.body.removeChild(container);
54
- container = null;
55
- });
56
-
57
- it('should work as expected with autofocus', () => {
58
- const inputRef = React.createRef();
59
- const input2Ref = React.createRef();
60
- const buttonRef = React.createRef();
61
- const butto2nRef = React.createRef();
62
- const divRef = React.createRef();
63
-
64
- const SimpleFocusScope = () => (
65
- <div responders={<FocusScopeResponder autoFocus={true} />}>
66
- <input ref={inputRef} />
67
- <button ref={buttonRef} />
68
- <div ref={divRef} tabIndex={0} />
69
- <input ref={input2Ref} tabIndex={-1} />
70
- <button ref={butto2nRef} />
71
- </div>
72
- );
73
-
74
- ReactDOM.render(<SimpleFocusScope />, container);
75
- expect(document.activeElement).toBe(inputRef.current);
76
- document.activeElement.dispatchEvent(createTabForward());
77
- expect(document.activeElement).toBe(buttonRef.current);
78
- document.activeElement.dispatchEvent(createTabForward());
79
- expect(document.activeElement).toBe(divRef.current);
80
- document.activeElement.dispatchEvent(createTabForward());
81
- expect(document.activeElement).toBe(butto2nRef.current);
82
- document.activeElement.dispatchEvent(createTabBackward());
83
- expect(document.activeElement).toBe(divRef.current);
84
- });
85
-
86
- it('should work as expected with autoFocus and contain', () => {
87
- const inputRef = React.createRef();
88
- const input2Ref = React.createRef();
89
- const buttonRef = React.createRef();
90
- const button2Ref = React.createRef();
91
-
92
- const SimpleFocusScope = () => (
93
- <div responders={<FocusScopeResponder autoFocus={true} contain={true} />}>
94
- <input ref={inputRef} tabIndex={-1} />
95
- <button ref={buttonRef} id={1} />
96
- <button ref={button2Ref} id={2} />
97
- <input ref={input2Ref} tabIndex={-1} />
98
- </div>
99
- );
100
-
101
- ReactDOM.render(<SimpleFocusScope />, container);
102
- expect(document.activeElement).toBe(buttonRef.current);
103
- document.activeElement.dispatchEvent(createTabForward());
104
- expect(document.activeElement).toBe(button2Ref.current);
105
- document.activeElement.dispatchEvent(createTabForward());
106
- expect(document.activeElement).toBe(buttonRef.current);
107
- document.activeElement.dispatchEvent(createTabForward());
108
- expect(document.activeElement).toBe(button2Ref.current);
109
- document.activeElement.dispatchEvent(createTabBackward());
110
- expect(document.activeElement).toBe(buttonRef.current);
111
- document.activeElement.dispatchEvent(createTabBackward());
112
- expect(document.activeElement).toBe(button2Ref.current);
113
- });
114
-
115
- it('should work as expected when nested', () => {
116
- const inputRef = React.createRef();
117
- const input2Ref = React.createRef();
118
- const buttonRef = React.createRef();
119
- const button2Ref = React.createRef();
120
- const button3Ref = React.createRef();
121
- const button4Ref = React.createRef();
122
-
123
- const SimpleFocusScope = () => (
124
- <div responders={<FocusScopeResponder />}>
125
- <input ref={inputRef} tabIndex={-1} />
126
- <button ref={buttonRef} id={1} />
127
- <div responders={<FocusScopeResponder />}>
128
- <button ref={button2Ref} id={2} />
129
- <button ref={button3Ref} id={3} />
130
- </div>
131
- <input ref={input2Ref} tabIndex={-1} />
132
- <button ref={button4Ref} id={4} />
133
- </div>
134
- );
135
-
136
- ReactDOM.render(<SimpleFocusScope />, container);
137
- buttonRef.current.focus();
138
- expect(document.activeElement).toBe(buttonRef.current);
139
- document.activeElement.dispatchEvent(createTabForward());
140
- expect(document.activeElement).toBe(button2Ref.current);
141
- document.activeElement.dispatchEvent(createTabForward());
142
- expect(document.activeElement).toBe(button3Ref.current);
143
- document.activeElement.dispatchEvent(createTabForward());
144
- expect(document.activeElement).toBe(button4Ref.current);
145
- document.activeElement.dispatchEvent(createTabBackward());
146
- expect(document.activeElement).toBe(button3Ref.current);
147
- document.activeElement.dispatchEvent(createTabBackward());
148
- expect(document.activeElement).toBe(button2Ref.current);
149
- });
150
-
151
- it('should work as expected when nested with scope that is contained', () => {
152
- const inputRef = React.createRef();
153
- const input2Ref = React.createRef();
154
- const buttonRef = React.createRef();
155
- const button2Ref = React.createRef();
156
- const button3Ref = React.createRef();
157
- const button4Ref = React.createRef();
158
-
159
- const SimpleFocusScope = () => (
160
- <div responders={<FocusScopeResponder />}>
161
- <input ref={inputRef} tabIndex={-1} />
162
- <button ref={buttonRef} id={1} />
163
- <div responders={<FocusScopeResponder contain={true} />}>
164
- <button ref={button2Ref} id={2} />
165
- <button ref={button3Ref} id={3} />
166
- </div>
167
- <input ref={input2Ref} tabIndex={-1} />
168
- <button ref={button4Ref} id={4} />
169
- </div>
170
- );
171
-
172
- ReactDOM.render(<SimpleFocusScope />, container);
173
- buttonRef.current.focus();
174
- expect(document.activeElement).toBe(buttonRef.current);
175
- document.activeElement.dispatchEvent(createTabForward());
176
- expect(document.activeElement).toBe(button2Ref.current);
177
- document.activeElement.dispatchEvent(createTabForward());
178
- expect(document.activeElement).toBe(button3Ref.current);
179
- document.activeElement.dispatchEvent(createTabForward());
180
- expect(document.activeElement).toBe(button2Ref.current);
181
- document.activeElement.dispatchEvent(createTabBackward());
182
- expect(document.activeElement).toBe(button3Ref.current);
183
- document.activeElement.dispatchEvent(createTabBackward());
184
- expect(document.activeElement).toBe(button2Ref.current);
185
- });
186
-
187
- it('should work as expected with suspense fallbacks', () => {
188
- const buttonRef = React.createRef();
189
- const button2Ref = React.createRef();
190
- const button3Ref = React.createRef();
191
- const button4Ref = React.createRef();
192
- const button5Ref = React.createRef();
193
-
194
- function SuspendedComponent() {
195
- throw new Promise(() => {
196
- // Never resolve
197
- });
198
- }
199
-
200
- function Component() {
201
- return (
202
- <React.Fragment>
203
- <button ref={button5Ref} id={5} />
204
- <SuspendedComponent />
205
- </React.Fragment>
206
- );
207
- }
208
-
209
- const SimpleFocusScope = () => (
210
- <div responders={<FocusScopeResponder />}>
211
- <button ref={buttonRef} id={1} />
212
- <button ref={button2Ref} id={2} />
213
- <React.Suspense fallback={<button ref={button3Ref} id={3} />}>
214
- <Component />
215
- </React.Suspense>
216
- <button ref={button4Ref} id={4} />
217
- </div>
218
- );
219
-
220
- ReactDOM.render(<SimpleFocusScope />, container);
221
- buttonRef.current.focus();
222
- expect(document.activeElement).toBe(buttonRef.current);
223
- document.activeElement.dispatchEvent(createTabForward());
224
- expect(document.activeElement).toBe(button2Ref.current);
225
- document.activeElement.dispatchEvent(createTabForward());
226
- expect(document.activeElement).toBe(button3Ref.current);
227
- document.activeElement.dispatchEvent(createTabForward());
228
- expect(document.activeElement).toBe(button4Ref.current);
229
- document.activeElement.dispatchEvent(createTabBackward());
230
- expect(document.activeElement).toBe(button3Ref.current);
231
- document.activeElement.dispatchEvent(createTabBackward());
232
- expect(document.activeElement).toBe(button2Ref.current);
233
- });
234
-});
scripts/rollup/bundles.js
-15
@@ -464,21 +464,6 @@ const bundles = [
464
externals: ['react'],
465
},
466
467
- {
468
- bundleTypes: [
469
- UMD_DEV,
470
- UMD_PROD,
471
- NODE_DEV,
472
- NODE_PROD,
473
- FB_WWW_DEV,
474
- FB_WWW_PROD,
475
- ],
476
- moduleType: NON_FIBER_RENDERER,
477
- entry: 'react-events/focus-scope',
478
- global: 'ReactEventsFocusScope',
479
- externals: ['react'],
480
- },
481
-
467
{
468
bundleTypes: [
469
UMD_DEV,