Skip to content

[pickers] Only scroll the Digital Clock when its active item changes - #23942

Merged
LukasTy merged 2 commits into
mui:masterfrom
b84330808:fix/pickers-digital-clock-scroll
Oct 21, 2026
Merged

LukasTy merged 2 commits into
mui:masterfrom
b84330808:fix/pickers-digital-clock-scroll

Conversation

@b84330808

@b84330808 b84330808 commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #15758

DigitalClock and MultiSectionDigitalClock could scroll back to the selected time on unrelated re-renders. In DateTimePicker, pressing the mouse on a time blurs the day calendar, which changes focusedView and re-renders the clock. The list could then jump back to the selected time before the mouse was released, causing the click to land on another item or nowhere. The multi-section clock had the same issue because its previous scroll guard depended on the section having focused an item.

Both DigitalClock and MultiSectionDigitalClockSection now remember the last scrolled item together with its offsetTop, and skip scrolling only when both are unchanged. This preserves the user's scroll position on unrelated re-renders while still scrolling when the value changes, a clock mounted hidden becomes visible, or items above the active item change. Scroll tracking is separate from focus tracking, preserving the existing focus behavior.

Regression tests cover unrelated re-renders and controlled value changes for both clocks, plus a browser-only test for DigitalClock becoming visible after a hidden first mount. The DigitalClock, MultiSectionDigitalClock, TimePicker, and DateTimePicker tests pass.

I used Claude Code to write this change.

The effect set `scrollTop` on every render, so any re-render, like the one
when the day calendar loses focus in DateTimePicker, scrolled the list back
to the selected time and the click on another time was lost.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@code-infra-dashboard

code-infra-dashboard Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Deploy preview

https://deploy-preview-23942--material-ui-x.netlify.app/
QR code for https://deploy-preview-23942--material-ui-x.netlify.app/

Bundle size

Bundle Parsed size Gzip size
@mui/x-data-grid 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-pro 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-premium 0B(0.00%) 0B(0.00%)
@mui/x-charts 0B(0.00%) 0B(0.00%)
@mui/x-charts-pro 0B(0.00%) 0B(0.00%)
@mui/x-charts-premium 0B(0.00%) 0B(0.00%)
@mui/x-date-pickers 🔺+194B(+0.10%) 🔺+67B(+0.12%)
@mui/x-date-pickers-pro 🔺+194B(+0.07%) 🔺+57B(+0.07%)
@mui/x-tree-view 0B(0.00%) 0B(0.00%)
@mui/x-tree-view-pro 0B(0.00%) 0B(0.00%)
@mui/x-scheduler 0B(0.00%) 0B(0.00%)
@mui/x-scheduler-premium 0B(0.00%) 0B(0.00%)
@mui/x-chat 0B(0.00%) 0B(0.00%)
@mui/x-license 0B(0.00%) 0B(0.00%)

Details of bundle changes


Check out the code infra dashboard for more information about this PR.

@zannager zannager added the scope: pickers Changes related to the date/time pickers. label Oct 20, 2026
@LukasTy LukasTy added type: bug It doesn't behave as expected. component: TimePicker The React component. labels Oct 21, 2026
@LukasTy LukasTy changed the title [pickers] Only scroll the DigitalClock when its active item changes [pickers] Only scroll the DigitalClock when its active item changes Oct 21, 2026

@LukasTy LukasTy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the contribution. 🙏

I also reviewed with Claude, which found these new issues/regressions


The fix works for the reported case, and Tab / Shift+Tab focus behaves as on master. The new guard also skips two scrolls that are necessary, and the same bug remains in MultiSectionDigitalClockSection.

  • Regression: hidden first mount. If the first effect runs while the list is hidden (display: none), the guard stores the item and never scrolls to it later. Example: a single-column DesktopDateTimePicker with slotProps={{ popper: { keepMounted: true } }} and 12:30 PM selected opens with the list at midnight (scrollTop 0, master: 1050). The clock does not take focus there, so activeItem.focus() does not correct it. Reproduced in Chromium and WebKit.
  • Regression: layout change without scroll anchoring (Safari). If items above the active item change but the active element stays (skipDisabled with a new minTime or shouldDisableTime, or a new timeStep), the old scrollTop stays and the selected time leaves the view. WebKit shows this by default. Chromium shows it only with overflow-anchor: none.
  • Suggested fix for both: store offsetTop with the item and skip only when both match (snippet below). A "skip while hidden" check does not work in jsdom, where offsetParent, clientHeight and getClientRects() are always empty. With this change, the press stays fixed, both cases scroll again in Chromium and WebKit, and the new test passes in jsdom and in the browser.
  • Same bug, not covered: MultiSectionDigitalClockSection (the default DateTimePicker time view) also jumps. In Chromium, a 1 s press on hour "01" scrolls the hours section from 0 to 172 px, and the pointer is then over "05" (master and this PR). Its previousActive guard applies only after the section focuses an item, so the comparison in the description does not hold while focus is in the calendar. Apply the same guard there, or do not close the issue with this PR.
  • Tests: the new test only checks that a re-render does not scroll. Add a case where a new active item (for example a new value) still scrolls, and a browser test for the hidden mount.
const lastScrollRef = React.useRef<{ item: HTMLElement; offsetTop: number } | null>(null);

// Only scroll when the active item or its position changes, so that re-renders don't undo the user's scrolling
if (lastScrollRef.current?.item === activeItem && lastScrollRef.current.offsetTop === offsetTop) {
  return;
}
lastScrollRef.current = { item: activeItem, offsetTop };

Will you cover the same issue on MultiSectionDigitalClock in this PR, or should I create a separate one?

…iSectionDigitalClock

Skipping the scroll whenever the active item was the same also skipped
two scrolls that are needed: after a hidden first mount, and when items
above the active one change. Remember the item's offsetTop too and only
skip when both match.

MultiSectionDigitalClockSection had the same jump, because its
previousActive check only applies once the section has focused an item,
so it gets the same guard.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@b84330808

Copy link
Copy Markdown
Contributor Author

Thanks a lot for the thorough review! I'll cover MultiSectionDigitalClock here too, so the PR can still close #15758.

Pushed dfa1f84:

  • Both components now remember the item together with its offsetTop and only skip the scroll when both match, as you suggested. In MultiSectionDigitalClockSection that's a separate ref, so the previousActive focus logic is untouched.
  • Tests: a value change still scrolls (both components), a re-render doesn't (now also for the multi-section hours list, which fails on master), and a browser-only test for the hidden first mount. That one has to waitFor the scroll because the clock switches to smooth scrolling after its first render. It fails on the previous commit and passes on master and with this change.

@LukasTy LukasTy added needs cherry-pick The PR should be cherry-picked to master after merge. v8.x labels Oct 21, 2026
@LukasTy LukasTy changed the title [pickers] Only scroll the DigitalClock when its active item changes [pickers] Only scroll the Digital Clock when its active item changes Oct 21, 2026

@LukasTy LukasTy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work, thank you for fixing this and promptly addressing feedback. 🙏

@LukasTy
LukasTy enabled auto-merge (squash) October 21, 2026 14:11
@LukasTy
LukasTy merged commit be21ce4 into mui:master Oct 21, 2026
28 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Cherry-pick PRs will be created targeting branches: v8.x

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component: TimePicker The React component. needs cherry-pick The PR should be cherry-picked to master after merge. scope: pickers Changes related to the date/time pickers. type: bug It doesn't behave as expected. v8.x

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[pickers][DigitalClock] mouseup scrolling issue

3 participants