[react-interactions] Remove deprecated Scope APIs + update Focus components/docs (#17859)
Dominic Gannaway committed
Jan 17, 2020 at 12:16 UTC
9e075d16b250de8f65680c45c3686eb3faec8153
25 files changed
+15
-2710
packages/react-interactions/accessibility/README.md
deleted
-25
@@ -1,25 +0,0 @@
1
-# `react-interactions/accessibility`
2
-
3
-*This package is experimental. It is intended for use with the experimental React
4
-Scope API that is not available in open source builds.*
5
-
6
-Scopes allow for querying of the internal React sub-tree to collect handles to
7
-host nodes. Scopes also have their own separate tree structure that allows
8
-traversal of scopes of the same type.
9
-
10
-The core API is documented below. Documentation for individual Accessibility Components
11
-can be found [here](./docs).
12
-
13
-## React Scopes
14
-
15
-Note: React Scopes require the internal React flag `enableScopeAPI`.
16
-
17
-## React Scope Interface
18
-
19
-Scopes require a `ref` to access the internal interface of a particular scope.
20
-The internal interface (`ReactScopeInterface`) exposes the following scope API:
21
-
22
-### containsNode: (node: HTMLElement) => boolean
23
-
24
-Returns `true` or `false` depending on if the given `HTMLElement` is a descendant
25
-of the scope's sub-tree.
\ No newline at end of file
packages/react-interactions/accessibility/docs/FocusContain.md
deleted
-27
@@ -1,27 +0,0 @@
1
-# FocusContain
2
-
3
-`FocusContain` is a component that contains user-focusability to only that
4
-of the children of the component. This means focus control will not escape
5
-unless the componoent is disabled (using the `disabled` prop) or unmounted.
6
-Additionally, `FocusContain` can contain tab focus when passed a `ReactScope`
7
-using the `tabFocus` prop.
8
-
9
-## Usage
10
-
11
-```jsx
12
-import FocusContain from 'react-interactions/accessibility/focus-contain';
13
-import tabbableScopeQuery from 'react-interactions/accessibility/tabbable-scope-query';
14
-
15
-function MyDialog(props) {
16
- return (
17
- <FocusContain scopeQuery={tabbableScopeQuery} disabled={false}>
18
- <div>
19
- <h2>{props.title}<h2>
20
- <p>{props.text}</p>
21
- <Button onPress={...}>Accept</Button>
22
- <Button onPress={...}>Close</Button>
23
- </div>
24
- </FocusContain>
25
- )
26
-}
27
-```
packages/react-interactions/accessibility/docs/FocusManager.md
deleted
-63
@@ -1,63 +0,0 @@
1
-# FocusManager
2
-
3
-`FocusManager` is a module that exports a selection of helpful utility functions to be used
4
-in conjunction with the `ref` from a React Scope, such as `TabbableScope`.
5
-
6
-## Example
7
-
8
-```jsx
9
-import {
10
- focusFirst,
11
- focusNext,
12
- focusPrevious,
13
- getNextScope,
14
- getPreviousScope,
15
-} from 'react-interactions/accessibility/focus-manager';
16
-
17
-function scopeQuery(type) {
18
- return type === 'div';
19
-}
20
-
21
-function KeyboardFocusMover(props) {
22
- const scopeRef = useRef(null);
23
-
24
- useEffect(() => {
25
- const scope = scopeRef.current;
26
-
27
- if (scope) {
28
- // Focus the first tabbable DOM node in my children
29
- focusFirst(scopeQuery, scope);
30
- // Then focus the next chilkd
31
- focusNext(scopeQuery, scope);
32
- }
33
- });
34
-
35
- return (
36
- <TabbableScope ref={scopeRef}>
37
- {props.children}
38
- </TabbableScope>
39
- );
40
-}
41
-```
42
-
43
-## FocusManager API
44
-
45
-### `focusFirst`
46
-
47
-Focus the first node that matches the given scope.
48
-
49
-### `focusNext`
50
-
51
-Focus the next sequential node that matches the given scope.
52
-
53
-### `focusPrevious`
54
-
55
-Focus the previous sequential node that matches the given scope.
56
-
57
-### `getNextScope`
58
-
59
-Focus the first node that matches the next sibling scope from the given scope.
60
-
61
-### `getPreviousScope`
62
-
63
-Focus the first node that matches the previous sibling scope from the given scope.
packages/react-interactions/accessibility/docs/TabbableScopeQuery.md
deleted
-31
@@ -1,31 +0,0 @@
1
-# TabbableScopeQuery
2
-
3
-`TabbableScopeQuery` is a custom scope implementation that can be used with
4
-`FocusContain`, `FocusGroup`, `FocusTable` and `FocusManager` modules.
5
-
6
-## Usage
7
-
8
-```jsx
9
-import tabbableScopeQuery from 'react-interactions/accessibility/tabbable-scope-query';
10
-
11
-function FocusableNodeCollector(props) {
12
- const scopeRef = useRef(null);
13
-
14
- useEffect(() => {
15
- const scope = scopeRef.current;
16
-
17
- if (scope) {
18
- const tabFocusableNodes = scope.DO_NOT_USE_queryAllNodes(tabbableScopeQuery);
19
- if (tabFocusableNodes && props.onFocusableNodes) {
20
- props.onFocusableNodes(tabFocusableNodes);
21
- }
22
- }
23
- });
24
-
25
- return (
26
- <TabbableScope ref={scopeRef}>
27
- {props.children}
28
- </TabbableScope>
29
- );
30
-}
31
-```
packages/react-interactions/accessibility/focus-contain.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/FocusContain');
packages/react-interactions/accessibility/focus-group.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/FocusGroup');
packages/react-interactions/accessibility/focus-manager.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/FocusManager');
packages/react-interactions/accessibility/focus-table.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/FocusTable');
packages/react-interactions/accessibility/src/FocusContain.js
deleted
-88
@@ -1,88 +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
-import type {KeyboardEvent} from 'react-interactions/events/keyboard';
11
-
12
-import React from 'react';
13
-import {useFocusWithin} from 'react-interactions/events/focus';
14
-import {useKeyboard} from 'react-interactions/events/keyboard';
15
-import {
16
- focusPrevious,
17
- focusNext,
18
-} from 'react-interactions/accessibility/focus-manager';
19
-
20
-type FocusContainProps = {|
21
- children: React.Node,
22
- disabled?: boolean,
23
- scopeQuery: (type: string | Object, props: Object) => boolean,
24
-|};
25
-
26
-const {useLayoutEffect, useRef} = React;
27
-
28
-const FocusContainScope = React.unstable_createScope();
29
-
30
-export default function FocusContain({
31
- children,
32
- disabled,
33
- scopeQuery,
34
-}: FocusContainProps): React.Node {
35
- const scopeRef = useRef(null);
36
- // This ensures tabbing works through the React tree (including Portals and Suspense nodes)
37
- const keyboard = useKeyboard({
38
- onKeyDown(event: KeyboardEvent): void {
39
- if (disabled === true || event.key !== 'Tab') {
40
- event.continuePropagation();
41
- return;
42
- }
43
- const scope = scopeRef.current;
44
- if (scope !== null) {
45
- if (event.shiftKey) {
46
- focusPrevious(scopeQuery, scope, event, true);
47
- } else {
48
- focusNext(scopeQuery, scope, event, true);
49
- }
50
- }
51
- },
52
- });
53
- const focusWithin = useFocusWithin({
54
- onBlurWithin: function(event) {
55
- if (disabled === true) {
56
- event.continuePropagation();
57
- return;
58
- }
59
- const lastNode = event.target;
60
- if (lastNode) {
61
- requestAnimationFrame(() => {
62
- (lastNode: any).focus();
63
- });
64
- }
65
- },
66
- });
67
- useLayoutEffect(() => {
68
- const scope = scopeRef.current;
69
- if (
70
- scope !== null &&
71
- disabled !== true &&
72
- !scope.containsNode(document.activeElement)
73
- ) {
74
- const fistElem = scope.DO_NOT_USE_queryFirstNode(scopeQuery);
75
- if (fistElem !== null) {
76
- fistElem.focus();
77
- }
78
- }
79
- }, [disabled]);
80
-
81
- return (
82
- <FocusContainScope
83
- ref={scopeRef}
84
- DEPRECATED_flareListeners={[keyboard, focusWithin]}>
85
- {children}
86
- </FocusContainScope>
87
- );
88
-}
packages/react-interactions/accessibility/src/FocusGroup.js
deleted
-233
@@ -1,233 +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
-import type {ReactScopeMethods} from 'shared/ReactTypes';
11
-import type {KeyboardEvent} from 'react-interactions/events/keyboard';
12
-
13
-import React from 'react';
14
-import {useKeyboard} from 'react-interactions/events/keyboard';
15
-import setElementCanTab from './shared/setElementCanTab';
16
-
17
-type FocusItemProps = {
18
- children?: React.Node,
19
- onKeyDown?: KeyboardEvent => void,
20
- ...
21
-};
22
-
23
-type FocusGroupProps = {|
24
- children: React.Node,
25
- portrait: boolean,
26
- wrap?: boolean,
27
- tabScopeQuery?: (type: string | Object, props: Object) => boolean,
28
- allowModifiers?: boolean,
29
-|};
30
-
31
-const {useRef} = React;
32
-
33
-function focusGroupItem(
34
- scopeQuery: (type: string | Object, props: Object) => boolean,
35
- cell: ReactScopeMethods,
36
- event: KeyboardEvent,
37
-): void {
38
- const firstScopedNode = cell.DO_NOT_USE_queryFirstNode(scopeQuery);
39
- if (firstScopedNode !== null) {
40
- firstScopedNode.focus();
41
- event.preventDefault();
42
- }
43
-}
44
-
45
-function getPreviousGroupItem(
46
- group: ReactScopeMethods,
47
- currentItem: ReactScopeMethods,
48
-): null | ReactScopeMethods {
49
- const items = group.DO_NOT_USE_getChildren();
50
- if (items !== null) {
51
- const currentItemIndex = items.indexOf(currentItem);
52
- const wrap = getGroupProps(currentItem).wrap;
53
- if (currentItemIndex === 0 && wrap) {
54
- return items[items.length - 1] || null;
55
- } else if (currentItemIndex > 0) {
56
- return items[currentItemIndex - 1] || null;
57
- }
58
- }
59
- return null;
60
-}
61
-
62
-function getNextGroupItem(
63
- group: ReactScopeMethods,
64
- currentItem: ReactScopeMethods,
65
-): null | ReactScopeMethods {
66
- const items = group.DO_NOT_USE_getChildren();
67
- if (items !== null) {
68
- const currentItemIndex = items.indexOf(currentItem);
69
- const wrap = getGroupProps(currentItem).wrap;
70
- const end = currentItemIndex === items.length - 1;
71
- if (end && wrap) {
72
- return items[0] || null;
73
- } else if (currentItemIndex !== -1 && !end) {
74
- return items[currentItemIndex + 1] || null;
75
- }
76
- }
77
- return null;
78
-}
79
-
80
-function getGroupProps(currentCell: ReactScopeMethods): Object {
81
- const group = currentCell.DO_NOT_USE_getParent();
82
- if (group !== null) {
83
- const groupProps = group.DO_NOT_USE_getProps();
84
- if (groupProps && groupProps.type === 'group') {
85
- return groupProps;
86
- }
87
- }
88
- return {};
89
-}
90
-
91
-function hasModifierKey(event: KeyboardEvent): boolean {
92
- const {altKey, ctrlKey, metaKey, shiftKey} = event;
93
- return (
94
- altKey === true || ctrlKey === true || metaKey === true || shiftKey === true
95
- );
96
-}
97
-
98
-export function createFocusGroup(
99
- scopeQuery: (type: string | Object, props: Object) => boolean,
100
-): [(FocusGroupProps) => React.Node, (FocusItemProps) => React.Node] {
101
- const TableScope = React.unstable_createScope();
102
-
103
- function Group({
104
- children,
105
- portrait,
106
- wrap,
107
- tabScopeQuery,
108
- allowModifiers,
109
- }: FocusGroupProps): React.Node {
110
- return (
111
- <TableScope
112
- type="group"
113
- portrait={portrait}
114
- wrap={wrap}
115
- tabScopeQuery={tabScopeQuery}
116
- allowModifiers={allowModifiers}>
117
- {children}
118
- </TableScope>
119
- );
120
- }
121
-
122
- function Item({children, onKeyDown}: FocusItemProps): React.Node {
123
- const scopeRef = useRef(null);
124
- const keyboard = useKeyboard({
125
- onKeyDown(event: KeyboardEvent): void {
126
- const currentItem = scopeRef.current;
127
- if (currentItem !== null) {
128
- const group = currentItem.DO_NOT_USE_getParent();
129
- const groupProps = group && group.DO_NOT_USE_getProps();
130
- if (group !== null && groupProps.type === 'group') {
131
- const portrait = groupProps.portrait;
132
- const key = event.key;
133
-
134
- if (key === 'Tab') {
135
- const tabScopeQuery = getGroupProps(currentItem).tabScopeQuery;
136
- if (tabScopeQuery) {
137
- const groupScope = currentItem.DO_NOT_USE_getParent();
138
- if (groupScope) {
139
- const activeNode = document.activeElement;
140
- const nodes = groupScope.DO_NOT_USE_queryAllNodes(
141
- tabScopeQuery,
142
- );
143
- for (let i = 0; i < nodes.length; i++) {
144
- const node = nodes[i];
145
- if (node !== activeNode) {
146
- setElementCanTab(node, false);
147
- } else {
148
- setElementCanTab(node, true);
149
- }
150
- }
151
- return;
152
- }
153
- }
154
- event.continuePropagation();
155
- return;
156
- }
157
- // Using modifier keys with keyboard arrow events should be no-ops
158
- // unless an explicit allowModifiers flag is set on the FocusGroup.
159
- if (hasModifierKey(event)) {
160
- const allowModifiers = getGroupProps(currentItem).allowModifiers;
161
- if (!allowModifiers) {
162
- event.continuePropagation();
163
- return;
164
- }
165
- }
166
- switch (key) {
167
- case 'ArrowUp': {
168
- if (portrait) {
169
- const previousGroupItem = getPreviousGroupItem(
170
- group,
171
- currentItem,
172
- );
173
- if (previousGroupItem) {
174
- focusGroupItem(scopeQuery, previousGroupItem, event);
175
- return;
176
- }
177
- }
178
- break;
179
- }
180
- case 'ArrowDown': {
181
- if (portrait) {
182
- const nextGroupItem = getNextGroupItem(group, currentItem);
183
- if (nextGroupItem) {
184
- focusGroupItem(scopeQuery, nextGroupItem, event);
185
- return;
186
- }
187
- }
188
- break;
189
- }
190
- case 'ArrowLeft': {
191
- if (!portrait) {
192
- const previousGroupItem = getPreviousGroupItem(
193
- group,
194
- currentItem,
195
- );
196
- if (previousGroupItem) {
197
- focusGroupItem(scopeQuery, previousGroupItem, event);
198
- return;
199
- }
200
- }
201
- break;
202
- }
203
- case 'ArrowRight': {
204
- if (!portrait) {
205
- const nextGroupItem = getNextGroupItem(group, currentItem);
206
- if (nextGroupItem) {
207
- focusGroupItem(scopeQuery, nextGroupItem, event);
208
- return;
209
- }
210
- }
211
- break;
212
- }
213
- }
214
- }
215
- }
216
- if (onKeyDown) {
217
- onKeyDown(event);
218
- }
219
- event.continuePropagation();
220
- },
221
- });
222
- return (
223
- <TableScope
224
- DEPRECATED_flareListeners={keyboard}
225
- ref={scopeRef}
226
- type="item">
227
- {children}
228
- </TableScope>
229
- );
230
- }
231
-
232
- return [Group, Item];
233
-}
packages/react-interactions/accessibility/src/FocusManager.js
deleted
-127
@@ -1,127 +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
-import type {ReactScopeMethods} from 'shared/ReactTypes';
11
-import type {KeyboardEvent} from 'react-interactions/events/keyboard';
12
-
13
-import getTabbableNodes from './shared/getTabbableNodes';
14
-
15
-export function focusFirst(
16
- scopeQuery: (type: string | Object, props: Object) => boolean,
17
- scope: ReactScopeMethods,
18
-): void {
19
- const firstNode = scope.DO_NOT_USE_queryFirstNode(scopeQuery);
20
- if (firstNode) {
21
- focusElem(firstNode);
22
- }
23
-}
24
-
25
-function focusElem(elem: null | HTMLElement): void {
26
- if (elem !== null) {
27
- elem.focus();
28
- }
29
-}
30
-
31
-export function focusNext(
32
- scopeQuery: (type: string | Object, props: Object) => boolean,
33
- scope: ReactScopeMethods,
34
- event?: KeyboardEvent,
35
- contain?: boolean,
36
-): void {
37
- const [
38
- tabbableNodes,
39
- firstTabbableElem,
40
- lastTabbableElem,
41
- currentIndex,
42
- focusedElement,
43
- ] = getTabbableNodes(scopeQuery, scope);
44
-
45
- if (focusedElement === null) {
46
- if (event) {
47
- event.continuePropagation();
48
- }
49
- } else if (focusedElement === lastTabbableElem) {
50
- if (contain === true) {
51
- focusElem(firstTabbableElem);
52
- if (event) {
53
- event.preventDefault();
54
- }
55
- } else if (event) {
56
- event.continuePropagation();
57
- }
58
- } else if (tabbableNodes) {
59
- focusElem(tabbableNodes[currentIndex + 1]);
60
- if (event) {
61
- event.preventDefault();
62
- }
63
- }
64
-}
65
-
66
-export function focusPrevious(
67
- scopeQuery: (type: string | Object, props: Object) => boolean,
68
- scope: ReactScopeMethods,
69
- event?: KeyboardEvent,
70
- contain?: boolean,
71
-): void {
72
- const [
73
- tabbableNodes,
74
- firstTabbableElem,
75
- lastTabbableElem,
76
- currentIndex,
77
- focusedElement,
78
- ] = getTabbableNodes(scopeQuery, scope);
79
-
80
- if (focusedElement === null) {
81
- if (event) {
82
- event.continuePropagation();
83
- }
84
- } else if (focusedElement === firstTabbableElem) {
85
- if (contain === true) {
86
- focusElem(lastTabbableElem);
87
- if (event) {
88
- event.preventDefault();
89
- }
90
- } else if (event) {
91
- event.continuePropagation();
92
- }
93
- } else if (tabbableNodes) {
94
- focusElem(tabbableNodes[currentIndex - 1]);
95
- if (event) {
96
- event.preventDefault();
97
- }
98
- }
99
-}
100
-
101
-export function getNextScope(
102
- scope: ReactScopeMethods,
103
-): null | ReactScopeMethods {
104
- const allScopes = scope.DO_NOT_USE_getChildrenFromRoot();
105
- if (allScopes === null) {
106
- return null;
107
- }
108
- const currentScopeIndex = allScopes.indexOf(scope);
109
- if (currentScopeIndex === -1 || currentScopeIndex === allScopes.length - 1) {
110
- return null;
111
- }
112
- return allScopes[currentScopeIndex + 1];
113
-}
114
-
115
-export function getPreviousScope(
116
- scope: ReactScopeMethods,
117
-): null | ReactScopeMethods {
118
- const allScopes = scope.DO_NOT_USE_getChildrenFromRoot();
119
- if (allScopes === null) {
120
- return null;
121
- }
122
- const currentScopeIndex = allScopes.indexOf(scope);
123
- if (currentScopeIndex <= 0) {
124
- return null;
125
- }
126
- return allScopes[currentScopeIndex - 1];
127
-}
packages/react-interactions/accessibility/src/FocusTable.js
deleted
-360
@@ -1,360 +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
-import type {ReactScopeMethods} from 'shared/ReactTypes';
11
-import type {KeyboardEvent} from 'react-interactions/events/keyboard';
12
-
13
-import React from 'react';
14
-import {useKeyboard} from 'react-interactions/events/keyboard';
15
-import setElementCanTab from './shared/setElementCanTab';
16
-
17
-type FocusCellProps = {
18
- children?: React.Node,
19
- onKeyDown?: KeyboardEvent => void,
20
- colSpan?: number,
21
- ...
22
-};
23
-
24
-type FocusRowProps = {children: React.Node, ...};
25
-
26
-type FocusTableProps = {|
27
- children: React.Node,
28
- onKeyboardOut?: (
29
- direction: 'left' | 'right' | 'up' | 'down',
30
- event: KeyboardEvent,
31
- ) => void,
32
- wrapX?: boolean,
33
- wrapY?: boolean,
34
- tabScopeQuery?: (type: string | Object, props: Object) => boolean,
35
- allowModifiers?: boolean,
36
-|};
37
-
38
-const {useRef} = React;
39
-
40
-function focusScope(
41
- scopeQuery: (type: string | Object, props: Object) => boolean,
42
- cell: ReactScopeMethods,
43
- event?: KeyboardEvent,
44
-): void {
45
- const firstScopedNode = cell.DO_NOT_USE_queryFirstNode(scopeQuery);
46
- if (firstScopedNode !== null) {
47
- firstScopedNode.focus();
48
- if (event) {
49
- event.preventDefault();
50
- }
51
- }
52
-}
53
-
54
-// This takes into account colSpan
55
-function focusCellByColumnIndex(
56
- scopeQuery: (type: string | Object, props: Object) => boolean,
57
- row: ReactScopeMethods,
58
- columnIndex: number,
59
- event?: KeyboardEvent,
60
-): void {
61
- const cells = row.DO_NOT_USE_getChildren();
62
- if (cells !== null) {
63
- let colSize = 0;
64
- for (let i = 0; i < cells.length; i++) {
65
- const cell = cells[i];
66
- if (cell) {
67
- colSize += cell.DO_NOT_USE_getProps().colSpan || 1;
68
- if (colSize > columnIndex) {
69
- focusScope(scopeQuery, cell, event);
70
- return;
71
- }
72
- }
73
- }
74
- }
75
-}
76
-
77
-function getCellIndexes(
78
- cells: Array<ReactScopeMethods>,
79
- currentCell: ReactScopeMethods,
80
-): [number, number] {
81
- let totalColSpan = 0;
82
- for (let i = 0; i < cells.length; i++) {
83
- const cell = cells[i];
84
- if (cell === currentCell) {
85
- return [i, i + totalColSpan];
86
- }
87
- const colSpan = cell.DO_NOT_USE_getProps().colSpan;
88
- if (colSpan) {
89
- totalColSpan += colSpan - 1;
90
- }
91
- }
92
- return [-1, -1];
93
-}
94
-
95
-function getRowCells(currentCell: ReactScopeMethods) {
96
- const row = currentCell.DO_NOT_USE_getParent();
97
- if (row !== null && row.DO_NOT_USE_getProps().type === 'row') {
98
- const cells = row.DO_NOT_USE_getChildren();
99
- if (cells !== null) {
100
- const [rowIndex, rowIndexWithColSpan] = getCellIndexes(
101
- cells,
102
- currentCell,
103
- );
104
- return [cells, rowIndex, rowIndexWithColSpan];
105
- }
106
- }
107
- return [null, -1, -1];
108
-}
109
-
110
-function getRows(currentCell: ReactScopeMethods) {
111
- const row = currentCell.DO_NOT_USE_getParent();
112
- if (row !== null && row.DO_NOT_USE_getProps().type === 'row') {
113
- const table = row.DO_NOT_USE_getParent();
114
- if (table !== null && table.DO_NOT_USE_getProps().type === 'table') {
115
- const rows = table.DO_NOT_USE_getChildren();
116
- if (rows !== null) {
117
- const columnIndex = rows.indexOf(row);
118
- return [rows, columnIndex];
119
- }
120
- }
121
- }
122
- return [null, -1, -1];
123
-}
124
-
125
-function triggerNavigateOut(
126
- currentCell: ReactScopeMethods,
127
- direction: 'left' | 'right' | 'up' | 'down',
128
- event,
129
-): void {
130
- const row = currentCell.DO_NOT_USE_getParent();
131
- if (row !== null && row.DO_NOT_USE_getProps().type === 'row') {
132
- const table = row.DO_NOT_USE_getParent();
133
- if (table !== null) {
134
- const props = table.DO_NOT_USE_getProps();
135
- const onKeyboardOut = props.onKeyboardOut;
136
- if (props.type === 'table' && typeof onKeyboardOut === 'function') {
137
- onKeyboardOut(direction, event);
138
- return;
139
- }
140
- }
141
- }
142
- event.continuePropagation();
143
-}
144
-
145
-function getTableProps(currentCell: ReactScopeMethods): Object {
146
- const row = currentCell.DO_NOT_USE_getParent();
147
- if (row !== null && row.DO_NOT_USE_getProps().type === 'row') {
148
- const table = row.DO_NOT_USE_getParent();
149
- if (table !== null) {
150
- return table.DO_NOT_USE_getProps();
151
- }
152
- }
153
- return {};
154
-}
155
-
156
-function hasModifierKey(event: KeyboardEvent): boolean {
157
- const {altKey, ctrlKey, metaKey, shiftKey} = event;
158
- return (
159
- altKey === true || ctrlKey === true || metaKey === true || shiftKey === true
160
- );
161
-}
162
-
163
-export function createFocusTable(
164
- scopeQuery: (type: string | Object, props: Object) => boolean,
165
-): [
166
- (FocusTableProps) => React.Node,
167
- (FocusRowProps) => React.Node,
168
- (FocusCellProps) => React.Node,
169
-] {
170
- const TableScope = React.unstable_createScope();
171
-
172
- function Table({
173
- children,
174
- onKeyboardOut,
175
- wrapX,
176
- wrapY,
177
- tabScopeQuery,
178
- allowModifiers,
179
- }: FocusTableProps): React.Node {
180
- return (
181
- <TableScope
182
- type="table"
183
- onKeyboardOut={onKeyboardOut}
184
- wrapX={wrapX}
185
- wrapY={wrapY}
186
- tabScopeQuery={tabScopeQuery}
187
- allowModifiers={allowModifiers}>
188
- {children}
189
- </TableScope>
190
- );
191
- }
192
-
193
- function Row({children}: FocusRowProps): React.Node {
194
- return <TableScope type="row">{children}</TableScope>;
195
- }
196
-
197
- function Cell({children, onKeyDown, colSpan}: FocusCellProps): React.Node {
198
- const scopeRef = useRef(null);
199
- const keyboard = useKeyboard({
200
- onKeyDown(event: KeyboardEvent): void {
201
- const currentCell = scopeRef.current;
202
- if (currentCell === null) {
203
- event.continuePropagation();
204
- return;
205
- }
206
- const key = event.key;
207
- if (key === 'Tab') {
208
- const tabScopeQuery = getTableProps(currentCell).tabScopeQuery;
209
- if (tabScopeQuery) {
210
- const rowScope = currentCell.DO_NOT_USE_getParent();
211
- if (rowScope) {
212
- const tableScope = rowScope.DO_NOT_USE_getParent();
213
- if (tableScope) {
214
- const activeNode = document.activeElement;
215
- const nodes = tableScope.DO_NOT_USE_queryAllNodes(
216
- tabScopeQuery,
217
- );
218
- for (let i = 0; i < nodes.length; i++) {
219
- const node = nodes[i];
220
- if (node !== activeNode) {
221
- setElementCanTab(node, false);
222
- } else {
223
- setElementCanTab(node, true);
224
- }
225
- }
226
- return;
227
- }
228
- }
229
- }
230
- event.continuePropagation();
231
- return;
232
- }
233
- // Using modifier keys with keyboard arrow events should be no-ops
234
- // unless an explicit allowModifiers flag is set on the FocusTable.
235
- if (hasModifierKey(event)) {
236
- const allowModifiers = getTableProps(currentCell).allowModifiers;
237
- if (!allowModifiers) {
238
- event.continuePropagation();
239
- return;
240
- }
241
- }
242
- switch (key) {
243
- case 'ArrowUp': {
244
- const [cells, , cellIndexWithColSpan] = getRowCells(currentCell);
245
- if (cells !== null) {
246
- const [rows, rowIndex] = getRows(currentCell);
247
- if (rows !== null) {
248
- if (rowIndex > 0) {
249
- const row = rows[rowIndex - 1];
250
- focusCellByColumnIndex(
251
- scopeQuery,
252
- row,
253
- cellIndexWithColSpan,
254
- event,
255
- );
256
- } else if (rowIndex === 0) {
257
- const wrapY = getTableProps(currentCell).wrapY;
258
- if (wrapY) {
259
- const row = rows[rows.length - 1];
260
- focusCellByColumnIndex(
261
- scopeQuery,
262
- row,
263
- cellIndexWithColSpan,
264
- event,
265
- );
266
- } else {
267
- triggerNavigateOut(currentCell, 'up', event);
268
- }
269
- }
270
- }
271
- }
272
- return;
273
- }
274
- case 'ArrowDown': {
275
- const [cells, , cellIndexWithColSpan] = getRowCells(currentCell);
276
- if (cells !== null) {
277
- const [rows, rowIndex] = getRows(currentCell);
278
- if (rows !== null) {
279
- if (rowIndex !== -1) {
280
- if (rowIndex === rows.length - 1) {
281
- const wrapY = getTableProps(currentCell).wrapY;
282
- if (wrapY) {
283
- const row = rows[0];
284
- focusCellByColumnIndex(
285
- scopeQuery,
286
- row,
287
- cellIndexWithColSpan,
288
- event,
289
- );
290
- } else {
291
- triggerNavigateOut(currentCell, 'down', event);
292
- }
293
- } else {
294
- const row = rows[rowIndex + 1];
295
- focusCellByColumnIndex(
296
- scopeQuery,
297
- row,
298
- cellIndexWithColSpan,
299
- event,
300
- );
301
- }
302
- }
303
- }
304
- }
305
- return;
306
- }
307
- case 'ArrowLeft': {
308
- const [cells, rowIndex] = getRowCells(currentCell);
309
- if (cells !== null) {
310
- if (rowIndex > 0) {
311
- focusScope(scopeQuery, cells[rowIndex - 1]);
312
- event.preventDefault();
313
- } else if (rowIndex === 0) {
314
- const wrapX = getTableProps(currentCell).wrapX;
315
- if (wrapX) {
316
- focusScope(scopeQuery, cells[cells.length - 1], event);
317
- } else {
318
- triggerNavigateOut(currentCell, 'left', event);
319
- }
320
- }
321
- }
322
- return;
323
- }
324
- case 'ArrowRight': {
325
- const [cells, rowIndex] = getRowCells(currentCell);
326
- if (cells !== null) {
327
- if (rowIndex !== -1) {
328
- if (rowIndex === cells.length - 1) {
329
- const wrapX = getTableProps(currentCell).wrapX;
330
- if (wrapX) {
331
- focusScope(scopeQuery, cells[0], event);
332
- } else {
333
- triggerNavigateOut(currentCell, 'right', event);
334
- }
335
- } else {
336
- focusScope(scopeQuery, cells[rowIndex + 1], event);
337
- }
338
- }
339
- }
340
- return;
341
- }
342
- }
343
- if (onKeyDown) {
344
- onKeyDown(event);
345
- }
346
- },
347
- });
348
- return (
349
- <TableScope
350
- DEPRECATED_flareListeners={keyboard}
351
- ref={scopeRef}
352
- type="cell"
353
- colSpan={colSpan}>
354
- {children}
355
- </TableScope>
356
- );
357
- }
358
-
359
- return [Table, Row, Cell];
360
-}
packages/react-interactions/accessibility/src/TabbableScopeQuery.js
deleted
-33
@@ -1,33 +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
-const tabbableScopeQuery = (type: string, props: Object): boolean => {
11
- if (props.tabIndex === -1 || props.disabled) {
12
- return false;
13
- }
14
- if (props.tabIndex === 0 || props.contentEditable === true) {
15
- return true;
16
- }
17
- if (type === 'a' || type === 'area') {
18
- return !!props.href && props.rel !== 'ignore';
19
- }
20
- if (type === 'input') {
21
- return props.type !== 'hidden' && props.type !== 'file';
22
- }
23
- return (
24
- type === 'button' ||
25
- type === 'textarea' ||
26
- type === 'object' ||
27
- type === 'select' ||
28
- type === 'iframe' ||
29
- type === 'embed'
30
- );
31
-};
32
-
33
-export default tabbableScopeQuery;
packages/react-interactions/accessibility/src/__tests__/FocusContain-test.internal.js
deleted
-238
@@ -1,238 +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
-import {createEventTarget} from 'dom-event-testing-library';
11
-
12
-let React;
13
-let ReactFeatureFlags;
14
-let FocusContain;
15
-let tabbableScopeQuery;
16
-
17
-function tabNext(target) {
18
- target.keydown({key: 'Tab'});
19
- target.keyup({key: 'Tab'});
20
-}
21
-
22
-function tabPrevious(target) {
23
- target.keydown({key: 'Tab', shiftKey: true});
24
- target.keyup({key: 'Tab', shiftKey: true});
25
-}
26
-
27
-describe('FocusContain', () => {
28
- beforeEach(() => {
29
- jest.resetModules();
30
- ReactFeatureFlags = require('shared/ReactFeatureFlags');
31
- ReactFeatureFlags.enableScopeAPI = true;
32
- ReactFeatureFlags.enableDeprecatedFlareAPI = true;
33
- FocusContain = require('../FocusContain').default;
34
- tabbableScopeQuery = require('../TabbableScopeQuery').default;
35
- React = require('react');
36
- });
37
-
38
- describe('ReactDOM', () => {
39
- let ReactDOM;
40
- let container;
41
-
42
- beforeEach(() => {
43
- ReactDOM = require('react-dom');
44
- container = document.createElement('div');
45
- document.body.appendChild(container);
46
- });
47
-
48
- afterEach(() => {
49
- document.body.removeChild(container);
50
- container = null;
51
- });
52
-
53
- it('handles tab operations by default', () => {
54
- const inputRef = React.createRef();
55
- const input2Ref = React.createRef();
56
- const buttonRef = React.createRef();
57
- const button2Ref = React.createRef();
58
- const divRef = React.createRef();
59
-
60
- const Test = () => (
61
- <FocusContain scopeQuery={tabbableScopeQuery}>
62
- <input ref={inputRef} />
63
- <button ref={buttonRef} />
64
- <div ref={divRef} tabIndex={0} />
65
- <input ref={input2Ref} tabIndex={-1} />
66
- <button ref={button2Ref} />
67
- </FocusContain>
68
- );
69
-
70
- ReactDOM.render(<Test />, container);
71
- expect(document.activeElement).toBe(inputRef.current);
72
- tabNext(createEventTarget(document.activeElement));
73
- expect(document.activeElement).toBe(buttonRef.current);
74
- tabNext(createEventTarget(document.activeElement));
75
- expect(document.activeElement).toBe(divRef.current);
76
- tabNext(createEventTarget(document.activeElement));
77
- expect(document.activeElement).toBe(button2Ref.current);
78
- tabPrevious(createEventTarget(document.activeElement));
79
- expect(document.activeElement).toBe(divRef.current);
80
- });
81
-
82
- it('handles focus containment', () => {
83
- const inputRef = React.createRef();
84
- const input2Ref = React.createRef();
85
- const input3Ref = React.createRef();
86
- const buttonRef = React.createRef();
87
- const button2Ref = React.createRef();
88
-
89
- const Test = () => (
90
- <div>
91
- <FocusContain scopeQuery={tabbableScopeQuery}>
92
- <input ref={inputRef} tabIndex={-1} />
93
- <button ref={buttonRef} id={1} />
94
- <button ref={button2Ref} id={2} />
95
- <input ref={input2Ref} tabIndex={-1} />
96
- </FocusContain>
97
- <input ref={input3Ref} />
98
- </div>
99
- );
100
-
101
- ReactDOM.render(<Test />, container);
102
- buttonRef.current.focus();
103
- tabNext(createEventTarget(document.activeElement));
104
- expect(document.activeElement).toBe(button2Ref.current);
105
- tabNext(createEventTarget(document.activeElement));
106
- expect(document.activeElement).toBe(buttonRef.current);
107
- tabNext(createEventTarget(document.activeElement));
108
- expect(document.activeElement).toBe(button2Ref.current);
109
- tabPrevious(createEventTarget(document.activeElement));
110
- expect(document.activeElement).toBe(buttonRef.current);
111
- tabPrevious(createEventTarget(document.activeElement));
112
- expect(document.activeElement).toBe(button2Ref.current);
113
- // Focus should be restored to the contained area
114
- const rAF = window.requestAnimationFrame;
115
- window.requestAnimationFrame = x => setTimeout(x);
116
- input3Ref.current.focus();
117
- jest.advanceTimersByTime(1);
118
- window.requestAnimationFrame = rAF;
119
- expect(document.activeElement).toBe(button2Ref.current);
120
- });
121
-
122
- it('works with a disabled nested FocusContain', () => {
123
- const inputRef = React.createRef();
124
- const input2Ref = React.createRef();
125
- const buttonRef = React.createRef();
126
- const button2Ref = React.createRef();
127
- const button3Ref = React.createRef();
128
- const button4Ref = React.createRef();
129
-
130
- const Test = () => (
131
- <FocusContain scopeQuery={tabbableScopeQuery}>
132
- <input ref={inputRef} tabIndex={-1} />
133
- <button ref={buttonRef} id={1} />
134
- <FocusContain scopeQuery={tabbableScopeQuery} disabled={true}>
135
- <button ref={button2Ref} id={2} />
136
- <button ref={button3Ref} id={3} />
137
- </FocusContain>
138
- <input ref={input2Ref} tabIndex={-1} />
139
- <button ref={button4Ref} id={4} />
140
- </FocusContain>
141
- );
142
-
143
- ReactDOM.render(<Test />, container);
144
- expect(document.activeElement).toBe(buttonRef.current);
145
- tabNext(createEventTarget(document.activeElement));
146
- expect(document.activeElement).toBe(button2Ref.current);
147
- tabNext(createEventTarget(document.activeElement));
148
- expect(document.activeElement).toBe(button3Ref.current);
149
- tabNext(createEventTarget(document.activeElement));
150
- expect(document.activeElement).toBe(button4Ref.current);
151
- tabPrevious(createEventTarget(document.activeElement));
152
- expect(document.activeElement).toBe(button3Ref.current);
153
- tabPrevious(createEventTarget(document.activeElement));
154
- expect(document.activeElement).toBe(button2Ref.current);
155
- });
156
-
157
- it('works with enabled nested FocusContain', () => {
158
- const inputRef = React.createRef();
159
- const input2Ref = React.createRef();
160
- const buttonRef = React.createRef();
161
- const button2Ref = React.createRef();
162
- const button3Ref = React.createRef();
163
- const button4Ref = React.createRef();
164
-
165
- const Test = () => (
166
- <FocusContain scopeQuery={tabbableScopeQuery}>
167
- <input ref={inputRef} tabIndex={-1} />
168
- <button ref={buttonRef} id={1} />
169
- <FocusContain scopeQuery={tabbableScopeQuery} disabled={false}>
170
- <button ref={button2Ref} id={2} />
171
- <button ref={button3Ref} id={3} />
172
- </FocusContain>
173
- <input ref={input2Ref} tabIndex={-1} />
174
- <button ref={button4Ref} id={4} />
175
- </FocusContain>
176
- );
177
-
178
- ReactDOM.render(<Test />, container);
179
- expect(document.activeElement).toBe(button2Ref.current);
180
- tabNext(createEventTarget(document.activeElement));
181
- expect(document.activeElement).toBe(button3Ref.current);
182
- tabNext(createEventTarget(document.activeElement));
183
- expect(document.activeElement).toBe(button2Ref.current);
184
- tabPrevious(createEventTarget(document.activeElement));
185
- expect(document.activeElement).toBe(button3Ref.current);
186
- tabPrevious(createEventTarget(document.activeElement));
187
- expect(document.activeElement).toBe(button2Ref.current);
188
- });
189
-
190
- it('should work as expected with suspense fallbacks', () => {
191
- const buttonRef = React.createRef();
192
- const button2Ref = React.createRef();
193
- const button3Ref = React.createRef();
194
- const button4Ref = React.createRef();
195
- const button5Ref = React.createRef();
196
-
197
- function SuspendedComponent() {
198
- throw new Promise(() => {
199
- // Never resolve
200
- });
201
- }
202
-
203
- function Component() {
204
- return (
205
- <React.Fragment>
206
- <button ref={button5Ref} id={5} />
207
- <SuspendedComponent />
208
- </React.Fragment>
209
- );
210
- }
211
-
212
- const Test = () => (
213
- <FocusContain scopeQuery={tabbableScopeQuery}>
214
- <button ref={buttonRef} id={1} />
215
- <button ref={button2Ref} id={2} />
216
- <React.Suspense fallback={<button ref={button3Ref} id={3} />}>
217
- <Component />
218
- </React.Suspense>
219
- <button ref={button4Ref} id={4} />
220
- </FocusContain>
221
- );
222
-
223
- ReactDOM.render(<Test />, container);
224
- buttonRef.current.focus();
225
- expect(document.activeElement).toBe(buttonRef.current);
226
- tabNext(createEventTarget(document.activeElement));
227
- expect(document.activeElement).toBe(button2Ref.current);
228
- tabNext(createEventTarget(document.activeElement));
229
- expect(document.activeElement).toBe(button3Ref.current);
230
- tabNext(createEventTarget(document.activeElement));
231
- expect(document.activeElement).toBe(button4Ref.current);
232
- tabPrevious(createEventTarget(document.activeElement));
233
- expect(document.activeElement).toBe(button3Ref.current);
234
- tabPrevious(createEventTarget(document.activeElement));
235
- expect(document.activeElement).toBe(button2Ref.current);
236
- });
237
- });
238
-});
packages/react-interactions/accessibility/src/__tests__/FocusGroup-test.internal.js
deleted
-256
@@ -1,256 +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
-import {createEventTarget} from 'dom-event-testing-library';
11
-import {emulateBrowserTab} from '../shared/emulateBrowserTab';
12
-
13
-let React;
14
-let ReactFeatureFlags;
15
-let createFocusGroup;
16
-let tabbableScopeQuery;
17
-
18
-describe('FocusGroup', () => {
19
- beforeEach(() => {
20
- jest.resetModules();
21
- ReactFeatureFlags = require('shared/ReactFeatureFlags');
22
- ReactFeatureFlags.enableScopeAPI = true;
23
- ReactFeatureFlags.enableDeprecatedFlareAPI = true;
24
- createFocusGroup = require('../FocusGroup').createFocusGroup;
25
- tabbableScopeQuery = require('../TabbableScopeQuery').default;
26
- React = require('react');
27
- });
28
-
29
- describe('ReactDOM', () => {
30
- let ReactDOM;
31
- let container;
32
-
33
- beforeEach(() => {
34
- ReactDOM = require('react-dom');
35
- container = document.createElement('div');
36
- document.body.appendChild(container);
37
- });
38
-
39
- afterEach(() => {
40
- document.body.removeChild(container);
41
- container = null;
42
- });
43
-
44
- function createFocusGroupComponent() {
45
- const [FocusGroup, FocusItem] = createFocusGroup(tabbableScopeQuery);
46
-
47
- return ({portrait, wrap, allowModifiers}) => (
48
- <FocusGroup
49
- portrait={portrait}
50
- wrap={wrap}
51
- allowModifiers={allowModifiers}>
52
- <ul>
53
- <FocusItem>
54
- <li tabIndex={0}>Item 1</li>
55
- </FocusItem>
56
- <FocusItem>
57
- <li tabIndex={0}>Item 2</li>
58
- </FocusItem>
59
- <FocusItem>
60
- <li tabIndex={0}>Item 3</li>
61
- </FocusItem>
62
- </ul>
63
- </FocusGroup>
64
- );
65
- }
66
-
67
- it('handles keyboard arrow operations (portrait)', () => {
68
- const Test = createFocusGroupComponent();
69
-
70
- ReactDOM.render(<Test portrait={true} />, container);
71
- const groupItems = document.querySelectorAll('li');
72
- const firstGroupItem = createEventTarget(groupItems[0]);
73
- firstGroupItem.focus();
74
- firstGroupItem.keydown({
75
- key: 'ArrowDown',
76
- });
77
- expect(document.activeElement.textContent).toBe('Item 2');
78
-
79
- const secondGroupItem = createEventTarget(document.activeElement);
80
- secondGroupItem.keydown({
81
- key: 'ArrowDown',
82
- });
83
- expect(document.activeElement.textContent).toBe('Item 3');
84
-
85
- const thirdGroupItem = createEventTarget(document.activeElement);
86
- thirdGroupItem.keydown({
87
- key: 'ArrowDown',
88
- });
89
- expect(document.activeElement.textContent).toBe('Item 3');
90
- thirdGroupItem.keydown({
91
- key: 'ArrowRight',
92
- });
93
- expect(document.activeElement.textContent).toBe('Item 3');
94
- thirdGroupItem.keydown({
95
- key: 'ArrowLeft',
96
- });
97
- expect(document.activeElement.textContent).toBe('Item 3');
98
- // Should be a no-op due to modifier
99
- thirdGroupItem.keydown({
100
- key: 'ArrowUp',
101
- altKey: true,
102
- });
103
- expect(document.activeElement.textContent).toBe('Item 3');
104
- });
105
-
106
- it('handles keyboard arrow operations (landscape)', () => {
107
- const Test = createFocusGroupComponent();
108
-
109
- ReactDOM.render(<Test portrait={false} />, container);
110
- const GroupItems = document.querySelectorAll('li');
111
- const firstGroupItem = createEventTarget(GroupItems[0]);
112
- firstGroupItem.focus();
113
- firstGroupItem.keydown({
114
- key: 'ArrowRight',
115
- });
116
- expect(document.activeElement.textContent).toBe('Item 2');
117
-
118
- const secondGroupItem = createEventTarget(document.activeElement);
119
- secondGroupItem.keydown({
120
- key: 'ArrowRight',
121
- });
122
- expect(document.activeElement.textContent).toBe('Item 3');
123
-
124
- const thirdGroupItem = createEventTarget(document.activeElement);
125
- thirdGroupItem.keydown({
126
- key: 'ArrowRight',
127
- });
128
- expect(document.activeElement.textContent).toBe('Item 3');
129
- thirdGroupItem.keydown({
130
- key: 'ArrowUp',
131
- });
132
- expect(document.activeElement.textContent).toBe('Item 3');
133
- thirdGroupItem.keydown({
134
- key: 'ArrowDown',
135
- });
136
- expect(document.activeElement.textContent).toBe('Item 3');
137
- });
138
-
139
- it('handles keyboard arrow operations (portrait) with wrapping enabled', () => {
140
- const Test = createFocusGroupComponent();
141
-
142
- ReactDOM.render(<Test portrait={true} wrap={true} />, container);
143
- const GroupItems = document.querySelectorAll('li');
144
- let firstGroupItem = createEventTarget(GroupItems[0]);
145
- firstGroupItem.focus();
146
- firstGroupItem.keydown({
147
- key: 'ArrowDown',
148
- });
149
- expect(document.activeElement.textContent).toBe('Item 2');
150
-
151
- const secondGroupItem = createEventTarget(document.activeElement);
152
- secondGroupItem.keydown({
153
- key: 'ArrowDown',
154
- });
155
- expect(document.activeElement.textContent).toBe('Item 3');
156
-
157
- const thirdGroupItem = createEventTarget(document.activeElement);
158
- thirdGroupItem.keydown({
159
- key: 'ArrowDown',
160
- });
161
- expect(document.activeElement.textContent).toBe('Item 1');
162
-
163
- firstGroupItem = createEventTarget(document.activeElement);
164
- firstGroupItem.keydown({
165
- key: 'ArrowUp',
166
- });
167
- expect(document.activeElement.textContent).toBe('Item 3');
168
- });
169
-
170
- it('handles keyboard arrow operations (portrait) with allowModifiers', () => {
171
- const Test = createFocusGroupComponent();
172
-
173
- ReactDOM.render(
174
- <Test portrait={true} allowModifiers={true} />,
175
- container,
176
- );
177
- const GroupItems = document.querySelectorAll('li');
178
- let firstGroupItem = createEventTarget(GroupItems[0]);
179
- firstGroupItem.focus();
180
- firstGroupItem.keydown({
181
- key: 'ArrowDown',
182
- altKey: true,
183
- });
184
- expect(document.activeElement.textContent).toBe('Item 2');
185
- });
186
-
187
- it('handles keyboard arrow operations mixed with tabbing', () => {
188
- const [FocusGroup, FocusItem] = createFocusGroup(tabbableScopeQuery);
189
- const beforeRef = React.createRef();
190
- const afterRef = React.createRef();
191
-
192
- function Test() {
193
- return (
194
- <>
195
- <input placeholder="Before" ref={beforeRef} />
196
- <FocusGroup tabScopeQuery={tabbableScopeQuery} portrait={true}>
197
- <ul>
198
- <FocusItem>
199
- <li>
200
- <input placeholder="A" />
201
- </li>
202
- </FocusItem>
203
- <FocusItem>
204
- <li>
205
- <input placeholder="B" />
206
- </li>
207
- </FocusItem>
208
- <FocusItem>
209
- <li>
210
- <input placeholder="C" />
211
- </li>
212
- </FocusItem>
213
- <FocusItem>
214
- <li>
215
- <input placeholder="D" />
216
- </li>
217
- </FocusItem>
218
- <FocusItem>
219
- <li>
220
- <input placeholder="E" />
221
- </li>
222
- </FocusItem>
223
- <FocusItem>
224
- <li>
225
- <input placeholder="F" />
226
- </li>
227
- </FocusItem>
228
- </ul>
229
- </FocusGroup>
230
- <input placeholder="After" ref={afterRef} />
231
- </>
232
- );
233
- }
234
-
235
- ReactDOM.render(<Test />, container);
236
- beforeRef.current.focus();
237
-
238
- expect(document.activeElement.placeholder).toBe('Before');
239
- emulateBrowserTab();
240
- expect(document.activeElement.placeholder).toBe('A');
241
- emulateBrowserTab();
242
- expect(document.activeElement.placeholder).toBe('After');
243
- emulateBrowserTab(true);
244
- expect(document.activeElement.placeholder).toBe('A');
245
- const a = createEventTarget(document.activeElement);
246
- a.keydown({
247
- key: 'ArrowDown',
248
- });
249
- expect(document.activeElement.placeholder).toBe('B');
250
- emulateBrowserTab();
251
- expect(document.activeElement.placeholder).toBe('After');
252
- emulateBrowserTab(true);
253
- expect(document.activeElement.placeholder).toBe('B');
254
- });
255
- });
256
-});
packages/react-interactions/accessibility/src/__tests__/FocusManager-test.internal.js
deleted
-86
@@ -1,86 +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
-let React;
11
-let ReactFeatureFlags;
12
-let FocusManager;
13
-let tabbableScopeQuery;
14
-
15
-describe('FocusManager', () => {
16
- beforeEach(() => {
17
- jest.resetModules();
18
- ReactFeatureFlags = require('shared/ReactFeatureFlags');
19
- ReactFeatureFlags.enableScopeAPI = true;
20
- ReactFeatureFlags.enableDeprecatedFlareAPI = true;
21
- FocusManager = require('../FocusManager');
22
- tabbableScopeQuery = require('../TabbableScopeQuery').default;
23
- React = require('react');
24
- });
25
-
26
- describe('ReactDOM', () => {
27
- let ReactDOM;
28
- let container;
29
-
30
- beforeEach(() => {
31
- ReactDOM = require('react-dom');
32
- container = document.createElement('div');
33
- document.body.appendChild(container);
34
- });
35
-
36
- afterEach(() => {
37
- document.body.removeChild(container);
38
- container = null;
39
- });
40
-
41
- it('allows for imperative tab focus control with a scope', () => {
42
- const firstFocusControllerRef = React.createRef();
43
- const secondFocusControllerRef = React.createRef();
44
- const buttonRef = React.createRef();
45
- const button2Ref = React.createRef();
46
- const divRef = React.createRef();
47
- const TestScope = React.unstable_createScope();
48
-
49
- const Test = () => (
50
- <div>
51
- <TestScope ref={firstFocusControllerRef}>
52
- <input tabIndex={-1} />
53
- <button ref={buttonRef} />
54
- <button ref={button2Ref} />
55
- <input tabIndex={-1} />
56
- </TestScope>
57
- <TestScope ref={secondFocusControllerRef}>
58
- <input tabIndex={-1} />
59
- <div ref={divRef} tabIndex={0} />
60
- </TestScope>
61
- </div>
62
- );
63
-
64
- ReactDOM.render(<Test />, container);
65
- const firstFocusController = firstFocusControllerRef.current;
66
- const secondFocusController = secondFocusControllerRef.current;
67
-
68
- FocusManager.focusFirst(tabbableScopeQuery, firstFocusController);
69
- expect(document.activeElement).toBe(buttonRef.current);
70
- FocusManager.focusNext(tabbableScopeQuery, firstFocusController);
71
- expect(document.activeElement).toBe(button2Ref.current);
72
- FocusManager.focusPrevious(tabbableScopeQuery, firstFocusController);
73
- expect(document.activeElement).toBe(buttonRef.current);
74
-
75
- const nextController = FocusManager.getNextScope(firstFocusController);
76
- expect(nextController).toBe(secondFocusController);
77
- FocusManager.focusFirst(tabbableScopeQuery, nextController);
78
- expect(document.activeElement).toBe(divRef.current);
79
-
80
- const previousController = FocusManager.getPreviousScope(nextController);
81
- expect(previousController).toBe(firstFocusController);
82
- FocusManager.focusFirst(tabbableScopeQuery, previousController);
83
- expect(document.activeElement).toBe(buttonRef.current);
84
- });
85
- });
86
-});
packages/react-interactions/accessibility/src/__tests__/FocusTable-test.internal.js
deleted
-573
@@ -1,573 +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
-import {createEventTarget} from 'dom-event-testing-library';
11
-import {emulateBrowserTab} from '../shared/emulateBrowserTab';
12
-
13
-let React;
14
-let ReactFeatureFlags;
15
-let createFocusTable;
16
-let tabbableScopeQuery;
17
-
18
-describe('FocusTable', () => {
19
- beforeEach(() => {
20
- jest.resetModules();
21
- ReactFeatureFlags = require('shared/ReactFeatureFlags');
22
- ReactFeatureFlags.enableScopeAPI = true;
23
- ReactFeatureFlags.enableDeprecatedFlareAPI = true;
24
- createFocusTable = require('../FocusTable').createFocusTable;
25
- tabbableScopeQuery = require('../TabbableScopeQuery').default;
26
- React = require('react');
27
- });
28
-
29
- describe('ReactDOM', () => {
30
- let ReactDOM;
31
- let container;
32
-
33
- beforeEach(() => {
34
- ReactDOM = require('react-dom');
35
- container = document.createElement('div');
36
- document.body.appendChild(container);
37
- });
38
-
39
- afterEach(() => {
40
- document.body.removeChild(container);
41
- container = null;
42
- });
43
-
44
- function createFocusTableComponent() {
45
- const [FocusTable, FocusTableRow, FocusTableCell] = createFocusTable(
46
- tabbableScopeQuery,
47
- );
48
-
49
- return ({onKeyboardOut, wrapX, wrapY, allowModifiers}) => (
50
- <FocusTable
51
- onKeyboardOut={onKeyboardOut}
52
- wrapX={wrapX}
53
- wrapY={wrapY}
54
- allowModifiers={allowModifiers}>
55
- <table>
56
- <tbody>
57
- <FocusTableRow>
58
- <tr>
59
- <FocusTableCell>
60
- <td>
61
- <button>A1</button>
62
- </td>
63
- </FocusTableCell>
64
- <FocusTableCell>
65
- <td>
66
- <button>A2</button>
67
- </td>
68
- </FocusTableCell>
69
- <FocusTableCell>
70
- <td>
71
- <button>A3</button>
72
- </td>
73
- </FocusTableCell>
74
- </tr>
75
- </FocusTableRow>
76
- <FocusTableRow>
77
- <tr>
78
- <FocusTableCell>
79
- <td>
80
- <button>B1</button>
81
- </td>
82
- </FocusTableCell>
83
- <FocusTableCell>
84
- <td>
85
- <button>B2</button>
86
- </td>
87
- </FocusTableCell>
88
- <FocusTableCell>
89
- <td>
90
- <button>B3</button>
91
- </td>
92
- </FocusTableCell>
93
- </tr>
94
- </FocusTableRow>
95
- <FocusTableRow>
96
- <tr>
97
- <FocusTableCell>
98
- <td>
99
- <button>C1</button>
100
- </td>
101
- </FocusTableCell>
102
- <FocusTableCell>
103
- <td>
104
- <button>C2</button>
105
- </td>
106
- </FocusTableCell>
107
- <FocusTableCell>
108
- <td>
109
- <button>C3</button>
110
- </td>
111
- </FocusTableCell>
112
- </tr>
113
- </FocusTableRow>
114
- </tbody>
115
- </table>
116
- </FocusTable>
117
- );
118
- }
119
-
120
- it('handles keyboard arrow operations with allowModifiers', () => {
121
- const Test = createFocusTableComponent();
122
-
123
- ReactDOM.render(<Test allowModifiers={true} />, container);
124
- const buttons = document.querySelectorAll('button');
125
- const a1 = createEventTarget(buttons[0]);
126
- a1.focus();
127
- a1.keydown({
128
- key: 'ArrowRight',
129
- altKey: true,
130
- });
131
- expect(document.activeElement.textContent).toBe('A2');
132
- });
133
-
134
- it('handles keyboard arrow operations', () => {
135
- const Test = createFocusTableComponent();
136
-
137
- ReactDOM.render(<Test />, container);
138
- const buttons = document.querySelectorAll('button');
139
- const a1 = createEventTarget(buttons[0]);
140
- a1.focus();
141
- a1.keydown({
142
- key: 'ArrowRight',
143
- });
144
- expect(document.activeElement.textContent).toBe('A2');
145
-
146
- const a2 = createEventTarget(document.activeElement);
147
- a2.keydown({
148
- key: 'ArrowDown',
149
- });
150
- expect(document.activeElement.textContent).toBe('B2');
151
-
152
- let b2 = createEventTarget(document.activeElement);
153
- b2.keydown({
154
- key: 'ArrowLeft',
155
- });
156
- expect(document.activeElement.textContent).toBe('B1');
157
-
158
- const b1 = createEventTarget(document.activeElement);
159
- b1.keydown({
160
- key: 'ArrowDown',
161
- });
162
- expect(document.activeElement.textContent).toBe('C1');
163
-
164
- const c1 = createEventTarget(document.activeElement);
165
- c1.keydown({
166
- key: 'ArrowDown',
167
- });
168
- expect(document.activeElement.textContent).toBe('C1');
169
- c1.keydown({
170
- key: 'ArrowUp',
171
- });
172
- expect(document.activeElement.textContent).toBe('B1');
173
- // Should be a no-op due to modifier
174
- b2 = createEventTarget(document.activeElement);
175
- b2.keydown({
176
- key: 'ArrowUp',
177
- altKey: true,
178
- });
179
- b2 = createEventTarget(document.activeElement);
180
- expect(document.activeElement.textContent).toBe('B1');
181
- });
182
-
183
- it('handles keyboard arrow operations between nested tables', () => {
184
- const leftSidebarRef = React.createRef();
185
- const [
186
- MainFocusTable,
187
- MainFocusTableRow,
188
- MainFocusTableCell,
189
- ] = createFocusTable(tabbableScopeQuery);
190
- const SubFocusTable = createFocusTableComponent();
191
- const onKeyboardOut = jest.fn((direction, event) =>
192
- event.continuePropagation(),
193
- );
194
-
195
- function Test() {
196
- return (
197
- <MainFocusTable>
198
- <MainFocusTableRow>
199
- <div>
200
- <h1>Title</h1>
201
- <aside ref={leftSidebarRef}>
202
- <h2>Left Sidebar</h2>
203
- <MainFocusTableCell>
204
- <SubFocusTable onKeyboardOut={onKeyboardOut} />
205
- </MainFocusTableCell>
206
- </aside>
207
- <section>
208
- <h2>Content</h2>
209
- <MainFocusTableCell>
210
- <SubFocusTable onKeyboardOut={onKeyboardOut} />
211
- </MainFocusTableCell>
212
- </section>
213
- <aside>
214
- <h2>Right Sidebar</h2>
215
- <MainFocusTableCell>
216
- <SubFocusTable onKeyboardOut={onKeyboardOut} />
217
- </MainFocusTableCell>
218
- </aside>
219
- </div>
220
- </MainFocusTableRow>
221
- </MainFocusTable>
222
- );
223
- }
224
-
225
- ReactDOM.render(<Test />, container);
226
- const buttons = document.querySelectorAll('button');
227
- let a1 = createEventTarget(buttons[0]);
228
- a1.focus();
229
- a1.keydown({
230
- key: 'ArrowRight',
231
- });
232
- expect(document.activeElement.textContent).toBe('A2');
233
-
234
- let a2 = createEventTarget(document.activeElement);
235
- a2.keydown({
236
- key: 'ArrowRight',
237
- });
238
- expect(document.activeElement.textContent).toBe('A3');
239
-
240
- let a3 = createEventTarget(document.activeElement);
241
- a3.keydown({
242
- key: 'ArrowRight',
243
- });
244
- expect(onKeyboardOut).toHaveBeenCalledTimes(1);
245
- expect(document.activeElement.textContent).toBe('A1');
246
-
247
- a1 = createEventTarget(document.activeElement);
248
- a1.keydown({
249
- key: 'ArrowRight',
250
- });
251
- expect(document.activeElement.textContent).toBe('A2');
252
-
253
- a2 = createEventTarget(document.activeElement);
254
- a2.keydown({
255
- key: 'ArrowRight',
256
- });
257
- expect(document.activeElement.textContent).toBe('A3');
258
-
259
- a3 = createEventTarget(document.activeElement);
260
- a3.keydown({
261
- key: 'ArrowRight',
262
- });
263
- expect(onKeyboardOut).toHaveBeenCalledTimes(2);
264
- expect(document.activeElement.textContent).toBe('A1');
265
-
266
- a1 = createEventTarget(document.activeElement);
267
- a1.keydown({
268
- key: 'ArrowRight',
269
- });
270
- expect(document.activeElement.textContent).toBe('A2');
271
-
272
- a2 = createEventTarget(document.activeElement);
273
- a2.keydown({
274
- key: 'ArrowRight',
275
- });
276
- expect(document.activeElement.textContent).toBe('A3');
277
-
278
- a3 = createEventTarget(document.activeElement);
279
- a3.keydown({
280
- key: 'ArrowRight',
281
- });
282
- expect(document.activeElement.textContent).toBe('A3');
283
- });
284
-
285
- it('handles nested tables correctly', () => {
286
- const customScopeQuery = (type, props) => {
287
- return type === 'input';
288
- };
289
- const [FocusTable, FocusRow, FocusCell] = createFocusTable(
290
- customScopeQuery,
291
- );
292
- const firstRef = React.createRef();
293
-
294
- function Test() {
295
- return (
296
- <FocusTable>
297
- <div>
298
- <FocusRow>
299
- <FocusCell>
300
- <FocusTable>
301
- <FocusRow>
302
- <FocusCell>
303
- <input
304
- placeholder="Nested A1"
305
- tabIndex={0}
306
- ref={firstRef}
307
- />
308
- </FocusCell>
309
- <FocusCell>
310
- <input placeholder="Nested B1" tabIndex={0} />
311
- </FocusCell>
312
- </FocusRow>
313
- </FocusTable>
314
- </FocusCell>
315
- <FocusCell>
316
- <input placeholder="B1" tabIndex={-1} />
317
- </FocusCell>
318
- <FocusCell>
319
- <input placeholder="C1" tabIndex={-1} />
320
- </FocusCell>
321
- </FocusRow>
322
- </div>
323
- <div>
324
- <FocusRow>
325
- <FocusCell>
326
- <input placeholder="A2" tabIndex={-1} />
327
- </FocusCell>
328
- <FocusCell>
329
- <input placeholder="B2" tabIndex={-1} />
330
- </FocusCell>
331
- <FocusCell>
332
- <input placeholder="C1" tabIndex={-1} />
333
- </FocusCell>
334
- </FocusRow>
335
- </div>
336
- </FocusTable>
337
- );
338
- }
339
-
340
- ReactDOM.render(<Test />, container);
341
- firstRef.current.focus();
342
-
343
- const nestedA1 = createEventTarget(document.activeElement);
344
- nestedA1.keydown({
345
- key: 'ArrowRight',
346
- });
347
- expect(document.activeElement.placeholder).toBe('Nested B1');
348
-
349
- const nestedB1 = createEventTarget(document.activeElement);
350
- nestedB1.keydown({
351
- key: 'ArrowRight',
352
- });
353
- expect(document.activeElement.placeholder).toBe('B1');
354
- });
355
-
356
- it('handles keyboard arrow operations with X wrapping enabled', () => {
357
- const Test = createFocusTableComponent();
358
-
359
- ReactDOM.render(<Test wrapX={true} />, container);
360
- const buttons = document.querySelectorAll('button');
361
- let a1 = createEventTarget(buttons[0]);
362
- a1.focus();
363
- a1.keydown({
364
- key: 'ArrowRight',
365
- });
366
- expect(document.activeElement.textContent).toBe('A2');
367
-
368
- const a2 = createEventTarget(document.activeElement);
369
- a2.keydown({
370
- key: 'ArrowRight',
371
- });
372
- expect(document.activeElement.textContent).toBe('A3');
373
-
374
- const a3 = createEventTarget(document.activeElement);
375
- a3.keydown({
376
- key: 'ArrowRight',
377
- });
378
- expect(document.activeElement.textContent).toBe('A1');
379
-
380
- a1 = createEventTarget(document.activeElement);
381
- a1.keydown({
382
- key: 'ArrowLeft',
383
- });
384
- expect(document.activeElement.textContent).toBe('A3');
385
- });
386
-
387
- it('handles keyboard arrow operations with Y wrapping enabled', () => {
388
- const Test = createFocusTableComponent();
389
-
390
- ReactDOM.render(<Test wrapY={true} />, container);
391
- const buttons = document.querySelectorAll('button');
392
- let a1 = createEventTarget(buttons[0]);
393
- a1.focus();
394
- a1.keydown({
395
- key: 'ArrowDown',
396
- });
397
- expect(document.activeElement.textContent).toBe('B1');
398
-
399
- const a2 = createEventTarget(document.activeElement);
400
- a2.keydown({
401
- key: 'ArrowDown',
402
- });
403
- expect(document.activeElement.textContent).toBe('C1');
404
-
405
- const a3 = createEventTarget(document.activeElement);
406
- a3.keydown({
407
- key: 'ArrowDown',
408
- });
409
- expect(document.activeElement.textContent).toBe('A1');
410
-
411
- a1 = createEventTarget(document.activeElement);
412
- a1.keydown({
413
- key: 'ArrowUp',
414
- });
415
- expect(document.activeElement.textContent).toBe('C1');
416
- });
417
-
418
- it('handles keyboard arrow operations mixed with tabbing', () => {
419
- const [FocusTable, FocusRow, FocusCell] = createFocusTable(
420
- tabbableScopeQuery,
421
- );
422
- const beforeRef = React.createRef();
423
- const afterRef = React.createRef();
424
-
425
- function Test() {
426
- return (
427
- <>
428
- <input placeholder="Before" ref={beforeRef} />
429
- <FocusTable tabScopeQuery={tabbableScopeQuery}>
430
- <div>
431
- <FocusRow>
432
- <FocusCell>
433
- <input placeholder="A1" />
434
- </FocusCell>
435
- <FocusCell>
436
- <input placeholder="B1" />
437
- </FocusCell>
438
- <FocusCell>
439
- <input placeholder="C1" />
440
- </FocusCell>
441
- </FocusRow>
442
- </div>
443
- <div>
444
- <FocusRow>
445
- <FocusCell>
446
- <input placeholder="A2" />
447
- </FocusCell>
448
- <FocusCell>
449
- <input placeholder="B2" />
450
- </FocusCell>
451
- <FocusCell>
452
- <input placeholder="C1" />
453
- </FocusCell>
454
- </FocusRow>
455
- </div>
456
- </FocusTable>
457
- <input placeholder="After" ref={afterRef} />
458
- </>
459
- );
460
- }
461
-
462
- ReactDOM.render(<Test />, container);
463
- beforeRef.current.focus();
464
-
465
- expect(document.activeElement.placeholder).toBe('Before');
466
- emulateBrowserTab();
467
- expect(document.activeElement.placeholder).toBe('A1');
468
- emulateBrowserTab();
469
- expect(document.activeElement.placeholder).toBe('After');
470
- emulateBrowserTab(true);
471
- expect(document.activeElement.placeholder).toBe('A1');
472
- const a1 = createEventTarget(document.activeElement);
473
- a1.keydown({
474
- key: 'ArrowRight',
475
- });
476
- expect(document.activeElement.placeholder).toBe('B1');
477
- emulateBrowserTab();
478
- expect(document.activeElement.placeholder).toBe('After');
479
- emulateBrowserTab(true);
480
- expect(document.activeElement.placeholder).toBe('B1');
481
- });
482
-
483
- it('handles keyboard arrow operations with colSpan', () => {
484
- const firstRef = React.createRef();
485
- const [FocusTable, FocusRow, FocusCell] = createFocusTable(
486
- tabbableScopeQuery,
487
- );
488
-
489
- function Test() {
490
- return (
491
- <>
492
- <FocusTable tabScopeQuery={tabbableScopeQuery}>
493
- <div>
494
- <FocusRow>
495
- <FocusCell>
496
- <input placeholder="A1" ref={firstRef} />
497
- </FocusCell>
498
- <FocusCell colSpan={2}>
499
- <input placeholder="B1" />
500
- </FocusCell>
501
- <FocusCell>
502
- <input placeholder="C1" />
503
- </FocusCell>
504
- </FocusRow>
505
- </div>
506
- <div>
507
- <FocusRow>
508
- <FocusCell>
509
- <input placeholder="A2" />
510
- </FocusCell>
511
- <FocusCell>
512
- <input placeholder="B2" />
513
- </FocusCell>
514
- <FocusCell>
515
- <input placeholder="C2" />
516
- </FocusCell>
517
- <FocusCell>
518
- <input placeholder="D2" />
519
- </FocusCell>
520
- </FocusRow>
521
- </div>
522
- </FocusTable>
523
- </>
524
- );
525
- }
526
-
527
- ReactDOM.render(<Test />, container);
528
- firstRef.current.focus();
529
-
530
- expect(document.activeElement.placeholder).toBe('A1');
531
- const a1 = createEventTarget(document.activeElement);
532
- a1.keydown({
533
- key: 'ArrowRight',
534
- });
535
- expect(document.activeElement.placeholder).toBe('B1');
536
- let b1 = createEventTarget(document.activeElement);
537
- b1.keydown({
538
- key: 'ArrowRight',
539
- });
540
- expect(document.activeElement.placeholder).toBe('C1');
541
- let c1 = createEventTarget(document.activeElement);
542
- c1.keydown({
543
- key: 'ArrowDown',
544
- });
545
- expect(document.activeElement.placeholder).toBe('D2');
546
- let d2 = createEventTarget(document.activeElement);
547
- d2.keydown({
548
- key: 'ArrowUp',
549
- });
550
- expect(document.activeElement.placeholder).toBe('C1');
551
- c1 = createEventTarget(document.activeElement);
552
- c1.keydown({
553
- key: 'ArrowLeft',
554
- });
555
- expect(document.activeElement.placeholder).toBe('B1');
556
- b1 = createEventTarget(document.activeElement);
557
- b1.keydown({
558
- key: 'ArrowDown',
559
- });
560
- expect(document.activeElement.placeholder).toBe('B2');
561
- const b2 = createEventTarget(document.activeElement);
562
- b2.keydown({
563
- key: 'ArrowRight',
564
- });
565
- expect(document.activeElement.placeholder).toBe('C2');
566
- const c2 = createEventTarget(document.activeElement);
567
- c2.keydown({
568
- key: 'ArrowUp',
569
- });
570
- expect(document.activeElement.placeholder).toBe('B1');
571
- });
572
- });
573
-});
packages/react-interactions/accessibility/src/__tests__/TabbableScopeQuery-test.internal.js
deleted
-75
@@ -1,75 +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
-let React;
11
-let ReactFeatureFlags;
12
-let tabbableScopeQuery;
13
-
14
-describe('TabbableScopeQuery', () => {
15
- beforeEach(() => {
16
- jest.resetModules();
17
- ReactFeatureFlags = require('shared/ReactFeatureFlags');
18
- ReactFeatureFlags.enableScopeAPI = true;
19
- tabbableScopeQuery = require('../TabbableScopeQuery').default;
20
- React = require('react');
21
- });
22
-
23
- describe('ReactDOM', () => {
24
- let ReactDOM;
25
- let container;
26
-
27
- beforeEach(() => {
28
- ReactDOM = require('react-dom');
29
- container = document.createElement('div');
30
- document.body.appendChild(container);
31
- });
32
-
33
- afterEach(() => {
34
- document.body.removeChild(container);
35
- container = null;
36
- });
37
-
38
- it('DO_NOT_USE_queryAllNodes() works as intended', () => {
39
- const scopeRef = React.createRef();
40
- const nodeRefA = React.createRef();
41
- const nodeRefB = React.createRef();
42
- const nodeRefC = React.createRef();
43
- const nodeRefD = React.createRef();
44
-
45
- const TestScope = React.unstable_createScope();
46
-
47
- function Test() {
48
- return (
49
- <TestScope ref={scopeRef}>
50
- <input ref={nodeRefA} />
51
- <textarea ref={nodeRefB} />
52
- <div tabIndex={0} ref={nodeRefC}>
53
- <input tabIndex={-1} />
54
- <div tabIndex={0} ref={nodeRefD} />
55
- </div>
56
- <input disabled={true} />
57
- <div tabIndex={-1} />
58
- </TestScope>
59
- );
60
- }
61
-
62
- ReactDOM.render(<Test />, container);
63
- let nodes = scopeRef.current.DO_NOT_USE_queryAllNodes(tabbableScopeQuery);
64
- expect(nodes).toEqual([
65
- nodeRefA.current,
66
- nodeRefB.current,
67
- nodeRefC.current,
68
- nodeRefD.current,
69
- ]);
70
- expect(nodes.length).toBe(4);
71
- ReactDOM.render(null, container);
72
- expect(scopeRef.current).toBe(null);
73
- });
74
- });
75
-});
packages/react-interactions/accessibility/src/shared/emulateBrowserTab.js
deleted
-38
@@ -1,38 +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
-import {createEventTarget} from 'dom-event-testing-library';
13
-
14
-// This function is used by the a11y modules for testing
15
-export function emulateBrowserTab(backwards: boolean): void {
16
- const activeElement = document.activeElement;
17
- const focusedElem = createEventTarget(activeElement);
18
- let defaultPrevented = false;
19
- focusedElem.keydown({
20
- key: 'Tab',
21
- shiftKey: backwards,
22
- preventDefault() {
23
- defaultPrevented = true;
24
- },
25
- });
26
- if (!defaultPrevented) {
27
- // This is not a full spec compliant version, but should be suffice for this test
28
- const focusableElems = Array.from(
29
- document.querySelectorAll(
30
- 'input, button, select, textarea, a[href], [tabindex], [contenteditable], iframe, object, embed',
31
- ),
32
- ).filter(elem => elem.tabIndex > -1 && !elem.disabled);
33
- const idx = focusableElems.indexOf(activeElement);
34
- if (idx !== -1) {
35
- focusableElems[backwards ? idx - 1 : idx + 1].focus();
36
- }
37
- }
38
-}
packages/react-interactions/accessibility/src/shared/getTabbableNodes.js
deleted
-40
@@ -1,40 +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
-import type {ReactScopeMethods} from 'shared/ReactTypes';
11
-
12
-export default function getTabbableNodes(
13
- scopeQuery: (type: string | Object, props: Object) => boolean,
14
- scope: ReactScopeMethods,
15
-): [
16
- null | Array<HTMLElement>,
17
- null | HTMLElement,
18
- null | HTMLElement,
19
- number,
20
- null | HTMLElement,
21
-] {
22
- const tabbableNodes = scope.DO_NOT_USE_queryAllNodes(scopeQuery);
23
- if (tabbableNodes === null || tabbableNodes.length === 0) {
24
- return [null, null, null, 0, null];
25
- }
26
- const firstTabbableElem = tabbableNodes[0];
27
- const lastTabbableElem = tabbableNodes[tabbableNodes.length - 1];
28
- const currentIndex = tabbableNodes.indexOf(document.activeElement);
29
- let focusedElement = null;
30
- if (currentIndex !== -1) {
31
- focusedElement = tabbableNodes[currentIndex];
32
- }
33
- return [
34
- tabbableNodes,
35
- firstTabbableElem,
36
- lastTabbableElem,
37
- currentIndex,
38
- focusedElement,
39
- ];
40
-}
packages/react-interactions/accessibility/src/shared/setElementCanTab.js
deleted
-50
@@ -1,50 +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
-const tabIndexDesc = Object.getOwnPropertyDescriptor(
11
- HTMLElement.prototype,
12
- 'tabIndex',
13
-);
14
-const tabIndexSetter = (tabIndexDesc: any).set;
15
-
16
-export default function setElementCanTab(
17
- elem: HTMLElement,
18
- canTab: boolean,
19
-): void {
20
- let tabIndexState = (elem: any)._tabIndexState;
21
- if (!tabIndexState) {
22
- tabIndexState = {
23
- value: elem.tabIndex,
24
- canTab,
25
- };
26
- (elem: any)._tabIndexState = tabIndexState;
27
- if (!canTab) {
28
- elem.tabIndex = -1;
29
- }
30
- // We track the tabIndex value so we can restore the correct
31
- // tabIndex after we're done with it.
32
- // $FlowFixMe: Flow comoplains that we are missing value?
33
- Object.defineProperty(elem, 'tabIndex', {
34
- enumerable: false,
35
- configurable: true,
36
- get() {
37
- return tabIndexState.canTab ? tabIndexState.value : -1;
38
- },
39
- set(val) {
40
- if (tabIndexState.canTab) {
41
- tabIndexSetter.call(elem, val);
42
- }
43
- tabIndexState.value = val;
44
- },
45
- });
46
- } else if (tabIndexState.canTab !== canTab) {
47
- tabIndexSetter.call(elem, canTab ? tabIndexState.value : -1);
48
- tabIndexState.canTab = canTab;
49
- }
50
-}
packages/react-interactions/accessibility/tabbable-scope-query.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/TabbableScopeQuery');
packages/react-reconciler/src/ReactFiberScope.js
+7
-89
@@ -13,6 +13,7 @@ import type {
13
ReactScopeInstance,
14
ReactScopeMethods,
15
ReactContext,
16
+ ReactScopeQuery,
17
} from 'shared/ReactTypes';
18
19
import {getPublicInstance, getInstanceFromNode} from './ReactFiberHostConfig';
@@ -37,7 +38,7 @@ const emptyObject = {};
38
39
function collectScopedNodes(
40
node: Fiber,
40
- fn: (type: string | Object, props: Object, instance: Object) => boolean,
41
+ fn: ReactScopeQuery,
42
scopedNodes: Array<any>,
43
): void {
44
if (enableScopeAPI) {
@@ -64,7 +65,7 @@ function collectScopedNodes(
65
66
function collectFirstScopedNode(
67
node: Fiber,
67
- fn: (type: string | Object, props: Object, instance: Object) => boolean,
68
+ fn: ReactScopeQuery,
69
): null | Object {
70
if (enableScopeAPI) {
71
if (node.tag === HostComponent) {
@@ -88,7 +89,7 @@ function collectFirstScopedNode(
89
90
function collectScopedNodesFromChildren(
91
startingChild: Fiber,
91
- fn: (type: string | Object, props: Object, instance: Object) => boolean,
92
+ fn: ReactScopeQuery,
93
scopedNodes: Array<any>,
94
): void {
95
let child = startingChild;
@@ -100,7 +101,7 @@ function collectScopedNodesFromChildren(
101
102
function collectFirstScopedNodeFromChildren(
103
startingChild: Fiber,
103
- fn: (type: string | Object, props: Object, instance: Object) => boolean,
104
+ fn: ReactScopeQuery,
105
): Object | null {
106
let child = startingChild;
107
while (child !== null) {
@@ -113,37 +114,6 @@ function collectFirstScopedNodeFromChildren(
114
return null;
115
}
116
116
-function collectNearestScopeMethods(
117
- node: Fiber,
118
- scope: ReactScope,
119
- childrenScopes: Array<ReactScopeMethods>,
120
-): void {
121
- if (isValidScopeNode(node, scope)) {
122
- childrenScopes.push(node.stateNode.methods);
123
- } else {
124
- let child = node.child;
125
-
126
- if (isFiberSuspenseAndTimedOut(node)) {
127
- child = getSuspenseFallbackChild(node);
128
- }
129
- if (child !== null) {
130
- collectNearestChildScopeMethods(child, scope, childrenScopes);
131
- }
132
- }
133
-}
134
-
135
-function collectNearestChildScopeMethods(
136
- startingChild: Fiber | null,
137
- scope: ReactScope,
138
- childrenScopes: Array<ReactScopeMethods>,
139
-): void {
140
- let child = startingChild;
141
- while (child !== null) {
142
- collectNearestScopeMethods(child, scope, childrenScopes);
143
- child = child.sibling;
144
- }
145
-}
146
-
117
function collectNearestContextValues<T>(
118
node: Fiber,
119
context: ReactContext<T>,
@@ -176,62 +146,12 @@ function collectNearestChildContextValues<T>(
146
}
147
}
148
179
-function isValidScopeNode(node, scope) {
180
- return (
181
- node.tag === ScopeComponent &&
182
- node.type === scope &&
183
- node.stateNode !== null
184
- );
185
-}
186
-
149
export function createScopeMethods(
150
scope: ReactScope,
151
instance: ReactScopeInstance,
152
): ReactScopeMethods {
153
return {
192
- DO_NOT_USE_getChildren(): null | Array<ReactScopeMethods> {
193
- const currentFiber = ((instance.fiber: any): Fiber);
194
- const child = currentFiber.child;
195
- const childrenScopes = [];
196
- if (child !== null) {
197
- collectNearestChildScopeMethods(child, scope, childrenScopes);
198
- }
199
- return childrenScopes.length === 0 ? null : childrenScopes;
200
- },
201
- DO_NOT_USE_getChildrenFromRoot(): null | Array<ReactScopeMethods> {
202
- const currentFiber = ((instance.fiber: any): Fiber);
203
- let node = currentFiber;
204
- while (node !== null) {
205
- const parent = node.return;
206
- if (parent === null) {
207
- break;
208
- }
209
- node = parent;
210
- if (node.tag === ScopeComponent && node.type === scope) {
211
- break;
212
- }
213
- }
214
- const childrenScopes = [];
215
- collectNearestChildScopeMethods(node.child, scope, childrenScopes);
216
- return childrenScopes.length === 0 ? null : childrenScopes;
217
- },
218
- DO_NOT_USE_getParent(): null | ReactScopeMethods {
219
- let node = ((instance.fiber: any): Fiber).return;
220
- while (node !== null) {
221
- if (node.tag === ScopeComponent && node.type === scope) {
222
- return node.stateNode.methods;
223
- }
224
- node = node.return;
225
- }
226
- return null;
227
- },
228
- DO_NOT_USE_getProps(): Object {
229
- const currentFiber = ((instance.fiber: any): Fiber);
230
- return currentFiber.memoizedProps;
231
- },
232
- DO_NOT_USE_queryAllNodes(
233
- fn: (type: string | Object, props: Object, instance: Object) => boolean,
234
- ): null | Array<Object> {
154
+ DO_NOT_USE_queryAllNodes(fn: ReactScopeQuery): null | Array<Object> {
155
const currentFiber = ((instance.fiber: any): Fiber);
156
const child = currentFiber.child;
157
const scopedNodes = [];
@@ -240,9 +160,7 @@ export function createScopeMethods(
160
}
161
return scopedNodes.length === 0 ? null : scopedNodes;
162
},
243
- DO_NOT_USE_queryFirstNode(
244
- fn: (type: string | Object, props: Object, instance: Object) => boolean,
245
- ): null | Object {
163
+ DO_NOT_USE_queryFirstNode(fn: ReactScopeQuery): null | Object {
164
const currentFiber = ((instance.fiber: any): Fiber);
165
const child = currentFiber.child;
166
if (child !== null) {
packages/react-reconciler/src/__tests__/ReactScope-test.internal.js
-208
@@ -201,106 +201,6 @@ describe('ReactScope', () => {
201
expect(scopeRef.current.containsNode(emRef.current)).toBe(false);
202
});
203
204
- it('mixed DO_NOT_USE_getParent() and queryAllNodes() works as intended', () => {
205
- const testScopeQuery = (type, props) => true;
206
- const TestScope = React.unstable_createScope();
207
- const TestScope2 = React.unstable_createScope();
208
- const refA = React.createRef();
209
- const refB = React.createRef();
210
- const refC = React.createRef();
211
- const refD = React.createRef();
212
- const spanA = React.createRef();
213
- const spanB = React.createRef();
214
- const divA = React.createRef();
215
- const divB = React.createRef();
216
-
217
- function Test() {
218
- return (
219
- <div>
220
- <TestScope ref={refA}>
221
- <span ref={spanA}>
222
- <TestScope2 ref={refB}>
223
- <div ref={divA}>
224
- <TestScope ref={refC}>
225
- <span ref={spanB}>
226
- <TestScope2 ref={refD}>
227
- <div ref={divB}>>Hello world</div>
228
- </TestScope2>
229
- </span>
230
- </TestScope>
231
- </div>
232
- </TestScope2>
233
- </span>
234
- </TestScope>
235
- </div>
236
- );
237
- }
238
-
239
- ReactDOM.render(<Test />, container);
240
- const dParent = refD.current.DO_NOT_USE_getParent();
241
- expect(dParent).not.toBe(null);
242
- expect(dParent.DO_NOT_USE_queryAllNodes(testScopeQuery)).toEqual([
243
- divA.current,
244
- spanB.current,
245
- divB.current,
246
- ]);
247
- const cParent = refC.current.DO_NOT_USE_getParent();
248
- expect(cParent).not.toBe(null);
249
- expect(cParent.DO_NOT_USE_queryAllNodes(testScopeQuery)).toEqual([
250
- spanA.current,
251
- divA.current,
252
- spanB.current,
253
- divB.current,
254
- ]);
255
- expect(refB.current.DO_NOT_USE_getParent()).toBe(null);
256
- expect(refA.current.DO_NOT_USE_getParent()).toBe(null);
257
- });
258
-
259
- it('DO_NOT_USE_getChildren() works as intended', () => {
260
- const TestScope = React.unstable_createScope();
261
- const TestScope2 = React.unstable_createScope();
262
- const refA = React.createRef();
263
- const refB = React.createRef();
264
- const refC = React.createRef();
265
- const refD = React.createRef();
266
- const spanA = React.createRef();
267
- const spanB = React.createRef();
268
- const divA = React.createRef();
269
- const divB = React.createRef();
270
-
271
- function Test() {
272
- return (
273
- <div>
274
- <TestScope ref={refA}>
275
- <span ref={spanA}>
276
- <TestScope2 ref={refB}>
277
- <div ref={divA}>
278
- <TestScope ref={refC}>
279
- <span ref={spanB}>
280
- <TestScope2 ref={refD}>
281
- <div ref={divB}>>Hello world</div>
282
- </TestScope2>
283
- </span>
284
- </TestScope>
285
- </div>
286
- </TestScope2>
287
- </span>
288
- </TestScope>
289
- </div>
290
- );
291
- }
292
-
293
- ReactDOM.render(<Test />, container);
294
- const dChildren = refD.current.DO_NOT_USE_getChildren();
295
- expect(dChildren).toBe(null);
296
- const cChildren = refC.current.DO_NOT_USE_getChildren();
297
- expect(cChildren).toBe(null);
298
- const bChildren = refB.current.DO_NOT_USE_getChildren();
299
- expect(bChildren).toEqual([refD.current]);
300
- const aChildren = refA.current.DO_NOT_USE_getChildren();
301
- expect(aChildren).toEqual([refC.current]);
302
- });
303
-
204
it('scopes support server-side rendering and hydration', () => {
205
const TestScope = React.unstable_createScope();
206
const ReactDOMServer = require('react-dom/server');
@@ -534,113 +434,5 @@ describe('ReactScope', () => {
434
renderer.update(<Test toggle={true} />);
435
expect(scopeRef.current.containsNode(emRef.current)).toBe(false);
436
});
537
-
538
- it('mixed DO_NOT_USE_getParent() and DO_NOT_USE_queryAllNodes() works as intended', () => {
539
- const testScopeQuery = (type, props) => true;
540
- const TestScope = React.unstable_createScope();
541
- const TestScope2 = React.unstable_createScope();
542
- const refA = React.createRef();
543
- const refB = React.createRef();
544
- const refC = React.createRef();
545
- const refD = React.createRef();
546
- const spanA = React.createRef();
547
- const spanB = React.createRef();
548
- const divA = React.createRef();
549
- const divB = React.createRef();
550
-
551
- function Test() {
552
- return (
553
- <div>
554
- <TestScope ref={refA}>
555
- <span ref={spanA}>
556
- <TestScope2 ref={refB}>
557
- <div ref={divA}>
558
- <TestScope ref={refC}>
559
- <span ref={spanB}>
560
- <TestScope2 ref={refD}>
561
- <div ref={divB}>>Hello world</div>
562
- </TestScope2>
563
- </span>
564
- </TestScope>
565
- </div>
566
- </TestScope2>
567
- </span>
568
- </TestScope>
569
- </div>
570
- );
571
- }
572
-
573
- ReactTestRenderer.create(<Test />, {
574
- createNodeMock: element => {
575
- return element;
576
- },
577
- });
578
- const dParent = refD.current.DO_NOT_USE_getParent();
579
- expect(dParent).not.toBe(null);
580
- expect(dParent.DO_NOT_USE_queryAllNodes(testScopeQuery)).toEqual([
581
- divA.current,
582
- spanB.current,
583
- divB.current,
584
- ]);
585
- const cParent = refC.current.DO_NOT_USE_getParent();
586
- expect(cParent).not.toBe(null);
587
- expect(cParent.DO_NOT_USE_queryAllNodes(testScopeQuery)).toEqual([
588
- spanA.current,
589
- divA.current,
590
- spanB.current,
591
- divB.current,
592
- ]);
593
- expect(refB.current.DO_NOT_USE_getParent()).toBe(null);
594
- expect(refA.current.DO_NOT_USE_getParent()).toBe(null);
595
- });
596
-
597
- it('DO_NOT_USE_getChildren() works as intended', () => {
598
- const TestScope = React.unstable_createScope();
599
- const TestScope2 = React.unstable_createScope();
600
- const refA = React.createRef();
601
- const refB = React.createRef();
602
- const refC = React.createRef();
603
- const refD = React.createRef();
604
- const spanA = React.createRef();
605
- const spanB = React.createRef();
606
- const divA = React.createRef();
607
- const divB = React.createRef();
608
-
609
- function Test() {
610
- return (
611
- <div>
612
- <TestScope ref={refA}>
613
- <span ref={spanA}>
614
- <TestScope2 ref={refB}>
615
- <div ref={divA}>
616
- <TestScope ref={refC}>
617
- <span ref={spanB}>
618
- <TestScope2 ref={refD}>
619
- <div ref={divB}>>Hello world</div>
620
- </TestScope2>
621
- </span>
622
- </TestScope>
623
- </div>
624
- </TestScope2>
625
- </span>
626
- </TestScope>
627
- </div>
628
- );
629
- }
630
-
631
- ReactTestRenderer.create(<Test />, {
632
- createNodeMock: element => {
633
- return element;
634
- },
635
- });
636
- const dChildren = refD.current.DO_NOT_USE_getChildren();
637
- expect(dChildren).toBe(null);
638
- const cChildren = refC.current.DO_NOT_USE_getChildren();
639
- expect(cChildren).toBe(null);
640
- const bChildren = refB.current.DO_NOT_USE_getChildren();
641
- expect(bChildren).toEqual([refD.current]);
642
- const aChildren = refA.current.DO_NOT_USE_getChildren();
643
- expect(aChildren).toEqual([refC.current]);
644
- });
437
});
438
});
packages/shared/ReactTypes.js
+8
-10
@@ -170,17 +170,15 @@ export type ReactScope = {|
170
$$typeof: Symbol | number,
171
|};
172
173
+export type ReactScopeQuery = (
174
+ type: string,
175
+ props: {[string]: mixed, ...},
176
+ instance: mixed,
177
+) => boolean;
178
+
179
export type ReactScopeMethods = {|
174
- DO_NOT_USE_getChildren(): null | Array<ReactScopeMethods>,
175
- DO_NOT_USE_getChildrenFromRoot(): null | Array<ReactScopeMethods>,
176
- DO_NOT_USE_getParent(): null | ReactScopeMethods,
177
- DO_NOT_USE_getProps(): Object,
178
- DO_NOT_USE_queryAllNodes(
179
- (type: string | Object, props: Object, instance: Object) => boolean,
180
- ): null | Array<Object>,
181
- DO_NOT_USE_queryFirstNode(
182
- (type: string | Object, props: Object, instance: Object) => boolean,
183
- ): null | Object,
180
+ DO_NOT_USE_queryAllNodes(ReactScopeQuery): null | Array<Object>,
181
+ DO_NOT_USE_queryFirstNode(ReactScopeQuery): null | Object,
182
containsNode(Object): boolean,
183
getChildContextValues: <T>(context: ReactContext<T>) => Array<T>,
184
|};