Skip to content

[RovingFocusGroup] Refactor internals to take into account hidden items #618

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

Merged
merged 23 commits into from
May 3, 2021

Conversation

benoitgrelard
Copy link
Contributor

@benoitgrelard benoitgrelard commented Apr 23, 2021

Closes #581


Note: Better view with whitespace turned off


This PR primarily refactors the internals of RovingFocusGroup so we can take into account the fact that hidden items don't get focused by the browser.

The crux of the implementation here is that we've introduced a wrapper as part of that API which will act as a proxy to send focus programatically to the correct place. We have a reference to all items in the group and so, we can programatically and progressively attempt to focus the "first" item, and eventually land on the correct one (the first visible one) without having to resort to calculating computed styles to know if an item is indeed hidden.

Once that was done, all of the dependent packages needed updated accordingly.

This has made the overall implementation(s) more declarative, and more the React way.

Note: There are now technically new changes to document for some packages because they now get the underlying props from RovingFocusGroup

  • RadioGroup now gets new orientation, dir and loop props
  • ToggleGroup now gets new orientation, dir and loop props

cc @andy-hook, may be good for you to take a quick look through this to see if there's any impact on what you've been working on.

Comment on lines +271 to 264
/**
* Wraps an array around itself at a given start index
* Example: `wrapArray(['a', 'b', 'c', 'd'], 2) === ['c', 'd', 'a', 'b']`
*/
function wrapArray<T>(array: T[], startIndex: number) {
return array.map((_, index) => array[(startIndex + index) % array.length]);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is duplicated from menuTypeahead.tsx. Let me know if you think we should do something about it.

@benoitgrelard benoitgrelard marked this pull request as ready for review April 26, 2021 08:28
@benoitgrelard benoitgrelard requested a review from jjenzz as a code owner April 26, 2021 08:28
Copy link
Contributor

@jjenzz jjenzz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super excited about this 🎉

I noticed one little thing when testing that felt a bit odd to me. If the items have been interacted with, and then the focused one is hidden/removed, it feels like we need to reset hasInteracted.

CleanShot 2021-04-26 at 12 34 44

Happy to have a call about this one if i'm not making sense 🙂

@benoitgrelard
Copy link
Contributor Author

Super excited about this 🎉

I noticed one little thing when testing that felt a bit odd to me. If the items have been interacted with, and then the focused one is hidden/removed, it feels like we need to reset hasInteracted.

CleanShot 2021-04-26 at 12 34 44

Happy to have a call about this one if i'm not making sense 🙂

What I see in the gif seems to make sense to me, so yeah perhaps we should have a call on that particular one!

Copy link
Contributor Author

@benoitgrelard benoitgrelard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jjenzz This is now ready for review again. The last commit contains the changes to @radix-ui/react-collection to use @radix-ui/react-slot.

I've left a few new comments/questions inline for you below.

Also, I'm pretty sure that we'll be able to reuse the collection we've just put in place in Menu and its items for the typeahead stuff instead of the custom hook. I think it'll help also to bring it inside the main file as part of the process, all in one place.

const context = React.useContext(Context);

React.useEffect(() => {
context.itemMap.set(ref, { ref, ...((itemData as unknown) as ItemData) });
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

had to do as unknow as for some reason once I added React.forwardRef

>;

const RovingFocusGroup = React.forwardRef((props, forwardedRef) => {
const RovingFocusGroupImpl = React.forwardRef((props, forwardedRef) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added extra Impl component so we can get access to the useCollection hook.

Copy link
Contributor

@jjenzz jjenzz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've left a comment above about the onEntryFocus naming but just something to consider, I'll leave it up to you as I don't want to hold this up because of it.

@jjenzz
Copy link
Contributor

jjenzz commented Apr 30, 2021

All good from me except this one is still there if that's the answer you were looking for? #618 (comment)

@benoitgrelard benoitgrelard merged commit 978271c into main May 3, 2021
@benoitgrelard benoitgrelard deleted the roving-focus-group branch May 3, 2021 09:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[DropdownMenu] Items that are invisible cause focus issues
3 participants