Skip to content

Commit

Permalink
chore(collection)!: Move initial cursor to roving focus (#2205)
Browse files Browse the repository at this point in the history
Fixes: #1234 

Not all collections need an initial cursor position to function. This functionality is more suitable for roving tabindex and doesn't work as well for `aria-activedescendant`. This change moves the functionality out of the cursor model and into `useListItemRovingFocus`

[category:Components]

### BREAKING CHANGES
If you use `useListModel` to create AND need the initially registered item to be the cursor position AND do not use `useListItemRovingFocus`, you'll have to add logic to set the cursor to that first item in the list. If you use both `useListModel` and `useListItemRovingFocus`, there is not change in behavior.
  • Loading branch information
NicholasBoll committed May 8, 2023
1 parent 5fd436a commit 9304d13
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 15 deletions.
17 changes: 17 additions & 0 deletions modules/docs/mdx/9.0-UPGRADE-GUIDE.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,23 @@ knows where it needs to go, but the identifier may not be loaded yet. The mechan
is private and should not breaking anything. If you created a custom navigation manager, the
signature has been changed.

The `useListModel` modelHook no longers sets the initial `cursorId` to the first item if no
`initialCursorId` config option is set. This functionality has been moved to the
`useListItemRovingFocus` elemProps hook. If you use `useListItemRovingFocus`, the behavior is
unchanged. If you need the first item to be set as the `cursorId` and you do not use
`useListItemRovingFocus`, you will have to add this functionality back to your collection logic.

The logic to set the `cursorId` to the first item should go into an item elemProps hook that
contains the following:

```ts
React.useEffect(() => {
if (!model.state.cursorId && model.state.items.length) {
model.events.goTo({id: model.state.items[0].id});
}
}, [model.state.cursorId, model.state.items, model.events]);
```

## Utility Updates

### useTheme and getTheme
Expand Down
15 changes: 0 additions & 15 deletions modules/react/collection/lib/useCursorListModel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -322,9 +322,6 @@ export const useCursorListModel = createModelHook({
const pageSizeRef = React.useRef(config.pageSize);
const columnCount = config.columnCount || 0;
const list = useBaseListModel(config);
const initialCurrentRef = React.useRef(
config.initialCursorId || (config.items?.length ? list.state.items![0].id : '')
);
const navigation = config.navigation;
const cursorIndexRef = React.useRef(-1);
const setCursor = (index: number) => {
Expand Down Expand Up @@ -358,18 +355,6 @@ export const useCursorListModel = createModelHook({

const events = {
...list.events,
/**
* Register an item to the list. Takes in an identifier, a React.Ref and an optional index. This
* should be called on component mount
*/
registerItem(data: Parameters<typeof list.events.registerItem>[0]) {
// point the cursor at the first item
if (!initialCurrentRef.current) {
initialCurrentRef.current = list.getId(data.item);
setCursorId(initialCurrentRef.current);
}
list.events.registerItem(data);
},
/** Directly sets the cursor to the list item by its identifier. */
goTo(data: {id: string}) {
const index = state.items.findIndex(item => item.id === data.id);
Expand Down
7 changes: 7 additions & 0 deletions modules/react/collection/lib/useListItemRovingFocus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ export const useListItemRovingFocus = createElemPropsHook(useCursorListModel)(
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [model.state.cursorId]);

// Roving focus must always have a focus stop to function correctly
React.useEffect(() => {
if (!model.state.cursorId && model.state.items.length) {
model.events.goTo({id: model.state.items[0].id});
}
}, [model.state.cursorId, model.state.items, model.events]);

return {
onKeyDown(event: React.KeyboardEvent) {
const handled = keyboardEventToCursorEvents(event, model, isRTL);
Expand Down

0 comments on commit 9304d13

Please sign in to comment.