diff --git a/docs/pages/x/api/date-pickers/date-time-range-picker.json b/docs/pages/x/api/date-pickers/date-time-range-picker.json
index d08722fb1fd1..6980f341feb1 100644
--- a/docs/pages/x/api/date-pickers/date-time-range-picker.json
+++ b/docs/pages/x/api/date-pickers/date-time-range-picker.json
@@ -367,7 +367,10 @@
}
],
"classes": [],
+ "spread": false,
+ "themeDefaultProps": false,
"muiName": "MuiDateTimeRangePicker",
+ "forwardsRefTo": "HTMLDivElement",
"filename": "/packages/x-date-pickers-pro/src/DateTimeRangePicker/DateTimeRangePicker.tsx",
"inheritance": null,
"demos": "
",
diff --git a/docs/pages/x/api/date-pickers/desktop-date-time-range-picker.json b/docs/pages/x/api/date-pickers/desktop-date-time-range-picker.json
index 537034267407..2ffc29c59437 100644
--- a/docs/pages/x/api/date-pickers/desktop-date-time-range-picker.json
+++ b/docs/pages/x/api/date-pickers/desktop-date-time-range-picker.json
@@ -345,7 +345,10 @@
{ "name": "field", "description": "", "class": null }
],
"classes": [],
+ "spread": false,
+ "themeDefaultProps": false,
"muiName": "MuiDesktopDateTimeRangePicker",
+ "forwardsRefTo": "HTMLDivElement",
"filename": "/packages/x-date-pickers-pro/src/DesktopDateTimeRangePicker/DesktopDateTimeRangePicker.tsx",
"inheritance": null,
"demos": "",
diff --git a/docs/pages/x/api/date-pickers/mobile-date-range-picker.json b/docs/pages/x/api/date-pickers/mobile-date-range-picker.json
index 2c0481a80fd6..4e4b02c80de7 100644
--- a/docs/pages/x/api/date-pickers/mobile-date-range-picker.json
+++ b/docs/pages/x/api/date-pickers/mobile-date-range-picker.json
@@ -200,6 +200,12 @@
"default": "ArrowDropDown",
"class": null
},
+ {
+ "name": "dialog",
+ "description": "Custom component for the dialog inside which the views are rendered on mobile.",
+ "default": "PickersModalDialogRoot",
+ "class": null
+ },
{
"name": "actionBar",
"description": "Custom component for the action bar, it is placed below the picker views.",
@@ -245,12 +251,6 @@
"default": "IconButton",
"class": null
},
- {
- "name": "dialog",
- "description": "Custom component for the dialog inside which the views are rendered on mobile.",
- "default": "PickersModalDialogRoot",
- "class": null
- },
{
"name": "mobilePaper",
"description": "Custom component for the paper rendered inside the mobile picker's Dialog.",
diff --git a/docs/pages/x/api/date-pickers/mobile-date-time-range-picker.json b/docs/pages/x/api/date-pickers/mobile-date-time-range-picker.json
index 4b62aadfa72e..21c66292a2a0 100644
--- a/docs/pages/x/api/date-pickers/mobile-date-time-range-picker.json
+++ b/docs/pages/x/api/date-pickers/mobile-date-time-range-picker.json
@@ -269,6 +269,12 @@
"default": "MenuItem from '@mui/material'",
"class": null
},
+ {
+ "name": "dialog",
+ "description": "Custom component for the dialog inside which the views are rendered on mobile.",
+ "default": "PickersModalDialogRoot",
+ "class": null
+ },
{
"name": "actionBar",
"description": "Custom component for the action bar, it is placed below the picker views.",
@@ -314,12 +320,6 @@
"default": "IconButton",
"class": null
},
- {
- "name": "dialog",
- "description": "Custom component for the dialog inside which the views are rendered on mobile.",
- "default": "PickersModalDialogRoot",
- "class": null
- },
{
"name": "mobilePaper",
"description": "Custom component for the paper rendered inside the mobile picker's Dialog.",
@@ -335,7 +335,10 @@
{ "name": "field", "description": "", "class": null }
],
"classes": [],
+ "spread": false,
+ "themeDefaultProps": false,
"muiName": "MuiMobileDateTimeRangePicker",
+ "forwardsRefTo": "HTMLDivElement",
"filename": "/packages/x-date-pickers-pro/src/MobileDateTimeRangePicker/MobileDateTimeRangePicker.tsx",
"inheritance": null,
"demos": "",
diff --git a/packages/x-date-pickers-pro/src/DateTimeRangePicker/DateTimeRangePickerToolbar.tsx b/packages/x-date-pickers-pro/src/DateTimeRangePicker/DateTimeRangePickerToolbar.tsx
index 858bdfa0336e..32fbf3ec18ba 100644
--- a/packages/x-date-pickers-pro/src/DateTimeRangePicker/DateTimeRangePickerToolbar.tsx
+++ b/packages/x-date-pickers-pro/src/DateTimeRangePicker/DateTimeRangePickerToolbar.tsx
@@ -133,7 +133,6 @@ const DateTimeRangePickerToolbar = React.forwardRef(function DateTimeRangePicker
hidden,
toolbarFormat,
toolbarPlaceholder,
- titleId,
};
const localeText = useLocaleText();
@@ -182,6 +181,10 @@ const DateTimeRangePickerToolbar = React.forwardRef(function DateTimeRangePicker
[onChange, onRangePositionChange, props.value, rangePosition, utils],
);
+ if (hidden) {
+ return null;
+ }
+
return (
@@ -210,6 +214,7 @@ const DateTimeRangePickerToolbar = React.forwardRef(function DateTimeRangePicker
view={rangePosition === 'end' ? view : undefined}
className={classes.endToolbar}
onChange={handleOnChange}
+ titleId={titleId ? `${titleId}-end-toolbar` : undefined}
{...commonToolbarProps}
/>
diff --git a/packages/x-date-pickers-pro/src/DateTimeRangePicker/shared.tsx b/packages/x-date-pickers-pro/src/DateTimeRangePicker/shared.tsx
index cba6a59a1aa2..cbbaa6102347 100644
--- a/packages/x-date-pickers-pro/src/DateTimeRangePicker/shared.tsx
+++ b/packages/x-date-pickers-pro/src/DateTimeRangePicker/shared.tsx
@@ -170,8 +170,27 @@ export function useDateTimeRangePickerDefaultizedProps<
ampm,
disableFuture: themeProps.disableFuture ?? false,
disablePast: themeProps.disablePast ?? false,
- minDate: applyDefaultDate(utils, themeProps.minDate, defaultDates.minDate),
- maxDate: applyDefaultDate(utils, themeProps.maxDate, defaultDates.maxDate),
+ minDate: applyDefaultDate(
+ utils,
+ themeProps.minDateTime ?? themeProps.minDate,
+ defaultDates.minDate,
+ ),
+ maxDate: applyDefaultDate(
+ utils,
+ themeProps.maxDateTime ?? themeProps.maxDate,
+ defaultDates.maxDate,
+ ),
+ minTime: themeProps.minDateTime ?? themeProps.minTime,
+ maxTime: themeProps.maxDateTime ?? themeProps.maxTime,
+ disableIgnoringDatePartForTimeValidation:
+ themeProps.disableIgnoringDatePartForTimeValidation ??
+ Boolean(
+ themeProps.minDateTime ||
+ themeProps.maxDateTime ||
+ // allow digital clocks to correctly check time validity: https://github.com/mui/mui-x/issues/12048
+ themeProps.disablePast ||
+ themeProps.disableFuture,
+ ),
slots: {
tabs: DateTimeRangePickerTabs,
toolbar: DateTimeRangePickerToolbar,
diff --git a/packages/x-date-pickers-pro/src/DateTimeRangePicker/tests/describes.DateTimeRangePicker.test.tsx b/packages/x-date-pickers-pro/src/DateTimeRangePicker/tests/describes.DateTimeRangePicker.test.tsx
new file mode 100644
index 000000000000..4778e65099b2
--- /dev/null
+++ b/packages/x-date-pickers-pro/src/DateTimeRangePicker/tests/describes.DateTimeRangePicker.test.tsx
@@ -0,0 +1,27 @@
+import * as React from 'react';
+import { createPickerRenderer, wrapPickerMount } from 'test/utils/pickers';
+import { describeConformance } from 'test/utils/describeConformance';
+import { DateTimeRangePicker } from '../DateTimeRangePicker';
+
+describe(' - Describes', () => {
+ const { render } = createPickerRenderer({ clock: 'fake' });
+
+ describeConformance(, () => ({
+ classes: {} as any,
+ render,
+ muiName: 'MuiDateTimeRangePicker',
+ wrapMount: wrapPickerMount,
+ refInstanceof: window.HTMLDivElement,
+ skip: [
+ 'componentProp',
+ 'componentsProp',
+ 'themeDefaultProps',
+ 'themeStyleOverrides',
+ 'themeVariants',
+ 'mergeClassName',
+ 'propsSpread',
+ 'rootClass',
+ 'reactTestRenderer',
+ ],
+ }));
+});
diff --git a/packages/x-date-pickers-pro/src/DesktopDateTimeRangePicker/tests/DesktopDateTimeRangePicker.test.tsx b/packages/x-date-pickers-pro/src/DesktopDateTimeRangePicker/tests/DesktopDateTimeRangePicker.test.tsx
new file mode 100644
index 000000000000..793d2a6682d9
--- /dev/null
+++ b/packages/x-date-pickers-pro/src/DesktopDateTimeRangePicker/tests/DesktopDateTimeRangePicker.test.tsx
@@ -0,0 +1,168 @@
+import * as React from 'react';
+import { expect } from 'chai';
+import { screen } from '@mui-internal/test-utils';
+import { createPickerRenderer, adapterToUse } from 'test/utils/pickers';
+import { DesktopDateTimeRangePicker } from '../DesktopDateTimeRangePicker';
+
+describe('', () => {
+ const { render } = createPickerRenderer({
+ clock: 'fake',
+ clockConfig: new Date(2018, 0, 10, 10, 16, 0),
+ });
+
+ describe('disabled dates', () => {
+ it('should respect the "disablePast" prop', () => {
+ render();
+
+ expect(screen.getByRole('gridcell', { name: '8' })).to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '9' })).to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '10' })).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '11' })).not.to.have.attribute('disabled');
+
+ expect(screen.getByRole('option', { name: '9 hours' })).to.have.attribute(
+ 'aria-disabled',
+ 'true',
+ );
+ expect(screen.getByRole('option', { name: '10 hours' })).not.to.have.attribute(
+ 'aria-disabled',
+ );
+
+ expect(screen.getByRole('option', { name: '15 minutes' })).to.have.attribute(
+ 'aria-disabled',
+ 'true',
+ );
+ });
+
+ // Asserts correct behavior: https://github.com/mui/mui-x/issues/12048
+ it('should respect the "disablePast" prop combined with "referenceDate"', () => {
+ render(
+ ,
+ );
+
+ expect(screen.getByRole('gridcell', { name: '8' })).to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '9' })).to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '10' })).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '11' })).not.to.have.attribute('disabled');
+
+ expect(screen.getByRole('option', { name: '9 hours' })).not.to.have.attribute(
+ 'aria-disabled',
+ );
+ expect(screen.getByRole('option', { name: '10 hours' })).not.to.have.attribute(
+ 'aria-disabled',
+ );
+
+ expect(screen.getByRole('option', { name: '15 minutes' })).not.to.have.attribute(
+ 'aria-disabled',
+ );
+ });
+
+ it('should respect the "disableFuture" prop', () => {
+ render();
+
+ 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');
+
+ expect(screen.getByRole('option', { name: '10 hours' })).not.to.have.attribute(
+ 'aria-disabled',
+ );
+ expect(screen.getByRole('option', { name: '11 hours' })).to.have.attribute(
+ 'aria-disabled',
+ 'true',
+ );
+ });
+
+ // Asserts correct behavior: https://github.com/mui/mui-x/issues/12048
+ it('should respect the "disableFuture" prop combined with "referenceDate"', () => {
+ render(
+ ,
+ );
+
+ 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');
+
+ expect(screen.getByRole('option', { name: '10 hours' })).not.to.have.attribute(
+ 'aria-disabled',
+ );
+ expect(screen.getByRole('option', { name: '11 hours' })).not.to.have.attribute(
+ 'aria-disabled',
+ );
+ });
+
+ it('should respect the "minDateTime" prop', () => {
+ render(
+ ,
+ );
+
+ expect(screen.getByRole('gridcell', { name: '8' })).to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '9' })).to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '10' })).not.to.have.attribute('disabled');
+ expect(screen.getByRole('gridcell', { name: '11' })).not.to.have.attribute('disabled');
+
+ expect(screen.getByRole('option', { name: '9 hours' })).to.have.attribute(
+ 'aria-disabled',
+ 'true',
+ );
+ expect(screen.getByRole('option', { name: '10 hours' })).not.to.have.attribute(
+ 'aria-disabled',
+ );
+
+ expect(screen.getByRole('option', { name: '15 minutes' })).to.have.attribute(
+ 'aria-disabled',
+ 'true',
+ );
+ expect(screen.getByRole('option', { name: '20 minutes' })).not.to.have.attribute(
+ 'aria-disabled',
+ );
+ });
+
+ it('should respect the "maxDateTime" prop', () => {
+ render(
+ ,
+ );
+
+ 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('option', { name: '10 hours' })).not.to.have.attribute(
+ 'aria-disabled',
+ );
+ expect(screen.getByRole('option', { name: '11 hours' })).to.have.attribute(
+ 'aria-disabled',
+ 'true',
+ );
+
+ expect(screen.getByRole('option', { name: '15 minutes' })).not.to.have.attribute(
+ 'aria-disabled',
+ );
+ expect(screen.getByRole('option', { name: '20 minutes' })).to.have.attribute(
+ 'aria-disabled',
+ 'true',
+ );
+ });
+ });
+});
diff --git a/packages/x-date-pickers-pro/src/DesktopDateTimeRangePicker/tests/describes.DesktopDateTimeRangePicker.test.tsx b/packages/x-date-pickers-pro/src/DesktopDateTimeRangePicker/tests/describes.DesktopDateTimeRangePicker.test.tsx
new file mode 100644
index 000000000000..9c29cbf35fcf
--- /dev/null
+++ b/packages/x-date-pickers-pro/src/DesktopDateTimeRangePicker/tests/describes.DesktopDateTimeRangePicker.test.tsx
@@ -0,0 +1,157 @@
+import * as React from 'react';
+import { describeConformance, screen, userEvent } from '@mui-internal/test-utils';
+import {
+ createPickerRenderer,
+ adapterToUse,
+ expectFieldValueV7,
+ describeValue,
+ describePicker,
+ describeRangeValidation,
+ wrapPickerMount,
+ getFieldSectionsContainer,
+} from 'test/utils/pickers';
+import { DesktopDateTimeRangePicker } from '../DesktopDateTimeRangePicker';
+
+describe(' - Describes', () => {
+ const { render, clock } = createPickerRenderer({
+ clock: 'fake',
+ });
+
+ describePicker(DesktopDateTimeRangePicker, {
+ render,
+ fieldType: 'multi-input',
+ variant: 'desktop',
+ });
+
+ describeRangeValidation(DesktopDateTimeRangePicker, () => ({
+ render,
+ clock,
+ views: ['day', 'hours', 'minutes'],
+ componentFamily: 'picker',
+ variant: 'desktop',
+ }));
+
+ describeConformance(, () => ({
+ classes: {} as any,
+ render,
+ muiName: 'MuiDesktopDateTimeRangePicker',
+ wrapMount: wrapPickerMount,
+ refInstanceof: window.HTMLDivElement,
+ skip: [
+ 'componentProp',
+ 'componentsProp',
+ 'themeDefaultProps',
+ 'themeStyleOverrides',
+ 'themeVariants',
+ 'mergeClassName',
+ 'propsSpread',
+ 'rootClass',
+ 'reactTestRenderer',
+ ],
+ }));
+
+ describeValue(DesktopDateTimeRangePicker, () => ({
+ render,
+ componentFamily: 'picker',
+ type: 'date-time-range',
+ variant: 'desktop',
+ initialFocus: 'start',
+ clock,
+ values: [
+ // initial start and end dates
+ [adapterToUse.date('2018-01-01T11:30:00'), adapterToUse.date('2018-01-04T11:45:00')],
+ // start and end dates after `setNewValue`
+ [adapterToUse.date('2018-01-02T12:35:00'), adapterToUse.date('2018-01-05T12:50:00')],
+ ],
+ emptyValue: [null, null],
+ assertRenderedValue: (expectedValues: any[]) => {
+ const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale();
+ const expectedPlaceholder = hasMeridiem ? 'MM/DD/YYYY hh:mm aa' : 'MM/DD/YYYY hh:mm';
+
+ const startSectionsContainer = getFieldSectionsContainer(0);
+ const expectedStartValueStr = expectedValues[0]
+ ? adapterToUse.format(
+ expectedValues[0],
+ hasMeridiem ? 'keyboardDateTime12h' : 'keyboardDateTime24h',
+ )
+ : expectedPlaceholder;
+ expectFieldValueV7(startSectionsContainer, expectedStartValueStr);
+
+ const endSectionsContainer = getFieldSectionsContainer(1);
+ const expectedEndValueStr = expectedValues[1]
+ ? adapterToUse.format(
+ expectedValues[1],
+ hasMeridiem ? 'keyboardDateTime12h' : 'keyboardDateTime24h',
+ )
+ : expectedPlaceholder;
+ expectFieldValueV7(endSectionsContainer, expectedEndValueStr);
+ },
+ setNewValue: (
+ value,
+ { isOpened, applySameValue, setEndDate = false, selectSection, pressKey },
+ ) => {
+ let newValue: any[];
+ if (applySameValue) {
+ newValue = value;
+ } else if (setEndDate) {
+ newValue = [
+ value[0],
+ adapterToUse.addMinutes(adapterToUse.addHours(adapterToUse.addDays(value[1], 1), 1), 5),
+ ];
+ } else {
+ newValue = [
+ adapterToUse.addMinutes(adapterToUse.addHours(adapterToUse.addDays(value[0], 1), 1), 5),
+ value[1],
+ ];
+ }
+ if (isOpened) {
+ userEvent.mousePress(
+ screen.getByRole('gridcell', {
+ name: adapterToUse.getDate(newValue[setEndDate ? 1 : 0]).toString(),
+ }),
+ );
+ const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale();
+ const hours = adapterToUse.format(
+ newValue[setEndDate ? 1 : 0],
+ hasMeridiem ? 'hours12h' : 'hours24h',
+ );
+ const hoursNumber = adapterToUse.getHours(newValue[setEndDate ? 1 : 0]);
+ userEvent.mousePress(screen.getByRole('option', { name: `${parseInt(hours, 10)} hours` }));
+ userEvent.mousePress(
+ screen.getByRole('option', {
+ name: `${adapterToUse.getMinutes(newValue[setEndDate ? 1 : 0])} minutes`,
+ }),
+ );
+ if (hasMeridiem) {
+ // meridiem is an extra view on `DesktopDateTimeRangePicker`
+ // we need to click it to finish selection
+ userEvent.mousePress(
+ screen.getByRole('option', { name: hoursNumber >= 12 ? 'PM' : 'AM' }),
+ );
+ }
+ } else {
+ selectSection('day');
+ pressKey(undefined, 'ArrowUp');
+
+ selectSection('hours');
+ pressKey(undefined, 'ArrowUp');
+
+ selectSection('minutes');
+ pressKey(undefined, 'PageUp'); // increment by 5 minutes
+
+ const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale();
+ if (hasMeridiem) {
+ selectSection('meridiem');
+ const previousHours = adapterToUse.getHours(value[setEndDate ? 1 : 0]);
+ const newHours = adapterToUse.getHours(newValue[setEndDate ? 1 : 0]);
+ // update meridiem section if it changed
+ if ((previousHours < 12 && newHours >= 12) || (previousHours >= 12 && newHours < 12)) {
+ pressKey(undefined, 'ArrowUp');
+ }
+ }
+ }
+
+ return newValue;
+ },
+ }));
+});
diff --git a/packages/x-date-pickers-pro/src/MobileDateTimeRangePicker/tests/describes.MobileDateTimeRangePicker.test.tsx b/packages/x-date-pickers-pro/src/MobileDateTimeRangePicker/tests/describes.MobileDateTimeRangePicker.test.tsx
new file mode 100644
index 000000000000..8b7f439ca168
--- /dev/null
+++ b/packages/x-date-pickers-pro/src/MobileDateTimeRangePicker/tests/describes.MobileDateTimeRangePicker.test.tsx
@@ -0,0 +1,157 @@
+import * as React from 'react';
+import { describeConformance, fireEvent, screen, userEvent } from '@mui-internal/test-utils';
+import {
+ createPickerRenderer,
+ adapterToUse,
+ expectFieldValueV7,
+ describeValue,
+ describePicker,
+ describeRangeValidation,
+ wrapPickerMount,
+ getFieldSectionsContainer,
+ openPicker,
+} from 'test/utils/pickers';
+import { MobileDateTimeRangePicker } from '../MobileDateTimeRangePicker';
+
+describe(' - Describes', () => {
+ const { render, clock } = createPickerRenderer({
+ clock: 'fake',
+ });
+
+ describePicker(MobileDateTimeRangePicker, {
+ render,
+ fieldType: 'multi-input',
+ variant: 'mobile',
+ });
+
+ describeRangeValidation(MobileDateTimeRangePicker, () => ({
+ render,
+ clock,
+ views: ['day', 'hours', 'minutes'],
+ componentFamily: 'picker',
+ variant: 'mobile',
+ }));
+
+ describeConformance(, () => ({
+ classes: {} as any,
+ render,
+ muiName: 'MuiMobileDateTimeRangePicker',
+ wrapMount: wrapPickerMount,
+ refInstanceof: window.HTMLDivElement,
+ skip: [
+ 'componentProp',
+ 'componentsProp',
+ 'themeDefaultProps',
+ 'themeStyleOverrides',
+ 'themeVariants',
+ 'mergeClassName',
+ 'propsSpread',
+ 'rootClass',
+ 'reactTestRenderer',
+ ],
+ }));
+
+ describeValue(MobileDateTimeRangePicker, () => ({
+ render,
+ componentFamily: 'picker',
+ type: 'date-time-range',
+ variant: 'mobile',
+ initialFocus: 'start',
+ clock,
+ values: [
+ // initial start and end dates
+ [adapterToUse.date('2018-01-01T11:30:00'), adapterToUse.date('2018-01-04T11:45:00')],
+ // start and end dates after `setNewValue`
+ [adapterToUse.date('2018-01-02T12:35:00'), adapterToUse.date('2018-01-05T12:50:00')],
+ ],
+ emptyValue: [null, null],
+ assertRenderedValue: (expectedValues: any[]) => {
+ const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale();
+ const expectedPlaceholder = hasMeridiem ? 'MM/DD/YYYY hh:mm aa' : 'MM/DD/YYYY hh:mm';
+
+ const startSectionsContainer = getFieldSectionsContainer(0);
+ const expectedStartValueStr = expectedValues[0]
+ ? adapterToUse.format(
+ expectedValues[0],
+ hasMeridiem ? 'keyboardDateTime12h' : 'keyboardDateTime24h',
+ )
+ : expectedPlaceholder;
+ expectFieldValueV7(startSectionsContainer, expectedStartValueStr);
+
+ const endSectionsContainer = getFieldSectionsContainer(1);
+ const expectedEndValueStr = expectedValues[1]
+ ? adapterToUse.format(
+ expectedValues[1],
+ hasMeridiem ? 'keyboardDateTime12h' : 'keyboardDateTime24h',
+ )
+ : expectedPlaceholder;
+ expectFieldValueV7(endSectionsContainer, expectedEndValueStr);
+ },
+ setNewValue: (value, { isOpened, applySameValue, setEndDate = false }) => {
+ if (!isOpened) {
+ openPicker({
+ type: 'date-time-range',
+ variant: 'mobile',
+ initialFocus: setEndDate ? 'end' : 'start',
+ });
+ }
+ let newValue: any[];
+ if (applySameValue) {
+ newValue = value;
+ } else if (setEndDate) {
+ newValue = [
+ value[0],
+ adapterToUse.addMinutes(adapterToUse.addHours(adapterToUse.addDays(value[1], 1), 1), 5),
+ ];
+ } else {
+ newValue = [
+ adapterToUse.addMinutes(adapterToUse.addHours(adapterToUse.addDays(value[0], 1), 1), 5),
+ value[1],
+ ];
+ }
+
+ // if we want to set the end date, we firstly need to switch to end date "range position"
+ if (setEndDate) {
+ userEvent.mousePress(
+ screen.getByRole('button', { name: adapterToUse.format(value[1], 'shortDate') }),
+ );
+ }
+
+ userEvent.mousePress(
+ screen.getByRole('gridcell', {
+ name: adapterToUse.getDate(newValue[setEndDate ? 1 : 0]).toString(),
+ }),
+ );
+ const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale();
+ const hours = adapterToUse.format(
+ newValue[setEndDate ? 1 : 0],
+ hasMeridiem ? 'hours12h' : 'hours24h',
+ );
+ const hoursNumber = adapterToUse.getHours(newValue[setEndDate ? 1 : 0]);
+ userEvent.mousePress(screen.getByRole('option', { name: `${parseInt(hours, 10)} hours` }));
+ userEvent.mousePress(
+ screen.getByRole('option', {
+ name: `${adapterToUse.getMinutes(newValue[setEndDate ? 1 : 0])} minutes`,
+ }),
+ );
+ if (hasMeridiem) {
+ // meridiem is an extra view on `MobileDateTimeRangePicker`
+ // we need to click it to finish selection
+ userEvent.mousePress(screen.getByRole('option', { name: hoursNumber >= 12 ? 'PM' : 'AM' }));
+ }
+ // Close the picker
+ if (!isOpened) {
+ // eslint-disable-next-line material-ui/disallow-active-element-as-key-event-target
+ fireEvent.keyDown(document.activeElement!, { key: 'Escape' });
+ clock.runToLast();
+ } else {
+ // return to the start date view in case we'd like to repeat the selection process
+ userEvent.mousePress(
+ screen.getByRole('button', { name: adapterToUse.format(newValue[0], 'shortDate') }),
+ );
+ }
+
+ return newValue;
+ },
+ }));
+});
diff --git a/packages/x-date-pickers-pro/src/internals/hooks/useMobileRangePicker/useMobileRangePicker.tsx b/packages/x-date-pickers-pro/src/internals/hooks/useMobileRangePicker/useMobileRangePicker.tsx
index b0095be48249..e0016ea48423 100644
--- a/packages/x-date-pickers-pro/src/internals/hooks/useMobileRangePicker/useMobileRangePicker.tsx
+++ b/packages/x-date-pickers-pro/src/internals/hooks/useMobileRangePicker/useMobileRangePicker.tsx
@@ -196,7 +196,10 @@ export const useMobileRangePicker = <
...contextLocaleText,
...localeText,
};
- let labelledById = labelId;
+ let labelledById =
+ pickerParams.valueType === 'date-time'
+ ? `${labelId}-start-toolbar ${labelId}-end-toolbar`
+ : labelId;
if (isToolbarHidden) {
const labels: string[] = [];
if (fieldType === 'multi-input') {
diff --git a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.tsx b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.tsx
index 3aae2c8dcb26..5c309e3f02e4 100644
--- a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.tsx
+++ b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.tsx
@@ -292,7 +292,7 @@ export const MultiSectionDigitalClock = React.forwardRef(function MultiSectionDi
value,
ampm,
utils,
- isDisabled: (hours) => disabled || isTimeDisabled(hours, 'hours'),
+ isDisabled: (hours) => isTimeDisabled(hours, 'hours'),
timeStep: timeSteps.hours,
resolveAriaLabel: localeText.hoursClockNumberText,
valueOrReferenceDate,
@@ -312,7 +312,7 @@ export const MultiSectionDigitalClock = React.forwardRef(function MultiSectionDi
items: getTimeSectionOptions({
value: utils.getMinutes(valueOrReferenceDate),
utils,
- isDisabled: (minutes) => disabled || isTimeDisabled(minutes, 'minutes'),
+ isDisabled: (minutes) => isTimeDisabled(minutes, 'minutes'),
resolveLabel: (minutes) => utils.format(utils.setMinutes(now, minutes), 'minutes'),
timeStep: timeSteps.minutes,
hasValue: !!value,
@@ -333,7 +333,7 @@ export const MultiSectionDigitalClock = React.forwardRef(function MultiSectionDi
items: getTimeSectionOptions({
value: utils.getSeconds(valueOrReferenceDate),
utils,
- isDisabled: (seconds) => disabled || isTimeDisabled(seconds, 'seconds'),
+ isDisabled: (seconds) => isTimeDisabled(seconds, 'seconds'),
resolveLabel: (seconds) => utils.format(utils.setSeconds(now, seconds), 'seconds'),
timeStep: timeSteps.seconds,
hasValue: !!value,
@@ -384,7 +384,6 @@ export const MultiSectionDigitalClock = React.forwardRef(function MultiSectionDi
meridiemMode,
setValueAndGoToNextView,
valueOrReferenceDate,
- disabled,
isTimeDisabled,
handleMeridiemChange,
],
diff --git a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx
index ced796ed21fa..ced944954913 100644
--- a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx
+++ b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx
@@ -187,7 +187,9 @@ export const MultiSectionDigitalClockSection = React.forwardRef(
{...other}
>
{items.map((option, index) => {
- if (skipDisabled && option.isDisabled?.(option.value)) {
+ const isItemDisabled = option.isDisabled?.(option.value);
+ const isDisabled = disabled || isItemDisabled;
+ if (skipDisabled && isDisabled) {
return null;
}
const isSelected = option.isSelected(option.value);
@@ -198,11 +200,11 @@ export const MultiSectionDigitalClockSection = React.forwardRef(
key={option.label}
onClick={() => !readOnly && onChange(option.value)}
selected={isSelected}
- disabled={disabled || option.isDisabled?.(option.value)}
+ disabled={isDisabled}
disableRipple={readOnly}
role="option"
// aria-readonly is not supported here and does not have any effect
- aria-disabled={readOnly}
+ aria-disabled={readOnly || isDisabled || undefined}
aria-label={option.ariaLabel}
aria-selected={isSelected}
tabIndex={tabIndex}
diff --git a/test/utils/pickers/assertions.ts b/test/utils/pickers/assertions.ts
index e9ce9c7288d8..b66dd0ee095e 100644
--- a/test/utils/pickers/assertions.ts
+++ b/test/utils/pickers/assertions.ts
@@ -30,11 +30,11 @@ export const expectFieldPlaceholderV6 = (
};
export function expectPickerChangeHandlerValue(
- type: 'date' | 'date-time' | 'time' | 'date-range',
+ type: 'date' | 'date-time' | 'time' | 'date-range' | 'date-time-range',
spyCallback: SinonSpy,
expectedValue: any,
) {
- if (type === 'date-range') {
+ if (['date-range', 'date-time-range'].includes(type)) {
spyCallback.lastCall.firstArg.forEach((value, index) => {
expect(value).to.deep.equal(expectedValue[index]);
});
diff --git a/test/utils/pickers/describePicker/describePicker.tsx b/test/utils/pickers/describePicker/describePicker.tsx
index 7752ded4ed3f..aee9affd8606 100644
--- a/test/utils/pickers/describePicker/describePicker.tsx
+++ b/test/utils/pickers/describePicker/describePicker.tsx
@@ -134,23 +134,33 @@ function innerDescribePicker(ElementToTest: React.ElementType, options: Describe
this.skip();
}
- render();
+ render(
+ ,
+ );
if (variant === 'desktop') {
- expect(screen.queryByMuiTest('picker-toolbar')).to.equal(null);
+ expect(screen.queryByTestId('pickers-toolbar')).to.equal(null);
} else {
- expect(screen.getByMuiTest('picker-toolbar')).toBeVisible();
+ expect(screen.getByTestId('pickers-toolbar')).toBeVisible();
}
});
- it('should render toolbar when `hidden` is `false`', function test() {
+ it.skip('should render toolbar when `hidden` is `false`', function test() {
if (hasNoView) {
this.skip();
}
- render();
+ render(
+ ,
+ );
- expect(screen.getByMuiTest('picker-toolbar')).toBeVisible();
+ expect(screen.getByTestId('pickers-toolbar')).toBeVisible();
});
it('should not render toolbar when `hidden` is `true`', function test() {
@@ -158,9 +168,14 @@ function innerDescribePicker(ElementToTest: React.ElementType, options: Describe
this.skip();
}
- render();
+ render(
+ ,
+ );
- expect(screen.queryByMuiTest('picker-toolbar')).to.equal(null);
+ expect(screen.queryByTestId('pickers-toolbar')).to.equal(null);
});
});
}
diff --git a/test/utils/pickers/describeRangeValidation/testDayViewRangeValidation.tsx b/test/utils/pickers/describeRangeValidation/testDayViewRangeValidation.tsx
index 6d5e8ead8f97..fc1a3865ab93 100644
--- a/test/utils/pickers/describeRangeValidation/testDayViewRangeValidation.tsx
+++ b/test/utils/pickers/describeRangeValidation/testDayViewRangeValidation.tsx
@@ -3,17 +3,12 @@ import { expect } from 'chai';
import { screen } from '@mui-internal/test-utils';
import { adapterToUse } from 'test/utils/pickers';
-const isDisable = (el: HTMLElement) => el.getAttribute('disabled') !== null;
+const isDisabled = (el: HTMLElement) => el.getAttribute('disabled') !== null;
-const isFieldElement = (el: HTMLElement) => el.className.includes('MuiPickersInput');
-
-const testDisabledDate = (day: string, expectedAnswer: boolean[], isDesktop: boolean) => {
- expect(
- screen
- .getAllByText(day)
- .filter((el) => !isFieldElement(el))
- .map(isDisable),
- ).to.deep.equal(isDesktop ? expectedAnswer : expectedAnswer.slice(0, 1));
+const testDisabledDate = (day: string, expectedAnswer: boolean[], isSingleCalendar: boolean) => {
+ expect(screen.getAllByRole('gridcell', { name: day }).map(isDisabled)).to.deep.equal(
+ isSingleCalendar ? expectedAnswer.slice(0, 1) : expectedAnswer,
+ );
};
const testMonthSwitcherAreDisable = (areDisable: [boolean, boolean]) => {
@@ -44,6 +39,7 @@ export function testDayViewRangeValidation(ElementToTest, getOptions) {
}
const isDesktop = variant === 'desktop';
+ const includesTimeView = views.includes('hours');
const defaultProps = {
referenceDate: adapterToUse.date('2018-03-12'),
@@ -62,8 +58,8 @@ export function testDayViewRangeValidation(ElementToTest, getOptions) {
/>,
);
- testDisabledDate('10', [false, true], isDesktop);
- testDisabledDate('11', [true, true], isDesktop);
+ testDisabledDate('10', [false, true], !isDesktop || includesTimeView);
+ testDisabledDate('11', [true, true], !isDesktop || includesTimeView);
});
it('should apply disablePast', function test() {
@@ -80,14 +76,26 @@ export function testDayViewRangeValidation(ElementToTest, getOptions) {
const tomorrow = adapterToUse.addDays(now, 1);
const yesterday = adapterToUse.addDays(now, -1);
- testDisabledDate(adapterToUse.format(now, 'dayOfMonth'), [false, false], isDesktop);
- testDisabledDate(adapterToUse.format(tomorrow, 'dayOfMonth'), [false, false], isDesktop);
+ testDisabledDate(
+ adapterToUse.format(now, 'dayOfMonth'),
+ [false, false],
+ !isDesktop || includesTimeView,
+ );
+ testDisabledDate(
+ adapterToUse.format(tomorrow, 'dayOfMonth'),
+ [false, false],
+ !isDesktop || includesTimeView,
+ );
if (!adapterToUse.isSameMonth(yesterday, tomorrow)) {
setProps({ value: [yesterday, null] });
clock.runToLast();
}
- testDisabledDate(adapterToUse.format(yesterday, 'dayOfMonth'), [true, false], isDesktop);
+ testDisabledDate(
+ adapterToUse.format(yesterday, 'dayOfMonth'),
+ [true, false],
+ !isDesktop || includesTimeView,
+ );
});
it('should apply disableFuture', function test() {
@@ -104,14 +112,26 @@ export function testDayViewRangeValidation(ElementToTest, getOptions) {
const tomorrow = adapterToUse.addDays(now, 1);
const yesterday = adapterToUse.addDays(now, -1);
- testDisabledDate(adapterToUse.format(now, 'dayOfMonth'), [false, true], isDesktop);
- testDisabledDate(adapterToUse.format(tomorrow, 'dayOfMonth'), [true, true], isDesktop);
+ testDisabledDate(
+ adapterToUse.format(now, 'dayOfMonth'),
+ [false, true],
+ !isDesktop || includesTimeView,
+ );
+ testDisabledDate(
+ adapterToUse.format(tomorrow, 'dayOfMonth'),
+ [true, true],
+ !isDesktop || includesTimeView,
+ );
if (!adapterToUse.isSameMonth(yesterday, tomorrow)) {
setProps({ value: [yesterday, null] });
clock.runToLast();
}
- testDisabledDate(adapterToUse.format(yesterday, 'dayOfMonth'), [false, true], isDesktop);
+ testDisabledDate(
+ adapterToUse.format(yesterday, 'dayOfMonth'),
+ [false, true],
+ !isDesktop || includesTimeView,
+ );
});
it('should apply minDate', function test() {
@@ -125,10 +145,10 @@ export function testDayViewRangeValidation(ElementToTest, getOptions) {
/>,
);
- testDisabledDate('1', [true, false], isDesktop);
- testDisabledDate('3', [true, false], isDesktop);
- testDisabledDate('4', [false, false], isDesktop);
- testDisabledDate('15', [false, false], isDesktop);
+ testDisabledDate('1', [true, false], !isDesktop || includesTimeView);
+ testDisabledDate('3', [true, false], !isDesktop || includesTimeView);
+ testDisabledDate('4', [false, false], !isDesktop || includesTimeView);
+ testDisabledDate('15', [false, false], !isDesktop || includesTimeView);
testMonthSwitcherAreDisable([true, false]);
});
@@ -144,10 +164,10 @@ export function testDayViewRangeValidation(ElementToTest, getOptions) {
/>,
);
- testDisabledDate('1', [false, true], isDesktop);
- testDisabledDate('4', [false, true], isDesktop);
- testDisabledDate('5', [true, true], isDesktop);
- testDisabledDate('15', [true, true], isDesktop);
+ testDisabledDate('1', [false, true], !isDesktop || includesTimeView);
+ testDisabledDate('4', [false, true], !isDesktop || includesTimeView);
+ testDisabledDate('5', [true, true], !isDesktop || includesTimeView);
+ testDisabledDate('15', [true, true], !isDesktop || includesTimeView);
testMonthSwitcherAreDisable([false, true]);
});
diff --git a/test/utils/pickers/describeValue/testControlledUnControlled.tsx b/test/utils/pickers/describeValue/testControlledUnControlled.tsx
index 63477036c9b6..493b9c66cce4 100644
--- a/test/utils/pickers/describeValue/testControlledUnControlled.tsx
+++ b/test/utils/pickers/describeValue/testControlledUnControlled.tsx
@@ -28,6 +28,9 @@ export const testControlledUnControlled: DescribeValueTestSuite = (
const params = pickerParams as DescribeValueOptions<'picker', any>;
+ const isRangeType = params.type === 'date-range' || params.type === 'date-time-range';
+ const isDesktopRange = params.variant === 'desktop' && isRangeType;
+
describe('Controlled / uncontrolled value', () => {
it('should render `props.defaultValue` if no `props.value` is passed', () => {
renderWithProps({ enableAccessibleFieldDOMStructure: true, defaultValue: values[0] });
@@ -170,10 +173,7 @@ export const testControlledUnControlled: DescribeValueTestSuite = (
});
it('should have correct labelledby relationship when toolbar is shown', () => {
- if (
- componentFamily !== 'picker' ||
- (params.variant === 'desktop' && params.type === 'date-range')
- ) {
+ if (componentFamily !== 'picker' || isDesktopRange) {
return;
}
@@ -184,14 +184,15 @@ export const testControlledUnControlled: DescribeValueTestSuite = (
localeText: { toolbarTitle: 'Test toolbar' },
});
- expect(screen.getByLabelText('Test toolbar')).to.have.attribute('role', 'dialog');
+ if (params.variant === 'mobile' && params.type === 'date-time-range') {
+ expect(screen.getByLabelText('Start End')).to.have.attribute('role', 'dialog');
+ } else {
+ expect(screen.getByLabelText('Test toolbar')).to.have.attribute('role', 'dialog');
+ }
});
it('should have correct labelledby relationship with provided label when toolbar is hidden', () => {
- if (
- componentFamily !== 'picker' ||
- (params.variant === 'desktop' && params.type === 'date-range')
- ) {
+ if (componentFamily !== 'picker' || isDesktopRange) {
return;
}
@@ -199,7 +200,7 @@ export const testControlledUnControlled: DescribeValueTestSuite = (
enableAccessibleFieldDOMStructure: true,
open: true,
slotProps: { toolbar: { hidden: true } },
- ...(params.type === 'date-range'
+ ...(isRangeType
? {
localeText: {
start: 'test',
@@ -213,10 +214,7 @@ export const testControlledUnControlled: DescribeValueTestSuite = (
});
it('should have correct labelledby relationship without label and hidden toolbar but external props', () => {
- if (
- componentFamily !== 'picker' ||
- (params.variant === 'desktop' && params.type === 'date-range')
- ) {
+ if (componentFamily !== 'picker' || isDesktopRange) {
return;
}
@@ -226,7 +224,7 @@ export const testControlledUnControlled: DescribeValueTestSuite = (
= (
expect(fieldRoot).to.have.class(inputBaseClasses.error);
expect(fieldRoot).to.have.attribute('aria-invalid', 'true');
- if (params.type === 'date-range' && !params.isSingleInput) {
+ if (isRangeType && !params.isSingleInput) {
const fieldRootEnd = getFieldInputRoot(1);
expect(fieldRootEnd).to.have.class(inputBaseClasses.error);
expect(fieldRootEnd).to.have.attribute('aria-invalid', 'true');
diff --git a/test/utils/pickers/describeValue/testPickerActionBar.tsx b/test/utils/pickers/describeValue/testPickerActionBar.tsx
index db94681f65c8..c58458a91776 100644
--- a/test/utils/pickers/describeValue/testPickerActionBar.tsx
+++ b/test/utils/pickers/describeValue/testPickerActionBar.tsx
@@ -27,6 +27,8 @@ export const testPickerActionBar: DescribeValueTestSuite = (
return;
}
+ const isRangeType = pickerParams.type === 'date-range' || pickerParams.type === 'date-time-range';
+
describe('Picker action bar', () => {
describe('clear action', () => {
it('should call onClose, onChange with empty value and onAccept with empty value', () => {
@@ -105,7 +107,7 @@ export const testPickerActionBar: DescribeValueTestSuite = (
expect(onChange.callCount).to.equal(
getExpectedOnChangeCount(componentFamily, pickerParams) + 1,
);
- if (pickerParams.type === 'date-range') {
+ if (isRangeType) {
values[0].forEach((value, index) => {
expect(onChange.lastCall.args[0][index]).toEqualDateTime(value);
});
@@ -245,7 +247,7 @@ export const testPickerActionBar: DescribeValueTestSuite = (
let startOfToday: any;
if (pickerParams.type === 'date') {
startOfToday = adapterToUse.startOfDay(adapterToUse.date());
- } else if (pickerParams.type === 'date-range') {
+ } else if (isRangeType) {
startOfToday = [adapterToUse.date(), adapterToUse.date()];
} else {
startOfToday = adapterToUse.date();
diff --git a/test/utils/pickers/describeValue/testPickerOpenCloseLifeCycle.tsx b/test/utils/pickers/describeValue/testPickerOpenCloseLifeCycle.tsx
index fc0824b139e4..24ef112fc206 100644
--- a/test/utils/pickers/describeValue/testPickerOpenCloseLifeCycle.tsx
+++ b/test/utils/pickers/describeValue/testPickerOpenCloseLifeCycle.tsx
@@ -16,8 +16,8 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite
return;
}
- const viewWrapperRole =
- pickerParams.type === 'date-range' && pickerParams.variant === 'desktop' ? 'tooltip' : 'dialog';
+ const isRangeType = pickerParams.type === 'date-range' || pickerParams.type === 'date-time-range';
+ const viewWrapperRole = isRangeType && pickerParams.variant === 'desktop' ? 'tooltip' : 'dialog';
describe('Picker open / close lifecycle', () => {
it('should not open on mount if `props.open` is false', () => {
@@ -70,7 +70,7 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite
// Change the value
let newValue = setNewValue(values[0], { isOpened: true, selectSection, pressKey });
expect(onChange.callCount).to.equal(getExpectedOnChangeCount(componentFamily, pickerParams));
- if (pickerParams.type === 'date-range') {
+ if (isRangeType) {
newValue = setNewValue(newValue, {
isOpened: true,
setEndDate: true,
@@ -128,7 +128,7 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite
// Change the value
let newValue = setNewValue(values[0], { isOpened: true, selectSection, pressKey });
expect(onChange.callCount).to.equal(getExpectedOnChangeCount(componentFamily, pickerParams));
- if (pickerParams.type === 'date-range') {
+ if (isRangeType) {
newValue = setNewValue(newValue, {
isOpened: true,
setEndDate: true,
@@ -165,7 +165,7 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite
// Change the value (same value)
setNewValue(values[0], { isOpened: true, applySameValue: true, selectSection, pressKey });
- if (pickerParams.type === 'date-range') {
+ if (isRangeType) {
setNewValue(values[0], {
isOpened: true,
applySameValue: true,
@@ -202,7 +202,7 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite
let newValue = setNewValue(values[0], { isOpened: true, selectSection, pressKey });
const initialChangeCount = getExpectedOnChangeCount(componentFamily, pickerParams);
expect(onChange.callCount).to.equal(initialChangeCount);
- if (pickerParams.type === 'date-range') {
+ if (isRangeType) {
newValue = setNewValue(newValue, {
isOpened: true,
setEndDate: true,
@@ -220,8 +220,12 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite
// Change the value
let newValueBis = setNewValue(newValue, { isOpened: true, selectSection, pressKey });
- if (pickerParams.type === 'date-range') {
- expect(onChange.callCount).to.equal(3);
+ if (isRangeType) {
+ expect(onChange.callCount).to.equal(
+ initialChangeCount +
+ getExpectedOnChangeCount(componentFamily, pickerParams) * 2 -
+ (pickerParams.type === 'date-time-range' ? 1 : 0),
+ );
newValueBis = setNewValue(newValueBis, {
isOpened: true,
setEndDate: true,
@@ -269,7 +273,7 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite
userEvent.keyPress(document.activeElement!, { key: 'Escape' });
expect(onChange.callCount).to.equal(getExpectedOnChangeCount(componentFamily, pickerParams));
expect(onAccept.callCount).to.equal(1);
- if (pickerParams.type === 'date-range') {
+ if (isRangeType) {
newValue.forEach((value, index) => {
expect(onChange.lastCall.args[0][index]).toEqualDateTime(value);
});
@@ -281,7 +285,7 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite
it('should call onClose when clicking outside of the picker without prior change', function test() {
// TODO: Fix this test and enable it on mobile and date-range
- if (pickerParams.variant === 'mobile' || pickerParams.type === 'date-range') {
+ if (pickerParams.variant === 'mobile' || isRangeType) {
this.skip();
}
@@ -310,7 +314,7 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite
it('should call onClose and onAccept with the live value when clicking outside of the picker', function test() {
// TODO: Fix this test and enable it on mobile and date-range
- if (pickerParams.variant === 'mobile' || pickerParams.type === 'date-range') {
+ if (pickerParams.variant === 'mobile' || isRangeType) {
this.skip();
}
diff --git a/test/utils/pickers/misc.ts b/test/utils/pickers/misc.ts
index 14afbe820874..9e189dc2c8dc 100644
--- a/test/utils/pickers/misc.ts
+++ b/test/utils/pickers/misc.ts
@@ -40,6 +40,12 @@ export const getExpectedOnChangeCount = (
params.variant === 'desktop' ? 'multi-section-digital-clock' : 'clock',
);
}
+ if (componentFamily === 'picker' && params.type === 'date-time-range') {
+ return (
+ getChangeCountForComponentFamily(componentFamily) +
+ getChangeCountForComponentFamily('multi-section-digital-clock')
+ );
+ }
if (componentFamily === 'clock') {
// the `TimeClock` fires change for both touch move and touch end
// but does not have meridiem control
diff --git a/test/utils/pickers/openPicker.ts b/test/utils/pickers/openPicker.ts
index a0726c9553f7..2de6210fca38 100644
--- a/test/utils/pickers/openPicker.ts
+++ b/test/utils/pickers/openPicker.ts
@@ -8,7 +8,7 @@ export type OpenPickerParams =
variant: 'mobile' | 'desktop';
}
| {
- type: 'date-range';
+ type: 'date-range' | 'date-time-range';
variant: 'mobile' | 'desktop';
initialFocus: 'start' | 'end';
/**
@@ -18,11 +18,12 @@ export type OpenPickerParams =
};
export const openPicker = (params: OpenPickerParams) => {
+ const isRangeType = params.type === 'date-range' || params.type === 'date-time-range';
const fieldSectionsContainer = getFieldSectionsContainer(
- params.type === 'date-range' && !params.isSingleInput && params.initialFocus === 'end' ? 1 : 0,
+ isRangeType && !params.isSingleInput && params.initialFocus === 'end' ? 1 : 0,
);
- if (params.type === 'date-range') {
+ if (isRangeType) {
userEvent.mousePress(fieldSectionsContainer);
if (params.isSingleInput && params.initialFocus === 'end') {