-
Notifications
You must be signed in to change notification settings - Fork 844
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
[EuiFlyout] Scope close events to mask when ownFocus=true
#5876
Conversation
Preview documentation changes for this PR: https://eui.elastic.co/pr_5876/ |
Preview documentation changes for this PR: https://eui.elastic.co/pr_5876/ |
Preview documentation changes for this PR: https://eui.elastic.co/pr_5876/ |
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 should get us exactly what we're needing for the specific cases in Kibana. I appreciate the Cypress specs (with real events!) to test the overlay and EuiToast
cases. I'll defer to Constance for the code changes, but wanted to chime in the experience matches a mockup I put together to test UX quickly.
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.
Mostly looks great to me! My comments are mainly around the onClickOutside
logic and if we're in a hurry can be skipped, but I think it's worth clarifying for developer experience/understanding
src/components/flyout/flyout.tsx
Outdated
const onClickOutside = (event: MouseEvent | TouchEvent) => | ||
(isDefaultConfiguration && | ||
outsideClickCloses !== false && | ||
event.target === maskRef.current) || | ||
outsideClickCloses === true | ||
? onClose | ||
? onClose(event) | ||
: undefined; |
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 still feels pretty hard for me to read personally 😅 Since this is a function now, can we prefer early returns?
const onClickOutside = (event: MouseEvent | TouchEvent) => {
if (outsideClickCloses === true) return onClose(event);
if (outsideClickCloses === false) return undefined;
// If outsideClickCloses is not specified, only close on clicks to the overlay mask
if (isDefaultConfiguration && event.target === maskRef) return onClose(event);
// Otherwise if ownFocus is false, outside clicks should not close the flyout
return undefined;
}
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'll refactor for readability, but here's the logic:
- If
ownFocus=true
(default), only clicks on the mask should close the flyout, even ifoutsideClickCloses=true
- If
ownFocus=false
, the flyout will only close ifoutsideClickCloses=true
, and in that case clicking any external element will close the flyout
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 ownFocus=true (default), only clicks on the mask should close the flyout, even if outsideClickCloses=true
That's just not what the current branching is doing though?? So you'd need to change this logic if that's the behavior you want. outsideClickCloses === true
will always evaluate to returning onClose()
. Am I reading this the same way as you?
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.
const onClickOutside = (event: MouseEvent | TouchEvent) => {
// Do not close the flyout for any external click
if (outsideClickCloses === false) return undefined;
if (isDefaultConfiguration) {
// The overlay mask is present, so only clicks targeting the mask should close the flyout
if (event.target === maskRef.current) return onClose(event);
} else {
// No overlay mask is present, so any outside clicks should close the flyout
if (outsideClickCloses === true) return onClose(event);
}
// Otherwise if ownFocus is false, outside clicks should not close the flyout
return undefined;
};
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.
That's just not what the current branching is doing though??
Yep I had the conditional wrong. #5876 (comment) is correct; will push the change once we get confirmation from @cchaos
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.
@thompsongl Nice! Definitely much easier to grok now, especially w/ comments. For the last comment/return, I might make it a bit more specific to help follow the possible branch paths:
// Otherwise if ownFocus is false and outsideClickCloses is undefined, outside clicks should not close the flyout
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 also think we should be more specific about the isDefaultConfiguration
mask behavior:
// The overlay mask is present, so only clicks on the mask should close the flyout, regardless of outsideClickCloses
It's not that the business logic/UX doesn't make sense, it's just that it's a bit tortuous so more comments should help with that (I think?)
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.
Oh man, I remember how tricky this logic was. I'm going to repeat the logic just to ensure I understand.
- Default
ownFocus = true
: The overlay mask renders and the flyout will only be closed when clicking mask (or close button) ownFocus = false && outsideClickCloses = false
: No overlay mask and can only be closed via close button (or some other in-page toggle)ownFocus = false && outsideClickCloses = true
: Clicking anywhere outside will close flyout (does this include dragging with mouse up or does this now mean a special extra configuration?)
I feel like that's how it was before, but maybe I'm remembering another config wrong?
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.
ownFocus = false && outsideClickCloses = true: Clicking anywhere outside will close flyout (does this include dragging with mouse up or does this now mean a special extra configuration?)
By default the close event will happen if mousedown happens outside the flyout. Consumers can change this to occur on mouseup if they have some reason to do so.
By default the close event will not happen if mousedown happens inside the flyout, even if mouseup happens outside the flyout (e.g., mousedown inside, drag outside, release. The flyout remains open)
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.
43ce709 incorporates all suggestions in this thread
Preview documentation changes for this PR: https://eui.elastic.co/pr_5876/ |
outsideClickCloses === true | ||
? onClose | ||
: undefined; | ||
const hasOverlayMask = ownFocus && !isPushed; |
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 think this name change will be super helpful for understanding
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 is great!!
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.
Thanks as always for the thorough review, @constancecchen! |
Preview documentation changes for this PR: https://eui.elastic.co/pr_5876/ |
Summary
Restores previous decision to only consider an click event as "outside" when
EuiOverlayMask
is the target andownFocus=true
.EuiOverlayMask
accepts a React ref viamaskRef
EuiFlyout
takes advantage of ^ to scope outside click events in the case ofownFocus=true
(the default case)EuiFlyout
provides event data toonClose
callback (derived in most cases fromEuiFocusTrap
)When
ownFocus=false
andoutsideClickCloses=true
, we will not scope the close event to any particular element. Use theshards
prop to prevent closing when clicking certain external elements.Checklist