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

fix: Fix bug that prevented keyboard navigation in flyouts. #8687

Merged
merged 2 commits into from
Jan 9, 2025

Conversation

gonfunko
Copy link
Contributor

@gonfunko gonfunko commented Dec 5, 2024

The basics

The details

This PR fixes an issue that caused findNextLocationInFlyout() to return invalid items. With the flyout changes in v12, spacers (and potentially custom user-defined items) are included in the list of flyout contents, but are not valid keyboard navigation locations. As such, simply taking the next/previous item relative to the current one is insufficient; that item may be inspected, but if it is of a non-navigable type, the subsequent item needs to be checked until a valid navigation location is found.

@gonfunko gonfunko requested a review from a team as a code owner December 5, 2024 20:58
@github-actions github-actions bot added the PR: fix Fixes a bug label Dec 5, 2024
resultIndex >= 0 &&
resultIndex < flyoutContents.length &&
!(
flyoutContents[resultIndex].element instanceof BlockSvg ||
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you file a bug to create an isNavigable API or similar for flyout items? This will do for now but eventually those custom items will need to be navigable (a text entry box for searching being the most obvious case).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, this is why I was asking about ASTNode in the chat :) I think we probably just want to add that method to IASTNodeLocation; should I just go ahead and do that in this PR?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think so; @cpcallen to confirm. This would be in the v12 beta so we'll still have time to change it.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think one of us might be confused, because it doesn't make sense to me for IASTNodeLocation to have an isNavigable method, because (in practice) an IASTNodeLocation is a thing one has already navigated to (i.e., that is the value of a .location property of an ASTNode—so you'd have to have called .isNavigable on the possible location before you create the ASTNode for it (which is the first time it definitely needs to fulfil the interface), and you won't have any reason to call isNavigable again because you already know it's navigable. :-/

I think the method should probably be called isFocusable and be on FlyoutItem, but I'm not sure as I don't fully understand the flyout changes in v12.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you, I was the confused one – I didn't realize the already navigated to aspect of IASTNodeLocation. As suggested, I've added isFocusable() to FlyoutItem. Since this is still only in beta and part of a larger breaking change, I took the liberty of refactoring the APIs to be a bit more consistent.

Comment on lines +16 to +20
constructor(
private element: IBoundedElement,
private type: string,
private focusable: boolean,
) {}
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there some reasonably foreseeable scenario in which this class might be subclassed? If not, it might be better to make the members (public) readonly and omit the get* methods. The styleguide has this rule for get/set accessors (get foo()) which I think seems like good advice also for ordinary getter methods (getFoo()).

Getters and setters, also known as accessors, for class members may be used…

If an accessor is used to hide a class property, the hidden property may be prefixed or suffixed with any whole word, like internal or wrapped. When using these private properties, access the value through the accessor whenever possible. At least one accessor for a property must be non-trivial: do not define pass-through accessors only for the purpose of hiding a property. Instead, make the property public (or consider making it readonly rather than just defining a getter with no setter).

Of course if this class is likely to get subclassed then the current approach seems sound.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I could imagine a situation where somebody with a custom flyout might want to add additional metadata to the items in it, so perhaps? Happy to adjust if you think that's preferable though.

@gonfunko gonfunko merged commit 75efba9 into google:rc/v12.0.0 Jan 9, 2025
7 checks passed
@gonfunko gonfunko deleted the keyboard-nav branch January 9, 2025 22:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PR: fix Fixes a bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants