From 0653dc3c82a43cc544150409c1edfda881e9eabe Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 4 Sep 2024 12:00:59 +0200 Subject: [PATCH] [pickers] Clean the validation internals DX to prepare for publication --- .../experimentation/CustomField.js | 52 ++++++++++ .../experimentation/CustomField.tsx | 54 ++++++++++ .../experimentation/CustomField.tsx.preview | 6 ++ .../experimentation/experimentation.md | 11 +++ .../x/react-date-pickers/experimentation.js | 7 ++ .../date-pickers/date-field/date-field.json | 6 +- .../date-pickers/date-picker/date-picker.json | 6 +- .../date-range-picker/date-range-picker.json | 6 +- .../date-time-field/date-time-field.json | 6 +- .../date-time-picker/date-time-picker.json | 6 +- .../date-time-range-picker.json | 6 +- .../desktop-date-picker.json | 6 +- .../desktop-date-range-picker.json | 6 +- .../desktop-date-time-picker.json | 6 +- .../desktop-date-time-range-picker.json | 6 +- .../desktop-time-picker.json | 6 +- .../mobile-date-picker.json | 6 +- .../mobile-date-range-picker.json | 6 +- .../mobile-date-time-picker.json | 6 +- .../mobile-date-time-range-picker.json | 6 +- .../mobile-time-picker.json | 6 +- .../multi-input-date-range-field.json | 6 +- .../multi-input-date-time-range-field.json | 6 +- .../multi-input-time-range-field.json | 6 +- .../single-input-date-range-field.json | 6 +- .../single-input-date-time-range-field.json | 6 +- .../single-input-time-range-field.json | 6 +- .../static-date-picker.json | 6 +- .../static-date-range-picker.json | 6 +- .../static-date-time-picker.json | 6 +- .../static-time-picker.json | 6 +- .../date-pickers/time-field/time-field.json | 6 +- .../date-pickers/time-picker/time-picker.json | 6 +- .../src/DateRangePicker/DateRangePicker.tsx | 10 +- .../DateTimeRangePicker.tsx | 10 +- .../DesktopDateRangePicker.tsx | 15 +-- .../DesktopDateTimeRangePicker.tsx | 14 +-- .../MobileDateRangePicker.tsx | 15 +-- .../MobileDateTimeRangePicker.tsx | 14 +-- .../MultiInputDateRangeField.tsx | 8 +- .../MultiInputDateTimeRangeField.tsx | 8 +- ...nputDateTimeRangeField.validation.test.tsx | 21 ---- ...ibes.MultiInputDateTimeRangeField.test.tsx | 19 +++- .../MultiInputTimeRangeField.tsx | 8 +- ...ltiInputTimeRangeField.validation.test.tsx | 21 ---- ...escribes.MultiInputTimeRangeField.test.tsx | 19 +++- .../SingleInputDateRangeField.tsx | 8 +- .../useSingleInputDateRangeField.ts | 2 +- .../SingleInputDateTimeRangeField.tsx | 8 +- .../useSingleInputDateTimeRangeField.ts | 2 +- .../SingleInputTimeRangeField.tsx | 8 +- .../useSingleInputTimeRangeField.ts | 2 +- .../StaticDateRangePicker.tsx | 12 +-- packages/x-date-pickers-pro/src/index.ts | 1 + .../useDesktopRangePicker.tsx | 8 +- .../useMobileRangePicker.tsx | 8 +- .../useMultiInputDateRangeField.ts | 36 +++---- .../useMultiInputDateTimeRangeField.ts | 35 +++---- .../useMultiInputTimeRangeField.ts | 35 +++---- .../src/validation/index.ts | 8 ++ .../validation/validateDateRange.ts | 32 +++--- .../validation/validateDateTimeRange.ts | 33 +++---- .../validation/validateTimeRange.ts | 28 +++--- .../src/DateCalendar/useIsDateDisabled.ts | 12 +-- .../src/DateField/DateField.tsx | 8 +- .../src/DateField/DateField.types.ts | 16 ++- .../x-date-pickers/src/DateField/index.ts | 1 + .../src/DateField/useDateField.ts | 2 +- .../src/DatePicker/DatePicker.tsx | 10 +- .../src/DateTimeField/DateTimeField.tsx | 8 +- .../src/DateTimeField/DateTimeField.types.ts | 20 +++- .../x-date-pickers/src/DateTimeField/index.ts | 1 + .../src/DateTimeField/useDateTimeField.ts | 2 +- .../src/DateTimePicker/DateTimePicker.tsx | 10 +- .../DesktopDatePicker/DesktopDatePicker.tsx | 13 ++- .../DesktopDateTimePicker.tsx | 13 ++- .../DesktopTimePicker/DesktopTimePicker.tsx | 13 ++- .../src/MobileDatePicker/MobileDatePicker.tsx | 13 ++- .../MobileDateTimePicker.tsx | 13 ++- .../src/MobileTimePicker/MobileTimePicker.tsx | 13 ++- .../src/StaticDatePicker/StaticDatePicker.tsx | 12 +-- .../StaticDateTimePicker.tsx | 12 +-- .../src/StaticTimePicker/StaticTimePicker.tsx | 12 +-- .../src/TimeField/TimeField.tsx | 8 +- .../src/TimeField/TimeField.types.ts | 16 ++- .../x-date-pickers/src/TimeField/index.ts | 1 + .../src/TimeField/useTimeField.ts | 2 +- .../src/TimePicker/TimePicker.tsx | 10 +- packages/x-date-pickers/src/index.ts | 3 +- .../useDesktopPicker/useDesktopPicker.tsx | 2 +- .../src/internals/hooks/useField/useField.ts | 17 ++-- .../hooks/useField/useField.types.ts | 31 ++---- .../internals/hooks/useField/useFieldState.ts | 5 +- .../hooks/useMobilePicker/useMobilePicker.tsx | 2 +- .../internals/hooks/usePicker/usePicker.ts | 3 +- .../hooks/usePicker/usePickerValue.ts | 29 +++--- .../hooks/usePicker/usePickerValue.types.ts | 24 +---- .../src/internals/hooks/useValidation.ts | 63 ------------ .../x-date-pickers/src/internals/index.ts | 6 -- .../src/internals/utils/fields.ts | 2 +- .../utils/validation/validateDateTime.ts | 31 ------ .../x-date-pickers/src/models/validation.ts | 18 ++++ .../validation/extractValidationProps.ts | 2 +- .../x-date-pickers/src/validation/index.ts | 13 +++ .../src/validation/useValidation.ts | 99 +++++++++++++++++++ .../utils => }/validation/validateDate.ts | 31 +++--- .../src/validation/validateDateTime.ts | 36 +++++++ .../utils => }/validation/validateTime.ts | 22 ++--- scripts/x-date-pickers-pro.exports.json | 20 ++++ scripts/x-date-pickers.exports.json | 14 +++ 110 files changed, 839 insertions(+), 621 deletions(-) create mode 100644 docs/data/date-pickers/experimentation/CustomField.js create mode 100644 docs/data/date-pickers/experimentation/CustomField.tsx create mode 100644 docs/data/date-pickers/experimentation/CustomField.tsx.preview create mode 100644 docs/data/date-pickers/experimentation/experimentation.md create mode 100644 docs/pages/x/react-date-pickers/experimentation.js delete mode 100644 packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/tests/MultiInputDateTimeRangeField.validation.test.tsx delete mode 100644 packages/x-date-pickers-pro/src/MultiInputTimeRangeField/tests/MultiInputTimeRangeField.validation.test.tsx create mode 100644 packages/x-date-pickers-pro/src/validation/index.ts rename packages/x-date-pickers-pro/src/{internals/utils => }/validation/validateDateRange.ts (55%) rename packages/x-date-pickers-pro/src/{internals/utils => }/validation/validateDateTimeRange.ts (55%) rename packages/x-date-pickers-pro/src/{internals/utils => }/validation/validateTimeRange.ts (52%) delete mode 100644 packages/x-date-pickers/src/internals/hooks/useValidation.ts delete mode 100644 packages/x-date-pickers/src/internals/utils/validation/validateDateTime.ts rename packages/x-date-pickers/src/{internals/utils => }/validation/extractValidationProps.ts (97%) create mode 100644 packages/x-date-pickers/src/validation/index.ts create mode 100644 packages/x-date-pickers/src/validation/useValidation.ts rename packages/x-date-pickers/src/{internals/utils => }/validation/validateDate.ts (67%) create mode 100644 packages/x-date-pickers/src/validation/validateDateTime.ts rename packages/x-date-pickers/src/{internals/utils => }/validation/validateTime.ts (73%) diff --git a/docs/data/date-pickers/experimentation/CustomField.js b/docs/data/date-pickers/experimentation/CustomField.js new file mode 100644 index 0000000000000..109be39349565 --- /dev/null +++ b/docs/data/date-pickers/experimentation/CustomField.js @@ -0,0 +1,52 @@ +import * as React from 'react'; +import dayjs from 'dayjs'; +import { DemoContainer } from '@mui/x-date-pickers/internals/demo'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import TextField from '@mui/material/TextField'; + +import { useValidation, validateDate } from '@mui/x-date-pickers/validation'; + +import { splitFieldInternalAndForwardedProps } from '@mui/x-date-pickers/internals'; + +function ReadOnlyField(props) { + const { internalProps, forwardedProps } = splitFieldInternalAndForwardedProps( + 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 b/docs/data/date-pickers/experimentation/CustomField.tsx new file mode 100644 index 0000000000000..d776da5a76e93 --- /dev/null +++ b/docs/data/date-pickers/experimentation/CustomField.tsx @@ -0,0 +1,54 @@ +import * as React from 'react'; +import dayjs, { Dayjs } from 'dayjs'; +import { DemoContainer } from '@mui/x-date-pickers/internals/demo'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import TextField from '@mui/material/TextField'; +import { useValidation, validateDate } from '@mui/x-date-pickers/validation'; +import { + DateFieldInPickerProps, + UseDateFieldProps, +} from '@mui/x-date-pickers/DateField'; +import { splitFieldInternalAndForwardedProps } from '@mui/x-date-pickers/internals'; + +function ReadOnlyField(props: DateFieldInPickerProps) { + const { internalProps, forwardedProps } = splitFieldInternalAndForwardedProps< + typeof props, + keyof UseDateFieldProps + >(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 0000000000000..b489d70b2175b --- /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 0000000000000..d5f906e328682 --- /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 0000000000000..bf599b0bb69ec --- /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/docs/translations/api-docs/date-pickers/date-field/date-field.json b/docs/translations/api-docs/date-pickers/date-field/date-field.json index e023b91557cb4..618744b87c466 100644 --- a/docs/translations/api-docs/date-pickers/date-field/date-field.json +++ b/docs/translations/api-docs/date-pickers/date-field/date-field.json @@ -66,10 +66,10 @@ }, "onClear": { "description": "Callback fired when the clear button is clicked." }, "onError": { - "description": "Callback fired when the error associated to the current value changes.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onSelectedSectionsChange": { diff --git a/docs/translations/api-docs/date-pickers/date-picker/date-picker.json b/docs/translations/api-docs/date-pickers/date-picker/date-picker.json index fa776f7501a50..5555b7f4ffa45 100644 --- a/docs/translations/api-docs/date-pickers/date-picker/date-picker.json +++ b/docs/translations/api-docs/date-pickers/date-picker/date-picker.json @@ -77,10 +77,10 @@ "description": "Callback fired when the popup requests to be closed. Use in controlled mode (see open)." }, "onError": { - "description": "Callback fired when the error associated to the current value changes. If the error has a non-null value, then the TextField will be rendered in error state.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error describing why the current value is not valid.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onMonthChange": { diff --git a/docs/translations/api-docs/date-pickers/date-range-picker/date-range-picker.json b/docs/translations/api-docs/date-pickers/date-range-picker/date-range-picker.json index b847113e3e878..e36497d596783 100644 --- a/docs/translations/api-docs/date-pickers/date-range-picker/date-range-picker.json +++ b/docs/translations/api-docs/date-pickers/date-range-picker/date-range-picker.json @@ -91,10 +91,10 @@ "description": "Callback fired when the popup requests to be closed. Use in controlled mode (see open)." }, "onError": { - "description": "Callback fired when the error associated to the current value changes. If the error has a non-null value, then the TextField will be rendered in error state.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error describing why the current value is not valid.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onMonthChange": { diff --git a/docs/translations/api-docs/date-pickers/date-time-field/date-time-field.json b/docs/translations/api-docs/date-pickers/date-time-field/date-time-field.json index 4e77982370d01..95121dacea2d8 100644 --- a/docs/translations/api-docs/date-pickers/date-time-field/date-time-field.json +++ b/docs/translations/api-docs/date-pickers/date-time-field/date-time-field.json @@ -83,10 +83,10 @@ }, "onClear": { "description": "Callback fired when the clear button is clicked." }, "onError": { - "description": "Callback fired when the error associated to the current value changes.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onSelectedSectionsChange": { diff --git a/docs/translations/api-docs/date-pickers/date-time-picker/date-time-picker.json b/docs/translations/api-docs/date-pickers/date-time-picker/date-time-picker.json index e6bc667be2c41..d9f6a88b0bac8 100644 --- a/docs/translations/api-docs/date-pickers/date-time-picker/date-time-picker.json +++ b/docs/translations/api-docs/date-pickers/date-time-picker/date-time-picker.json @@ -97,10 +97,10 @@ "description": "Callback fired when the popup requests to be closed. Use in controlled mode (see open)." }, "onError": { - "description": "Callback fired when the error associated to the current value changes. If the error has a non-null value, then the TextField will be rendered in error state.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error describing why the current value is not valid.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onMonthChange": { diff --git a/docs/translations/api-docs/date-pickers/date-time-range-picker/date-time-range-picker.json b/docs/translations/api-docs/date-pickers/date-time-range-picker/date-time-range-picker.json index 38ff71b4a0bc2..5c365483d5c30 100644 --- a/docs/translations/api-docs/date-pickers/date-time-range-picker/date-time-range-picker.json +++ b/docs/translations/api-docs/date-pickers/date-time-range-picker/date-time-range-picker.json @@ -108,10 +108,10 @@ "description": "Callback fired when the popup requests to be closed. Use in controlled mode (see open)." }, "onError": { - "description": "Callback fired when the error associated to the current value changes. If the error has a non-null value, then the TextField will be rendered in error state.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error describing why the current value is not valid.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onMonthChange": { diff --git a/docs/translations/api-docs/date-pickers/desktop-date-picker/desktop-date-picker.json b/docs/translations/api-docs/date-pickers/desktop-date-picker/desktop-date-picker.json index bd5ad6552128e..c87e5e21816dc 100644 --- a/docs/translations/api-docs/date-pickers/desktop-date-picker/desktop-date-picker.json +++ b/docs/translations/api-docs/date-pickers/desktop-date-picker/desktop-date-picker.json @@ -74,10 +74,10 @@ "description": "Callback fired when the popup requests to be closed. Use in controlled mode (see open)." }, "onError": { - "description": "Callback fired when the error associated to the current value changes. If the error has a non-null value, then the TextField will be rendered in error state.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error describing why the current value is not valid.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onMonthChange": { diff --git a/docs/translations/api-docs/date-pickers/desktop-date-range-picker/desktop-date-range-picker.json b/docs/translations/api-docs/date-pickers/desktop-date-range-picker/desktop-date-range-picker.json index 280773356bff1..0640005848218 100644 --- a/docs/translations/api-docs/date-pickers/desktop-date-range-picker/desktop-date-range-picker.json +++ b/docs/translations/api-docs/date-pickers/desktop-date-range-picker/desktop-date-range-picker.json @@ -88,10 +88,10 @@ "description": "Callback fired when the popup requests to be closed. Use in controlled mode (see open)." }, "onError": { - "description": "Callback fired when the error associated to the current value changes. If the error has a non-null value, then the TextField will be rendered in error state.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error describing why the current value is not valid.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onMonthChange": { diff --git a/docs/translations/api-docs/date-pickers/desktop-date-time-picker/desktop-date-time-picker.json b/docs/translations/api-docs/date-pickers/desktop-date-time-picker/desktop-date-time-picker.json index 850e02434af95..fd18750143b3c 100644 --- a/docs/translations/api-docs/date-pickers/desktop-date-time-picker/desktop-date-time-picker.json +++ b/docs/translations/api-docs/date-pickers/desktop-date-time-picker/desktop-date-time-picker.json @@ -94,10 +94,10 @@ "description": "Callback fired when the popup requests to be closed. Use in controlled mode (see open)." }, "onError": { - "description": "Callback fired when the error associated to the current value changes. If the error has a non-null value, then the TextField will be rendered in error state.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error describing why the current value is not valid.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onMonthChange": { diff --git a/docs/translations/api-docs/date-pickers/desktop-date-time-range-picker/desktop-date-time-range-picker.json b/docs/translations/api-docs/date-pickers/desktop-date-time-range-picker/desktop-date-time-range-picker.json index 44308fe67c437..e078dbbc15d4d 100644 --- a/docs/translations/api-docs/date-pickers/desktop-date-time-range-picker/desktop-date-time-range-picker.json +++ b/docs/translations/api-docs/date-pickers/desktop-date-time-range-picker/desktop-date-time-range-picker.json @@ -105,10 +105,10 @@ "description": "Callback fired when the popup requests to be closed. Use in controlled mode (see open)." }, "onError": { - "description": "Callback fired when the error associated to the current value changes. If the error has a non-null value, then the TextField will be rendered in error state.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error describing why the current value is not valid.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onMonthChange": { diff --git a/docs/translations/api-docs/date-pickers/desktop-time-picker/desktop-time-picker.json b/docs/translations/api-docs/date-pickers/desktop-time-picker/desktop-time-picker.json index cb08e6dbe9b52..4ddf39b2d428c 100644 --- a/docs/translations/api-docs/date-pickers/desktop-time-picker/desktop-time-picker.json +++ b/docs/translations/api-docs/date-pickers/desktop-time-picker/desktop-time-picker.json @@ -66,10 +66,10 @@ "description": "Callback fired when the popup requests to be closed. Use in controlled mode (see open)." }, "onError": { - "description": "Callback fired when the error associated to the current value changes. If the error has a non-null value, then the TextField will be rendered in error state.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error describing why the current value is not valid.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onOpen": { diff --git a/docs/translations/api-docs/date-pickers/mobile-date-picker/mobile-date-picker.json b/docs/translations/api-docs/date-pickers/mobile-date-picker/mobile-date-picker.json index 6fdc6eaca82e2..79e03e0738980 100644 --- a/docs/translations/api-docs/date-pickers/mobile-date-picker/mobile-date-picker.json +++ b/docs/translations/api-docs/date-pickers/mobile-date-picker/mobile-date-picker.json @@ -74,10 +74,10 @@ "description": "Callback fired when the popup requests to be closed. Use in controlled mode (see open)." }, "onError": { - "description": "Callback fired when the error associated to the current value changes. If the error has a non-null value, then the TextField will be rendered in error state.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error describing why the current value is not valid.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onMonthChange": { diff --git a/docs/translations/api-docs/date-pickers/mobile-date-range-picker/mobile-date-range-picker.json b/docs/translations/api-docs/date-pickers/mobile-date-range-picker/mobile-date-range-picker.json index 4f31ae0b29206..9979387f247d7 100644 --- a/docs/translations/api-docs/date-pickers/mobile-date-range-picker/mobile-date-range-picker.json +++ b/docs/translations/api-docs/date-pickers/mobile-date-range-picker/mobile-date-range-picker.json @@ -85,10 +85,10 @@ "description": "Callback fired when the popup requests to be closed. Use in controlled mode (see open)." }, "onError": { - "description": "Callback fired when the error associated to the current value changes. If the error has a non-null value, then the TextField will be rendered in error state.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error describing why the current value is not valid.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onMonthChange": { diff --git a/docs/translations/api-docs/date-pickers/mobile-date-time-picker/mobile-date-time-picker.json b/docs/translations/api-docs/date-pickers/mobile-date-time-picker/mobile-date-time-picker.json index 3731b8d7d2f73..4d5af55928457 100644 --- a/docs/translations/api-docs/date-pickers/mobile-date-time-picker/mobile-date-time-picker.json +++ b/docs/translations/api-docs/date-pickers/mobile-date-time-picker/mobile-date-time-picker.json @@ -94,10 +94,10 @@ "description": "Callback fired when the popup requests to be closed. Use in controlled mode (see open)." }, "onError": { - "description": "Callback fired when the error associated to the current value changes. If the error has a non-null value, then the TextField will be rendered in error state.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error describing why the current value is not valid.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onMonthChange": { diff --git a/docs/translations/api-docs/date-pickers/mobile-date-time-range-picker/mobile-date-time-range-picker.json b/docs/translations/api-docs/date-pickers/mobile-date-time-range-picker/mobile-date-time-range-picker.json index 743e5f688d34d..c2eb9b111653d 100644 --- a/docs/translations/api-docs/date-pickers/mobile-date-time-range-picker/mobile-date-time-range-picker.json +++ b/docs/translations/api-docs/date-pickers/mobile-date-time-range-picker/mobile-date-time-range-picker.json @@ -102,10 +102,10 @@ "description": "Callback fired when the popup requests to be closed. Use in controlled mode (see open)." }, "onError": { - "description": "Callback fired when the error associated to the current value changes. If the error has a non-null value, then the TextField will be rendered in error state.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error describing why the current value is not valid.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onMonthChange": { diff --git a/docs/translations/api-docs/date-pickers/mobile-time-picker/mobile-time-picker.json b/docs/translations/api-docs/date-pickers/mobile-time-picker/mobile-time-picker.json index cd9f9871246e8..9e4aee022d836 100644 --- a/docs/translations/api-docs/date-pickers/mobile-time-picker/mobile-time-picker.json +++ b/docs/translations/api-docs/date-pickers/mobile-time-picker/mobile-time-picker.json @@ -66,10 +66,10 @@ "description": "Callback fired when the popup requests to be closed. Use in controlled mode (see open)." }, "onError": { - "description": "Callback fired when the error associated to the current value changes. If the error has a non-null value, then the TextField will be rendered in error state.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error describing why the current value is not valid.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onOpen": { diff --git a/docs/translations/api-docs/date-pickers/multi-input-date-range-field/multi-input-date-range-field.json b/docs/translations/api-docs/date-pickers/multi-input-date-range-field/multi-input-date-range-field.json index 3249b81c4e446..5091832ca274a 100644 --- a/docs/translations/api-docs/date-pickers/multi-input-date-range-field/multi-input-date-range-field.json +++ b/docs/translations/api-docs/date-pickers/multi-input-date-range-field/multi-input-date-range-field.json @@ -34,10 +34,10 @@ } }, "onError": { - "description": "Callback fired when the error associated to the current value changes.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onSelectedSectionsChange": { diff --git a/docs/translations/api-docs/date-pickers/multi-input-date-time-range-field/multi-input-date-time-range-field.json b/docs/translations/api-docs/date-pickers/multi-input-date-time-range-field/multi-input-date-time-range-field.json index 5a80d81c50d94..2657b5ed1a3fb 100644 --- a/docs/translations/api-docs/date-pickers/multi-input-date-time-range-field/multi-input-date-time-range-field.json +++ b/docs/translations/api-docs/date-pickers/multi-input-date-time-range-field/multi-input-date-time-range-field.json @@ -51,10 +51,10 @@ } }, "onError": { - "description": "Callback fired when the error associated to the current value changes.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onSelectedSectionsChange": { diff --git a/docs/translations/api-docs/date-pickers/multi-input-time-range-field/multi-input-time-range-field.json b/docs/translations/api-docs/date-pickers/multi-input-time-range-field/multi-input-time-range-field.json index 36121c5af67de..29a60e20be62b 100644 --- a/docs/translations/api-docs/date-pickers/multi-input-time-range-field/multi-input-time-range-field.json +++ b/docs/translations/api-docs/date-pickers/multi-input-time-range-field/multi-input-time-range-field.json @@ -43,10 +43,10 @@ } }, "onError": { - "description": "Callback fired when the error associated to the current value changes.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onSelectedSectionsChange": { diff --git a/docs/translations/api-docs/date-pickers/single-input-date-range-field/single-input-date-range-field.json b/docs/translations/api-docs/date-pickers/single-input-date-range-field/single-input-date-range-field.json index 4b27f2a622210..a24a00361f7d6 100644 --- a/docs/translations/api-docs/date-pickers/single-input-date-range-field/single-input-date-range-field.json +++ b/docs/translations/api-docs/date-pickers/single-input-date-range-field/single-input-date-range-field.json @@ -67,10 +67,10 @@ }, "onClear": { "description": "Callback fired when the clear button is clicked." }, "onError": { - "description": "Callback fired when the error associated to the current value changes.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onSelectedSectionsChange": { diff --git a/docs/translations/api-docs/date-pickers/single-input-date-time-range-field/single-input-date-time-range-field.json b/docs/translations/api-docs/date-pickers/single-input-date-time-range-field/single-input-date-time-range-field.json index 2fd5f39606135..e98b84401aca0 100644 --- a/docs/translations/api-docs/date-pickers/single-input-date-time-range-field/single-input-date-time-range-field.json +++ b/docs/translations/api-docs/date-pickers/single-input-date-time-range-field/single-input-date-time-range-field.json @@ -84,10 +84,10 @@ }, "onClear": { "description": "Callback fired when the clear button is clicked." }, "onError": { - "description": "Callback fired when the error associated to the current value changes.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onSelectedSectionsChange": { diff --git a/docs/translations/api-docs/date-pickers/single-input-time-range-field/single-input-time-range-field.json b/docs/translations/api-docs/date-pickers/single-input-time-range-field/single-input-time-range-field.json index d14d95e0e73be..7814309458a6f 100644 --- a/docs/translations/api-docs/date-pickers/single-input-time-range-field/single-input-time-range-field.json +++ b/docs/translations/api-docs/date-pickers/single-input-time-range-field/single-input-time-range-field.json @@ -76,10 +76,10 @@ }, "onClear": { "description": "Callback fired when the clear button is clicked." }, "onError": { - "description": "Callback fired when the error associated to the current value changes.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onSelectedSectionsChange": { diff --git a/docs/translations/api-docs/date-pickers/static-date-picker/static-date-picker.json b/docs/translations/api-docs/date-pickers/static-date-picker/static-date-picker.json index 7ebb4803d895d..289d1ade78f4e 100644 --- a/docs/translations/api-docs/date-pickers/static-date-picker/static-date-picker.json +++ b/docs/translations/api-docs/date-pickers/static-date-picker/static-date-picker.json @@ -60,10 +60,10 @@ "description": "Callback fired when component requests to be closed. Can be fired when selecting (by default on desktop mode) or clearing a value." }, "onError": { - "description": "Callback fired when the error associated to the current value changes. If the error has a non-null value, then the TextField will be rendered in error state.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error describing why the current value is not valid.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onMonthChange": { diff --git a/docs/translations/api-docs/date-pickers/static-date-range-picker/static-date-range-picker.json b/docs/translations/api-docs/date-pickers/static-date-range-picker/static-date-range-picker.json index a5948b80f3836..902cddeda7033 100644 --- a/docs/translations/api-docs/date-pickers/static-date-range-picker/static-date-range-picker.json +++ b/docs/translations/api-docs/date-pickers/static-date-range-picker/static-date-range-picker.json @@ -70,10 +70,10 @@ "description": "Callback fired when component requests to be closed. Can be fired when selecting (by default on desktop mode) or clearing a value." }, "onError": { - "description": "Callback fired when the error associated to the current value changes. If the error has a non-null value, then the TextField will be rendered in error state.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error describing why the current value is not valid.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onMonthChange": { diff --git a/docs/translations/api-docs/date-pickers/static-date-time-picker/static-date-time-picker.json b/docs/translations/api-docs/date-pickers/static-date-time-picker/static-date-time-picker.json index 49348e6733294..f249adf4217e2 100644 --- a/docs/translations/api-docs/date-pickers/static-date-time-picker/static-date-time-picker.json +++ b/docs/translations/api-docs/date-pickers/static-date-time-picker/static-date-time-picker.json @@ -80,10 +80,10 @@ "description": "Callback fired when component requests to be closed. Can be fired when selecting (by default on desktop mode) or clearing a value." }, "onError": { - "description": "Callback fired when the error associated to the current value changes. If the error has a non-null value, then the TextField will be rendered in error state.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error describing why the current value is not valid.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onMonthChange": { diff --git a/docs/translations/api-docs/date-pickers/static-time-picker/static-time-picker.json b/docs/translations/api-docs/date-pickers/static-time-picker/static-time-picker.json index 8ba5b454d5e6b..df19b36acd95c 100644 --- a/docs/translations/api-docs/date-pickers/static-time-picker/static-time-picker.json +++ b/docs/translations/api-docs/date-pickers/static-time-picker/static-time-picker.json @@ -52,10 +52,10 @@ "description": "Callback fired when component requests to be closed. Can be fired when selecting (by default on desktop mode) or clearing a value." }, "onError": { - "description": "Callback fired when the error associated to the current value changes. If the error has a non-null value, then the TextField will be rendered in error state.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error describing why the current value is not valid.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onViewChange": { diff --git a/docs/translations/api-docs/date-pickers/time-field/time-field.json b/docs/translations/api-docs/date-pickers/time-field/time-field.json index afe1273696503..e54b304b64b5a 100644 --- a/docs/translations/api-docs/date-pickers/time-field/time-field.json +++ b/docs/translations/api-docs/date-pickers/time-field/time-field.json @@ -75,10 +75,10 @@ }, "onClear": { "description": "Callback fired when the clear button is clicked." }, "onError": { - "description": "Callback fired when the error associated to the current value changes.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onSelectedSectionsChange": { diff --git a/docs/translations/api-docs/date-pickers/time-picker/time-picker.json b/docs/translations/api-docs/date-pickers/time-picker/time-picker.json index 3e18c58879a8d..17d4ee6c9f6ac 100644 --- a/docs/translations/api-docs/date-pickers/time-picker/time-picker.json +++ b/docs/translations/api-docs/date-pickers/time-picker/time-picker.json @@ -69,10 +69,10 @@ "description": "Callback fired when the popup requests to be closed. Use in controlled mode (see open)." }, "onError": { - "description": "Callback fired when the error associated to the current value changes. If the error has a non-null value, then the TextField will be rendered in error state.", + "description": "Callback fired when the error associated to 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.", "typeDescriptions": { - "error": "The new error describing why the current value is not valid.", - "value": "The value associated to the error." + "error": "The reason why the current value is not valid.", + "value": "The value associated to the error" } }, "onOpen": { diff --git a/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.tsx b/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.tsx index 7fa21dbb2ee85..8002fc939798d 100644 --- a/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.tsx +++ b/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.tsx @@ -217,12 +217,12 @@ DateRangePicker.propTypes = { onClose: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * If the error has a non-null value, then the `TextField` will be rendered in `error` state. - * - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error describing why the current value is not valid. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, /** diff --git a/packages/x-date-pickers-pro/src/DateTimeRangePicker/DateTimeRangePicker.tsx b/packages/x-date-pickers-pro/src/DateTimeRangePicker/DateTimeRangePicker.tsx index 4d94546246f86..9249006814e55 100644 --- a/packages/x-date-pickers-pro/src/DateTimeRangePicker/DateTimeRangePicker.tsx +++ b/packages/x-date-pickers-pro/src/DateTimeRangePicker/DateTimeRangePicker.tsx @@ -250,12 +250,12 @@ DateTimeRangePicker.propTypes = { onClose: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * If the error has a non-null value, then the `TextField` will be rendered in `error` state. - * - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error describing why the current value is not valid. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, /** diff --git a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.tsx b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.tsx index 1a583b12807bb..025c7c1ca7865 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 = (< @@ -252,12 +253,12 @@ DesktopDateRangePicker.propTypes = { onClose: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * If the error has a non-null value, then the `TextField` will be rendered in `error` state. - * - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error describing why the current value is not valid. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, /** diff --git a/packages/x-date-pickers-pro/src/DesktopDateTimeRangePicker/DesktopDateTimeRangePicker.tsx b/packages/x-date-pickers-pro/src/DesktopDateTimeRangePicker/DesktopDateTimeRangePicker.tsx index 069e4d66cc750..89e375c96dab0 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 { @@ -418,12 +418,12 @@ DesktopDateTimeRangePicker.propTypes = { onClose: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * If the error has a non-null value, then the `TextField` will be rendered in `error` state. - * - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error describing why the current value is not valid. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, /** diff --git a/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.tsx b/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.tsx index 54558b1607ea9..c3605de2d5ace 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 = (< @@ -248,12 +249,12 @@ MobileDateRangePicker.propTypes = { onClose: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * If the error has a non-null value, then the `TextField` will be rendered in `error` state. - * - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error describing why the current value is not valid. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, /** diff --git a/packages/x-date-pickers-pro/src/MobileDateTimeRangePicker/MobileDateTimeRangePicker.tsx b/packages/x-date-pickers-pro/src/MobileDateTimeRangePicker/MobileDateTimeRangePicker.tsx index 27036c430d8e7..cdbabfe04eeae 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 { @@ -408,12 +408,12 @@ MobileDateTimeRangePicker.propTypes = { onClose: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * If the error has a non-null value, then the `TextField` will be rendered in `error` state. - * - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error describing why the current value is not valid. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, /** diff --git a/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx b/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx index 909fc6c29e7c0..4fb1f46224280 100644 --- a/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx +++ b/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx @@ -270,10 +270,12 @@ MultiInputDateRangeField.propTypes = { onChange: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, /** diff --git a/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx b/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx index 133685debe583..95eee8c613eb6 100644 --- a/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx +++ b/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx @@ -302,10 +302,12 @@ MultiInputDateTimeRangeField.propTypes = { onChange: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, /** diff --git a/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/tests/MultiInputDateTimeRangeField.validation.test.tsx b/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/tests/MultiInputDateTimeRangeField.validation.test.tsx deleted file mode 100644 index 4fd962807ba7f..0000000000000 --- a/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/tests/MultiInputDateTimeRangeField.validation.test.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { MultiInputDateTimeRangeField } from '@mui/x-date-pickers-pro/MultiInputDateTimeRangeField'; -import { - createPickerRenderer, - adapterToUse, - describeRangeValidation, - setValueOnFieldInput, -} from 'test/utils/pickers'; - -describe('', () => { - const { render, clock } = createPickerRenderer({ clock: 'fake' }); - - describeRangeValidation(MultiInputDateTimeRangeField, () => ({ - render, - clock, - componentFamily: 'field', - views: ['year', 'month', 'day', 'hours', 'minutes'], - setValue: (value, { setEndDate } = {}) => { - setValueOnFieldInput(adapterToUse.format(value, 'keyboardDateTime'), setEndDate ? 1 : 0); - }, - })); -}); diff --git a/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/tests/describes.MultiInputDateTimeRangeField.test.tsx b/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/tests/describes.MultiInputDateTimeRangeField.test.tsx index 7bb1d6cc437b2..5bc464f729e50 100644 --- a/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/tests/describes.MultiInputDateTimeRangeField.test.tsx +++ b/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/tests/describes.MultiInputDateTimeRangeField.test.tsx @@ -1,10 +1,15 @@ import * as React from 'react'; import { MultiInputDateTimeRangeField } from '@mui/x-date-pickers-pro/MultiInputDateTimeRangeField'; -import { createPickerRenderer } from 'test/utils/pickers'; +import { + adapterToUse, + createPickerRenderer, + describeRangeValidation, + setValueOnFieldInput, +} from 'test/utils/pickers'; import { describeConformance } from 'test/utils/describeConformance'; describe('', () => { - const { render } = createPickerRenderer(); + const { render, clock } = createPickerRenderer(); describeConformance(, () => ({ classes: {} as any, @@ -14,4 +19,14 @@ describe('', () => { refInstanceof: window.HTMLDivElement, skip: ['componentProp', 'componentsProp', 'themeVariants'], })); + + describeRangeValidation(MultiInputDateTimeRangeField, () => ({ + render, + clock, + componentFamily: 'field', + views: ['year', 'month', 'day', 'hours', 'minutes'], + setValue: (value, { setEndDate } = {}) => { + setValueOnFieldInput(adapterToUse.format(value, 'keyboardDateTime'), setEndDate ? 1 : 0); + }, + })); }); diff --git a/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.tsx b/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.tsx index ee04099dff91d..a682b755b87b9 100644 --- a/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.tsx +++ b/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.tsx @@ -287,10 +287,12 @@ MultiInputTimeRangeField.propTypes = { onChange: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, /** diff --git a/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/tests/MultiInputTimeRangeField.validation.test.tsx b/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/tests/MultiInputTimeRangeField.validation.test.tsx deleted file mode 100644 index a84da9d27b0a9..0000000000000 --- a/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/tests/MultiInputTimeRangeField.validation.test.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { MultiInputTimeRangeField } from '@mui/x-date-pickers-pro/MultiInputTimeRangeField'; -import { - createPickerRenderer, - adapterToUse, - describeRangeValidation, - setValueOnFieldInput, -} from 'test/utils/pickers'; - -describe('', () => { - const { render, clock } = createPickerRenderer({ clock: 'fake' }); - - describeRangeValidation(MultiInputTimeRangeField, () => ({ - render, - clock, - componentFamily: 'field', - views: ['hours', 'minutes'], - setValue: (value, { setEndDate } = {}) => { - setValueOnFieldInput(adapterToUse.format(value, 'fullTime'), setEndDate ? 1 : 0); - }, - })); -}); diff --git a/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/tests/describes.MultiInputTimeRangeField.test.tsx b/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/tests/describes.MultiInputTimeRangeField.test.tsx index 47047c3007d4c..f25cd1cd33f44 100644 --- a/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/tests/describes.MultiInputTimeRangeField.test.tsx +++ b/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/tests/describes.MultiInputTimeRangeField.test.tsx @@ -1,10 +1,15 @@ import * as React from 'react'; import { MultiInputTimeRangeField } from '@mui/x-date-pickers-pro/MultiInputTimeRangeField'; -import { createPickerRenderer } from 'test/utils/pickers'; +import { + adapterToUse, + createPickerRenderer, + describeRangeValidation, + setValueOnFieldInput, +} from 'test/utils/pickers'; import { describeConformance } from 'test/utils/describeConformance'; describe('', () => { - const { render } = createPickerRenderer(); + const { render, clock } = createPickerRenderer(); describeConformance(, () => ({ classes: {} as any, @@ -14,4 +19,14 @@ describe('', () => { refInstanceof: window.HTMLDivElement, skip: ['themeVariants', 'componentProp', 'componentsProp'], })); + + describeRangeValidation(MultiInputTimeRangeField, () => ({ + render, + clock, + componentFamily: 'field', + views: ['hours', 'minutes'], + setValue: (value, { setEndDate } = {}) => { + setValueOnFieldInput(adapterToUse.format(value, 'fullTime'), setEndDate ? 1 : 0); + }, + })); }); diff --git a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.tsx b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.tsx index edc1e5b91b690..88e25ad3ff8e6 100644 --- a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.tsx +++ b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.tsx @@ -230,10 +230,12 @@ SingleInputDateRangeField.propTypes = { onClear: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, onFocus: PropTypes.func, diff --git a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/useSingleInputDateRangeField.ts b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/useSingleInputDateRangeField.ts index acd9177c5fa67..6b49a74f5bdd6 100644 --- a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/useSingleInputDateRangeField.ts +++ b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/useSingleInputDateRangeField.ts @@ -7,7 +7,7 @@ import { 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/SingleInputDateTimeRangeField.tsx b/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.tsx index 57d0a57f3a91f..aded6d5ac2052 100644 --- a/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.tsx +++ b/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.tsx @@ -263,10 +263,12 @@ SingleInputDateTimeRangeField.propTypes = { onClear: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, onFocus: PropTypes.func, diff --git a/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/useSingleInputDateTimeRangeField.ts b/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/useSingleInputDateTimeRangeField.ts index c27ab9b259b30..1fe2b4100a06e 100644 --- a/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/useSingleInputDateTimeRangeField.ts +++ b/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/useSingleInputDateTimeRangeField.ts @@ -7,7 +7,7 @@ import { 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/SingleInputTimeRangeField.tsx b/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.tsx index cd37622a001e2..414b36c21cf21 100644 --- a/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.tsx +++ b/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.tsx @@ -245,10 +245,12 @@ SingleInputTimeRangeField.propTypes = { onClear: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, onFocus: PropTypes.func, diff --git a/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/useSingleInputTimeRangeField.ts b/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/useSingleInputTimeRangeField.ts index 98d9703e7eb85..c245dec8acb5e 100644 --- a/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/useSingleInputTimeRangeField.ts +++ b/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/useSingleInputTimeRangeField.ts @@ -7,7 +7,7 @@ import { 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 74ff932d922db..163cfddca378b 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 = (( @@ -197,12 +197,12 @@ StaticDateRangePicker.propTypes = { onClose: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * If the error has a non-null value, then the `TextField` will be rendered in `error` state. - * - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error describing why the current value is not valid. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, /** diff --git a/packages/x-date-pickers-pro/src/index.ts b/packages/x-date-pickers-pro/src/index.ts index 55114fb14fb87..8a7bb7e61c04b 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 cb635fdba2ca8..55d56d9b6ca58 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 8c0ff74043266..518914bdb1b59 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 84dfaeeb86312..f89396ebc68bf 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 cecc16b5cc6eb..60efbe608a10a 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 a27dcb41a897f..86db843894f5a 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 0000000000000..9b7737a17ed25 --- /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 bb138c82f438b..125449ca9a6fc 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 2eae3041a2e4a..d86d65e8d3b33 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 15b4a2bc2cd66..483523a4b4994 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 7fbde8e847724..2c677f6f7653d 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.tsx b/packages/x-date-pickers/src/DateField/DateField.tsx index 5f707a99d31ab..9a8b2f4d5bb15 100644 --- a/packages/x-date-pickers/src/DateField/DateField.tsx +++ b/packages/x-date-pickers/src/DateField/DateField.tsx @@ -222,10 +222,12 @@ DateField.propTypes = { onClear: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, onFocus: PropTypes.func, diff --git a/packages/x-date-pickers/src/DateField/DateField.types.ts b/packages/x-date-pickers/src/DateField/DateField.types.ts index cf1bdfcc800ac..57ab1e1830aec 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 cd6119e98a255..e5f8fe21dcb29 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 a6ea87980fdd4..0ac1bb8c6954a 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 { splitFieldInternalAndForwardedProps } from '../internals/utils/fields'; import { FieldSection, PickerValidDate } from '../models'; import { useDefaultizedDateField } from '../internals/hooks/defaultizedFieldProps'; diff --git a/packages/x-date-pickers/src/DatePicker/DatePicker.tsx b/packages/x-date-pickers/src/DatePicker/DatePicker.tsx index e93c88833ba49..15836ef4d4c1d 100644 --- a/packages/x-date-pickers/src/DatePicker/DatePicker.tsx +++ b/packages/x-date-pickers/src/DatePicker/DatePicker.tsx @@ -194,12 +194,12 @@ DatePicker.propTypes = { onClose: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * If the error has a non-null value, then the `TextField` will be rendered in `error` state. - * - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error describing why the current value is not valid. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, /** diff --git a/packages/x-date-pickers/src/DateTimeField/DateTimeField.tsx b/packages/x-date-pickers/src/DateTimeField/DateTimeField.tsx index 44db7bb3fb500..1b56a21d24e3b 100644 --- a/packages/x-date-pickers/src/DateTimeField/DateTimeField.tsx +++ b/packages/x-date-pickers/src/DateTimeField/DateTimeField.tsx @@ -255,10 +255,12 @@ DateTimeField.propTypes = { onClear: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, onFocus: PropTypes.func, diff --git a/packages/x-date-pickers/src/DateTimeField/DateTimeField.types.ts b/packages/x-date-pickers/src/DateTimeField/DateTimeField.types.ts index 625f70532e552..e19e41a6345ad 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 95952dde94748..212ce42432f5a 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 b92124fe93731..86a4f826fc09d 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 { splitFieldInternalAndForwardedProps } from '../internals/utils/fields'; import { FieldSection, PickerValidDate } from '../models'; import { useDefaultizedDateTimeField } from '../internals/hooks/defaultizedFieldProps'; diff --git a/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.tsx b/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.tsx index a8126e9c5474c..6008984bf9b9c 100644 --- a/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.tsx @@ -232,12 +232,12 @@ DateTimePicker.propTypes = { onClose: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * If the error has a non-null value, then the `TextField` will be rendered in `error` state. - * - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error describing why the current value is not valid. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, /** diff --git a/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.tsx b/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.tsx index 5e0c6e75ae8a4..df305d499291f 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 { 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 { extractValidationProps, validateDate } from '../validation'; import { renderDateViewCalendar } from '../dateViewRenderers'; import { resolveDateFormat } from '../internals/utils/date-utils'; @@ -239,12 +238,12 @@ DesktopDatePicker.propTypes = { onClose: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * If the error has a non-null value, then the `TextField` will be rendered in `error` state. - * - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error describing why the current value is not valid. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, /** diff --git a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx index f728453aa5ae6..cdc5ab894cb57 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 { DateOrTimeViewWithMeridiem } from '../internals/models'; import { CalendarIcon } from '../icons'; import { UseDesktopPickerProps, useDesktopPicker } from '../internals/hooks/useDesktopPicker'; -import { extractValidationProps } from '../internals/utils/validation/extractValidationProps'; +import { extractValidationProps, validateDateTime } from '../validation'; import { PickerViewsRendererProps } from '../internals/hooks/usePicker'; import { resolveDateTimeFormat, @@ -413,12 +412,12 @@ DesktopDateTimePicker.propTypes = { onClose: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * If the error has a non-null value, then the `TextField` will be rendered in `error` state. - * - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error describing why the current value is not valid. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, /** diff --git a/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.tsx b/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.tsx index 31232c0cfb7e8..aecc9706b9df6 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 { ClockIcon } from '../icons'; import { useDesktopPicker } from '../internals/hooks/useDesktopPicker'; -import { extractValidationProps } from '../internals/utils/validation/extractValidationProps'; +import { extractValidationProps, validateTime } from '../validation'; import { renderDigitalClockTimeView, renderMultiSectionDigitalClockTimeView, @@ -265,12 +264,12 @@ DesktopTimePicker.propTypes = { onClose: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * If the error has a non-null value, then the `TextField` will be rendered in `error` state. - * - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error describing why the current value is not valid. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, /** diff --git a/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.tsx b/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.tsx index 5f33b9a15dcfd..bfe87f5b6f3cc 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 { DateView, PickerValidDate } from '../models'; import { DateField } from '../DateField'; -import { extractValidationProps } from '../internals/utils/validation/extractValidationProps'; +import { extractValidationProps, validateDate } from '../validation'; import { singleItemValueManager } from '../internals/utils/valueManagers'; import { renderDateViewCalendar } from '../dateViewRenderers'; import { resolveDateFormat } from '../internals/utils/date-utils'; @@ -236,12 +235,12 @@ MobileDatePicker.propTypes = { onClose: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * If the error has a non-null value, then the `TextField` will be rendered in `error` state. - * - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error describing why the current value is not valid. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, /** diff --git a/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx b/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx index 50ab45e2b37f9..24ede5a090c77 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 { DateOrTimeView, PickerValidDate } from '../models'; import { useMobilePicker } from '../internals/hooks/useMobilePicker'; -import { extractValidationProps } from '../internals/utils/validation/extractValidationProps'; +import { extractValidationProps, validateDateTime } from '../validation'; import { renderDateViewCalendar } from '../dateViewRenderers'; import { renderTimeViewClock } from '../timeViewRenderers'; import { resolveDateTimeFormat } from '../internals/utils/date-time-utils'; @@ -289,12 +288,12 @@ MobileDateTimePicker.propTypes = { onClose: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * If the error has a non-null value, then the `TextField` will be rendered in `error` state. - * - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error describing why the current value is not valid. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, /** diff --git a/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.tsx b/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.tsx index 021ec100c6809..d0c69b038c5b0 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 { PickerValidDate, TimeView } from '../models'; import { useMobilePicker } from '../internals/hooks/useMobilePicker'; -import { extractValidationProps } from '../internals/utils/validation/extractValidationProps'; +import { extractValidationProps, validateTime } from '../validation'; import { renderTimeViewClock } from '../timeViewRenderers'; import { resolveTimeFormat } from '../internals/utils/time-utils'; @@ -228,12 +227,12 @@ MobileTimePicker.propTypes = { onClose: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * If the error has a non-null value, then the `TextField` will be rendered in `error` state. - * - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error describing why the current value is not valid. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, /** diff --git a/packages/x-date-pickers/src/StaticDatePicker/StaticDatePicker.tsx b/packages/x-date-pickers/src/StaticDatePicker/StaticDatePicker.tsx index 7482205b4b959..a17c01f09a799 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'; @@ -175,12 +175,12 @@ StaticDatePicker.propTypes = { onClose: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * If the error has a non-null value, then the `TextField` will be rendered in `error` state. - * - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error describing why the current value is not valid. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, /** diff --git a/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.tsx b/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.tsx index 01dd868201d14..50cab5e658830 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, @@ -227,12 +227,12 @@ StaticDateTimePicker.propTypes = { onClose: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * If the error has a non-null value, then the `TextField` will be rendered in `error` state. - * - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error describing why the current value is not valid. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, /** diff --git a/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.tsx b/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.tsx index b3b2b27047a03..14a4a493badd3 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, @@ -166,12 +166,12 @@ StaticTimePicker.propTypes = { onClose: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * If the error has a non-null value, then the `TextField` will be rendered in `error` state. - * - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error describing why the current value is not valid. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, /** diff --git a/packages/x-date-pickers/src/TimeField/TimeField.tsx b/packages/x-date-pickers/src/TimeField/TimeField.tsx index 10d4918133ed3..e11d67c90237a 100644 --- a/packages/x-date-pickers/src/TimeField/TimeField.tsx +++ b/packages/x-date-pickers/src/TimeField/TimeField.tsx @@ -237,10 +237,12 @@ TimeField.propTypes = { onClear: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, onFocus: PropTypes.func, diff --git a/packages/x-date-pickers/src/TimeField/TimeField.types.ts b/packages/x-date-pickers/src/TimeField/TimeField.types.ts index 478cf08ee9ee2..0ce1a53d6e40f 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 f335f0f8fd769..cf9c1e6a58813 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 bd81134e599f9..691ce2b5741f6 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 { splitFieldInternalAndForwardedProps } from '../internals/utils/fields'; import { PickerValidDate, FieldSection } from '../models'; import { useDefaultizedTimeField } from '../internals/hooks/defaultizedFieldProps'; diff --git a/packages/x-date-pickers/src/TimePicker/TimePicker.tsx b/packages/x-date-pickers/src/TimePicker/TimePicker.tsx index 2883b0d1d45bc..ffdf2a0602577 100644 --- a/packages/x-date-pickers/src/TimePicker/TimePicker.tsx +++ b/packages/x-date-pickers/src/TimePicker/TimePicker.tsx @@ -182,12 +182,12 @@ TimePicker.propTypes = { onClose: PropTypes.func, /** * Callback fired when the error associated to the current value changes. - * If the error has a non-null value, then the `TextField` will be rendered in `error` state. - * - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. + * 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error describing why the current value is not valid. - * @param {TValue} value The value associated to the error. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to the error */ onError: PropTypes.func, /** diff --git a/packages/x-date-pickers/src/index.ts b/packages/x-date-pickers/src/index.ts index cd2e4e9586e5b..f0987002adb1a 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 56e943302f79f..0e9097ef1eaeb 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 { useUtils } from '../useUtils'; 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 1d8bc5fdcffb3..2ad8363212a92 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 df0915e71b7f1..2d4d0c292e12a 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,14 +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 to the current value changes. - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. - * @template TError The validation error type. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error. - * @param {TValue} value The value associated to the error. - */ - onError?: (error: TError, value: TValue) => void; /** * Format of the date when rendered in the input(s). */ @@ -396,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 23e6bbc669000..846ab4be676da 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 b98cf417300b3..69f3f1d2644fb 100644 --- a/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.tsx +++ b/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.tsx @@ -13,12 +13,12 @@ import { onSpaceOrEnter } from '../../utils/utils'; import { useUtils } from '../useUtils'; 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 b150b8fe3dee3..cb5b46ae746de 100644 --- a/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.ts +++ b/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.ts @@ -2,9 +2,8 @@ import { UsePickerParams, UsePickerProps, UsePickerResponse } from './usePicker. import { usePickerValue } from './usePickerValue'; import { usePickerViews } from './usePickerViews'; import { usePickerLayoutProps } from './usePickerLayoutProps'; -import { InferError } from '../useValidation'; import { warnOnce } from '../../utils/warning'; -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 3bd7a5eb08a23..8d6255b5f5627 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 9372640881c4e..94c43c8c3c1b6 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 to the current value changes. - * If the error has a non-null value, then the `TextField` will be rendered in `error` state. - * - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. - * @template TError The validation error type. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} error The new error describing why the current value is not valid. - * @param {TValue} value The value associated to 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 406dcf61d0db8..0000000000000 --- 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 that fired when input value or new `value` prop validation returns **new** validation error (or value is valid after error). - * In case of validation error detected `reason` prop return non-null value and `TextField` must be displayed in `error` state. - * This can be used to render appropriate form error. - * @template TError The validation error type. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. - * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. - * @param {TError} reason The reason why the current value is not valid. - * @param {TValue} value The invalid value. - */ - onError?: (reason: 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 bf9c07dd4c1fe..af32e690905cf 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'; @@ -146,10 +144,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 { warnOnce } from './utils/warning'; diff --git a/packages/x-date-pickers/src/internals/utils/fields.ts b/packages/x-date-pickers/src/internals/utils/fields.ts index ef5e19e58f089..93ed036d8548c 100644 --- a/packages/x-date-pickers/src/internals/utils/fields.ts +++ b/packages/x-date-pickers/src/internals/utils/fields.ts @@ -3,7 +3,7 @@ import { DATE_TIME_VALIDATION_PROP_NAMES, DATE_VALIDATION_PROP_NAMES, TIME_VALIDATION_PROP_NAMES, -} from './validation/extractValidationProps'; +} from '../../validation/extractValidationProps'; const SHARED_FIELD_INTERNAL_PROP_NAMES = [ 'value', 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 ff14f93ac1eb4..0000000000000 --- 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 397ea4334d03a..6d25a672e0f63 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 to 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. Will be either `string` or a `null`. Can be in `[start, end]` format in case of range value. + * @template TValue The value type. Will be either the same type as `value` or `null`. 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 to 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 a2d90cde15101..3b2bd180137e2 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 0000000000000..b3f964c143f8c --- /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 0000000000000..284e36d4860d3 --- /dev/null +++ b/packages/x-date-pickers/src/validation/useValidation.ts @@ -0,0 +1,99 @@ +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`. + */ + 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; +} + +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 363b13a96f70c..e3bf707969aae 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 0000000000000..1e48246355326 --- /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 c3d109e7ec0f9..2124913a3c14d 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 809ccdd38ed4d..f50b211610e93 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" }, @@ -366,6 +371,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" }, @@ -419,6 +425,20 @@ { "name": "UseSingleInputTimeRangeFieldProps", "kind": "Interface" }, { "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 f816e70b63e67..a3e10847b48a2 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" }, @@ -265,6 +270,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" }, @@ -301,6 +307,14 @@ { "name": "usePickersTranslations", "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" },