Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DateTimeRangePicker] Fix validation behavior #12243

Merged
merged 19 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading