,
@@ -26,26 +28,50 @@ const DesktopDateTimePicker = React.forwardRef(function DesktopDateTimePicker
>(inProps, 'MuiDesktopDateTimePicker');
- const viewRenderers: PickerViewRendererLookup = {
- day: renderDateViewCalendar,
- month: renderDateViewCalendar,
- year: renderDateViewCalendar,
- hours: null,
- minutes: null,
- seconds: null,
- ...defaultizedProps.viewRenderers,
- };
+ const timeSteps = { hours: 1, minutes: 5, seconds: 5, ...defaultizedProps.timeSteps };
+ const shouldUseNewRenderer =
+ !defaultizedProps.viewRenderers || Object.keys(defaultizedProps.viewRenderers).length === 0;
+
+ const viewRenderers: PickerViewRendererLookup =
+ // we can only ensure the expected two-column layout if none of the renderers are overridden
+ shouldUseNewRenderer
+ ? {
+ day: renderDesktopDateTimeView,
+ month: renderDesktopDateTimeView,
+ year: renderDesktopDateTimeView,
+ hours: renderDesktopDateTimeView,
+ minutes: renderDesktopDateTimeView,
+ seconds: renderDesktopDateTimeView,
+ meridiem: renderDesktopDateTimeView,
+ }
+ : {
+ day: renderDateViewCalendar,
+ month: renderDateViewCalendar,
+ year: renderDateViewCalendar,
+ hours: null,
+ minutes: null,
+ seconds: null,
+ meridiem: null,
+ ...defaultizedProps.viewRenderers,
+ };
const ampmInClock = defaultizedProps.ampmInClock ?? true;
+ // add "accept" action only when the new date time view renderers are used
+ const actionBarActions: PickersActionBarAction[] = shouldUseNewRenderer ? ['accept'] : [];
// Props with the default values specific to the desktop variant
const props = {
...defaultizedProps,
viewRenderers,
+ views: (defaultizedProps.ampm
+ ? [...defaultizedProps.views, 'meridiem']
+ : defaultizedProps.views) as DateOrTimeViewWithMeridiem[],
yearsPerRow: defaultizedProps.yearsPerRow ?? 4,
ampmInClock,
+ timeSteps,
slots: {
field: DateTimeField,
openPickerIcon: Calendar,
@@ -61,16 +87,21 @@ const DesktopDateTimePicker = React.forwardRef(function DesktopDateTimePicker({
+ const { renderPicker } = useDesktopPicker({
props,
valueManager: singleItemValueManager,
valueType: 'date-time',
@@ -318,7 +349,7 @@ DesktopDateTimePicker.propTypes = {
* Used when the component view is not controlled.
* Must be a valid option from `views` list.
*/
- openTo: PropTypes.oneOf(['day', 'hours', 'minutes', 'month', 'seconds', 'year']),
+ openTo: PropTypes.oneOf(['day', 'hours', 'meridiem', 'minutes', 'month', 'seconds', 'year']),
/**
* Force rendering in particular orientation.
*/
@@ -410,6 +441,11 @@ DesktopDateTimePicker.propTypes = {
* @default false
*/
showDaysOutsideCurrentMonth: PropTypes.bool,
+ /**
+ * If `true`, disabled digital clock items will not be rendered.
+ * @default false
+ */
+ skipDisabled: PropTypes.bool,
/**
* The props used for each component slot.
* @default {}
@@ -428,6 +464,17 @@ DesktopDateTimePicker.propTypes = {
PropTypes.func,
PropTypes.object,
]),
+ /**
+ * The time steps between two time unit options.
+ * For example, if `timeStep.minutes = 8`, then the available minute options will be `[0, 8, 16, 24, 32, 40, 48, 56]`.
+ * When single column time renderer is used, only `timeStep.minutes` will be used.
+ * @default{ hours: 1, minutes: 5, seconds: 5 }
+ */
+ timeSteps: PropTypes.shape({
+ hours: PropTypes.number,
+ minutes: PropTypes.number,
+ seconds: PropTypes.number,
+ }),
/**
* The selected value.
* Used when the component is controlled.
@@ -438,7 +485,7 @@ DesktopDateTimePicker.propTypes = {
* Used when the component view is controlled.
* Must be a valid option from `views` list.
*/
- view: PropTypes.oneOf(['day', 'hours', 'minutes', 'month', 'seconds', 'year']),
+ view: PropTypes.oneOf(['day', 'hours', 'meridiem', 'minutes', 'month', 'seconds', 'year']),
/**
* Define custom view renderers for each section.
* If `null`, the section will only have field editing.
@@ -447,6 +494,7 @@ DesktopDateTimePicker.propTypes = {
viewRenderers: PropTypes.shape({
day: PropTypes.func,
hours: PropTypes.func,
+ meridiem: PropTypes.func,
minutes: PropTypes.func,
month: PropTypes.func,
seconds: PropTypes.func,
diff --git a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.types.ts b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.types.ts
index 45c68f08a00d..04795749a5f7 100644
--- a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.types.ts
+++ b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.types.ts
@@ -11,21 +11,34 @@ import {
import { MakeOptional } from '../internals/models/helpers';
import { DateOrTimeView } from '../models';
import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration';
+import { DesktopOnlyTimePickerProps } from '../internals/models/props/clock';
+import { DateOrTimeViewWithMeridiem } from '../internals/models';
+import {
+ MultiSectionDigitalClockSlotsComponent,
+ MultiSectionDigitalClockSlotsComponentsProps,
+} from '../MultiSectionDigitalClock';
export interface DesktopDateTimePickerSlotsComponent
extends BaseDateTimePickerSlotsComponent,
MakeOptional<
- UseDesktopPickerSlotsComponent,
+ UseDesktopPickerSlotsComponent,
'Field' | 'OpenPickerIcon'
- > {}
+ >,
+ MultiSectionDigitalClockSlotsComponent {}
export interface DesktopDateTimePickerSlotsComponentsProps
extends BaseDateTimePickerSlotsComponentsProps,
- ExportedUseDesktopPickerSlotsComponentsProps {}
+ ExportedUseDesktopPickerSlotsComponentsProps,
+ MultiSectionDigitalClockSlotsComponentsProps {}
export interface DesktopDateTimePickerProps
- extends BaseDateTimePickerProps,
- DesktopOnlyPickerProps {
+ extends BaseDateTimePickerProps,
+ DesktopOnlyPickerProps,
+ Omit, 'thresholdToRenderTimeInASingleColumn'> {
+ /**
+ * Available views.
+ */
+ views?: readonly DateOrTimeView[];
/**
* Years rendered per row.
* @default 4
diff --git a/packages/x-date-pickers/src/DesktopDateTimePicker/tests/DesktopDateTimePicker.test.tsx b/packages/x-date-pickers/src/DesktopDateTimePicker/tests/DesktopDateTimePicker.test.tsx
index a93e76a74a5a..aac8ac0dfd51 100644
--- a/packages/x-date-pickers/src/DesktopDateTimePicker/tests/DesktopDateTimePicker.test.tsx
+++ b/packages/x-date-pickers/src/DesktopDateTimePicker/tests/DesktopDateTimePicker.test.tsx
@@ -33,7 +33,7 @@ describe('', () => {
onChange={onChange}
onAccept={onAccept}
onClose={onClose}
- defaultValue={adapterToUse.date(new Date(2018, 0, 1, 11, 53))}
+ defaultValue={adapterToUse.date(new Date(2018, 0, 1, 11, 55))}
openTo="year"
/>,
);
@@ -43,14 +43,23 @@ describe('', () => {
// Select year
userEvent.mousePress(screen.getByRole('button', { name: '2025' }));
expect(onChange.callCount).to.equal(1);
- expect(onChange.lastCall.args[0]).toEqualDateTime(new Date(2025, 0, 1, 11, 53));
+ expect(onChange.lastCall.args[0]).toEqualDateTime(new Date(2025, 0, 1, 11, 55));
expect(onAccept.callCount).to.equal(0);
expect(onClose.callCount).to.equal(0);
// Change the date (same value)
userEvent.mousePress(screen.getByRole('gridcell', { name: '1' }));
expect(onChange.callCount).to.equal(1); // Don't call onChange again since the value did not change
- expect(onAccept.callCount).to.equal(0);
+ // Change the hours (same value)
+ userEvent.mousePress(screen.getByRole('option', { name: '11 hours' }));
+ expect(onChange.callCount).to.equal(1); // Don't call onChange again since the value did not change
+ // Change the minutes (same value)
+ userEvent.mousePress(screen.getByRole('option', { name: '55 minutes' }));
+ expect(onChange.callCount).to.equal(1); // Don't call onChange again since the value did not change
+ // Change the meridiem (same value)
+ userEvent.mousePress(screen.getByRole('option', { name: 'AM' }));
+ expect(onChange.callCount).to.equal(1); // Don't call onChange again since the value did not change
+ expect(onAccept.callCount).to.equal(1);
expect(onClose.callCount).to.equal(1);
});
});
diff --git a/packages/x-date-pickers/src/DesktopDateTimePicker/tests/describes.DesktopDateTimePicker.test.tsx b/packages/x-date-pickers/src/DesktopDateTimePicker/tests/describes.DesktopDateTimePicker.test.tsx
index 94141841a484..0b009f7be51e 100644
--- a/packages/x-date-pickers/src/DesktopDateTimePicker/tests/describes.DesktopDateTimePicker.test.tsx
+++ b/packages/x-date-pickers/src/DesktopDateTimePicker/tests/describes.DesktopDateTimePicker.test.tsx
@@ -29,11 +29,10 @@ describe(' - Describes', () => {
componentFamily: 'picker',
type: 'date-time',
variant: 'desktop',
- defaultProps: {
- views: ['day'],
- openTo: 'day',
- },
- values: [adapterToUse.date(new Date(2018, 0, 1)), adapterToUse.date(new Date(2018, 0, 2))],
+ values: [
+ adapterToUse.date(new Date(2018, 0, 1, 11, 30)),
+ adapterToUse.date(new Date(2018, 0, 2, 12, 35)),
+ ],
emptyValue: null,
clock,
assertRenderedValue: (expectedValue: any) => {
@@ -52,16 +51,51 @@ describe(' - Describes', () => {
expectInputValue(input, expectedValueStr);
},
setNewValue: (value, { isOpened, applySameValue, selectSection }) => {
- const newValue = applySameValue ? value : adapterToUse.addDays(value, 1);
+ const newValue = applySameValue
+ ? value
+ : adapterToUse.addMinutes(adapterToUse.addHours(adapterToUse.addDays(value, 1), 1), 5);
if (isOpened) {
userEvent.mousePress(
screen.getByRole('gridcell', { name: adapterToUse.getDate(newValue).toString() }),
);
+ const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale();
+ const hours = adapterToUse.format(newValue, hasMeridiem ? 'hours12h' : 'hours24h');
+ const hoursNumber = adapterToUse.getHours(newValue);
+ userEvent.mousePress(screen.getByRole('option', { name: `${parseInt(hours, 10)} hours` }));
+ userEvent.mousePress(
+ screen.getByRole('option', { name: `${adapterToUse.getMinutes(newValue)} minutes` }),
+ );
+ if (hasMeridiem) {
+ // meridiem is an extra view on `DesktopDateTimePicker`
+ // we need to click it to finish selection
+ userEvent.mousePress(
+ screen.getByRole('option', { name: hoursNumber >= 12 ? 'PM' : 'AM' }),
+ );
+ }
} else {
selectSection('day');
const input = getTextbox();
userEvent.keyPress(input, { key: 'ArrowUp' });
+ // move to the hours section
+ userEvent.keyPress(input, { key: 'ArrowRight' });
+ userEvent.keyPress(input, { key: 'ArrowRight' });
+ userEvent.keyPress(input, { key: 'ArrowUp' });
+ // move to the minutes section
+ userEvent.keyPress(input, { key: 'ArrowRight' });
+ // increment by 5 minutes
+ userEvent.keyPress(input, { key: 'PageUp' });
+ const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale();
+ if (hasMeridiem) {
+ // move to the meridiem section
+ userEvent.keyPress(input, { key: 'ArrowRight' });
+ const previousHours = adapterToUse.getHours(value);
+ const newHours = adapterToUse.getHours(newValue);
+ // update meridiem section if it changed
+ if ((previousHours < 12 && newHours >= 12) || (previousHours >= 12 && newHours < 12)) {
+ userEvent.keyPress(input, { key: 'ArrowUp' });
+ }
+ }
}
return newValue;
diff --git a/packages/x-date-pickers/src/DesktopTimePicker/tests/describes.DesktopTimePicker.test.tsx b/packages/x-date-pickers/src/DesktopTimePicker/tests/describes.DesktopTimePicker.test.tsx
index 265c5be72fbf..77cca31430e5 100644
--- a/packages/x-date-pickers/src/DesktopTimePicker/tests/describes.DesktopTimePicker.test.tsx
+++ b/packages/x-date-pickers/src/DesktopTimePicker/tests/describes.DesktopTimePicker.test.tsx
@@ -54,12 +54,9 @@ describe(' - Describes', () => {
componentFamily: 'picker',
type: 'time',
variant: 'desktop',
- defaultProps: {
- views: ['hours'],
- },
values: [
- adapterToUse.date(new Date(2018, 0, 1, 15, 30)),
- adapterToUse.date(new Date(2018, 0, 1, 18, 30)),
+ adapterToUse.date(new Date(2018, 0, 1, 11, 30)),
+ adapterToUse.date(new Date(2018, 0, 1, 12, 35)),
],
emptyValue: null,
clock,
@@ -77,13 +74,18 @@ describe(' - Describes', () => {
);
},
setNewValue: (value, { isOpened, applySameValue, selectSection }) => {
- const newValue = applySameValue ? value : adapterToUse.addHours(value, 1);
+ const newValue = applySameValue
+ ? value
+ : adapterToUse.addMinutes(adapterToUse.addHours(value, 1), 5);
if (isOpened) {
const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale();
const hours = adapterToUse.format(newValue, hasMeridiem ? 'hours12h' : 'hours24h');
const hoursNumber = adapterToUse.getHours(newValue);
userEvent.mousePress(screen.getByRole('option', { name: `${parseInt(hours, 10)} hours` }));
+ userEvent.mousePress(
+ screen.getByRole('option', { name: `${adapterToUse.getMinutes(newValue)} minutes` }),
+ );
if (hasMeridiem) {
// meridiem is an extra view on `DesktopTimePicker`
// we need to click it to finish selection
@@ -95,6 +97,21 @@ describe(' - Describes', () => {
selectSection('hours');
const input = getTextbox();
userEvent.keyPress(input, { key: 'ArrowUp' });
+ // move to the minutes section
+ userEvent.keyPress(input, { key: 'ArrowRight' });
+ // increment by 5 minutes
+ userEvent.keyPress(input, { key: 'PageUp' });
+ const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale();
+ if (hasMeridiem) {
+ // move to the meridiem section
+ userEvent.keyPress(input, { key: 'ArrowRight' });
+ const previousHours = adapterToUse.getHours(value);
+ const newHours = adapterToUse.getHours(newValue);
+ // update meridiem section if it changed
+ if ((previousHours < 12 && newHours >= 12) || (previousHours >= 12 && newHours < 12)) {
+ userEvent.keyPress(input, { key: 'ArrowUp' });
+ }
+ }
}
return newValue;
diff --git a/packages/x-date-pickers/src/DigitalClock/tests/describes.DigitalClock.test.tsx b/packages/x-date-pickers/src/DigitalClock/tests/describes.DigitalClock.test.tsx
index b3c358217efb..ecc047d8aea4 100644
--- a/packages/x-date-pickers/src/DigitalClock/tests/describes.DigitalClock.test.tsx
+++ b/packages/x-date-pickers/src/DigitalClock/tests/describes.DigitalClock.test.tsx
@@ -62,14 +62,15 @@ describe(' - Describes', () => {
}
},
setNewValue: (value) => {
+ const newValue = adapterToUse.addMinutes(adapterToUse.addHours(value, 1), 30);
const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale();
const formattedLabel = adapterToUse.format(
- value,
+ newValue,
hasMeridiem ? 'fullTime12h' : 'fullTime24h',
);
userEvent.mousePress(screen.getByRole('option', { name: formattedLabel }));
- return value;
+ return newValue;
},
}));
});
diff --git a/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx b/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx
index 3d4cea70370c..490dd5369d7e 100644
--- a/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx
+++ b/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx
@@ -25,6 +25,7 @@ const MobileDateTimePicker = React.forwardRef(function MobileDateTimePicker
>(inProps, 'MuiMobileDateTimePicker');
diff --git a/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.types.ts b/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.types.ts
index 29e4e02019f7..61be2967f7a7 100644
--- a/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.types.ts
+++ b/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.types.ts
@@ -11,38 +11,45 @@ import {
import { MakeOptional } from '../internals/models/helpers';
import { DateOrTimeView } from '../models';
import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration';
+import { DateOrTimeViewWithMeridiem } from '../internals/models';
-export interface MobileDateTimePickerSlotsComponent
- extends BaseDateTimePickerSlotsComponent,
- MakeOptional, 'Field'> {}
+export interface MobileDateTimePickerSlotsComponent<
+ TDate,
+ TView extends DateOrTimeViewWithMeridiem = DateOrTimeView,
+> extends BaseDateTimePickerSlotsComponent,
+ MakeOptional, 'Field'> {}
-export interface MobileDateTimePickerSlotsComponentsProps
- extends BaseDateTimePickerSlotsComponentsProps,
- ExportedUseMobilePickerSlotsComponentsProps {}
+export interface MobileDateTimePickerSlotsComponentsProps<
+ TDate,
+ TView extends DateOrTimeViewWithMeridiem = DateOrTimeView,
+> extends BaseDateTimePickerSlotsComponentsProps,
+ ExportedUseMobilePickerSlotsComponentsProps {}
-export interface MobileDateTimePickerProps
- extends BaseDateTimePickerProps,
+export interface MobileDateTimePickerProps<
+ TDate,
+ TView extends DateOrTimeViewWithMeridiem = DateOrTimeView,
+> extends BaseDateTimePickerProps,
MobileOnlyPickerProps {
/**
* Overridable components.
* @default {}
* @deprecated Please use `slots`.
*/
- components?: MobileDateTimePickerSlotsComponent;
+ components?: MobileDateTimePickerSlotsComponent;
/**
* The props used for each component slot.
* @default {}
* @deprecated Please use `slotProps`.
*/
- componentsProps?: MobileDateTimePickerSlotsComponentsProps;
+ componentsProps?: MobileDateTimePickerSlotsComponentsProps;
/**
* Overridable component slots.
* @default {}
*/
- slots?: UncapitalizeObjectKeys>;
+ slots?: UncapitalizeObjectKeys>;
/**
* The props used for each component slot.
* @default {}
*/
- slotProps?: MobileDateTimePickerSlotsComponentsProps;
+ slotProps?: MobileDateTimePickerSlotsComponentsProps;
}
diff --git a/packages/x-date-pickers/src/MobileDateTimePicker/tests/describes.MobileDateTimePicker.test.tsx b/packages/x-date-pickers/src/MobileDateTimePicker/tests/describes.MobileDateTimePicker.test.tsx
index f2a14eb63407..53aa9d381a21 100644
--- a/packages/x-date-pickers/src/MobileDateTimePicker/tests/describes.MobileDateTimePicker.test.tsx
+++ b/packages/x-date-pickers/src/MobileDateTimePicker/tests/describes.MobileDateTimePicker.test.tsx
@@ -34,11 +34,11 @@ describe(' - Describes', () => {
componentFamily: 'picker',
type: 'date-time',
variant: 'mobile',
- defaultProps: {
- openTo: 'minutes',
- },
clock,
- values: [adapterToUse.date(new Date(2018, 0, 1)), adapterToUse.date(new Date(2018, 0, 2))],
+ values: [
+ adapterToUse.date(new Date(2018, 0, 1, 11, 30)),
+ adapterToUse.date(new Date(2018, 0, 2, 12, 35)),
+ ],
emptyValue: null,
assertRenderedValue: (expectedValue: any) => {
const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale();
@@ -57,18 +57,41 @@ describe(' - Describes', () => {
},
setNewValue: (value, { isOpened, applySameValue }) => {
if (!isOpened) {
- openPicker({ type: 'time', variant: 'mobile' });
+ openPicker({ type: 'date-time', variant: 'mobile' });
}
- const newValue = applySameValue ? value : adapterToUse.addMinutes(value, 1);
- const hourClockEvent = getClockTouchEvent(adapterToUse.getMinutes(newValue), 'minutes');
+ const newValue = applySameValue
+ ? value
+ : adapterToUse.addMinutes(adapterToUse.addHours(adapterToUse.addDays(value, 1), 1), 5);
+ userEvent.mousePress(
+ screen.getByRole('gridcell', { name: adapterToUse.getDate(newValue).toString() }),
+ );
+ const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale();
+ // change hours
+ const hourClockEvent = getClockTouchEvent(
+ adapterToUse.getHours(newValue),
+ hasMeridiem ? '12hours' : '24hours',
+ );
fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchmove', hourClockEvent);
fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchend', hourClockEvent);
+ // change minutes
+ const minutesClockEvent = getClockTouchEvent(adapterToUse.getMinutes(newValue), 'minutes');
+ fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchmove', minutesClockEvent);
+ fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchend', minutesClockEvent);
+
+ if (hasMeridiem) {
+ const newHours = adapterToUse.getHours(newValue);
+ // select appropriate meridiem
+ userEvent.mousePress(screen.getByRole('button', { name: newHours >= 12 ? 'PM' : 'AM' }));
+ }
- // Close the picker to return to the initial state
+ // Close the picker
if (!isOpened) {
userEvent.keyPress(document.activeElement!, { key: 'Escape' });
clock.runToLast();
+ } else {
+ // return to the date view in case we'd like to repeat the selection process
+ userEvent.mousePress(screen.getByRole('tab', { name: 'pick date' }));
}
return newValue;
diff --git a/packages/x-date-pickers/src/MobileTimePicker/tests/describes.MobileTimePicker.test.tsx b/packages/x-date-pickers/src/MobileTimePicker/tests/describes.MobileTimePicker.test.tsx
index f9b385141ece..56b4090585ce 100644
--- a/packages/x-date-pickers/src/MobileTimePicker/tests/describes.MobileTimePicker.test.tsx
+++ b/packages/x-date-pickers/src/MobileTimePicker/tests/describes.MobileTimePicker.test.tsx
@@ -61,13 +61,10 @@ describe(' - Describes', () => {
type: 'time',
variant: 'mobile',
values: [
- adapterToUse.date(new Date(2018, 0, 1, 15, 30)),
- adapterToUse.date(new Date(2018, 0, 1, 18, 30)),
+ adapterToUse.date(new Date(2018, 0, 1, 11, 30)),
+ adapterToUse.date(new Date(2018, 0, 1, 12, 35)),
],
emptyValue: null,
- defaultProps: {
- openTo: 'minutes',
- },
clock,
assertRenderedValue: (expectedValue: any) => {
const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale();
@@ -86,15 +83,35 @@ describe(' - Describes', () => {
openPicker({ type: 'time', variant: 'mobile' });
}
- const newValue = applySameValue ? value : adapterToUse.addMinutes(value, 1);
- const hourClockEvent = getClockTouchEvent(adapterToUse.getMinutes(newValue), 'minutes');
+ const newValue = applySameValue
+ ? value
+ : adapterToUse.addMinutes(adapterToUse.addHours(value, 1), 5);
+ const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale();
+ // change hours
+ const hourClockEvent = getClockTouchEvent(
+ adapterToUse.getHours(newValue),
+ hasMeridiem ? '12hours' : '24hours',
+ );
fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchmove', hourClockEvent);
fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchend', hourClockEvent);
+ // change minutes
+ const minutesClockEvent = getClockTouchEvent(adapterToUse.getMinutes(newValue), 'minutes');
+ fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchmove', minutesClockEvent);
+ fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchend', minutesClockEvent);
+
+ if (hasMeridiem) {
+ const newHours = adapterToUse.getHours(newValue);
+ // select appropriate meridiem
+ userEvent.mousePress(screen.getByRole('button', { name: newHours >= 12 ? 'PM' : 'AM' }));
+ }
- // Close the picker to return to the initial state
+ // Close the picker
if (!isOpened) {
userEvent.keyPress(document.activeElement!, { key: 'Escape' });
clock.runToLast();
+ } else {
+ // return to the hours view in case we'd like to repeat the selection process
+ userEvent.mousePress(screen.getByRole('button', { name: 'open previous view' }));
}
return newValue;
diff --git a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx
index f6b032929f39..7d52f4466f9a 100644
--- a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx
+++ b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx
@@ -15,7 +15,10 @@ import type {
MultiSectionDigitalClockSlotsComponentsProps,
} from './MultiSectionDigitalClock.types';
import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration';
-import { DIGITAL_CLOCK_VIEW_HEIGHT } from '../internals/constants/dimensions';
+import {
+ DIGITAL_CLOCK_VIEW_HEIGHT,
+ MULTI_SECTION_CLOCK_SECTION_WIDTH,
+} from '../internals/constants/dimensions';
export interface ExportedMultiSectionDigitalClockSectionProps {
className?: string;
@@ -66,7 +69,8 @@ const MultiSectionDigitalClockSectionRoot = styled(MenuList, {
'&:after': {
display: 'block',
content: '""',
- height: 188,
+ // subtracting the height of one item, extra margin and borders to make sure the max height is correct
+ height: 'calc(100% - 40px - 6px)',
},
}),
);
@@ -78,7 +82,7 @@ const MultiSectionDigitalClockSectionItem = styled(MenuItem, {
})(({ theme }) => ({
padding: 8,
margin: '2px 4px',
- width: 48,
+ width: MULTI_SECTION_CLOCK_SECTION_WIDTH,
justifyContent: 'center',
'&:first-of-type': {
marginTop: 4,
diff --git a/packages/x-date-pickers/src/MultiSectionDigitalClock/tests/describes.MultiSectionDigitalClock.test.tsx b/packages/x-date-pickers/src/MultiSectionDigitalClock/tests/describes.MultiSectionDigitalClock.test.tsx
index 02cb357fb8cb..205470e0d858 100644
--- a/packages/x-date-pickers/src/MultiSectionDigitalClock/tests/describes.MultiSectionDigitalClock.test.tsx
+++ b/packages/x-date-pickers/src/MultiSectionDigitalClock/tests/describes.MultiSectionDigitalClock.test.tsx
@@ -42,8 +42,8 @@ describe(' - Describes', () => {
type: 'time',
variant: 'desktop',
values: [
- adapterToUse.date(new Date(2018, 0, 1, 15, 30)),
- adapterToUse.date(new Date(2018, 0, 1, 16, 15)),
+ adapterToUse.date(new Date(2018, 0, 1, 11, 30)),
+ adapterToUse.date(new Date(2018, 0, 1, 12, 35)),
],
emptyValue: null,
clock,
@@ -62,29 +62,30 @@ describe(' - Describes', () => {
expect(selectedItems[1]).to.have.text(minutesLabel);
if (hasMeridiem) {
expect(selectedItems[2]).to.have.text(
- adapterToUse.getMeridiemText(adapterToUse.getHours(expectedValue) > 12 ? 'pm' : 'am'),
+ adapterToUse.getMeridiemText(adapterToUse.getHours(expectedValue) >= 12 ? 'pm' : 'am'),
);
}
}
},
setNewValue: (value) => {
+ const newValue = adapterToUse.addMinutes(adapterToUse.addHours(value, 1), 5);
const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale();
const hoursLabel = parseInt(
- adapterToUse.format(value, hasMeridiem ? 'hours12h' : 'hours24h'),
+ adapterToUse.format(newValue, hasMeridiem ? 'hours12h' : 'hours24h'),
10,
);
- const minutesLabel = adapterToUse.getMinutes(value).toString();
+ const minutesLabel = adapterToUse.getMinutes(newValue).toString();
userEvent.mousePress(screen.getByRole('option', { name: `${hoursLabel} hours` }));
userEvent.mousePress(screen.getByRole('option', { name: `${minutesLabel} minutes` }));
if (hasMeridiem) {
userEvent.mousePress(
screen.getByRole('option', {
- name: adapterToUse.getMeridiemText(adapterToUse.getHours(value) > 12 ? 'pm' : 'am'),
+ name: adapterToUse.getMeridiemText(adapterToUse.getHours(newValue) >= 12 ? 'pm' : 'am'),
}),
);
}
- return value;
+ return newValue;
},
}));
});
diff --git a/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.tsx b/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.tsx
index 22bb0b0cd4b0..439865c30d8d 100644
--- a/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.tsx
+++ b/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.tsx
@@ -20,6 +20,7 @@ const StaticDateTimePicker = React.forwardRef(function StaticDateTimePicker
>(inProps, 'MuiStaticDateTimePicker');
diff --git a/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.types.ts b/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.types.ts
index f32150d6c35e..8ac7948fd2e4 100644
--- a/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.types.ts
+++ b/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.types.ts
@@ -20,7 +20,7 @@ export interface StaticDateTimePickerSlotsComponentsProps
UseStaticPickerSlotsComponentsProps {}
export interface StaticDateTimePickerProps
- extends BaseDateTimePickerProps,
+ extends BaseDateTimePickerProps,
MakeOptional {
/**
* Overridable components.
diff --git a/packages/x-date-pickers/src/TimeClock/tests/describes.TimeClock.test.tsx b/packages/x-date-pickers/src/TimeClock/tests/describes.TimeClock.test.tsx
index 9938e53d561a..12fb9ce5cf07 100644
--- a/packages/x-date-pickers/src/TimeClock/tests/describes.TimeClock.test.tsx
+++ b/packages/x-date-pickers/src/TimeClock/tests/describes.TimeClock.test.tsx
@@ -31,13 +31,10 @@ describe(' - Describes', () => {
render,
componentFamily: 'clock',
values: [
- adapterToUse.date(new Date(2018, 0, 1, 15, 30)),
- adapterToUse.date(new Date(2018, 0, 1, 18, 30)),
+ adapterToUse.date(new Date(2018, 0, 1, 12, 30)),
+ adapterToUse.date(new Date(2018, 0, 1, 13, 35)),
],
emptyValue: null,
- defaultProps: {
- openTo: 'minutes',
- },
clock,
assertRenderedValue: (expectedValue: any) => {
const clockPointer = document.querySelector(`.${clockPointerClasses.root}`);
@@ -45,14 +42,32 @@ describe(' - Describes', () => {
expect(clockPointer).to.equal(null);
} else {
const transform = clockPointer?.style?.transform;
- expect(transform).to.equal(`rotateZ(${adapterToUse.getMinutes(expectedValue) * 6}deg)`);
+ const isMinutesView = screen
+ .getByRole('listbox')
+ .getAttribute('aria-label')
+ ?.includes('minutes');
+ if (isMinutesView) {
+ expect(transform).to.equal(`rotateZ(${adapterToUse.getMinutes(expectedValue) * 6}deg)`);
+ } else {
+ const hours = adapterToUse.getHours(expectedValue);
+ expect(transform).to.equal(`rotateZ(${(hours > 12 ? hours % 12 : hours) * 30}deg)`);
+ }
}
},
setNewValue: (value) => {
- const newValue = adapterToUse.addMinutes(value, 1);
- const hourClockEvent = getClockTouchEvent(adapterToUse.getMinutes(newValue), 'minutes');
+ const newValue = adapterToUse.addMinutes(adapterToUse.addHours(value, 1), 5);
+ const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale();
+ // change hours
+ const hourClockEvent = getClockTouchEvent(
+ adapterToUse.getHours(newValue),
+ hasMeridiem ? '12hours' : '24hours',
+ );
fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchmove', hourClockEvent);
fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchend', hourClockEvent);
+ // change minutes
+ const minutesClockEvent = getClockTouchEvent(adapterToUse.getMinutes(newValue), 'minutes');
+ fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchmove', minutesClockEvent);
+ fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchend', minutesClockEvent);
return newValue;
},
diff --git a/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx b/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx
new file mode 100644
index 000000000000..de38812ef2b9
--- /dev/null
+++ b/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx
@@ -0,0 +1,181 @@
+import * as React from 'react';
+import Divider from '@mui/material/Divider';
+import { resolveComponentProps } from '@mui/base/utils';
+import { DateCalendar, DateCalendarProps } from '../DateCalendar';
+import { DateOrTimeViewWithMeridiem } from '../internals/models';
+import {
+ MultiSectionDigitalClock,
+ MultiSectionDigitalClockProps,
+ multiSectionDigitalClockSectionClasses,
+} from '../MultiSectionDigitalClock';
+import { DateTimeViewWrapper } from '../internals/components/DateTimeViewWrapper';
+import { isInternalTimeView } from '../internals/utils/time-utils';
+import { isDatePickerView } from '../internals/utils/date-utils';
+import type { DateTimePickerProps } from '../DateTimePicker/DateTimePicker.types';
+
+export interface DateTimeViewRendererProps
+ extends Omit<
+ DateCalendarProps & MultiSectionDigitalClockProps,
+ | 'views'
+ | 'openTo'
+ | 'view'
+ | 'onViewChange'
+ | 'focusedView'
+ | 'components'
+ | 'componentsProps'
+ | 'slots'
+ | 'slotProps'
+ >,
+ Pick, 'components' | 'componentsProps' | 'slots' | 'slotProps'> {
+ view: DateOrTimeViewWithMeridiem;
+ onViewChange?: (view: DateOrTimeViewWithMeridiem) => void;
+ views: readonly DateOrTimeViewWithMeridiem[];
+ focusedView: DateOrTimeViewWithMeridiem | null;
+ timeViewsCount: number;
+}
+
+export const renderDesktopDateTimeView = ({
+ view,
+ onViewChange,
+ views,
+ focusedView,
+ onFocusedViewChange,
+ value,
+ defaultValue,
+ onChange,
+ className,
+ classes,
+ disableFuture,
+ disablePast,
+ minDate,
+ minTime,
+ maxDate,
+ maxTime,
+ shouldDisableDate,
+ shouldDisableMonth,
+ shouldDisableYear,
+ shouldDisableTime,
+ shouldDisableClock,
+ reduceAnimations,
+ minutesStep,
+ ampm,
+ onMonthChange,
+ monthsPerRow,
+ onYearChange,
+ yearsPerRow,
+ defaultCalendarMonth,
+ components,
+ componentsProps,
+ slots,
+ slotProps,
+ loading,
+ renderLoading,
+ disableHighlightToday,
+ readOnly,
+ disabled,
+ showDaysOutsideCurrentMonth,
+ dayOfWeekFormatter,
+ sx,
+ autoFocus,
+ fixedWeekNumber,
+ displayWeekNumber,
+ disableIgnoringDatePartForTimeValidation,
+ timeSteps,
+ skipDisabled,
+ timeViewsCount,
+}: DateTimeViewRendererProps) => {
+ const isActionBarVisible = !!resolveComponentProps(
+ slotProps?.actionBar ?? componentsProps?.actionBar,
+ {} as any,
+ )?.actions?.length;
+ return (
+
+
+
+ {timeViewsCount > 0 && (
+
+
+
+
+ )}
+
+ {isActionBarVisible && }
+
+ );
+};
diff --git a/packages/x-date-pickers/src/dateTimeViewRenderers/index.ts b/packages/x-date-pickers/src/dateTimeViewRenderers/index.ts
new file mode 100644
index 000000000000..d909595c47ae
--- /dev/null
+++ b/packages/x-date-pickers/src/dateTimeViewRenderers/index.ts
@@ -0,0 +1,2 @@
+export { renderDesktopDateTimeView } from './dateTimeViewRenderers';
+export type { DateTimeViewRendererProps } from './dateTimeViewRenderers';
diff --git a/packages/x-date-pickers/src/dateViewRenderers/dateViewRenderers.tsx b/packages/x-date-pickers/src/dateViewRenderers/dateViewRenderers.tsx
index f5e73ed1d6ec..0a6a23a64507 100644
--- a/packages/x-date-pickers/src/dateViewRenderers/dateViewRenderers.tsx
+++ b/packages/x-date-pickers/src/dateViewRenderers/dateViewRenderers.tsx
@@ -2,9 +2,7 @@ import * as React from 'react';
import { DateCalendar, DateCalendarProps } from '../DateCalendar';
import { DateView } from '../models';
import { DateOrTimeViewWithMeridiem } from '../internals/models';
-
-const isDatePickerView = (view: unknown): view is DateView =>
- view === 'year' || view === 'month' || view === 'day';
+import { isDatePickerView } from '../internals/utils/date-utils';
export interface DateViewRendererProps
extends Omit<
@@ -56,12 +54,12 @@ export const renderDateViewCalendar = ({
autoFocus,
fixedWeekNumber,
displayWeekNumber,
-}: DateViewRendererProps) => (
+}: DateViewRendererProps) => (
;
+ width?: number;
}
const useUtilityClasses = (ownerState: PickersToolbarButtonProps) => {
@@ -40,7 +41,8 @@ const PickersToolbarButtonRoot = styled(Button, {
export const PickersToolbarButton: React.FunctionComponent =
React.forwardRef(function PickersToolbarButton(inProps, ref) {
const props = useThemeProps({ props: inProps, name: 'MuiPickersToolbarButton' });
- const { align, className, selected, typographyClassName, value, variant, ...other } = props;
+ const { align, className, selected, typographyClassName, value, variant, width, ...other } =
+ props;
const classes = useUtilityClasses(props);
@@ -50,6 +52,7 @@ export const PickersToolbarButton: React.FunctionComponent
{
- const numberUITimeViews = views.reduce((acc, viewForReduce) => {
- if (viewRenderers[viewForReduce] != null && isTimeView(viewForReduce)) {
- return acc + 1;
- }
- return acc;
- }, 0);
-
- return numberUITimeViews > 1;
- }, [viewRenderers, views]);
+ const timeViewsCount = React.useMemo(
+ () =>
+ views.reduce((acc, viewForReduce) => {
+ if (viewRenderers[viewForReduce] != null && isTimeView(viewForReduce)) {
+ return acc + 1;
+ }
+ return acc;
+ }, 0),
+ [viewRenderers, views],
+ );
const currentViewMode = viewModeLookup[view];
const shouldRestoreFocus = useEventCallback(() => currentViewMode === 'UI');
@@ -270,7 +270,8 @@ export const usePickerViews = <
onViewChange: setView,
focusedView,
onFocusedViewChange: setFocusedView,
- showViewSwitcher: hasMultipleUITimeView,
+ showViewSwitcher: timeViewsCount > 1,
+ timeViewsCount,
});
},
};
diff --git a/packages/x-date-pickers/src/internals/hooks/useViews.tsx b/packages/x-date-pickers/src/internals/hooks/useViews.tsx
index 649f8ee37a22..1555a56a7e96 100644
--- a/packages/x-date-pickers/src/internals/hooks/useViews.tsx
+++ b/packages/x-date-pickers/src/internals/hooks/useViews.tsx
@@ -147,17 +147,6 @@ export function useViews({
const previousView: TView | null = views[viewIndex - 1] ?? null;
const nextView: TView | null = views[viewIndex + 1] ?? null;
- const handleChangeView = useEventCallback((newView: TView) => {
- if (newView === view) {
- return;
- }
- setView(newView);
-
- if (onViewChange) {
- onViewChange(newView);
- }
- });
-
const handleFocusedViewChange = useEventCallback((viewToFocus: TView, hasFocus: boolean) => {
if (hasFocus) {
// Focus event
@@ -172,6 +161,17 @@ export function useViews({
onFocusedViewChange?.(viewToFocus, hasFocus);
});
+ const handleChangeView = useEventCallback((newView: TView) => {
+ if (newView === view) {
+ return;
+ }
+ setView(newView);
+ handleFocusedViewChange(newView, true);
+
+ if (onViewChange) {
+ onViewChange(newView);
+ }
+ });
const goToNextView = useEventCallback(() => {
if (nextView) {
handleChangeView(nextView);
diff --git a/packages/x-date-pickers/src/internals/utils/date-utils.ts b/packages/x-date-pickers/src/internals/utils/date-utils.ts
index 21e1f29acdc5..a9edff13431a 100644
--- a/packages/x-date-pickers/src/internals/utils/date-utils.ts
+++ b/packages/x-date-pickers/src/internals/utils/date-utils.ts
@@ -1,4 +1,5 @@
-import { FieldValueType, MuiPickersAdapter } from '../../models';
+import { DateView, FieldValueType, MuiPickersAdapter } from '../../models';
+import { DateOrTimeViewWithMeridiem } from '../models';
interface FindClosestDateParams {
date: TDate;
@@ -137,3 +138,7 @@ export const mergeDateAndTime = (
export const getTodayDate = (utils: MuiPickersAdapter, valueType: FieldValueType) =>
valueType === 'date' ? utils.startOfDay(utils.date()!) : utils.date()!;
+
+const dateViews = ['year', 'month', 'day'];
+export const isDatePickerView = (view: DateOrTimeViewWithMeridiem): view is DateView =>
+ dateViews.includes(view);
diff --git a/packages/x-date-pickers/src/internals/utils/time-utils.ts b/packages/x-date-pickers/src/internals/utils/time-utils.ts
index 7710220e78ab..877632c33769 100644
--- a/packages/x-date-pickers/src/internals/utils/time-utils.ts
+++ b/packages/x-date-pickers/src/internals/utils/time-utils.ts
@@ -1,9 +1,13 @@
import { MuiPickersAdapter } from '../../models';
-import { DateOrTimeViewWithMeridiem } from '../models';
+import { DateOrTimeViewWithMeridiem, TimeViewWithMeridiem } from '../models';
const timeViews = ['hours', 'minutes', 'seconds'];
export const isTimeView = (view: DateOrTimeViewWithMeridiem) => timeViews.includes(view);
+export const isInternalTimeView = (
+ view: DateOrTimeViewWithMeridiem,
+): view is TimeViewWithMeridiem => timeViews.includes(view) || view === 'meridiem';
+
export type Meridiem = 'am' | 'pm';
export const getMeridiem = (
diff --git a/packages/x-date-pickers/src/tests/describeValidation/testDayViewValidation.tsx b/packages/x-date-pickers/src/tests/describeValidation/testDayViewValidation.tsx
index 555445d09467..1268da250106 100644
--- a/packages/x-date-pickers/src/tests/describeValidation/testDayViewValidation.tsx
+++ b/packages/x-date-pickers/src/tests/describeValidation/testDayViewValidation.tsx
@@ -17,11 +17,7 @@ export const testDayViewValidation: DescribeValidationTestSuite = (ElementToTest
open: true,
view: 'day',
reduceAnimations: true,
- ...(componentFamily.includes('legacy-')
- ? {
- componentsProps: { toolbar: { hidden: true } },
- }
- : { slotProps: { toolbar: { hidden: true } } }),
+ slotProps: { toolbar: { hidden: true } },
};
it('should apply shouldDisableDate', function test() {
@@ -35,10 +31,10 @@ export const testDayViewValidation: DescribeValidationTestSuite = (ElementToTest
/>,
);
- expect(screen.getByText('9')).not.to.have.attribute('disabled');
- expect(screen.getByText('10')).not.to.have.attribute('disabled');
- expect(screen.getByText('11')).to.have.attribute('disabled');
- expect(screen.getByText('12')).to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '9' })).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '10' })).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '11' })).to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '12' })).to.have.attribute('disabled');
});
it('should apply shouldDisableYear', function test() {
@@ -50,16 +46,16 @@ export const testDayViewValidation: DescribeValidationTestSuite = (ElementToTest
/>,
);
- expect(screen.getByText('1')).to.have.attribute('disabled');
- expect(screen.getByText('15')).to.have.attribute('disabled');
- expect(screen.getByText('30')).to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '1' })).to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '15' })).to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '30' })).to.have.attribute('disabled');
setProps({ value: adapterToUse.date(new Date(2019, 0, 1)) });
clock.runToLast();
- expect(screen.getByText('1')).not.to.have.attribute('disabled');
- expect(screen.getByText('15')).not.to.have.attribute('disabled');
- expect(screen.getByText('30')).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '1' })).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '15' })).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '30' })).not.to.have.attribute('disabled');
});
it('should apply shouldDisableMonth', function test() {
@@ -71,16 +67,16 @@ export const testDayViewValidation: DescribeValidationTestSuite = (ElementToTest
/>,
);
- expect(screen.getByText('1')).to.have.attribute('disabled');
- expect(screen.getByText('15')).to.have.attribute('disabled');
- expect(screen.getByText('30')).to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '1' })).to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '15' })).to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '30' })).to.have.attribute('disabled');
setProps({ value: adapterToUse.date(new Date(2018, 1, 1)) });
clock.runToLast();
- expect(screen.getByText('1')).not.to.have.attribute('disabled');
- expect(screen.getByText('15')).not.to.have.attribute('disabled');
- expect(screen.getByText('28')).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '1' })).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '15' })).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '28' })).not.to.have.attribute('disabled');
});
it('should apply disablePast', function test() {
@@ -94,25 +90,25 @@ export const testDayViewValidation: DescribeValidationTestSuite = (ElementToTest
const tomorrow = adapterToUse.addDays(now, 1);
const yesterday = adapterToUse.addDays(now, -1);
- expect(screen.getByText(adapterToUse.format(now, 'dayOfMonth'))).not.to.have.attribute(
- 'disabled',
- );
+ expect(
+ screen.getByRole('gridcell', { name: adapterToUse.format(now, 'dayOfMonth') }),
+ ).not.to.have.attribute('disabled');
if (!adapterToUse.isSameMonth(now, tomorrow)) {
setProps({ value: tomorrow });
clock.runToLast();
}
- expect(screen.getByText(adapterToUse.format(tomorrow, 'dayOfMonth'))).not.to.have.attribute(
- 'disabled',
- );
+ expect(
+ screen.getByRole('gridcell', { name: adapterToUse.format(tomorrow, 'dayOfMonth') }),
+ ).not.to.have.attribute('disabled');
if (!adapterToUse.isSameMonth(yesterday, tomorrow)) {
setProps({ value: yesterday });
clock.runToLast();
}
- expect(screen.getByText(adapterToUse.format(yesterday, 'dayOfMonth'))).to.have.attribute(
- 'disabled',
- );
+ expect(
+ screen.getByRole('gridcell', { name: adapterToUse.format(yesterday, 'dayOfMonth') }),
+ ).to.have.attribute('disabled');
});
it('should apply disableFuture', function test() {
@@ -126,25 +122,25 @@ export const testDayViewValidation: DescribeValidationTestSuite = (ElementToTest
const tomorrow = adapterToUse.addDays(now, 1);
const yesterday = adapterToUse.addDays(now, -1);
- expect(screen.getByText(adapterToUse.format(now, 'dayOfMonth'))).not.to.have.attribute(
- 'disabled',
- );
+ expect(
+ screen.getByRole('gridcell', { name: adapterToUse.format(now, 'dayOfMonth') }),
+ ).not.to.have.attribute('disabled');
if (!adapterToUse.isSameMonth(now, tomorrow)) {
setProps({ value: tomorrow });
clock.runToLast();
}
- expect(screen.getByText(adapterToUse.format(tomorrow, 'dayOfMonth'))).to.have.attribute(
- 'disabled',
- );
+ expect(
+ screen.getByRole('gridcell', { name: adapterToUse.format(tomorrow, 'dayOfMonth') }),
+ ).to.have.attribute('disabled');
if (!adapterToUse.isSameMonth(yesterday, tomorrow)) {
setProps({ value: yesterday });
clock.runToLast();
}
- expect(screen.getByText(adapterToUse.format(yesterday, 'dayOfMonth'))).not.to.have.attribute(
- 'disabled',
- );
+ expect(
+ screen.getByRole('gridcell', { name: adapterToUse.format(yesterday, 'dayOfMonth') }),
+ ).not.to.have.attribute('disabled');
});
it('should apply minDate', function test() {
@@ -155,11 +151,11 @@ export const testDayViewValidation: DescribeValidationTestSuite = (ElementToTest
minDate={adapterToUse.date(new Date(2019, 5, 4))}
/>,
);
- expect(screen.getByText('1')).to.have.attribute('disabled');
- expect(screen.getByText('3')).to.have.attribute('disabled');
- expect(screen.getByText('4')).not.to.have.attribute('disabled');
- expect(screen.getByText('5')).not.to.have.attribute('disabled');
- expect(screen.getByText('30')).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '1' })).to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '3' })).to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '4' })).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '5' })).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '30' })).not.to.have.attribute('disabled');
expect(screen.getByLabelText('Previous month')).to.have.attribute('disabled');
expect(screen.getByLabelText('Next month')).not.to.have.attribute('disabled');
});
@@ -172,11 +168,11 @@ export const testDayViewValidation: DescribeValidationTestSuite = (ElementToTest
maxDate={adapterToUse.date(new Date(2019, 5, 4))}
/>,
);
- expect(screen.getByText('1')).not.to.have.attribute('disabled');
- expect(screen.getByText('3')).not.to.have.attribute('disabled');
- expect(screen.getByText('4')).not.to.have.attribute('disabled');
- expect(screen.getByText('5')).to.have.attribute('disabled');
- expect(screen.getByText('30')).to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '1' })).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '3' })).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '4' })).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '5' })).to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '30' })).to.have.attribute('disabled');
expect(screen.getByLabelText('Previous month')).not.to.have.attribute('disabled');
expect(screen.getByLabelText('Next month')).to.have.attribute('disabled');
});
@@ -194,11 +190,11 @@ export const testDayViewValidation: DescribeValidationTestSuite = (ElementToTest
maxDateTime={adapterToUse.date(new Date(2019, 5, 4, 12, 0, 0))}
/>,
);
- expect(screen.getByText('1')).not.to.have.attribute('disabled');
- expect(screen.getByText('3')).not.to.have.attribute('disabled');
- expect(screen.getByText('4')).not.to.have.attribute('disabled');
- expect(screen.getByText('5')).to.have.attribute('disabled');
- expect(screen.getByText('30')).to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '1' })).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '3' })).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '4' })).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '5' })).to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '30' })).to.have.attribute('disabled');
});
it('should apply minDateTime', function test() {
@@ -214,11 +210,11 @@ export const testDayViewValidation: DescribeValidationTestSuite = (ElementToTest
minDateTime={adapterToUse.date(new Date(2019, 5, 4, 12, 0, 0))}
/>,
);
- expect(screen.getByText('1')).to.have.attribute('disabled');
- expect(screen.getByText('3')).to.have.attribute('disabled');
- expect(screen.getByText('4')).not.to.have.attribute('disabled');
- expect(screen.getByText('5')).not.to.have.attribute('disabled');
- expect(screen.getByText('30')).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '1' })).to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '3' })).to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '4' })).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '5' })).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '30' })).not.to.have.attribute('disabled');
});
});
};
diff --git a/packages/x-date-pickers/src/tests/describeValidation/testMinutesViewValidation.tsx b/packages/x-date-pickers/src/tests/describeValidation/testMinutesViewValidation.tsx
index 7450de4d9614..5d81ffdcac67 100644
--- a/packages/x-date-pickers/src/tests/describeValidation/testMinutesViewValidation.tsx
+++ b/packages/x-date-pickers/src/tests/describeValidation/testMinutesViewValidation.tsx
@@ -29,11 +29,7 @@ export const testMinutesViewValidation: DescribeValidationTestSuite = (
view: 'minutes',
openTo: 'minutes',
reduceAnimations: true,
- ...(componentFamily.includes('legacy-')
- ? {
- componentsProps: { toolbar: { hidden: true } },
- }
- : { slotProps: { toolbar: { hidden: true } } }),
+ slotProps: { toolbar: { hidden: true } },
};
it('should apply shouldDisableTime', function test() {
diff --git a/packages/x-date-pickers/src/tests/describeValidation/testMonthViewValidation.tsx b/packages/x-date-pickers/src/tests/describeValidation/testMonthViewValidation.tsx
index 49d55aec623b..1dee3a03455a 100644
--- a/packages/x-date-pickers/src/tests/describeValidation/testMonthViewValidation.tsx
+++ b/packages/x-date-pickers/src/tests/describeValidation/testMonthViewValidation.tsx
@@ -22,11 +22,7 @@ export const testMonthViewValidation: DescribeValidationTestSuite = (ElementToTe
...(componentFamily !== 'calendar' && {
open: true,
reduceAnimations: true,
- ...(componentFamily.includes('legacy-')
- ? {
- componentsProps: { toolbar: { hidden: true } },
- }
- : { slotProps: { toolbar: { hidden: true } } }),
+ slotProps: { toolbar: { hidden: true } },
}),
};
diff --git a/packages/x-date-pickers/src/tests/describeValidation/testYearViewValidation.tsx b/packages/x-date-pickers/src/tests/describeValidation/testYearViewValidation.tsx
index 45bedd32abb4..43c5b19dd86d 100644
--- a/packages/x-date-pickers/src/tests/describeValidation/testYearViewValidation.tsx
+++ b/packages/x-date-pickers/src/tests/describeValidation/testYearViewValidation.tsx
@@ -22,11 +22,7 @@ export const testYearViewValidation: DescribeValidationTestSuite = (ElementToTes
...(componentFamily !== 'calendar' && {
open: true,
reduceAnimations: true,
- ...(componentFamily.includes('legacy-')
- ? {
- componentsProps: { toolbar: { hidden: true } },
- }
- : { slotProps: { toolbar: { hidden: true } } }),
+ slotProps: { toolbar: { hidden: true } },
}),
};
diff --git a/packages/x-date-pickers/src/tests/describeValue/describeValue.tsx b/packages/x-date-pickers/src/tests/describeValue/describeValue.tsx
index ddc2690e46d7..16ae7bbd061a 100644
--- a/packages/x-date-pickers/src/tests/describeValue/describeValue.tsx
+++ b/packages/x-date-pickers/src/tests/describeValue/describeValue.tsx
@@ -24,20 +24,22 @@ function innerDescribeValue(
function WrappedElementToTest(
props: BasePickerInputProps &
- UsePickerValueNonStaticProps,
+ UsePickerValueNonStaticProps & { hook?: any },
) {
- return ;
+ const { hook, ...other } = props;
+ const hookResult = hook?.(props);
+ return ;
}
let renderWithProps: BuildFieldInteractionsResponse['renderWithProps'];
if (componentFamily === 'field' || componentFamily === 'picker') {
const interactions = buildFieldInteractions({ clock, render, Component: ElementToTest });
- renderWithProps = (props: any) =>
- interactions.renderWithProps({ ...defaultProps, ...props }, componentFamily);
+ renderWithProps = (props: any, hook?: any) =>
+ interactions.renderWithProps({ ...defaultProps, ...props }, hook, componentFamily);
} else {
- renderWithProps = (props: any) => {
- const response = render();
+ renderWithProps = (props: any, hook?: any) => {
+ const response = render();
return {
...response,
diff --git a/packages/x-date-pickers/src/tests/describeValue/testControlledUnControlled.tsx b/packages/x-date-pickers/src/tests/describeValue/testControlledUnControlled.tsx
index a814da564420..1c9a52114b38 100644
--- a/packages/x-date-pickers/src/tests/describeValue/testControlledUnControlled.tsx
+++ b/packages/x-date-pickers/src/tests/describeValue/testControlledUnControlled.tsx
@@ -53,7 +53,7 @@ export const testControlledUnControlled: DescribeValueTestSuite = (
assertRenderedValue(newValue);
// TODO: Clean this exception or change the clock behavior
- expect(onChange.callCount).to.equal(getExpectedOnChangeCount(componentFamily));
+ expect(onChange.callCount).to.equal(getExpectedOnChangeCount(componentFamily, params));
if (Array.isArray(newValue)) {
newValue.forEach((value, index) => {
expect(onChange.lastCall.args[0][index]).toEqualDateTime(value);
@@ -66,10 +66,25 @@ export const testControlledUnControlled: DescribeValueTestSuite = (
it('should call onChange when updating a value defined with `props.value`', () => {
const onChange = spy();
- const { selectSection } = renderWithProps({ defaultValue: values[0], onChange });
+ const useControlledElement = (props) => {
+ const [value, setValue] = React.useState(props?.value || null);
+ const handleChange = React.useCallback(
+ (newValue) => {
+ setValue(newValue);
+ props?.onChange(newValue);
+ },
+ [props],
+ );
+ return { value, onChange: handleChange };
+ };
+
+ const { selectSection } = renderWithProps(
+ { value: values[0], onChange },
+ useControlledElement,
+ );
const newValue = setNewValue(values[0], { selectSection });
- expect(onChange.callCount).to.equal(getExpectedOnChangeCount(componentFamily));
+ expect(onChange.callCount).to.equal(getExpectedOnChangeCount(componentFamily, params));
if (Array.isArray(newValue)) {
newValue.forEach((value, index) => {
expect(onChange.lastCall.args[0][index]).toEqualDateTime(value);
diff --git a/packages/x-date-pickers/src/tests/describeValue/testPickerActionBar.tsx b/packages/x-date-pickers/src/tests/describeValue/testPickerActionBar.tsx
index ab4bb8d57de4..33fa18ce55c2 100644
--- a/packages/x-date-pickers/src/tests/describeValue/testPickerActionBar.tsx
+++ b/packages/x-date-pickers/src/tests/describeValue/testPickerActionBar.tsx
@@ -2,15 +2,22 @@ import * as React from 'react';
import { expect } from 'chai';
import { spy } from 'sinon';
import { screen, userEvent } from '@mui/monorepo/test/utils';
-import { adapterToUse } from 'test/utils/pickers-utils';
+import { adapterToUse, getExpectedOnChangeCount } from 'test/utils/pickers-utils';
import { DescribeValueTestSuite } from './describeValue.types';
export const testPickerActionBar: DescribeValueTestSuite = (
ElementToTest,
options,
) => {
- const { componentFamily, render, renderWithProps, values, emptyValue, setNewValue, type } =
- options;
+ const {
+ componentFamily,
+ render,
+ renderWithProps,
+ values,
+ emptyValue,
+ setNewValue,
+ ...pickerParams
+ } = options;
if (componentFamily !== 'picker') {
return;
@@ -37,7 +44,7 @@ export const testPickerActionBar: DescribeValueTestSuite = (
// Clear the date
userEvent.mousePress(screen.getByText(/clear/i));
expect(onChange.callCount).to.equal(1);
- if (type === 'date-range') {
+ if (pickerParams.type === 'date-range') {
onChange.lastCall.args[0].forEach((value, index) => {
expect(value).to.deep.equal(emptyValue[index]);
});
@@ -45,7 +52,7 @@ export const testPickerActionBar: DescribeValueTestSuite = (
expect(onChange.lastCall.args[0]).to.deep.equal(emptyValue);
}
expect(onAccept.callCount).to.equal(1);
- if (type === 'date-range') {
+ if (pickerParams.type === 'date-range') {
onAccept.lastCall.args[0].forEach((value, index) => {
expect(value).to.deep.equal(emptyValue[index]);
});
@@ -100,8 +107,10 @@ export const testPickerActionBar: DescribeValueTestSuite = (
// Cancel the modifications
userEvent.mousePress(screen.getByText(/cancel/i));
- expect(onChange.callCount).to.equal(2);
- if (type === 'date-range') {
+ expect(onChange.callCount).to.equal(
+ getExpectedOnChangeCount(componentFamily, pickerParams) + 1,
+ );
+ if (pickerParams.type === 'date-range') {
values[0].forEach((value, index) => {
expect(onChange.lastCall.args[0][index]).toEqualDateTime(value);
});
@@ -158,7 +167,9 @@ export const testPickerActionBar: DescribeValueTestSuite = (
// Accept the modifications
userEvent.mousePress(screen.getByText(/ok/i));
- expect(onChange.callCount).to.equal(1); // The accepted value as already been committed, don't call onChange again
+ expect(onChange.callCount).to.equal(
+ getExpectedOnChangeCount(componentFamily, pickerParams),
+ ); // The accepted value as already been committed, don't call onChange again
expect(onAccept.callCount).to.equal(1);
expect(onClose.callCount).to.equal(1);
});
@@ -232,10 +243,12 @@ export const testPickerActionBar: DescribeValueTestSuite = (
userEvent.mousePress(screen.getByText(/today/i));
const startOfToday =
- type === 'date' ? adapterToUse.startOfDay(adapterToUse.date()) : adapterToUse.date();
+ pickerParams.type === 'date'
+ ? adapterToUse.startOfDay(adapterToUse.date())
+ : adapterToUse.date();
expect(onChange.callCount).to.equal(1);
- if (type === 'date-range') {
+ if (pickerParams.type === 'date-range') {
onChange.lastCall.args[0].forEach((value) => {
expect(value).toEqualDateTime(startOfToday);
});
@@ -243,7 +256,7 @@ export const testPickerActionBar: DescribeValueTestSuite = (
expect(onChange.lastCall.args[0]).toEqualDateTime(startOfToday);
}
expect(onAccept.callCount).to.equal(1);
- if (type === 'date-range') {
+ if (pickerParams.type === 'date-range') {
onAccept.lastCall.args[0].forEach((value) => {
expect(value).toEqualDateTime(startOfToday);
});
diff --git a/packages/x-date-pickers/src/tests/describeValue/testPickerOpenCloseLifeCycle.tsx b/packages/x-date-pickers/src/tests/describeValue/testPickerOpenCloseLifeCycle.tsx
index 24642d7f5570..96b622282171 100644
--- a/packages/x-date-pickers/src/tests/describeValue/testPickerOpenCloseLifeCycle.tsx
+++ b/packages/x-date-pickers/src/tests/describeValue/testPickerOpenCloseLifeCycle.tsx
@@ -2,7 +2,7 @@ import * as React from 'react';
import { expect } from 'chai';
import { spy } from 'sinon';
import { screen, userEvent } from '@mui/monorepo/test/utils';
-import { getTextbox, openPicker } from 'test/utils/pickers-utils';
+import { getExpectedOnChangeCount, getTextbox, openPicker } from 'test/utils/pickers-utils';
import { DescribeValueTestSuite } from './describeValue.types';
export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite = (
@@ -65,7 +65,7 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite
// Change the value
let newValue = setNewValue(values[0], { isOpened: true, selectSection });
- expect(onChange.callCount).to.equal(1);
+ expect(onChange.callCount).to.equal(getExpectedOnChangeCount(componentFamily, pickerParams));
if (pickerParams.type === 'date-range') {
newValue = setNewValue(newValue, { isOpened: true, setEndDate: true, selectSection });
newValue.forEach((value, index) => {
@@ -116,7 +116,7 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite
// Change the value
let newValue = setNewValue(values[0], { isOpened: true, selectSection });
- expect(onChange.callCount).to.equal(1);
+ expect(onChange.callCount).to.equal(getExpectedOnChangeCount(componentFamily, pickerParams));
if (pickerParams.type === 'date-range') {
newValue = setNewValue(newValue, { isOpened: true, setEndDate: true, selectSection });
newValue.forEach((value, index) => {
@@ -175,7 +175,8 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite
// Change the value
let newValue = setNewValue(values[0], { isOpened: true, selectSection });
- expect(onChange.callCount).to.equal(1);
+ const initialChangeCount = getExpectedOnChangeCount(componentFamily, pickerParams);
+ expect(onChange.callCount).to.equal(initialChangeCount);
if (pickerParams.type === 'date-range') {
newValue = setNewValue(newValue, { isOpened: true, setEndDate: true, selectSection });
newValue.forEach((value, index) => {
@@ -196,7 +197,12 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite
expect(onChange.lastCall.args[0][index]).toEqualDateTime(value);
});
} else {
- expect(onChange.callCount).to.equal(2);
+ expect(onChange.callCount).to.equal(
+ initialChangeCount +
+ getExpectedOnChangeCount(componentFamily, pickerParams) -
+ // meridiem does not change this time in case of multi section digital clock
+ (pickerParams.type === 'time' || pickerParams.type === 'date-time' ? 1 : 0),
+ );
expect(onChange.lastCall.args[0]).toEqualDateTime(newValueBis as any);
}
expect(onAccept.callCount).to.equal(0);
@@ -222,7 +228,7 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite
// Dismiss the picker
userEvent.keyPress(document.activeElement!, { key: 'Escape' });
- expect(onChange.callCount).to.equal(1);
+ expect(onChange.callCount).to.equal(getExpectedOnChangeCount(componentFamily, pickerParams));
expect(onAccept.callCount).to.equal(1);
if (pickerParams.type === 'date-range') {
newValue.forEach((value, index) => {
@@ -286,7 +292,7 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite
// Dismiss the picker
userEvent.mousePress(document.body);
- expect(onChange.callCount).to.equal(1);
+ expect(onChange.callCount).to.equal(getExpectedOnChangeCount(componentFamily, pickerParams));
expect(onAccept.callCount).to.equal(1);
expect(onAccept.lastCall.args[0]).toEqualDateTime(newValue as any);
expect(onClose.callCount).to.equal(1);
diff --git a/packages/x-date-pickers/src/timeViewRenderers/timeViewRenderers.tsx b/packages/x-date-pickers/src/timeViewRenderers/timeViewRenderers.tsx
index 0e7162d692db..539511d8c35e 100644
--- a/packages/x-date-pickers/src/timeViewRenderers/timeViewRenderers.tsx
+++ b/packages/x-date-pickers/src/timeViewRenderers/timeViewRenderers.tsx
@@ -50,13 +50,13 @@ export const renderTimeViewClock = ({
autoFocus,
showViewSwitcher,
disableIgnoringDatePartForTimeValidation,
-}: TimeViewRendererProps>) => (
+}: TimeViewRendererProps>) => (
- view={view as TimeView}
+ view={view}
onViewChange={onViewChange}
- focusedView={focusedView as TimeView}
+ focusedView={focusedView && isTimeView(focusedView) ? focusedView : null}
onFocusedViewChange={onFocusedViewChange}
- views={views.filter(isTimeView) as TimeView[]}
+ views={views.filter(isTimeView)}
value={value}
defaultValue={defaultValue}
onChange={onChange}
diff --git a/test/utils/pickers-utils.tsx b/test/utils/pickers-utils.tsx
index 5875ffd84b88..3ccf62ffae11 100644
--- a/test/utils/pickers-utils.tsx
+++ b/test/utils/pickers-utils.tsx
@@ -366,6 +366,7 @@ export type FieldSectionSelector = (
export interface BuildFieldInteractionsResponse {
renderWithProps: (
props: P,
+ hook?: (props: P) => Record,
componentFamily?: 'picker' | 'field',
) => ReturnType['render']> & {
input: HTMLInputElement;
@@ -419,13 +420,19 @@ export const buildFieldInteractions = ({
});
};
- const renderWithProps = (props: P, componentFamily: 'picker' | 'field' = 'field') => {
+ const renderWithProps: BuildFieldInteractionsResponse ['renderWithProps'] = (
+ props,
+ hook,
+ componentFamily = 'field',
+ ) => {
let fieldRef: React.RefObject> = { current: null };
function WrappedComponent() {
fieldRef = React.useRef>(null);
+ const hookResult = hook?.(props);
const allProps = {
...props,
+ ...hookResult,
} as any;
if (componentFamily === 'field') {
@@ -615,13 +622,40 @@ export class MockedDataTransfer implements DataTransfer {
}
}
-export const getExpectedOnChangeCount = (componentFamily: PickerComponentFamily) => {
+const getChangeCountForComponentFamily = (componentFamily: PickerComponentFamily) => {
switch (componentFamily) {
case 'clock':
- return 2;
case 'multi-section-digital-clock':
return 3;
default:
return 1;
}
};
+
+export const getExpectedOnChangeCount = (
+ componentFamily: PickerComponentFamily,
+ params: OpenPickerParams,
+) => {
+ if (componentFamily === 'digital-clock') {
+ return getChangeCountForComponentFamily(componentFamily);
+ }
+ if (params.type === 'date-time') {
+ return (
+ getChangeCountForComponentFamily(componentFamily) +
+ getChangeCountForComponentFamily(
+ params.variant === 'desktop' ? 'multi-section-digital-clock' : 'clock',
+ )
+ );
+ }
+ if (componentFamily === 'picker' && params.type === 'time') {
+ return getChangeCountForComponentFamily(
+ params.variant === 'desktop' ? 'multi-section-digital-clock' : 'clock',
+ );
+ }
+ if (componentFamily === 'clock') {
+ // the `TimeClock` fires change for both touch move and touch end
+ // but does not have meridiem control
+ return (getChangeCountForComponentFamily(componentFamily) - 1) * 2;
+ }
+ return getChangeCountForComponentFamily(componentFamily);
+};
| |