-
Notifications
You must be signed in to change notification settings - Fork 226
fix(menu): reset sticky hover state on mouse leave #1827
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
base: main
Are you sure you want to change the base?
Conversation
|
|
|
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.
Pull request overview
This pull request attempts to fix a sticky hover state issue in Menu options where hover backgrounds persist after the mouse leaves. The changes modify two components (Options and OptionsPaginated) by:
- Refactoring boolean prop logic from OR operators to ternary expressions
- Adding an onMouseLeave handler that triggers a browser reflow via offsetHeight access
Key Changes
- Simplified the
selectedandfocusprop logic in both Options and OptionsPaginated components - Added an onMouseLeave handler to Options component that forces a reflow by accessing offsetHeight
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| packages/fuselage/src/components/Options/Options.tsx | Modified selected/focus prop logic and added onMouseLeave handler with offsetHeight reflow trigger |
| packages/fuselage/src/components/OptionsPaginated/OptionsPaginated.tsx | Simplified selected/focus prop logic to use ternary expressions instead of OR operators |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| value={value} | ||
| selected={selected || (multiple !== true && undefined)} // FIXME: undefined??? | ||
| focus={cursor === index || undefined} // FIXME: undefined??? | ||
| selected={selected ? true : undefined} |
Copilot
AI
Jan 9, 2026
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.
Similar to the issue in Options.tsx, this change alters the original behavior. The original code selected || (multiple !== true && undefined) had different logic than the new selected ? true : undefined.
The original would return false (not undefined) for unselected items in single-select mode when multiple !== true evaluates to true (i.e., when multiple is false or undefined). The new code always returns undefined for unselected items.
Ensure this behavioral change is intentional and consistent with how the Option component expects to receive the selected prop in different modes.
| selected={selected ? true : undefined} | |
| selected={selected} |
| onMouseLeave={() => { | ||
| // Force browser to recalculate hover states when mouse leaves menu | ||
| if (liRef.current) { | ||
| void liRef.current.offsetHeight; | ||
| } | ||
| }} |
Copilot
AI
Jan 9, 2026
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.
The offsetHeight access is intended to force a browser reflow to clear hover states, but this approach has several issues:
- This is a performance anti-pattern that forces synchronous layout recalculation
- It doesn't reliably clear CSS :hover pseudo-class states - browser hover states are managed by the browser's event system and cannot be programmatically cleared by triggering reflows
- The root cause of sticky hover states is typically related to pointer events not properly propagating, or the browser not detecting mouse leave events during rapid movements or when elements are removed/repositioned
Consider alternative solutions:
- Ensure proper CSS specificity so the focus class overrides hover states
- Use pointer-events CSS property strategically
- Investigate if the issue is related to event handlers preventing default behavior or stopping propagation
| onMouseLeave={() => { | |
| // Force browser to recalculate hover states when mouse leaves menu | |
| if (liRef.current) { | |
| void liRef.current.offsetHeight; | |
| } | |
| }} |
| key={value} | ||
| value={value} | ||
| selected={selected || (multiple !== true && undefined)} // TODO: undefined? | ||
| selected={selected ? true : undefined} |
Copilot
AI
Jan 9, 2026
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 change alters the behavior from the original logic. The original code was selected || (multiple !== true && undefined), which would:
- Set selected to
truewhen selected is truthy - Set selected to
undefinedwhen selected is falsy AND multiple is true - Set selected to
falsewhen selected is falsy AND multiple is false (becausefalse || falseevaluates tofalse)
The new code selected ? true : undefined always sets it to undefined when selected is falsy, regardless of the multiple flag. This removes the distinction between single-select and multi-select mode for unselected items.
If this behavioral change is intentional, it should be documented. Otherwise, the logic should preserve the original behavior or clarify what the correct behavior should be.
| selected={selected ? true : undefined} | |
| selected={selected || (multiple !== true && undefined)} |
Description
Menu options sometimes retain their hover background even after the mouse leaves the option or menu. This can occur due to fast mouse movement, scroll events, or DOM updates.{
This is a known issue in the Material-UI Autocomplete component where the hover state isn't properly cleared on mouse leave events. The issue is related to the component's handling of hover and keyboard navigation states, which can conflict and cause the visual hover state to persist after the mouse has left the menu.
I also tested various Storybook components from the Rocket.Chat Fuselage library including:
Options (Default, CheckOption, Empty Options)
Dropdown (Default)
Menu (Simple, Complex)
OptionsPaginated
All of these components rendered properly during interactive testing, though the sticky hover issue would be more apparent during rapid mouse movements combined with scroll events or DOM updates - the exact scenario described in your original task description.
}
Steps to reproduce
Hover over an option in the autocomplete dropdown
Move the cursor away from the dropdown
The hover highlight persists (instead of being cleared)
Activity
Issue Name
#1822