Skip to content

Commit

Permalink
[react-interactions] Add more documentation for a11y components (#16894)
Browse files Browse the repository at this point in the history
  • Loading branch information
trueadm committed Sep 25, 2019
1 parent a06d181 commit 7c3bd08
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 8 deletions.
60 changes: 60 additions & 0 deletions packages/react-interactions/accessibility/docs/FocusControl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# FocusControl

`FocusControl` is a module that exports a selection of helpful utility functions to be used
in conjunction with the `ref` from a React Scope, such as `TabbableScope`.
A ref from `FocusManager` can also be used instead.

## Example

```jsx
const {
focusFirst,
focusNext,
focusPrevious,
getNextScope,
getPreviousScope,
} = FocusControl;

function KeyboardFocusMover(props) {
const scopeRef = useRef(null);

useEffect(() => {
const scope = scopeRef.current;

if (scope) {
// Focus the first tabbable DOM node in my children
focusFirst(scope);
// Then focus the next chilkd
focusNext(scope);
}
});

return (
<TabbableScope ref={scopeRef}>
{props.children}
</TabbableScope>
);
}
```

## FocusControl API

### `focusFirst`

Focus the first node that matches the given scope.

### `focusNext`

Focus the next sequential node that matchs the given scope.

### `focusPrevious`

Focus the previous sequential node that matchs the given scope.

### `getNextScope`

Focus the first node that matches the next sibling scope from the given scope.

### `getPreviousScope`

Focus the first node that matches the previous sibling scope from the given scope.
39 changes: 39 additions & 0 deletions packages/react-interactions/accessibility/docs/FocusManager.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# FocusManager

`FocusManager` is a component that is designed to provide basic focus management
control. These are the various props that `FocusManager` accepts:

## Usage

```jsx
function MyDialog(props) {
return (
<FocusManager containFocus={true} autoFocus={true}>
<div>
<h2>{props.title}<h2>
<p>{props.text}</p>
<Button onPress={...}>Accept</Button>
<Button onPress={...}>Close</Button>
</div>
</FocusManager>
)
}
```

### `scope`
`FocusManager` accepts a custom `ReactScope`. If a custom one is not supplied, `FocusManager`
will default to using `TabbableScope`.

### `autoFocus`
When enabled, the first host node that matches the `FocusManager` scope will be focused
upon the `FocusManager` mounting.

### `restoreFocus`
When enabled, the previous host node that was focused as `FocusManager` is mounted,
has its focus restored upon `FocusManager` unmounting.

### `containFocus`
This contains the user focus to only that of `FocusManager`s sub-tree. Tabbing or
interacting with nodes outside the sub-tree will restore focus back into the `FocusManager`.
This is useful for modals, dialogs, dropdowns and other UI elements that require
a form of user-focus control that is similar to the `inert` property on the web.
35 changes: 35 additions & 0 deletions packages/react-interactions/accessibility/docs/TabbableScope.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# TabbableScope

`TabbableScope` is a custom scope implementation that can be used with
`FocusManager`, `FocusList`, `FocusTable` and `FocusControl` modules.

## Usage

```jsx
function FocusableNodeCollector(props) {
const scopeRef = useRef(null);

useEffect(() => {
const scope = scopeRef.current;

if (scope) {
const tabFocusableNodes = scope.getScopedNodes();
if (tabFocusableNodes && props.onFocusableNodes) {
props.onFocusableNodes(tabFocusableNodes);
}
}
});

return (
<TabbableScope ref={scopeRef}>
{props.children}
</TabbableScope>
);
}
```

## Implementation

`TabbableScope` uses the experimental `React.unstable_createScope` API. The query
function used for the scope is designed to collect DOM nodes that are tab focusable
to the browser. See the [implementation](../src/TabbableScope.js#L12-L33) here.
4 changes: 2 additions & 2 deletions packages/react-interactions/accessibility/src/FocusControl.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export function focusPrevious(
}
}

export function getNextController(
export function getNextScope(
scope: ReactScopeMethods,
): null | ReactScopeMethods {
const allScopes = scope.getChildrenFromRoot();
Expand All @@ -124,7 +124,7 @@ export function getNextController(
return allScopes[currentScopeIndex + 1];
}

export function getPreviousController(
export function getPreviousScope(
scope: ReactScopeMethods,
): null | ReactScopeMethods {
const allScopes = scope.getChildrenFromRoot();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -301,16 +301,12 @@ describe('FocusManager', () => {
FocusControl.focusPrevious(firstFocusController);
expect(document.activeElement).toBe(buttonRef.current);

const nextController = FocusControl.getNextController(
firstFocusController,
);
const nextController = FocusControl.getNextScope(firstFocusController);
expect(nextController).toBe(secondFocusController);
FocusControl.focusFirst(nextController);
expect(document.activeElement).toBe(divRef.current);

const previousController = FocusControl.getPreviousController(
nextController,
);
const previousController = FocusControl.getPreviousScope(nextController);
expect(previousController).toBe(firstFocusController);
FocusControl.focusFirst(previousController);
expect(document.activeElement).toBe(buttonRef.current);
Expand Down

0 comments on commit 7c3bd08

Please sign in to comment.