) {
+ const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');
+
+ const { value, timezone, format } = internalProps;
+ const { InputProps, slotProps, slots, ...other } = forwardedProps;
+
+ const { hasValidationError } = useValidation({
+ validator: validateDate,
+ value,
+ timezone,
+ props: internalProps,
+ });
+
+ return (
+
+ );
+}
+
+export default function CustomField() {
+ return (
+
+
+
+
+
+ );
+}
diff --git a/docs/data/date-pickers/experimentation/CustomField.tsx.preview b/docs/data/date-pickers/experimentation/CustomField.tsx.preview
new file mode 100644
index 000000000000..b489d70b2175
--- /dev/null
+++ b/docs/data/date-pickers/experimentation/CustomField.tsx.preview
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/docs/data/date-pickers/experimentation/experimentation.md b/docs/data/date-pickers/experimentation/experimentation.md
new file mode 100644
index 000000000000..d5f906e32868
--- /dev/null
+++ b/docs/data/date-pickers/experimentation/experimentation.md
@@ -0,0 +1,11 @@
+---
+productId: x-date-pickers
+---
+
+# Date and Time Pickers experimentation
+
+Demos not accessible through the navbar of the doc
+
+## Custom field
+
+{{"demo": "CustomField.js"}}
diff --git a/docs/pages/x/react-date-pickers/experimentation.js b/docs/pages/x/react-date-pickers/experimentation.js
new file mode 100644
index 000000000000..bf599b0bb69e
--- /dev/null
+++ b/docs/pages/x/react-date-pickers/experimentation.js
@@ -0,0 +1,7 @@
+import * as React from 'react';
+import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs';
+import * as pageProps from 'docsx/data/date-pickers/experimentation/experimentation.md?muiMarkdown';
+
+export default function Page() {
+ return ;
+}
diff --git a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.tsx b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.tsx
index f76e7d1ad76b..383e41d3a9e1 100644
--- a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.tsx
+++ b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.tsx
@@ -1,6 +1,7 @@
import * as React from 'react';
import PropTypes from 'prop-types';
-import { extractValidationProps, PickerViewRendererLookup } from '@mui/x-date-pickers/internals';
+import { PickerViewRendererLookup } from '@mui/x-date-pickers/internals';
+import { extractValidationProps } from '@mui/x-date-pickers/validation';
import { PickerValidDate } from '@mui/x-date-pickers/models';
import resolveComponentProps from '@mui/utils/resolveComponentProps';
import { refType } from '@mui/utils';
@@ -10,7 +11,7 @@ import { useDateRangePickerDefaultizedProps } from '../DateRangePicker/shared';
import { renderDateRangeViewCalendar } from '../dateRangeViewRenderers';
import { MultiInputDateRangeField } from '../MultiInputDateRangeField';
import { useDesktopRangePicker } from '../internals/hooks/useDesktopRangePicker';
-import { validateDateRange } from '../internals/utils/validation/validateDateRange';
+import { validateDateRange } from '../validation';
import { DateRange } from '../models';
type DesktopDateRangePickerComponent = (<
diff --git a/packages/x-date-pickers-pro/src/DesktopDateTimeRangePicker/DesktopDateTimeRangePicker.tsx b/packages/x-date-pickers-pro/src/DesktopDateTimeRangePicker/DesktopDateTimeRangePicker.tsx
index b25801087ea9..6c23460aac80 100644
--- a/packages/x-date-pickers-pro/src/DesktopDateTimeRangePicker/DesktopDateTimeRangePicker.tsx
+++ b/packages/x-date-pickers-pro/src/DesktopDateTimeRangePicker/DesktopDateTimeRangePicker.tsx
@@ -2,7 +2,6 @@ import * as React from 'react';
import PropTypes from 'prop-types';
import {
DefaultizedProps,
- extractValidationProps,
isDatePickerView,
isInternalTimeView,
PickerViewRenderer,
@@ -10,6 +9,7 @@ import {
resolveDateTimeFormat,
useUtils,
} from '@mui/x-date-pickers/internals';
+import { extractValidationProps } from '@mui/x-date-pickers/validation';
import { PickerValidDate } from '@mui/x-date-pickers/models';
import resolveComponentProps from '@mui/utils/resolveComponentProps';
import { refType } from '@mui/utils';
@@ -32,7 +32,7 @@ import {
useDesktopRangePicker,
UseDesktopRangePickerProps,
} from '../internals/hooks/useDesktopRangePicker';
-import { validateDateTimeRange } from '../internals/utils/validation/validateDateTimeRange';
+import { validateDateTimeRange } from '../validation';
import { DateTimeRangePickerView } from '../internals/models';
import { DateRange } from '../models';
import {
diff --git a/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.tsx b/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.tsx
index 6cb841389f50..a93c3eefd388 100644
--- a/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.tsx
+++ b/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.tsx
@@ -1,6 +1,7 @@
import * as React from 'react';
import PropTypes from 'prop-types';
-import { extractValidationProps, PickerViewRendererLookup } from '@mui/x-date-pickers/internals';
+import { PickerViewRendererLookup } from '@mui/x-date-pickers/internals';
+import { extractValidationProps } from '@mui/x-date-pickers/validation';
import { PickerValidDate } from '@mui/x-date-pickers/models';
import resolveComponentProps from '@mui/utils/resolveComponentProps';
import { refType } from '@mui/utils';
@@ -10,7 +11,7 @@ import { useDateRangePickerDefaultizedProps } from '../DateRangePicker/shared';
import { renderDateRangeViewCalendar } from '../dateRangeViewRenderers';
import { MultiInputDateRangeField } from '../MultiInputDateRangeField';
import { useMobileRangePicker } from '../internals/hooks/useMobileRangePicker';
-import { validateDateRange } from '../internals/utils/validation/validateDateRange';
+import { validateDateRange } from '../validation';
import { DateRange } from '../models';
type MobileDateRangePickerComponent = (<
diff --git a/packages/x-date-pickers-pro/src/MobileDateTimeRangePicker/MobileDateTimeRangePicker.tsx b/packages/x-date-pickers-pro/src/MobileDateTimeRangePicker/MobileDateTimeRangePicker.tsx
index 63637dbce374..42149a94852a 100644
--- a/packages/x-date-pickers-pro/src/MobileDateTimeRangePicker/MobileDateTimeRangePicker.tsx
+++ b/packages/x-date-pickers-pro/src/MobileDateTimeRangePicker/MobileDateTimeRangePicker.tsx
@@ -4,7 +4,6 @@ import { refType } from '@mui/utils';
import {
DIALOG_WIDTH,
VIEW_HEIGHT,
- extractValidationProps,
isInternalTimeView,
isDatePickerView,
PickerViewRenderer,
@@ -14,6 +13,7 @@ import {
resolveDateTimeFormat,
useUtils,
} from '@mui/x-date-pickers/internals';
+import { extractValidationProps } from '@mui/x-date-pickers/validation';
import { PickerValidDate } from '@mui/x-date-pickers/models';
import resolveComponentProps from '@mui/utils/resolveComponentProps';
import {
@@ -32,7 +32,7 @@ import {
UseMobileRangePickerProps,
useMobileRangePicker,
} from '../internals/hooks/useMobileRangePicker';
-import { validateDateTimeRange } from '../internals/utils/validation/validateDateTimeRange';
+import { validateDateTimeRange } from '../validation';
import { DateTimeRangePickerView } from '../internals/models';
import { DateRange } from '../models';
import {
diff --git a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/useSingleInputDateRangeField.ts b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/useSingleInputDateRangeField.ts
index 06bcd2efd335..37a89e42ee95 100644
--- a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/useSingleInputDateRangeField.ts
+++ b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/useSingleInputDateRangeField.ts
@@ -4,7 +4,7 @@ import { useSplitFieldProps } from '@mui/x-date-pickers/hooks';
import { PickerValidDate } from '@mui/x-date-pickers/models';
import { UseSingleInputDateRangeFieldProps } from './SingleInputDateRangeField.types';
import { rangeValueManager, getRangeFieldValueManager } from '../internals/utils/valueManagers';
-import { validateDateRange } from '../internals/utils/validation/validateDateRange';
+import { validateDateRange } from '../validation';
import { RangeFieldSection, DateRange } from '../models';
export const useSingleInputDateRangeField = <
diff --git a/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/useSingleInputDateTimeRangeField.ts b/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/useSingleInputDateTimeRangeField.ts
index 62356882aa58..3d42c8b318ad 100644
--- a/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/useSingleInputDateTimeRangeField.ts
+++ b/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/useSingleInputDateTimeRangeField.ts
@@ -4,7 +4,7 @@ import { useSplitFieldProps } from '@mui/x-date-pickers/hooks';
import { PickerValidDate } from '@mui/x-date-pickers/models';
import { UseSingleInputDateTimeRangeFieldProps } from './SingleInputDateTimeRangeField.types';
import { rangeValueManager, getRangeFieldValueManager } from '../internals/utils/valueManagers';
-import { validateDateTimeRange } from '../internals/utils/validation/validateDateTimeRange';
+import { validateDateTimeRange } from '../validation';
import { RangeFieldSection, DateRange } from '../models';
export const useSingleInputDateTimeRangeField = <
diff --git a/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/useSingleInputTimeRangeField.ts b/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/useSingleInputTimeRangeField.ts
index 2d5836c8882c..a60f71f889b3 100644
--- a/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/useSingleInputTimeRangeField.ts
+++ b/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/useSingleInputTimeRangeField.ts
@@ -4,7 +4,7 @@ import { useSplitFieldProps } from '@mui/x-date-pickers/hooks';
import { PickerValidDate } from '@mui/x-date-pickers/models';
import { UseSingleInputTimeRangeFieldProps } from './SingleInputTimeRangeField.types';
import { rangeValueManager, getRangeFieldValueManager } from '../internals/utils/valueManagers';
-import { validateTimeRange } from '../internals/utils/validation/validateTimeRange';
+import { validateTimeRange } from '../validation';
import { RangeFieldSection, DateRange } from '../models';
export const useSingleInputTimeRangeField = <
diff --git a/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.tsx b/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.tsx
index 5283f9efdac6..7f0cd35a8c68 100644
--- a/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.tsx
+++ b/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.tsx
@@ -7,7 +7,7 @@ import { StaticDateRangePickerProps } from './StaticDateRangePicker.types';
import { useDateRangePickerDefaultizedProps } from '../DateRangePicker/shared';
import { renderDateRangeViewCalendar } from '../dateRangeViewRenderers';
import { rangeValueManager } from '../internals/utils/valueManagers';
-import { validateDateRange } from '../internals/utils/validation/validateDateRange';
+import { validateDateRange } from '../validation';
import { DateRange } from '../models';
type StaticDateRangePickerComponent = ((
diff --git a/packages/x-date-pickers-pro/src/index.ts b/packages/x-date-pickers-pro/src/index.ts
index 55114fb14fb8..8a7bb7e61c04 100644
--- a/packages/x-date-pickers-pro/src/index.ts
+++ b/packages/x-date-pickers-pro/src/index.ts
@@ -35,3 +35,4 @@ export * from './MobileDateTimeRangePicker';
export * from './dateRangeViewRenderers';
export * from './models';
+export * from './validation';
diff --git a/packages/x-date-pickers-pro/src/internals/hooks/useDesktopRangePicker/useDesktopRangePicker.tsx b/packages/x-date-pickers-pro/src/internals/hooks/useDesktopRangePicker/useDesktopRangePicker.tsx
index cb635fdba2ca..55d56d9b6ca5 100644
--- a/packages/x-date-pickers-pro/src/internals/hooks/useDesktopRangePicker/useDesktopRangePicker.tsx
+++ b/packages/x-date-pickers-pro/src/internals/hooks/useDesktopRangePicker/useDesktopRangePicker.tsx
@@ -8,12 +8,16 @@ import {
getActiveElement,
usePicker,
PickersPopper,
- InferError,
ExportedBaseToolbarProps,
DateOrTimeViewWithMeridiem,
ExportedBaseTabsProps,
} from '@mui/x-date-pickers/internals';
-import { PickerValidDate, FieldRef, BaseSingleInputFieldProps } from '@mui/x-date-pickers/models';
+import {
+ PickerValidDate,
+ FieldRef,
+ BaseSingleInputFieldProps,
+ InferError,
+} from '@mui/x-date-pickers/models';
import {
DesktopRangePickerAdditionalViewProps,
UseDesktopRangePickerParams,
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 8c0ff7404326..518914bdb1b5 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
@@ -6,13 +6,17 @@ import { PickersLayout, PickersLayoutSlotProps } from '@mui/x-date-pickers/Picke
import {
usePicker,
PickersModalDialog,
- InferError,
ExportedBaseToolbarProps,
DateOrTimeViewWithMeridiem,
ExportedBaseTabsProps,
} from '@mui/x-date-pickers/internals';
import { usePickersTranslations } from '@mui/x-date-pickers/hooks';
-import { PickerValidDate, FieldRef, BaseSingleInputFieldProps } from '@mui/x-date-pickers/models';
+import {
+ PickerValidDate,
+ FieldRef,
+ BaseSingleInputFieldProps,
+ InferError,
+} from '@mui/x-date-pickers/models';
import useId from '@mui/utils/useId';
import {
MobileRangePickerAdditionalViewProps,
diff --git a/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputDateRangeField.ts b/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputDateRangeField.ts
index 84dfaeeb8631..f89396ebc68b 100644
--- a/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputDateRangeField.ts
+++ b/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputDateRangeField.ts
@@ -4,23 +4,19 @@ import {
UseDateFieldComponentProps,
} from '@mui/x-date-pickers/DateField';
import {
- useLocalizationContext,
- useValidation,
FieldChangeHandler,
FieldChangeHandlerContext,
UseFieldResponse,
useControlledValueWithTimezone,
useDefaultizedDateField,
} from '@mui/x-date-pickers/internals';
+import { useValidation } from '@mui/x-date-pickers/validation';
import { DateValidationError, PickerValidDate } from '@mui/x-date-pickers/models';
import {
UseMultiInputDateRangeFieldParams,
UseMultiInputDateRangeFieldProps,
} from '../../../MultiInputDateRangeField/MultiInputDateRangeField.types';
-import {
- DateRangeComponentValidationProps,
- validateDateRange,
-} from '../../utils/validation/validateDateRange';
+import { validateDateRange } from '../../../validation';
import { rangeValueManager } from '../../utils/valueManagers';
import type { UseMultiInputRangeFieldResponse } from './useMultiInputRangeField.types';
import { DateRangeValidationError, DateRange } from '../../../models';
@@ -48,8 +44,6 @@ export const useMultiInputDateRangeField = <
typeof inSharedProps
>(inSharedProps);
- const adapter = useLocalizationContext();
-
const {
value: valueProp,
defaultValue,
@@ -75,6 +69,14 @@ export const useMultiInputDateRangeField = <
valueManager: rangeValueManager,
});
+ const { validationError, getValidationErrorForNewValue } = useValidation({
+ props: sharedProps,
+ value,
+ timezone,
+ validator: validateDateRange,
+ onError: sharedProps.onError,
+ });
+
// TODO: Maybe export utility from `useField` instead of copy/pasting the logic
const buildChangeHandler = (
index: 0 | 1,
@@ -85,11 +87,7 @@ export const useMultiInputDateRangeField = <
const context: FieldChangeHandlerContext = {
...rawContext,
- validationError: validateDateRange({
- adapter,
- value: newDateRange,
- props: { ...sharedProps, timezone },
- }),
+ validationError: getValidationErrorForNewValue(newDateRange),
};
handleValueChange(newDateRange, context);
@@ -99,18 +97,6 @@ export const useMultiInputDateRangeField = <
const handleStartDateChange = useEventCallback(buildChangeHandler(0));
const handleEndDateChange = useEventCallback(buildChangeHandler(1));
- const validationError = useValidation<
- DateRange,
- TDate,
- DateRangeValidationError,
- DateRangeComponentValidationProps
- >(
- { ...sharedProps, value, timezone },
- validateDateRange,
- rangeValueManager.isSameError,
- rangeValueManager.defaultErrorState,
- );
-
const selectedSectionsResponse = useMultiInputFieldSelectedSections({
selectedSections,
onSelectedSectionsChange,
diff --git a/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputDateTimeRangeField.ts b/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputDateTimeRangeField.ts
index cecc16b5cc6e..60efbe608a10 100644
--- a/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputDateTimeRangeField.ts
+++ b/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputDateTimeRangeField.ts
@@ -4,8 +4,6 @@ import {
UseDateTimeFieldComponentProps,
} from '@mui/x-date-pickers/DateTimeField';
import {
- useLocalizationContext,
- useValidation,
FieldChangeHandler,
FieldChangeHandlerContext,
UseFieldResponse,
@@ -13,15 +11,13 @@ import {
useDefaultizedDateTimeField,
} from '@mui/x-date-pickers/internals';
import { DateTimeValidationError, PickerValidDate } from '@mui/x-date-pickers/models';
+import { useValidation } from '@mui/x-date-pickers/validation';
import type {
UseMultiInputDateTimeRangeFieldParams,
UseMultiInputDateTimeRangeFieldProps,
} from '../../../MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.types';
import { DateTimeRangeValidationError, DateRange } from '../../../models';
-import {
- DateTimeRangeComponentValidationProps,
- validateDateTimeRange,
-} from '../../utils/validation/validateDateTimeRange';
+import { validateDateTimeRange } from '../../../validation';
import { rangeValueManager } from '../../utils/valueManagers';
import type { UseMultiInputRangeFieldResponse } from './useMultiInputRangeField.types';
import { excludeProps } from './shared';
@@ -47,7 +43,6 @@ export const useMultiInputDateTimeRangeField = <
UseMultiInputDateTimeRangeFieldProps,
typeof inSharedProps
>(inSharedProps);
- const adapter = useLocalizationContext();
const {
value: valueProp,
@@ -74,6 +69,14 @@ export const useMultiInputDateTimeRangeField = <
valueManager: rangeValueManager,
});
+ const { validationError, getValidationErrorForNewValue } = useValidation({
+ props: sharedProps,
+ value,
+ timezone,
+ validator: validateDateTimeRange,
+ onError: sharedProps.onError,
+ });
+
// TODO: Maybe export utility from `useField` instead of copy/pasting the logic
const buildChangeHandler = (
index: 0 | 1,
@@ -84,11 +87,7 @@ export const useMultiInputDateTimeRangeField = <
const context: FieldChangeHandlerContext = {
...rawContext,
- validationError: validateDateTimeRange({
- adapter,
- value: newDateRange,
- props: { ...sharedProps, timezone },
- }),
+ validationError: getValidationErrorForNewValue(newDateRange),
};
handleValueChange(newDateRange, context);
@@ -98,18 +97,6 @@ export const useMultiInputDateTimeRangeField = <
const handleStartDateChange = useEventCallback(buildChangeHandler(0));
const handleEndDateChange = useEventCallback(buildChangeHandler(1));
- const validationError = useValidation<
- DateRange,
- TDate,
- DateTimeRangeValidationError,
- DateTimeRangeComponentValidationProps
- >(
- { ...sharedProps, value, timezone },
- validateDateTimeRange,
- rangeValueManager.isSameError,
- rangeValueManager.defaultErrorState,
- );
-
const selectedSectionsResponse = useMultiInputFieldSelectedSections({
selectedSections,
onSelectedSectionsChange,
diff --git a/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputTimeRangeField.ts b/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputTimeRangeField.ts
index a27dcb41a897..86db843894f5 100644
--- a/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputTimeRangeField.ts
+++ b/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputTimeRangeField.ts
@@ -4,19 +4,15 @@ import {
UseTimeFieldComponentProps,
} from '@mui/x-date-pickers/TimeField';
import {
- useLocalizationContext,
- useValidation,
FieldChangeHandler,
FieldChangeHandlerContext,
UseFieldResponse,
useControlledValueWithTimezone,
useDefaultizedTimeField,
} from '@mui/x-date-pickers/internals';
+import { useValidation } from '@mui/x-date-pickers/validation';
import { PickerValidDate, TimeValidationError } from '@mui/x-date-pickers/models';
-import {
- validateTimeRange,
- TimeRangeComponentValidationProps,
-} from '../../utils/validation/validateTimeRange';
+import { validateTimeRange } from '../../../validation';
import { TimeRangeValidationError, DateRange } from '../../../models';
import type {
UseMultiInputTimeRangeFieldParams,
@@ -47,7 +43,6 @@ export const useMultiInputTimeRangeField = <
UseMultiInputTimeRangeFieldProps,
typeof inSharedProps
>(inSharedProps);
- const adapter = useLocalizationContext();
const {
value: valueProp,
@@ -74,6 +69,14 @@ export const useMultiInputTimeRangeField = <
valueManager: rangeValueManager,
});
+ const { validationError, getValidationErrorForNewValue } = useValidation({
+ props: sharedProps,
+ validator: validateTimeRange,
+ value,
+ timezone,
+ onError: sharedProps.onError,
+ });
+
// TODO: Maybe export utility from `useField` instead of copy/pasting the logic
const buildChangeHandler = (
index: 0 | 1,
@@ -84,11 +87,7 @@ export const useMultiInputTimeRangeField = <
const context: FieldChangeHandlerContext = {
...rawContext,
- validationError: validateTimeRange({
- adapter,
- value: newDateRange,
- props: { ...sharedProps, timezone },
- }),
+ validationError: getValidationErrorForNewValue(newDateRange),
};
handleValueChange(newDateRange, context);
@@ -98,18 +97,6 @@ export const useMultiInputTimeRangeField = <
const handleStartDateChange = useEventCallback(buildChangeHandler(0));
const handleEndDateChange = useEventCallback(buildChangeHandler(1));
- const validationError = useValidation<
- DateRange,
- TDate,
- TimeRangeValidationError,
- TimeRangeComponentValidationProps
- >(
- { ...sharedProps, value, timezone },
- validateTimeRange,
- rangeValueManager.isSameError,
- rangeValueManager.defaultErrorState,
- );
-
const selectedSectionsResponse = useMultiInputFieldSelectedSections({
selectedSections,
onSelectedSectionsChange,
diff --git a/packages/x-date-pickers-pro/src/validation/index.ts b/packages/x-date-pickers-pro/src/validation/index.ts
new file mode 100644
index 000000000000..9b7737a17ed2
--- /dev/null
+++ b/packages/x-date-pickers-pro/src/validation/index.ts
@@ -0,0 +1,8 @@
+export { validateDateRange } from './validateDateRange';
+export type { ValidateDateRangeProps } from './validateDateRange';
+
+export { validateTimeRange } from './validateTimeRange';
+export type { ValidateTimeRangeProps } from './validateTimeRange';
+
+export { validateDateTimeRange } from './validateDateTimeRange';
+export type { ValidateDateTimeRangeProps } from './validateDateTimeRange';
diff --git a/packages/x-date-pickers-pro/src/internals/utils/validation/validateDateRange.ts b/packages/x-date-pickers-pro/src/validation/validateDateRange.ts
similarity index 55%
rename from packages/x-date-pickers-pro/src/internals/utils/validation/validateDateRange.ts
rename to packages/x-date-pickers-pro/src/validation/validateDateRange.ts
index bb138c82f438..125449ca9a6f 100644
--- a/packages/x-date-pickers-pro/src/internals/utils/validation/validateDateRange.ts
+++ b/packages/x-date-pickers-pro/src/validation/validateDateRange.ts
@@ -1,25 +1,21 @@
-import { PickerValidDate, TimezoneProps } from '@mui/x-date-pickers/models';
-import {
- Validator,
- validateDate,
- BaseDateValidationProps,
- DefaultizedProps,
-} from '@mui/x-date-pickers/internals';
-import { isRangeValid } from '../date-utils';
-import { DayRangeValidationProps } from '../../models/dateRange';
-import { DateRangeValidationError, DateRange } from '../../../models';
-
-export interface DateRangeComponentValidationProps
+import { PickerValidDate } from '@mui/x-date-pickers/models';
+import { validateDate, Validator } from '@mui/x-date-pickers/validation';
+import { BaseDateValidationProps } from '@mui/x-date-pickers/internals';
+import { isRangeValid } from '../internals/utils/date-utils';
+import { DayRangeValidationProps } from '../internals/models/dateRange';
+import { DateRangeValidationError, DateRange } from '../models';
+import { rangeValueManager } from '../internals/utils/valueManagers';
+
+export interface ValidateDateRangeProps
extends DayRangeValidationProps,
- Required>,
- DefaultizedProps {}
+ Required> {}
export const validateDateRange: Validator<
DateRange,
any,
DateRangeValidationError,
- DateRangeComponentValidationProps
-> = ({ props, value, adapter }) => {
+ ValidateDateRangeProps
+> = ({ adapter, value, timezone, props }) => {
const [start, end] = value;
const { shouldDisableDate, ...otherProps } = props;
@@ -28,6 +24,7 @@ export const validateDateRange: Validator<
validateDate({
adapter,
value: start,
+ timezone,
props: {
...otherProps,
shouldDisableDate: (day) => !!shouldDisableDate?.(day, 'start'),
@@ -36,6 +33,7 @@ export const validateDateRange: Validator<
validateDate({
adapter,
value: end,
+ timezone,
props: {
...otherProps,
shouldDisableDate: (day) => !!shouldDisableDate?.(day, 'end'),
@@ -58,3 +56,5 @@ export const validateDateRange: Validator<
return [null, null];
};
+
+validateDateRange.valueManager = rangeValueManager;
diff --git a/packages/x-date-pickers-pro/src/internals/utils/validation/validateDateTimeRange.ts b/packages/x-date-pickers-pro/src/validation/validateDateTimeRange.ts
similarity index 55%
rename from packages/x-date-pickers-pro/src/internals/utils/validation/validateDateTimeRange.ts
rename to packages/x-date-pickers-pro/src/validation/validateDateTimeRange.ts
index 2eae3041a2e4..d86d65e8d3b3 100644
--- a/packages/x-date-pickers-pro/src/internals/utils/validation/validateDateTimeRange.ts
+++ b/packages/x-date-pickers-pro/src/validation/validateDateTimeRange.ts
@@ -1,27 +1,22 @@
-import { PickerValidDate, TimezoneProps } from '@mui/x-date-pickers/models';
-import {
- Validator,
- validateDateTime,
- BaseDateValidationProps,
- TimeValidationProps,
- DefaultizedProps,
-} from '@mui/x-date-pickers/internals';
-import { isRangeValid } from '../date-utils';
-import { DayRangeValidationProps } from '../../models/dateRange';
-import { DateTimeRangeValidationError, DateRange } from '../../../models';
-
-export interface DateTimeRangeComponentValidationProps
+import { PickerValidDate } from '@mui/x-date-pickers/models';
+import { validateDateTime, Validator } from '@mui/x-date-pickers/validation';
+import { BaseDateValidationProps, TimeValidationProps } from '@mui/x-date-pickers/internals';
+import { isRangeValid } from '../internals/utils/date-utils';
+import { DayRangeValidationProps } from '../internals/models/dateRange';
+import { DateTimeRangeValidationError, DateRange } from '../models';
+import { rangeValueManager } from '../internals/utils/valueManagers';
+
+export interface ValidateDateTimeRangeProps
extends DayRangeValidationProps,
TimeValidationProps,
- Required>,
- DefaultizedProps {}
+ Required> {}
export const validateDateTimeRange: Validator<
DateRange,
any,
DateTimeRangeValidationError,
- DateTimeRangeComponentValidationProps
-> = ({ props, value, adapter }) => {
+ ValidateDateTimeRangeProps
+> = ({ adapter, value, timezone, props }) => {
const [start, end] = value;
const { shouldDisableDate, ...otherProps } = props;
@@ -30,6 +25,7 @@ export const validateDateTimeRange: Validator<
validateDateTime({
adapter,
value: start,
+ timezone,
props: {
...otherProps,
shouldDisableDate: (day) => !!shouldDisableDate?.(day, 'start'),
@@ -38,6 +34,7 @@ export const validateDateTimeRange: Validator<
validateDateTime({
adapter,
value: end,
+ timezone,
props: {
...otherProps,
shouldDisableDate: (day) => !!shouldDisableDate?.(day, 'end'),
@@ -60,3 +57,5 @@ export const validateDateTimeRange: Validator<
return [null, null];
};
+
+validateDateTimeRange.valueManager = rangeValueManager;
diff --git a/packages/x-date-pickers-pro/src/internals/utils/validation/validateTimeRange.ts b/packages/x-date-pickers-pro/src/validation/validateTimeRange.ts
similarity index 52%
rename from packages/x-date-pickers-pro/src/internals/utils/validation/validateTimeRange.ts
rename to packages/x-date-pickers-pro/src/validation/validateTimeRange.ts
index 15b4a2bc2cd6..483523a4b499 100644
--- a/packages/x-date-pickers-pro/src/internals/utils/validation/validateTimeRange.ts
+++ b/packages/x-date-pickers-pro/src/validation/validateTimeRange.ts
@@ -1,34 +1,30 @@
-import { TimezoneProps } from '@mui/x-date-pickers/models';
-import {
- Validator,
- validateTime,
- BaseTimeValidationProps,
- DefaultizedProps,
-} from '@mui/x-date-pickers/internals';
-import { isRangeValid } from '../date-utils';
-import { TimeRangeValidationError, DateRange } from '../../../models';
-
-export interface TimeRangeComponentValidationProps
- extends Required,
- DefaultizedProps {}
+import { validateTime, Validator } from '@mui/x-date-pickers/validation';
+import { BaseTimeValidationProps } from '@mui/x-date-pickers/internals';
+import { isRangeValid } from '../internals/utils/date-utils';
+import { TimeRangeValidationError, DateRange } from '../models';
+import { rangeValueManager } from '../internals/utils/valueManagers';
+
+export interface ValidateTimeRangeProps extends Required {}
export const validateTimeRange: Validator<
DateRange,
any,
TimeRangeValidationError,
- TimeRangeComponentValidationProps
-> = ({ props, value, adapter }) => {
+ ValidateTimeRangeProps
+> = ({ adapter, value, timezone, props }) => {
const [start, end] = value;
const dateTimeValidations: TimeRangeValidationError = [
validateTime({
adapter,
value: start,
+ timezone,
props,
}),
validateTime({
adapter,
value: end,
+ timezone,
props,
}),
];
@@ -48,3 +44,5 @@ export const validateTimeRange: Validator<
return [null, null];
};
+
+validateTimeRange.valueManager = rangeValueManager;
diff --git a/packages/x-date-pickers/src/DateCalendar/useIsDateDisabled.ts b/packages/x-date-pickers/src/DateCalendar/useIsDateDisabled.ts
index 7fbde8e84772..2c677f6f7653 100644
--- a/packages/x-date-pickers/src/DateCalendar/useIsDateDisabled.ts
+++ b/packages/x-date-pickers/src/DateCalendar/useIsDateDisabled.ts
@@ -1,10 +1,8 @@
import * as React from 'react';
-import {
- DateComponentValidationProps,
- validateDate,
-} from '../internals/utils/validation/validateDate';
+import { ValidateDateProps, validateDate } from '../validation';
import { useLocalizationContext } from '../internals/hooks/useUtils';
-import { PickerValidDate } from '../models';
+import { PickerValidDate, TimezoneProps } from '../models';
+import { DefaultizedProps } from '../internals/models/helpers';
export const useIsDateDisabled = ({
shouldDisableDate,
@@ -15,7 +13,7 @@ export const useIsDateDisabled = ({
disableFuture,
disablePast,
timezone,
-}: DateComponentValidationProps) => {
+}: ValidateDateProps & DefaultizedProps) => {
const adapter = useLocalizationContext();
return React.useCallback(
@@ -23,6 +21,7 @@ export const useIsDateDisabled = ({
validateDate({
adapter,
value: day,
+ timezone,
props: {
shouldDisableDate,
shouldDisableMonth,
@@ -31,7 +30,6 @@ export const useIsDateDisabled = ({
maxDate,
disableFuture,
disablePast,
- timezone,
},
}) !== null,
[
diff --git a/packages/x-date-pickers/src/DateField/DateField.types.ts b/packages/x-date-pickers/src/DateField/DateField.types.ts
index cf1bdfcc800a..57ab1e1830ae 100644
--- a/packages/x-date-pickers/src/DateField/DateField.types.ts
+++ b/packages/x-date-pickers/src/DateField/DateField.types.ts
@@ -11,9 +11,10 @@ import {
FieldSection,
PickerValidDate,
BuiltInFieldTextFieldProps,
+ BaseSingleInputFieldProps,
} from '../models';
import { UseFieldInternalProps } from '../internals/hooks/useField';
-import { MakeOptional } from '../internals/models/helpers';
+import { MakeOptional, DefaultizedProps } from '../internals/models/helpers';
import {
BaseDateValidationProps,
DayValidationProps,
@@ -40,6 +41,19 @@ export interface UseDateFieldProps<
BaseDateValidationProps,
ExportedUseClearableFieldProps {}
+/**
+ * Props the field can receive when used inside a date picker.
+ * (`DatePicker`, `DesktopDatePicker` or `MobileDatePicker` component).
+ */
+export type DateFieldInPickerProps<
+ TDate extends PickerValidDate,
+ TEnableAccessibleFieldDOMStructure extends boolean,
+> = DefaultizedProps<
+ UseDateFieldProps,
+ 'format' | 'timezone' | keyof BaseDateValidationProps
+> &
+ BaseSingleInputFieldProps;
+
export type UseDateFieldComponentProps<
TDate extends PickerValidDate,
TEnableAccessibleFieldDOMStructure extends boolean,
diff --git a/packages/x-date-pickers/src/DateField/index.ts b/packages/x-date-pickers/src/DateField/index.ts
index cd6119e98a25..e5f8fe21dcb2 100644
--- a/packages/x-date-pickers/src/DateField/index.ts
+++ b/packages/x-date-pickers/src/DateField/index.ts
@@ -4,4 +4,5 @@ export type {
UseDateFieldProps,
UseDateFieldComponentProps,
DateFieldProps,
+ DateFieldInPickerProps,
} from './DateField.types';
diff --git a/packages/x-date-pickers/src/DateField/useDateField.ts b/packages/x-date-pickers/src/DateField/useDateField.ts
index ff4404d47421..4d7fe61a702a 100644
--- a/packages/x-date-pickers/src/DateField/useDateField.ts
+++ b/packages/x-date-pickers/src/DateField/useDateField.ts
@@ -4,7 +4,7 @@ import {
} from '../internals/utils/valueManagers';
import { useField } from '../internals/hooks/useField';
import { UseDateFieldProps } from './DateField.types';
-import { validateDate } from '../internals/utils/validation/validateDate';
+import { validateDate } from '../validation';
import { useSplitFieldProps } from '../hooks';
import { FieldSection, PickerValidDate } from '../models';
import { useDefaultizedDateField } from '../internals/hooks/defaultizedFieldProps';
diff --git a/packages/x-date-pickers/src/DateTimeField/DateTimeField.types.ts b/packages/x-date-pickers/src/DateTimeField/DateTimeField.types.ts
index 625f70532e55..e19e41a6345a 100644
--- a/packages/x-date-pickers/src/DateTimeField/DateTimeField.types.ts
+++ b/packages/x-date-pickers/src/DateTimeField/DateTimeField.types.ts
@@ -6,9 +6,10 @@ import {
FieldSection,
PickerValidDate,
BuiltInFieldTextFieldProps,
+ BaseSingleInputFieldProps,
} from '../models';
import { UseFieldInternalProps } from '../internals/hooks/useField';
-import { MakeOptional } from '../internals/models/helpers';
+import { DefaultizedProps, MakeOptional } from '../internals/models/helpers';
import {
BaseDateValidationProps,
BaseTimeValidationProps,
@@ -52,6 +53,23 @@ export interface UseDateTimeFieldProps<
ampm?: boolean;
}
+/**
+ * Props the field can receive when used inside a date time picker.
+ * (`DateTimePicker`, `DesktopDateTimePicker` or `MobileDateTimePicker` component).
+ */
+export type DateTimeFieldInPickerProps<
+ TDate extends PickerValidDate,
+ TEnableAccessibleFieldDOMStructure extends boolean,
+> = DefaultizedProps<
+ UseDateTimeFieldProps,
+ | 'format'
+ | 'timezone'
+ | 'ampm'
+ | keyof BaseDateValidationProps
+ | keyof BaseTimeValidationProps
+> &
+ BaseSingleInputFieldProps;
+
export type UseDateTimeFieldComponentProps<
TDate extends PickerValidDate,
TEnableAccessibleFieldDOMStructure extends boolean,
diff --git a/packages/x-date-pickers/src/DateTimeField/index.ts b/packages/x-date-pickers/src/DateTimeField/index.ts
index 95952dde9474..212ce42432f5 100644
--- a/packages/x-date-pickers/src/DateTimeField/index.ts
+++ b/packages/x-date-pickers/src/DateTimeField/index.ts
@@ -4,4 +4,5 @@ export type {
UseDateTimeFieldProps,
UseDateTimeFieldComponentProps,
DateTimeFieldProps,
+ DateTimeFieldInPickerProps,
} from './DateTimeField.types';
diff --git a/packages/x-date-pickers/src/DateTimeField/useDateTimeField.ts b/packages/x-date-pickers/src/DateTimeField/useDateTimeField.ts
index 3b698f37a133..bcdc1c92b745 100644
--- a/packages/x-date-pickers/src/DateTimeField/useDateTimeField.ts
+++ b/packages/x-date-pickers/src/DateTimeField/useDateTimeField.ts
@@ -4,7 +4,7 @@ import {
} from '../internals/utils/valueManagers';
import { useField } from '../internals/hooks/useField';
import { UseDateTimeFieldProps } from './DateTimeField.types';
-import { validateDateTime } from '../internals/utils/validation/validateDateTime';
+import { validateDateTime } from '../validation';
import { useSplitFieldProps } from '../hooks';
import { FieldSection, PickerValidDate } from '../models';
import { useDefaultizedDateTimeField } from '../internals/hooks/defaultizedFieldProps';
diff --git a/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.tsx b/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.tsx
index fcea15c593a7..ca78737d2675 100644
--- a/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.tsx
+++ b/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.tsx
@@ -7,12 +7,11 @@ import { DesktopDatePickerProps } from './DesktopDatePicker.types';
import { DatePickerViewRenderers, useDatePickerDefaultizedProps } from '../DatePicker/shared';
import { usePickersTranslations } from '../hooks/usePickersTranslations';
import { useUtils } from '../internals/hooks/useUtils';
-import { validateDate } from '../internals/utils/validation/validateDate';
+import { validateDate, extractValidationProps } from '../validation';
import { DateView, PickerValidDate } from '../models';
import { useDesktopPicker } from '../internals/hooks/useDesktopPicker';
import { CalendarIcon } from '../icons';
import { DateField } from '../DateField';
-import { extractValidationProps } from '../internals/utils/validation/extractValidationProps';
import { renderDateViewCalendar } from '../dateViewRenderers';
import { resolveDateFormat } from '../internals/utils/date-utils';
import { buildGetOpenDialogAriaText } from '../locales/utils/getPickersLocalization';
diff --git a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx
index 259a84364176..605c07e6fca4 100644
--- a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx
+++ b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx
@@ -13,11 +13,10 @@ import {
import { renderDateViewCalendar } from '../dateViewRenderers/dateViewRenderers';
import { usePickersTranslations } from '../hooks/usePickersTranslations';
import { useUtils } from '../internals/hooks/useUtils';
-import { validateDateTime } from '../internals/utils/validation/validateDateTime';
+import { validateDateTime, extractValidationProps } from '../validation';
import { DateOrTimeViewWithMeridiem } from '../internals/models';
import { CalendarIcon } from '../icons';
import { UseDesktopPickerProps, useDesktopPicker } from '../internals/hooks/useDesktopPicker';
-import { extractValidationProps } from '../internals/utils/validation/extractValidationProps';
import { PickerViewsRendererProps } from '../internals/hooks/usePicker';
import {
resolveDateTimeFormat,
diff --git a/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.tsx b/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.tsx
index 42919188531c..d9c0436dd47d 100644
--- a/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.tsx
+++ b/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.tsx
@@ -8,10 +8,9 @@ import { DesktopTimePickerProps } from './DesktopTimePicker.types';
import { TimePickerViewRenderers, useTimePickerDefaultizedProps } from '../TimePicker/shared';
import { usePickersTranslations } from '../hooks/usePickersTranslations';
import { useUtils } from '../internals/hooks/useUtils';
-import { validateTime } from '../internals/utils/validation/validateTime';
+import { extractValidationProps, validateTime } from '../validation';
import { ClockIcon } from '../icons';
import { useDesktopPicker } from '../internals/hooks/useDesktopPicker';
-import { extractValidationProps } from '../internals/utils/validation/extractValidationProps';
import {
renderDigitalClockTimeView,
renderMultiSectionDigitalClockTimeView,
diff --git a/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.tsx b/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.tsx
index 34fece6808b9..63d582d95c90 100644
--- a/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.tsx
+++ b/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.tsx
@@ -7,10 +7,9 @@ import { MobileDatePickerProps } from './MobileDatePicker.types';
import { DatePickerViewRenderers, useDatePickerDefaultizedProps } from '../DatePicker/shared';
import { usePickersTranslations } from '../hooks/usePickersTranslations';
import { useUtils } from '../internals/hooks/useUtils';
-import { validateDate } from '../internals/utils/validation/validateDate';
+import { extractValidationProps, validateDate } from '../validation';
import { DateView, PickerValidDate } from '../models';
import { DateField } from '../DateField';
-import { extractValidationProps } from '../internals/utils/validation/extractValidationProps';
import { singleItemValueManager } from '../internals/utils/valueManagers';
import { renderDateViewCalendar } from '../dateViewRenderers';
import { resolveDateFormat } from '../internals/utils/date-utils';
diff --git a/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx b/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx
index b04083666c4e..61f00976f527 100644
--- a/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx
+++ b/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx
@@ -11,10 +11,9 @@ import {
} from '../DateTimePicker/shared';
import { usePickersTranslations } from '../hooks/usePickersTranslations';
import { useUtils } from '../internals/hooks/useUtils';
-import { validateDateTime } from '../internals/utils/validation/validateDateTime';
+import { extractValidationProps, validateDateTime } from '../validation';
import { DateOrTimeView, PickerValidDate } from '../models';
import { useMobilePicker } from '../internals/hooks/useMobilePicker';
-import { extractValidationProps } from '../internals/utils/validation/extractValidationProps';
import { renderDateViewCalendar } from '../dateViewRenderers';
import { renderTimeViewClock } from '../timeViewRenderers';
import { resolveDateTimeFormat } from '../internals/utils/date-time-utils';
diff --git a/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.tsx b/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.tsx
index 7a398cc43d1a..1577e1ea0fd6 100644
--- a/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.tsx
+++ b/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.tsx
@@ -8,10 +8,9 @@ import { MobileTimePickerProps } from './MobileTimePicker.types';
import { TimePickerViewRenderers, useTimePickerDefaultizedProps } from '../TimePicker/shared';
import { usePickersTranslations } from '../hooks/usePickersTranslations';
import { useUtils } from '../internals/hooks/useUtils';
-import { validateTime } from '../internals/utils/validation/validateTime';
+import { extractValidationProps, validateTime } from '../validation';
import { PickerValidDate, TimeView } from '../models';
import { useMobilePicker } from '../internals/hooks/useMobilePicker';
-import { extractValidationProps } from '../internals/utils/validation/extractValidationProps';
import { renderTimeViewClock } from '../timeViewRenderers';
import { resolveTimeFormat } from '../internals/utils/time-utils';
import { buildGetOpenDialogAriaText } from '../locales/utils/getPickersLocalization';
diff --git a/packages/x-date-pickers/src/StaticDatePicker/StaticDatePicker.tsx b/packages/x-date-pickers/src/StaticDatePicker/StaticDatePicker.tsx
index 4338718c441b..46e6308aba4d 100644
--- a/packages/x-date-pickers/src/StaticDatePicker/StaticDatePicker.tsx
+++ b/packages/x-date-pickers/src/StaticDatePicker/StaticDatePicker.tsx
@@ -4,7 +4,7 @@ import { StaticDatePickerProps } from './StaticDatePicker.types';
import { DatePickerViewRenderers, useDatePickerDefaultizedProps } from '../DatePicker/shared';
import { renderDateViewCalendar } from '../dateViewRenderers';
import { useStaticPicker } from '../internals/hooks/useStaticPicker';
-import { validateDate } from '../internals/utils/validation/validateDate';
+import { validateDate } from '../validation';
import { DateView, PickerValidDate } from '../models';
import { singleItemValueManager } from '../internals/utils/valueManagers';
diff --git a/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.tsx b/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.tsx
index a93916d40fcd..f1f7a5005e49 100644
--- a/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.tsx
+++ b/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.tsx
@@ -10,7 +10,7 @@ import { renderDateViewCalendar } from '../dateViewRenderers';
import { singleItemValueManager } from '../internals/utils/valueManagers';
import { useStaticPicker } from '../internals/hooks/useStaticPicker';
import { DateOrTimeView, PickerValidDate } from '../models';
-import { validateDateTime } from '../internals/utils/validation/validateDateTime';
+import { validateDateTime } from '../validation';
type StaticDateTimePickerComponent = ((
props: StaticDateTimePickerProps & React.RefAttributes,
diff --git a/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.tsx b/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.tsx
index 49466bcc2123..31a54d09e0db 100644
--- a/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.tsx
+++ b/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.tsx
@@ -6,7 +6,7 @@ import { TimePickerViewRenderers, useTimePickerDefaultizedProps } from '../TimeP
import { renderTimeViewClock } from '../timeViewRenderers';
import { singleItemValueManager } from '../internals/utils/valueManagers';
import { useStaticPicker } from '../internals/hooks/useStaticPicker';
-import { validateTime } from '../internals/utils/validation/validateTime';
+import { validateTime } from '../validation';
type StaticTimePickerComponent = ((
props: StaticTimePickerProps & React.RefAttributes,
diff --git a/packages/x-date-pickers/src/TimeField/TimeField.types.ts b/packages/x-date-pickers/src/TimeField/TimeField.types.ts
index 478cf08ee9ee..0ce1a53d6e40 100644
--- a/packages/x-date-pickers/src/TimeField/TimeField.types.ts
+++ b/packages/x-date-pickers/src/TimeField/TimeField.types.ts
@@ -2,13 +2,14 @@ import * as React from 'react';
import { SlotComponentProps } from '@mui/utils';
import TextField from '@mui/material/TextField';
import { UseFieldInternalProps } from '../internals/hooks/useField';
-import { MakeOptional } from '../internals/models/helpers';
+import { DefaultizedProps, MakeOptional } from '../internals/models/helpers';
import { BaseTimeValidationProps, TimeValidationProps } from '../internals/models/validation';
import {
FieldSection,
PickerValidDate,
TimeValidationError,
BuiltInFieldTextFieldProps,
+ BaseSingleInputFieldProps,
} from '../models';
import {
ExportedUseClearableFieldProps,
@@ -39,6 +40,19 @@ export interface UseTimeFieldProps<
ampm?: boolean;
}
+/**
+ * Props the field can receive when used inside a time picker.
+ * (`TimePicker`, `DesktopTimePicker` or `MobileTimePicker` component).
+ */
+export type TimeFieldInPickerProps<
+ TDate extends PickerValidDate,
+ TEnableAccessibleFieldDOMStructure extends boolean,
+> = DefaultizedProps<
+ UseTimeFieldProps,
+ 'format' | 'timezone' | 'ampm' | keyof BaseTimeValidationProps
+> &
+ BaseSingleInputFieldProps;
+
export type UseTimeFieldComponentProps<
TDate extends PickerValidDate,
TEnableAccessibleFieldDOMStructure extends boolean,
diff --git a/packages/x-date-pickers/src/TimeField/index.ts b/packages/x-date-pickers/src/TimeField/index.ts
index f335f0f8fd76..cf9c1e6a5881 100644
--- a/packages/x-date-pickers/src/TimeField/index.ts
+++ b/packages/x-date-pickers/src/TimeField/index.ts
@@ -4,4 +4,5 @@ export type {
UseTimeFieldProps,
UseTimeFieldComponentProps,
TimeFieldProps,
+ TimeFieldInPickerProps,
} from './TimeField.types';
diff --git a/packages/x-date-pickers/src/TimeField/useTimeField.ts b/packages/x-date-pickers/src/TimeField/useTimeField.ts
index e3f97c8b4b5c..db4d9aa859df 100644
--- a/packages/x-date-pickers/src/TimeField/useTimeField.ts
+++ b/packages/x-date-pickers/src/TimeField/useTimeField.ts
@@ -4,7 +4,7 @@ import {
} from '../internals/utils/valueManagers';
import { useField } from '../internals/hooks/useField';
import { UseTimeFieldProps } from './TimeField.types';
-import { validateTime } from '../internals/utils/validation/validateTime';
+import { validateTime } from '../validation';
import { useSplitFieldProps } from '../hooks';
import { PickerValidDate, FieldSection } from '../models';
import { useDefaultizedTimeField } from '../internals/hooks/defaultizedFieldProps';
diff --git a/packages/x-date-pickers/src/hooks/useSplitFieldProps.ts b/packages/x-date-pickers/src/hooks/useSplitFieldProps.ts
index 150823985f69..3498bbf38df1 100644
--- a/packages/x-date-pickers/src/hooks/useSplitFieldProps.ts
+++ b/packages/x-date-pickers/src/hooks/useSplitFieldProps.ts
@@ -4,7 +4,7 @@ import {
DATE_TIME_VALIDATION_PROP_NAMES,
DATE_VALIDATION_PROP_NAMES,
TIME_VALIDATION_PROP_NAMES,
-} from '../internals/utils/validation/extractValidationProps';
+} from '../validation/extractValidationProps';
const SHARED_FIELD_INTERNAL_PROP_NAMES = [
'value',
diff --git a/packages/x-date-pickers/src/index.ts b/packages/x-date-pickers/src/index.ts
index cd2e4e9586e5..f0987002adb1 100644
--- a/packages/x-date-pickers/src/index.ts
+++ b/packages/x-date-pickers/src/index.ts
@@ -53,7 +53,6 @@ export * from './PickersSectionList';
export { DEFAULT_DESKTOP_MODE_MEDIA_QUERY } from './internals/utils/utils';
export * from './models';
-
export * from './icons';
-
export * from './hooks';
+export * from './validation';
diff --git a/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx b/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx
index 97fb108f8733..b13a78700e27 100644
--- a/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx
+++ b/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx
@@ -14,12 +14,12 @@ import {
import { usePicker } from '../usePicker';
import { LocalizationProvider } from '../../../LocalizationProvider';
import { PickersLayout } from '../../../PickersLayout';
-import { InferError } from '../useValidation';
import {
FieldSection,
PickerValidDate,
FieldRef,
BaseSingleInputFieldProps,
+ InferError,
} from '../../../models';
import { DateOrTimeViewWithMeridiem } from '../../models';
diff --git a/packages/x-date-pickers/src/internals/hooks/useField/useField.ts b/packages/x-date-pickers/src/internals/hooks/useField/useField.ts
index 1d8bc5fdcffb..2ad8363212a9 100644
--- a/packages/x-date-pickers/src/internals/hooks/useField/useField.ts
+++ b/packages/x-date-pickers/src/internals/hooks/useField/useField.ts
@@ -2,7 +2,7 @@ import * as React from 'react';
import useEnhancedEffect from '@mui/utils/useEnhancedEffect';
import useEventCallback from '@mui/utils/useEventCallback';
import { useRtl } from '@mui/system/RtlProvider';
-import { useValidation } from '../useValidation';
+import { useValidation } from '../../../validation';
import { useUtils } from '../useUtils';
import {
UseFieldParams,
@@ -226,12 +226,13 @@ export const useField = <
interactions.syncSelectionToDOM();
});
- const validationError = useValidation(
- { ...internalProps, value: state.value, timezone },
+ const { hasValidationError } = useValidation({
+ props: internalProps,
validator,
- valueManager.isSameError,
- valueManager.defaultErrorState,
- );
+ timezone,
+ value: state.value,
+ onError: internalProps.onError,
+ });
const inputError = React.useMemo(() => {
// only override when `error` is undefined.
@@ -240,8 +241,8 @@ export const useField = <
return error;
}
- return valueManager.hasError(validationError);
- }, [valueManager, validationError, error]);
+ return hasValidationError;
+ }, [hasValidationError, error]);
React.useEffect(() => {
if (!inputError && activeSectionIndex == null) {
diff --git a/packages/x-date-pickers/src/internals/hooks/useField/useField.types.ts b/packages/x-date-pickers/src/internals/hooks/useField/useField.types.ts
index 7700c06357b6..e558a7b3b7ee 100644
--- a/packages/x-date-pickers/src/internals/hooks/useField/useField.types.ts
+++ b/packages/x-date-pickers/src/internals/hooks/useField/useField.types.ts
@@ -7,12 +7,13 @@ import {
TimezoneProps,
FieldSectionContentType,
FieldValueType,
- PickersTimezone,
PickerValidDate,
FieldRef,
+ OnErrorProps,
+ InferError,
} from '../../../models';
import type { PickerValueManager } from '../usePicker';
-import { InferError, Validator } from '../useValidation';
+import type { Validator } from '../../../validation';
import type { UseFieldStateResponse } from './useFieldState';
import type { UseFieldCharacterEditingResponse } from './useFieldCharacterEditing';
import { PickersSectionElement, PickersSectionListRef } from '../../../PickersSectionList';
@@ -37,12 +38,7 @@ export interface UseFieldParams<
internalProps: TInternalProps;
valueManager: PickerValueManager>;
fieldValueManager: FieldValueManager;
- validator: Validator<
- TValue,
- TDate,
- InferError,
- UseFieldValidationProps
- >;
+ validator: Validator, TInternalProps>;
valueType: FieldValueType;
}
@@ -52,7 +48,8 @@ export interface UseFieldInternalProps<
TSection extends FieldSection,
TEnableAccessibleFieldDOMStructure extends boolean,
TError,
-> extends TimezoneProps {
+> extends TimezoneProps,
+ OnErrorProps {
/**
* The selected value.
* Used when the component is controlled.
@@ -76,16 +73,6 @@ export interface UseFieldInternalProps<
* @param {FieldChangeHandlerContext} context The context containing the validation result of the current value.
*/
onChange?: FieldChangeHandler;
- /**
- * Callback fired when the error associated with the current value changes.
- * When a validation error is detected, the `error` parameter contains a non-null value.
- * This can be used to render an appropriate form error.
- * @template TError The validation error type. It will be either `string` or a `null`. It can be in `[start, end]` format in case of range value.
- * @template TValue The value type. It will be the same type as `value` or `null`. It can be in `[start, end]` format in case of range value.
- * @param {TError} error The reason why the current value is not valid.
- * @param {TValue} value The value associated with the error.
- */
- onError?: (error: TError, value: TValue) => void;
/**
* Format of the date when rendered in the input(s).
*/
@@ -398,14 +385,6 @@ export interface UseFieldState {
tempValueStrAndroid: string | null;
}
-export type UseFieldValidationProps<
- TValue,
- TInternalProps extends { value?: TValue; defaultValue?: TValue; timezone?: PickersTimezone },
-> = Omit & {
- value: TValue;
- timezone: PickersTimezone;
-};
-
export type AvailableAdjustKeyCode =
| 'ArrowUp'
| 'ArrowDown'
diff --git a/packages/x-date-pickers/src/internals/hooks/useField/useFieldState.ts b/packages/x-date-pickers/src/internals/hooks/useField/useFieldState.ts
index 23e6bbc66900..846ab4be676d 100644
--- a/packages/x-date-pickers/src/internals/hooks/useField/useFieldState.ts
+++ b/packages/x-date-pickers/src/internals/hooks/useField/useFieldState.ts
@@ -21,12 +21,12 @@ import {
getLocalizedDigits,
} from './useField.utils';
import { buildSectionsFromFormat } from './buildSectionsFromFormat';
-import { InferError } from '../useValidation';
import {
FieldSection,
FieldSelectedSections,
PickersTimezone,
PickerValidDate,
+ InferError,
} from '../../../models';
import { useValueWithTimezone } from '../useValueWithTimezone';
import {
@@ -228,7 +228,8 @@ export const useFieldState = <
validationError: validator({
adapter,
value,
- props: { ...internalProps, value, timezone },
+ timezone,
+ props: internalProps,
}),
};
diff --git a/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.tsx b/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.tsx
index 15b31e4fc63a..6145d21537ae 100644
--- a/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.tsx
+++ b/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.tsx
@@ -12,12 +12,12 @@ import { usePicker } from '../usePicker';
import { onSpaceOrEnter } from '../../utils/utils';
import { LocalizationProvider } from '../../../LocalizationProvider';
import { PickersLayout } from '../../../PickersLayout';
-import { InferError } from '../useValidation';
import {
FieldSection,
BaseSingleInputFieldProps,
PickerValidDate,
FieldRef,
+ InferError,
} from '../../../models';
import { DateOrTimeViewWithMeridiem } from '../../models';
diff --git a/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.ts b/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.ts
index 4ac218f211b0..8c74daccc437 100644
--- a/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.ts
+++ b/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.ts
@@ -3,8 +3,7 @@ import { UsePickerParams, UsePickerProps, UsePickerResponse } from './usePicker.
import { usePickerValue } from './usePickerValue';
import { usePickerViews } from './usePickerViews';
import { usePickerLayoutProps } from './usePickerLayoutProps';
-import { InferError } from '../useValidation';
-import { FieldSection, PickerValidDate } from '../../../models';
+import { FieldSection, PickerValidDate, InferError } from '../../../models';
import { DateOrTimeViewWithMeridiem } from '../../models';
export const usePicker = <
diff --git a/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerValue.ts b/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerValue.ts
index 3bd7a5eb08a2..8d6255b5f562 100644
--- a/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerValue.ts
+++ b/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerValue.ts
@@ -3,8 +3,13 @@ import useEventCallback from '@mui/utils/useEventCallback';
import { useOpenState } from '../useOpenState';
import { useLocalizationContext, useUtils } from '../useUtils';
import { FieldChangeHandlerContext } from '../useField';
-import { InferError, useValidation } from '../useValidation';
-import { FieldSection, PickerChangeHandlerContext, PickerValidDate } from '../../../models';
+import { useValidation } from '../../../validation';
+import {
+ FieldSection,
+ PickerChangeHandlerContext,
+ PickerValidDate,
+ InferError,
+} from '../../../models';
import {
PickerShortcutChangeImportance,
PickersShortcutsItemContext,
@@ -241,12 +246,13 @@ export const usePickerValue = <
};
});
- useValidation(
- { ...props, value: dateState.draft, timezone },
+ const { getValidationErrorForNewValue } = useValidation({
+ props,
validator,
- valueManager.isSameError,
- valueManager.defaultErrorState,
- );
+ timezone,
+ value: dateState.draft,
+ onError: props.onError,
+ });
const updateDate = useEventCallback((action: PickerValueUpdateAction) => {
const updaterParams: PickerValueUpdaterParams = {
@@ -275,11 +281,7 @@ export const usePickerValue = <
const validationError =
action.name === 'setValueFromField'
? action.context.validationError
- : validator({
- adapter,
- value: action.value,
- props: { ...props, value: action.value, timezone },
- });
+ : getValidationErrorForNewValue(action.value);
cachedContext = {
validationError,
@@ -440,7 +442,8 @@ export const usePickerValue = <
const error = validator({
adapter,
value: testedValue,
- props: { ...props, value: testedValue, timezone },
+ timezone,
+ props,
});
return !valueManager.hasError(error);
diff --git a/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerValue.types.ts b/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerValue.types.ts
index d2a170a8728e..a2882f62f57f 100644
--- a/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerValue.types.ts
+++ b/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerValue.types.ts
@@ -1,6 +1,5 @@
import { FieldChangeHandlerContext, UseFieldInternalProps } from '../useField';
-import { InferError, Validator } from '../useValidation';
-import { UseFieldValidationProps } from '../useField/useField.types';
+import { Validator } from '../../../validation';
import { WrapperVariant } from '../../models/common';
import {
FieldSection,
@@ -10,6 +9,8 @@ import {
PickersTimezone,
PickerChangeHandlerContext,
PickerValidDate,
+ OnErrorProps,
+ InferError,
} from '../../../models';
import { GetDefaultReferenceDateProps } from '../../utils/getDefaultReferenceDate';
import {
@@ -207,7 +208,7 @@ export type PickerValueUpdateAction =
/**
* Props used to handle the value that are common to all pickers.
*/
-export interface UsePickerValueBaseProps {
+export interface UsePickerValueBaseProps extends OnErrorProps {
/**
* The selected value.
* Used when the component is controlled.
@@ -234,16 +235,6 @@ export interface UsePickerValueBaseProps {
* @param {FieldChangeHandlerContext} context The context containing the validation result of the current value.
*/
onAccept?: (value: TValue, context: PickerChangeHandlerContext) => void;
- /**
- * Callback fired when the error associated with the current value changes.
- * When a validation error is detected, the `error` parameter contains a non-null value.
- * This can be used to render an appropriate form error.
- * @template TError The validation error type. It will be either `string` or a `null`. It can be in `[start, end]` format in case of range value.
- * @template TValue The value type. It will be the same type as `value` or `null`. It can be in `[start, end]` format in case of range value.
- * @param {TError} error The reason why the current value is not valid.
- * @param {TValue} value The value associated with the error.
- */
- onError?: (error: TError, value: TValue) => void;
}
/**
@@ -289,12 +280,7 @@ export interface UsePickerValueParams<
valueManager: PickerValueManager>;
valueType: FieldValueType;
wrapperVariant: WrapperVariant;
- validator: Validator<
- TValue,
- TDate,
- InferError,
- UseFieldValidationProps
- >;
+ validator: Validator, TExternalProps>;
}
export interface UsePickerValueActions {
diff --git a/packages/x-date-pickers/src/internals/hooks/useValidation.ts b/packages/x-date-pickers/src/internals/hooks/useValidation.ts
deleted file mode 100644
index ce2fb99cfb29..000000000000
--- a/packages/x-date-pickers/src/internals/hooks/useValidation.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import * as React from 'react';
-import { useLocalizationContext } from './useUtils';
-import { MuiPickersAdapterContextValue } from '../../LocalizationProvider/LocalizationProvider';
-import { PickerValidDate } from '../../models';
-
-interface ValidationCommonProps {
- /**
- * Callback fired when the error associated with the current value changes.
- * When a validation error is detected, the `error` parameter contains a non-null value.
- * This can be used to render an appropriate form error.
- * @template TError The validation error type. It will be either `string` or a `null`. It can be in `[start, end]` format in case of range value.
- * @template TValue The value type. It will be the same type as `value` or `null`. It can be in `[start, end]` format in case of range value.
- * @param {TError} error The reason why the current value is not valid.
- * @param {TValue} value The value associated with the error.
- */
- onError?: (error: TError, value: TValue) => void;
- value: TValue;
-}
-
-export type ValidationProps = ValidationCommonProps<
- TError,
- TValue
-> &
- TValidationProps;
-
-export type InferError =
- TProps extends Pick, 'onError'>
- ? Parameters>[0]
- : never;
-
-export type Validator = (params: {
- adapter: MuiPickersAdapterContextValue;
- value: TValue;
- props: Omit;
-}) => TError;
-
-export function useValidation<
- TValue,
- TDate extends PickerValidDate,
- TError,
- TValidationProps extends {},
->(
- props: ValidationProps,
- validate: Validator,
- isSameError: (a: TError, b: TError | null) => boolean,
- defaultErrorState: TError,
-): TError {
- const { value, onError } = props;
- const adapter = useLocalizationContext();
- const previousValidationErrorRef = React.useRef(defaultErrorState);
-
- const validationError = validate({ adapter, value, props });
-
- React.useEffect(() => {
- if (onError && !isSameError(validationError, previousValidationErrorRef.current)) {
- onError(validationError, value);
- }
-
- previousValidationErrorRef.current = validationError;
- }, [isSameError, onError, previousValidationErrorRef, validationError, value]);
-
- return validationError;
-}
diff --git a/packages/x-date-pickers/src/internals/index.ts b/packages/x-date-pickers/src/internals/index.ts
index 709f02096df7..6bd9dab4cd19 100644
--- a/packages/x-date-pickers/src/internals/index.ts
+++ b/packages/x-date-pickers/src/internals/index.ts
@@ -92,8 +92,6 @@ export type {
export { useLocalizationContext, useDefaultDates, useUtils, useNow } from './hooks/useUtils';
export type { ExportedUseViewsOptions, UseViewsOptions } from './hooks/useViews';
export { useViews } from './hooks/useViews';
-export { useValidation } from './hooks/useValidation';
-export type { ValidationProps, Validator, InferError } from './hooks/useValidation';
export { usePreviousMonthDisabled, useNextMonthDisabled } from './hooks/date-helpers-hooks';
export type { BaseFieldProps } from './models/fields';
@@ -145,10 +143,6 @@ export {
useDefaultizedDateTimeField,
} from './hooks/defaultizedFieldProps';
export { useDefaultReduceAnimations } from './hooks/useDefaultReduceAnimations';
-export { extractValidationProps } from './utils/validation/extractValidationProps';
-export { validateDate } from './utils/validation/validateDate';
-export { validateDateTime } from './utils/validation/validateDateTime';
-export { validateTime } from './utils/validation/validateTime';
export { applyDefaultViewProps } from './utils/views';
export { DayCalendar } from '../DateCalendar/DayCalendar';
diff --git a/packages/x-date-pickers/src/internals/utils/validation/validateDateTime.ts b/packages/x-date-pickers/src/internals/utils/validation/validateDateTime.ts
deleted file mode 100644
index ff14f93ac1eb..000000000000
--- a/packages/x-date-pickers/src/internals/utils/validation/validateDateTime.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { Validator } from '../../hooks/useValidation';
-import { validateDate, DateComponentValidationProps } from './validateDate';
-import { validateTime, TimeComponentValidationProps } from './validateTime';
-import { DateTimeValidationError, PickerValidDate } from '../../../models';
-
-export interface DateTimeComponentValidationProps
- extends DateComponentValidationProps,
- TimeComponentValidationProps {}
-
-export const validateDateTime: Validator<
- any | null,
- any,
- DateTimeValidationError,
- DateTimeComponentValidationProps
-> = ({ props, value, adapter }) => {
- const dateValidationResult = validateDate({
- adapter,
- value,
- props,
- });
-
- if (dateValidationResult !== null) {
- return dateValidationResult;
- }
-
- return validateTime({
- adapter,
- value,
- props,
- });
-};
diff --git a/packages/x-date-pickers/src/models/validation.ts b/packages/x-date-pickers/src/models/validation.ts
index 397ea4334d03..5dcee050db5f 100644
--- a/packages/x-date-pickers/src/models/validation.ts
+++ b/packages/x-date-pickers/src/models/validation.ts
@@ -21,3 +21,21 @@ export type TimeValidationError =
| 'shouldDisableTime-seconds';
export type DateTimeValidationError = DateValidationError | TimeValidationError;
+
+export interface OnErrorProps {
+ /**
+ * Callback fired when the error associated with the current value changes.
+ * When a validation error is detected, the `error` parameter contains a non-null value.
+ * This can be used to render an appropriate form error.
+ * @template TError The validation error type. It will be either `string` or a `null`. It can be in `[start, end]` format in case of range value.
+ * @template TValue The value type. It will be the same type as `value` or `null`. It can be in `[start, end]` format in case of range value.
+ * @param {TError} error The reason why the current value is not valid.
+ * @param {TValue} value The value associated with the error.
+ */
+ onError?: (error: TError, value: TValue) => void;
+}
+
+export type InferError =
+ TProps extends Pick, 'onError'>
+ ? Parameters>[0]
+ : never;
diff --git a/packages/x-date-pickers/src/internals/utils/validation/extractValidationProps.ts b/packages/x-date-pickers/src/validation/extractValidationProps.ts
similarity index 97%
rename from packages/x-date-pickers/src/internals/utils/validation/extractValidationProps.ts
rename to packages/x-date-pickers/src/validation/extractValidationProps.ts
index a2d90cde1510..3b2bd180137e 100644
--- a/packages/x-date-pickers/src/internals/utils/validation/extractValidationProps.ts
+++ b/packages/x-date-pickers/src/validation/extractValidationProps.ts
@@ -6,7 +6,7 @@ import {
MonthValidationProps,
TimeValidationProps,
YearValidationProps,
-} from '../../models/validation';
+} from '../internals/models/validation';
export const DATE_VALIDATION_PROP_NAMES: (
| keyof BaseDateValidationProps
diff --git a/packages/x-date-pickers/src/validation/index.ts b/packages/x-date-pickers/src/validation/index.ts
new file mode 100644
index 000000000000..b3f964c143f8
--- /dev/null
+++ b/packages/x-date-pickers/src/validation/index.ts
@@ -0,0 +1,13 @@
+export { validateDate } from './validateDate';
+export type { ValidateDateProps } from './validateDate';
+
+export { validateTime } from './validateTime';
+export type { ValidateTimeProps } from './validateTime';
+
+export { validateDateTime } from './validateDateTime';
+export type { ValidateDateTimeProps } from './validateDateTime';
+
+export { extractValidationProps } from './extractValidationProps';
+
+export { useValidation } from './useValidation';
+export type { Validator } from './useValidation';
diff --git a/packages/x-date-pickers/src/validation/useValidation.ts b/packages/x-date-pickers/src/validation/useValidation.ts
new file mode 100644
index 000000000000..392d6780725a
--- /dev/null
+++ b/packages/x-date-pickers/src/validation/useValidation.ts
@@ -0,0 +1,113 @@
+import * as React from 'react';
+import useEventCallback from '@mui/utils/useEventCallback';
+import { useLocalizationContext } from '../internals/hooks/useUtils';
+import { MuiPickersAdapterContextValue } from '../LocalizationProvider/LocalizationProvider';
+import { OnErrorProps, PickersTimezone, PickerValidDate } from '../models';
+import type { PickerValueManager } from '../internals/hooks/usePicker';
+
+export type Validator = {
+ (params: {
+ adapter: MuiPickersAdapterContextValue;
+ value: TValue;
+ timezone: PickersTimezone;
+ props: TValidationProps;
+ }): TError;
+ valueManager: PickerValueManager;
+};
+
+interface UseValidationOptions<
+ TValue,
+ TDate extends PickerValidDate,
+ TError,
+ TValidationProps extends {},
+> extends OnErrorProps {
+ /**
+ * The value to validate.
+ */
+ value: TValue;
+ /**
+ * The timezone to use for the validation.
+ */
+ timezone: PickersTimezone;
+ /**
+ * The validator function to use.
+ * They can be imported from `@mui/x-date-pickers/validation` and `@mui/x-date-pickers-pro/validation`.
+ * It is recommended to only use the validator exported by the MUI X packages,
+ * otherwise you may have inconsistent behaviors between the field and the views.
+ */
+ validator: Validator;
+ /**
+ * The validation props, they differ depending on the component.
+ * For example, the `validateTime` function supports `minTime`, `maxTime`, etc.
+ */
+ props: TValidationProps;
+}
+
+interface UseValidationReturnValue {
+ /**
+ * The validation error associated to the value passed to the `useValidation` hook.
+ */
+ validationError: TError;
+ /**
+ * `true` if the current error is not null.
+ * For single value components, it means that the value is invalid.
+ * For range components, it means that either start or end value is invalid.
+ */
+ hasValidationError: boolean;
+ /**
+ * Get the validation error for a new value.
+ * This can be used to validate the value in a change handler before updating the state.
+ * @template TValue The value type.
+ * @param {TValue} newValue The value to validate.
+ * @returns {TError} The validation error associated to the new value.
+ */
+ getValidationErrorForNewValue: (newValue: TValue) => TError;
+}
+
+/**
+ * Utility hook to check if a given value is valid based on the provided validation props.
+ * @template TDate
+ * @template TValue The value type. It will be either the same type as `value` or `null`. It can be in `[start, end]` format in case of range value.
+ * @template TError The validation error type. It will be either `string` or a `null`. It can be in `[start, end]` format in case of range value.
+ * @param {UseValidationOptions} options The options to configure the hook.
+ * @param {TValue} options.value The value to validate.
+ * @param {PickersTimezone} options.timezone The timezone to use for the validation.
+ * @param {Validator} options.validator The validator function to use.
+ * @param {TValidationProps} options.props The validation props, they differ depending on the component.
+ * @param {(error: TError, value: TValue) => void} options.onError Callback fired when the error associated with the current value changes.
+ */
+export function useValidation<
+ TValue,
+ TDate extends PickerValidDate,
+ TError,
+ TValidationProps extends {},
+>(
+ options: UseValidationOptions,
+): UseValidationReturnValue {
+ const { props, validator, value, timezone, onError } = options;
+
+ const adapter = useLocalizationContext();
+ const previousValidationErrorRef = React.useRef(
+ validator.valueManager.defaultErrorState,
+ );
+
+ const validationError = validator({ adapter, value, timezone, props });
+ const hasValidationError = validator.valueManager.hasError(validationError);
+
+ React.useEffect(() => {
+ if (
+ onError &&
+ !validator.valueManager.isSameError(validationError, previousValidationErrorRef.current)
+ ) {
+ onError(validationError, value);
+ }
+
+ previousValidationErrorRef.current = validationError;
+ }, [validator, onError, validationError, value]);
+
+ const getValidationErrorForNewValue = useEventCallback((newValue: TValue) => {
+ return validator({ adapter, value: newValue, timezone, props });
+ });
+
+ return { validationError, hasValidationError, getValidationErrorForNewValue };
+}
diff --git a/packages/x-date-pickers/src/internals/utils/validation/validateDate.ts b/packages/x-date-pickers/src/validation/validateDate.ts
similarity index 67%
rename from packages/x-date-pickers/src/internals/utils/validation/validateDate.ts
rename to packages/x-date-pickers/src/validation/validateDate.ts
index 363b13a96f70..e3bf707969aa 100644
--- a/packages/x-date-pickers/src/internals/utils/validation/validateDate.ts
+++ b/packages/x-date-pickers/src/validation/validateDate.ts
@@ -1,39 +1,32 @@
-import { Validator } from '../../hooks/useValidation';
+import { Validator } from './useValidation';
import {
BaseDateValidationProps,
DayValidationProps,
MonthValidationProps,
YearValidationProps,
-} from '../../models/validation';
-import { DateValidationError, PickerValidDate, TimezoneProps } from '../../../models';
-import { applyDefaultDate } from '../date-utils';
-import { DefaultizedProps } from '../../models/helpers';
+} from '../internals/models/validation';
+import { DateValidationError, PickerValidDate } from '../models';
+import { applyDefaultDate } from '../internals/utils/date-utils';
+import { singleItemValueManager } from '../internals/utils/valueManagers';
-export interface DateComponentValidationProps
+export interface ValidateDateProps
extends DayValidationProps,
MonthValidationProps,
YearValidationProps,
- Required>,
- DefaultizedProps {}
+ Required> {}
export const validateDate: Validator<
any | null,
any,
DateValidationError,
- DateComponentValidationProps
-> = ({ props, value, adapter }): DateValidationError => {
+ ValidateDateProps
+> = ({ props, value, timezone, adapter }): DateValidationError => {
if (value === null) {
return null;
}
- const {
- shouldDisableDate,
- shouldDisableMonth,
- shouldDisableYear,
- disablePast,
- disableFuture,
- timezone,
- } = props;
+ const { shouldDisableDate, shouldDisableMonth, shouldDisableYear, disablePast, disableFuture } =
+ props;
const now = adapter.utils.date(undefined, timezone);
const minDate = applyDefaultDate(adapter.utils, props.minDate, adapter.defaultDates.minDate);
@@ -68,3 +61,5 @@ export const validateDate: Validator<
return null;
}
};
+
+validateDate.valueManager = singleItemValueManager;
diff --git a/packages/x-date-pickers/src/validation/validateDateTime.ts b/packages/x-date-pickers/src/validation/validateDateTime.ts
new file mode 100644
index 000000000000..1e4824635532
--- /dev/null
+++ b/packages/x-date-pickers/src/validation/validateDateTime.ts
@@ -0,0 +1,36 @@
+import { Validator } from './useValidation';
+import { validateDate, ValidateDateProps } from './validateDate';
+import { validateTime, ValidateTimeProps } from './validateTime';
+import { DateTimeValidationError, PickerValidDate } from '../models';
+import { singleItemValueManager } from '../internals/utils/valueManagers';
+
+export interface ValidateDateTimeProps
+ extends ValidateDateProps,
+ ValidateTimeProps {}
+
+export const validateDateTime: Validator<
+ any | null,
+ any,
+ DateTimeValidationError,
+ ValidateDateTimeProps
+> = ({ adapter, value, timezone, props }) => {
+ const dateValidationResult = validateDate({
+ adapter,
+ value,
+ timezone,
+ props,
+ });
+
+ if (dateValidationResult !== null) {
+ return dateValidationResult;
+ }
+
+ return validateTime({
+ adapter,
+ value,
+ timezone,
+ props,
+ });
+};
+
+validateDateTime.valueManager = singleItemValueManager;
diff --git a/packages/x-date-pickers/src/internals/utils/validation/validateTime.ts b/packages/x-date-pickers/src/validation/validateTime.ts
similarity index 73%
rename from packages/x-date-pickers/src/internals/utils/validation/validateTime.ts
rename to packages/x-date-pickers/src/validation/validateTime.ts
index c3d109e7ec0f..2124913a3c14 100644
--- a/packages/x-date-pickers/src/internals/utils/validation/validateTime.ts
+++ b/packages/x-date-pickers/src/validation/validateTime.ts
@@ -1,20 +1,19 @@
-import { createIsAfterIgnoreDatePart } from '../time-utils';
-import { Validator } from '../../hooks/useValidation';
-import { BaseTimeValidationProps, TimeValidationProps } from '../../models/validation';
-import { PickerValidDate, TimeValidationError, TimezoneProps } from '../../../models';
-import { DefaultizedProps } from '../../models/helpers';
+import { createIsAfterIgnoreDatePart } from '../internals/utils/time-utils';
+import { Validator } from './useValidation';
+import { BaseTimeValidationProps, TimeValidationProps } from '../internals/models/validation';
+import { PickerValidDate, TimeValidationError } from '../models';
+import { singleItemValueManager } from '../internals/utils/valueManagers';
-export interface TimeComponentValidationProps
+export interface ValidateTimeProps
extends Required,
- TimeValidationProps,
- DefaultizedProps {}
+ TimeValidationProps {}
export const validateTime: Validator<
any | null,
any,
TimeValidationError,
- TimeComponentValidationProps
-> = ({ adapter, value, props }): TimeValidationError => {
+ ValidateTimeProps
+> = ({ adapter, value, timezone, props }): TimeValidationError => {
if (value === null) {
return null;
}
@@ -27,7 +26,6 @@ export const validateTime: Validator<
disableIgnoringDatePartForTimeValidation = false,
disablePast,
disableFuture,
- timezone,
} = props;
const now = adapter.utils.date(undefined, timezone);
@@ -68,3 +66,5 @@ export const validateTime: Validator<
return null;
}
};
+
+validateTime.valueManager = singleItemValueManager;
diff --git a/scripts/x-date-pickers-pro.exports.json b/scripts/x-date-pickers-pro.exports.json
index 9674b138dfd2..24beed350813 100644
--- a/scripts/x-date-pickers-pro.exports.json
+++ b/scripts/x-date-pickers-pro.exports.json
@@ -34,6 +34,7 @@
{ "name": "DateCalendarSlotProps", "kind": "Interface" },
{ "name": "DateCalendarSlots", "kind": "Interface" },
{ "name": "DateField", "kind": "Variable" },
+ { "name": "DateFieldInPickerProps", "kind": "TypeAlias" },
{ "name": "DateFieldProps", "kind": "TypeAlias" },
{ "name": "DateOrTimeView", "kind": "TypeAlias" },
{ "name": "DatePicker", "kind": "Variable" },
@@ -71,6 +72,7 @@
{ "name": "DateRangeValidationError", "kind": "TypeAlias" },
{ "name": "DateRangeViewRendererProps", "kind": "Interface" },
{ "name": "DateTimeField", "kind": "Variable" },
+ { "name": "DateTimeFieldInPickerProps", "kind": "TypeAlias" },
{ "name": "DateTimeFieldProps", "kind": "TypeAlias" },
{ "name": "DateTimePicker", "kind": "Variable" },
{ "name": "DateTimePickerProps", "kind": "Interface" },
@@ -154,6 +156,7 @@
{ "name": "ExportedPickersYearProps", "kind": "Interface" },
{ "name": "ExportedSlideTransitionProps", "kind": "Interface" },
{ "name": "ExportedUseClearableFieldProps", "kind": "Interface" },
+ { "name": "extractValidationProps", "kind": "Variable" },
{ "name": "FieldFormatTokenMap", "kind": "TypeAlias" },
{ "name": "FieldRef", "kind": "Interface" },
{ "name": "FieldSection", "kind": "Interface" },
@@ -184,6 +187,7 @@
{ "name": "getPickersTextFieldUtilityClass", "kind": "Function" },
{ "name": "getTimeClockUtilityClass", "kind": "Function" },
{ "name": "getYearCalendarUtilityClass", "kind": "Function" },
+ { "name": "InferError", "kind": "TypeAlias" },
{ "name": "LicenseInfo", "kind": "Class" },
{ "name": "LocalizationProvider", "kind": "Variable" },
{ "name": "LocalizationProviderProps", "kind": "Interface" },
@@ -242,6 +246,7 @@
{ "name": "MultiSectionDigitalClockSlotProps", "kind": "Interface" },
{ "name": "MultiSectionDigitalClockSlots", "kind": "Interface" },
{ "name": "NonEmptyDateRange", "kind": "TypeAlias" },
+ { "name": "OnErrorProps", "kind": "Interface" },
{ "name": "PickerChangeHandlerContext", "kind": "Interface" },
{ "name": "PickersActionBar", "kind": "Function" },
{ "name": "PickersActionBarAction", "kind": "TypeAlias" },
@@ -367,6 +372,7 @@
{ "name": "TimeClockSlotProps", "kind": "Interface" },
{ "name": "TimeClockSlots", "kind": "Interface" },
{ "name": "TimeField", "kind": "Variable" },
+ { "name": "TimeFieldInPickerProps", "kind": "TypeAlias" },
{ "name": "TimeFieldProps", "kind": "TypeAlias" },
{ "name": "TimeIcon", "kind": "Variable" },
{ "name": "TimePicker", "kind": "Variable" },
@@ -421,6 +427,20 @@
{ "name": "useSplitFieldProps", "kind": "Variable" },
{ "name": "UseTimeFieldComponentProps", "kind": "TypeAlias" },
{ "name": "UseTimeFieldProps", "kind": "Interface" },
+ { "name": "useValidation", "kind": "Function" },
+ { "name": "validateDate", "kind": "Variable" },
+ { "name": "ValidateDateProps", "kind": "Interface" },
+ { "name": "validateDateRange", "kind": "Variable" },
+ { "name": "ValidateDateRangeProps", "kind": "Interface" },
+ { "name": "validateDateTime", "kind": "Variable" },
+ { "name": "ValidateDateTimeProps", "kind": "Interface" },
+ { "name": "validateDateTimeRange", "kind": "Variable" },
+ { "name": "ValidateDateTimeRangeProps", "kind": "Interface" },
+ { "name": "validateTime", "kind": "Variable" },
+ { "name": "ValidateTimeProps", "kind": "Interface" },
+ { "name": "validateTimeRange", "kind": "Variable" },
+ { "name": "ValidateTimeRangeProps", "kind": "Interface" },
+ { "name": "Validator", "kind": "TypeAlias" },
{ "name": "YearCalendar", "kind": "Variable" },
{ "name": "yearCalendarClasses", "kind": "Variable" },
{ "name": "YearCalendarClasses", "kind": "Interface" },
diff --git a/scripts/x-date-pickers.exports.json b/scripts/x-date-pickers.exports.json
index 1df3b6ad87cb..4ae16b168cba 100644
--- a/scripts/x-date-pickers.exports.json
+++ b/scripts/x-date-pickers.exports.json
@@ -31,6 +31,7 @@
{ "name": "DateCalendarSlotProps", "kind": "Interface" },
{ "name": "DateCalendarSlots", "kind": "Interface" },
{ "name": "DateField", "kind": "Variable" },
+ { "name": "DateFieldInPickerProps", "kind": "TypeAlias" },
{ "name": "DateFieldProps", "kind": "TypeAlias" },
{ "name": "DateOrTimeView", "kind": "TypeAlias" },
{ "name": "DatePicker", "kind": "Variable" },
@@ -44,6 +45,7 @@
{ "name": "DatePickerToolbarProps", "kind": "Interface" },
{ "name": "DateRangeIcon", "kind": "Variable" },
{ "name": "DateTimeField", "kind": "Variable" },
+ { "name": "DateTimeFieldInPickerProps", "kind": "TypeAlias" },
{ "name": "DateTimeFieldProps", "kind": "TypeAlias" },
{ "name": "DateTimePicker", "kind": "Variable" },
{ "name": "DateTimePickerProps", "kind": "Interface" },
@@ -102,6 +104,7 @@
{ "name": "ExportedPickersYearProps", "kind": "Interface" },
{ "name": "ExportedSlideTransitionProps", "kind": "Interface" },
{ "name": "ExportedUseClearableFieldProps", "kind": "Interface" },
+ { "name": "extractValidationProps", "kind": "Variable" },
{ "name": "FieldFormatTokenMap", "kind": "TypeAlias" },
{ "name": "FieldRef", "kind": "Interface" },
{ "name": "FieldSection", "kind": "Interface" },
@@ -123,6 +126,7 @@
{ "name": "getPickersTextFieldUtilityClass", "kind": "Function" },
{ "name": "getTimeClockUtilityClass", "kind": "Function" },
{ "name": "getYearCalendarUtilityClass", "kind": "Function" },
+ { "name": "InferError", "kind": "TypeAlias" },
{ "name": "LocalizationProvider", "kind": "Variable" },
{ "name": "LocalizationProviderProps", "kind": "Interface" },
{ "name": "LocalizedComponent", "kind": "TypeAlias" },
@@ -157,6 +161,7 @@
{ "name": "MultiSectionDigitalClockSectionClassKey", "kind": "TypeAlias" },
{ "name": "MultiSectionDigitalClockSlotProps", "kind": "Interface" },
{ "name": "MultiSectionDigitalClockSlots", "kind": "Interface" },
+ { "name": "OnErrorProps", "kind": "Interface" },
{ "name": "PickerChangeHandlerContext", "kind": "Interface" },
{ "name": "PickersActionBar", "kind": "Function" },
{ "name": "PickersActionBarAction", "kind": "TypeAlias" },
@@ -266,6 +271,7 @@
{ "name": "TimeClockSlotProps", "kind": "Interface" },
{ "name": "TimeClockSlots", "kind": "Interface" },
{ "name": "TimeField", "kind": "Variable" },
+ { "name": "TimeFieldInPickerProps", "kind": "TypeAlias" },
{ "name": "TimeFieldProps", "kind": "TypeAlias" },
{ "name": "TimeIcon", "kind": "Variable" },
{ "name": "TimePicker", "kind": "Variable" },
@@ -303,6 +309,14 @@
{ "name": "useSplitFieldProps", "kind": "Variable" },
{ "name": "UseTimeFieldComponentProps", "kind": "TypeAlias" },
{ "name": "UseTimeFieldProps", "kind": "Interface" },
+ { "name": "useValidation", "kind": "Function" },
+ { "name": "validateDate", "kind": "Variable" },
+ { "name": "ValidateDateProps", "kind": "Interface" },
+ { "name": "validateDateTime", "kind": "Variable" },
+ { "name": "ValidateDateTimeProps", "kind": "Interface" },
+ { "name": "validateTime", "kind": "Variable" },
+ { "name": "ValidateTimeProps", "kind": "Interface" },
+ { "name": "Validator", "kind": "TypeAlias" },
{ "name": "YearCalendar", "kind": "Variable" },
{ "name": "yearCalendarClasses", "kind": "Variable" },
{ "name": "YearCalendarClasses", "kind": "Interface" },