diff --git a/packages/x-date-pickers/src/CalendarPicker/CalendarPicker.test.tsx b/packages/x-date-pickers/src/CalendarPicker/CalendarPicker.test.tsx
index c232bd22977da..c8ae0bb16a547 100644
--- a/packages/x-date-pickers/src/CalendarPicker/CalendarPicker.test.tsx
+++ b/packages/x-date-pickers/src/CalendarPicker/CalendarPicker.test.tsx
@@ -14,6 +14,8 @@ import {
createPickerRenderer,
} from '../../../../test/utils/pickers-utils';
+const isJSDOM = /jsdom/.test(window.navigator.userAgent);
+
describe('', () => {
const { render, clock } = createPickerRenderer({ clock: 'fake' });
@@ -412,5 +414,33 @@ describe('', () => {
expect(onChange.callCount).to.equal(0);
expect(screen.getByMuiTest('calendar-month-and-year-text')).to.have.text('January 2022');
});
+
+ it('should scroll to show the selected year', function test() {
+ if (isJSDOM) {
+ this.skip(); // Needs layout
+ }
+ render(
+ {}}
+ views={['year']}
+ openTo="year"
+ />,
+ );
+
+ // const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller')!;
+ //
+ const rootElement = document.querySelector('.MuiCalendarPicker-root')!;
+ const selectedButton = document.querySelector('.Mui-selected')!;
+
+ expect(rootElement).not.to.equal(null);
+ expect(selectedButton).not.to.equal(null);
+
+ const parentBoundingBox = rootElement.getBoundingClientRect();
+ const buttonBoundingBox = selectedButton.getBoundingClientRect();
+
+ expect(parentBoundingBox.top).not.to.greaterThan(buttonBoundingBox.top);
+ expect(parentBoundingBox.bottom).not.to.lessThan(buttonBoundingBox.bottom);
+ });
});
});
diff --git a/packages/x-date-pickers/src/CalendarPicker/CalendarPicker.tsx b/packages/x-date-pickers/src/CalendarPicker/CalendarPicker.tsx
index 939d504b20dca..002edc9a9c54d 100644
--- a/packages/x-date-pickers/src/CalendarPicker/CalendarPicker.tsx
+++ b/packages/x-date-pickers/src/CalendarPicker/CalendarPicker.tsx
@@ -204,9 +204,7 @@ const CalendarPickerViewTransitionContainer = styled(PickersFadeTransitionGroup,
name: 'MuiCalendarPicker',
slot: 'ViewTransitionContainer',
overridesResolver: (props, styles) => styles.viewTransitionContainer,
-})<{ ownerState: CalendarPickerProps }>({
- overflowY: 'auto',
-});
+})<{ ownerState: CalendarPickerProps }>({});
type CalendarPickerComponent = ((
props: CalendarPickerProps & React.RefAttributes,
@@ -462,6 +460,11 @@ export const CalendarPicker = React.forwardRef(function CalendarPicker(
},
);
+ React.useEffect(() => {
+ // Set focus to the button when switching from a view to another
+ handleFocusedViewChange(openView)(true);
+ }, [openView, handleFocusedViewChange]);
+
return (
{
const classes = useUtilityClasses(ownerState);
- // TODO: Can we just forward this to the button?
+ // We can't forward the `autoFocus` to the button because it is a native button, not a MUI Button
React.useEffect(() => {
if (autoFocus) {
// `ref.current` being `null` would be a bug in MUI.
diff --git a/packages/x-date-pickers/src/YearPicker/YearPicker.tsx b/packages/x-date-pickers/src/YearPicker/YearPicker.tsx
index eda57d29453b2..f47c32d50b65f 100644
--- a/packages/x-date-pickers/src/YearPicker/YearPicker.tsx
+++ b/packages/x-date-pickers/src/YearPicker/YearPicker.tsx
@@ -4,7 +4,7 @@ import clsx from 'clsx';
import { SxProps, useTheme } from '@mui/system';
import { styled, useThemeProps, Theme } from '@mui/material/styles';
import { unstable_composeClasses as composeClasses } from '@mui/material';
-import '@mui/material/utils';
+import { useForkRef } from '@mui/material/utils';
import {
unstable_useControlled as useControlled,
unstable_useEventCallback as useEventCallback,
@@ -60,7 +60,8 @@ const YearPickerRoot = styled('div', {
flexWrap: 'wrap',
overflowY: 'auto',
height: '100%',
- margin: '0 4px',
+ padding: '0 4px',
+ maxHeight: '304px',
});
export interface YearPickerProps
@@ -149,6 +150,7 @@ export const YearPicker = React.forwardRef(function YearPicker(
return utils.getYear(now);
}, [now, value, utils, disableHighlightToday]);
+
const [focusedYear, setFocusedYear] = React.useState(() => selectedYear || todayYear);
const [internalHasFocus, setInternalHasFocus] = useControlled({
@@ -243,9 +245,37 @@ export const YearPicker = React.forwardRef(function YearPicker(
}
});
+ const scrollerRef = React.useRef(null);
+ const handleRef = useForkRef(ref, scrollerRef);
+ React.useEffect(() => {
+ if (autoFocus || scrollerRef.current === null) {
+ return;
+ }
+ const tabbableButton = scrollerRef.current.querySelector('[tabindex="0"]');
+ if (!tabbableButton) {
+ return;
+ }
+
+ // Taken from useScroll in x-data-grid, but vertically centered
+ const offsetHeight = tabbableButton.offsetHeight;
+ const offsetTop = tabbableButton.offsetTop;
+
+ const clientHeight = scrollerRef.current.clientHeight;
+ const scrollTop = scrollerRef.current.scrollTop;
+
+ const elementBottom = offsetTop + offsetHeight;
+
+ if (offsetHeight > clientHeight || offsetTop < scrollTop) {
+ // Button already visible
+ return;
+ }
+
+ scrollerRef.current.scrollTop = elementBottom - clientHeight / 2 - offsetHeight / 2;
+ }, [autoFocus]);
+
return (