Skip to content

Conversation

@ZeeshanTamboli
Copy link
Member

@ZeeshanTamboli ZeeshanTamboli commented Nov 13, 2025

Closes #47203

Fix: prevent listbox scroll from resetting to top after selection when reopened via input click

Issue explanation

When disableCloseOnSelect is enabled in <Autocomplete />, the listbox's scroll position jumps back to the top after selecting an option and reopening the popup by clicking on the input.
This does not happen when the popup is closed by clicking outside and then reopened.

Steps to Reproduce

  1. Render an <Autocomplete multiple disableCloseOnSelect /> with enough options to make the listbox scrollable.
  2. Open the popup by clicking on the input.
  3. Close it by clicking the input again.
  4. Re-open it (again by clicking the input).
  5. Scroll the listbox down and click on an option near the bottom.

Expected: The listbox should keep its current scroll position.
Actual: The listbox scrolls back to the top.

Why it Happened

The effect below was always re-running syncHighlightedIndex() whenever the popup was open:

React.useEffect(() => {
  if (filteredOptionsChanged || popupOpen) {
    syncHighlightedIndex();
  }
}, [syncHighlightedIndex, filteredOptionsChanged, popupOpen]);

When closing via input click, there is an intermediate render with popupOpen = false, causing filteredOptions = [] to be stored in previousProps.filteredOptions.
On reopening, filteredOptions becomes non-empty again, so filteredOptionsChanged is true.
The effect runs during option selection, and syncHighlightedIndex() resets the highlighted index to -1, which sets listboxNode.scrollTop = 0, causing the scroll jump.

When closing by clicking outside, the input blurs and the listbox ref unmounts; reopening triggers handleListboxRef() to sync the highlighted index after mount instead. In this path, previousProps.filteredOptions stays filled, so filteredOptionsChanged is false, and the scroll position is preserved.

Fix

Run the sync effect on popup open only when disableCloseOnSelect is not set:

- if (filteredOptionsChanged || popupOpen) {
+ if (filteredOptionsChanged || (popupOpen && !disableCloseOnSelect)) {
    syncHighlightedIndex();
  }

This prevents unnecessary synchronization while the popup remains open for multi-select use cases.

Test Added

Added a regression test verifying that:

  • When reopening the listbox by clicking the input and selecting a scrolled-down option,
  • The listbox does not reset its scroll position to top.

Before: https://stackblitz.com/edit/h6i4uehm-igrkcoz4?file=src%2FDemo.tsx
After: https://stackblitz.com/edit/jbaa3hry?file=src%2FApp.tsx

@ZeeshanTamboli ZeeshanTamboli added scope: autocomplete Changes related to the autocomplete. This includes ComboBox. type: regression A bug, but worse, it used to behave as expected. labels Nov 13, 2025
@mui-bot
Copy link

mui-bot commented Nov 13, 2025

Netlify deploy preview

https://deploy-preview-47248--material-ui.netlify.app/

Bundle size report

Bundle Parsed size Gzip size
@mui/material 🔺+6B(0.00%) 🔺+6B(0.00%)
@mui/lab 0B(0.00%) 0B(0.00%)
@mui/system 0B(0.00%) 0B(0.00%)
@mui/utils 0B(0.00%) 0B(0.00%)

Details of bundle changes

Generated by 🚫 dangerJS against 7c2bff8

@ZeeshanTamboli ZeeshanTamboli marked this pull request as ready for review November 14, 2025 06:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: autocomplete Changes related to the autocomplete. This includes ComboBox. type: regression A bug, but worse, it used to behave as expected.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[autocomplete] Scroll resets to top on option selection when popup is reopened by clicking the input

2 participants