From 412d8db2feb1a9e14ecc9cbe4270cf25749b4678 Mon Sep 17 00:00:00 2001 From: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Date: Fri, 26 Aug 2022 14:12:56 +0200 Subject: [PATCH] [DateRangePicker] disable day on date range (#5773) --- .../date-pickers/date-range-picker-pt.json | 2 +- .../date-pickers/date-range-picker-zh.json | 2 +- .../date-pickers/date-range-picker.json | 2 +- .../desktop-date-range-picker-pt.json | 2 +- .../desktop-date-range-picker-zh.json | 2 +- .../desktop-date-range-picker.json | 2 +- .../mobile-date-range-picker-pt.json | 2 +- .../mobile-date-range-picker-zh.json | 2 +- .../mobile-date-range-picker.json | 2 +- .../static-date-range-picker-pt.json | 2 +- .../static-date-range-picker-zh.json | 2 +- .../static-date-range-picker.json | 2 +- .../src/DateRangePicker/DateRangePicker.tsx | 1 + .../DateRangePicker/DateRangePickerView.tsx | 21 +++++++++-- .../DesktopDateRangePicker.test.tsx | 29 +++++++++++++++ .../DesktopDateRangePicker.tsx | 1 + .../MobileDateRangePicker.test.tsx | 37 +++++++++++++++++-- .../MobileDateRangePicker.tsx | 1 + .../StaticDateRangePicker.tsx | 1 + .../validation/useDateRangeValidation.ts | 25 ++++++++++--- .../src/internal/models/dateRange.ts | 14 +++++++ 21 files changed, 129 insertions(+), 25 deletions(-) diff --git a/docs/translations/api-docs/date-pickers/date-range-picker-pt.json b/docs/translations/api-docs/date-pickers/date-range-picker-pt.json index 9576efec6cfe..6a76893c0e23 100644 --- a/docs/translations/api-docs/date-pickers/date-range-picker-pt.json +++ b/docs/translations/api-docs/date-pickers/date-range-picker-pt.json @@ -47,7 +47,7 @@ "renderLoading": "Component displaying when passed loading true.

Signature:
function() => React.ReactNode

returns (React.ReactNode): The node to render when loading.", "rifmFormatter": "Custom formatter to be passed into Rifm component.

Signature:
function(str: string) => string
str: The un-formatted string.
returns (string): The formatted string.", "rightArrowButtonText": "Right arrow icon aria-label text.", - "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate) => boolean
day: The date to test.
returns (boolean): Returns true if the date should be disabled.", + "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate, position: string) => boolean
day: The date to test.
position: The date to test, 'start' or 'end'.
returns (boolean): Returns true if the date should be disabled.", "shouldDisableMonth": "Disable specific months dynamically. Works like shouldDisableDate but for month selection view @DateIOType.

Signature:
function(month: TDate) => boolean
month: The month to check.
returns (boolean): If true the month will be disabled.", "shouldDisableYear": "Disable specific years dynamically. Works like shouldDisableDate but for year selection view @DateIOType.

Signature:
function(year: TDate) => boolean
year: The year to test.
returns (boolean): Returns true if the year should be disabled.", "showDaysOutsideCurrentMonth": "If true, days that have outsideCurrentMonth={true} are displayed.", diff --git a/docs/translations/api-docs/date-pickers/date-range-picker-zh.json b/docs/translations/api-docs/date-pickers/date-range-picker-zh.json index 9576efec6cfe..6a76893c0e23 100644 --- a/docs/translations/api-docs/date-pickers/date-range-picker-zh.json +++ b/docs/translations/api-docs/date-pickers/date-range-picker-zh.json @@ -47,7 +47,7 @@ "renderLoading": "Component displaying when passed loading true.

Signature:
function() => React.ReactNode

returns (React.ReactNode): The node to render when loading.", "rifmFormatter": "Custom formatter to be passed into Rifm component.

Signature:
function(str: string) => string
str: The un-formatted string.
returns (string): The formatted string.", "rightArrowButtonText": "Right arrow icon aria-label text.", - "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate) => boolean
day: The date to test.
returns (boolean): Returns true if the date should be disabled.", + "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate, position: string) => boolean
day: The date to test.
position: The date to test, 'start' or 'end'.
returns (boolean): Returns true if the date should be disabled.", "shouldDisableMonth": "Disable specific months dynamically. Works like shouldDisableDate but for month selection view @DateIOType.

Signature:
function(month: TDate) => boolean
month: The month to check.
returns (boolean): If true the month will be disabled.", "shouldDisableYear": "Disable specific years dynamically. Works like shouldDisableDate but for year selection view @DateIOType.

Signature:
function(year: TDate) => boolean
year: The year to test.
returns (boolean): Returns true if the year should be disabled.", "showDaysOutsideCurrentMonth": "If true, days that have outsideCurrentMonth={true} are displayed.", diff --git a/docs/translations/api-docs/date-pickers/date-range-picker.json b/docs/translations/api-docs/date-pickers/date-range-picker.json index 9576efec6cfe..6a76893c0e23 100644 --- a/docs/translations/api-docs/date-pickers/date-range-picker.json +++ b/docs/translations/api-docs/date-pickers/date-range-picker.json @@ -47,7 +47,7 @@ "renderLoading": "Component displaying when passed loading true.

Signature:
function() => React.ReactNode

returns (React.ReactNode): The node to render when loading.", "rifmFormatter": "Custom formatter to be passed into Rifm component.

Signature:
function(str: string) => string
str: The un-formatted string.
returns (string): The formatted string.", "rightArrowButtonText": "Right arrow icon aria-label text.", - "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate) => boolean
day: The date to test.
returns (boolean): Returns true if the date should be disabled.", + "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate, position: string) => boolean
day: The date to test.
position: The date to test, 'start' or 'end'.
returns (boolean): Returns true if the date should be disabled.", "shouldDisableMonth": "Disable specific months dynamically. Works like shouldDisableDate but for month selection view @DateIOType.

Signature:
function(month: TDate) => boolean
month: The month to check.
returns (boolean): If true the month will be disabled.", "shouldDisableYear": "Disable specific years dynamically. Works like shouldDisableDate but for year selection view @DateIOType.

Signature:
function(year: TDate) => boolean
year: The year to test.
returns (boolean): Returns true if the year should be disabled.", "showDaysOutsideCurrentMonth": "If true, days that have outsideCurrentMonth={true} are displayed.", diff --git a/docs/translations/api-docs/date-pickers/desktop-date-range-picker-pt.json b/docs/translations/api-docs/date-pickers/desktop-date-range-picker-pt.json index a74e8a7b4839..efd923f090cf 100644 --- a/docs/translations/api-docs/date-pickers/desktop-date-range-picker-pt.json +++ b/docs/translations/api-docs/date-pickers/desktop-date-range-picker-pt.json @@ -45,7 +45,7 @@ "renderLoading": "Component displaying when passed loading true.

Signature:
function() => React.ReactNode

returns (React.ReactNode): The node to render when loading.", "rifmFormatter": "Custom formatter to be passed into Rifm component.

Signature:
function(str: string) => string
str: The un-formatted string.
returns (string): The formatted string.", "rightArrowButtonText": "Right arrow icon aria-label text.", - "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate) => boolean
day: The date to test.
returns (boolean): Returns true if the date should be disabled.", + "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate, position: string) => boolean
day: The date to test.
position: The date to test, 'start' or 'end'.
returns (boolean): Returns true if the date should be disabled.", "shouldDisableMonth": "Disable specific months dynamically. Works like shouldDisableDate but for month selection view @DateIOType.

Signature:
function(month: TDate) => boolean
month: The month to check.
returns (boolean): If true the month will be disabled.", "shouldDisableYear": "Disable specific years dynamically. Works like shouldDisableDate but for year selection view @DateIOType.

Signature:
function(year: TDate) => boolean
year: The year to test.
returns (boolean): Returns true if the year should be disabled.", "showDaysOutsideCurrentMonth": "If true, days that have outsideCurrentMonth={true} are displayed.", diff --git a/docs/translations/api-docs/date-pickers/desktop-date-range-picker-zh.json b/docs/translations/api-docs/date-pickers/desktop-date-range-picker-zh.json index a74e8a7b4839..efd923f090cf 100644 --- a/docs/translations/api-docs/date-pickers/desktop-date-range-picker-zh.json +++ b/docs/translations/api-docs/date-pickers/desktop-date-range-picker-zh.json @@ -45,7 +45,7 @@ "renderLoading": "Component displaying when passed loading true.

Signature:
function() => React.ReactNode

returns (React.ReactNode): The node to render when loading.", "rifmFormatter": "Custom formatter to be passed into Rifm component.

Signature:
function(str: string) => string
str: The un-formatted string.
returns (string): The formatted string.", "rightArrowButtonText": "Right arrow icon aria-label text.", - "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate) => boolean
day: The date to test.
returns (boolean): Returns true if the date should be disabled.", + "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate, position: string) => boolean
day: The date to test.
position: The date to test, 'start' or 'end'.
returns (boolean): Returns true if the date should be disabled.", "shouldDisableMonth": "Disable specific months dynamically. Works like shouldDisableDate but for month selection view @DateIOType.

Signature:
function(month: TDate) => boolean
month: The month to check.
returns (boolean): If true the month will be disabled.", "shouldDisableYear": "Disable specific years dynamically. Works like shouldDisableDate but for year selection view @DateIOType.

Signature:
function(year: TDate) => boolean
year: The year to test.
returns (boolean): Returns true if the year should be disabled.", "showDaysOutsideCurrentMonth": "If true, days that have outsideCurrentMonth={true} are displayed.", diff --git a/docs/translations/api-docs/date-pickers/desktop-date-range-picker.json b/docs/translations/api-docs/date-pickers/desktop-date-range-picker.json index a74e8a7b4839..efd923f090cf 100644 --- a/docs/translations/api-docs/date-pickers/desktop-date-range-picker.json +++ b/docs/translations/api-docs/date-pickers/desktop-date-range-picker.json @@ -45,7 +45,7 @@ "renderLoading": "Component displaying when passed loading true.

Signature:
function() => React.ReactNode

returns (React.ReactNode): The node to render when loading.", "rifmFormatter": "Custom formatter to be passed into Rifm component.

Signature:
function(str: string) => string
str: The un-formatted string.
returns (string): The formatted string.", "rightArrowButtonText": "Right arrow icon aria-label text.", - "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate) => boolean
day: The date to test.
returns (boolean): Returns true if the date should be disabled.", + "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate, position: string) => boolean
day: The date to test.
position: The date to test, 'start' or 'end'.
returns (boolean): Returns true if the date should be disabled.", "shouldDisableMonth": "Disable specific months dynamically. Works like shouldDisableDate but for month selection view @DateIOType.

Signature:
function(month: TDate) => boolean
month: The month to check.
returns (boolean): If true the month will be disabled.", "shouldDisableYear": "Disable specific years dynamically. Works like shouldDisableDate but for year selection view @DateIOType.

Signature:
function(year: TDate) => boolean
year: The year to test.
returns (boolean): Returns true if the year should be disabled.", "showDaysOutsideCurrentMonth": "If true, days that have outsideCurrentMonth={true} are displayed.", diff --git a/docs/translations/api-docs/date-pickers/mobile-date-range-picker-pt.json b/docs/translations/api-docs/date-pickers/mobile-date-range-picker-pt.json index bfa2be4611d0..f7c3789b0a21 100644 --- a/docs/translations/api-docs/date-pickers/mobile-date-range-picker-pt.json +++ b/docs/translations/api-docs/date-pickers/mobile-date-range-picker-pt.json @@ -44,7 +44,7 @@ "renderLoading": "Component displaying when passed loading true.

Signature:
function() => React.ReactNode

returns (React.ReactNode): The node to render when loading.", "rifmFormatter": "Custom formatter to be passed into Rifm component.

Signature:
function(str: string) => string
str: The un-formatted string.
returns (string): The formatted string.", "rightArrowButtonText": "Right arrow icon aria-label text.", - "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate) => boolean
day: The date to test.
returns (boolean): Returns true if the date should be disabled.", + "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate, position: string) => boolean
day: The date to test.
position: The date to test, 'start' or 'end'.
returns (boolean): Returns true if the date should be disabled.", "shouldDisableMonth": "Disable specific months dynamically. Works like shouldDisableDate but for month selection view @DateIOType.

Signature:
function(month: TDate) => boolean
month: The month to check.
returns (boolean): If true the month will be disabled.", "shouldDisableYear": "Disable specific years dynamically. Works like shouldDisableDate but for year selection view @DateIOType.

Signature:
function(year: TDate) => boolean
year: The year to test.
returns (boolean): Returns true if the year should be disabled.", "showDaysOutsideCurrentMonth": "If true, days that have outsideCurrentMonth={true} are displayed.", diff --git a/docs/translations/api-docs/date-pickers/mobile-date-range-picker-zh.json b/docs/translations/api-docs/date-pickers/mobile-date-range-picker-zh.json index bfa2be4611d0..f7c3789b0a21 100644 --- a/docs/translations/api-docs/date-pickers/mobile-date-range-picker-zh.json +++ b/docs/translations/api-docs/date-pickers/mobile-date-range-picker-zh.json @@ -44,7 +44,7 @@ "renderLoading": "Component displaying when passed loading true.

Signature:
function() => React.ReactNode

returns (React.ReactNode): The node to render when loading.", "rifmFormatter": "Custom formatter to be passed into Rifm component.

Signature:
function(str: string) => string
str: The un-formatted string.
returns (string): The formatted string.", "rightArrowButtonText": "Right arrow icon aria-label text.", - "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate) => boolean
day: The date to test.
returns (boolean): Returns true if the date should be disabled.", + "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate, position: string) => boolean
day: The date to test.
position: The date to test, 'start' or 'end'.
returns (boolean): Returns true if the date should be disabled.", "shouldDisableMonth": "Disable specific months dynamically. Works like shouldDisableDate but for month selection view @DateIOType.

Signature:
function(month: TDate) => boolean
month: The month to check.
returns (boolean): If true the month will be disabled.", "shouldDisableYear": "Disable specific years dynamically. Works like shouldDisableDate but for year selection view @DateIOType.

Signature:
function(year: TDate) => boolean
year: The year to test.
returns (boolean): Returns true if the year should be disabled.", "showDaysOutsideCurrentMonth": "If true, days that have outsideCurrentMonth={true} are displayed.", diff --git a/docs/translations/api-docs/date-pickers/mobile-date-range-picker.json b/docs/translations/api-docs/date-pickers/mobile-date-range-picker.json index bfa2be4611d0..f7c3789b0a21 100644 --- a/docs/translations/api-docs/date-pickers/mobile-date-range-picker.json +++ b/docs/translations/api-docs/date-pickers/mobile-date-range-picker.json @@ -44,7 +44,7 @@ "renderLoading": "Component displaying when passed loading true.

Signature:
function() => React.ReactNode

returns (React.ReactNode): The node to render when loading.", "rifmFormatter": "Custom formatter to be passed into Rifm component.

Signature:
function(str: string) => string
str: The un-formatted string.
returns (string): The formatted string.", "rightArrowButtonText": "Right arrow icon aria-label text.", - "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate) => boolean
day: The date to test.
returns (boolean): Returns true if the date should be disabled.", + "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate, position: string) => boolean
day: The date to test.
position: The date to test, 'start' or 'end'.
returns (boolean): Returns true if the date should be disabled.", "shouldDisableMonth": "Disable specific months dynamically. Works like shouldDisableDate but for month selection view @DateIOType.

Signature:
function(month: TDate) => boolean
month: The month to check.
returns (boolean): If true the month will be disabled.", "shouldDisableYear": "Disable specific years dynamically. Works like shouldDisableDate but for year selection view @DateIOType.

Signature:
function(year: TDate) => boolean
year: The year to test.
returns (boolean): Returns true if the year should be disabled.", "showDaysOutsideCurrentMonth": "If true, days that have outsideCurrentMonth={true} are displayed.", diff --git a/docs/translations/api-docs/date-pickers/static-date-range-picker-pt.json b/docs/translations/api-docs/date-pickers/static-date-range-picker-pt.json index 8341d0575c58..9cf47868e35e 100644 --- a/docs/translations/api-docs/date-pickers/static-date-range-picker-pt.json +++ b/docs/translations/api-docs/date-pickers/static-date-range-picker-pt.json @@ -41,7 +41,7 @@ "renderLoading": "Component displaying when passed loading true.

Signature:
function() => React.ReactNode

returns (React.ReactNode): The node to render when loading.", "rifmFormatter": "Custom formatter to be passed into Rifm component.

Signature:
function(str: string) => string
str: The un-formatted string.
returns (string): The formatted string.", "rightArrowButtonText": "Right arrow icon aria-label text.", - "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate) => boolean
day: The date to test.
returns (boolean): Returns true if the date should be disabled.", + "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate, position: string) => boolean
day: The date to test.
position: The date to test, 'start' or 'end'.
returns (boolean): Returns true if the date should be disabled.", "shouldDisableMonth": "Disable specific months dynamically. Works like shouldDisableDate but for month selection view @DateIOType.

Signature:
function(month: TDate) => boolean
month: The month to check.
returns (boolean): If true the month will be disabled.", "shouldDisableYear": "Disable specific years dynamically. Works like shouldDisableDate but for year selection view @DateIOType.

Signature:
function(year: TDate) => boolean
year: The year to test.
returns (boolean): Returns true if the year should be disabled.", "showDaysOutsideCurrentMonth": "If true, days that have outsideCurrentMonth={true} are displayed.", diff --git a/docs/translations/api-docs/date-pickers/static-date-range-picker-zh.json b/docs/translations/api-docs/date-pickers/static-date-range-picker-zh.json index 8341d0575c58..9cf47868e35e 100644 --- a/docs/translations/api-docs/date-pickers/static-date-range-picker-zh.json +++ b/docs/translations/api-docs/date-pickers/static-date-range-picker-zh.json @@ -41,7 +41,7 @@ "renderLoading": "Component displaying when passed loading true.

Signature:
function() => React.ReactNode

returns (React.ReactNode): The node to render when loading.", "rifmFormatter": "Custom formatter to be passed into Rifm component.

Signature:
function(str: string) => string
str: The un-formatted string.
returns (string): The formatted string.", "rightArrowButtonText": "Right arrow icon aria-label text.", - "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate) => boolean
day: The date to test.
returns (boolean): Returns true if the date should be disabled.", + "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate, position: string) => boolean
day: The date to test.
position: The date to test, 'start' or 'end'.
returns (boolean): Returns true if the date should be disabled.", "shouldDisableMonth": "Disable specific months dynamically. Works like shouldDisableDate but for month selection view @DateIOType.

Signature:
function(month: TDate) => boolean
month: The month to check.
returns (boolean): If true the month will be disabled.", "shouldDisableYear": "Disable specific years dynamically. Works like shouldDisableDate but for year selection view @DateIOType.

Signature:
function(year: TDate) => boolean
year: The year to test.
returns (boolean): Returns true if the year should be disabled.", "showDaysOutsideCurrentMonth": "If true, days that have outsideCurrentMonth={true} are displayed.", diff --git a/docs/translations/api-docs/date-pickers/static-date-range-picker.json b/docs/translations/api-docs/date-pickers/static-date-range-picker.json index 8341d0575c58..9cf47868e35e 100644 --- a/docs/translations/api-docs/date-pickers/static-date-range-picker.json +++ b/docs/translations/api-docs/date-pickers/static-date-range-picker.json @@ -41,7 +41,7 @@ "renderLoading": "Component displaying when passed loading true.

Signature:
function() => React.ReactNode

returns (React.ReactNode): The node to render when loading.", "rifmFormatter": "Custom formatter to be passed into Rifm component.

Signature:
function(str: string) => string
str: The un-formatted string.
returns (string): The formatted string.", "rightArrowButtonText": "Right arrow icon aria-label text.", - "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate) => boolean
day: The date to test.
returns (boolean): Returns true if the date should be disabled.", + "shouldDisableDate": "Disable specific date. @DateIOType

Signature:
function(day: TDate, position: string) => boolean
day: The date to test.
position: The date to test, 'start' or 'end'.
returns (boolean): Returns true if the date should be disabled.", "shouldDisableMonth": "Disable specific months dynamically. Works like shouldDisableDate but for month selection view @DateIOType.

Signature:
function(month: TDate) => boolean
month: The month to check.
returns (boolean): If true the month will be disabled.", "shouldDisableYear": "Disable specific years dynamically. Works like shouldDisableDate but for year selection view @DateIOType.

Signature:
function(year: TDate) => boolean
year: The year to test.
returns (boolean): Returns true if the year should be disabled.", "showDaysOutsideCurrentMonth": "If true, days that have outsideCurrentMonth={true} are displayed.", diff --git a/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.tsx b/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.tsx index f0f6ae127af7..e5161776b348 100644 --- a/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.tsx +++ b/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.tsx @@ -380,6 +380,7 @@ DateRangePicker.propTypes = { * Disable specific date. @DateIOType * @template TDate * @param {TDate} day The date to test. + * @param {string} position The date to test, 'start' or 'end'. * @returns {boolean} Returns `true` if the date should be disabled. */ shouldDisableDate: PropTypes.func, diff --git a/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePickerView.tsx b/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePickerView.tsx index eca5f3c198fd..379c940c4726 100644 --- a/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePickerView.tsx +++ b/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePickerView.tsx @@ -11,8 +11,13 @@ import { PickerStatePickerProps, DayPickerProps, BaseDateValidationProps, + DayValidationProps, } from '@mui/x-date-pickers/internals'; -import { DateRange, CurrentlySelectingRangeEndProps } from '../internal/models/dateRange'; +import { + DateRange, + CurrentlySelectingRangeEndProps, + DayRangeValidationProps, +} from '../internal/models/dateRange'; import { isRangeValid } from '../internal/utils/date-utils'; import { calculateRangeChange } from './date-range-manager'; import { DateRangePickerToolbar } from './DateRangePickerToolbar'; @@ -38,9 +43,13 @@ export interface DateRangePickerViewSlotsComponentsProps export interface ExportedDateRangePickerViewProps extends ExportedDesktopDateRangeCalendarProps, + DayRangeValidationProps, Omit< ExportedCalendarPickerProps, - 'onYearChange' | 'renderDay' | keyof BaseDateValidationProps + | 'onYearChange' + | 'renderDay' + | keyof BaseDateValidationProps + | keyof DayValidationProps > { /** * Overrideable components. @@ -130,6 +139,10 @@ function DateRangePickerViewRaw( const utils = useUtils(); const wrapperVariant = React.useContext(WrapperVariantContext); + const wrappedShouldDisableDate = + shouldDisableDate && + ((dayToTest: TDate) => shouldDisableDate?.(dayToTest, currentlySelectingRangeEnd)); + const [start, end] = parsedValue; const { changeMonth, @@ -147,7 +160,7 @@ function DateRangePickerViewRaw( minDate, onMonthChange, reduceAnimations, - shouldDisableDate, + shouldDisableDate: wrappedShouldDisableDate, }); const toShowToolbar = showToolbar ?? wrapperVariant !== 'desktop'; @@ -234,7 +247,7 @@ function DateRangePickerViewRaw( disablePast, minDate, maxDate, - shouldDisableDate, + shouldDisableDate: wrappedShouldDisableDate, ...calendarState, ...other, }; diff --git a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.test.tsx b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.test.tsx index 4e871f4e59d9..a0dde01316e4 100644 --- a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.test.tsx +++ b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.test.tsx @@ -95,6 +95,35 @@ describe('', () => { expect(textboxes[0]).to.have.attribute('aria-invalid', 'true'); expect(textboxes[1]).to.have.attribute('aria-invalid', 'true'); }); + + it('should allow `shouldDisableDate` to depends on start or end date', () => { + render( + { + if (position === 'start') { + return adapterToUse.isAfter(date as any, adapterToUse.date(new Date(2018, 0, 15))); + } + return adapterToUse.isBefore(date as any, adapterToUse.date(new Date(2018, 0, 15))); + }} + />, + ); + + openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'start' }); + + const firstPicker = getPickerDay('5', 'January 2018'); + const secondPicker = getPickerDay('25', 'January 2018'); + + expect(firstPicker).not.to.have.attribute('disabled'); + expect(secondPicker).to.have.attribute('disabled'); + fireEvent.click(firstPicker); + + expect(firstPicker).to.have.attribute('disabled'); + expect(secondPicker).not.to.have.attribute('disabled'); + }); }); it('should highlight the selected range of dates', () => { diff --git a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.tsx b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.tsx index 75c1b2b43fbd..06d5f1a19ff8 100644 --- a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.tsx +++ b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.tsx @@ -409,6 +409,7 @@ DesktopDateRangePicker.propTypes = { * Disable specific date. @DateIOType * @template TDate * @param {TDate} day The date to test. + * @param {string} position The date to test, 'start' or 'end'. * @returns {boolean} Returns `true` if the date should be disabled. */ shouldDisableDate: PropTypes.func, diff --git a/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.test.tsx b/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.test.tsx index 1f27ae6b877f..6fd67c1fa117 100644 --- a/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.test.tsx +++ b/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.test.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { spy } from 'sinon'; import { expect } from 'chai'; import TextField from '@mui/material/TextField'; -import { describeConformance, screen, userEvent } from '@mui/monorepo/test/utils'; +import { describeConformance, screen, userEvent, fireEvent } from '@mui/monorepo/test/utils'; import { MobileDateRangePicker } from '@mui/x-date-pickers-pro/MobileDateRangePicker'; import { wrapPickerMount, @@ -295,8 +295,37 @@ describe('', () => { expect(onClose.callCount).to.equal(1); }); - // TODO: Write test - // it('should call onClose and onAccept with the live value when clicking outside of the picker', () => { - // }) + it('should allow `shouldDisableDate` to depends on start or end date', () => { + render( + { + if (position === 'start') { + return adapterToUse.isAfter(date as any, adapterToUse.date(new Date(2018, 0, 15))); + } + return adapterToUse.isBefore(date as any, adapterToUse.date(new Date(2018, 0, 15))); + }} + />, + ); + + openPicker({ type: 'date-range', variant: 'mobile', initialFocus: 'start' }); + + const firstPicker = screen.getByText('5'); + const secondPicker = screen.getByText('25'); + + expect(firstPicker).not.to.have.attribute('disabled'); + expect(secondPicker).to.have.attribute('disabled'); + fireEvent.click(firstPicker); + + expect(firstPicker).to.have.attribute('disabled'); + expect(secondPicker).not.to.have.attribute('disabled'); + }); }); + + // TODO: Write test + // it('should call onClose and onAccept with the live value when clicking outside of the picker', () => { + // }) }); diff --git a/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.tsx b/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.tsx index 4469c90153bd..d760916118fd 100644 --- a/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.tsx +++ b/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.tsx @@ -401,6 +401,7 @@ MobileDateRangePicker.propTypes = { * Disable specific date. @DateIOType * @template TDate * @param {TDate} day The date to test. + * @param {string} position The date to test, 'start' or 'end'. * @returns {boolean} Returns `true` if the date should be disabled. */ shouldDisableDate: PropTypes.func, diff --git a/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.tsx b/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.tsx index 18c638121208..eb2b58dd3469 100644 --- a/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.tsx +++ b/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.tsx @@ -381,6 +381,7 @@ StaticDateRangePicker.propTypes = { * Disable specific date. @DateIOType * @template TDate * @param {TDate} day The date to test. + * @param {string} position The date to test, 'start' or 'end'. * @returns {boolean} Returns `true` if the date should be disabled. */ shouldDisableDate: PropTypes.func, diff --git a/packages/x-date-pickers-pro/src/internal/hooks/validation/useDateRangeValidation.ts b/packages/x-date-pickers-pro/src/internal/hooks/validation/useDateRangeValidation.ts index c9b1c372dc42..d4420e9b2dd3 100644 --- a/packages/x-date-pickers-pro/src/internal/hooks/validation/useDateRangeValidation.ts +++ b/packages/x-date-pickers-pro/src/internal/hooks/validation/useDateRangeValidation.ts @@ -5,13 +5,12 @@ import { DateValidationError, validateDate, BaseDateValidationProps, - DayValidationProps, } from '@mui/x-date-pickers/internals'; import { isRangeValid, parseRangeInputValue } from '../../utils/date-utils'; -import { DateRange } from '../../models'; +import { DateRange, DayRangeValidationProps } from '../../models/dateRange'; export interface DateRangeValidationProps - extends DayValidationProps, + extends DayRangeValidationProps, Required>, ValidationProps> {} @@ -27,9 +26,25 @@ export const validateDateRange: Validator !!shouldDisableDate?.(day, 'start'), + }, + }), + validateDate({ + adapter, + value: end, + props: { + ...otherProps, + shouldDisableDate: (day) => !!shouldDisableDate?.(day, 'end'), + }, + }), ]; if (dateValidations[0] || dateValidations[1]) { diff --git a/packages/x-date-pickers-pro/src/internal/models/dateRange.ts b/packages/x-date-pickers-pro/src/internal/models/dateRange.ts index 79fee141dc0f..1b5245c54874 100644 --- a/packages/x-date-pickers-pro/src/internal/models/dateRange.ts +++ b/packages/x-date-pickers-pro/src/internal/models/dateRange.ts @@ -5,3 +5,17 @@ export interface CurrentlySelectingRangeEndProps { currentlySelectingRangeEnd: 'start' | 'end'; setCurrentlySelectingRangeEnd: (newSelectingEnd: 'start' | 'end') => void; } + +/** + * Props used to validate a day value in range pickers. + */ +export interface DayRangeValidationProps { + /** + * Disable specific date. @DateIOType + * @template TDate + * @param {TDate} day The date to test. + * @param {string} position The date to test, 'start' or 'end'. + * @returns {boolean} Returns `true` if the date should be disabled. + */ + shouldDisableDate?: (day: TDate, position: 'start' | 'end') => boolean; +}