Skip to content

Commit b71ab61

Browse files
authored
[react-interactions] Adds more experimental Scope API methods (#17042)
1 parent 5a71cbe commit b71ab61

File tree

17 files changed

+261
-37
lines changed

17 files changed

+261
-37
lines changed

packages/react-art/src/ReactARTHostConfig.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,3 +461,7 @@ export function updateFundamentalComponent(fundamentalInstance) {
461461
export function unmountFundamentalComponent(fundamentalInstance) {
462462
throw new Error('Not yet implemented.');
463463
}
464+
465+
export function getInstanceFromNode(node) {
466+
throw new Error('Not yet implemented.');
467+
}

packages/react-dom/src/client/ReactDOMHostConfig.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
* @flow
88
*/
99

10-
import {precacheFiberNode, updateFiberProps} from './ReactDOMComponentTree';
10+
import {
11+
precacheFiberNode,
12+
updateFiberProps,
13+
getClosestInstanceFromNode,
14+
} from './ReactDOMComponentTree';
1115
import {
1216
createElement,
1317
createTextNode,
@@ -976,3 +980,7 @@ export function unmountFundamentalComponent(
976980
}
977981
}
978982
}
983+
984+
export function getInstanceFromNode(node: HTMLElement): null | Object {
985+
return getClosestInstanceFromNode(node) || null;
986+
}

packages/react-interactions/accessibility/README.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ function MyComponent(props) {
4242
);
4343
}
4444

45-
// Using the ref, we can get the host nodes via getScopedNodes()
46-
const divs = divOnlyScope.current.getScopedNodes();
45+
// Using the ref, we can get the host nodes via getAllNodes()
46+
const divs = divOnlyScope.current.getAllNodes();
4747

4848
// [<div>DIV 1</div>, <div>DIV 2</div>, <div>DIV 3</div>]
4949
console.log(divs);
@@ -72,7 +72,17 @@ Returns the parent `ReactScopeInterface` of the scope node or `null` if none exi
7272

7373
Returns the current `props` object of the scope node.
7474

75-
### getScopedNodes: () => null | Array<HTMLElement>
75+
### getAllNodes: () => null | Array<HTMLElement>
7676

7777
Returns an array of all child host nodes that successfully match when queried using the
78-
query function passed to the scope. Returns `null` if there are no matching host nodes.
78+
query function passed to the scope. Returns `null` if there are no matching host nodes.
79+
80+
### getFirstNode: () => null | HTMLElement
81+
82+
Returns the first child host node that successfully matches when queried using the
83+
query function passed to the scope. Returns `null` if there is no matching host node.
84+
85+
### containsNode: (node: HTMLElement) => boolean
86+
87+
Returns `true` or `false` depending on if the given `HTMLElement` is a descendant
88+
of the scope's sub-tree.

packages/react-interactions/accessibility/docs/TabbableScope.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ function FocusableNodeCollector(props) {
1515
const scope = scopeRef.current;
1616

1717
if (scope) {
18-
const tabFocusableNodes = scope.getScopedNodes();
18+
const tabFocusableNodes = scope.getAllNodes();
1919
if (tabFocusableNodes && props.onFocusableNodes) {
2020
props.onFocusableNodes(tabFocusableNodes);
2121
}

packages/react-interactions/accessibility/src/FocusContain.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,14 @@ export default function FocusContain({
6666
useLayoutEffect(
6767
() => {
6868
const scope = scopeRef.current;
69-
if (scope !== null && disabled !== true) {
70-
const elems = scope.getScopedNodes();
71-
if (elems && elems.indexOf(document.activeElement) === -1) {
72-
elems[0].focus();
69+
if (
70+
scope !== null &&
71+
disabled !== true &&
72+
!scope.containsNode(document.activeElement)
73+
) {
74+
const fistElem = scope.getFirstNode();
75+
if (fistElem !== null) {
76+
fistElem.focus();
7377
}
7478
}
7579
},

packages/react-interactions/accessibility/src/FocusGroup.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ type FocusGroupProps = {|
3030
const {useRef} = React;
3131

3232
function focusGroupItem(cell: ReactScopeMethods, event: KeyboardEvent): void {
33-
const tabbableNodes = cell.getScopedNodes();
34-
if (tabbableNodes !== null && tabbableNodes.length > 0) {
35-
tabbableNodes[0].focus();
33+
const firstScopedNode = cell.getFirstNode();
34+
if (firstScopedNode !== null) {
35+
firstScopedNode.focus();
3636
event.preventDefault();
3737
}
3838
}
@@ -135,7 +135,7 @@ export function createFocusGroup(
135135
const tabScope = getGroupProps(currentItem).tabScopeRef.current;
136136
if (tabScope) {
137137
const activeNode = document.activeElement;
138-
const nodes = tabScope.getScopedNodes();
138+
const nodes = tabScope.getAllNodes();
139139
for (let i = 0; i < nodes.length; i++) {
140140
const node = nodes[i];
141141
if (node !== activeNode) {

packages/react-interactions/accessibility/src/FocusTable.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ type FocusTableProps = {|
3939
const {useRef} = React;
4040

4141
function focusScope(cell: ReactScopeMethods, event?: KeyboardEvent): void {
42-
const tabbableNodes = cell.getScopedNodes();
43-
if (tabbableNodes !== null && tabbableNodes.length > 0) {
44-
tabbableNodes[0].focus();
42+
const firstScopedNode = cell.getFirstNode();
43+
if (firstScopedNode !== null) {
44+
firstScopedNode.focus();
4545
if (event) {
4646
event.preventDefault();
4747
}
@@ -209,7 +209,7 @@ export function createFocusTable(
209209
const tabScope = getTableProps(currentCell).tabScopeRef.current;
210210
if (tabScope) {
211211
const activeNode = document.activeElement;
212-
const nodes = tabScope.getScopedNodes();
212+
const nodes = tabScope.getAllNodes();
213213
for (let i = 0; i < nodes.length; i++) {
214214
const node = nodes[i];
215215
if (node !== activeNode) {

packages/react-interactions/accessibility/src/__tests__/TabbableScope-test.internal.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ describe('TabbableScope', () => {
3535
container = null;
3636
});
3737

38-
it('getScopedNodes() works as intended', () => {
38+
it('getAllNodes() works as intended', () => {
3939
const scopeRef = React.createRef();
4040
const nodeRefA = React.createRef();
4141
const nodeRefB = React.createRef();
@@ -58,7 +58,7 @@ describe('TabbableScope', () => {
5858
}
5959

6060
ReactDOM.render(<Test />, container);
61-
let nodes = scopeRef.current.getScopedNodes();
61+
let nodes = scopeRef.current.getAllNodes();
6262
expect(nodes).toEqual([
6363
nodeRefA.current,
6464
nodeRefB.current,

packages/react-interactions/accessibility/src/shared/getTabbableNodes.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default function getTabbableNodes(
1818
number,
1919
null | HTMLElement,
2020
] {
21-
const tabbableNodes = scope.getScopedNodes();
21+
const tabbableNodes = scope.getAllNodes();
2222
if (tabbableNodes === null || tabbableNodes.length === 0) {
2323
return [null, null, null, 0, null];
2424
}

packages/react-native-renderer/src/ReactFabricHostConfig.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,3 +491,7 @@ export function unmountFundamentalComponent(fundamentalInstance) {
491491
export function cloneFundamentalInstance(fundamentalInstance) {
492492
throw new Error('Not yet implemented.');
493493
}
494+
495+
export function getInstanceFromNode(node) {
496+
throw new Error('Not yet implemented.');
497+
}

0 commit comments

Comments
 (0)