Skip to content

Commit

Permalink
[DateTimeRangePicker] Fix validation behavior (#12243)
Browse files Browse the repository at this point in the history
  • Loading branch information
LukasTy authored Feb 29, 2024
1 parent 0bb5ec1 commit a31fb9f
Show file tree
Hide file tree
Showing 21 changed files with 683 additions and 91 deletions.
3 changes: 3 additions & 0 deletions docs/pages/x/api/date-pickers/date-time-range-picker.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "<ul><li><a href=\"/x/react-date-pickers/date-time-range-picker/\">Date Time Range Picker <a href=\"/x/introduction/licensing/#pro-plan\" title=\"Pro plan\"><span class=\"plan-pro\"></span></a></a></li></ul>",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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": "<ul><li><a href=\"/x/react-date-pickers/date-time-range-picker/\">Date Time Range Picker <a href=\"/x/introduction/licensing/#pro-plan\" title=\"Pro plan\"><span class=\"plan-pro\"></span></a></a></li></ul>",
Expand Down
12 changes: 6 additions & 6 deletions docs/pages/x/api/date-pickers/mobile-date-range-picker.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down Expand Up @@ -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.",
Expand Down
15 changes: 9 additions & 6 deletions docs/pages/x/api/date-pickers/mobile-date-time-range-picker.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down Expand Up @@ -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.",
Expand All @@ -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": "<ul><li><a href=\"/x/react-date-pickers/date-time-range-picker/\">Date Time Range Picker <a href=\"/x/introduction/licensing/#pro-plan\" title=\"Pro plan\"><span class=\"plan-pro\"></span></a></a></li></ul>",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@ const DateTimeRangePickerToolbar = React.forwardRef(function DateTimeRangePicker
hidden,
toolbarFormat,
toolbarPlaceholder,
titleId,
};

const localeText = useLocaleText<TDate>();
Expand Down Expand Up @@ -182,6 +181,10 @@ const DateTimeRangePickerToolbar = React.forwardRef(function DateTimeRangePicker
[onChange, onRangePositionChange, props.value, rangePosition, utils],
);

if (hidden) {
return null;
}

return (
<DateTimeRangePickerToolbarRoot
className={clsx(className, classes.root)}
Expand All @@ -199,6 +202,7 @@ const DateTimeRangePickerToolbar = React.forwardRef(function DateTimeRangePicker
view={rangePosition === 'start' ? view : undefined}
className={classes.startToolbar}
onChange={handleOnChange}
titleId={titleId ? `${titleId}-start-toolbar` : undefined}
{...commonToolbarProps}
/>
<DateTimeRangePickerToolbarEnd<TDate>
Expand All @@ -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}
/>
</DateTimeRangePickerToolbarRoot>
Expand Down
23 changes: 21 additions & 2 deletions packages/x-date-pickers-pro/src/DateTimeRangePicker/shared.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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('<DateTimeRangePicker /> - Describes', () => {
const { render } = createPickerRenderer({ clock: 'fake' });

describeConformance(<DateTimeRangePicker />, () => ({
classes: {} as any,
render,
muiName: 'MuiDateTimeRangePicker',
wrapMount: wrapPickerMount,
refInstanceof: window.HTMLDivElement,
skip: [
'componentProp',
'componentsProp',
'themeDefaultProps',
'themeStyleOverrides',
'themeVariants',
'mergeClassName',
'propsSpread',
'rootClass',
'reactTestRenderer',
],
}));
});
Original file line number Diff line number Diff line change
@@ -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('<DesktopDateTimeRangePicker />', () => {
const { render } = createPickerRenderer({
clock: 'fake',
clockConfig: new Date(2018, 0, 10, 10, 16, 0),
});

describe('disabled dates', () => {
it('should respect the "disablePast" prop', () => {
render(<DesktopDateTimeRangePicker enableAccessibleFieldDOMStructure disablePast open />);

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(
<DesktopDateTimeRangePicker
enableAccessibleFieldDOMStructure
disablePast
open
referenceDate={adapterToUse.date('2018-01-11')}
/>,
);

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(<DesktopDateTimeRangePicker enableAccessibleFieldDOMStructure disableFuture open />);

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(
<DesktopDateTimeRangePicker
enableAccessibleFieldDOMStructure
disableFuture
open
referenceDate={adapterToUse.date('2018-01-09')}
/>,
);

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(
<DesktopDateTimeRangePicker
enableAccessibleFieldDOMStructure
minDateTime={adapterToUse.date('2018-01-10T10:16:00')}
referenceDate={adapterToUse.date('2018-01-10T10:00:00')}
open
/>,
);

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(
<DesktopDateTimeRangePicker
enableAccessibleFieldDOMStructure
maxDateTime={adapterToUse.date('2018-01-10T10:16:00')}
referenceDate={adapterToUse.date('2018-01-10T10:00:00')}
open
/>,
);

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',
);
});
});
});
Loading

0 comments on commit a31fb9f

Please sign in to comment.