Skip to content

Commit

Permalink
[pickers] Add verification to disable skipped hours in spring forward…
Browse files Browse the repository at this point in the history
… DST (#15849)

Signed-off-by: Flavien DELANGLE <flaviendelangle@gmail.com>
Co-authored-by: arthurbalduini <arthurbalduini2@gmail.com>
Co-authored-by: Michel Engelen <32863416+michelengelen@users.noreply.github.com>
  • Loading branch information
3 people authored and web-flow committed Dec 17, 2024
1 parent 761f3ec commit 86fc30e
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,11 @@ export const MultiSectionDigitalClock = React.forwardRef(function MultiSectionDi
case 'hours': {
const valueWithMeridiem = convertValueToMeridiem(rawValue, meridiemMode, ampm);
const dateWithNewHours = utils.setHours(valueOrReferenceDate, valueWithMeridiem);

if (utils.getHours(dateWithNewHours) !== valueWithMeridiem) {
return true;
}

const start = utils.setSeconds(utils.setMinutes(dateWithNewHours, 0), 0);
const end = utils.setSeconds(utils.setMinutes(dateWithNewHours, 59), 59);

Expand Down
25 changes: 16 additions & 9 deletions packages/x-date-pickers/src/TimeClock/Clock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ export interface ClockProps<TDate extends PickerValidDate>
/**
* The current full date value.
*/
value: TDate | null;
disabled?: boolean;
readOnly?: boolean;
value: PickerValidDate | null;
/**
* Minimum and maximum value of the clock.
*/
viewRange: [number, number];
className?: string;
classes?: Partial<ClockClasses>;
}
Expand Down Expand Up @@ -217,6 +219,7 @@ export function Clock<TDate extends PickerValidDate>(inProps: ClockProps<TDate>)
selectedId,
type,
viewValue,
viewRange: [minViewValue, maxViewValue],
disabled = false,
readOnly,
className,
Expand Down Expand Up @@ -309,6 +312,10 @@ export function Clock<TDate extends PickerValidDate>(inProps: ClockProps<TDate>)
}
}, [autoFocus]);

const clampValue = (newValue: number) => Math.max(minViewValue, Math.min(maxViewValue, newValue));

const circleValue = (newValue: number) => (newValue + (maxViewValue + 1)) % (maxViewValue + 1);

const handleKeyDown = (event: React.KeyboardEvent) => {
// TODO: Why this early exit?
if (isMoving.current) {
Expand All @@ -318,27 +325,27 @@ export function Clock<TDate extends PickerValidDate>(inProps: ClockProps<TDate>)
switch (event.key) {
case 'Home':
// reset both hours and minutes
handleValueChange(0, 'partial');
handleValueChange(minViewValue, 'partial');
event.preventDefault();
break;
case 'End':
handleValueChange(type === 'minutes' ? 59 : 23, 'partial');
handleValueChange(maxViewValue, 'partial');
event.preventDefault();
break;
case 'ArrowUp':
handleValueChange(viewValue + keyboardControlStep, 'partial');
handleValueChange(circleValue(viewValue + keyboardControlStep), 'partial');
event.preventDefault();
break;
case 'ArrowDown':
handleValueChange(viewValue - keyboardControlStep, 'partial');
handleValueChange(circleValue(viewValue - keyboardControlStep), 'partial');
event.preventDefault();
break;
case 'PageUp':
handleValueChange(viewValue + 5, 'partial');
handleValueChange(clampValue(viewValue + 5), 'partial');
event.preventDefault();
break;
case 'PageDown':
handleValueChange(viewValue - 5, 'partial');
handleValueChange(clampValue(viewValue - 5), 'partial');
event.preventDefault();
break;
case 'Enter':
Expand Down
27 changes: 23 additions & 4 deletions packages/x-date-pickers/src/TimeClock/TimeClock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@ export const TimeClock = React.forwardRef(function TimeClock<TDate extends Picke
case 'hours': {
const valueWithMeridiem = convertValueToMeridiem(rawValue, meridiemMode, ampm);
const dateWithNewHours = utils.setHours(valueOrReferenceDate, valueWithMeridiem);

if (utils.getHours(dateWithNewHours) !== valueWithMeridiem) {
return true;
}

const start = utils.setSeconds(utils.setMinutes(dateWithNewHours, 0), 0);
const end = utils.setSeconds(utils.setMinutes(dateWithNewHours, 59), 59);

Expand Down Expand Up @@ -246,10 +251,8 @@ export const TimeClock = React.forwardRef(function TimeClock<TDate extends Picke
],
);

const selectedId = useId();

const viewProps = React.useMemo<
Pick<ClockProps<TDate>, 'onChange' | 'viewValue' | 'children'>
Pick<ClockProps, 'onChange' | 'viewValue' | 'viewRange' | 'children'>
>(() => {
switch (view) {
case 'hours': {
Expand All @@ -262,9 +265,22 @@ export const TimeClock = React.forwardRef(function TimeClock<TDate extends Picke
);
};

const viewValue = utils.getHours(valueOrReferenceDate);

let viewRange: [number, number];
if (ampm) {
if (viewValue > 12) {
viewRange = [12, 23];
} else {
viewRange = [0, 11];
}
} else {
viewRange = [0, 23];
}

return {
onChange: handleHoursChange,
viewValue: utils.getHours(valueOrReferenceDate),
viewValue,
children: getHourNumbers({
value,
utils,
Expand All @@ -274,6 +290,7 @@ export const TimeClock = React.forwardRef(function TimeClock<TDate extends Picke
isDisabled: (hourValue) => disabled || isTimeDisabled(hourValue, 'hours'),
selectedId,
}),
viewRange,
};
}

Expand All @@ -298,6 +315,7 @@ export const TimeClock = React.forwardRef(function TimeClock<TDate extends Picke
isDisabled: (minuteValue) => disabled || isTimeDisabled(minuteValue, 'minutes'),
selectedId,
}),
viewRange: [0, 59],
};
}

Expand All @@ -322,6 +340,7 @@ export const TimeClock = React.forwardRef(function TimeClock<TDate extends Picke
isDisabled: (secondValue) => disabled || isTimeDisabled(secondValue, 'seconds'),
selectedId,
}),
viewRange: [0, 59],
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ describe('<TimeClock />', () => {

expect(handleChange.callCount).to.equal(1);
const [newDate, reason] = handleChange.firstCall.args;
expect(adapterToUse.getHours(newDate)).to.equal(3);
expect(adapterToUse.getHours(newDate)).to.equal(23);
expect(adapterToUse.getMinutes(newDate)).to.equal(20);
expect(reason).to.equal('partial');
});
Expand All @@ -174,7 +174,7 @@ describe('<TimeClock />', () => {

expect(handleChange.callCount).to.equal(1);
const [newDate, reason] = handleChange.firstCall.args;
expect(adapterToUse.getHours(newDate)).to.equal(21);
expect(adapterToUse.getHours(newDate)).to.equal(0);
expect(adapterToUse.getMinutes(newDate)).to.equal(20);
expect(reason).to.equal('partial');
});
Expand Down

0 comments on commit 86fc30e

Please sign in to comment.