Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[react-interactions] Add more documentation for a11y components #16894

Merged
merged 2 commits into from Sep 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
60 changes: 60 additions & 0 deletions packages/react-interactions/accessibility/docs/FocusControl.md
@@ -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
@@ -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
@@ -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
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
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