-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Components: Add onFocusOutside replacement to Popover onClickOutside #14851
Conversation
There seems to be a different behavior when using mouse and keyboard. Well, there might be something wrong with the URL popover and autocomplete:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will finish this review tomorrow. It's looking great so far. I left some questions.
*/ | ||
import clickOutside from 'react-click-outside'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated question to this PR. Can we also refactor Modal
component to stop using react-click-outside
? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated question to this PR. Can we also refactor
Modal
component to stop usingreact-click-outside
? :)
I would like to, yes.
( #6261 (comment) 😞 )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is still hope then :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://unpkg.com/[email protected]/index.js
Well, it looks like react-click-outside
is a simplified version of our own HOC :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I opened a follow-up to address it – #16878.
* | ||
* @param {FocusEvent} event Focus event from onFocusOutside. | ||
*/ | ||
onFocusOutside( event ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How can I test this shim? By updating one of the places with usage?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
3e8ffc4
to
6419f0d
Compare
This one was a pain to rebase, in good part because of #15053 (cc @youknowriad). Worth noting that this will resolve some lingering errors after #11360 logged frequently in StrictMode concerning |
} | ||
|
||
render() { | ||
return this.props.children; | ||
} | ||
} | ||
|
||
export default clickOutside( PopoverDetectOutside ); | ||
export default withFocusOutside( PopoverDetectOutside ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be a nice hook const useOnFocusOutside( ref, handler )
b1b2f79
to
6308529
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rebased this PR to fix some conflicts that existed in the CHANGELOG.md
files. You may want to double check that the changes to CHANGELOG.md
make sense.
I followed the testing steps in the description and everything seems okay. I also played with some Popover
and DropdownMenu
components and couldn't see anything off. I was not able to follow the original steps in the linked issue as that issue was fixed via a different approach.
Would love to get this in as it fixes a bug that's blocking #16582.
Thanks @noisysocks for the assist here. Looking again over the code and with some final testing, I think this is in good shape for merge. 👍 |
Hmm, the end-to-end test failures may be legitimate. |
The end-to-end tests seem to be getting stuck when trying to switch from "Code" editor back to "Visual" in the container blocks test cases. I can't see any reason why it would be such a specific issue here. My hunches are:
Separately, I stumbled upon this code, which was introduced more recently and seems like something we might want to update:
|
I figured out how to recreate the bug with some simple steps, but I'm no closer to figuring out what's causing it:
Any thoughts, @aduth? |
Figured it out! It's because clicking on the menu item causes a focus loss. This was reported in #14849. I'll include a fix here. |
Switching between `Button` and `IconButton` causes React to remount the `MenuItem` component. This causes a focus loss as well as E2E failures. There's no need to use a `Button` for `MenuItems` that are unselected, since we can simply pass `icon={ undefined }` to the `IconButton`.
I pushed up 738d987 which fixes the E2E test failures and fixes #14849. Would you mind giving this a quick test, @gziolo? Testing instructions: Verify that #14849 is fixed:
Verify there are no changes in behavior from other Popover usage, particularly specialized cases such as:
|
tagName, | ||
{ | ||
return ( | ||
<IconButton |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a bit stretched use of IconButton in the case when there is no icon 😅
However, I can't think of another simple way of handling it where this doesn't trigger re-render of the button. I noticed that this behavior is broken only because we swap IconButton
with Button
for no reason when only the icon gets removed/added.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, exactly! Switching between the two components causes React to remount the component and lose focus. Looking at IconButton
, it seemed to me that icon={}
is an optional prop so I felt comfortable doing it this way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing we keep discussing here and there is why not just add an icon
prop to the Button
block and deprecate the IconButton
(or make it just a shortcut)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sure @drw158 @cburton4 have some good ideas on how to approach it :)
I'm fine with anything which makes it less confusing. I guess having more flexible Button
and IconButton
as a backward-compatible alias makes a lot of sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have an issue open to discuss combining IconButton and Button in #16541
We are leaning towards keeping them separate, but I'll look at this closer to understand the disadvantage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm assuming from the code that this is an issue because Button does not have an icon option. Switching between icon and non-icon buttons for Menu Items is causing technical problems.
Could this be solved by adding an icon option to the Button component, but still keep IconButton a separate component? That way, the unchecked and checked versions of the Menu Item could use just a single component, Button.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be fair, we already do the opposite here. Not pass the "icon" prop to the IconButton (so it behaves like a Button). If it doesn't make sense to merge the two components into one, I feel we shouldn't do anything specific about it.
@afercia, would you mind running some sanity check for the Aside: yes, I still keep it in mind that we should have no links in the dropdown menu :) I need to find a related issue where @mapk proposes we move it to the Manage Blocks modal and tackle it at last. Regardless, I think we should move forward with this PR given that it makes More Menu finally usable for keyboard users. The last step to make the experience good is to stop closing this menu when opening modals 🎉 |
This PR will also partially resolve #15501. There is a remaining issue with focus loss when switching between Code and Visual editor when the post is empty. |
Thanks for working on this! @gziolo I tested a bit and seems to me everything works as expected. Just one small thing: should the small horizontal "jump" of menuitems with the check icon be fixed in this PR? See animated GIF: Regarding the way screen readers announce However, it still creates some issues. For example: in VoiceOver, this menuitem is not listed in the "Form Controls" list while the other ones are:
For clarity, menuitems can be links. However, an ARIA menu is supposed to be either a menu of actions or a navigation menu. When it's a navigation menu, it should be wrapped in a Menu or Menu bar design pattern: Navigation Menubar Example:
The point is that an ARIA menu can't be a mix of the two things. In this case, all the menuitems perform an action in the current page except |
Good catch, it needs to be fixed, it would be a regression. |
Thanks for testing all!
Thanks for going into this. Let's address the issues with Manage All Reusable Blocks as part of #13390. I agree we should remove the menu item altogether.
This bug exists in |
…14851) * Components: Add onFocusOutside alternative to Popover onClickOutside * Components: Refactor Dropdown to use onFocusOutside * Format Library: Refactor inline link to use onFocusOutside * MenuItem: Always use an IconButton component so as to avoid focus loss. Switching between `Button` and `IconButton` causes React to remount the `MenuItem` component. This causes a focus loss as well as E2E failures. There's no need to use a `Button` for `MenuItems` that are unselected, since we can simply pass `icon={ undefined }` to the `IconButton`.
…14851) * Components: Add onFocusOutside alternative to Popover onClickOutside * Components: Refactor Dropdown to use onFocusOutside * Format Library: Refactor inline link to use onFocusOutside * MenuItem: Always use an IconButton component so as to avoid focus loss. Switching between `Button` and `IconButton` causes React to remount the `MenuItem` component. This causes a focus loss as well as E2E failures. There's no need to use a `Button` for `MenuItems` that are unselected, since we can simply pass `icon={ undefined }` to the `IconButton`.
Fixes #14754, fixes #14849.
This pull request seeks to refactor popovers to avoid relying on "click outside" behaviors in popovers, deprecating the
Popover
component'sonClickOutside
prop in favor of a substituteonFocusOutside
. This accounts for more reasons for focus transitions not previously reflected inonClickOutside
, such as that described by the bug of #14754.Status: While functionally complete, I'm considering this to be blocked by an apparent incompatibility with a tangentially-related bug described at #14849 . Notably, when toggling an option in the More Options menu, the menu will no longer become closed again from clicks elsewhere in the page.
Testing instructions:
Repeat Steps to Reproduce from #14754 , verifying that the menu is closed as expected.
Verify there are no changes in behavior from other Popover usage, particularly specialized cases such as: