-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
[pickers] Avoid stealing focus on click away #13434
base: master
Are you sure you want to change the base?
Conversation
Deploy preview: https://deploy-preview-13434--material-ui-x.netlify.app/ |
@@ -195,15 +195,15 @@ const useMultiInputFieldSlotProps = < | |||
}, [rangePosition, open, currentView, startFieldRef, endFieldRef]); | |||
|
|||
const openRangeStartSelection: React.UIEventHandler = (event) => { | |||
event.stopPropagation(); | |||
event.preventDefault(); |
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.
These fixes prevent the following issue:
Screen.Recording.2024-06-10.at.13.43.10.mov
The regular Picker is not closed when opening a range picker
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.
Could we add a comment on top of this preventDefault to explain why it's here? In general, I think we should aim to justify each, they (preventDefault and especially stopPropagation) tend to be quickly food guns.
@@ -219,7 +220,8 @@ export const DigitalClock = React.forwardRef(function DigitalClock<TDate extends | |||
return; | |||
} | |||
const offsetTop = activeItem.offsetTop; | |||
if (autoFocus || !!focusedView) { | |||
if ((autoFocus || !!focusedView) && activeItem !== lastActiveRef.current) { |
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.
Fix the issue of stealing the focus when closing a picker with DigitalClock:
Screen.Recording.2024-06-10.at.15.37.51.mov
@@ -172,7 +173,7 @@ export const MultiSectionDigitalClockSection = React.forwardRef( | |||
const activeItem = containerRef.current.querySelector<HTMLElement>( | |||
'[role="option"][tabindex="0"], [role="option"][aria-selected="true"]', | |||
); | |||
if (active && autoFocus && activeItem) { | |||
if (activeItem && active && autoFocus && activeItem !== previousActive.current) { |
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.
Fix the issue of stealing the focus when closing a picker with MultiSectionDigitalClock:
Screen.Recording.2024-06-10.at.15.38.47.mov
@@ -360,6 +360,16 @@ export function PickersPopper(inProps: PickerPopperProps) { | |||
}, [onDismiss, open]); | |||
|
|||
const lastFocusedElementRef = React.useRef<Element | null>(null); | |||
|
|||
const handleClickAway: OnClickAway = useEventCallback(() => { | |||
lastFocusedElementRef.current = null; |
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.
Fix the root cause why pickers where returning the focus back to the open button after being clicked away:
Screen.Recording.2024-06-10.at.15.39.53.mov
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.
LGTM! 🚢
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.
Looks solid!
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.
In HEAD, we restore the focus back to the trigger. e.g. https://mui.com/material-ui/react-menu/
Screen.Recording.2024-06-10.at.18.37.04.mov
It doesn't do it anymore:
Screen.Recording.2024-06-10.at.18.34.53.mov
A bug?
@@ -195,15 +195,15 @@ const useMultiInputFieldSlotProps = < | |||
}, [rangePosition, open, currentView, startFieldRef, endFieldRef]); | |||
|
|||
const openRangeStartSelection: React.UIEventHandler = (event) => { | |||
event.stopPropagation(); | |||
event.preventDefault(); |
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.
Could we add a comment on top of this preventDefault to explain why it's here? In general, I think we should aim to justify each, they (preventDefault and especially stopPropagation) tend to be quickly food guns.
@@ -777,6 +777,76 @@ async function initializeEnvironment( | |||
// check that selecting a day doesn't change the day text | |||
expect(await day11.textContent()).to.equal('11'); | |||
}); | |||
|
|||
describe('Focus behavior', () => { |
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 suspect a regular karma test would be enough here (so better since faster to run)? We are doing relatively simple events.
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.
If I'm not mistaken, I tried writing unit tests initially, but failed to make them work.
I didn't try skipping them in jest
though. I'll look into that.
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've tried karma tests and they have the same problem...
Maybe it's some sort of a race condition with timeouts, but on those tests, the document.activeElement
is resolved/set on the button element. 🙈
Thank you for flagging this @oliviertassinari! 🙏 |
@oliviertassinari I've explored other solutions and here is the rundown:
Given these arguments, I think that the behavioral solution in this PR is valid and more correct than the current one. WDYT @flaviendelangle @arthurbalduini @joserodolfofreitas?
|
@LukasTy Thanks for the dive. In my view, the behavior before feels better (Angular Material and React Spectrum ). At least, the things that are important properties we want seem to be:
On the solution, I don't think that 1. (using a backdrop) is that much correlated to the root of the problem, this feels like taking a different path (which works, true). Now, it's not the end of the world, maybe it can work ok like this. At the root, there might be nothing to change in MUI X (I assume what we do here is to trigger ). The problem might really be with the FocusTrap logic. Maybe the logic that restores the focus should give up if that focus is already set outside of its boundary. I mean, this would make a lot of sense to me. It makes me think of mui/material-ui#35307. For this, we should involve the FocusTrap's ower? https://www.notion.so/mui-org/0c8156b0b1634ea5bfd903dd005ec1cf?v=89ea8d7b7e104bf8b3bb68a9edca5e16&p=fbe1aed295f748c19576d024e9a48c62&pm=s |
Hi! Is this fix still being considered? I just spent the better part of last week migrating from react-dates to this date picker, but that bug is a deal breaker for us. I haven't see any comments since June on this issue, I was wondering if maybe it had been forgotten? Thanks |
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
Hi, thank you for reminding us about this problem. 🙏 |
Hello! Was there any progress on this issue in the meanwhile? If not, can the community help in anyway? |
Fixes #13332
Fixes #14801
stopPropagation
calls were preventing click away listener from working properly.focus()
calls in effects were stealing the focus before closing the popperPreview: https://deploy-preview-13434--material-ui-x.netlify.app/x/react-date-pickers/