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

[UI Framework] Improve KuiContextMenu keyboard navigation UX #14434

Merged
merged 17 commits into from
Oct 25, 2017

Conversation

cjcenizal
Copy link
Contributor

@cjcenizal cjcenizal commented Oct 12, 2017

Resolves #14239
Also addresses #14183 (review)

@timroes I implemented your suggestions for the improved keyboard navigation. @weltenwort I refactored the code a bit and got rid of that isVisible prop. It turns out it wasn't necessary at all --embarrassingly, it didn't even do what I originally thought it was doing.

@cjcenizal cjcenizal added Team:Platform-Design Team Label for Kibana Design Team. Support the Analyze group of plugins. v6.1.0 v7.0.0 labels Oct 12, 2017
@cjcenizal cjcenizal force-pushed the 14239/context-menu-focus branch from bef2de5 to f63d868 Compare October 12, 2017 00:06
@cjcenizal
Copy link
Contributor Author

jenkins, test this

@timroes
Copy link
Contributor

timroes commented Oct 13, 2017

@cjcenizal you have to merge master into this, to get rid of the licencing issue. I merged a fix for that yesterday.

@timroes
Copy link
Contributor

timroes commented Oct 13, 2017

I think this PR destroyed (on purpose?) some of the old behavior.

In panels that are menu only, I cannot use Shift+Tab anymore to move focus to the header (and press enter there to move an layer up). I can now only use Left to move a layer up. But the content dialog in the example (i.e. a non menu dialog?) can still focus that header and use enter.

Is this a desired behavior or got this broken by accident?

@timroes
Copy link
Contributor

timroes commented Oct 13, 2017

Due to the overflow: hidden on the .kuiContextMenuthe focus highlight isn't shown in the first example in the docs, but in the third it's shown.

To be honest I don't find that focus ring in this place very attractive and I would like to discuss other solutions.

I would either try to create a more beautiful focus ring for this, though I had a hard time coming up with an idea how this could look like, or get rid of the focus ring for the dialog in general.

Why would I even say such a thing? Here some arguments why I think we can get rid of it in this place:

  • Having focus rings is WCAG AA - so even they don't consider it the very most important thing to place it in A level compliance
  • Even though the element is focused, you cannot interact with it via keyboard in this moment. So pressing Enter or Space doesn't do anything, so I think it's not as important to know, that this element currently has the focus.
  • The popover is seen as kind of a modal over the page. If you click outside it, it vanished, if you press Escape, it vanishes. So as soon as it opens the attention of the user is anyway at it. Also pressing Tab in that moment will directly interact with the dialog, same as the arrow keys.

I just don't see a good reason, that this focus ring would add much for the user in this case. That she would expect more likely that pressing keys will now interact with it? Wouldn't most users assume that anyway for something behaving very modal like? I just feel we lose too much from a design perspective for gaining little to nothing in usability.

@cjcenizal cjcenizal force-pushed the 14239/context-menu-focus branch from f63d868 to 2303ea7 Compare October 13, 2017 15:11
@cjcenizal
Copy link
Contributor Author

cjcenizal commented Oct 13, 2017

@timroes Thanks for those terrific catches! I completely agree that the focus ring provides no benefit and sends the wrong message to the user (I didn't notice it being applied originally). I've removed it as you suggested. I also fixed that bug with the tabbing UX -- that was a regression. Could you take another look?

Copy link
Member

@weltenwort weltenwort left a comment

Choose a reason for hiding this comment

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

The UX appears fine to me. I have some reservations about the very frugal use of refs which are passed across component boundaries, which I think we could reduce. But that might better be done in a separate "clean up component interfaces and ref usage" PR.

isVisible won't be missed, but it still seems to be present in the KuiContextMenu tests. 😉

if (node) {
this.content = node;
}
};
Copy link
Member

Choose a reason for hiding this comment

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

If we store a reference to the dom element here, we should also clear it when we're called with null. Just storing this.content = node would do that.

@cjcenizal
Copy link
Contributor Author

@weltenwort Bah thanks for catching that mistake in the tests. I've addressed both of your points. Mind taking one last quick look?

Copy link
Member

@weltenwort weltenwort left a comment

Choose a reason for hiding this comment

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

Nice, thank you 😄

I just noticed a small thing with keyboard focus in the dashboard: After maximizing a panel via the context menu, I can not focus any other element in Kibana without first clicking somewhere. Maybe the focus trap was not reset properly?

this.panel = node;
this.panel = node;

if (this.panel) {
this.panel.addEventListener('animationend', this.onTransitionComplete);
Copy link
Member

Choose a reason for hiding this comment

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

Now if we just could remove the event listener again when the previous this.panel was an element but the node isn't... or even better replace it with the onAnimationEnd react prop 💭

@cjcenizal
Copy link
Contributor Author

@weltenwort Done and done! You are my new favorite code reviewer.

@timroes
Copy link
Contributor

timroes commented Oct 16, 2017

Okay now I unfortunately must start nitpicking a bit 😅

When pressing Tab you can cycle through all options, and it will start from the top (i.e. the dialog itself) again when reaching the end. The same behavior doesn't work when using Shift+Tab to cycle backwards. You will be stuck on the dialog in this case.

If this is an actual issue in one of the libraries, I wouldn't consider it blocking or too worse, just crossed my mind.

Copy link
Contributor

@timroes timroes left a comment

Choose a reason for hiding this comment

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

If you could fix the small findings, it would be great. Otherwise this looks good to me.


onDeletePanel = () => {
this.closePopover();
this.props.onDeletePanel();
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you think we should check whether the onDeletePanel prop exists, since it's not marked as required in the prop types, even though as far as I understand, this case can in our current setup never occur?

}],
};

this.panels = flattenPanelTree(panelTree);
}

onButtonClick() {
onButtonClick = () => {
this.setState({
isPopoverOpen: !this.state.isPopoverOpen,
Copy link
Contributor

Choose a reason for hiding this comment

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

Not really touched in the PR, but since I currently see this in the review, might you change this to use an updater function?

@@ -18,21 +18,21 @@ export default class extends Component {
};
}

onButtonClick() {
onButtonClick = () => {
this.setState({
isPopoverOpen: !this.state.isPopoverOpen,
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here with updater function.

@timroes
Copy link
Contributor

timroes commented Oct 17, 2017

As discussed in person: If the trigger button is already focused, and you scroll away, pressing Enter will open the menu, but not scroll back to the menu (in contrast to all other actions with the menu). Very nitpicking, but you might want to look into it :-)

Copy link
Member

@weltenwort weltenwort left a comment

Choose a reason for hiding this comment

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

This is such a great component. I didn't notice anything else beyond what has been mentioned by @timroes and my comment below.

// If there aren't any items then this is probably a form or something.
// So let's focus the first tabbable item and expedite input from the user.
if (!this.menuItems.length) {
const tabbableItems = tabbable(this.content);
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we should guard against this.content being null | undefined?

@cjcenizal
Copy link
Contributor Author

@timroes @weltenwort Comments addressed! Could you take another look?

@cjcenizal cjcenizal force-pushed the 14239/context-menu-focus branch from 1f8862a to 3374111 Compare October 24, 2017 19:00
@cjcenizal
Copy link
Contributor Author

jenkins, test this

@cjcenizal cjcenizal force-pushed the 14239/context-menu-focus branch from 9649eb0 to c427b62 Compare October 24, 2017 22:59
@cjcenizal cjcenizal merged commit ec38367 into elastic:master Oct 25, 2017
@cjcenizal cjcenizal deleted the 14239/context-menu-focus branch October 25, 2017 00:37
cjcenizal added a commit to cjcenizal/kibana that referenced this pull request Oct 25, 2017
…#14434)

* Refactor focus state logic to use the React lifecycle correctly.
* Update KuiPopover snapshots.
* Remove unnecessary isVisible prop from KuiContextMenu.
* Allow user to both tab AND use the arrow keys for navigation.
* Reinstate ability to tab and shift-tab to the title of KuiContextMenuPanel.
* Release focus from Dashboard panel options KuiContextMenu by closing it when you select an option.
* Update KuiContextMenu example to demonstrate best practice of closing the menu when an item is clicked.
* Replace native transitionend event handler with onAnimationEnd React event handler.
cjcenizal added a commit that referenced this pull request Oct 25, 2017
…#14556)

* Refactor focus state logic to use the React lifecycle correctly.
* Update KuiPopover snapshots.
* Remove unnecessary isVisible prop from KuiContextMenu.
* Allow user to both tab AND use the arrow keys for navigation.
* Reinstate ability to tab and shift-tab to the title of KuiContextMenuPanel.
* Release focus from Dashboard panel options KuiContextMenu by closing it when you select an option.
* Update KuiContextMenu example to demonstrate best practice of closing the menu when an item is clicked.
* Replace native transitionend event handler with onAnimationEnd React event handler.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Team:Platform-Design Team Label for Kibana Design Team. Support the Analyze group of plugins. v6.1.0 v7.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants