Skip to content

Commit

Permalink
[YearPicker] Scroll to the current year even with autoFocus=false (#…
Browse files Browse the repository at this point in the history
…6089)

Co-authored-by: Flavien DELANGLE <[email protected]>
Co-authored-by: Vytautas Butkus <[email protected]>
  • Loading branch information
3 people authored Sep 14, 2022
1 parent 9c443b2 commit 31038b4
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 7 deletions.
30 changes: 30 additions & 0 deletions packages/x-date-pickers/src/CalendarPicker/CalendarPicker.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
createPickerRenderer,
} from '../../../../test/utils/pickers-utils';

const isJSDOM = /jsdom/.test(window.navigator.userAgent);

describe('<CalendarPicker />', () => {
const { render, clock } = createPickerRenderer({ clock: 'fake' });

Expand Down Expand Up @@ -412,5 +414,33 @@ describe('<CalendarPicker />', () => {
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(
<CalendarPicker
value={adapterToUse.date(new Date(2019, 3, 29))}
onChange={() => {}}
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);
});
});
});
9 changes: 6 additions & 3 deletions packages/x-date-pickers/src/CalendarPicker/CalendarPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,7 @@ const CalendarPickerViewTransitionContainer = styled(PickersFadeTransitionGroup,
name: 'MuiCalendarPicker',
slot: 'ViewTransitionContainer',
overridesResolver: (props, styles) => styles.viewTransitionContainer,
})<{ ownerState: CalendarPickerProps<any> }>({
overflowY: 'auto',
});
})<{ ownerState: CalendarPickerProps<any> }>({});

type CalendarPickerComponent = (<TDate>(
props: CalendarPickerProps<TDate> & React.RefAttributes<HTMLDivElement>,
Expand Down Expand Up @@ -462,6 +460,11 @@ export const CalendarPicker = React.forwardRef(function CalendarPicker<TDate>(
},
);

React.useEffect(() => {
// Set focus to the button when switching from a view to another
handleFocusedViewChange(openView)(true);
}, [openView, handleFocusedViewChange]);

return (
<CalendarPickerRoot
ref={ref}
Expand Down
2 changes: 1 addition & 1 deletion packages/x-date-pickers/src/YearPicker/PickersYear.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ const PickersYearRaw = (props: PickersYearProps) => {

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.
Expand Down
36 changes: 33 additions & 3 deletions packages/x-date-pickers/src/YearPicker/YearPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<TDate>
Expand Down Expand Up @@ -149,6 +150,7 @@ export const YearPicker = React.forwardRef(function YearPicker<TDate>(

return utils.getYear(now);
}, [now, value, utils, disableHighlightToday]);

const [focusedYear, setFocusedYear] = React.useState(() => selectedYear || todayYear);

const [internalHasFocus, setInternalHasFocus] = useControlled<boolean>({
Expand Down Expand Up @@ -243,9 +245,37 @@ export const YearPicker = React.forwardRef(function YearPicker<TDate>(
}
});

const scrollerRef = React.useRef<HTMLDivElement>(null);
const handleRef = useForkRef(ref, scrollerRef);
React.useEffect(() => {
if (autoFocus || scrollerRef.current === null) {
return;
}
const tabbableButton = scrollerRef.current.querySelector<HTMLElement>('[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 (
<YearPickerRoot
ref={ref}
ref={handleRef}
className={clsx(classes.root, className)}
ownerState={ownerState}
{...other}
Expand Down

0 comments on commit 31038b4

Please sign in to comment.