diff --git a/docs/data/date-pickers/base-concepts/base-concepts.md b/docs/data/date-pickers/base-concepts/base-concepts.md
index ca61ae0cbe0f0..37ee4a4c2c584 100644
--- a/docs/data/date-pickers/base-concepts/base-concepts.md
+++ b/docs/data/date-pickers/base-concepts/base-concepts.md
@@ -113,10 +113,10 @@ Each _Picker_ is available in a responsive, desktop and mobile variant:
- The responsive component (for example `DatePicker`) which renders the desktop component or the mobile one depending on the device it runs on.
- The desktop component (for example `DesktopDatePicker`) which works best for mouse devices and large screens.
- It renders the views inside a popover and allows editing values directly inside the field.
+ It renders the views inside a popover and a field for keyboard editing.
- The mobile component (for example `MobileDatePicker`) which works best for touch devices and small screens.
- It renders the view inside a modal and does not allow editing values directly inside the field.
+ It renders the view inside a modal and a field for keyboard editing.
{{"demo": "ResponsivePickers.js"}}
diff --git a/docs/data/date-pickers/calendar-systems/AdapterHijri.js b/docs/data/date-pickers/calendar-systems/AdapterHijri.js
index 3260e904fba9b..fb8974424621a 100644
--- a/docs/data/date-pickers/calendar-systems/AdapterHijri.js
+++ b/docs/data/date-pickers/calendar-systems/AdapterHijri.js
@@ -24,16 +24,7 @@ const cacheRtl = createCache({
function ButtonDateTimeField(props) {
const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');
- const {
- InputProps,
- slotProps,
- slots,
- ownerState,
- label,
- focused,
- name,
- ...other
- } = forwardedProps;
+ const { ownerState, label, focused, name, ...other } = forwardedProps;
const pickerContext = usePickerContext();
const parsedFormat = useParsedFormat();
@@ -54,7 +45,7 @@ function ButtonDateTimeField(props) {
{...other}
variant="outlined"
color={hasValidationError ? 'error' : 'primary'}
- ref={InputProps?.ref}
+ ref={pickerContext.triggerRef}
onClick={() => pickerContext.setOpen((prev) => !prev)}
>
{label ? `${label}: ${valueStr}` : valueStr}
diff --git a/docs/data/date-pickers/calendar-systems/AdapterHijri.tsx b/docs/data/date-pickers/calendar-systems/AdapterHijri.tsx
index 39689b23af0fc..fc6da7e2e11bb 100644
--- a/docs/data/date-pickers/calendar-systems/AdapterHijri.tsx
+++ b/docs/data/date-pickers/calendar-systems/AdapterHijri.tsx
@@ -28,16 +28,7 @@ const cacheRtl = createCache({
function ButtonDateTimeField(props: DateTimePickerFieldProps) {
const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');
- const {
- InputProps,
- slotProps,
- slots,
- ownerState,
- label,
- focused,
- name,
- ...other
- } = forwardedProps;
+ const { ownerState, label, focused, name, ...other } = forwardedProps;
const pickerContext = usePickerContext();
const parsedFormat = useParsedFormat();
@@ -58,7 +49,7 @@ function ButtonDateTimeField(props: DateTimePickerFieldProps) {
{...other}
variant="outlined"
color={hasValidationError ? 'error' : 'primary'}
- ref={InputProps?.ref}
+ ref={pickerContext.triggerRef}
onClick={() => pickerContext.setOpen((prev) => !prev)}
>
{label ? `${label}: ${valueStr}` : valueStr}
diff --git a/docs/data/date-pickers/custom-field/BrowserV7Field.js b/docs/data/date-pickers/custom-field/BrowserV7Field.js
index 11f5b168ae3fc..f4a7c867b747a 100644
--- a/docs/data/date-pickers/custom-field/BrowserV7Field.js
+++ b/docs/data/date-pickers/custom-field/BrowserV7Field.js
@@ -1,11 +1,14 @@
import * as React from 'react';
import useForkRef from '@mui/utils/useForkRef';
import { styled } from '@mui/material/styles';
+import IconButton from '@mui/material/IconButton';
+import { CalendarIcon } from '@mui/x-date-pickers/icons';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { unstable_useDateField as useDateField } from '@mui/x-date-pickers/DateField';
import { Unstable_PickersSectionList as PickersSectionList } from '@mui/x-date-pickers/PickersSectionList';
+import { usePickerContext } from '@mui/x-date-pickers/hooks';
const BrowserFieldRoot = styled('div', { name: 'BrowserField', slot: 'Root' })({
display: 'flex',
@@ -41,6 +44,8 @@ const BrowserDateField = React.forwardRef((props, ref) => {
onInput,
onPaste,
onKeyDown,
+ // Should be passed to the button that opens the picker
+ openPickerAriaLabel,
// Can be passed to a hidden element
onChange,
value,
@@ -55,16 +60,15 @@ const BrowserDateField = React.forwardRef((props, ref) => {
readOnly,
focused,
error,
- InputProps: { ref: InputPropsRef, startAdornment, endAdornment } = {},
// The rest can be passed to the root element
...other
} = fieldResponse;
- const handleRef = useForkRef(InputPropsRef, ref);
+ const pickerContext = usePickerContext();
+ const handleRef = useForkRef(pickerContext.triggerRef, ref);
return (
- {startAdornment}
{
onKeyDown={onKeyDown}
/>
- {endAdornment}
+ pickerContext.setOpen((prev) => !prev)}
+ sx={{ marginLeft: 1.5 }}
+ aria-label={openPickerAriaLabel}
+ >
+
+
);
});
diff --git a/docs/data/date-pickers/custom-field/BrowserV7Field.tsx b/docs/data/date-pickers/custom-field/BrowserV7Field.tsx
index a218b603030fb..75545b14ce8cc 100644
--- a/docs/data/date-pickers/custom-field/BrowserV7Field.tsx
+++ b/docs/data/date-pickers/custom-field/BrowserV7Field.tsx
@@ -1,6 +1,8 @@
import * as React from 'react';
import useForkRef from '@mui/utils/useForkRef';
import { styled } from '@mui/material/styles';
+import IconButton from '@mui/material/IconButton';
+import { CalendarIcon } from '@mui/x-date-pickers/icons';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import {
@@ -10,6 +12,7 @@ import {
} from '@mui/x-date-pickers/DatePicker';
import { unstable_useDateField as useDateField } from '@mui/x-date-pickers/DateField';
import { Unstable_PickersSectionList as PickersSectionList } from '@mui/x-date-pickers/PickersSectionList';
+import { usePickerContext } from '@mui/x-date-pickers/hooks';
const BrowserFieldRoot = styled('div', { name: 'BrowserField', slot: 'Root' })({
display: 'flex',
@@ -48,6 +51,9 @@ const BrowserDateField = React.forwardRef(
onPaste,
onKeyDown,
+ // Should be passed to the button that opens the picker
+ openPickerAriaLabel,
+
// Can be passed to a hidden element
onChange,
value,
@@ -66,17 +72,15 @@ const BrowserDateField = React.forwardRef(
focused,
error,
- InputProps: { ref: InputPropsRef, startAdornment, endAdornment } = {},
-
// The rest can be passed to the root element
...other
} = fieldResponse;
- const handleRef = useForkRef(InputPropsRef, ref);
+ const pickerContext = usePickerContext();
+ const handleRef = useForkRef(pickerContext.triggerRef, ref);
return (
- {startAdornment}
- {endAdornment}
+ pickerContext.setOpen((prev) => !prev)}
+ sx={{ marginLeft: 1.5 }}
+ aria-label={openPickerAriaLabel}
+ >
+
+
);
},
diff --git a/docs/data/date-pickers/custom-field/BrowserV7MultiInputRangeField.js b/docs/data/date-pickers/custom-field/BrowserV7MultiInputRangeField.js
index adca0c631ed59..7743be94934f5 100644
--- a/docs/data/date-pickers/custom-field/BrowserV7MultiInputRangeField.js
+++ b/docs/data/date-pickers/custom-field/BrowserV7MultiInputRangeField.js
@@ -94,13 +94,13 @@ const BrowserMultiInputDateRangeField = React.forwardRef((props, ref) => {
const startTextFieldProps = useSlotProps({
elementType: 'input',
externalSlotProps: slotProps?.textField,
- ownerState: { ...props, position: 'start' },
+ ownerState: { position: 'start' },
});
const endTextFieldProps = useSlotProps({
elementType: 'input',
externalSlotProps: slotProps?.textField,
- ownerState: { ...props, position: 'end' },
+ ownerState: { position: 'end' },
});
const fieldResponse = useMultiInputDateRangeField({
@@ -119,6 +119,21 @@ const BrowserMultiInputDateRangeField = React.forwardRef((props, ref) => {
unstableEndFieldRef,
});
+ const {
+ // The multi input range field do not support clearable and onClear
+ onClear: onClearStartDate,
+ clearable: isStartDateClearable,
+ openPickerAriaLabel: openPickerStartDateAriaLabel,
+ ...startDateProps
+ } = fieldResponse.startDate;
+ const {
+ // The multi input range field do not support clearable and onClear
+ onClear: onClearEndDate,
+ clearable: isEndDateClearable,
+ openPickerAriaLabel: openPickerEndDateAriaLabel,
+ ...endDateProps
+ } = fieldResponse.endDate;
+
return (
{
overflow="auto"
className={className}
>
-
+
–
-
+
);
});
diff --git a/docs/data/date-pickers/custom-field/BrowserV7MultiInputRangeField.tsx b/docs/data/date-pickers/custom-field/BrowserV7MultiInputRangeField.tsx
index 7477ed13aaa04..b5c605dc0ac8f 100644
--- a/docs/data/date-pickers/custom-field/BrowserV7MultiInputRangeField.tsx
+++ b/docs/data/date-pickers/custom-field/BrowserV7MultiInputRangeField.tsx
@@ -106,7 +106,11 @@ interface BrowserMultiInputDateRangeFieldProps
DateRangePickerFieldProps,
'unstableFieldRef' | 'clearable' | 'onClear'
>,
- MultiInputFieldRefs {}
+ MultiInputFieldRefs {
+ slotProps: {
+ textField: any;
+ };
+}
type BrowserMultiInputDateRangeFieldComponent = ((
props: BrowserMultiInputDateRangeFieldProps & React.RefAttributes,
@@ -130,13 +134,13 @@ const BrowserMultiInputDateRangeField = React.forwardRef(
const startTextFieldProps = useSlotProps({
elementType: 'input',
externalSlotProps: slotProps?.textField,
- ownerState: { ...props, position: 'start' },
+ ownerState: { position: 'start' } as any,
}) as MultiInputFieldSlotTextFieldProps;
const endTextFieldProps = useSlotProps({
elementType: 'input',
externalSlotProps: slotProps?.textField,
- ownerState: { ...props, position: 'end' },
+ ownerState: { position: 'end' } as any,
}) as MultiInputFieldSlotTextFieldProps;
const fieldResponse = useMultiInputDateRangeField<
@@ -158,6 +162,21 @@ const BrowserMultiInputDateRangeField = React.forwardRef(
unstableEndFieldRef,
});
+ const {
+ // The multi input range field do not support clearable and onClear
+ onClear: onClearStartDate,
+ clearable: isStartDateClearable,
+ openPickerAriaLabel: openPickerStartDateAriaLabel,
+ ...startDateProps
+ } = fieldResponse.startDate;
+ const {
+ // The multi input range field do not support clearable and onClear
+ onClear: onClearEndDate,
+ clearable: isEndDateClearable,
+ openPickerAriaLabel: openPickerEndDateAriaLabel,
+ ...endDateProps
+ } = fieldResponse.endDate;
+
return (
-
+
–
-
+
);
},
diff --git a/docs/data/date-pickers/custom-field/BrowserV7SingleInputRangeField.js b/docs/data/date-pickers/custom-field/BrowserV7SingleInputRangeField.js
index ae55770f60785..5c2fdcc30f448 100644
--- a/docs/data/date-pickers/custom-field/BrowserV7SingleInputRangeField.js
+++ b/docs/data/date-pickers/custom-field/BrowserV7SingleInputRangeField.js
@@ -59,13 +59,12 @@ const BrowserSingleInputDateRangeField = React.forwardRef((props, ref) => {
readOnly,
focused,
error,
- InputProps: { ref: InputPropsRef, startAdornment, endAdornment } = {},
// The rest can be passed to the root element
...other
} = fieldResponse;
const pickerContext = usePickerContext();
- const handleRef = useForkRef(InputPropsRef, ref);
+ const handleRef = useForkRef(pickerContext.triggerRef, ref);
return (
{
minWidth: 300,
}}
>
- {startAdornment}
{
onKeyDown={onKeyDown}
/>
- {endAdornment}
pickerContext.setOpen((prev) => !prev)}>
diff --git a/docs/data/date-pickers/custom-field/BrowserV7SingleInputRangeField.tsx b/docs/data/date-pickers/custom-field/BrowserV7SingleInputRangeField.tsx
index 68f5f4c21737b..bcb591ba663af 100644
--- a/docs/data/date-pickers/custom-field/BrowserV7SingleInputRangeField.tsx
+++ b/docs/data/date-pickers/custom-field/BrowserV7SingleInputRangeField.tsx
@@ -77,14 +77,12 @@ const BrowserSingleInputDateRangeField = React.forwardRef(
focused,
error,
- InputProps: { ref: InputPropsRef, startAdornment, endAdornment } = {},
-
// The rest can be passed to the root element
...other
} = fieldResponse;
const pickerContext = usePickerContext();
- const handleRef = useForkRef(InputPropsRef, ref);
+ const handleRef = useForkRef(pickerContext.triggerRef, ref);
return (
- {startAdornment}
- {endAdornment}
pickerContext.setOpen((prev) => !prev)}>
diff --git a/docs/data/date-pickers/custom-field/JoyV6Field.js b/docs/data/date-pickers/custom-field/JoyV6Field.js
index e445dc0c0fb50..6efa50f33ba40 100644
--- a/docs/data/date-pickers/custom-field/JoyV6Field.js
+++ b/docs/data/date-pickers/custom-field/JoyV6Field.js
@@ -11,12 +11,20 @@ import {
THEME_ID,
} from '@mui/joy/styles';
import Input from '@mui/joy/Input';
+import IconButton from '@mui/joy/IconButton';
import FormControl from '@mui/joy/FormControl';
import FormLabel from '@mui/joy/FormLabel';
+import { createSvgIcon } from '@mui/joy/utils';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { unstable_useDateField as useDateField } from '@mui/x-date-pickers/DateField';
+import { usePickerContext } from '@mui/x-date-pickers/hooks';
+
+const CalendarIcon = createSvgIcon(
+ ,
+ 'Calendar',
+);
const joyTheme = extendJoyTheme();
@@ -26,27 +34,40 @@ const JoyDateField = React.forwardRef((props, ref) => {
const {
// Should be ignored
enableAccessibleFieldDOMStructure,
+ // Should be passed to the button that opens the picker
+ openPickerAriaLabel,
// Can be passed to the button that clears the value
onClear,
clearable,
- disabled,
- id,
+ // Can be used to render a custom label
label,
- InputProps: { ref: containerRef, startAdornment, endAdornment } = {},
+ // Can be used to style the component
+ disabled,
+ readOnly,
+ focused,
+ error,
inputRef,
- slots,
- slotProps,
+ // The rest can be passed to the root element
+ id,
...other
} = fieldResponse;
+ const pickerContext = usePickerContext();
+
return (
{label}
pickerContext.setOpen((prev) => !prev)}
+ aria-label={openPickerAriaLabel}
+ >
+
+
+ }
slotProps={{
input: { ref: inputRef },
}}
diff --git a/docs/data/date-pickers/custom-field/JoyV6Field.tsx b/docs/data/date-pickers/custom-field/JoyV6Field.tsx
index 8cddb46983906..97d8c85b4be5d 100644
--- a/docs/data/date-pickers/custom-field/JoyV6Field.tsx
+++ b/docs/data/date-pickers/custom-field/JoyV6Field.tsx
@@ -11,8 +11,10 @@ import {
THEME_ID,
} from '@mui/joy/styles';
import Input from '@mui/joy/Input';
+import IconButton from '@mui/joy/IconButton';
import FormControl from '@mui/joy/FormControl';
import FormLabel from '@mui/joy/FormLabel';
+import { createSvgIcon } from '@mui/joy/utils';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import {
@@ -21,6 +23,12 @@ import {
DatePickerProps,
} from '@mui/x-date-pickers/DatePicker';
import { unstable_useDateField as useDateField } from '@mui/x-date-pickers/DateField';
+import { usePickerContext } from '@mui/x-date-pickers/hooks';
+
+const CalendarIcon = createSvgIcon(
+ ,
+ 'Calendar',
+);
const joyTheme = extendJoyTheme();
@@ -32,28 +40,44 @@ const JoyDateField = React.forwardRef(
// Should be ignored
enableAccessibleFieldDOMStructure,
+ // Should be passed to the button that opens the picker
+ openPickerAriaLabel,
+
// Can be passed to the button that clears the value
onClear,
clearable,
- disabled,
- id,
+ // Can be used to render a custom label
label,
- InputProps: { ref: containerRef, startAdornment, endAdornment } = {},
+
+ // Can be used to style the component
+ disabled,
+ readOnly,
+ focused,
+ error,
inputRef,
- slots,
- slotProps,
+
+ // The rest can be passed to the root element
+ id,
...other
} = fieldResponse;
+ const pickerContext = usePickerContext();
+
return (
{label}
pickerContext.setOpen((prev) => !prev)}
+ aria-label={openPickerAriaLabel}
+ >
+
+
+ }
slotProps={{
input: { ref: inputRef },
}}
diff --git a/docs/data/date-pickers/custom-field/JoyV6MultiInputRangeField.js b/docs/data/date-pickers/custom-field/JoyV6MultiInputRangeField.js
index af4a282bd9b6f..6adab583bfa13 100644
--- a/docs/data/date-pickers/custom-field/JoyV6MultiInputRangeField.js
+++ b/docs/data/date-pickers/custom-field/JoyV6MultiInputRangeField.js
@@ -118,13 +118,13 @@ const JoyMultiInputDateRangeField = React.forwardRef((props, ref) => {
const startTextFieldProps = useSlotProps({
elementType: FormControl,
externalSlotProps: slotProps?.textField,
- ownerState: { ...props, position: 'start' },
+ ownerState: { position: 'start' },
});
const endTextFieldProps = useSlotProps({
elementType: FormControl,
externalSlotProps: slotProps?.textField,
- ownerState: { ...props, position: 'end' },
+ ownerState: { position: 'end' },
});
const fieldResponse = useMultiInputDateRangeField({
@@ -143,11 +143,26 @@ const JoyMultiInputDateRangeField = React.forwardRef((props, ref) => {
unstableEndFieldRef,
});
+ const {
+ // The multi input range field do not support clearable, onClear and openPickerAriaLabel
+ onClear: onClearStartDate,
+ clearable: isStartDateClearable,
+ openPickerAriaLabel: openPickerStartDateAriaLabel,
+ ...startDateProps
+ } = fieldResponse.startDate;
+ const {
+ // The multi input range field do not support clearable, onClear and openPickerAriaLabel
+ onClear: onClearEndDate,
+ clearable: isEndDateClearable,
+ openPickerAriaLabel: openPickerEndDateAriaLabel,
+ ...endDateProps
+ } = fieldResponse.endDate;
+
return (
-
+
-
+
);
});
diff --git a/docs/data/date-pickers/custom-field/JoyV6MultiInputRangeField.tsx b/docs/data/date-pickers/custom-field/JoyV6MultiInputRangeField.tsx
index f7d9fa0bd3e29..9f5cbc60eff72 100644
--- a/docs/data/date-pickers/custom-field/JoyV6MultiInputRangeField.tsx
+++ b/docs/data/date-pickers/custom-field/JoyV6MultiInputRangeField.tsx
@@ -132,7 +132,11 @@ interface JoyMultiInputDateRangeFieldProps
DateRangePickerFieldProps,
'unstableFieldRef' | 'clearable' | 'onClear'
>,
- MultiInputFieldRefs {}
+ MultiInputFieldRefs {
+ slotProps: {
+ textField: any;
+ };
+}
type JoyMultiInputDateRangeFieldComponent = ((
props: JoyMultiInputDateRangeFieldProps & React.RefAttributes,
@@ -156,13 +160,13 @@ const JoyMultiInputDateRangeField = React.forwardRef(
const startTextFieldProps = useSlotProps({
elementType: FormControl,
externalSlotProps: slotProps?.textField,
- ownerState: { ...props, position: 'start' },
+ ownerState: { position: 'start' } as any,
}) as MultiInputFieldSlotTextFieldProps;
const endTextFieldProps = useSlotProps({
elementType: FormControl,
externalSlotProps: slotProps?.textField,
- ownerState: { ...props, position: 'end' },
+ ownerState: { position: 'end' } as any,
}) as MultiInputFieldSlotTextFieldProps;
const fieldResponse = useMultiInputDateRangeField<
@@ -184,11 +188,26 @@ const JoyMultiInputDateRangeField = React.forwardRef(
unstableEndFieldRef,
});
+ const {
+ // The multi input range field do not support clearable, onClear and openPickerAriaLabel
+ onClear: onClearStartDate,
+ clearable: isStartDateClearable,
+ openPickerAriaLabel: openPickerStartDateAriaLabel,
+ ...startDateProps
+ } = fieldResponse.startDate;
+ const {
+ // The multi input range field do not support clearable, onClear and openPickerAriaLabel
+ onClear: onClearEndDate,
+ clearable: isEndDateClearable,
+ openPickerAriaLabel: openPickerEndDateAriaLabel,
+ ...endDateProps
+ } = fieldResponse.endDate;
+
return (
-
+
-
+
);
},
diff --git a/docs/data/date-pickers/custom-field/JoyV6SingleInputRangeField.js b/docs/data/date-pickers/custom-field/JoyV6SingleInputRangeField.js
index 7e0c7f6ed10a7..eadea36d43b24 100644
--- a/docs/data/date-pickers/custom-field/JoyV6SingleInputRangeField.js
+++ b/docs/data/date-pickers/custom-field/JoyV6SingleInputRangeField.js
@@ -19,7 +19,9 @@ import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { DateRangePicker } from '@mui/x-date-pickers-pro/DateRangePicker';
import { unstable_useSingleInputDateRangeField as useSingleInputDateRangeField } from '@mui/x-date-pickers-pro/SingleInputDateRangeField';
-export const DateRangeIcon = createSvgIcon(
+import { usePickerContext } from '@mui/x-date-pickers/hooks';
+
+const DateRangeIcon = createSvgIcon(
,
'DateRange',
);
@@ -38,13 +40,12 @@ const JoySingleInputDateRangeField = React.forwardRef((props, ref) => {
disabled,
id,
label,
- InputProps: { ref: containerRef } = {},
inputRef,
- slots,
- slotProps,
...other
} = fieldResponse;
+ const pickerContext = usePickerContext();
+
return (
{
>
{label}
}
slotProps={{
diff --git a/docs/data/date-pickers/custom-field/JoyV6SingleInputRangeField.tsx b/docs/data/date-pickers/custom-field/JoyV6SingleInputRangeField.tsx
index 07b8927cc1820..31da6b6756efe 100644
--- a/docs/data/date-pickers/custom-field/JoyV6SingleInputRangeField.tsx
+++ b/docs/data/date-pickers/custom-field/JoyV6SingleInputRangeField.tsx
@@ -23,8 +23,9 @@ import {
} from '@mui/x-date-pickers-pro/DateRangePicker';
import { unstable_useSingleInputDateRangeField as useSingleInputDateRangeField } from '@mui/x-date-pickers-pro/SingleInputDateRangeField';
import { FieldType } from '@mui/x-date-pickers-pro/models';
+import { usePickerContext } from '@mui/x-date-pickers/hooks';
-export const DateRangeIcon = createSvgIcon(
+const DateRangeIcon = createSvgIcon(
,
'DateRange',
);
@@ -50,13 +51,12 @@ const JoySingleInputDateRangeField = React.forwardRef(
disabled,
id,
label,
- InputProps: { ref: containerRef } = {},
inputRef,
- slots,
- slotProps,
...other
} = fieldResponse;
+ const pickerContext = usePickerContext();
+
return (
{label}
}
slotProps={{
diff --git a/docs/data/date-pickers/custom-field/behavior-autocomplete/MaterialDatePicker.js b/docs/data/date-pickers/custom-field/behavior-autocomplete/MaterialDatePicker.js
index 32bc8082bad18..324a11699329c 100644
--- a/docs/data/date-pickers/custom-field/behavior-autocomplete/MaterialDatePicker.js
+++ b/docs/data/date-pickers/custom-field/behavior-autocomplete/MaterialDatePicker.js
@@ -1,8 +1,9 @@
import * as React from 'react';
import dayjs from 'dayjs';
import Autocomplete from '@mui/material/Autocomplete';
+import IconButton from '@mui/material/IconButton';
+import { CalendarIcon } from '@mui/x-date-pickers/icons';
import TextField from '@mui/material/TextField';
-import Stack from '@mui/material/Stack';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
@@ -13,18 +14,16 @@ function AutocompleteField(props) {
const { forwardedProps, internalProps } = useSplitFieldProps(props, 'date');
const { timezone, value, setValue } = usePickerContext();
const {
- InputProps,
- slotProps,
- slots,
ownerState,
label,
focused,
name,
options = [],
- inputProps,
...other
} = forwardedProps;
+ const pickerContext = usePickerContext();
+
const { hasValidationError, getValidationErrorForNewValue } = useValidation({
validator: validateDate,
value,
@@ -32,50 +31,38 @@ function AutocompleteField(props) {
props: internalProps,
});
- const mergeAdornments = (...adornments) => {
- const nonNullAdornments = adornments.filter((el) => el != null);
- if (nonNullAdornments.length === 0) {
- return null;
- }
-
- if (nonNullAdornments.length === 1) {
- return nonNullAdornments[0];
- }
-
- return (
-
- {nonNullAdornments.map((adornment, index) => (
- {adornment}
- ))}
-
- );
- };
-
return (
(
-
- )}
+ renderInput={(params) => {
+ const endAdornment = params.InputProps.endAdornment;
+ return (
+
+ pickerContext.setOpen((prev) => !prev)}
+ size="small"
+ >
+
+
+ {endAdornment.props.children}
+
+ ),
+ }),
+ }}
+ />
+ );
+ }}
getOptionLabel={(option) => {
if (!dayjs.isDayjs(option)) {
return '';
diff --git a/docs/data/date-pickers/custom-field/behavior-autocomplete/MaterialDatePicker.tsx b/docs/data/date-pickers/custom-field/behavior-autocomplete/MaterialDatePicker.tsx
index 43870edeb6e9a..e4884ca5b144d 100644
--- a/docs/data/date-pickers/custom-field/behavior-autocomplete/MaterialDatePicker.tsx
+++ b/docs/data/date-pickers/custom-field/behavior-autocomplete/MaterialDatePicker.tsx
@@ -1,8 +1,9 @@
import * as React from 'react';
import dayjs, { Dayjs } from 'dayjs';
import Autocomplete from '@mui/material/Autocomplete';
+import IconButton from '@mui/material/IconButton';
+import { CalendarIcon } from '@mui/x-date-pickers/icons';
import TextField from '@mui/material/TextField';
-import Stack from '@mui/material/Stack';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import {
@@ -24,18 +25,16 @@ function AutocompleteField(props: AutocompleteFieldProps) {
const { forwardedProps, internalProps } = useSplitFieldProps(props, 'date');
const { timezone, value, setValue } = usePickerContext();
const {
- InputProps,
- slotProps,
- slots,
ownerState,
label,
focused,
name,
options = [],
- inputProps,
...other
} = forwardedProps;
+ const pickerContext = usePickerContext();
+
const { hasValidationError, getValidationErrorForNewValue } = useValidation({
validator: validateDate,
value,
@@ -43,50 +42,39 @@ function AutocompleteField(props: AutocompleteFieldProps) {
props: internalProps,
});
- const mergeAdornments = (...adornments: React.ReactNode[]) => {
- const nonNullAdornments = adornments.filter((el) => el != null);
- if (nonNullAdornments.length === 0) {
- return null;
- }
-
- if (nonNullAdornments.length === 1) {
- return nonNullAdornments[0];
- }
-
- return (
-
- {nonNullAdornments.map((adornment, index) => (
- {adornment}
- ))}
-
- );
- };
-
return (
(
-
- )}
+ renderInput={(params) => {
+ const endAdornment = params.InputProps
+ .endAdornment as React.ReactElement;
+ return (
+
+ pickerContext.setOpen((prev) => !prev)}
+ size="small"
+ >
+
+
+ {endAdornment.props.children}
+
+ ),
+ }),
+ }}
+ />
+ );
+ }}
getOptionLabel={(option) => {
if (!dayjs.isDayjs(option)) {
return '';
diff --git a/docs/data/date-pickers/custom-field/behavior-button/MaterialDatePicker.js b/docs/data/date-pickers/custom-field/behavior-button/MaterialDatePicker.js
index e9ae897f8f41a..d6690a9fee09a 100644
--- a/docs/data/date-pickers/custom-field/behavior-button/MaterialDatePicker.js
+++ b/docs/data/date-pickers/custom-field/behavior-button/MaterialDatePicker.js
@@ -12,16 +12,7 @@ import {
function ButtonDateField(props) {
const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');
- const {
- InputProps,
- slotProps,
- slots,
- ownerState,
- label,
- focused,
- name,
- ...other
- } = forwardedProps;
+ const { ownerState, label, focused, name, ...other } = forwardedProps;
const pickerContext = usePickerContext();
const parsedFormat = useParsedFormat();
@@ -42,7 +33,7 @@ function ButtonDateField(props) {
{...other}
variant="outlined"
color={hasValidationError ? 'error' : 'primary'}
- ref={InputProps?.ref}
+ ref={pickerContext.triggerRef}
onClick={() => pickerContext.setOpen((prev) => !prev)}
>
{label ? `${label}: ${valueStr}` : valueStr}
diff --git a/docs/data/date-pickers/custom-field/behavior-button/MaterialDatePicker.tsx b/docs/data/date-pickers/custom-field/behavior-button/MaterialDatePicker.tsx
index 28793f7cad3c7..56af8ea31b4dc 100644
--- a/docs/data/date-pickers/custom-field/behavior-button/MaterialDatePicker.tsx
+++ b/docs/data/date-pickers/custom-field/behavior-button/MaterialDatePicker.tsx
@@ -16,16 +16,7 @@ import {
function ButtonDateField(props: DatePickerFieldProps) {
const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');
- const {
- InputProps,
- slotProps,
- slots,
- ownerState,
- label,
- focused,
- name,
- ...other
- } = forwardedProps;
+ const { ownerState, label, focused, name, ...other } = forwardedProps;
const pickerContext = usePickerContext();
const parsedFormat = useParsedFormat();
@@ -46,7 +37,7 @@ function ButtonDateField(props: DatePickerFieldProps) {
{...other}
variant="outlined"
color={hasValidationError ? 'error' : 'primary'}
- ref={InputProps?.ref}
+ ref={pickerContext.triggerRef}
onClick={() => pickerContext.setOpen((prev) => !prev)}
>
{label ? `${label}: ${valueStr}` : valueStr}
diff --git a/docs/data/date-pickers/custom-field/behavior-button/MaterialDateRangePicker.js b/docs/data/date-pickers/custom-field/behavior-button/MaterialDateRangePicker.js
index 201f32fa8344f..84c9ce3f03eb0 100644
--- a/docs/data/date-pickers/custom-field/behavior-button/MaterialDateRangePicker.js
+++ b/docs/data/date-pickers/custom-field/behavior-button/MaterialDateRangePicker.js
@@ -14,16 +14,7 @@ import {
function ButtonDateRangeField(props) {
const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');
- const {
- InputProps,
- slotProps,
- slots,
- ownerState,
- label,
- focused,
- name,
- ...other
- } = forwardedProps;
+ const { ownerState, label, focused, name, ...other } = forwardedProps;
const pickerContext = usePickerContext();
const parsedFormat = useParsedFormat();
@@ -45,7 +36,7 @@ function ButtonDateRangeField(props) {
{...other}
variant="outlined"
color={hasValidationError ? 'error' : 'primary'}
- ref={InputProps?.ref}
+ ref={pickerContext.triggerRef}
onClick={() => pickerContext.setOpen((prev) => !prev)}
>
{label ? `${label}: ${formattedValue}` : formattedValue}
diff --git a/docs/data/date-pickers/custom-field/behavior-button/MaterialDateRangePicker.tsx b/docs/data/date-pickers/custom-field/behavior-button/MaterialDateRangePicker.tsx
index cb66e4a2935a5..135ddbd46ec80 100644
--- a/docs/data/date-pickers/custom-field/behavior-button/MaterialDateRangePicker.tsx
+++ b/docs/data/date-pickers/custom-field/behavior-button/MaterialDateRangePicker.tsx
@@ -18,16 +18,7 @@ import {
function ButtonDateRangeField(props: DateRangePickerFieldProps) {
const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');
- const {
- InputProps,
- slotProps,
- slots,
- ownerState,
- label,
- focused,
- name,
- ...other
- } = forwardedProps;
+ const { ownerState, label, focused, name, ...other } = forwardedProps;
const pickerContext = usePickerContext();
const parsedFormat = useParsedFormat();
@@ -49,7 +40,7 @@ function ButtonDateRangeField(props: DateRangePickerFieldProps) {
{...other}
variant="outlined"
color={hasValidationError ? 'error' : 'primary'}
- ref={InputProps?.ref}
+ ref={pickerContext.triggerRef}
onClick={() => pickerContext.setOpen((prev) => !prev)}
>
{label ? `${label}: ${formattedValue}` : formattedValue}
diff --git a/docs/data/date-pickers/custom-field/behavior-masked-text-field/MaskedMaterialTextField.js b/docs/data/date-pickers/custom-field/behavior-masked-text-field/MaskedMaterialTextField.js
index 2c7f4751d7e87..4ee50736a08b2 100644
--- a/docs/data/date-pickers/custom-field/behavior-masked-text-field/MaskedMaterialTextField.js
+++ b/docs/data/date-pickers/custom-field/behavior-masked-text-field/MaskedMaterialTextField.js
@@ -2,6 +2,8 @@ import * as React from 'react';
import dayjs from 'dayjs';
import { useRifm } from 'rifm';
import TextField from '@mui/material/TextField';
+import InputAdornment from '@mui/material/InputAdornment';
+import IconButton from '@mui/material/IconButton';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
@@ -11,6 +13,7 @@ import {
usePickerContext,
} from '@mui/x-date-pickers/hooks';
import { useValidation, validateDate } from '@mui/x-date-pickers/validation';
+import { CalendarIcon } from '@mui/x-date-pickers/icons';
const MASK_USER_INPUT_SYMBOL = '_';
const ACCEPT_REGEX = /[\d]/gi;
@@ -27,9 +30,7 @@ function getInputValueFromValue(value, format) {
}
function MaskedDateField(props) {
- const { slots, slotProps, ...other } = props;
-
- const { forwardedProps, internalProps } = useSplitFieldProps(other, 'date');
+ const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');
const pickerContext = usePickerContext();
const parsedFormat = useParsedFormat();
@@ -131,9 +132,22 @@ function MaskedDateField(props) {
return (
+ pickerContext.setOpen((prev) => !prev)}
+ edge="end"
+ >
+
+
+
+ ),
+ }}
/>
);
}
diff --git a/docs/data/date-pickers/custom-field/behavior-masked-text-field/MaskedMaterialTextField.tsx b/docs/data/date-pickers/custom-field/behavior-masked-text-field/MaskedMaterialTextField.tsx
index 918c1c302ef32..7e61770e25ad6 100644
--- a/docs/data/date-pickers/custom-field/behavior-masked-text-field/MaskedMaterialTextField.tsx
+++ b/docs/data/date-pickers/custom-field/behavior-masked-text-field/MaskedMaterialTextField.tsx
@@ -2,6 +2,8 @@ import * as React from 'react';
import dayjs, { Dayjs } from 'dayjs';
import { useRifm } from 'rifm';
import TextField from '@mui/material/TextField';
+import InputAdornment from '@mui/material/InputAdornment';
+import IconButton from '@mui/material/IconButton';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import {
@@ -15,6 +17,7 @@ import {
usePickerContext,
} from '@mui/x-date-pickers/hooks';
import { useValidation, validateDate } from '@mui/x-date-pickers/validation';
+import { CalendarIcon } from '@mui/x-date-pickers/icons';
const MASK_USER_INPUT_SYMBOL = '_';
const ACCEPT_REGEX = /[\d]/gi;
@@ -31,9 +34,7 @@ function getInputValueFromValue(value: Dayjs | null, format: string) {
}
function MaskedDateField(props: DatePickerFieldProps) {
- const { slots, slotProps, ...other } = props;
-
- const { forwardedProps, internalProps } = useSplitFieldProps(other, 'date');
+ const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');
const pickerContext = usePickerContext();
const parsedFormat = useParsedFormat();
@@ -135,9 +136,22 @@ function MaskedDateField(props: DatePickerFieldProps) {
return (
+ pickerContext.setOpen((prev) => !prev)}
+ edge="end"
+ >
+
+
+
+ ),
+ }}
/>
);
}
diff --git a/docs/data/date-pickers/custom-field/behavior-read-only-mobile-text-field/MaterialDatePicker.js b/docs/data/date-pickers/custom-field/behavior-read-only-mobile-text-field/MaterialDatePicker.js
new file mode 100644
index 0000000000000..a386d83c03392
--- /dev/null
+++ b/docs/data/date-pickers/custom-field/behavior-read-only-mobile-text-field/MaterialDatePicker.js
@@ -0,0 +1,74 @@
+import * as React from 'react';
+import TextField from '@mui/material/TextField';
+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 { useValidation, validateDate } from '@mui/x-date-pickers/validation';
+import {
+ useSplitFieldProps,
+ useParsedFormat,
+ usePickerContext,
+} from '@mui/x-date-pickers/hooks';
+import { CalendarIcon } from '@mui/x-date-pickers/icons';
+import { DateField } from '@mui/x-date-pickers/DateField';
+
+function ReadOnlyDateField(props) {
+ const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');
+
+ const pickerContext = usePickerContext();
+
+ const parsedFormat = useParsedFormat();
+ const { hasValidationError } = useValidation({
+ validator: validateDate,
+ value: pickerContext.value,
+ timezone: pickerContext.timezone,
+ props: internalProps,
+ });
+
+ return (
+ ,
+ sx: { cursor: 'pointer', '& *': { cursor: 'inherit' } },
+ }}
+ error={hasValidationError}
+ onClick={() => pickerContext.setOpen((prev) => !prev)}
+ />
+ );
+}
+
+function ReadOnlyOnMobileDateField(props) {
+ const pickerContext = usePickerContext();
+
+ if (pickerContext.variant === 'mobile') {
+ return ;
+ }
+
+ return ;
+}
+
+function ReadOnlyFieldDatePicker(props) {
+ return (
+
+ );
+}
+
+export default function MaterialDatePicker() {
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/date-pickers/custom-field/behavior-read-only-mobile-text-field/MaterialDatePicker.tsx b/docs/data/date-pickers/custom-field/behavior-read-only-mobile-text-field/MaterialDatePicker.tsx
new file mode 100644
index 0000000000000..3b3e0e22bdc96
--- /dev/null
+++ b/docs/data/date-pickers/custom-field/behavior-read-only-mobile-text-field/MaterialDatePicker.tsx
@@ -0,0 +1,78 @@
+import * as React from 'react';
+import TextField from '@mui/material/TextField';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
+import {
+ DatePicker,
+ DatePickerProps,
+ DatePickerFieldProps,
+} from '@mui/x-date-pickers/DatePicker';
+import { useValidation, validateDate } from '@mui/x-date-pickers/validation';
+import {
+ useSplitFieldProps,
+ useParsedFormat,
+ usePickerContext,
+} from '@mui/x-date-pickers/hooks';
+import { CalendarIcon } from '@mui/x-date-pickers/icons';
+import { DateField } from '@mui/x-date-pickers/DateField';
+
+function ReadOnlyDateField(props: DatePickerFieldProps) {
+ const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');
+
+ const pickerContext = usePickerContext();
+
+ const parsedFormat = useParsedFormat();
+ const { hasValidationError } = useValidation({
+ validator: validateDate,
+ value: pickerContext.value,
+ timezone: pickerContext.timezone,
+ props: internalProps,
+ });
+
+ return (
+ ,
+ sx: { cursor: 'pointer', '& *': { cursor: 'inherit' } },
+ }}
+ error={hasValidationError}
+ onClick={() => pickerContext.setOpen((prev) => !prev)}
+ />
+ );
+}
+
+function ReadOnlyOnMobileDateField(props: DatePickerFieldProps) {
+ const pickerContext = usePickerContext();
+
+ if (pickerContext.variant === 'mobile') {
+ return ;
+ }
+
+ return ;
+}
+
+function ReadOnlyFieldDatePicker(props: DatePickerProps) {
+ return (
+
+ );
+}
+
+export default function MaterialDatePicker() {
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/date-pickers/custom-field/behavior-read-only-mobile-text-field/MaterialDatePicker.tsx.preview b/docs/data/date-pickers/custom-field/behavior-read-only-mobile-text-field/MaterialDatePicker.tsx.preview
new file mode 100644
index 0000000000000..e3842a12cb5d3
--- /dev/null
+++ b/docs/data/date-pickers/custom-field/behavior-read-only-mobile-text-field/MaterialDatePicker.tsx.preview
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/data/date-pickers/custom-field/behavior-read-only-text-field/MaterialDatePicker.js b/docs/data/date-pickers/custom-field/behavior-read-only-text-field/MaterialDatePicker.js
index 213c1df27df35..c1dbf31a52867 100644
--- a/docs/data/date-pickers/custom-field/behavior-read-only-text-field/MaterialDatePicker.js
+++ b/docs/data/date-pickers/custom-field/behavior-read-only-text-field/MaterialDatePicker.js
@@ -13,7 +13,6 @@ import { CalendarIcon } from '@mui/x-date-pickers/icons';
function ReadOnlyDateField(props) {
const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');
- const { InputProps, slotProps, slots, ...other } = forwardedProps;
const pickerContext = usePickerContext();
const parsedFormat = useParsedFormat();
@@ -26,7 +25,7 @@ function ReadOnlyDateField(props) {
return (
,
sx: { cursor: 'pointer', '& *': { cursor: 'inherit' } },
diff --git a/docs/data/date-pickers/custom-field/behavior-read-only-text-field/MaterialDatePicker.tsx b/docs/data/date-pickers/custom-field/behavior-read-only-text-field/MaterialDatePicker.tsx
index 16f52863cc24a..ab5c6c38f0cc7 100644
--- a/docs/data/date-pickers/custom-field/behavior-read-only-text-field/MaterialDatePicker.tsx
+++ b/docs/data/date-pickers/custom-field/behavior-read-only-text-field/MaterialDatePicker.tsx
@@ -17,7 +17,6 @@ import { CalendarIcon } from '@mui/x-date-pickers/icons';
function ReadOnlyDateField(props: DatePickerFieldProps) {
const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');
- const { InputProps, slotProps, slots, ...other } = forwardedProps;
const pickerContext = usePickerContext();
const parsedFormat = useParsedFormat();
@@ -30,7 +29,7 @@ function ReadOnlyDateField(props: DatePickerFieldProps) {
return (
,
sx: { cursor: 'pointer', '& *': { cursor: 'inherit' } },
diff --git a/docs/data/date-pickers/custom-field/behavior-tutorial/MaterialDatePicker.js b/docs/data/date-pickers/custom-field/behavior-tutorial/MaterialDatePicker.js
new file mode 100644
index 0000000000000..24f16cc407460
--- /dev/null
+++ b/docs/data/date-pickers/custom-field/behavior-tutorial/MaterialDatePicker.js
@@ -0,0 +1,86 @@
+import * as React from 'react';
+import dayjs from 'dayjs';
+import TextField from '@mui/material/TextField';
+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 {
+ useSplitFieldProps,
+ useParsedFormat,
+ usePickerContext,
+} from '@mui/x-date-pickers/hooks';
+import { useValidation, validateDate } from '@mui/x-date-pickers/validation';
+
+function CustomDateField(props) {
+ const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');
+
+ const pickerContext = usePickerContext();
+ const placeholder = useParsedFormat();
+ const [inputValue, setInputValue] = useInputValue();
+
+ // Check if the current value is valid or not.
+ const { hasValidationError } = useValidation({
+ value: pickerContext.value,
+ timezone: pickerContext.timezone,
+ props: internalProps,
+ validator: validateDate,
+ });
+
+ const handleChange = (event) => {
+ const newInputValue = event.target.value;
+ const newValue = dayjs(newInputValue, pickerContext.fieldFormat);
+ setInputValue(newInputValue);
+ pickerContext.setValue(newValue);
+ };
+
+ return (
+
+ );
+}
+
+function useInputValue() {
+ const pickerContext = usePickerContext();
+ const [lastValueProp, setLastValueProp] = React.useState(pickerContext.value);
+ const [inputValue, setInputValue] = React.useState(() =>
+ createInputValue(pickerContext.value, pickerContext.fieldFormat),
+ );
+
+ if (lastValueProp !== pickerContext.value) {
+ setLastValueProp(pickerContext.value);
+ if (pickerContext.value && pickerContext.value.isValid()) {
+ setInputValue(
+ createInputValue(pickerContext.value, pickerContext.fieldFormat),
+ );
+ }
+ }
+
+ return [inputValue, setInputValue];
+}
+
+function createInputValue(value, format) {
+ if (value == null) {
+ return '';
+ }
+
+ return value.isValid() ? value.format(format) : '';
+}
+
+function CustomFieldDatePicker(props) {
+ return (
+
+ );
+}
+
+export default function MaterialDatePicker() {
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/date-pickers/custom-field/behavior-tutorial/MaterialDatePicker.tsx b/docs/data/date-pickers/custom-field/behavior-tutorial/MaterialDatePicker.tsx
new file mode 100644
index 0000000000000..9d5f1711c88c6
--- /dev/null
+++ b/docs/data/date-pickers/custom-field/behavior-tutorial/MaterialDatePicker.tsx
@@ -0,0 +1,90 @@
+import * as React from 'react';
+import dayjs, { Dayjs } from 'dayjs';
+import TextField from '@mui/material/TextField';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
+import {
+ DatePicker,
+ DatePickerProps,
+ DatePickerFieldProps,
+} from '@mui/x-date-pickers/DatePicker';
+import {
+ useSplitFieldProps,
+ useParsedFormat,
+ usePickerContext,
+} from '@mui/x-date-pickers/hooks';
+import { useValidation, validateDate } from '@mui/x-date-pickers/validation';
+
+function CustomDateField(props: DatePickerFieldProps) {
+ const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');
+
+ const pickerContext = usePickerContext();
+ const placeholder = useParsedFormat();
+ const [inputValue, setInputValue] = useInputValue();
+
+ // Check if the current value is valid or not.
+ const { hasValidationError } = useValidation({
+ value: pickerContext.value,
+ timezone: pickerContext.timezone,
+ props: internalProps,
+ validator: validateDate,
+ });
+
+ const handleChange = (event: React.ChangeEvent) => {
+ const newInputValue = event.target.value;
+ const newValue = dayjs(newInputValue, pickerContext.fieldFormat);
+ setInputValue(newInputValue);
+ pickerContext.setValue(newValue);
+ };
+
+ return (
+
+ );
+}
+
+function useInputValue() {
+ const pickerContext = usePickerContext();
+ const [lastValueProp, setLastValueProp] = React.useState(pickerContext.value);
+ const [inputValue, setInputValue] = React.useState(() =>
+ createInputValue(pickerContext.value, pickerContext.fieldFormat),
+ );
+
+ if (lastValueProp !== pickerContext.value) {
+ setLastValueProp(pickerContext.value);
+ if (pickerContext.value && pickerContext.value.isValid()) {
+ setInputValue(
+ createInputValue(pickerContext.value, pickerContext.fieldFormat),
+ );
+ }
+ }
+
+ return [inputValue, setInputValue] as const;
+}
+
+function createInputValue(value: Dayjs | null, format: string) {
+ if (value == null) {
+ return '';
+ }
+
+ return value.isValid() ? value.format(format) : '';
+}
+
+function CustomFieldDatePicker(props: DatePickerProps) {
+ return (
+
+ );
+}
+
+export default function MaterialDatePicker() {
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/date-pickers/custom-field/behavior-tutorial/MaterialDatePicker.tsx.preview b/docs/data/date-pickers/custom-field/behavior-tutorial/MaterialDatePicker.tsx.preview
new file mode 100644
index 0000000000000..63be53d3e536f
--- /dev/null
+++ b/docs/data/date-pickers/custom-field/behavior-tutorial/MaterialDatePicker.tsx.preview
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/data/date-pickers/custom-field/custom-field.md b/docs/data/date-pickers/custom-field/custom-field.md
index e30850de8502d..197857b20fc4f 100644
--- a/docs/data/date-pickers/custom-field/custom-field.md
+++ b/docs/data/date-pickers/custom-field/custom-field.md
@@ -144,6 +144,12 @@ but you still want the UI to look like a Text Field, you can replace the field w
{{"demo": "behavior-read-only-text-field/MaterialDatePicker.js", "defaultCodeOpen": false}}
+### Using a read-only Text Field on mobile
+
+If you want to keep the default behavior on desktop but have a read-only TextField on mobile, you can conditionally render the custom field presented in the previous section:
+
+{{"demo": "behavior-read-only-mobile-text-field/MaterialDatePicker.js", "defaultCodeOpen": false}}
+
### Using a Button
If you want users to select a value exclusively through the views
@@ -154,3 +160,157 @@ and you don't want the UI to look like a Text Field, you can replace the field w
The same logic can be applied to any Range Picker:
{{"demo": "behavior-button/MaterialDateRangePicker.js", "defaultCodeOpen": false}}
+
+## Build your own custom field
+
+:::success
+The sections below show how to build a field for your Picker.
+Unlike the field components exposed by `@mui/x-date-pickers` and `@mui/x-date-pickers-pro`, those fields are not suitable for a standalone usage.
+:::
+
+### Typing
+
+Each Picker component exposes an interface describing the props it passes to its field.
+You can import it from the same endpoint as the Picker component and use it to type the props of your field:
+
+```tsx
+import { DatePickerFieldProps } from '@mui/x-date-pickers/DatePicker';
+import { DateRangePickerFieldProps } from '@mui/x-date-pickers-pro/DateRangePicker';
+
+function CustomDateField(props: DatePickerFieldProps) {
+ // Your custom field
+}
+
+function CustomDateRangeField(props: DateRangePickerFieldProps) {
+ // Your custom field
+}
+```
+
+#### Import
+
+| Picker component | Field props interface |
+| ---------------------: | :------------------------------ |
+| Date Picker | `DatePickerFieldProps` |
+| Time Picker | `TimePickerFieldProps` |
+| Date Time Picker | `DateTimePickerFieldProps` |
+| Date Range Picker | `DateRangePickerFieldProps` |
+| Date Time Range Picker | `DateTimeRangePickerFieldProps` |
+
+### Validation
+
+You can use the `useValidation` hook to check if the current value passed to your field is valid or not:
+
+```ts
+import { useValidation, validateDate } from '@mui/x-date-pickers/validation';
+
+const {
+ // The error associated with the current value.
+ // For example: "minDate" if `props.value < props.minDate`.
+ validationError,
+ // `true` if the value is invalid.
+ // On range Pickers it is true if the start date or the end date is invalid.
+ hasValidationError,
+ // Imperatively get the error of a value.
+ getValidationErrorForNewValue,
+} = useValidation({
+ // If you have a value in an internal state, you should pass it here.
+ // Otherwise, you can pass the value returned by `usePickerContext()`.
+ value,
+ timezone,
+ props,
+ validator: validateDate,
+});
+```
+
+#### Import
+
+Each Picker component has a validator adapted to its value type:
+
+| Picker component | Import validator |
+| ---------------------: | :--------------------------------------------------------------------------- |
+| Date Picker | `import { validateDate } from '@mui/x-date-pickers/validation'` |
+| Time Picker | `import { validateTime } from '@mui/x-date-pickers/validation'` |
+| Date Time Picker | `import { validateDateTime } from '@mui/x-date-pickers/validation'` |
+| Date Range Picker | `import { validateDateRange } from '@mui/x-date-pickers-pro/validation'` |
+| Date Time Range Picker | `import { validateDateTimeRange } from '@mui/x-date-pickers-pro/validation'` |
+
+### Localized placeholder
+
+You can use the `useParsedFormat` to get a clean placeholder.
+This hook applies two main transformations on the format:
+
+1. It replaces all the localized tokens (for example `L` for a date with `dayjs`) with their expanded value (`DD/MM/YYYY` for the same date with `dayjs`).
+2. It replaces each token with its token from the localization object (for example `YYYY` remains `YYYY` for the English locale but becomes `AAAA` for the French locale).
+
+:::warning
+The format returned by `useParsedFormat` cannot be parsed by your date library.
+:::
+
+```js
+import { useParsedFormat } from '@mui/x-date-pickers/hooks';
+
+// Uses the format defined by your Picker
+const parsedFormat = useParsedFormat();
+
+// Uses the custom format provided
+const parsedFormat = useParsedFormat({ format: 'MM/DD/YYYY' });
+```
+
+### Spread props to the DOM
+
+The field receives a lot of props that cannot be forwarded to the DOM element without warnings.
+You can use the `useSplitFieldProps` hook to get the props that can be forwarded safely to the DOM:
+
+```tsx
+const { internalProps, forwardedProps } = useSplitFieldProps(
+ // The props received by the field component
+ props,
+ // The value type ("date", "time" or "date-time")
+ 'date',
+);
+
+return (
+
+)
+```
+
+:::success
+The `forwardedProps` contains the `sx` which is specific to MUI.
+You can omit it if the component your are forwarding the props to does not support this concept:
+
+```jsx
+const { sx, ...other } = props;
+const { internalProps, forwardedProps } = useSplitFieldProps(other, 'date');
+
+return (
+
+)
+```
+
+:::
+
+### Pass the field to the Picker
+
+You can pass your custom field to your Picker using the `field` slot:
+
+```tsx
+function DatePickerWithCustomField() {
+ return (
+
+ )
+}
+
+// Also works with the other variants of the component
+function DesktopDatePickerWithCustomField() {
+ return (
+
+ )
+}
+
+```
+
+### Full custom example
+
+Here is a live demo of the example created in all the previous sections:
+
+{{"demo": "behavior-tutorial/MaterialDatePicker.js", "defaultCodeOpen": false}}
diff --git a/docs/data/date-pickers/custom-opening-button/CustomPropsOpeningButton.js b/docs/data/date-pickers/custom-opening-button/CustomPropsOpeningButton.js
index 5ecabf3b7b204..5331d61230248 100644
--- a/docs/data/date-pickers/custom-opening-button/CustomPropsOpeningButton.js
+++ b/docs/data/date-pickers/custom-opening-button/CustomPropsOpeningButton.js
@@ -16,7 +16,7 @@ export default function CustomPropsOpeningButton() {
},
// Targets the `InputAdornment` component.
inputAdornment: {
- position: 'start',
+ component: 'span',
},
}}
/>
diff --git a/docs/data/date-pickers/custom-opening-button/CustomPropsOpeningButton.tsx b/docs/data/date-pickers/custom-opening-button/CustomPropsOpeningButton.tsx
index 5ecabf3b7b204..5331d61230248 100644
--- a/docs/data/date-pickers/custom-opening-button/CustomPropsOpeningButton.tsx
+++ b/docs/data/date-pickers/custom-opening-button/CustomPropsOpeningButton.tsx
@@ -16,7 +16,7 @@ export default function CustomPropsOpeningButton() {
},
// Targets the `InputAdornment` component.
inputAdornment: {
- position: 'start',
+ component: 'span',
},
}}
/>
diff --git a/docs/data/date-pickers/custom-opening-button/CustomPropsOpeningButton.tsx.preview b/docs/data/date-pickers/custom-opening-button/CustomPropsOpeningButton.tsx.preview
index 685e218097c64..1487b57d2a908 100644
--- a/docs/data/date-pickers/custom-opening-button/CustomPropsOpeningButton.tsx.preview
+++ b/docs/data/date-pickers/custom-opening-button/CustomPropsOpeningButton.tsx.preview
@@ -6,7 +6,7 @@
},
// Targets the `InputAdornment` component.
inputAdornment: {
- position: 'start',
+ component: 'span',
},
}}
/>
\ No newline at end of file
diff --git a/docs/data/date-pickers/custom-opening-button/StartEdgeOpeningButton.js b/docs/data/date-pickers/custom-opening-button/StartEdgeOpeningButton.js
new file mode 100644
index 0000000000000..04a1a2803acd9
--- /dev/null
+++ b/docs/data/date-pickers/custom-opening-button/StartEdgeOpeningButton.js
@@ -0,0 +1,19 @@
+import * as React from 'react';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
+import { DemoContainer } from '@mui/x-date-pickers/internals/demo';
+import { DatePicker } from '@mui/x-date-pickers/DatePicker';
+
+export default function StartEdgeOpeningButton() {
+ return (
+
+
+
+
+
+ );
+}
diff --git a/docs/data/date-pickers/custom-opening-button/StartEdgeOpeningButton.tsx b/docs/data/date-pickers/custom-opening-button/StartEdgeOpeningButton.tsx
new file mode 100644
index 0000000000000..04a1a2803acd9
--- /dev/null
+++ b/docs/data/date-pickers/custom-opening-button/StartEdgeOpeningButton.tsx
@@ -0,0 +1,19 @@
+import * as React from 'react';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
+import { DemoContainer } from '@mui/x-date-pickers/internals/demo';
+import { DatePicker } from '@mui/x-date-pickers/DatePicker';
+
+export default function StartEdgeOpeningButton() {
+ return (
+
+
+
+
+
+ );
+}
diff --git a/docs/data/date-pickers/custom-opening-button/StartEdgeOpeningButton.tsx.preview b/docs/data/date-pickers/custom-opening-button/StartEdgeOpeningButton.tsx.preview
new file mode 100644
index 0000000000000..0a9731df02dd7
--- /dev/null
+++ b/docs/data/date-pickers/custom-opening-button/StartEdgeOpeningButton.tsx.preview
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/docs/data/date-pickers/custom-opening-button/custom-opening-button.md b/docs/data/date-pickers/custom-opening-button/custom-opening-button.md
index c0ef70d69eb9f..eb5061b66a5c7 100644
--- a/docs/data/date-pickers/custom-opening-button/custom-opening-button.md
+++ b/docs/data/date-pickers/custom-opening-button/custom-opening-button.md
@@ -39,6 +39,12 @@ If you want to track the opening of the picker, you should use the `onOpen` / `o
:::
+## Render the opening button at the start of the input
+
+You can use the `openPickerButtonPosition` on the `field` slot to position the opening button at the start or the end of the input:
+
+{{"demo": "StartEdgeOpeningButton.js"}}
+
## Add an icon next to the opening button
If you want to add an icon next to the opening button, you can use the `inputAdornment` slot.
diff --git a/docs/data/date-pickers/date-picker/date-picker.md b/docs/data/date-pickers/date-picker/date-picker.md
index 8467168363613..f702279ebb37d 100644
--- a/docs/data/date-pickers/date-picker/date-picker.md
+++ b/docs/data/date-pickers/date-picker/date-picker.md
@@ -46,10 +46,10 @@ Learn more about the _Controlled and uncontrolled_ pattern in the [React documen
The component is available in four variants:
- The `DesktopDatePicker` component which works best for mouse devices and large screens.
- It renders the views inside a popover and allows editing values directly inside the field.
+ It renders the views inside a popover and a field for keyboard editing.
- The `MobileDatePicker` component which works best for touch devices and small screens.
- It renders the view inside a modal and does not allow editing values directly inside the field.
+ It renders the view inside a modal and a field for keyboard editing.
- The `DatePicker` component which renders `DesktopDatePicker` or `MobileDatePicker` depending on the device it runs on.
@@ -125,12 +125,6 @@ You can enable the clearable behavior:
See [Field components—Clearable behavior](/x/react-date-pickers/fields/#clearable-behavior) for more details.
:::
-:::warning
-The clearable prop is not supported yet by the mobile Picker variants.
-
-See discussion [in this GitHub issue](https://github.com/mui/mui-x/issues/10842#issuecomment-1951887408) for more information.
-:::
-
## Localization
See the [Date format and localization](/x/react-date-pickers/adapters-locale/) and [Translated components](/x/react-date-pickers/localization/) documentation pages for more details.
diff --git a/docs/data/date-pickers/date-range-picker/date-range-picker.md b/docs/data/date-pickers/date-range-picker/date-range-picker.md
index 43e680df8d33a..227d9b971b183 100644
--- a/docs/data/date-pickers/date-range-picker/date-range-picker.md
+++ b/docs/data/date-pickers/date-range-picker/date-range-picker.md
@@ -46,10 +46,10 @@ Learn more about the _Controlled and uncontrolled_ pattern in the [React documen
The component is available in four variants:
- The `DesktopDateRangePicker` component which works best for mouse devices and large screens.
- It renders the views inside a popover and allows editing values directly inside the field.
+ It renders the views inside a popover and a field for keyboard editing.
- The `MobileDateRangePicker` component which works best for touch devices and small screens.
- It renders the view inside a modal and does not allow editing values directly inside the field.
+ It renders the view inside a modal and does not allow editing values with the keyboard in the field.
- The `DateRangePicker` component which renders `DesktopDateRangePicker` or `MobileDateRangePicker` depending on the device it runs on.
diff --git a/docs/data/date-pickers/date-time-picker/date-time-picker.md b/docs/data/date-pickers/date-time-picker/date-time-picker.md
index 075ec25dd1567..dcead399e2fc0 100644
--- a/docs/data/date-pickers/date-time-picker/date-time-picker.md
+++ b/docs/data/date-pickers/date-time-picker/date-time-picker.md
@@ -48,10 +48,10 @@ Learn more about the _Controlled and uncontrolled_ pattern in the [React documen
The component is available in four variants:
- The `DesktopDateTimePicker` component which works best for mouse devices and large screens.
- It renders the views inside a popover and allows editing values directly inside the field.
+ It renders the views inside a popover and a field for keyboard editing.
- The `MobileDateTimePicker` component which works best for touch devices and small screens.
- It renders the view inside a modal and does not allow editing values directly inside the field.
+ It renders the view inside a modal and a field for keyboard editing.
- The `DateTimePicker` component which renders `DesktopDateTimePicker` or `MobileDateTimePicker` depending on the device it runs on.
diff --git a/docs/data/date-pickers/date-time-range-picker/date-time-range-picker.md b/docs/data/date-pickers/date-time-range-picker/date-time-range-picker.md
index 92137b72ba818..3b94e397925fb 100644
--- a/docs/data/date-pickers/date-time-range-picker/date-time-range-picker.md
+++ b/docs/data/date-pickers/date-time-range-picker/date-time-range-picker.md
@@ -47,10 +47,10 @@ Learn more about the _Controlled and uncontrolled_ pattern in the [React documen
The component is available in three variants:
- The `DesktopDateTimeRangePicker` component which works best for mouse devices and large screens.
- It renders the views inside a popover and allows editing values directly inside the field.
+ It renders the views inside a popover and a field for keyboard editing.
- The `MobileDateTimeRangePicker` component which works best for touch devices and small screens.
- It renders the view inside a modal and does not allow editing values directly inside the field.
+ It renders the view inside a modal and does not allow editing values with the keyboard in the field.
- The `DateTimeRangePicker` component which renders `DesktopDateTimeRangePicker` or `MobileDateTimeRangePicker` depending on the device it runs on.
diff --git a/docs/data/date-pickers/time-picker/time-picker.md b/docs/data/date-pickers/time-picker/time-picker.md
index d6db6b9d53c99..3332371393684 100644
--- a/docs/data/date-pickers/time-picker/time-picker.md
+++ b/docs/data/date-pickers/time-picker/time-picker.md
@@ -47,10 +47,10 @@ Learn more about the _Controlled and uncontrolled_ pattern in the [React documen
The component is available in four variants:
- The `DesktopTimePicker` component which works best for mouse devices and large screens.
- It renders the views inside a popover and allows editing values directly inside the field.
+ It renders the views inside a popover and a field for keyboard editing.
- The `MobileTimePicker` component which works best for touch devices and small screens.
- It renders the view inside a modal and does not allow editing values directly inside the field.
+ It renders the view inside a modal and a field for keyboard editing.
- The `TimePicker` component which renders `DesktopTimePicker` or `MobileTimePicker` depending on the device it runs on.
diff --git a/docs/data/migration/migration-pickers-v7/migration-pickers-v7.md b/docs/data/migration/migration-pickers-v7/migration-pickers-v7.md
index e581a783ba8f8..d8b17b13012ec 100644
--- a/docs/data/migration/migration-pickers-v7/migration-pickers-v7.md
+++ b/docs/data/migration/migration-pickers-v7/migration-pickers-v7.md
@@ -262,6 +262,20 @@ const theme = createTheme({
});
```
+### Field editing on mobile Pickers
+
+The field is now editable if rendered inside a mobile Picker.
+Before v8, if rendered inside a mobile Picker, the field was read-only, and clicking anywhere on it would open the Picker.
+The mobile and desktop Pickers now behave similarly:
+
+- clicking on the field allows editing the value with the keyboard
+- clicking on the input adornment opens the Picker
+
+:::success
+If you prefer the old behavior, you can create a custom field that renders a read-only Text Field on mobile.
+See [Custom field—Using a read-only Text Field on mobile](/x/react-date-pickers/custom-field/#using-a-read-only-text-field-on-mobile) to learn more.
+:::
+
### Month Calendar
To simplify the theme and class structure, the `` component has been moved inside the Month Calendar component.
@@ -357,6 +371,82 @@ If the updated values do not fit your use case, you can [override them](/x/react
### Slot: `field`
+- The component passed to the `field` slot no longer receives `InputProps` and `inputProps` props.
+ You now need to manually add the UI to open the picker using the `usePickerContext` hook:
+
+ ```diff
+ import { unstable_useDateField } from '@mui/x-date-pickers/DateField';
+ +import { usePickerContext } from '@mui/x-date-pickers/hooks';
+
+ function CustomField(props) {
+ + const pickerContext = usePickerContext();
+
+ return (
+
+ + pickerContext.setOpen((prev) => !prev)}
+ + edge="end"
+ + aria-label={fieldResponse.openPickerAriaLabel}
+ + >
+ +
+ +
+ +
+ + ),
+ + }}
+ />
+ );
+ }
+ ```
+
+ If you are extracting the `ref` from `InputProps` to pass it to another trigger component, you can replace it with `pickerContext.triggerRef`:
+
+ ```diff
+ +import { usePickerContext } from '@mui/x-date-pickers/hooks';
+
+ function CustomField(props) {
+ + const pickerContext = usePickerContext();
+
+ return (
+
+ );
+ }
+ ```
+
+ If you are using a custom editing behavior, instead of using the `openPickerAriaLabel` property returned by the `useXXXField` hooks, you can generate it manually:
+
+ ```diff
+ +import { usePickerTranslations } from '@mui/x-date-pickers/hooks';
+
+ function CustomField(props) {
+ + const translations = usePickerTranslations();
+ + const formattedValue = props.value?.isValid() ? value.format('ll') : null;
+ + const ariaLabel = translations.openDatePickerDialogue(formattedValue);
+
+ return (
+
+ );
+ }
+ ```
+
- The component passed to the `field` slot no longer receives the `value`, `onChange`, `timezone`, `format` and `disabled` props.
You can use the `usePickerContext` hook instead:
@@ -418,6 +508,21 @@ If the updated values do not fit your use case, you can [override them](/x/react
If you are using a hook like `useDateField`, you don't have to do anything, the value from the context are automatically applied.
:::
+### Slot: `inputAdornment`
+
+- The `position` props passed to the `inputAdornment` slot props no longer sets the position of the opening button.
+ This allows defining the position of the opening and clear buttons independently.
+ You can use the `openPickerButtonPosition` prop instead:
+
+ ```diff
+
+ ```
+
### Slot: `layout`
- The `` and `` components must now receive the `ownerState` returned by `usePickerLayout` instead of their props:
diff --git a/docs/pages/x/api/charts/chart-data-provider.json b/docs/pages/x/api/charts/chart-data-provider.json
index 2f7893bf38fec..dd5b141136850 100644
--- a/docs/pages/x/api/charts/chart-data-provider.json
+++ b/docs/pages/x/api/charts/chart-data-provider.json
@@ -1,7 +1,6 @@
{
"props": {
"colors": { "type": { "name": "any" }, "default": "blueberryTwilightPalette" },
- "dataset": { "type": { "name": "any" } },
"height": { "type": { "name": "any" } },
"highlightedItem": { "type": { "name": "any" } },
"id": { "type": { "name": "any" } },
@@ -9,8 +8,7 @@
"onHighlightChange": { "type": { "name": "any" } },
"series": { "type": { "name": "any" } },
"skipAnimation": { "type": { "name": "any" } },
- "width": { "type": { "name": "any" } },
- "zAxis": { "type": { "name": "any" } }
+ "width": { "type": { "name": "any" } }
},
"name": "ChartDataProvider",
"imports": [
diff --git a/docs/pages/x/api/date-pickers/date-field.json b/docs/pages/x/api/date-pickers/date-field.json
index 6a53307f3dc31..90efc47fd6603 100644
--- a/docs/pages/x/api/date-pickers/date-field.json
+++ b/docs/pages/x/api/date-pickers/date-field.json
@@ -2,6 +2,10 @@
"props": {
"autoFocus": { "type": { "name": "bool" }, "default": "false" },
"clearable": { "type": { "name": "bool" }, "default": "false" },
+ "clearButtonPosition": {
+ "type": { "name": "enum", "description": "'end'
| 'start'" },
+ "default": "'end'"
+ },
"color": {
"type": {
"name": "enum",
@@ -61,6 +65,10 @@
"describedArgs": ["newValue"]
}
},
+ "openPickerButtonPosition": {
+ "type": { "name": "enum", "description": "'end'
| 'start'" },
+ "default": "'end'"
+ },
"readOnly": { "type": { "name": "bool" }, "default": "false" },
"referenceDate": {
"type": { "name": "object" },
diff --git a/docs/pages/x/api/date-pickers/date-time-field.json b/docs/pages/x/api/date-pickers/date-time-field.json
index 8ddb6b01c5cc3..cadb7130bc609 100644
--- a/docs/pages/x/api/date-pickers/date-time-field.json
+++ b/docs/pages/x/api/date-pickers/date-time-field.json
@@ -3,6 +3,10 @@
"ampm": { "type": { "name": "bool" }, "default": "utils.is12HourCycleInCurrentLocale()" },
"autoFocus": { "type": { "name": "bool" }, "default": "false" },
"clearable": { "type": { "name": "bool" }, "default": "false" },
+ "clearButtonPosition": {
+ "type": { "name": "enum", "description": "'end'
| 'start'" },
+ "default": "'end'"
+ },
"color": {
"type": {
"name": "enum",
@@ -68,6 +72,10 @@
"describedArgs": ["newValue"]
}
},
+ "openPickerButtonPosition": {
+ "type": { "name": "enum", "description": "'end'
| 'start'" },
+ "default": "'end'"
+ },
"readOnly": { "type": { "name": "bool" }, "default": "false" },
"referenceDate": {
"type": { "name": "object" },
diff --git a/docs/pages/x/api/date-pickers/mobile-date-picker.json b/docs/pages/x/api/date-pickers/mobile-date-picker.json
index 840d432dd53a4..08b336b439cb0 100644
--- a/docs/pages/x/api/date-pickers/mobile-date-picker.json
+++ b/docs/pages/x/api/date-pickers/mobile-date-picker.json
@@ -204,6 +204,18 @@
"default": "PickersCalendarHeader",
"class": null
},
+ {
+ "name": "clearButton",
+ "description": "Button to clear the value.",
+ "default": "IconButton",
+ "class": null
+ },
+ {
+ "name": "clearIcon",
+ "description": "Icon to display inside the clear button.",
+ "default": "ClearIcon",
+ "class": null
+ },
{
"name": "day",
"description": "Custom component for day.\nCheck the [PickersDay](https://mui.com/x/api/date-pickers/pickers-day/) component.",
@@ -221,6 +233,12 @@
"description": "Component used to enter the date with the keyboard.",
"class": null
},
+ {
+ "name": "inputAdornment",
+ "description": "Component displayed on the start or end input adornment used to open the picker on desktop.",
+ "default": "InputAdornment",
+ "class": null
+ },
{
"name": "layout",
"description": "Custom component for wrapping the layout.\nIt wraps the toolbar, views, action bar, and shortcuts.",
@@ -256,6 +274,17 @@
"default": "IconButton",
"class": null
},
+ {
+ "name": "openPickerButton",
+ "description": "Button to open the picker on desktop.",
+ "default": "IconButton",
+ "class": null
+ },
+ {
+ "name": "openPickerIcon",
+ "description": "Icon displayed in the open picker button on desktop.",
+ "class": null
+ },
{
"name": "previousIconButton",
"description": "Button allowing to switch to the left view.",
@@ -288,8 +317,8 @@
},
{
"name": "textField",
- "description": "Form control with an input to render the value inside the default field.",
- "default": "TextField from '@mui/material' or PickersTextField if `enableAccessibleFieldDOMStructure` is `true`.",
+ "description": "Form control with an input to render the value.",
+ "default": ", or from '@mui/material' if `enableAccessibleFieldDOMStructure` is `false`.",
"class": null
},
{
diff --git a/docs/pages/x/api/date-pickers/mobile-date-time-picker.json b/docs/pages/x/api/date-pickers/mobile-date-time-picker.json
index bfc1d0d67b717..5547ade99e31d 100644
--- a/docs/pages/x/api/date-pickers/mobile-date-time-picker.json
+++ b/docs/pages/x/api/date-pickers/mobile-date-time-picker.json
@@ -223,6 +223,18 @@
"default": "PickersCalendarHeader",
"class": null
},
+ {
+ "name": "clearButton",
+ "description": "Button to clear the value.",
+ "default": "IconButton",
+ "class": null
+ },
+ {
+ "name": "clearIcon",
+ "description": "Icon to display inside the clear button.",
+ "default": "ClearIcon",
+ "class": null
+ },
{
"name": "day",
"description": "Custom component for day.\nCheck the [PickersDay](https://mui.com/x/api/date-pickers/pickers-day/) component.",
@@ -240,6 +252,12 @@
"description": "Component used to enter the date with the keyboard.",
"class": null
},
+ {
+ "name": "inputAdornment",
+ "description": "Component displayed on the start or end input adornment used to open the picker on desktop.",
+ "default": "InputAdornment",
+ "class": null
+ },
{
"name": "layout",
"description": "Custom component for wrapping the layout.\nIt wraps the toolbar, views, action bar, and shortcuts.",
@@ -275,6 +293,17 @@
"default": "IconButton",
"class": null
},
+ {
+ "name": "openPickerButton",
+ "description": "Button to open the picker on desktop.",
+ "default": "IconButton",
+ "class": null
+ },
+ {
+ "name": "openPickerIcon",
+ "description": "Icon displayed in the open picker button on desktop.",
+ "class": null
+ },
{
"name": "previousIconButton",
"description": "Button allowing to switch to the left view.",
@@ -313,8 +342,8 @@
},
{
"name": "textField",
- "description": "Form control with an input to render the value inside the default field.",
- "default": "TextField from '@mui/material' or PickersTextField if `enableAccessibleFieldDOMStructure` is `true`.",
+ "description": "Form control with an input to render the value.",
+ "default": ", or from '@mui/material' if `enableAccessibleFieldDOMStructure` is `false`.",
"class": null
},
{
diff --git a/docs/pages/x/api/date-pickers/mobile-time-picker.json b/docs/pages/x/api/date-pickers/mobile-time-picker.json
index 42e778868e5eb..30f368b62370c 100644
--- a/docs/pages/x/api/date-pickers/mobile-time-picker.json
+++ b/docs/pages/x/api/date-pickers/mobile-time-picker.json
@@ -140,6 +140,18 @@
"default": "PickersActionBar",
"class": null
},
+ {
+ "name": "clearButton",
+ "description": "Button to clear the value.",
+ "default": "IconButton",
+ "class": null
+ },
+ {
+ "name": "clearIcon",
+ "description": "Icon to display inside the clear button.",
+ "default": "ClearIcon",
+ "class": null
+ },
{
"name": "dialog",
"description": "Custom component for the dialog inside which the views are rendered on mobile.",
@@ -151,6 +163,12 @@
"description": "Component used to enter the date with the keyboard.",
"class": null
},
+ {
+ "name": "inputAdornment",
+ "description": "Component displayed on the start or end input adornment used to open the picker on desktop.",
+ "default": "InputAdornment",
+ "class": null
+ },
{
"name": "layout",
"description": "Custom component for wrapping the layout.\nIt wraps the toolbar, views, action bar, and shortcuts.",
@@ -180,6 +198,17 @@
"default": "IconButton",
"class": null
},
+ {
+ "name": "openPickerButton",
+ "description": "Button to open the picker on desktop.",
+ "default": "IconButton",
+ "class": null
+ },
+ {
+ "name": "openPickerIcon",
+ "description": "Icon displayed in the open picker button on desktop.",
+ "class": null
+ },
{
"name": "previousIconButton",
"description": "Button allowing to switch to the left view.",
@@ -200,8 +229,8 @@
},
{
"name": "textField",
- "description": "Form control with an input to render the value inside the default field.",
- "default": "TextField from '@mui/material' or PickersTextField if `enableAccessibleFieldDOMStructure` is `true`.",
+ "description": "Form control with an input to render the value.",
+ "default": ", or from '@mui/material' if `enableAccessibleFieldDOMStructure` is `false`.",
"class": null
},
{
diff --git a/docs/pages/x/api/date-pickers/single-input-date-range-field.json b/docs/pages/x/api/date-pickers/single-input-date-range-field.json
index 179b58765222e..b12c8e6071cd9 100644
--- a/docs/pages/x/api/date-pickers/single-input-date-range-field.json
+++ b/docs/pages/x/api/date-pickers/single-input-date-range-field.json
@@ -2,6 +2,10 @@
"props": {
"autoFocus": { "type": { "name": "bool" }, "default": "false" },
"clearable": { "type": { "name": "bool" }, "default": "false" },
+ "clearButtonPosition": {
+ "type": { "name": "enum", "description": "'end'
| 'start'" },
+ "default": "'end'"
+ },
"color": {
"type": {
"name": "enum",
diff --git a/docs/pages/x/api/date-pickers/single-input-date-time-range-field.json b/docs/pages/x/api/date-pickers/single-input-date-time-range-field.json
index 483f9c0016493..119936cbddf19 100644
--- a/docs/pages/x/api/date-pickers/single-input-date-time-range-field.json
+++ b/docs/pages/x/api/date-pickers/single-input-date-time-range-field.json
@@ -3,6 +3,10 @@
"ampm": { "type": { "name": "bool" }, "default": "utils.is12HourCycleInCurrentLocale()" },
"autoFocus": { "type": { "name": "bool" }, "default": "false" },
"clearable": { "type": { "name": "bool" }, "default": "false" },
+ "clearButtonPosition": {
+ "type": { "name": "enum", "description": "'end'
| 'start'" },
+ "default": "'end'"
+ },
"color": {
"type": {
"name": "enum",
diff --git a/docs/pages/x/api/date-pickers/single-input-time-range-field.json b/docs/pages/x/api/date-pickers/single-input-time-range-field.json
index fbfcdcd2781ec..ceeded9aa6802 100644
--- a/docs/pages/x/api/date-pickers/single-input-time-range-field.json
+++ b/docs/pages/x/api/date-pickers/single-input-time-range-field.json
@@ -3,6 +3,10 @@
"ampm": { "type": { "name": "bool" }, "default": "utils.is12HourCycleInCurrentLocale()" },
"autoFocus": { "type": { "name": "bool" }, "default": "false" },
"clearable": { "type": { "name": "bool" }, "default": "false" },
+ "clearButtonPosition": {
+ "type": { "name": "enum", "description": "'end'
| 'start'" },
+ "default": "'end'"
+ },
"color": {
"type": {
"name": "enum",
diff --git a/docs/pages/x/api/date-pickers/time-field.json b/docs/pages/x/api/date-pickers/time-field.json
index fb99ace144ee2..3bb471d4c5f04 100644
--- a/docs/pages/x/api/date-pickers/time-field.json
+++ b/docs/pages/x/api/date-pickers/time-field.json
@@ -3,6 +3,10 @@
"ampm": { "type": { "name": "bool" }, "default": "utils.is12HourCycleInCurrentLocale()" },
"autoFocus": { "type": { "name": "bool" }, "default": "false" },
"clearable": { "type": { "name": "bool" }, "default": "false" },
+ "clearButtonPosition": {
+ "type": { "name": "enum", "description": "'end'
| 'start'" },
+ "default": "'end'"
+ },
"color": {
"type": {
"name": "enum",
@@ -64,6 +68,10 @@
"describedArgs": ["newValue"]
}
},
+ "openPickerButtonPosition": {
+ "type": { "name": "enum", "description": "'end'
| 'start'" },
+ "default": "'end'"
+ },
"readOnly": { "type": { "name": "bool" }, "default": "false" },
"referenceDate": {
"type": { "name": "object" },
diff --git a/docs/src/modules/components/overview/mainDemo/PickerButton.tsx b/docs/src/modules/components/overview/mainDemo/PickerButton.tsx
index 3a823c2c492c1..e90418b6c09ee 100644
--- a/docs/src/modules/components/overview/mainDemo/PickerButton.tsx
+++ b/docs/src/modules/components/overview/mainDemo/PickerButton.tsx
@@ -9,8 +9,7 @@ import { useValidation, validateDate } from '@mui/x-date-pickers/validation';
function ButtonDateField(props: DatePickerFieldProps) {
const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');
- const { InputProps, slotProps, slots, ownerState, label, focused, name, ...other } =
- forwardedProps;
+ const { ownerState, label, focused, name, ...other } = forwardedProps;
const pickerContext = usePickerContext();
@@ -36,7 +35,7 @@ function ButtonDateField(props: DatePickerFieldProps) {
sx={{ minWidth: 'fit-content' }}
fullWidth
color={hasValidationError ? 'error' : 'primary'}
- ref={InputProps?.ref}
+ ref={pickerContext.triggerRef}
onClick={() => pickerContext.setOpen((prev) => !prev)}
>
{label ? `${label}: ${valueStr}` : valueStr}
diff --git a/docs/translations/api-docs/charts/chart-data-provider/chart-data-provider.json b/docs/translations/api-docs/charts/chart-data-provider/chart-data-provider.json
index 0679bb83a3f88..ead6d2b938eec 100644
--- a/docs/translations/api-docs/charts/chart-data-provider/chart-data-provider.json
+++ b/docs/translations/api-docs/charts/chart-data-provider/chart-data-provider.json
@@ -2,9 +2,6 @@
"componentDescription": "Orchestrates the data providers for the chart components and hooks.\n\nUse this component if you have custom HTML components that need to access the chart data.",
"propDescriptions": {
"colors": { "description": "Color palette used to colorize multiple series." },
- "dataset": {
- "description": "An array of objects that can be used to populate series and axes data using their dataKey
property."
- },
"height": {
"description": "The height of the chart in px. If not defined, it takes the height of the parent element."
},
@@ -26,8 +23,7 @@
},
"width": {
"description": "The width of the chart in px. If not defined, it takes the width of the parent element."
- },
- "zAxis": { "description": "The configuration of the z-axes." }
+ }
},
"classDescriptions": {}
}
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 08b247cab6671..6022893a1b8c9 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
@@ -7,6 +7,9 @@
"clearable": {
"description": "If true
, a clear button will be shown in the field allowing value clearing."
},
+ "clearButtonPosition": {
+ "description": "The position at which the clear button is placed. If the field is not clearable, the button is not rendered."
+ },
"color": {
"description": "The color of the component. It supports both default and custom theme colors, which can be added as shown in the palette customization guide."
},
@@ -78,6 +81,9 @@
"description": "Callback fired when the selected sections change.",
"typeDescriptions": { "newValue": "The new selected sections." }
},
+ "openPickerButtonPosition": {
+ "description": "The position at which the opening button is placed. If there is no picker to open, the button is not rendered"
+ },
"readOnly": {
"description": "If true
, the component is read-only. When read-only, the value cannot be changed but the user can interact with the interface."
},
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 30b020a97b080..3a6bc00dd54fb 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
@@ -8,6 +8,9 @@
"clearable": {
"description": "If true
, a clear button will be shown in the field allowing value clearing."
},
+ "clearButtonPosition": {
+ "description": "The position at which the clear button is placed. If the field is not clearable, the button is not rendered."
+ },
"color": {
"description": "The color of the component. It supports both default and custom theme colors, which can be added as shown in the palette customization guide."
},
@@ -95,6 +98,9 @@
"description": "Callback fired when the selected sections change.",
"typeDescriptions": { "newValue": "The new selected sections." }
},
+ "openPickerButtonPosition": {
+ "description": "The position at which the opening button is placed. If there is no picker to open, the button is not rendered"
+ },
"readOnly": {
"description": "If true
, the component is read-only. When read-only, the value cannot be changed but the user can interact with the interface."
},
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 d3bf52f7d9609..698da88d81492 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
@@ -170,21 +170,26 @@
"slotDescriptions": {
"actionBar": "Custom component for the action bar, it is placed below the picker views.",
"calendarHeader": "Custom component for calendar header. Check the PickersCalendarHeader component.",
+ "clearButton": "Button to clear the value.",
+ "clearIcon": "Icon to display inside the clear button.",
"day": "Custom component for day. Check the PickersDay component.",
"dialog": "Custom component for the dialog inside which the views are rendered on mobile.",
"field": "Component used to enter the date with the keyboard.",
+ "inputAdornment": "Component displayed on the start or end input adornment used to open the picker on desktop.",
"layout": "Custom component for wrapping the layout. It wraps the toolbar, views, action bar, and shortcuts.",
"leftArrowIcon": "Icon displayed in the left view switch button.",
"mobilePaper": "Custom component for the paper rendered inside the mobile picker's Dialog.",
"mobileTransition": "Custom component for the mobile dialog Transition.",
"monthButton": "Button displayed to render a single month in the month
view.",
"nextIconButton": "Button allowing to switch to the right view.",
+ "openPickerButton": "Button to open the picker on desktop.",
+ "openPickerIcon": "Icon displayed in the open picker button on desktop.",
"previousIconButton": "Button allowing to switch to the left view.",
"rightArrowIcon": "Icon displayed in the right view switch button.",
"shortcuts": "Custom component for the shortcuts.",
"switchViewButton": "Button displayed to switch between different calendar views.",
"switchViewIcon": "Icon displayed in the SwitchViewButton. Rotated by 180° when the open view is year
.",
- "textField": "Form control with an input to render the value inside the default field.",
+ "textField": "Form control with an input to render the value.",
"toolbar": "Custom component for the toolbar rendered above the views.",
"yearButton": "Button displayed to render a single year in the year
view."
}
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 e08489562eeb6..661fad9c125c6 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
@@ -198,22 +198,27 @@
"slotDescriptions": {
"actionBar": "Custom component for the action bar, it is placed below the picker views.",
"calendarHeader": "Custom component for calendar header. Check the PickersCalendarHeader component.",
+ "clearButton": "Button to clear the value.",
+ "clearIcon": "Icon to display inside the clear button.",
"day": "Custom component for day. Check the PickersDay component.",
"dialog": "Custom component for the dialog inside which the views are rendered on mobile.",
"field": "Component used to enter the date with the keyboard.",
+ "inputAdornment": "Component displayed on the start or end input adornment used to open the picker on desktop.",
"layout": "Custom component for wrapping the layout. It wraps the toolbar, views, action bar, and shortcuts.",
"leftArrowIcon": "Icon displayed in the left view switch button.",
"mobilePaper": "Custom component for the paper rendered inside the mobile picker's Dialog.",
"mobileTransition": "Custom component for the mobile dialog Transition.",
"monthButton": "Button displayed to render a single month in the month
view.",
"nextIconButton": "Button allowing to switch to the right view.",
+ "openPickerButton": "Button to open the picker on desktop.",
+ "openPickerIcon": "Icon displayed in the open picker button on desktop.",
"previousIconButton": "Button allowing to switch to the left view.",
"rightArrowIcon": "Icon displayed in the right view switch button.",
"shortcuts": "Custom component for the shortcuts.",
"switchViewButton": "Button displayed to switch between different calendar views.",
"switchViewIcon": "Icon displayed in the SwitchViewButton. Rotated by 180° when the open view is year
.",
"tabs": "Tabs enabling toggling between date and time pickers.",
- "textField": "Form control with an input to render the value inside the default field.",
+ "textField": "Form control with an input to render the value.",
"toolbar": "Custom component for the toolbar rendered above the views.",
"yearButton": "Button displayed to render a single year in the year
view."
}
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 8d76eacc2d628..10d1272f67d3d 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
@@ -129,17 +129,22 @@
"classDescriptions": {},
"slotDescriptions": {
"actionBar": "Custom component for the action bar, it is placed below the picker views.",
+ "clearButton": "Button to clear the value.",
+ "clearIcon": "Icon to display inside the clear button.",
"dialog": "Custom component for the dialog inside which the views are rendered on mobile.",
"field": "Component used to enter the date with the keyboard.",
+ "inputAdornment": "Component displayed on the start or end input adornment used to open the picker on desktop.",
"layout": "Custom component for wrapping the layout. It wraps the toolbar, views, action bar, and shortcuts.",
"leftArrowIcon": "Icon displayed in the left view switch button.",
"mobilePaper": "Custom component for the paper rendered inside the mobile picker's Dialog.",
"mobileTransition": "Custom component for the mobile dialog Transition.",
"nextIconButton": "Button allowing to switch to the right view.",
+ "openPickerButton": "Button to open the picker on desktop.",
+ "openPickerIcon": "Icon displayed in the open picker button on desktop.",
"previousIconButton": "Button allowing to switch to the left view.",
"rightArrowIcon": "Icon displayed in the right view switch button.",
"shortcuts": "Custom component for the shortcuts.",
- "textField": "Form control with an input to render the value inside the default field.",
+ "textField": "Form control with an input to render the value.",
"toolbar": "Custom component for the toolbar rendered above the views."
}
}
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 72e27d93359fe..03f4bf83d3a7a 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
@@ -7,6 +7,9 @@
"clearable": {
"description": "If true
, a clear button will be shown in the field allowing value clearing."
},
+ "clearButtonPosition": {
+ "description": "The position at which the clear button is placed. If the field is not clearable, the button is not rendered."
+ },
"color": {
"description": "The color of the component. It supports both default and custom theme colors, which can be added as shown in the palette customization guide."
},
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 871afaaa25741..08f57b1fb609a 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
@@ -8,6 +8,9 @@
"clearable": {
"description": "If true
, a clear button will be shown in the field allowing value clearing."
},
+ "clearButtonPosition": {
+ "description": "The position at which the clear button is placed. If the field is not clearable, the button is not rendered."
+ },
"color": {
"description": "The color of the component. It supports both default and custom theme colors, which can be added as shown in the palette customization guide."
},
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 11ab48673c0fa..32b0d8c393e7b 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
@@ -8,6 +8,9 @@
"clearable": {
"description": "If true
, a clear button will be shown in the field allowing value clearing."
},
+ "clearButtonPosition": {
+ "description": "The position at which the clear button is placed. If the field is not clearable, the button is not rendered."
+ },
"color": {
"description": "The color of the component. It supports both default and custom theme colors, which can be added as shown in the palette customization guide."
},
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 376ab6e3e4225..9a6ba3f53aab4 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
@@ -8,6 +8,9 @@
"clearable": {
"description": "If true
, a clear button will be shown in the field allowing value clearing."
},
+ "clearButtonPosition": {
+ "description": "The position at which the clear button is placed. If the field is not clearable, the button is not rendered."
+ },
"color": {
"description": "The color of the component. It supports both default and custom theme colors, which can be added as shown in the palette customization guide."
},
@@ -87,6 +90,9 @@
"description": "Callback fired when the selected sections change.",
"typeDescriptions": { "newValue": "The new selected sections." }
},
+ "openPickerButtonPosition": {
+ "description": "The position at which the opening button is placed. If there is no picker to open, the button is not rendered"
+ },
"readOnly": {
"description": "If true
, the component is read-only. When read-only, the value cannot be changed but the user can interact with the interface."
},
diff --git a/packages/x-charts-pro/src/ChartContainerPro/ChartContainerPro.tsx b/packages/x-charts-pro/src/ChartContainerPro/ChartContainerPro.tsx
index 8fb844eb2ca29..7cd6613028db6 100644
--- a/packages/x-charts-pro/src/ChartContainerPro/ChartContainerPro.tsx
+++ b/packages/x-charts-pro/src/ChartContainerPro/ChartContainerPro.tsx
@@ -6,16 +6,13 @@ import { Watermark } from '@mui/x-license/Watermark';
import { useLicenseVerifier } from '@mui/x-license/useLicenseVerifier';
import { ChartsSurface, ChartsSurfaceProps } from '@mui/x-charts/ChartsSurface';
import { ChartDataProvider, ChartDataProviderProps } from '@mui/x-charts/context';
-import { ChartSeriesType, UseChartCartesianAxisSignature } from '@mui/x-charts/internals';
+import { ChartSeriesType } from '@mui/x-charts/internals';
import { getReleaseInfo } from '../internals/utils/releaseInfo';
import { useChartContainerProProps } from './useChartContainerProProps';
-import { UseChartProZoomSignature } from '../internals/plugins/useChartProZoom/useChartProZoom.types';
+import { AllPluginSignatures } from '../internals/plugins/allPlugins';
export interface ChartContainerProProps
- extends ChartDataProviderProps<
- [UseChartCartesianAxisSignature, UseChartProZoomSignature],
- TSeries
- >,
+ extends ChartDataProviderProps>,
ChartsSurfaceProps {}
const releaseInfo = getReleaseInfo();
@@ -53,9 +50,7 @@ const ChartContainerPro = React.forwardRef(function ChartContainerProInner<
useLicenseVerifier('x-charts-pro', releaseInfo);
return (
- , UseChartProZoomSignature]>
- {...chartDataProviderProProps}
- >
+ > {...chartDataProviderProProps}>
{children}
diff --git a/packages/x-charts-pro/src/ChartContainerPro/useChartContainerProProps.ts b/packages/x-charts-pro/src/ChartContainerPro/useChartContainerProProps.ts
index c5bb363ae5c70..3c127fb68a6eb 100644
--- a/packages/x-charts-pro/src/ChartContainerPro/useChartContainerProProps.ts
+++ b/packages/x-charts-pro/src/ChartContainerPro/useChartContainerProProps.ts
@@ -1,26 +1,19 @@
'use client';
import {
ChartDataProviderProps,
- ChartPlugin,
ChartSeriesType,
- useChartCartesianAxis,
- UseChartCartesianAxisSignature,
useChartContainerProps,
UseChartContainerPropsReturnValue,
} from '@mui/x-charts/internals';
import * as React from 'react';
import type { ChartContainerProProps } from './ChartContainerPro';
-import { useChartProZoom } from '../internals/plugins/useChartProZoom';
-import { UseChartProZoomSignature } from '../internals/plugins/useChartProZoom/useChartProZoom.types';
+import { ALL_PLUGINS, AllPluginsType, AllPluginSignatures } from '../internals/plugins/allPlugins';
export type UseChartContainerProPropsReturnValue = Pick<
UseChartContainerPropsReturnValue,
'chartsSurfaceProps' | 'children'
> & {
- chartDataProviderProProps: ChartDataProviderProps<
- [UseChartCartesianAxisSignature, UseChartProZoomSignature],
- TSeries
- >;
+ chartDataProviderProProps: ChartDataProviderProps>;
};
export const useChartContainerProProps = (
@@ -30,10 +23,7 @@ export const useChartContainerProProps = , UseChartProZoomSignature],
- TSeries
- >,
+ ChartDataProviderProps>,
'initialZoom' | 'onZoomChange'
> = {
initialZoom,
@@ -50,12 +40,7 @@ export const useChartContainerProProps = >,
- // eslint-disable-next-line react-compiler/react-compiler
- useChartProZoom,
- ],
+ plugins: plugins ?? (ALL_PLUGINS as unknown as AllPluginsType),
},
chartsSurfaceProps,
children,
diff --git a/packages/x-charts-pro/src/ScatterChartPro/ScatterChartPro.tsx b/packages/x-charts-pro/src/ScatterChartPro/ScatterChartPro.tsx
index 3fa127fd00ec9..011536b3a1e99 100644
--- a/packages/x-charts-pro/src/ScatterChartPro/ScatterChartPro.tsx
+++ b/packages/x-charts-pro/src/ScatterChartPro/ScatterChartPro.tsx
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import { useThemeProps } from '@mui/material/styles';
import { ChartsOverlay } from '@mui/x-charts/ChartsOverlay';
import { ScatterChartProps, ScatterPlot } from '@mui/x-charts/ScatterChart';
-import { ChartDataProvider, ZAxisContextProvider } from '@mui/x-charts/context';
+import { ChartDataProvider } from '@mui/x-charts/context';
import { ChartsVoronoiHandler } from '@mui/x-charts/ChartsVoronoiHandler';
import { ChartsAxis } from '@mui/x-charts/ChartsAxis';
import { ChartsGrid } from '@mui/x-charts/ChartsGrid';
@@ -39,7 +39,6 @@ const ScatterChartPro = React.forwardRef(function ScatterChartPro(
const {
chartsWrapperProps,
chartContainerProps,
- zAxisProps,
voronoiHandlerProps,
chartsAxisProps,
gridProps,
@@ -66,19 +65,17 @@ const ScatterChartPro = React.forwardRef(function ScatterChartPro(
{!props.hideLegend && }
-
- {!props.disableVoronoi && }
-
-
-
- {/* The `data-drawing-container` indicates that children are part of the drawing area. Ref: https://github.com/mui/mui-x/issues/13659 */}
-
-
-
-
- {!props.loading && }
- {children}
-
+ {!props.disableVoronoi && }
+
+
+
+ {/* The `data-drawing-container` indicates that children are part of the drawing area. Ref: https://github.com/mui/mui-x/issues/13659 */}
+
+
+
+
+ {!props.loading && }
+ {children}
diff --git a/packages/x-charts-pro/src/internals/plugins/allPlugins.ts b/packages/x-charts-pro/src/internals/plugins/allPlugins.ts
new file mode 100644
index 0000000000000..f7c3b8feeb125
--- /dev/null
+++ b/packages/x-charts-pro/src/internals/plugins/allPlugins.ts
@@ -0,0 +1,30 @@
+// This file should be removed after creating all plugins in favor of a file per chart type.
+
+import {
+ ChartSeriesType,
+ ConvertSignaturesIntoPlugins,
+ useChartCartesianAxis,
+ UseChartCartesianAxisSignature,
+ useChartInteraction,
+ UseChartInteractionSignature,
+ useChartZAxis,
+ UseChartZAxisSignature,
+} from '@mui/x-charts/internals';
+import { useChartProZoom, UseChartProZoomSignature } from './useChartProZoom';
+
+export type AllPluginSignatures = [
+ UseChartZAxisSignature,
+ UseChartCartesianAxisSignature,
+ UseChartInteractionSignature,
+ UseChartProZoomSignature,
+];
+
+export type AllPluginsType =
+ ConvertSignaturesIntoPlugins>;
+
+export const ALL_PLUGINS = [
+ useChartZAxis,
+ useChartCartesianAxis,
+ useChartInteraction,
+ useChartProZoom,
+];
diff --git a/packages/x-charts-pro/src/internals/plugins/useChartProZoom/index.ts b/packages/x-charts-pro/src/internals/plugins/useChartProZoom/index.ts
index 48ce05ccb5283..7cdc3e4f2cd33 100644
--- a/packages/x-charts-pro/src/internals/plugins/useChartProZoom/index.ts
+++ b/packages/x-charts-pro/src/internals/plugins/useChartProZoom/index.ts
@@ -1,2 +1,3 @@
export * from './useChartProZoom.selectors';
export * from './useChartProZoom';
+export * from './useChartProZoom.types';
diff --git a/packages/x-charts/src/ChartContainer/ChartContainer.tsx b/packages/x-charts/src/ChartContainer/ChartContainer.tsx
index 015aaab009bf6..cc7951aa287a4 100644
--- a/packages/x-charts/src/ChartContainer/ChartContainer.tsx
+++ b/packages/x-charts/src/ChartContainer/ChartContainer.tsx
@@ -5,14 +5,10 @@ import { ChartSeriesType } from '../models/seriesType/config';
import { ChartDataProvider, ChartDataProviderProps } from '../context/ChartDataProvider';
import { useChartContainerProps } from './useChartContainerProps';
import { ChartsSurface, ChartsSurfaceProps } from '../ChartsSurface';
-import { UseChartCartesianAxisSignature } from '../internals/plugins/featurePlugins/useChartCartesianAxis';
-import { UseChartInteractionSignature } from '../internals/plugins/featurePlugins/useChartInteraction';
+import { AllPluginSignatures } from '../internals/plugins/allPlugins';
export interface ChartContainerProps
- extends Omit<
- ChartDataProviderProps<[UseChartCartesianAxisSignature], SeriesType>,
- 'children'
- >,
+ extends Omit>, 'children'>,
ChartsSurfaceProps {}
/**
@@ -49,12 +45,7 @@ const ChartContainer = React.forwardRef(function ChartContainer, UseChartInteractionSignature]
- >
- {...chartDataProviderProps}
- >
+ > {...chartDataProviderProps}>
{children}
);
diff --git a/packages/x-charts/src/ChartContainer/useChartContainerProps.ts b/packages/x-charts/src/ChartContainer/useChartContainerProps.ts
index e45d511eb9713..9e9b14a3e064d 100644
--- a/packages/x-charts/src/ChartContainer/useChartContainerProps.ts
+++ b/packages/x-charts/src/ChartContainer/useChartContainerProps.ts
@@ -3,21 +3,11 @@ import * as React from 'react';
import { ChartsSurfaceProps } from '../ChartsSurface';
import { ChartDataProviderProps } from '../context/ChartDataProvider';
import type { ChartContainerProps } from './ChartContainer';
-import {
- useChartCartesianAxis,
- UseChartCartesianAxisSignature,
-} from '../internals/plugins/featurePlugins/useChartCartesianAxis';
-import {
- useChartInteraction,
- UseChartInteractionSignature,
-} from '../internals/plugins/featurePlugins/useChartInteraction';
import { ChartSeriesType } from '../models/seriesType/config';
+import { ALL_PLUGINS, AllPluginSignatures, AllPluginsType } from '../internals/plugins/allPlugins';
export type UseChartContainerPropsReturnValue = {
- chartDataProviderProps: ChartDataProviderProps<
- [UseChartCartesianAxisSignature, UseChartInteractionSignature],
- TSeries
- >;
+ chartDataProviderProps: ChartDataProviderProps>;
chartsSurfaceProps: ChartsSurfaceProps & { ref: React.Ref };
children: React.ReactNode;
};
@@ -58,10 +48,7 @@ export const useChartContainerProps = , UseChartInteractionSignature],
- TSeries
- >,
+ ChartDataProviderProps>,
'children'
> = {
margin,
@@ -77,7 +64,7 @@ export const useChartContainerProps = ,
};
return {
diff --git a/packages/x-charts/src/ChartsLegend/useAxis.ts b/packages/x-charts/src/ChartsLegend/useAxis.ts
index 4ae98a2ad5e95..790fdca1536f5 100644
--- a/packages/x-charts/src/ChartsLegend/useAxis.ts
+++ b/packages/x-charts/src/ChartsLegend/useAxis.ts
@@ -1,7 +1,7 @@
'use client';
import { AxisDefaultized } from '../models/axis';
import { ZAxisDefaultized } from '../models/z-axis';
-import { useZAxis } from '../hooks/useZAxis';
+import { useZAxes } from '../hooks/useZAxis';
import { useXAxes, useYAxes } from '../hooks/useAxis';
import { ColorLegendSelector } from './colorLegend.types';
@@ -14,7 +14,7 @@ export function useAxis({
}: ColorLegendSelector): ZAxisDefaultized | AxisDefaultized {
const { xAxis, xAxisIds } = useXAxes();
const { yAxis, yAxisIds } = useYAxes();
- const { zAxis, zAxisIds } = useZAxis();
+ const { zAxis, zAxisIds } = useZAxes();
switch (axisDirection) {
case 'x': {
diff --git a/packages/x-charts/src/ChartsTooltip/useAxisTooltip.tsx b/packages/x-charts/src/ChartsTooltip/useAxisTooltip.tsx
index 0004b28032981..2a239a72afa4d 100644
--- a/packages/x-charts/src/ChartsTooltip/useAxisTooltip.tsx
+++ b/packages/x-charts/src/ChartsTooltip/useAxisTooltip.tsx
@@ -9,7 +9,7 @@ import { getLabel } from '../internals/getLabel';
import { isCartesianSeriesType } from '../internals/isCartesian';
import { utcFormatter } from './utils';
import { useXAxes, useXAxis, useYAxes, useYAxis } from '../hooks/useAxis';
-import { useZAxis } from '../hooks/useZAxis';
+import { useZAxes } from '../hooks/useZAxis';
import {
selectorChartsInteractionXAxis,
selectorChartsInteractionYAxis,
@@ -53,7 +53,7 @@ export function useAxisTooltip(): UseAxisTooltipReturnValue | null {
const { xAxis } = useXAxes();
const { yAxis } = useYAxes();
- const { zAxis, zAxisIds } = useZAxis();
+ const { zAxis, zAxisIds } = useZAxes();
const colorProcessors = useColorProcessor();
if (axisData === null) {
diff --git a/packages/x-charts/src/ChartsTooltip/useItemTooltip.tsx b/packages/x-charts/src/ChartsTooltip/useItemTooltip.tsx
index 885856da21b0c..b8dc93fcfd80f 100644
--- a/packages/x-charts/src/ChartsTooltip/useItemTooltip.tsx
+++ b/packages/x-charts/src/ChartsTooltip/useItemTooltip.tsx
@@ -11,8 +11,8 @@ import { getLabel } from '../internals/getLabel';
import { selectorChartsInteractionItem } from '../internals/plugins/featurePlugins/useChartInteraction';
import { useSelector } from '../internals/store/useSelector';
import { useStore } from '../internals/store/useStore';
-import { useXAxes, useYAxes } from '../hooks';
-import { useZAxis } from '../hooks/useZAxis';
+import { useXAxes, useYAxes } from '../hooks/useAxis';
+import { useZAxes } from '../hooks/useZAxis';
import { ChartsLabelMarkProps } from '../ChartsLabel';
export interface UseItemTooltipReturnValue {
@@ -32,7 +32,7 @@ export function useItemTooltip(): null | UseItemToolt
const { xAxis, xAxisIds } = useXAxes();
const { yAxis, yAxisIds } = useYAxes();
- const { zAxis, zAxisIds } = useZAxis();
+ const { zAxis, zAxisIds } = useZAxes();
const colorProcessors = useColorProcessor();
const xAxisId = (series as any).xAxisId ?? xAxisIds[0];
diff --git a/packages/x-charts/src/ScatterChart/ScatterChart.tsx b/packages/x-charts/src/ScatterChart/ScatterChart.tsx
index b93da945a0557..6261eeb7384a2 100644
--- a/packages/x-charts/src/ScatterChart/ScatterChart.tsx
+++ b/packages/x-charts/src/ScatterChart/ScatterChart.tsx
@@ -28,7 +28,6 @@ import {
ChartsVoronoiHandlerProps,
} from '../ChartsVoronoiHandler/ChartsVoronoiHandler';
import { ChartsGrid, ChartsGridProps } from '../ChartsGrid';
-import { ZAxisContextProvider, ZAxisContextProviderProps } from '../context/ZAxisContextProvider';
import { useScatterChartProps } from './useScatterChartProps';
import { useChartContainerProps } from '../ChartContainer/useChartContainerProps';
import { ChartDataProvider } from '../context';
@@ -50,7 +49,6 @@ export interface ScatterChartSlotProps
export interface ScatterChartProps
extends Omit,
- Omit,
Omit,
Omit,
Omit {
@@ -114,7 +112,6 @@ const ScatterChart = React.forwardRef(function ScatterChart(
const {
chartsWrapperProps,
chartContainerProps,
- zAxisProps,
voronoiHandlerProps,
chartsAxisProps,
gridProps,
@@ -136,19 +133,17 @@ const ScatterChart = React.forwardRef(function ScatterChart(
{!props.hideLegend && }
-
- {!props.disableVoronoi && }
-
-
-
- {/* The `data-drawing-container` indicates that children are part of the drawing area. Ref: https://github.com/mui/mui-x/issues/13659 */}
-
-
-
-
- {!props.loading && }
- {children}
-
+ {!props.disableVoronoi && }
+
+
+
+ {/* The `data-drawing-container` indicates that children are part of the drawing area. Ref: https://github.com/mui/mui-x/issues/13659 */}
+
+
+
+
+ {!props.loading && }
+ {children}
diff --git a/packages/x-charts/src/ScatterChart/ScatterPlot.tsx b/packages/x-charts/src/ScatterChart/ScatterPlot.tsx
index e52f99bb7ac78..d50a68fe829c4 100644
--- a/packages/x-charts/src/ScatterChart/ScatterPlot.tsx
+++ b/packages/x-charts/src/ScatterChart/ScatterPlot.tsx
@@ -5,7 +5,7 @@ import { Scatter, ScatterProps } from './Scatter';
import getColor from './getColor';
import { useScatterSeries } from '../hooks/useSeries';
import { useXAxes, useYAxes } from '../hooks';
-import { useZAxis } from '../hooks/useZAxis';
+import { useZAxes } from '../hooks/useZAxis';
export interface ScatterPlotSlots {
scatter?: React.JSXElementConstructor;
@@ -43,7 +43,7 @@ function ScatterPlot(props: ScatterPlotProps) {
const seriesData = useScatterSeries();
const { xAxis, xAxisIds } = useXAxes();
const { yAxis, yAxisIds } = useYAxes();
- const { zAxis, zAxisIds } = useZAxis();
+ const { zAxis, zAxisIds } = useZAxes();
if (seriesData === undefined) {
return null;
diff --git a/packages/x-charts/src/ScatterChart/useScatterChartProps.ts b/packages/x-charts/src/ScatterChart/useScatterChartProps.ts
index b600574bf1a67..44a579aeed422 100644
--- a/packages/x-charts/src/ScatterChart/useScatterChartProps.ts
+++ b/packages/x-charts/src/ScatterChart/useScatterChartProps.ts
@@ -6,7 +6,6 @@ import { ChartsLegendSlotExtension } from '../ChartsLegend';
import { ChartsOverlayProps } from '../ChartsOverlay';
import type { ChartsVoronoiHandlerProps } from '../ChartsVoronoiHandler';
import { ChartContainerProps } from '../ChartContainer';
-import { ZAxisContextProviderProps } from '../context';
import type { ScatterChartProps } from './ScatterChart';
import type { ScatterPlotProps } from './ScatterPlot';
import type { ChartsWrapperProps } from '../internals/components/ChartsWrapper';
@@ -59,13 +58,12 @@ export const useScatterChartProps = (props: ScatterChartProps) => {
colors,
xAxis,
yAxis,
+ zAxis,
highlightedItem,
onHighlightChange,
className,
};
- const zAxisProps: Omit = {
- zAxis,
- };
+
const voronoiHandlerProps: ChartsVoronoiHandlerProps = {
voronoiMaxRadius,
onItemClick: onItemClick as ChartsVoronoiHandlerProps['onItemClick'],
@@ -116,7 +114,6 @@ export const useScatterChartProps = (props: ScatterChartProps) => {
return {
chartsWrapperProps,
chartContainerProps,
- zAxisProps,
voronoiHandlerProps,
chartsAxisProps,
gridProps,
diff --git a/packages/x-charts/src/context/ChartDataProvider/ChartDataProvider.tsx b/packages/x-charts/src/context/ChartDataProvider/ChartDataProvider.tsx
index dc0ea9c6f8ef0..a2c4752fd3d71 100644
--- a/packages/x-charts/src/context/ChartDataProvider/ChartDataProvider.tsx
+++ b/packages/x-charts/src/context/ChartDataProvider/ChartDataProvider.tsx
@@ -3,25 +3,22 @@ import * as React from 'react';
import PropTypes from 'prop-types';
import { useChartDataProviderProps } from './useChartDataProviderProps';
import { AnimationProvider, AnimationProviderProps } from '../AnimationProvider';
-import { ZAxisContextProvider, ZAxisContextProviderProps } from '../ZAxisContextProvider';
import { HighlightedProvider, HighlightedProviderProps } from '../HighlightedProvider';
import { ChartProvider, ChartProviderProps } from '../ChartProvider';
import { ChartSeriesType } from '../../models/seriesType/config';
import { ChartAnyPluginSignature } from '../../internals/plugins/models/plugin';
-import { UseChartCartesianAxisSignature } from '../../internals/plugins/featurePlugins/useChartCartesianAxis';
-import { UseChartInteractionSignature } from '../../internals/plugins/featurePlugins/useChartInteraction';
+import { AllPluginSignatures } from '../../internals/plugins/allPlugins';
export type ChartDataProviderProps<
- TSignatures extends readonly ChartAnyPluginSignature[],
TSeries extends ChartSeriesType = ChartSeriesType,
+ TSignatures extends readonly ChartAnyPluginSignature[] = AllPluginSignatures,
> = Omit<
- ZAxisContextProviderProps &
- HighlightedProviderProps &
+ HighlightedProviderProps &
AnimationProviderProps &
- ChartProviderProps['pluginParams'],
+ ChartProviderProps['pluginParams'],
'children'
> &
- Pick, 'seriesConfig' | 'plugins'> & {
+ Pick, 'seriesConfig' | 'plugins'> & {
children?: React.ReactNode;
};
@@ -54,26 +51,16 @@ export type ChartDataProviderProps<
*/
function ChartDataProvider<
TSeries extends ChartSeriesType = ChartSeriesType,
- TSignatures extends readonly ChartAnyPluginSignature[] = [
- UseChartCartesianAxisSignature,
- UseChartInteractionSignature,
- ],
->(props: ChartDataProviderProps) {
- const {
- children,
- zAxisContextProps,
- highlightedProviderProps,
- animationProviderProps,
- chartProviderProps,
- } = useChartDataProviderProps(props);
+ TSignatures extends readonly ChartAnyPluginSignature[] = AllPluginSignatures,
+>(props: ChartDataProviderProps) {
+ const { children, highlightedProviderProps, animationProviderProps, chartProviderProps } =
+ useChartDataProviderProps(props);
return (
- {...chartProviderProps}>
-
-
- {children}
-
-
+ {...chartProviderProps}>
+
+ {children}
+
);
}
@@ -90,9 +77,6 @@ ChartDataProvider.propTypes = {
* @default blueberryTwilightPalette
*/
colors: PropTypes.any,
- /**
- * An array of objects that can be used to populate series and axes data using their `dataKey` property.
- */
dataset: PropTypes.any,
/**
* The height of the chart in px. If not defined, it takes the height of the parent element.
@@ -135,10 +119,6 @@ ChartDataProvider.propTypes = {
* The width of the chart in px. If not defined, it takes the width of the parent element.
*/
width: PropTypes.any,
- /**
- * The configuration of the z-axes.
- */
- zAxis: PropTypes.any,
} as any;
export { ChartDataProvider };
diff --git a/packages/x-charts/src/context/ChartDataProvider/useChartDataProviderProps.ts b/packages/x-charts/src/context/ChartDataProvider/useChartDataProviderProps.ts
index ab4b65e871ae0..6905d4c2b0937 100644
--- a/packages/x-charts/src/context/ChartDataProvider/useChartDataProviderProps.ts
+++ b/packages/x-charts/src/context/ChartDataProvider/useChartDataProviderProps.ts
@@ -1,6 +1,5 @@
'use client';
import { useTheme } from '@mui/material/styles';
-import type { ZAxisContextProviderProps } from '../ZAxisContextProvider';
import type { ChartDataProviderProps } from './ChartDataProvider';
import { HighlightedProviderProps } from '../HighlightedProvider';
import { AnimationProviderProps } from '../AnimationProvider';
@@ -8,12 +7,13 @@ import { ChartProviderProps } from '../ChartProvider';
import { ChartAnyPluginSignature, MergeSignaturesProperty } from '../../internals/plugins/models';
import { ChartSeriesType } from '../../models/seriesType/config';
import { ChartCorePluginSignatures } from '../../internals/plugins/corePlugins';
+import { AllPluginSignatures } from '../../internals/plugins/allPlugins';
export const useChartDataProviderProps = <
- TSignatures extends readonly ChartAnyPluginSignature[],
TSeries extends ChartSeriesType = ChartSeriesType,
+ TSignatures extends readonly ChartAnyPluginSignature[] = AllPluginSignatures,
>(
- props: ChartDataProviderProps,
+ props: ChartDataProviderProps,
) => {
const {
apiRef,
@@ -21,7 +21,6 @@ export const useChartDataProviderProps = <
height,
series,
margin,
- zAxis,
colors,
dataset,
highlightedItem,
@@ -35,7 +34,7 @@ export const useChartDataProviderProps = <
const theme = useTheme();
- const chartProviderProps: Omit, 'children'> = {
+ const chartProviderProps: Omit, 'children'> = {
plugins,
seriesConfig,
pluginParams: {
@@ -58,11 +57,6 @@ export const useChartDataProviderProps = <
skipAnimation,
};
- const zAxisContextProps: Omit = {
- zAxis,
- dataset,
- };
-
const highlightedProviderProps: Omit = {
highlightedItem,
onHighlightChange,
@@ -70,7 +64,6 @@ export const useChartDataProviderProps = <
return {
children,
- zAxisContextProps,
highlightedProviderProps,
animationProviderProps,
chartProviderProps,
diff --git a/packages/x-charts/src/context/ChartProvider/ChartProvider.tsx b/packages/x-charts/src/context/ChartProvider/ChartProvider.tsx
index 2bc17b87e380a..a7a8e623f4279 100644
--- a/packages/x-charts/src/context/ChartProvider/ChartProvider.tsx
+++ b/packages/x-charts/src/context/ChartProvider/ChartProvider.tsx
@@ -10,6 +10,7 @@ import {
import { ChartSeriesConfig } from '../../internals/plugins/models/seriesConfig';
import { useChartCartesianAxis } from '../../internals/plugins/featurePlugins/useChartCartesianAxis';
import { useChartInteraction } from '../../internals/plugins/featurePlugins/useChartInteraction';
+import { useChartZAxis } from '../../internals/plugins/featurePlugins/useChartZAxis';
import { plugin as barPlugin } from '../../BarChart/plugin';
import { plugin as scatterPlugin } from '../../ScatterChart/plugin';
import { plugin as linePlugin } from '../../LineChart/plugin';
@@ -25,12 +26,12 @@ export const defaultSeriesConfig: ChartSeriesConfig<'bar' | 'scatter' | 'line' |
// For consistency with the v7, the cartesian axes are set by default.
// To remove them, you can provide a `plugins` props.
-const defaultPlugins = [useChartCartesianAxis, useChartInteraction];
+const defaultPlugins = [useChartZAxis, useChartCartesianAxis, useChartInteraction];
function ChartProvider<
- TSignatures extends readonly ChartAnyPluginSignature[],
TSeriesType extends ChartSeriesType,
->(props: ChartProviderProps) {
+ TSignatures extends readonly ChartAnyPluginSignature[],
+>(props: ChartProviderProps) {
const {
children,
plugins = defaultPlugins as unknown as ConvertSignaturesIntoPlugins,
@@ -38,7 +39,7 @@ function ChartProvider<
seriesConfig = defaultSeriesConfig as ChartSeriesConfig,
} = props;
- const { contextValue } = useCharts(plugins, pluginParams, seriesConfig);
+ const { contextValue } = useCharts(plugins, pluginParams, seriesConfig);
return {children};
}
diff --git a/packages/x-charts/src/context/ChartProvider/ChartProvider.types.ts b/packages/x-charts/src/context/ChartProvider/ChartProvider.types.ts
index 250ed16ae325e..da2c8eef42adb 100644
--- a/packages/x-charts/src/context/ChartProvider/ChartProvider.types.ts
+++ b/packages/x-charts/src/context/ChartProvider/ChartProvider.types.ts
@@ -9,6 +9,7 @@ import {
import { ChartStore } from '../../internals/plugins/utils/ChartStore';
import { ChartCorePluginSignatures } from '../../internals/plugins/corePlugins';
import { ChartSeriesConfig } from '../../internals/plugins/models/seriesConfig';
+import { AllPluginSignatures } from '../../internals/plugins/allPlugins';
import { UseChartBaseProps } from '../../internals/store/useCharts.types';
import { ChartSeriesType } from '../../models/seriesType/config';
@@ -35,8 +36,8 @@ export type ChartContextValue<
};
export interface ChartProviderProps<
- TSignatures extends readonly ChartAnyPluginSignature[],
TSeries extends ChartSeriesType = ChartSeriesType,
+ TSignatures extends readonly ChartAnyPluginSignature[] = AllPluginSignatures,
> {
/**
* Array of plugins used to add features to the chart.
diff --git a/packages/x-charts/src/context/ZAxisContextProvider.tsx b/packages/x-charts/src/context/ZAxisContextProvider.tsx
deleted file mode 100644
index 67763d9f7cc68..0000000000000
--- a/packages/x-charts/src/context/ZAxisContextProvider.tsx
+++ /dev/null
@@ -1,141 +0,0 @@
-'use client';
-import * as React from 'react';
-import PropTypes from 'prop-types';
-import { MakeOptional } from '@mui/x-internals/types';
-import { DatasetType } from '../models/seriesType/config';
-import { getColorScale, getOrdinalColorScale } from '../internals/colorScale';
-import { ZAxisConfig, ZAxisDefaultized } from '../models/z-axis';
-
-export type ZAxisContextProviderProps = {
- /**
- * The configuration of the z-axes.
- */
- zAxis?: MakeOptional[];
- /**
- * An array of objects that can be used to populate series and axes data using their `dataKey` property.
- */
- dataset?: DatasetType;
- children: React.ReactNode;
-};
-
-type DefaultizedZAxisConfig = {
- [axisId: string]: ZAxisDefaultized;
-};
-
-export const ZAxisContext = React.createContext<{
- /**
- * Mapping from z-axis key to scaling configuration.
- */
- zAxis: DefaultizedZAxisConfig;
- /**
- * The z-axes IDs sorted by order they got provided.
- */
- zAxisIds: string[];
-}>({ zAxis: {}, zAxisIds: [] });
-
-if (process.env.NODE_ENV !== 'production') {
- ZAxisContext.displayName = 'ZAxisContext';
-}
-
-function ZAxisContextProvider(props: ZAxisContextProviderProps) {
- const { zAxis: inZAxis, dataset, children } = props;
-
- const zAxis = React.useMemo(
- () =>
- inZAxis?.map((axisConfig) => {
- const dataKey = axisConfig.dataKey;
- if (dataKey === undefined || axisConfig.data !== undefined) {
- return axisConfig;
- }
- if (dataset === undefined) {
- throw new Error('MUI X: z-axis uses `dataKey` but no `dataset` is provided.');
- }
- return {
- ...axisConfig,
- data: dataset.map((d) => d[dataKey]),
- };
- }),
- [inZAxis, dataset],
- );
-
- const value = React.useMemo(() => {
- const allZAxis: ZAxisConfig[] =
- zAxis?.map((axis, index) => ({ id: `defaultized-z-axis-${index}`, ...axis })) ?? [];
-
- const completedZAxis: DefaultizedZAxisConfig = {};
- allZAxis.forEach((axis) => {
- completedZAxis[axis.id] = {
- ...axis,
- colorScale:
- axis.colorMap &&
- (axis.colorMap.type === 'ordinal' && axis.data
- ? getOrdinalColorScale({ values: axis.data, ...axis.colorMap })
- : getColorScale(
- axis.colorMap.type === 'continuous'
- ? { min: axis.min, max: axis.max, ...axis.colorMap }
- : axis.colorMap,
- )),
- };
- });
-
- return {
- zAxis: completedZAxis,
- zAxisIds: allZAxis.map(({ id }) => id),
- };
- }, [zAxis]);
-
- return {children};
-}
-
-ZAxisContextProvider.propTypes = {
- // ----------------------------- Warning --------------------------------
- // | These PropTypes are generated from the TypeScript type definitions |
- // | To update them edit the TypeScript types and run "pnpm proptypes" |
- // ----------------------------------------------------------------------
- children: PropTypes.node,
- /**
- * An array of objects that can be used to populate series and axes data using their `dataKey` property.
- */
- dataset: PropTypes.arrayOf(PropTypes.object),
- /**
- * The configuration of the z-axes.
- */
- zAxis: PropTypes.arrayOf(
- PropTypes.shape({
- colorMap: PropTypes.oneOfType([
- PropTypes.shape({
- colors: PropTypes.arrayOf(PropTypes.string).isRequired,
- type: PropTypes.oneOf(['ordinal']).isRequired,
- unknownColor: PropTypes.string,
- values: PropTypes.arrayOf(
- PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number, PropTypes.string])
- .isRequired,
- ),
- }),
- PropTypes.shape({
- color: PropTypes.oneOfType([
- PropTypes.arrayOf(PropTypes.string.isRequired),
- PropTypes.func,
- ]).isRequired,
- max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]),
- min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]),
- type: PropTypes.oneOf(['continuous']).isRequired,
- }),
- PropTypes.shape({
- colors: PropTypes.arrayOf(PropTypes.string).isRequired,
- thresholds: PropTypes.arrayOf(
- PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]).isRequired,
- ).isRequired,
- type: PropTypes.oneOf(['piecewise']).isRequired,
- }),
- ]),
- data: PropTypes.array,
- dataKey: PropTypes.string,
- id: PropTypes.string,
- max: PropTypes.number,
- min: PropTypes.number,
- }),
- ),
-} as any;
-
-export { ZAxisContextProvider };
diff --git a/packages/x-charts/src/context/index.ts b/packages/x-charts/src/context/index.ts
index c95a0ef9fb675..c3aa10f8cdee3 100644
--- a/packages/x-charts/src/context/index.ts
+++ b/packages/x-charts/src/context/index.ts
@@ -1,5 +1,3 @@
export * from './HighlightedProvider';
-export { ZAxisContextProvider } from './ZAxisContextProvider';
-export type { ZAxisContextProviderProps } from './ZAxisContextProvider';
export { ChartDataProvider } from './ChartDataProvider';
export type { ChartDataProviderProps } from './ChartDataProvider';
diff --git a/packages/x-charts/src/hooks/index.ts b/packages/x-charts/src/hooks/index.ts
index 23b19a9b0780e..9bbe0ac753cef 100644
--- a/packages/x-charts/src/hooks/index.ts
+++ b/packages/x-charts/src/hooks/index.ts
@@ -2,6 +2,7 @@ export * from './useDrawingArea';
export * from './useChartId';
export * from './useScale';
export * from './useAxis';
+export * from './useZAxis';
export * from './useColorScale';
export * from './useSvgRef';
export {
diff --git a/packages/x-charts/src/hooks/useColorScale.ts b/packages/x-charts/src/hooks/useColorScale.ts
index 6ba810e43795d..76406894f13ce 100644
--- a/packages/x-charts/src/hooks/useColorScale.ts
+++ b/packages/x-charts/src/hooks/useColorScale.ts
@@ -1,7 +1,7 @@
'use client';
import { AxisScaleComputedConfig, ScaleName } from '../models/axis';
import { useXAxes, useYAxes } from './useAxis';
-import { useZAxis } from './useZAxis';
+import { useZAxes } from './useZAxis';
export function useXColorScale(
identifier?: number | string,
@@ -26,7 +26,7 @@ export function useYColorScale(
export function useZColorScale(
identifier?: number | string,
): AxisScaleComputedConfig[S]['colorScale'] | undefined {
- const { zAxis, zAxisIds } = useZAxis();
+ const { zAxis, zAxisIds } = useZAxes();
const id = typeof identifier === 'string' ? identifier : zAxisIds[identifier ?? 0];
diff --git a/packages/x-charts/src/hooks/useZAxis.ts b/packages/x-charts/src/hooks/useZAxis.ts
index 7f99fda885da5..1107f59f70c71 100644
--- a/packages/x-charts/src/hooks/useZAxis.ts
+++ b/packages/x-charts/src/hooks/useZAxis.ts
@@ -1,8 +1,25 @@
'use client';
-import * as React from 'react';
-import { ZAxisContext } from '../context/ZAxisContextProvider';
+import { useStore } from '../internals/store/useStore';
+import {
+ selectorChartZAxis,
+ UseChartZAxisSignature,
+} from '../internals/plugins/featurePlugins/useChartZAxis';
+import { useSelector } from '../internals/store/useSelector';
-export const useZAxis = () => {
- const data = React.useContext(ZAxisContext);
- return data;
-};
+export function useZAxes() {
+ const store = useStore<[UseChartZAxisSignature]>();
+ const { axis: zAxis, axisIds: zAxisIds } = useSelector(store, selectorChartZAxis) ?? {
+ axis: {},
+ axisIds: [],
+ };
+
+ return { zAxis, zAxisIds };
+}
+
+export function useZAxis(identifier?: number | string) {
+ const { zAxis, zAxisIds } = useZAxes();
+
+ const id = typeof identifier === 'string' ? identifier : zAxisIds[identifier ?? 0];
+
+ return zAxis[id];
+}
diff --git a/packages/x-charts/src/internals/components/ChartsAxesGradients/ChartsAxesGradients.tsx b/packages/x-charts/src/internals/components/ChartsAxesGradients/ChartsAxesGradients.tsx
index 972c8872f1007..c53087db8d28a 100644
--- a/packages/x-charts/src/internals/components/ChartsAxesGradients/ChartsAxesGradients.tsx
+++ b/packages/x-charts/src/internals/components/ChartsAxesGradients/ChartsAxesGradients.tsx
@@ -3,7 +3,7 @@ import { useDrawingArea, useXAxes, useYAxes } from '../../../hooks';
import ChartsPiecewiseGradient from './ChartsPiecewiseGradient';
import ChartsContinuousGradient from './ChartsContinuousGradient';
import ChartsContinuousGradientObjectBound from './ChartsContinuousGradientObjectBound';
-import { useZAxis } from '../../../hooks/useZAxis';
+import { useZAxes } from '../../../hooks/useZAxis';
import {
useChartGradientIdBuilder,
useChartGradientIdObjectBoundBuilder,
@@ -20,7 +20,7 @@ export function ChartsAxesGradients() {
const { xAxis, xAxisIds } = useXAxes();
const { yAxis, yAxisIds } = useYAxes();
- const { zAxisIds, zAxis } = useZAxis();
+ const { zAxis, zAxisIds } = useZAxes();
const filteredYAxisIds = yAxisIds.filter((axisId) => yAxis[axisId].colorMap !== undefined);
const filteredXAxisIds = xAxisIds.filter((axisId) => xAxis[axisId].colorMap !== undefined);
diff --git a/packages/x-charts/src/internals/index.ts b/packages/x-charts/src/internals/index.ts
index 8b08bc54f2691..f970e13c70c87 100644
--- a/packages/x-charts/src/internals/index.ts
+++ b/packages/x-charts/src/internals/index.ts
@@ -17,6 +17,7 @@ export * from '../context/ChartDataProvider/useChartDataProviderProps';
export * from './plugins/corePlugins/useChartId';
export * from './plugins/corePlugins/useChartSeries';
export * from './plugins/corePlugins/useChartDimensions';
+export * from './plugins/featurePlugins/useChartZAxis';
export * from './plugins/featurePlugins/useChartCartesianAxis';
export * from './plugins/featurePlugins/useChartInteraction';
export * from './plugins/utils/selectors';
@@ -36,7 +37,6 @@ export * from './getScale';
// contexts
-export * from '../context/ZAxisContextProvider';
export * from '../context/AnimationProvider';
export type * from '../context/context.types';
export { getAxisExtremum } from './plugins/featurePlugins/useChartCartesianAxis/getAxisExtremum';
diff --git a/packages/x-charts/src/internals/plugins/allPlugins.ts b/packages/x-charts/src/internals/plugins/allPlugins.ts
index 1fd0a05f57325..e5af78e20ddc1 100644
--- a/packages/x-charts/src/internals/plugins/allPlugins.ts
+++ b/packages/x-charts/src/internals/plugins/allPlugins.ts
@@ -1,4 +1,23 @@
// This file should be removed after creating all plugins in favor of a file per chart type.
-import { useChartInteraction } from './featurePlugins/useChartInteraction';
+import { ChartSeriesType } from '../../models/seriesType/config';
+import {
+ useChartCartesianAxis,
+ UseChartCartesianAxisSignature,
+} from './featurePlugins/useChartCartesianAxis';
+import {
+ useChartInteraction,
+ UseChartInteractionSignature,
+} from './featurePlugins/useChartInteraction';
+import { useChartZAxis, UseChartZAxisSignature } from './featurePlugins/useChartZAxis';
+import { ConvertSignaturesIntoPlugins } from './models/helpers';
-export const ALL_PLUGINS = [useChartInteraction] as const;
+export type AllPluginSignatures = [
+ UseChartZAxisSignature,
+ UseChartCartesianAxisSignature,
+ UseChartInteractionSignature,
+];
+
+export type AllPluginsType =
+ ConvertSignaturesIntoPlugins>;
+
+export const ALL_PLUGINS = [useChartZAxis, useChartCartesianAxis, useChartInteraction];
diff --git a/packages/x-charts/src/internals/plugins/featurePlugins/useChartZAxis/index.ts b/packages/x-charts/src/internals/plugins/featurePlugins/useChartZAxis/index.ts
new file mode 100644
index 0000000000000..f6cc1b444b1b6
--- /dev/null
+++ b/packages/x-charts/src/internals/plugins/featurePlugins/useChartZAxis/index.ts
@@ -0,0 +1,3 @@
+export { useChartZAxis } from './useChartZAxis';
+export type * from './useChartZAxis.types';
+export * from './useChartZAxis.selectors';
diff --git a/packages/x-charts/src/internals/plugins/featurePlugins/useChartZAxis/useChartZAxis.selectors.ts b/packages/x-charts/src/internals/plugins/featurePlugins/useChartZAxis/useChartZAxis.selectors.ts
new file mode 100644
index 0000000000000..e670f79866478
--- /dev/null
+++ b/packages/x-charts/src/internals/plugins/featurePlugins/useChartZAxis/useChartZAxis.selectors.ts
@@ -0,0 +1,7 @@
+import { ChartState } from '../../models/chart';
+import { createSelector } from '../../utils/selectors';
+import { UseChartZAxisSignature } from './useChartZAxis.types';
+
+const selectRootState = (state: ChartState<[UseChartZAxisSignature]>) => state;
+
+export const selectorChartZAxis = createSelector([selectRootState], (state) => state.zAxis);
diff --git a/packages/x-charts/src/internals/plugins/featurePlugins/useChartZAxis/useChartZAxis.ts b/packages/x-charts/src/internals/plugins/featurePlugins/useChartZAxis/useChartZAxis.ts
new file mode 100644
index 0000000000000..08c68cf0be353
--- /dev/null
+++ b/packages/x-charts/src/internals/plugins/featurePlugins/useChartZAxis/useChartZAxis.ts
@@ -0,0 +1,100 @@
+'use client';
+import * as React from 'react';
+import { MakeOptional } from '@mui/x-internals/types';
+import { ChartPlugin } from '../../models';
+import { DatasetType } from '../../../../models/seriesType/config';
+import { UseChartZAxisSignature } from './useChartZAxis.types';
+import { ZAxisConfig, ZAxisDefaultized } from '../../../../models/z-axis';
+import { getColorScale, getOrdinalColorScale } from '../../../colorScale';
+
+function addDefaultId(axisConfig: MakeOptional, defaultId: string): ZAxisConfig {
+ if (axisConfig.id !== undefined) {
+ return axisConfig as ZAxisConfig;
+ }
+ return {
+ id: defaultId,
+ ...axisConfig,
+ };
+}
+
+function processColorMap(axisConfig: ZAxisConfig) {
+ if (!axisConfig.colorMap) {
+ return axisConfig;
+ }
+
+ return {
+ ...axisConfig,
+ colorScale:
+ axisConfig.colorMap.type === 'ordinal' && axisConfig.data
+ ? getOrdinalColorScale({ values: axisConfig.data, ...axisConfig.colorMap })
+ : getColorScale(
+ axisConfig.colorMap.type === 'continuous'
+ ? { min: axisConfig.min, max: axisConfig.max, ...axisConfig.colorMap }
+ : axisConfig.colorMap,
+ ),
+ };
+}
+
+function getZAxisState(zAxis?: MakeOptional[], dataset?: DatasetType) {
+ if (!zAxis || zAxis.length === 0) {
+ return { axis: {}, axisIds: [] };
+ }
+
+ const zAxisLookup: Record = {};
+ const axisIds: string[] = [];
+
+ zAxis.forEach((axisConfig, index) => {
+ const dataKey = axisConfig.dataKey;
+ const defaultizedId = axisConfig.id ?? `defaultized-z-axis-${index}`;
+ if (dataKey === undefined || axisConfig.data !== undefined) {
+ zAxisLookup[defaultizedId] = processColorMap(addDefaultId(axisConfig, defaultizedId));
+ axisIds.push(defaultizedId);
+ return;
+ }
+ if (dataset === undefined) {
+ throw new Error('MUI X: z-axis uses `dataKey` but no `dataset` is provided.');
+ }
+ zAxisLookup[defaultizedId] = processColorMap(
+ addDefaultId(
+ {
+ ...axisConfig,
+ data: dataset.map((d) => d[dataKey]),
+ },
+ defaultizedId,
+ ),
+ );
+ axisIds.push(defaultizedId);
+ });
+
+ return { axis: zAxisLookup, axisIds };
+}
+
+export const useChartZAxis: ChartPlugin = ({ params, store }) => {
+ const { zAxis, dataset } = params;
+
+ // The effect do not track any value defined synchronously during the 1st render by hooks called after `useChartZAxis`
+ // As a consequence, the state generated by the 1st run of this useEffect will always be equal to the initialization one
+ const isFirstRender = React.useRef(true);
+ React.useEffect(() => {
+ if (isFirstRender.current) {
+ isFirstRender.current = false;
+ return;
+ }
+
+ store.update((prev) => ({
+ ...prev,
+ zAxis: getZAxisState(zAxis, dataset),
+ }));
+ }, [zAxis, dataset, store]);
+
+ return {};
+};
+
+useChartZAxis.params = {
+ zAxis: true,
+ dataset: true,
+};
+
+useChartZAxis.getInitialState = (params) => ({
+ zAxis: getZAxisState(params.zAxis, params.dataset),
+});
diff --git a/packages/x-charts/src/internals/plugins/featurePlugins/useChartZAxis/useChartZAxis.types.ts b/packages/x-charts/src/internals/plugins/featurePlugins/useChartZAxis/useChartZAxis.types.ts
new file mode 100644
index 0000000000000..6c91aa9320caf
--- /dev/null
+++ b/packages/x-charts/src/internals/plugins/featurePlugins/useChartZAxis/useChartZAxis.types.ts
@@ -0,0 +1,43 @@
+import { MakeOptional } from '@mui/x-internals/types';
+import { ChartPluginSignature } from '../../models';
+import { DatasetType } from '../../../../models/seriesType/config';
+import { AxisId } from '../../../../models/axis';
+import { ZAxisConfig, ZAxisDefaultized } from '../../../../models/z-axis';
+
+type DefaultizedZAxisConfig = {
+ [axisId: string]: ZAxisDefaultized;
+};
+
+export interface UseChartZAxisParameters {
+ /**
+ * The configuration of the z-axes.
+ */
+ zAxis?: MakeOptional[];
+ /**
+ * An array of objects that can be used to populate series and axes data using their `dataKey` property.
+ */
+ dataset?: DatasetType;
+}
+
+export type UseChartZAxisDefaultizedParameters = UseChartZAxisParameters;
+
+export interface UseChartZAxisState {
+ zAxis: {
+ /**
+ * Mapping from z-axis key to scaling configuration.
+ */
+ axis: DefaultizedZAxisConfig;
+ /**
+ * The z-axes IDs sorted by order they got provided.
+ */
+ axisIds: AxisId[];
+ };
+}
+
+export interface UseChartZAxisInstance {}
+
+export type UseChartZAxisSignature = ChartPluginSignature<{
+ params: UseChartZAxisParameters;
+ defaultizedParams: UseChartZAxisDefaultizedParameters;
+ state: UseChartZAxisState;
+}>;
diff --git a/packages/x-charts/src/internals/plugins/featurePlugins/useChartZAxis/utils.ts b/packages/x-charts/src/internals/plugins/featurePlugins/useChartZAxis/utils.ts
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/packages/x-charts/src/internals/store/useCharts.ts b/packages/x-charts/src/internals/store/useCharts.ts
index f78a365697f5c..8d25137a9217a 100644
--- a/packages/x-charts/src/internals/store/useCharts.ts
+++ b/packages/x-charts/src/internals/store/useCharts.ts
@@ -36,8 +36,8 @@ export function useChartApiInitialization(
let globalId = 0;
export function useCharts<
- TSignatures extends readonly ChartAnyPluginSignature[],
TSeriesType extends ChartSeriesType,
+ TSignatures extends readonly ChartAnyPluginSignature[],
>(
inPlugins: ConvertSignaturesIntoPlugins,
props: Partial>,
diff --git a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/DesktopDateRangePicker.test.tsx b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/DesktopDateRangePicker.test.tsx
index a79441429611e..2ad4f2155adf4 100644
--- a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/DesktopDateRangePicker.test.tsx
+++ b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/DesktopDateRangePicker.test.tsx
@@ -35,14 +35,14 @@ describe('', () => {
/>,
);
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
expect(screen.getByText('May 2019')).toBeVisible();
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'end' });
+ openPicker({ type: 'date-range', initialFocus: 'end' });
expect(screen.getByText('October 2019')).toBeVisible();
// scroll back
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
expect(screen.getByText('May 2019')).toBeVisible();
});
@@ -51,7 +51,7 @@ describe('', () => {
,
);
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
expect(screen.getByRole('tooltip')).toBeVisible();
});
@@ -167,7 +167,7 @@ describe('', () => {
render();
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
expect(onOpen.callCount).to.equal(1);
expect(screen.getByRole('tooltip')).toBeVisible();
@@ -178,7 +178,7 @@ describe('', () => {
render();
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'end' });
+ openPicker({ type: 'date-range', initialFocus: 'end' });
expect(onOpen.callCount).to.equal(1);
expect(screen.getByRole('tooltip')).toBeVisible();
@@ -235,7 +235,7 @@ describe('', () => {
);
// Open the picker
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
expect(onChange.callCount).to.equal(0);
expect(onAccept.callCount).to.equal(0);
expect(onClose.callCount).to.equal(0);
@@ -277,7 +277,7 @@ describe('', () => {
);
// Open the picker
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'end' });
+ openPicker({ type: 'date-range', initialFocus: 'end' });
expect(onChange.callCount).to.equal(0);
expect(onAccept.callCount).to.equal(0);
expect(onClose.callCount).to.equal(0);
@@ -310,7 +310,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'end' });
+ openPicker({ type: 'date-range', initialFocus: 'end' });
// Change the end date
fireEvent.click(getPickerDay('3'));
@@ -337,7 +337,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
// Change the start date (already tested)
fireEvent.click(getPickerDay('3'));
@@ -364,7 +364,7 @@ describe('', () => {
,
);
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
// Dismiss the picker
const input = document.getElementById('test-id')!;
@@ -402,7 +402,7 @@ describe('', () => {
,
);
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
// Change the start date (already tested)
fireEvent.click(getPickerDay('3'));
@@ -458,7 +458,7 @@ describe('', () => {
,
);
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
expect(screen.getByRole('tooltip')).toBeVisible();
document.querySelector('#test')!.focus();
@@ -491,7 +491,7 @@ describe('', () => {
,
);
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
expect(screen.getByRole('tooltip')).toBeVisible();
// Change the start date (already tested)
@@ -529,7 +529,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
// Clear the date
fireEvent.click(screen.getByText(/clear/i));
@@ -555,7 +555,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
// Clear the date
fireEvent.click(screen.getByText(/clear/i));
@@ -582,10 +582,10 @@ describe('', () => {
);
// Open the picker (already tested)
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
// Switch to end date
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'end' });
+ openPicker({ type: 'date-range', initialFocus: 'end' });
expect(onChange.callCount).to.equal(0);
expect(onAccept.callCount).to.equal(0);
expect(onClose.callCount).to.equal(0);
@@ -606,10 +606,10 @@ describe('', () => {
);
// Open the picker (already tested)
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'end' });
+ openPicker({ type: 'date-range', initialFocus: 'end' });
// Switch to start date
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
expect(onChange.callCount).to.equal(0);
expect(onAccept.callCount).to.equal(0);
expect(onClose.callCount).to.equal(0);
@@ -620,7 +620,7 @@ describe('', () => {
it('should respect the disablePast prop', () => {
render();
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
expect(getPickerDay('8')).to.have.attribute('disabled');
expect(getPickerDay('9')).to.have.attribute('disabled');
@@ -632,7 +632,7 @@ describe('', () => {
it('should respect the disableFuture prop', () => {
render();
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
expect(getPickerDay('8')).not.to.have.attribute('disabled');
expect(getPickerDay('9')).not.to.have.attribute('disabled');
@@ -644,7 +644,7 @@ describe('', () => {
it('should respect the minDate prop', () => {
render();
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
expect(getPickerDay('13')).to.have.attribute('disabled');
expect(getPickerDay('14')).to.have.attribute('disabled');
@@ -656,7 +656,7 @@ describe('', () => {
it('should respect the maxDate prop', () => {
render();
- openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
expect(getPickerDay('13')).not.to.have.attribute('disabled');
expect(getPickerDay('14')).not.to.have.attribute('disabled');
diff --git a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/describes.DesktopDateRangePicker.test.tsx b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/describes.DesktopDateRangePicker.test.tsx
index f9255bdd65af6..0ece845e39397 100644
--- a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/describes.DesktopDateRangePicker.test.tsx
+++ b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/describes.DesktopDateRangePicker.test.tsx
@@ -28,6 +28,7 @@ describe(' - Describes', () => {
clock,
componentFamily: 'picker',
views: ['day'],
+ variant: 'desktop',
}));
describeConformance(, () => ({
diff --git a/packages/x-date-pickers-pro/src/DesktopDateTimeRangePicker/tests/DesktopDateTimeRangePicker.test.tsx b/packages/x-date-pickers-pro/src/DesktopDateTimeRangePicker/tests/DesktopDateTimeRangePicker.test.tsx
index e8537c5cfc1d3..4dea6eb3e1865 100644
--- a/packages/x-date-pickers-pro/src/DesktopDateTimeRangePicker/tests/DesktopDateTimeRangePicker.test.tsx
+++ b/packages/x-date-pickers-pro/src/DesktopDateTimeRangePicker/tests/DesktopDateTimeRangePicker.test.tsx
@@ -20,7 +20,7 @@ describe('', () => {
it('should allow to select range within the same day', () => {
render();
- openPicker({ type: 'date-time-range', variant: 'desktop', initialFocus: 'start' });
+ openPicker({ type: 'date-time-range', initialFocus: 'start' });
// select start date range
fireEvent.click(screen.getByRole('gridcell', { name: '11' }));
@@ -45,7 +45,7 @@ describe('', () => {
,
);
- openPicker({ type: 'date-time-range', variant: 'desktop', initialFocus: 'start' });
+ openPicker({ type: 'date-time-range', initialFocus: 'start' });
fireEvent.click(screen.getByRole('gridcell', { name: '11' }));
diff --git a/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/MobileDateRangePicker.test.tsx b/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/MobileDateRangePicker.test.tsx
index 83fee1c9814b9..83e2457bd23bd 100644
--- a/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/MobileDateRangePicker.test.tsx
+++ b/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/MobileDateRangePicker.test.tsx
@@ -44,7 +44,7 @@ describe('', () => {
render();
- openPicker({ type: 'date-range', variant: 'mobile', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
expect(onOpen.callCount).to.equal(1);
expect(screen.queryByRole('dialog')).toBeVisible();
@@ -55,7 +55,7 @@ describe('', () => {
render();
- openPicker({ type: 'date-range', variant: 'mobile', initialFocus: 'end' });
+ openPicker({ type: 'date-range', initialFocus: 'end' });
expect(onOpen.callCount).to.equal(1);
expect(screen.queryByRole('dialog')).toBeVisible();
@@ -80,7 +80,7 @@ describe('', () => {
);
// Open the picker
- openPicker({ type: 'date-range', variant: 'mobile', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
expect(onChange.callCount).to.equal(0);
expect(onAccept.callCount).to.equal(0);
expect(onClose.callCount).to.equal(0);
@@ -120,7 +120,7 @@ describe('', () => {
);
// Open the picker
- openPicker({ type: 'date-range', variant: 'mobile', initialFocus: 'end' });
+ openPicker({ type: 'date-range', initialFocus: 'end' });
expect(onChange.callCount).to.equal(0);
expect(onAccept.callCount).to.equal(0);
expect(onClose.callCount).to.equal(0);
@@ -151,7 +151,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'date-range', variant: 'mobile', initialFocus: 'end' });
+ openPicker({ type: 'date-range', initialFocus: 'end' });
// Change the end date
fireEvent.click(screen.getByRole('gridcell', { name: '3' }));
@@ -181,7 +181,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'date-range', variant: 'mobile', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
// Change the start date (already tested)
fireEvent.click(screen.getByRole('gridcell', { name: '3' }));
@@ -213,7 +213,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'date-range', variant: 'mobile', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
// Change the start date (already tested)
fireEvent.click(screen.getByRole('gridcell', { name: '3' }));
@@ -246,7 +246,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'date-range', variant: 'mobile', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
// Clear the date
fireEvent.click(screen.getByText(/clear/i));
@@ -272,7 +272,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'date-range', variant: 'mobile', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
// Clear the date
fireEvent.click(screen.getByText(/clear/i));
diff --git a/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/describes.MobileDateRangePicker.test.tsx b/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/describes.MobileDateRangePicker.test.tsx
index 8304680152111..1b8a91aec129f 100644
--- a/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/describes.MobileDateRangePicker.test.tsx
+++ b/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/describes.MobileDateRangePicker.test.tsx
@@ -84,7 +84,7 @@ describe(' - Describes', () => {
}
if (!isOpened) {
- openPicker({ type: 'date-range', variant: 'mobile', initialFocus: 'start' });
+ openPicker({ type: 'date-range', initialFocus: 'start' });
}
fireEvent.click(
diff --git a/packages/x-date-pickers-pro/src/MobileDateTimeRangePicker/tests/describes.MobileDateTimeRangePicker.test.tsx b/packages/x-date-pickers-pro/src/MobileDateTimeRangePicker/tests/describes.MobileDateTimeRangePicker.test.tsx
index 90a5dc4057ec1..fe4f71d11525d 100644
--- a/packages/x-date-pickers-pro/src/MobileDateTimeRangePicker/tests/describes.MobileDateTimeRangePicker.test.tsx
+++ b/packages/x-date-pickers-pro/src/MobileDateTimeRangePicker/tests/describes.MobileDateTimeRangePicker.test.tsx
@@ -88,7 +88,7 @@ describe(' - Describes', () => {
if (!isOpened) {
openPicker({
type: 'date-time-range',
- variant: 'mobile',
+
initialFocus: setEndDate ? 'end' : 'start',
});
}
diff --git a/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx b/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx
index cb1441aca093c..6d58b8a4bb59b 100644
--- a/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx
+++ b/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx
@@ -12,10 +12,7 @@ import {
unstable_generateUtilityClass as generateUtilityClass,
unstable_generateUtilityClasses as generateUtilityClasses,
} from '@mui/utils';
-import {
- convertFieldResponseIntoMuiTextFieldProps,
- useFieldOwnerState,
-} from '@mui/x-date-pickers/internals';
+import { cleanFieldResponse, useFieldOwnerState } from '@mui/x-date-pickers/internals';
import { useSplitFieldProps } from '@mui/x-date-pickers/hooks';
import { PickersTextField } from '@mui/x-date-pickers/PickersTextField';
import {
@@ -163,8 +160,8 @@ const MultiInputDateRangeField = React.forwardRef(function MultiInputDateRangeFi
unstableEndFieldRef,
});
- const startDateProps = convertFieldResponseIntoMuiTextFieldProps(fieldResponse.startDate);
- const endDateProps = convertFieldResponseIntoMuiTextFieldProps(fieldResponse.endDate);
+ const { textFieldProps: startDateProps } = cleanFieldResponse(fieldResponse.startDate);
+ const { textFieldProps: endDateProps } = cleanFieldResponse(fieldResponse.endDate);
const TextField =
slots?.textField ??
diff --git a/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx b/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx
index 26557e1ed3fb5..d74e569613ef4 100644
--- a/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx
+++ b/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx
@@ -12,10 +12,7 @@ import {
unstable_generateUtilityClass as generateUtilityClass,
unstable_generateUtilityClasses as generateUtilityClasses,
} from '@mui/utils';
-import {
- convertFieldResponseIntoMuiTextFieldProps,
- useFieldOwnerState,
-} from '@mui/x-date-pickers/internals';
+import { cleanFieldResponse, useFieldOwnerState } from '@mui/x-date-pickers/internals';
import { useSplitFieldProps } from '@mui/x-date-pickers/hooks';
import { PickersTextField } from '@mui/x-date-pickers/PickersTextField';
import {
@@ -161,8 +158,8 @@ const MultiInputDateTimeRangeField = React.forwardRef(function MultiInputDateTim
unstableEndFieldRef,
});
- const startDateProps = convertFieldResponseIntoMuiTextFieldProps(fieldResponse.startDate);
- const endDateProps = convertFieldResponseIntoMuiTextFieldProps(fieldResponse.endDate);
+ const { textFieldProps: startDateProps } = cleanFieldResponse(fieldResponse.startDate);
+ const { textFieldProps: endDateProps } = cleanFieldResponse(fieldResponse.endDate);
const TextField =
slots?.textField ??
diff --git a/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.tsx b/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.tsx
index d2d9bb8e6bf92..f71baf65aac82 100644
--- a/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.tsx
+++ b/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.tsx
@@ -12,10 +12,7 @@ import {
unstable_generateUtilityClass as generateUtilityClass,
unstable_generateUtilityClasses as generateUtilityClasses,
} from '@mui/utils';
-import {
- convertFieldResponseIntoMuiTextFieldProps,
- useFieldOwnerState,
-} from '@mui/x-date-pickers/internals';
+import { cleanFieldResponse, useFieldOwnerState } from '@mui/x-date-pickers/internals';
import { useSplitFieldProps } from '@mui/x-date-pickers/hooks';
import { PickersTextField } from '@mui/x-date-pickers/PickersTextField';
import {
@@ -164,8 +161,8 @@ const MultiInputTimeRangeField = React.forwardRef(function MultiInputTimeRangeFi
unstableEndFieldRef,
});
- const startDateProps = convertFieldResponseIntoMuiTextFieldProps(fieldResponse.startDate);
- const endDateProps = convertFieldResponseIntoMuiTextFieldProps(fieldResponse.endDate);
+ const { textFieldProps: startDateProps } = cleanFieldResponse(fieldResponse.startDate);
+ const { textFieldProps: endDateProps } = cleanFieldResponse(fieldResponse.endDate);
const TextField =
slots?.textField ??
diff --git a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.tsx b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.tsx
index 26edd8cc6ad0c..5ff8e081f95a2 100644
--- a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.tsx
+++ b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.tsx
@@ -1,16 +1,10 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
-import MuiTextField from '@mui/material/TextField';
import { useThemeProps } from '@mui/material/styles';
-import useSlotProps from '@mui/utils/useSlotProps';
import { refType } from '@mui/utils';
-import { useClearableField } from '@mui/x-date-pickers/hooks';
-import {
- convertFieldResponseIntoMuiTextFieldProps,
- useFieldOwnerState,
-} from '@mui/x-date-pickers/internals';
-import { PickersTextField } from '@mui/x-date-pickers/PickersTextField';
+import { DateRangeIcon } from '@mui/x-date-pickers/icons';
+import { PickerFieldUI, useFieldTextFieldProps } from '@mui/x-date-pickers/internals';
import { SingleInputDateRangeFieldProps } from './SingleInputDateRangeField.types';
import { useSingleInputDateRangeField } from './useSingleInputDateRangeField';
import { FieldType } from '../models';
@@ -41,41 +35,29 @@ const SingleInputDateRangeField = React.forwardRef(function SingleInputDateRange
name: 'MuiSingleInputDateRangeField',
});
- const { slots, slotProps, InputProps, inputProps, ...other } = themeProps;
+ const { slots, slotProps, ...other } = themeProps;
- const ownerState = useFieldOwnerState(themeProps);
-
- const textFieldProps = useSlotProps({
- elementType: PickersTextField,
- externalSlotProps: slotProps?.textField,
+ const textFieldProps = useFieldTextFieldProps<
+ SingleInputDateRangeFieldProps
+ >({
+ slotProps,
+ ref: inRef,
externalForwardedProps: other,
- ownerState,
- additionalProps: {
- ref: inRef,
- },
- }) as SingleInputDateRangeFieldProps;
-
- // TODO: Remove when mui/material-ui#35088 will be merged
- textFieldProps.inputProps = { ...inputProps, ...textFieldProps.inputProps };
- textFieldProps.InputProps = { ...InputProps, ...textFieldProps.InputProps };
+ });
const fieldResponse = useSingleInputDateRangeField<
TEnableAccessibleFieldDOMStructure,
typeof textFieldProps
>(textFieldProps);
- const convertedFieldResponse = convertFieldResponseIntoMuiTextFieldProps(fieldResponse);
-
- const processedFieldProps = useClearableField({
- ...convertedFieldResponse,
- slots,
- slotProps,
- });
-
- const TextField =
- slots?.textField ??
- (fieldResponse.enableAccessibleFieldDOMStructure === false ? MuiTextField : PickersTextField);
- return ;
+ return (
+
+ );
}) as DateRangeFieldComponent;
SingleInputDateRangeField.fieldType = 'single-input';
@@ -96,6 +78,12 @@ SingleInputDateRangeField.propTypes = {
* @default false
*/
clearable: PropTypes.bool,
+ /**
+ * The position at which the clear button is placed.
+ * If the field is not clearable, the button is not rendered.
+ * @default 'end'
+ */
+ clearButtonPosition: PropTypes.oneOf(['end', 'start']),
/**
* The color of the component.
* It supports both default and custom theme colors, which can be added as shown in the
diff --git a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.types.ts b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.types.ts
index 027ffbfbdb1de..7828b1643c3c0 100644
--- a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.types.ts
+++ b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.types.ts
@@ -1,20 +1,16 @@
-import * as React from 'react';
-import { TextFieldProps } from '@mui/material/TextField';
-import { SlotComponentPropsFromProps } from '@mui/x-internals/types';
-import { PickerRangeValue, UseFieldInternalProps } from '@mui/x-date-pickers/internals';
-import { BuiltInFieldTextFieldProps, FieldOwnerState } from '@mui/x-date-pickers/models';
import {
- ExportedUseClearableFieldProps,
- UseClearableFieldSlots,
- UseClearableFieldSlotProps,
-} from '@mui/x-date-pickers/hooks';
-import { PickersTextFieldProps } from '@mui/x-date-pickers/PickersTextField';
+ PickerRangeValue,
+ UseFieldInternalProps,
+ ExportedPickerFieldUIProps,
+ PickerFieldUISlots,
+ PickerFieldUISlotProps,
+} from '@mui/x-date-pickers/internals';
+import { BuiltInFieldTextFieldProps } from '@mui/x-date-pickers/models';
import type { DateRangeValidationError, UseDateRangeFieldProps } from '../models';
export interface UseSingleInputDateRangeFieldProps<
TEnableAccessibleFieldDOMStructure extends boolean,
> extends UseDateRangeFieldProps,
- ExportedUseClearableFieldProps,
Pick<
UseFieldInternalProps<
PickerRangeValue,
@@ -22,7 +18,9 @@ export interface UseSingleInputDateRangeFieldProps<
DateRangeValidationError
>,
'unstableFieldRef'
- > {}
+ >,
+ // TODO v8: Remove once the range fields open with a button.
+ Omit {}
export type SingleInputDateRangeFieldProps<
TEnableAccessibleFieldDOMStructure extends boolean = true,
@@ -43,18 +41,6 @@ export type SingleInputDateRangeFieldProps<
slotProps?: SingleInputDateRangeFieldSlotProps;
};
-export interface SingleInputDateRangeFieldSlots extends UseClearableFieldSlots {
- /**
- * Form control with an input to render the value.
- * @default , or from '@mui/material' if `enableAccessibleFieldDOMStructure` is `false`.
- */
- textField?: React.ElementType;
-}
+export interface SingleInputDateRangeFieldSlots extends PickerFieldUISlots {}
-export interface SingleInputDateRangeFieldSlotProps extends UseClearableFieldSlotProps {
- textField?: SlotComponentPropsFromProps<
- PickersTextFieldProps | TextFieldProps,
- {},
- FieldOwnerState
- >;
-}
+export interface SingleInputDateRangeFieldSlotProps extends PickerFieldUISlotProps {}
diff --git a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/useSingleInputDateRangeField.ts b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/useSingleInputDateRangeField.ts
index 3148b6e40b37b..af4f32912bb52 100644
--- a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/useSingleInputDateRangeField.ts
+++ b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/useSingleInputDateRangeField.ts
@@ -33,5 +33,6 @@ export const useSingleInputDateRangeField = <
fieldValueManager: manager.internal_fieldValueManager,
validator: manager.validator,
valueType: manager.valueType,
+ getOpenPickerButtonAriaLabel: manager.internal_getOpenPickerButtonAriaLabel,
});
};
diff --git a/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.tsx b/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.tsx
index 74a4ea87c9550..f004f28093d36 100644
--- a/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.tsx
+++ b/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.tsx
@@ -1,16 +1,10 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
-import MuiTextField from '@mui/material/TextField';
-import {
- convertFieldResponseIntoMuiTextFieldProps,
- useFieldOwnerState,
-} from '@mui/x-date-pickers/internals';
-import { PickersTextField } from '@mui/x-date-pickers/PickersTextField';
+import { DateRangeIcon } from '@mui/x-date-pickers/icons';
+import { PickerFieldUI, useFieldTextFieldProps } from '@mui/x-date-pickers/internals';
import { useThemeProps } from '@mui/material/styles';
import { refType } from '@mui/utils';
-import useSlotProps from '@mui/utils/useSlotProps';
-import { useClearableField } from '@mui/x-date-pickers/hooks';
import { SingleInputDateTimeRangeFieldProps } from './SingleInputDateTimeRangeField.types';
import { useSingleInputDateTimeRangeField } from './useSingleInputDateTimeRangeField';
import { FieldType } from '../models';
@@ -41,41 +35,29 @@ const SingleInputDateTimeRangeField = React.forwardRef(function SingleInputDateT
name: 'MuiSingleInputDateTimeRangeField',
});
- const { slots, slotProps, InputProps, inputProps, ...other } = themeProps;
+ const { slots, slotProps, ...other } = themeProps;
- const ownerState = useFieldOwnerState(themeProps);
-
- const textFieldProps = useSlotProps({
- elementType: PickersTextField,
- externalSlotProps: slotProps?.textField,
+ const textFieldProps = useFieldTextFieldProps<
+ SingleInputDateTimeRangeFieldProps
+ >({
+ slotProps,
+ ref: inRef,
externalForwardedProps: other,
- ownerState,
- additionalProps: {
- ref: inRef,
- },
- }) as SingleInputDateTimeRangeFieldProps;
-
- // TODO: Remove when mui/material-ui#35088 will be merged
- textFieldProps.inputProps = { ...inputProps, ...textFieldProps.inputProps };
- textFieldProps.InputProps = { ...InputProps, ...textFieldProps.InputProps };
+ });
const fieldResponse = useSingleInputDateTimeRangeField<
TEnableAccessibleFieldDOMStructure,
typeof textFieldProps
>(textFieldProps);
- const convertedFieldResponse = convertFieldResponseIntoMuiTextFieldProps(fieldResponse);
-
- const processedFieldProps = useClearableField({
- ...convertedFieldResponse,
- slots,
- slotProps,
- });
-
- const TextField =
- slots?.textField ??
- (fieldResponse.enableAccessibleFieldDOMStructure === false ? MuiTextField : PickersTextField);
- return ;
+ return (
+
+ );
}) as DateRangeFieldComponent;
SingleInputDateTimeRangeField.fieldType = 'single-input';
@@ -101,6 +83,12 @@ SingleInputDateTimeRangeField.propTypes = {
* @default false
*/
clearable: PropTypes.bool,
+ /**
+ * The position at which the clear button is placed.
+ * If the field is not clearable, the button is not rendered.
+ * @default 'end'
+ */
+ clearButtonPosition: PropTypes.oneOf(['end', 'start']),
/**
* The color of the component.
* It supports both default and custom theme colors, which can be added as shown in the
diff --git a/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.types.ts b/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.types.ts
index 9624ffa1c73e0..d7f51a40420b5 100644
--- a/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.types.ts
+++ b/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.types.ts
@@ -1,21 +1,17 @@
-import * as React from 'react';
-import { TextFieldProps } from '@mui/material/TextField';
-import { SlotComponentPropsFromProps } from '@mui/x-internals/types';
-import { PickersTextFieldProps } from '@mui/x-date-pickers/PickersTextField';
-import { PickerRangeValue, UseFieldInternalProps } from '@mui/x-date-pickers/internals';
-import { BuiltInFieldTextFieldProps, FieldOwnerState } from '@mui/x-date-pickers/models';
import {
- ExportedUseClearableFieldProps,
- UseClearableFieldSlots,
- UseClearableFieldSlotProps,
-} from '@mui/x-date-pickers/hooks';
+ ExportedPickerFieldUIProps,
+ PickerFieldUISlots,
+ PickerFieldUISlotProps,
+ PickerRangeValue,
+ UseFieldInternalProps,
+} from '@mui/x-date-pickers/internals';
+import { BuiltInFieldTextFieldProps } from '@mui/x-date-pickers/models';
import { UseDateTimeRangeFieldProps } from '../internals/models';
import { DateTimeRangeValidationError } from '../models';
export interface UseSingleInputDateTimeRangeFieldProps<
TEnableAccessibleFieldDOMStructure extends boolean,
> extends UseDateTimeRangeFieldProps,
- ExportedUseClearableFieldProps,
Pick<
UseFieldInternalProps<
PickerRangeValue,
@@ -23,7 +19,9 @@ export interface UseSingleInputDateTimeRangeFieldProps<
DateTimeRangeValidationError
>,
'unstableFieldRef'
- > {}
+ >,
+ // TODO v8: Remove once the range fields open with a button.
+ Omit {}
export type SingleInputDateTimeRangeFieldProps<
TEnableAccessibleFieldDOMStructure extends boolean = true,
@@ -44,18 +42,6 @@ export type SingleInputDateTimeRangeFieldProps<
slotProps?: SingleInputDateTimeRangeFieldSlotProps;
};
-export interface SingleInputDateTimeRangeFieldSlots extends UseClearableFieldSlots {
- /**
- * Form control with an input to render the value.
- * @default , or from '@mui/material' if `enableAccessibleFieldDOMStructure` is `false`.
- */
- textField?: React.ElementType;
-}
+export interface SingleInputDateTimeRangeFieldSlots extends PickerFieldUISlots {}
-export interface SingleInputDateTimeRangeFieldSlotProps extends UseClearableFieldSlotProps {
- textField?: SlotComponentPropsFromProps<
- PickersTextFieldProps | TextFieldProps,
- {},
- FieldOwnerState
- >;
-}
+export interface SingleInputDateTimeRangeFieldSlotProps extends PickerFieldUISlotProps {}
diff --git a/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/useSingleInputDateTimeRangeField.ts b/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/useSingleInputDateTimeRangeField.ts
index 12835555e9a57..c65e16c92ab8f 100644
--- a/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/useSingleInputDateTimeRangeField.ts
+++ b/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/useSingleInputDateTimeRangeField.ts
@@ -33,5 +33,6 @@ export const useSingleInputDateTimeRangeField = <
fieldValueManager: manager.internal_fieldValueManager,
validator: manager.validator,
valueType: manager.valueType,
+ getOpenPickerButtonAriaLabel: manager.internal_getOpenPickerButtonAriaLabel,
});
};
diff --git a/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.tsx b/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.tsx
index 509cf32d2daf6..a2c1fbe79cf76 100644
--- a/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.tsx
+++ b/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.tsx
@@ -1,15 +1,9 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
-import MuiTextField from '@mui/material/TextField';
-import { useClearableField } from '@mui/x-date-pickers/hooks';
-import {
- convertFieldResponseIntoMuiTextFieldProps,
- useFieldOwnerState,
-} from '@mui/x-date-pickers/internals';
-import { PickersTextField } from '@mui/x-date-pickers/PickersTextField';
+import { ClockIcon } from '@mui/x-date-pickers/icons';
+import { PickerFieldUI, useFieldTextFieldProps } from '@mui/x-date-pickers/internals';
import { useThemeProps } from '@mui/material/styles';
-import useSlotProps from '@mui/utils/useSlotProps';
import { refType } from '@mui/utils';
import { SingleInputTimeRangeFieldProps } from './SingleInputTimeRangeField.types';
import { useSingleInputTimeRangeField } from './useSingleInputTimeRangeField';
@@ -41,41 +35,29 @@ const SingleInputTimeRangeField = React.forwardRef(function SingleInputTimeRange
name: 'MuiSingleInputTimeRangeField',
});
- const { slots, slotProps, InputProps, inputProps, ...other } = themeProps;
+ const { slots, slotProps, ...other } = themeProps;
- const ownerState = useFieldOwnerState(themeProps);
-
- const textFieldProps = useSlotProps({
- elementType: PickersTextField,
- externalSlotProps: slotProps?.textField,
+ const textFieldProps = useFieldTextFieldProps<
+ SingleInputTimeRangeFieldProps
+ >({
+ slotProps,
+ ref: inRef,
externalForwardedProps: other,
- ownerState,
- additionalProps: {
- ref: inRef,
- },
- }) as SingleInputTimeRangeFieldProps;
-
- // TODO: Remove when mui/material-ui#35088 will be merged
- textFieldProps.inputProps = { ...inputProps, ...textFieldProps.inputProps };
- textFieldProps.InputProps = { ...InputProps, ...textFieldProps.InputProps };
+ });
const fieldResponse = useSingleInputTimeRangeField<
TEnableAccessibleFieldDOMStructure,
typeof textFieldProps
>(textFieldProps);
- const convertedFieldResponse = convertFieldResponseIntoMuiTextFieldProps(fieldResponse);
-
- const processedFieldProps = useClearableField({
- ...convertedFieldResponse,
- slots,
- slotProps,
- });
-
- const TextField =
- slots?.textField ??
- (fieldResponse.enableAccessibleFieldDOMStructure === false ? MuiTextField : PickersTextField);
- return ;
+ return (
+
+ );
}) as DateRangeFieldComponent;
SingleInputTimeRangeField.fieldType = 'single-input';
@@ -101,6 +83,12 @@ SingleInputTimeRangeField.propTypes = {
* @default false
*/
clearable: PropTypes.bool,
+ /**
+ * The position at which the clear button is placed.
+ * If the field is not clearable, the button is not rendered.
+ * @default 'end'
+ */
+ clearButtonPosition: PropTypes.oneOf(['end', 'start']),
/**
* The color of the component.
* It supports both default and custom theme colors, which can be added as shown in the
diff --git a/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.types.ts b/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.types.ts
index 46b019e84e955..56fcb66f9b5f9 100644
--- a/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.types.ts
+++ b/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.types.ts
@@ -1,21 +1,17 @@
-import * as React from 'react';
-import type { TextFieldProps } from '@mui/material/TextField';
-import { PickerRangeValue, UseFieldInternalProps } from '@mui/x-date-pickers/internals';
-import { BuiltInFieldTextFieldProps, FieldOwnerState } from '@mui/x-date-pickers/models';
-import { SlotComponentPropsFromProps } from '@mui/x-internals/types';
-import { PickersTextFieldProps } from '@mui/x-date-pickers/PickersTextField';
import {
- ExportedUseClearableFieldProps,
- UseClearableFieldSlots,
- UseClearableFieldSlotProps,
-} from '@mui/x-date-pickers/hooks';
+ ExportedPickerFieldUIProps,
+ PickerFieldUISlots,
+ PickerFieldUISlotProps,
+ PickerRangeValue,
+ UseFieldInternalProps,
+} from '@mui/x-date-pickers/internals';
+import { BuiltInFieldTextFieldProps } from '@mui/x-date-pickers/models';
import { UseTimeRangeFieldProps } from '../internals/models';
import { TimeRangeValidationError } from '../models';
export interface UseSingleInputTimeRangeFieldProps<
TEnableAccessibleFieldDOMStructure extends boolean,
> extends UseTimeRangeFieldProps,
- ExportedUseClearableFieldProps,
Pick<
UseFieldInternalProps<
PickerRangeValue,
@@ -23,7 +19,9 @@ export interface UseSingleInputTimeRangeFieldProps<
TimeRangeValidationError
>,
'unstableFieldRef'
- > {}
+ >,
+ // TODO v8: Remove once the range fields open with a button.
+ Omit {}
export type SingleInputTimeRangeFieldProps<
TEnableAccessibleFieldDOMStructure extends boolean = true,
@@ -44,18 +42,6 @@ export type SingleInputTimeRangeFieldProps<
slotProps?: SingleInputTimeRangeFieldSlotProps;
};
-export interface SingleInputTimeRangeFieldSlots extends UseClearableFieldSlots {
- /**
- * Form control with an input to render the value.
- * @default , or from '@mui/material' if `enableAccessibleFieldDOMStructure` is `false`.
- */
- textField?: React.ElementType;
-}
+export interface SingleInputTimeRangeFieldSlots extends PickerFieldUISlots {}
-export interface SingleInputTimeRangeFieldSlotProps extends UseClearableFieldSlotProps {
- textField?: SlotComponentPropsFromProps<
- PickersTextFieldProps | TextFieldProps,
- {},
- FieldOwnerState
- >;
-}
+export interface SingleInputTimeRangeFieldSlotProps extends PickerFieldUISlotProps {}
diff --git a/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/useSingleInputTimeRangeField.ts b/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/useSingleInputTimeRangeField.ts
index bfa482d68eedf..2416c57b1bd69 100644
--- a/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/useSingleInputTimeRangeField.ts
+++ b/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/useSingleInputTimeRangeField.ts
@@ -33,5 +33,6 @@ export const useSingleInputTimeRangeField = <
fieldValueManager: manager.internal_fieldValueManager,
validator: manager.validator,
valueType: manager.valueType,
+ getOpenPickerButtonAriaLabel: manager.internal_getOpenPickerButtonAriaLabel,
});
};
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 d0b5285994826..1b6a0c70a0802 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
@@ -11,6 +11,7 @@ import {
PickerProvider,
PickerValue,
PickerRangeValue,
+ PickerFieldUIContextProvider,
} from '@mui/x-date-pickers/internals';
import { FieldRef, InferError } from '@mui/x-date-pickers/models';
import {
@@ -58,7 +59,6 @@ export const useDesktopRangePicker = <
} = props;
const fieldContainerRef = React.useRef(null);
- const anchorRef = React.useRef(null);
const popperRef = React.useRef(null);
const startFieldRef = React.useRef>(null);
const endFieldRef = React.useRef>(null);
@@ -93,6 +93,9 @@ export const useDesktopRangePicker = <
localeText,
});
+ // Temporary hack to hide the opening button on the range pickers until we have migrate them to the new opening logic.
+ providerProps.contextValue.triggerStatus = 'hidden';
+
React.useEffect(() => {
if (providerProps.contextValue.view) {
initialView.current = providerProps.contextValue.view;
@@ -156,7 +159,7 @@ export const useDesktopRangePicker = <
pickerSlotProps: slotProps,
pickerSlots: slots,
fieldProps,
- anchorRef,
+ anchorRef: providerProps.contextValue.triggerRef,
startFieldRef,
endFieldRef,
singleInputFieldRef,
@@ -179,24 +182,26 @@ export const useDesktopRangePicker = <
...enrichedFieldResponse.fieldPrivateContextValue,
}}
>
-
-
-
-
- {renderCurrentView()}
-
-
-
+
+
+
+
+
+ {renderCurrentView()}
+
+
+
+
);
diff --git a/packages/x-date-pickers-pro/src/internals/hooks/useEnrichedRangePickerField.ts b/packages/x-date-pickers-pro/src/internals/hooks/useEnrichedRangePickerField.ts
index 3c48c357e8bd0..196673da40d24 100644
--- a/packages/x-date-pickers-pro/src/internals/hooks/useEnrichedRangePickerField.ts
+++ b/packages/x-date-pickers-pro/src/internals/hooks/useEnrichedRangePickerField.ts
@@ -110,7 +110,7 @@ export interface UseEnrichedRangePickerFieldPropsParams<
TEnableAccessibleFieldDOMStructure,
TError
>;
- anchorRef?: React.Ref;
+ anchorRef?: React.Ref;
currentView?: TView | null;
initialView?: TView;
startFieldRef: React.RefObject | null>;
@@ -310,10 +310,7 @@ const useSingleInputFieldSlotProps = <
rangePosition,
onRangePositionChange,
singleInputFieldRef,
- pickerSlots,
- pickerSlotProps,
fieldProps,
- anchorRef,
currentView,
}: UseEnrichedRangePickerFieldPropsParams<
true,
@@ -380,32 +377,12 @@ const useSingleInputFieldSlotProps = <
}
};
- const slots = {
- ...fieldProps.slots,
- textField: pickerSlots?.textField,
- clearButton: pickerSlots?.clearButton,
- clearIcon: pickerSlots?.clearIcon,
- };
-
- const slotProps = {
- ...fieldProps.slotProps,
- textField: pickerSlotProps?.textField,
- clearButton: pickerSlotProps?.clearButton,
- clearIcon: pickerSlotProps?.clearIcon,
- };
-
const enrichedFieldProps: ReturnType = {
...fieldProps,
- slots,
- slotProps,
label,
unstableFieldRef: handleFieldRef,
onKeyDown: onSpaceOrEnter(openPicker, fieldProps.onKeyDown),
onBlur,
- InputProps: {
- ref: anchorRef,
- ...fieldProps?.InputProps,
- },
focused: contextValue.open ? true : undefined,
...(labelId != null && { id: labelId }),
...(variant === 'mobile' && { readOnly: true }),
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 d6d2382e90148..459323308e98d 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
@@ -10,6 +10,7 @@ import {
PickerProvider,
PickerRangeValue,
PickerValue,
+ PickerFieldUIContextProvider,
} from '@mui/x-date-pickers/internals';
import { usePickerTranslations } from '@mui/x-date-pickers/hooks';
import { FieldRef, InferError } from '@mui/x-date-pickers/models';
@@ -90,8 +91,10 @@ export const useMobileRangePicker = <
localeText,
});
- const Field = slots.field;
+ // Temporary hack to hide the opening button on the range pickers until we have migrate them to the new opening logic.
+ providerProps.contextValue.triggerStatus = 'hidden';
+ const Field = slots.field;
const fieldProps: RangePickerPropsForFieldSlot<
boolean,
TEnableAccessibleFieldDOMStructure,
@@ -184,14 +187,16 @@ export const useMobileRangePicker = <
...enrichedFieldResponse.fieldPrivateContextValue,
}}
>
-
-
-
-
- {renderCurrentView()}
-
-
-
+
+
+
+
+
+ {renderCurrentView()}
+
+
+
+
);
diff --git a/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/shared.ts b/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/shared.ts
deleted file mode 100644
index f3568f99ad13d..0000000000000
--- a/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/shared.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-/* TODO: remove this when a clearable behavior for multiple input range fields is implemented */
-export const excludeProps = (
- props: TProps,
- excludedProps: Array,
-): TProps => {
- return (Object.keys(props) as Array).reduce((acc, key) => {
- if (!excludedProps.includes(key)) {
- acc[key] = props[key];
- }
- return acc;
- }, {} as TProps);
-};
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 d255c2e719c75..e2b0b8483f184 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
@@ -16,7 +16,6 @@ import { validateDateRange } from '../../../validation';
import { rangeValueManager } from '../../utils/valueManagers';
import type { UseMultiInputRangeFieldResponse } from './useMultiInputRangeField.types';
import { DateRangeValidationError } from '../../../models';
-import { excludeProps } from './shared';
import { useMultiInputFieldSelectedSections } from '../useMultiInputFieldSelectedSections';
import { useDateRangeManager } from '../../../managers';
@@ -143,9 +142,8 @@ export const useMultiInputDateRangeField = <
endFieldProps,
) as UseFieldResponse;
- /* TODO: Undo this change when a clearable behavior for multiple input range fields is implemented */
return {
- startDate: excludeProps(startDateResponse, ['clearable', 'onClear']),
- endDate: excludeProps(endDateResponse, ['clearable', 'onClear']),
+ startDate: startDateResponse,
+ endDate: endDateResponse,
};
};
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 f4a13c557de6d..840ef757f2e9e 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
@@ -16,7 +16,6 @@ import { validateDateTimeRange } from '../../../validation';
import { rangeValueManager } from '../../utils/valueManagers';
import type { UseMultiInputRangeFieldResponse } from './useMultiInputRangeField.types';
import { DateTimeRangeValidationError } from '../../../models';
-import { excludeProps } from './shared';
import { useMultiInputFieldSelectedSections } from '../useMultiInputFieldSelectedSections';
import { useDateTimeRangeManager } from '../../../managers';
@@ -144,9 +143,8 @@ export const useMultiInputDateTimeRangeField = <
typeof endFieldProps
>(endFieldProps) as UseFieldResponse;
- /* TODO: Undo this change when a clearable behavior for multiple input range fields is implemented */
return {
- startDate: excludeProps(startDateResponse, ['clearable', 'onClear']),
- endDate: excludeProps(endDateResponse, ['clearable', 'onClear']),
+ startDate: startDateResponse,
+ endDate: endDateResponse,
};
};
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 ea881fb4349d3..33c54bae45ae1 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
@@ -16,7 +16,6 @@ import { validateTimeRange } from '../../../validation';
import { rangeValueManager } from '../../utils/valueManagers';
import type { UseMultiInputRangeFieldResponse } from './useMultiInputRangeField.types';
import { TimeRangeValidationError } from '../../../models';
-import { excludeProps } from './shared';
import { useMultiInputFieldSelectedSections } from '../useMultiInputFieldSelectedSections';
import { useTimeRangeManager } from '../../../managers';
@@ -143,9 +142,8 @@ export const useMultiInputTimeRangeField = <
endFieldProps,
) as UseFieldResponse;
- /* TODO: Undo this change when a clearable behavior for multiple input range fields is implemented */
return {
- startDate: excludeProps(startDateResponse, ['clearable', 'onClear']),
- endDate: excludeProps(endDateResponse, ['clearable', 'onClear']),
+ startDate: startDateResponse,
+ endDate: endDateResponse,
};
};
diff --git a/packages/x-date-pickers-pro/src/managers/useDateRangeManager.ts b/packages/x-date-pickers-pro/src/managers/useDateRangeManager.ts
index ed4785f5e9ead..4e08aeaee8301 100644
--- a/packages/x-date-pickers-pro/src/managers/useDateRangeManager.ts
+++ b/packages/x-date-pickers-pro/src/managers/useDateRangeManager.ts
@@ -34,6 +34,8 @@ export function useDateRangeManager '',
}),
[enableAccessibleFieldDOMStructure, dateSeparator],
);
diff --git a/packages/x-date-pickers-pro/src/managers/useDateTimeRangeManager.ts b/packages/x-date-pickers-pro/src/managers/useDateTimeRangeManager.ts
index 8e779c3230b8b..dd99267e4f869 100644
--- a/packages/x-date-pickers-pro/src/managers/useDateTimeRangeManager.ts
+++ b/packages/x-date-pickers-pro/src/managers/useDateTimeRangeManager.ts
@@ -35,6 +35,8 @@ export function useDateTimeRangeManager '',
}),
[enableAccessibleFieldDOMStructure, dateSeparator],
);
diff --git a/packages/x-date-pickers-pro/src/managers/useTimeRangeManager.ts b/packages/x-date-pickers-pro/src/managers/useTimeRangeManager.ts
index a17555bb6b5e9..4563793170fec 100644
--- a/packages/x-date-pickers-pro/src/managers/useTimeRangeManager.ts
+++ b/packages/x-date-pickers-pro/src/managers/useTimeRangeManager.ts
@@ -35,6 +35,8 @@ export function useTimeRangeManager '',
}),
[enableAccessibleFieldDOMStructure, dateSeparator],
);
diff --git a/packages/x-date-pickers-pro/src/models/fields.ts b/packages/x-date-pickers-pro/src/models/fields.ts
index 648dc4c4f277a..0fe3c8c4415ad 100644
--- a/packages/x-date-pickers-pro/src/models/fields.ts
+++ b/packages/x-date-pickers-pro/src/models/fields.ts
@@ -6,7 +6,6 @@ import {
PickerRangeValue,
} from '@mui/x-date-pickers/internals';
import { FieldRef, PickerFieldSlotProps } from '@mui/x-date-pickers/models';
-import { UseClearableFieldResponse } from '@mui/x-date-pickers/hooks';
export type { FieldRangeSection } from '@mui/x-date-pickers/internals';
@@ -62,6 +61,13 @@ export type PickerRangeFieldSlotProps = UseClearableFieldResponse<
- UseFieldResponse
+> = Omit<
+ UseFieldResponse,
+ | 'slots'
+ | 'slotProps'
+ | 'clearable'
+ | 'onClear'
+ | 'openPickerButtonPosition'
+ | 'clearButtonPosition'
+ | 'openPickerAriaLabel'
>;
diff --git a/packages/x-date-pickers/src/DateField/DateField.tsx b/packages/x-date-pickers/src/DateField/DateField.tsx
index a7a085a71570e..fca2497ad6219 100644
--- a/packages/x-date-pickers/src/DateField/DateField.tsx
+++ b/packages/x-date-pickers/src/DateField/DateField.tsx
@@ -1,16 +1,12 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
-import MuiTextField from '@mui/material/TextField';
import { useThemeProps } from '@mui/material/styles';
-import useSlotProps from '@mui/utils/useSlotProps';
import { refType } from '@mui/utils';
import { DateFieldProps } from './DateField.types';
import { useDateField } from './useDateField';
-import { useClearableField } from '../hooks';
-import { PickersTextField } from '../PickersTextField';
-import { convertFieldResponseIntoMuiTextFieldProps } from '../internals/utils/convertFieldResponseIntoMuiTextFieldProps';
-import { useFieldOwnerState } from '../internals/hooks/useFieldOwnerState';
+import { PickerFieldUI, useFieldTextFieldProps } from '../internals/components/PickerFieldUI';
+import { CalendarIcon } from '../icons';
type DateFieldComponent = ((
props: DateFieldProps & React.RefAttributes,
@@ -34,40 +30,28 @@ const DateField = React.forwardRef(function DateField<
name: 'MuiDateField',
});
- const { slots, slotProps, InputProps, inputProps, ...other } = themeProps;
+ const { slots, slotProps, ...other } = themeProps;
- const ownerState = useFieldOwnerState(themeProps);
-
- const textFieldProps = useSlotProps({
- elementType: PickersTextField,
- externalSlotProps: slotProps?.textField,
- externalForwardedProps: other,
- additionalProps: {
+ const textFieldProps = useFieldTextFieldProps>(
+ {
+ slotProps,
ref: inRef,
+ externalForwardedProps: other,
},
- ownerState,
- }) as DateFieldProps;
-
- // TODO: Remove when mui/material-ui#35088 will be merged
- textFieldProps.inputProps = { ...inputProps, ...textFieldProps.inputProps };
- textFieldProps.InputProps = { ...InputProps, ...textFieldProps.InputProps };
+ );
const fieldResponse = useDateField(
textFieldProps,
);
- const convertedFieldResponse = convertFieldResponseIntoMuiTextFieldProps(fieldResponse);
-
- const processedFieldProps = useClearableField({
- ...convertedFieldResponse,
- slots,
- slotProps,
- });
-
- const TextField =
- slots?.textField ??
- (fieldResponse.enableAccessibleFieldDOMStructure === false ? MuiTextField : PickersTextField);
- return ;
+ return (
+
+ );
}) as DateFieldComponent;
DateField.propTypes = {
@@ -86,6 +70,12 @@ DateField.propTypes = {
* @default false
*/
clearable: PropTypes.bool,
+ /**
+ * The position at which the clear button is placed.
+ * If the field is not clearable, the button is not rendered.
+ * @default 'end'
+ */
+ clearButtonPosition: PropTypes.oneOf(['end', 'start']),
/**
* The color of the component.
* It supports both default and custom theme colors, which can be added as shown in the
@@ -229,6 +219,12 @@ DateField.propTypes = {
* @param {FieldSelectedSections} newValue The new selected sections.
*/
onSelectedSectionsChange: PropTypes.func,
+ /**
+ * The position at which the opening button is placed.
+ * If there is no picker to open, the button is not rendered
+ * @default 'end'
+ */
+ openPickerButtonPosition: PropTypes.oneOf(['end', 'start']),
/**
* If `true`, the component is read-only.
* When read-only, the value cannot be changed but the user can interact with the interface.
diff --git a/packages/x-date-pickers/src/DateField/DateField.types.ts b/packages/x-date-pickers/src/DateField/DateField.types.ts
index 7226c4ac65791..8a4d1c048b14b 100644
--- a/packages/x-date-pickers/src/DateField/DateField.types.ts
+++ b/packages/x-date-pickers/src/DateField/DateField.types.ts
@@ -1,16 +1,13 @@
-import * as React from 'react';
-import type { TextFieldProps } from '@mui/material/TextField';
-import { MakeOptional, SlotComponentPropsFromProps } from '@mui/x-internals/types';
-import {
- ExportedUseClearableFieldProps,
- UseClearableFieldSlots,
- UseClearableFieldSlotProps,
-} from '../hooks/useClearableField';
-import { DateValidationError, BuiltInFieldTextFieldProps, FieldOwnerState } from '../models';
+import { MakeOptional } from '@mui/x-internals/types';
+import { DateValidationError, BuiltInFieldTextFieldProps } from '../models';
import { UseFieldInternalProps } from '../internals/hooks/useField';
import { ExportedValidateDateProps } from '../validation/validateDate';
-import { PickersTextFieldProps } from '../PickersTextField';
import { PickerValue } from '../internals/models';
+import {
+ ExportedPickerFieldUIProps,
+ PickerFieldUISlotProps,
+ PickerFieldUISlots,
+} from '../internals/components/PickerFieldUI';
export interface UseDateFieldProps
extends MakeOptional<
@@ -18,7 +15,7 @@ export interface UseDateFieldProps,
ExportedValidateDateProps,
- ExportedUseClearableFieldProps {}
+ ExportedPickerFieldUIProps {}
export type DateFieldProps =
// The hook props
@@ -43,18 +40,6 @@ export type DateFieldProps =
DateFieldProps;
-export interface DateFieldSlots extends UseClearableFieldSlots {
- /**
- * Form control with an input to render the value.
- * @default , or from '@mui/material' if `enableAccessibleFieldDOMStructure` is `false`.
- */
- textField?: React.ElementType;
-}
+export interface DateFieldSlots extends PickerFieldUISlots {}
-export interface DateFieldSlotProps extends UseClearableFieldSlotProps {
- textField?: SlotComponentPropsFromProps<
- PickersTextFieldProps | TextFieldProps,
- {},
- FieldOwnerState
- >;
-}
+export interface DateFieldSlotProps extends PickerFieldUISlotProps {}
diff --git a/packages/x-date-pickers/src/DateField/useDateField.ts b/packages/x-date-pickers/src/DateField/useDateField.ts
index 49b1e6d8b57df..fcbd4f3c4261d 100644
--- a/packages/x-date-pickers/src/DateField/useDateField.ts
+++ b/packages/x-date-pickers/src/DateField/useDateField.ts
@@ -30,5 +30,6 @@ export const useDateField = <
fieldValueManager: manager.internal_fieldValueManager,
validator: manager.validator,
valueType: manager.valueType,
+ getOpenPickerButtonAriaLabel: manager.internal_getOpenPickerButtonAriaLabel,
});
};
diff --git a/packages/x-date-pickers/src/DatePicker/tests/DatePicker.test.tsx b/packages/x-date-pickers/src/DatePicker/tests/DatePicker.test.tsx
index 3ad845d85883b..902c7f656cfbb 100644
--- a/packages/x-date-pickers/src/DatePicker/tests/DatePicker.test.tsx
+++ b/packages/x-date-pickers/src/DatePicker/tests/DatePicker.test.tsx
@@ -1,12 +1,11 @@
import * as React from 'react';
import { expect } from 'chai';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
-import { screen } from '@mui/internal-test-utils/createRenderer';
+import { fireEvent, screen } from '@mui/internal-test-utils/createRenderer';
import { createPickerRenderer, stubMatchMedia } from 'test/utils/pickers';
-import { pickersInputBaseClasses } from '@mui/x-date-pickers/PickersTextField';
describe('', () => {
- const { render } = createPickerRenderer();
+ const { render } = createPickerRenderer({ clock: 'fake' });
it('should render in mobile mode when `useMediaQuery` returns `false`', () => {
const originalMatchMedia = window.matchMedia;
@@ -14,7 +13,8 @@ describe('', () => {
render();
- expect(screen.getByLabelText(/Choose date/)).to.have.class(pickersInputBaseClasses.input);
+ fireEvent.click(screen.getByLabelText(/Choose date/));
+ expect(screen.queryByRole('dialog')).to.not.equal(null);
window.matchMedia = originalMatchMedia;
});
diff --git a/packages/x-date-pickers/src/DateTimeField/DateTimeField.tsx b/packages/x-date-pickers/src/DateTimeField/DateTimeField.tsx
index 187f0ffe62568..1ee01deb4b4c7 100644
--- a/packages/x-date-pickers/src/DateTimeField/DateTimeField.tsx
+++ b/packages/x-date-pickers/src/DateTimeField/DateTimeField.tsx
@@ -1,16 +1,12 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
-import MuiTextField from '@mui/material/TextField';
import { useThemeProps } from '@mui/material/styles';
-import useSlotProps from '@mui/utils/useSlotProps';
import { refType } from '@mui/utils';
import { DateTimeFieldProps } from './DateTimeField.types';
import { useDateTimeField } from './useDateTimeField';
-import { useClearableField } from '../hooks';
-import { PickersTextField } from '../PickersTextField';
-import { convertFieldResponseIntoMuiTextFieldProps } from '../internals/utils/convertFieldResponseIntoMuiTextFieldProps';
-import { useFieldOwnerState } from '../internals/hooks/useFieldOwnerState';
+import { PickerFieldUI, useFieldTextFieldProps } from '../internals/components/PickerFieldUI';
+import { CalendarIcon } from '../icons';
type DateTimeFieldComponent = ((
props: DateTimeFieldProps &
@@ -38,40 +34,28 @@ const DateTimeField = React.forwardRef(function DateTimeField<
name: 'MuiDateTimeField',
});
- const { slots, slotProps, InputProps, inputProps, ...other } = themeProps;
+ const { slots, slotProps, ...other } = themeProps;
- const ownerState = useFieldOwnerState(themeProps);
-
- const textFieldProps = useSlotProps({
- elementType: PickersTextField,
- externalSlotProps: slotProps?.textField,
+ const textFieldProps = useFieldTextFieldProps<
+ DateTimeFieldProps
+ >({
+ slotProps,
+ ref: inRef,
externalForwardedProps: other,
- ownerState,
- additionalProps: {
- ref: inRef,
- },
- }) as DateTimeFieldProps;
-
- // TODO: Remove when mui/material-ui#35088 will be merged
- textFieldProps.inputProps = { ...inputProps, ...textFieldProps.inputProps };
- textFieldProps.InputProps = { ...InputProps, ...textFieldProps.InputProps };
+ });
const fieldResponse = useDateTimeField(
textFieldProps,
);
- const convertedFieldResponse = convertFieldResponseIntoMuiTextFieldProps(fieldResponse);
-
- const processedFieldProps = useClearableField({
- ...convertedFieldResponse,
- slots,
- slotProps,
- });
- const TextField =
- slots?.textField ??
- (fieldResponse.enableAccessibleFieldDOMStructure === false ? MuiTextField : PickersTextField);
-
- return ;
+ return (
+
+ );
}) as DateTimeFieldComponent;
DateTimeField.propTypes = {
@@ -95,6 +79,12 @@ DateTimeField.propTypes = {
* @default false
*/
clearable: PropTypes.bool,
+ /**
+ * The position at which the clear button is placed.
+ * If the field is not clearable, the button is not rendered.
+ * @default 'end'
+ */
+ clearButtonPosition: PropTypes.oneOf(['end', 'start']),
/**
* The color of the component.
* It supports both default and custom theme colors, which can be added as shown in the
@@ -266,6 +256,12 @@ DateTimeField.propTypes = {
* @param {FieldSelectedSections} newValue The new selected sections.
*/
onSelectedSectionsChange: PropTypes.func,
+ /**
+ * The position at which the opening button is placed.
+ * If there is no picker to open, the button is not rendered
+ * @default 'end'
+ */
+ openPickerButtonPosition: PropTypes.oneOf(['end', 'start']),
/**
* If `true`, the component is read-only.
* When read-only, the value cannot be changed but the user can interact with the interface.
diff --git a/packages/x-date-pickers/src/DateTimeField/DateTimeField.types.ts b/packages/x-date-pickers/src/DateTimeField/DateTimeField.types.ts
index 2c3964472c396..ec523e87383ab 100644
--- a/packages/x-date-pickers/src/DateTimeField/DateTimeField.types.ts
+++ b/packages/x-date-pickers/src/DateTimeField/DateTimeField.types.ts
@@ -1,17 +1,14 @@
-import * as React from 'react';
-import { MakeOptional, SlotComponentPropsFromProps } from '@mui/x-internals/types';
-import { TextFieldProps } from '@mui/material/TextField';
-import { DateTimeValidationError, BuiltInFieldTextFieldProps, FieldOwnerState } from '../models';
+import { MakeOptional } from '@mui/x-internals/types';
+import { DateTimeValidationError, BuiltInFieldTextFieldProps } from '../models';
import { UseFieldInternalProps } from '../internals/hooks/useField';
-import {
- ExportedUseClearableFieldProps,
- UseClearableFieldSlots,
- UseClearableFieldSlotProps,
-} from '../hooks/useClearableField';
import { ExportedValidateDateTimeProps } from '../validation/validateDateTime';
import { AmPmProps } from '../internals/models/props/time';
import { PickerValue } from '../internals/models';
-import { PickersTextFieldProps } from '../PickersTextField';
+import {
+ ExportedPickerFieldUIProps,
+ PickerFieldUISlotProps,
+ PickerFieldUISlots,
+} from '../internals/components/PickerFieldUI';
export interface UseDateTimeFieldProps
extends MakeOptional<
@@ -23,7 +20,7 @@ export interface UseDateTimeFieldProps,
ExportedValidateDateTimeProps,
- ExportedUseClearableFieldProps,
+ ExportedPickerFieldUIProps,
AmPmProps {}
export type DateTimeFieldProps =
@@ -46,18 +43,6 @@ export type DateTimeFieldProps, or from '@mui/material' if `enableAccessibleFieldDOMStructure` is `false`.
- */
- textField?: React.ElementType;
-}
+export interface DateTimeFieldSlots extends PickerFieldUISlots {}
-export interface DateTimeFieldSlotProps extends UseClearableFieldSlotProps {
- textField?: SlotComponentPropsFromProps<
- PickersTextFieldProps | TextFieldProps,
- {},
- FieldOwnerState
- >;
-}
+export interface DateTimeFieldSlotProps extends PickerFieldUISlotProps {}
diff --git a/packages/x-date-pickers/src/DateTimeField/useDateTimeField.ts b/packages/x-date-pickers/src/DateTimeField/useDateTimeField.ts
index 5943673b20c9b..7884f56705e76 100644
--- a/packages/x-date-pickers/src/DateTimeField/useDateTimeField.ts
+++ b/packages/x-date-pickers/src/DateTimeField/useDateTimeField.ts
@@ -30,5 +30,6 @@ export const useDateTimeField = <
fieldValueManager: manager.internal_fieldValueManager,
validator: manager.validator,
valueType: manager.valueType,
+ getOpenPickerButtonAriaLabel: manager.internal_getOpenPickerButtonAriaLabel,
});
};
diff --git a/packages/x-date-pickers/src/DateTimePicker/tests/DateTimePicker.test.tsx b/packages/x-date-pickers/src/DateTimePicker/tests/DateTimePicker.test.tsx
index 933f61c48f166..bd47d4b3f04db 100644
--- a/packages/x-date-pickers/src/DateTimePicker/tests/DateTimePicker.test.tsx
+++ b/packages/x-date-pickers/src/DateTimePicker/tests/DateTimePicker.test.tsx
@@ -1,12 +1,11 @@
import * as React from 'react';
import { expect } from 'chai';
import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker';
-import { screen } from '@mui/internal-test-utils/createRenderer';
+import { fireEvent, screen } from '@mui/internal-test-utils/createRenderer';
import { createPickerRenderer, stubMatchMedia } from 'test/utils/pickers';
-import { pickersInputBaseClasses } from '@mui/x-date-pickers/PickersTextField';
describe('', () => {
- const { render } = createPickerRenderer();
+ const { render } = createPickerRenderer({ clock: 'fake' });
it('should render in mobile mode when `useMediaQuery` returns `false`', () => {
const originalMatchMedia = window.matchMedia;
@@ -14,7 +13,8 @@ describe('', () => {
render();
- expect(screen.getByLabelText(/Choose date/)).to.have.class(pickersInputBaseClasses.input);
+ fireEvent.click(screen.getByLabelText(/Choose date/));
+ expect(screen.queryByRole('dialog')).to.not.equal(null);
window.matchMedia = originalMatchMedia;
});
diff --git a/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.tsx b/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.tsx
index 874017a90b7b8..4ee42b330bc98 100644
--- a/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.tsx
+++ b/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.tsx
@@ -6,16 +6,13 @@ import { refType } from '@mui/utils';
import { singleItemValueManager } from '../internals/utils/valueManagers';
import { DesktopDatePickerProps } from './DesktopDatePicker.types';
import { DatePickerViewRenderers, useDatePickerDefaultizedProps } from '../DatePicker/shared';
-import { usePickerTranslations } from '../hooks/usePickerTranslations';
import { useUtils } from '../internals/hooks/useUtils';
import { validateDate, extractValidationProps } from '../validation';
import { DateView, PickerOwnerState } from '../models';
import { useDesktopPicker } from '../internals/hooks/useDesktopPicker';
-import { CalendarIcon } from '../icons';
import { DateField } from '../DateField';
import { renderDateViewCalendar } from '../dateViewRenderers';
import { resolveDateFormat } from '../internals/utils/date-utils';
-import { buildGetOpenDialogAriaText } from '../locales/utils/getPickersLocalization';
import { PickerLayoutOwnerState } from '../PickersLayout';
import { PickersActionBarAction } from '../PickersActionBar';
@@ -42,7 +39,6 @@ const DesktopDatePicker = React.forwardRef(function DesktopDatePicker<
inProps: DesktopDatePickerProps,
ref: React.Ref,
) {
- const translations = usePickerTranslations();
const utils = useUtils();
// Props with the default values common to all date pickers
@@ -65,7 +61,6 @@ const DesktopDatePicker = React.forwardRef(function DesktopDatePicker<
format: resolveDateFormat(utils, defaultizedProps, false),
yearsPerRow: defaultizedProps.yearsPerRow ?? 4,
slots: {
- openPickerIcon: CalendarIcon,
field: DateField,
...defaultizedProps.slots,
},
@@ -95,12 +90,6 @@ const DesktopDatePicker = React.forwardRef(function DesktopDatePicker<
props,
valueManager: singleItemValueManager,
valueType: 'date',
- getOpenDialogAriaText: buildGetOpenDialogAriaText({
- utils,
- formatKey: 'fullDate',
- contextTranslation: translations.openDatePickerDialogue,
- propsTranslation: props.localeText?.openDatePickerDialogue,
- }),
validator: validateDate,
});
diff --git a/packages/x-date-pickers/src/DesktopDatePicker/tests/DesktopDatePicker.test.tsx b/packages/x-date-pickers/src/DesktopDatePicker/tests/DesktopDatePicker.test.tsx
index 1c55b5a4bbf50..9d63482b4442d 100644
--- a/packages/x-date-pickers/src/DesktopDatePicker/tests/DesktopDatePicker.test.tsx
+++ b/packages/x-date-pickers/src/DesktopDatePicker/tests/DesktopDatePicker.test.tsx
@@ -40,7 +40,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'date', variant: 'desktop' });
+ openPicker({ type: 'date' });
fireEvent.click(screen.getByLabelText(/switch to year view/i));
expect(handleViewChange.callCount).to.equal(1);
@@ -49,7 +49,7 @@ describe('', () => {
// eslint-disable-next-line material-ui/disallow-active-element-as-key-event-target
fireEvent.keyDown(document.activeElement!, { key: 'Escape' });
- openPicker({ type: 'date', variant: 'desktop' });
+ openPicker({ type: 'date' });
expect(handleViewChange.callCount).to.equal(2);
expect(handleViewChange.lastCall.firstArg).to.equal('day');
});
@@ -66,7 +66,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'date', variant: 'desktop' });
+ openPicker({ type: 'date' });
fireEvent.click(screen.getByLabelText(/switch to year view/i));
expect(handleViewChange.callCount).to.equal(1);
@@ -75,7 +75,7 @@ describe('', () => {
// eslint-disable-next-line material-ui/disallow-active-element-as-key-event-target
fireEvent.keyDown(document.activeElement!, { key: 'Escape' });
- openPicker({ type: 'date', variant: 'desktop' });
+ openPicker({ type: 'date' });
expect(handleViewChange.callCount).to.equal(2);
expect(handleViewChange.lastCall.firstArg).to.equal('month');
});
@@ -85,7 +85,7 @@ describe('', () => {
,
);
- openPicker({ type: 'date', variant: 'desktop' });
+ openPicker({ type: 'date' });
expect(screen.getByRole('radio', { checked: true, name: '2018' })).not.to.equal(null);
@@ -93,7 +93,7 @@ describe('', () => {
// eslint-disable-next-line material-ui/disallow-active-element-as-key-event-target
fireEvent.keyDown(document.activeElement!, { key: 'Escape' });
setProps({ views: ['month', 'year'] });
- openPicker({ type: 'date', variant: 'desktop' });
+ openPicker({ type: 'date' });
// wait for all pending changes to be flushed
clock.runToLast();
@@ -104,7 +104,7 @@ describe('', () => {
testSkipIf(isJSDOM)('should move the focus to the newly opened views', () => {
render();
- openPicker({ type: 'date', variant: 'desktop' });
+ openPicker({ type: 'date' });
expect(document.activeElement).to.have.text('2019');
fireEvent.click(screen.getByText('2020'));
@@ -120,7 +120,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'date', variant: 'desktop' });
+ openPicker({ type: 'date' });
expect(screen.getByRole('radio', { checked: true, name: 'January' })).not.to.equal(null);
@@ -128,7 +128,7 @@ describe('', () => {
// eslint-disable-next-line material-ui/disallow-active-element-as-key-event-target
fireEvent.keyDown(document.activeElement!, { key: 'Escape' });
setProps({ view: 'year' });
- openPicker({ type: 'date', variant: 'desktop' });
+ openPicker({ type: 'date' });
// wait for all pending changes to be flushed
clock.runToLast();
@@ -236,7 +236,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'date', variant: 'desktop' });
+ openPicker({ type: 'date' });
// Select year
fireEvent.click(screen.getByRole('radio', { name: '2025' }));
@@ -263,7 +263,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'date', variant: 'desktop' });
+ openPicker({ type: 'date' });
expect(screen.getByLabelText('Previous month')).to.have.attribute('disabled');
});
@@ -276,7 +276,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'date', variant: 'desktop' });
+ openPicker({ type: 'date' });
expect(screen.getByLabelText('Previous month')).not.to.have.attribute('disabled');
});
@@ -289,7 +289,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'date', variant: 'desktop' });
+ openPicker({ type: 'date' });
expect(screen.getByLabelText('Next month')).to.have.attribute('disabled');
});
@@ -302,7 +302,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'date', variant: 'desktop' });
+ openPicker({ type: 'date' });
expect(screen.getByLabelText('Next month')).not.to.have.attribute('disabled');
});
@@ -347,7 +347,7 @@ describe('', () => {
expect(() => {
render();
- openPicker({ type: 'date', variant: 'desktop' });
+ openPicker({ type: 'date' });
}).toWarnDev('MUI X: `openTo="month"` is not a valid prop.');
});
diff --git a/packages/x-date-pickers/src/DesktopDatePicker/tests/describes.DesktopDatePicker.test.tsx b/packages/x-date-pickers/src/DesktopDatePicker/tests/describes.DesktopDatePicker.test.tsx
index 1c79140c48074..1bb38f834e57b 100644
--- a/packages/x-date-pickers/src/DesktopDatePicker/tests/describes.DesktopDatePicker.test.tsx
+++ b/packages/x-date-pickers/src/DesktopDatePicker/tests/describes.DesktopDatePicker.test.tsx
@@ -23,7 +23,6 @@ describe(' - Describes', () => {
clock,
views: ['year', 'month', 'day'],
componentFamily: 'picker',
- variant: 'desktop',
}));
describeConformance(, () => ({
diff --git a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx
index fef966c911ec2..d4bf5da32c03c 100644
--- a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx
+++ b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx
@@ -9,11 +9,9 @@ import { DateTimeField } from '../DateTimeField';
import { DesktopDateTimePickerProps } from './DesktopDateTimePicker.types';
import { useDateTimePickerDefaultizedProps } from '../DateTimePicker/shared';
import { renderDateViewCalendar } from '../dateViewRenderers/dateViewRenderers';
-import { usePickerTranslations } from '../hooks/usePickerTranslations';
import { useUtils } from '../internals/hooks/useUtils';
import { validateDateTime, extractValidationProps } from '../validation';
import { DateOrTimeViewWithMeridiem, PickerValue } from '../internals/models';
-import { CalendarIcon } from '../icons';
import { useDesktopPicker } from '../internals/hooks/useDesktopPicker';
import {
resolveDateTimeFormat,
@@ -38,7 +36,6 @@ import {
} from '../internals/hooks/usePicker/usePickerViews';
import { isInternalTimeView } from '../internals/utils/time-utils';
import { isDatePickerView } from '../internals/utils/date-utils';
-import { buildGetOpenDialogAriaText } from '../locales/utils/getPickersLocalization';
const rendererInterceptor = function RendererInterceptor(
props: PickerRendererInterceptorProps,
@@ -112,7 +109,6 @@ const DesktopDateTimePicker = React.forwardRef(function DesktopDateTimePicker<
inProps: DesktopDateTimePickerProps,
ref: React.Ref,
) {
- const translations = usePickerTranslations();
const utils = useUtils();
// Props with the default values common to all date time pickers
@@ -164,7 +160,6 @@ const DesktopDateTimePicker = React.forwardRef(function DesktopDateTimePicker<
slots: {
field: DateTimeField,
layout: DesktopDateTimePickerLayout,
- openPickerIcon: CalendarIcon,
...defaultizedProps.slots,
},
slotProps: {
@@ -194,12 +189,6 @@ const DesktopDateTimePicker = React.forwardRef(function DesktopDateTimePicker<
props,
valueManager: singleItemValueManager,
valueType: 'date-time',
- getOpenDialogAriaText: buildGetOpenDialogAriaText({
- utils,
- formatKey: 'fullDate',
- contextTranslation: translations.openDatePickerDialogue,
- propsTranslation: props.localeText?.openDatePickerDialogue,
- }),
validator: validateDateTime,
rendererInterceptor,
});
diff --git a/packages/x-date-pickers/src/DesktopDateTimePicker/tests/DesktopDateTimePicker.test.tsx b/packages/x-date-pickers/src/DesktopDateTimePicker/tests/DesktopDateTimePicker.test.tsx
index fda2d1f68cf0f..f3b787bfe1473 100644
--- a/packages/x-date-pickers/src/DesktopDateTimePicker/tests/DesktopDateTimePicker.test.tsx
+++ b/packages/x-date-pickers/src/DesktopDateTimePicker/tests/DesktopDateTimePicker.test.tsx
@@ -35,7 +35,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'date-time', variant: 'desktop' });
+ openPicker({ type: 'date-time' });
// Select year
fireEvent.click(screen.getByRole('radio', { name: '2025' }));
@@ -81,7 +81,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'date-time', variant: 'desktop' });
+ openPicker({ type: 'date-time' });
// Change the date multiple times to check that picker doesn't close after cycling through all views internally
fireEvent.click(screen.getByRole('gridcell', { name: '2' }));
diff --git a/packages/x-date-pickers/src/DesktopDateTimePicker/tests/describes.DesktopDateTimePicker.test.tsx b/packages/x-date-pickers/src/DesktopDateTimePicker/tests/describes.DesktopDateTimePicker.test.tsx
index 1370af67311b2..551a73fee6e39 100644
--- a/packages/x-date-pickers/src/DesktopDateTimePicker/tests/describes.DesktopDateTimePicker.test.tsx
+++ b/packages/x-date-pickers/src/DesktopDateTimePicker/tests/describes.DesktopDateTimePicker.test.tsx
@@ -36,7 +36,6 @@ describe(' - Describes', () => {
clock,
views: ['year', 'month', 'day', 'hours', 'minutes'],
componentFamily: 'picker',
- variant: 'desktop',
}));
describeConformance(, () => ({
diff --git a/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.tsx b/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.tsx
index 01bcb39d8eccc..777a2806aeea7 100644
--- a/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.tsx
+++ b/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.tsx
@@ -7,10 +7,8 @@ import { singleItemValueManager } from '../internals/utils/valueManagers';
import { TimeField } from '../TimeField';
import { DesktopTimePickerProps } from './DesktopTimePicker.types';
import { TimePickerViewRenderers, useTimePickerDefaultizedProps } from '../TimePicker/shared';
-import { usePickerTranslations } from '../hooks/usePickerTranslations';
import { useUtils } from '../internals/hooks/useUtils';
import { extractValidationProps, validateTime } from '../validation';
-import { ClockIcon } from '../icons';
import { useDesktopPicker } from '../internals/hooks/useDesktopPicker';
import {
renderDigitalClockTimeView,
@@ -20,7 +18,6 @@ import { TimeViewWithMeridiem } from '../internals/models';
import { resolveTimeFormat } from '../internals/utils/time-utils';
import { resolveTimeViewsResponse } from '../internals/utils/date-time-utils';
import { TimeView, PickerOwnerState } from '../models';
-import { buildGetOpenDialogAriaText } from '../locales/utils/getPickersLocalization';
type DesktopTimePickerComponent = ((
props: DesktopTimePickerProps &
@@ -43,7 +40,6 @@ const DesktopTimePicker = React.forwardRef(function DesktopTimePicker<
inProps: DesktopTimePickerProps,
ref: React.Ref,
) {
- const translations = usePickerTranslations();
const utils = useUtils();
// Props with the default values common to all time pickers
@@ -90,7 +86,6 @@ const DesktopTimePicker = React.forwardRef(function DesktopTimePicker<
views: shouldRenderTimeInASingleColumn ? ['hours' as TimeViewWithMeridiem] : views,
slots: {
field: TimeField,
- openPickerIcon: ClockIcon,
...defaultizedProps.slots,
},
slotProps: {
@@ -116,12 +111,6 @@ const DesktopTimePicker = React.forwardRef(function DesktopTimePicker<
props,
valueManager: singleItemValueManager,
valueType: 'time',
- getOpenDialogAriaText: buildGetOpenDialogAriaText({
- utils,
- formatKey: 'fullTime',
- contextTranslation: translations.openTimePickerDialogue,
- propsTranslation: props.localeText?.openTimePickerDialogue,
- }),
validator: validateTime,
});
diff --git a/packages/x-date-pickers/src/DesktopTimePicker/tests/DesktopTimePicker.test.tsx b/packages/x-date-pickers/src/DesktopTimePicker/tests/DesktopTimePicker.test.tsx
index 096830e2565b8..e1fca7f54c978 100644
--- a/packages/x-date-pickers/src/DesktopTimePicker/tests/DesktopTimePicker.test.tsx
+++ b/packages/x-date-pickers/src/DesktopTimePicker/tests/DesktopTimePicker.test.tsx
@@ -75,7 +75,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'time', variant: 'desktop' });
+ openPicker({ type: 'time' });
fireEvent.click(screen.getByRole('option', { name: '09:00 AM' }));
expect(onChange.callCount).to.equal(1);
@@ -104,7 +104,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'time', variant: 'desktop' });
+ openPicker({ type: 'time' });
fireEvent.click(screen.getByRole('option', { name: '2 hours' }));
expect(onChange.callCount).to.equal(1);
@@ -142,7 +142,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'time', variant: 'desktop' });
+ openPicker({ type: 'time' });
fireEvent.click(screen.getByRole('option', { name: '15 minutes' }));
expect(onChange.callCount).to.equal(1);
@@ -185,7 +185,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'time', variant: 'desktop' });
+ openPicker({ type: 'time' });
fireEvent.click(screen.getByRole('option', { name: 'PM' }));
expect(onChange.callCount).to.equal(1);
diff --git a/packages/x-date-pickers/src/DesktopTimePicker/tests/describes.DesktopTimePicker.test.tsx b/packages/x-date-pickers/src/DesktopTimePicker/tests/describes.DesktopTimePicker.test.tsx
index 700a92a4156ff..77caadcfb15c8 100644
--- a/packages/x-date-pickers/src/DesktopTimePicker/tests/describes.DesktopTimePicker.test.tsx
+++ b/packages/x-date-pickers/src/DesktopTimePicker/tests/describes.DesktopTimePicker.test.tsx
@@ -28,7 +28,6 @@ describe(' - Describes', () => {
clock,
views: ['hours', 'minutes'],
componentFamily: 'picker',
- variant: 'desktop',
}));
describeConformance(, () => ({
diff --git a/packages/x-date-pickers/src/DigitalClock/tests/describes.DigitalClock.test.tsx b/packages/x-date-pickers/src/DigitalClock/tests/describes.DigitalClock.test.tsx
index c4f5013abc991..4ec445f96f690 100644
--- a/packages/x-date-pickers/src/DigitalClock/tests/describes.DigitalClock.test.tsx
+++ b/packages/x-date-pickers/src/DigitalClock/tests/describes.DigitalClock.test.tsx
@@ -21,7 +21,6 @@ describe(' - Describes', () => {
clock,
views: ['hours'],
componentFamily: 'digital-clock',
- variant: 'desktop',
}));
describeConformance(, () => ({
@@ -37,7 +36,6 @@ describe(' - Describes', () => {
render,
componentFamily: 'digital-clock',
type: 'time',
- variant: 'desktop',
defaultProps: {
views: ['hours'],
},
diff --git a/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.tsx b/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.tsx
index 3f40a6ad1f1f4..a9f01953e7dba 100644
--- a/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.tsx
+++ b/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.tsx
@@ -6,7 +6,6 @@ import { refType } from '@mui/utils';
import { useMobilePicker } from '../internals/hooks/useMobilePicker';
import { MobileDatePickerProps } from './MobileDatePicker.types';
import { DatePickerViewRenderers, useDatePickerDefaultizedProps } from '../DatePicker/shared';
-import { usePickerTranslations } from '../hooks/usePickerTranslations';
import { useUtils } from '../internals/hooks/useUtils';
import { extractValidationProps, validateDate } from '../validation';
import { DateView, PickerOwnerState } from '../models';
@@ -14,7 +13,6 @@ import { DateField } from '../DateField';
import { singleItemValueManager } from '../internals/utils/valueManagers';
import { renderDateViewCalendar } from '../dateViewRenderers';
import { resolveDateFormat } from '../internals/utils/date-utils';
-import { buildGetOpenDialogAriaText } from '../locales/utils/getPickersLocalization';
type MobileDatePickerComponent = ((
props: MobileDatePickerProps &
@@ -37,7 +35,6 @@ const MobileDatePicker = React.forwardRef(function MobileDatePicker<
inProps: MobileDatePickerProps,
ref: React.Ref,
) {
- const translations = usePickerTranslations();
const utils = useUtils();
// Props with the default values common to all date pickers
@@ -83,12 +80,6 @@ const MobileDatePicker = React.forwardRef(function MobileDatePicker<
props,
valueManager: singleItemValueManager,
valueType: 'date',
- getOpenDialogAriaText: buildGetOpenDialogAriaText({
- utils,
- formatKey: 'fullDate',
- contextTranslation: translations.openDatePickerDialogue,
- propsTranslation: props.localeText?.openDatePickerDialogue,
- }),
validator: validateDate,
});
diff --git a/packages/x-date-pickers/src/MobileDatePicker/tests/MobileDatePicker.test.tsx b/packages/x-date-pickers/src/MobileDatePicker/tests/MobileDatePicker.test.tsx
index 586d963955a2d..1c6255189b9e3 100644
--- a/packages/x-date-pickers/src/MobileDatePicker/tests/MobileDatePicker.test.tsx
+++ b/packages/x-date-pickers/src/MobileDatePicker/tests/MobileDatePicker.test.tsx
@@ -11,7 +11,6 @@ import {
expectFieldValueV7,
buildFieldInteractions,
openPicker,
- getFieldSectionsContainer,
} from 'test/utils/pickers';
describe('', () => {
@@ -132,17 +131,6 @@ describe('', () => {
});
describe('picker state', () => {
- it('should open when clicking the input', () => {
- const onOpen = spy();
-
- render();
-
- fireEvent.click(getFieldSectionsContainer());
-
- expect(onOpen.callCount).to.equal(1);
- expect(screen.queryByRole('dialog')).toBeVisible();
- });
-
it('should call `onAccept` even if controlled', () => {
const onAccept = spy();
@@ -154,7 +142,7 @@ describe('', () => {
render();
- openPicker({ type: 'date', variant: 'mobile' });
+ openPicker({ type: 'date' });
fireEvent.click(screen.getByText('15', { selector: 'button' }));
fireEvent.click(screen.getByText('OK', { selector: 'button' }));
@@ -176,7 +164,7 @@ describe('', () => {
expectFieldValueV7(view.getSectionsContainer(), 'MM/DD/YYYY');
// Open and Dismiss the picker
- openPicker({ type: 'date', variant: 'mobile' });
+ openPicker({ type: 'date' });
// eslint-disable-next-line material-ui/disallow-active-element-as-key-event-target
fireEvent.keyDown(document.activeElement!, { key: 'Escape' });
clock.runToLast();
diff --git a/packages/x-date-pickers/src/MobileDatePicker/tests/describes.MobileDatePicker.test.tsx b/packages/x-date-pickers/src/MobileDatePicker/tests/describes.MobileDatePicker.test.tsx
index 2a75c950b93df..ed5371703071e 100644
--- a/packages/x-date-pickers/src/MobileDatePicker/tests/describes.MobileDatePicker.test.tsx
+++ b/packages/x-date-pickers/src/MobileDatePicker/tests/describes.MobileDatePicker.test.tsx
@@ -61,7 +61,7 @@ describe(' - Describes', () => {
},
setNewValue: (value, { isOpened, applySameValue }) => {
if (!isOpened) {
- openPicker({ type: 'date', variant: 'mobile' });
+ openPicker({ type: 'date' });
}
const newValue = applySameValue ? value! : adapterToUse.addDays(value!, 1);
diff --git a/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx b/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx
index c0410f5d2499a..93cc5ca8ed812 100644
--- a/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx
+++ b/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx
@@ -7,7 +7,6 @@ import { singleItemValueManager } from '../internals/utils/valueManagers';
import { DateTimeField } from '../DateTimeField';
import { MobileDateTimePickerProps } from './MobileDateTimePicker.types';
import { useDateTimePickerDefaultizedProps } from '../DateTimePicker/shared';
-import { usePickerTranslations } from '../hooks/usePickerTranslations';
import { useUtils } from '../internals/hooks/useUtils';
import { extractValidationProps, validateDateTime } from '../validation';
import { DateOrTimeView, PickerOwnerState } from '../models';
@@ -15,7 +14,6 @@ import { useMobilePicker } from '../internals/hooks/useMobilePicker';
import { renderDateViewCalendar } from '../dateViewRenderers';
import { renderTimeViewClock } from '../timeViewRenderers';
import { resolveDateTimeFormat } from '../internals/utils/date-time-utils';
-import { buildGetOpenDialogAriaText } from '../locales/utils/getPickersLocalization';
import { PickerViewRendererLookup } from '../internals/hooks/usePicker/usePickerViews';
import { PickerValue } from '../internals/models';
@@ -40,7 +38,6 @@ const MobileDateTimePicker = React.forwardRef(function MobileDateTimePicker<
inProps: MobileDateTimePickerProps,
ref: React.Ref,
) {
- const translations = usePickerTranslations();
const utils = useUtils();
// Props with the default values common to all date time pickers
@@ -97,12 +94,6 @@ const MobileDateTimePicker = React.forwardRef(function MobileDateTimePicker<
props,
valueManager: singleItemValueManager,
valueType: 'date-time',
- getOpenDialogAriaText: buildGetOpenDialogAriaText({
- utils,
- formatKey: 'fullDate',
- contextTranslation: translations.openDatePickerDialogue,
- propsTranslation: props.localeText?.openDatePickerDialogue,
- }),
validator: validateDateTime,
});
diff --git a/packages/x-date-pickers/src/MobileDateTimePicker/tests/MobileDateTimePicker.test.tsx b/packages/x-date-pickers/src/MobileDateTimePicker/tests/MobileDateTimePicker.test.tsx
index 16a032c454a06..34f64d19cfe3e 100644
--- a/packages/x-date-pickers/src/MobileDateTimePicker/tests/MobileDateTimePicker.test.tsx
+++ b/packages/x-date-pickers/src/MobileDateTimePicker/tests/MobileDateTimePicker.test.tsx
@@ -8,7 +8,6 @@ import {
createPickerRenderer,
openPicker,
getClockTouchEvent,
- getFieldSectionsContainer,
} from 'test/utils/pickers';
import { hasTouchSupport, testSkipIf } from 'test/utils/skipIf';
@@ -84,17 +83,6 @@ describe('', () => {
});
describe('picker state', () => {
- it('should open when clicking the input', () => {
- const onOpen = spy();
-
- render();
-
- fireEvent.click(getFieldSectionsContainer());
-
- expect(onOpen.callCount).to.equal(1);
- expect(screen.queryByRole('dialog')).toBeVisible();
- });
-
testSkipIf(!hasTouchSupport)('should call onChange when selecting each view', () => {
const onChange = spy();
const onAccept = spy();
@@ -111,7 +99,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'date-time', variant: 'mobile' });
+ openPicker({ type: 'date-time' });
expect(onChange.callCount).to.equal(0);
expect(onAccept.callCount).to.equal(0);
expect(onClose.callCount).to.equal(0);
diff --git a/packages/x-date-pickers/src/MobileDateTimePicker/tests/describes.MobileDateTimePicker.test.tsx b/packages/x-date-pickers/src/MobileDateTimePicker/tests/describes.MobileDateTimePicker.test.tsx
index 30ae63e192e42..88975bfea2bd1 100644
--- a/packages/x-date-pickers/src/MobileDateTimePicker/tests/describes.MobileDateTimePicker.test.tsx
+++ b/packages/x-date-pickers/src/MobileDateTimePicker/tests/describes.MobileDateTimePicker.test.tsx
@@ -28,7 +28,6 @@ describe(' - Describes', () => {
clock,
views: ['year', 'day', 'hours', 'minutes'],
componentFamily: 'picker',
- variant: 'mobile',
}));
describeConformance(, () => ({
@@ -73,7 +72,7 @@ describe(' - Describes', () => {
},
setNewValue: (value, { isOpened, applySameValue }) => {
if (!isOpened) {
- openPicker({ type: 'date-time', variant: 'mobile' });
+ openPicker({ type: 'date-time' });
}
const newValue = applySameValue
diff --git a/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.tsx b/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.tsx
index e36e6cf1cd416..f73e78b7562fe 100644
--- a/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.tsx
+++ b/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.tsx
@@ -7,14 +7,12 @@ import { singleItemValueManager } from '../internals/utils/valueManagers';
import { TimeField } from '../TimeField';
import { MobileTimePickerProps } from './MobileTimePicker.types';
import { TimePickerViewRenderers, useTimePickerDefaultizedProps } from '../TimePicker/shared';
-import { usePickerTranslations } from '../hooks/usePickerTranslations';
import { useUtils } from '../internals/hooks/useUtils';
import { extractValidationProps, validateTime } from '../validation';
import { PickerOwnerState, TimeView } from '../models';
import { useMobilePicker } from '../internals/hooks/useMobilePicker';
import { renderTimeViewClock } from '../timeViewRenderers';
import { resolveTimeFormat } from '../internals/utils/time-utils';
-import { buildGetOpenDialogAriaText } from '../locales/utils/getPickersLocalization';
type MobileTimePickerComponent = ((
props: MobileTimePickerProps &
@@ -37,7 +35,6 @@ const MobileTimePicker = React.forwardRef(function MobileTimePicker<
inProps: MobileTimePickerProps,
ref: React.Ref,
) {
- const translations = usePickerTranslations();
const utils = useUtils();
// Props with the default values common to all time pickers
@@ -87,12 +84,6 @@ const MobileTimePicker = React.forwardRef(function MobileTimePicker<
props,
valueManager: singleItemValueManager,
valueType: 'time',
- getOpenDialogAriaText: buildGetOpenDialogAriaText({
- utils,
- formatKey: 'fullTime',
- contextTranslation: translations.openTimePickerDialogue,
- propsTranslation: props.localeText?.openTimePickerDialogue,
- }),
validator: validateTime,
});
diff --git a/packages/x-date-pickers/src/MobileTimePicker/tests/MobileTimePicker.test.tsx b/packages/x-date-pickers/src/MobileTimePicker/tests/MobileTimePicker.test.tsx
index b902e6808b17b..a673288bd284e 100644
--- a/packages/x-date-pickers/src/MobileTimePicker/tests/MobileTimePicker.test.tsx
+++ b/packages/x-date-pickers/src/MobileTimePicker/tests/MobileTimePicker.test.tsx
@@ -8,7 +8,6 @@ import {
adapterToUse,
openPicker,
getClockTouchEvent,
- getFieldSectionsContainer,
} from 'test/utils/pickers';
import { testSkipIf, hasTouchSupport } from 'test/utils/skipIf';
@@ -16,17 +15,6 @@ describe('', () => {
const { render } = createPickerRenderer({ clock: 'fake' });
describe('picker state', () => {
- it('should open when clicking the input', () => {
- const onOpen = spy();
-
- render();
-
- fireEvent.click(getFieldSectionsContainer());
-
- expect(onOpen.callCount).to.equal(1);
- expect(screen.queryByRole('dialog')).toBeVisible();
- });
-
it('should fire a change event when meridiem changes', () => {
const handleChange = spy();
render(
@@ -61,7 +49,7 @@ describe('', () => {
/>,
);
- openPicker({ type: 'time', variant: 'mobile' });
+ openPicker({ type: 'time' });
// Change the hours
const hourClockEvent = getClockTouchEvent(11, '12hours');
diff --git a/packages/x-date-pickers/src/MobileTimePicker/tests/describes.MobileTimePicker.test.tsx b/packages/x-date-pickers/src/MobileTimePicker/tests/describes.MobileTimePicker.test.tsx
index 5cff45ba0c9ce..cb211de30b1dc 100644
--- a/packages/x-date-pickers/src/MobileTimePicker/tests/describes.MobileTimePicker.test.tsx
+++ b/packages/x-date-pickers/src/MobileTimePicker/tests/describes.MobileTimePicker.test.tsx
@@ -29,7 +29,6 @@ describe(' - Describes', () => {
clock,
views: ['hours', 'minutes'],
componentFamily: 'picker',
- variant: 'mobile',
}));
describeConformance(, () => ({
@@ -71,7 +70,7 @@ describe(' - Describes', () => {
},
setNewValue: (value, { isOpened, applySameValue }) => {
if (!isOpened) {
- openPicker({ type: 'time', variant: 'mobile' });
+ openPicker({ type: 'time' });
}
const newValue = applySameValue
diff --git a/packages/x-date-pickers/src/MultiSectionDigitalClock/tests/describes.MultiSectionDigitalClock.test.tsx b/packages/x-date-pickers/src/MultiSectionDigitalClock/tests/describes.MultiSectionDigitalClock.test.tsx
index 3ab127d6bafa6..992c33dc6decd 100644
--- a/packages/x-date-pickers/src/MultiSectionDigitalClock/tests/describes.MultiSectionDigitalClock.test.tsx
+++ b/packages/x-date-pickers/src/MultiSectionDigitalClock/tests/describes.MultiSectionDigitalClock.test.tsx
@@ -23,7 +23,6 @@ describe(' - Describes', () => {
clock,
views: ['hours', 'minutes'],
componentFamily: 'multi-section-digital-clock',
- variant: 'desktop',
}));
describeConformance(, () => ({
@@ -39,7 +38,6 @@ describe(' - Describes', () => {
render,
componentFamily: 'multi-section-digital-clock',
type: 'time',
- variant: 'desktop',
values: [adapterToUse.date('2018-01-01T11:30:00'), adapterToUse.date('2018-01-01T12:35:00')],
emptyValue: null,
clock,
diff --git a/packages/x-date-pickers/src/TimeField/TimeField.tsx b/packages/x-date-pickers/src/TimeField/TimeField.tsx
index e99c0e4166965..c92a4ac60e896 100644
--- a/packages/x-date-pickers/src/TimeField/TimeField.tsx
+++ b/packages/x-date-pickers/src/TimeField/TimeField.tsx
@@ -1,16 +1,12 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
-import MuiTextField from '@mui/material/TextField';
import { useThemeProps } from '@mui/material/styles';
-import useSlotProps from '@mui/utils/useSlotProps';
import { refType } from '@mui/utils';
import { TimeFieldProps } from './TimeField.types';
import { useTimeField } from './useTimeField';
-import { useClearableField } from '../hooks';
-import { PickersTextField } from '../PickersTextField';
-import { convertFieldResponseIntoMuiTextFieldProps } from '../internals/utils/convertFieldResponseIntoMuiTextFieldProps';
-import { useFieldOwnerState } from '../internals/hooks/useFieldOwnerState';
+import { PickerFieldUI, useFieldTextFieldProps } from '../internals/components/PickerFieldUI';
+import { ClockIcon } from '../icons';
type TimeFieldComponent = ((
props: TimeFieldProps & React.RefAttributes,
@@ -36,38 +32,26 @@ const TimeField = React.forwardRef(function TimeField<
const { slots, slotProps, InputProps, inputProps, ...other } = themeProps;
- const ownerState = useFieldOwnerState(themeProps);
-
- const textFieldProps = useSlotProps({
- elementType: PickersTextField,
- externalSlotProps: slotProps?.textField,
- externalForwardedProps: other,
- ownerState,
- additionalProps: {
+ const textFieldProps = useFieldTextFieldProps>(
+ {
+ slotProps,
ref: inRef,
+ externalForwardedProps: other,
},
- }) as TimeFieldProps;
-
- // TODO: Remove when mui/material-ui#35088 will be merged
- textFieldProps.inputProps = { ...inputProps, ...textFieldProps.inputProps };
- textFieldProps.InputProps = { ...InputProps, ...textFieldProps.InputProps };
+ );
const fieldResponse = useTimeField(
textFieldProps,
);
- const convertedFieldResponse = convertFieldResponseIntoMuiTextFieldProps(fieldResponse);
-
- const processedFieldProps = useClearableField({
- ...convertedFieldResponse,
- slots,
- slotProps,
- });
-
- const TextField =
- slots?.textField ??
- (fieldResponse.enableAccessibleFieldDOMStructure === false ? MuiTextField : PickersTextField);
- return ;
+ return (
+
+ );
}) as TimeFieldComponent;
TimeField.propTypes = {
@@ -91,6 +75,12 @@ TimeField.propTypes = {
* @default false
*/
clearable: PropTypes.bool,
+ /**
+ * The position at which the clear button is placed.
+ * If the field is not clearable, the button is not rendered.
+ * @default 'end'
+ */
+ clearButtonPosition: PropTypes.oneOf(['end', 'start']),
/**
* The color of the component.
* It supports both default and custom theme colors, which can be added as shown in the
@@ -244,6 +234,12 @@ TimeField.propTypes = {
* @param {FieldSelectedSections} newValue The new selected sections.
*/
onSelectedSectionsChange: PropTypes.func,
+ /**
+ * The position at which the opening button is placed.
+ * If there is no picker to open, the button is not rendered
+ * @default 'end'
+ */
+ openPickerButtonPosition: PropTypes.oneOf(['end', 'start']),
/**
* If `true`, the component is read-only.
* When read-only, the value cannot be changed but the user can interact with the interface.
diff --git a/packages/x-date-pickers/src/TimeField/TimeField.types.ts b/packages/x-date-pickers/src/TimeField/TimeField.types.ts
index 819535baa8fb5..ce58f46cd7e27 100644
--- a/packages/x-date-pickers/src/TimeField/TimeField.types.ts
+++ b/packages/x-date-pickers/src/TimeField/TimeField.types.ts
@@ -1,17 +1,14 @@
-import * as React from 'react';
-import type { TextFieldProps } from '@mui/material/TextField';
-import { MakeOptional, SlotComponentPropsFromProps } from '@mui/x-internals/types';
+import { MakeOptional } from '@mui/x-internals/types';
import { UseFieldInternalProps } from '../internals/hooks/useField';
-import { TimeValidationError, BuiltInFieldTextFieldProps, FieldOwnerState } from '../models';
-import {
- ExportedUseClearableFieldProps,
- UseClearableFieldSlots,
- UseClearableFieldSlotProps,
-} from '../hooks/useClearableField';
+import { TimeValidationError, BuiltInFieldTextFieldProps } from '../models';
import { ExportedValidateTimeProps } from '../validation/validateTime';
import { AmPmProps } from '../internals/models/props/time';
import { PickerValue } from '../internals/models';
-import { PickersTextFieldProps } from '../PickersTextField';
+import {
+ ExportedPickerFieldUIProps,
+ PickerFieldUISlotProps,
+ PickerFieldUISlots,
+} from '../internals/components/PickerFieldUI';
export interface UseTimeFieldProps
extends MakeOptional<
@@ -19,7 +16,7 @@ export interface UseTimeFieldProps,
ExportedValidateTimeProps,
- ExportedUseClearableFieldProps,
+ ExportedPickerFieldUIProps,
AmPmProps {}
export type TimeFieldProps =
@@ -42,18 +39,6 @@ export type TimeFieldProps, or from '@mui/material' if `enableAccessibleFieldDOMStructure` is `false`.
- */
- textField?: React.ElementType;
-}
+export interface TimeFieldSlots extends PickerFieldUISlots {}
-export interface TimeFieldSlotProps extends UseClearableFieldSlotProps {
- textField?: SlotComponentPropsFromProps<
- PickersTextFieldProps | TextFieldProps,
- {},
- FieldOwnerState
- >;
-}
+export interface TimeFieldSlotProps extends PickerFieldUISlotProps {}
diff --git a/packages/x-date-pickers/src/TimeField/useTimeField.ts b/packages/x-date-pickers/src/TimeField/useTimeField.ts
index 5b6e155433d54..78630fb8e8e79 100644
--- a/packages/x-date-pickers/src/TimeField/useTimeField.ts
+++ b/packages/x-date-pickers/src/TimeField/useTimeField.ts
@@ -30,5 +30,6 @@ export const useTimeField = <
fieldValueManager: manager.internal_fieldValueManager,
validator: manager.validator,
valueType: manager.valueType,
+ getOpenPickerButtonAriaLabel: manager.internal_getOpenPickerButtonAriaLabel,
});
};
diff --git a/packages/x-date-pickers/src/TimePicker/tests/TimePicker.test.tsx b/packages/x-date-pickers/src/TimePicker/tests/TimePicker.test.tsx
index aaa91fd391fa5..d8c17b9ef0bc3 100644
--- a/packages/x-date-pickers/src/TimePicker/tests/TimePicker.test.tsx
+++ b/packages/x-date-pickers/src/TimePicker/tests/TimePicker.test.tsx
@@ -1,12 +1,11 @@
import * as React from 'react';
-import { TimePicker } from '@mui/x-date-pickers/TimePicker';
-import { screen } from '@mui/internal-test-utils/createRenderer';
import { expect } from 'chai';
+import { TimePicker } from '@mui/x-date-pickers/TimePicker';
+import { fireEvent, screen } from '@mui/internal-test-utils/createRenderer';
import { createPickerRenderer, stubMatchMedia } from 'test/utils/pickers';
-import { pickersInputBaseClasses } from '@mui/x-date-pickers/PickersTextField';
describe('', () => {
- const { render } = createPickerRenderer();
+ const { render } = createPickerRenderer({ clock: 'fake' });
it('should render in mobile mode when `useMediaQuery` returns `false`', () => {
const originalMatchMedia = window.matchMedia;
@@ -14,7 +13,8 @@ describe('', () => {
render();
- expect(screen.getByLabelText(/Choose time/)).to.have.class(pickersInputBaseClasses.input);
+ fireEvent.click(screen.getByLabelText(/Choose time/));
+ expect(screen.queryByRole('dialog')).to.not.equal(null);
window.matchMedia = originalMatchMedia;
});
diff --git a/packages/x-date-pickers/src/internals/components/PickerFieldUI.tsx b/packages/x-date-pickers/src/internals/components/PickerFieldUI.tsx
new file mode 100644
index 0000000000000..3f78963434412
--- /dev/null
+++ b/packages/x-date-pickers/src/internals/components/PickerFieldUI.tsx
@@ -0,0 +1,500 @@
+import * as React from 'react';
+import useEventCallback from '@mui/utils/useEventCallback';
+import resolveComponentProps from '@mui/utils/resolveComponentProps';
+import MuiTextField, { TextFieldProps } from '@mui/material/TextField';
+import MuiIconButton, { IconButtonProps } from '@mui/material/IconButton';
+import MuiInputAdornment, { InputAdornmentProps } from '@mui/material/InputAdornment';
+import { SvgIconProps } from '@mui/material/SvgIcon';
+import useSlotProps from '@mui/utils/useSlotProps';
+import { SlotComponentPropsFromProps } from '@mui/x-internals/types';
+import { FieldOwnerState } from '../../models';
+import { useFieldOwnerState, UseFieldOwnerStateParameters } from '../hooks/useFieldOwnerState';
+import { usePickerTranslations } from '../../hooks';
+import { ClearIcon as MuiClearIcon } from '../../icons';
+import { useNullablePickerContext } from '../hooks/useNullablePickerContext';
+import type { UseFieldResponse } from '../hooks/useField';
+import { PickersTextField, PickersTextFieldProps } from '../../PickersTextField';
+
+export const cleanFieldResponse = <
+ TFieldResponse extends UseFieldResponse,
+>({
+ enableAccessibleFieldDOMStructure,
+ ...fieldResponse
+}: TFieldResponse): ExportedPickerFieldUIProps & {
+ openPickerAriaLabel: string;
+ textFieldProps: TextFieldProps | PickersTextFieldProps;
+} => {
+ if (enableAccessibleFieldDOMStructure) {
+ const {
+ InputProps,
+ readOnly,
+ onClear,
+ clearable,
+ clearButtonPosition,
+ openPickerButtonPosition,
+ openPickerAriaLabel,
+ ...other
+ } = fieldResponse;
+
+ return {
+ clearable,
+ onClear,
+ clearButtonPosition,
+ openPickerButtonPosition,
+ openPickerAriaLabel,
+ textFieldProps: {
+ ...other,
+ InputProps: { ...(InputProps ?? {}), readOnly },
+ },
+ };
+ }
+
+ const {
+ onPaste,
+ onKeyDown,
+ inputMode,
+ readOnly,
+ InputProps,
+ inputProps,
+ inputRef,
+ onClear,
+ clearable,
+ clearButtonPosition,
+ openPickerButtonPosition,
+ openPickerAriaLabel,
+ ...other
+ } = fieldResponse;
+
+ return {
+ clearable,
+ onClear,
+ clearButtonPosition,
+ openPickerButtonPosition,
+ openPickerAriaLabel,
+ textFieldProps: {
+ ...other,
+ InputProps: { ...(InputProps ?? {}), readOnly },
+ inputProps: { ...(inputProps ?? {}), inputMode, onPaste, onKeyDown, ref: inputRef },
+ },
+ };
+};
+
+const PickerFieldUIContext = React.createContext({
+ slots: {},
+ slotProps: {},
+});
+
+/**
+ * Adds the button to open the picker and the button to clear the value of the field.
+ * @ignore - internal component.
+ */
+export function PickerFieldUI(props: PickerFieldUIProps) {
+ const { slots, slotProps, fieldResponse, defaultOpenPickerIcon } = props;
+
+ const translations = usePickerTranslations();
+ const pickerContext = useNullablePickerContext();
+ const pickerFieldUIContext = React.useContext(PickerFieldUIContext);
+ const {
+ textFieldProps,
+ onClear,
+ clearable,
+ openPickerAriaLabel,
+ clearButtonPosition: clearButtonPositionProp = 'end',
+ openPickerButtonPosition: openPickerButtonPositionProp = 'end',
+ } = cleanFieldResponse(fieldResponse);
+ const ownerState = useFieldOwnerState(textFieldProps);
+
+ const handleClickOpeningButton = useEventCallback((event: React.MouseEvent) => {
+ event.preventDefault();
+ pickerContext?.setOpen((prev) => !prev);
+ });
+
+ const triggerStatus = pickerContext ? pickerContext.triggerStatus : 'hidden';
+ const clearButtonPosition = clearable ? clearButtonPositionProp : null;
+ const openPickerButtonPosition = triggerStatus !== 'hidden' ? openPickerButtonPositionProp : null;
+
+ const TextField =
+ slots?.textField ??
+ pickerFieldUIContext.slots.textField ??
+ (fieldResponse.enableAccessibleFieldDOMStructure === false ? MuiTextField : PickersTextField);
+
+ const InputAdornment =
+ slots?.inputAdornment ?? pickerFieldUIContext.slots.inputAdornment ?? MuiInputAdornment;
+ const { ownerState: startInputAdornmentOwnerState, ...startInputAdornmentProps } = useSlotProps({
+ elementType: InputAdornment,
+ externalSlotProps: mergeSlotProps(
+ pickerFieldUIContext.slotProps.inputAdornment,
+ slotProps?.inputAdornment,
+ ),
+ additionalProps: {
+ position: 'start' as const,
+ },
+ ownerState: { ...ownerState, position: 'start' },
+ });
+ const { ownerState: endInputAdornmentOwnerState, ...endInputAdornmentProps } = useSlotProps({
+ elementType: InputAdornment,
+ externalSlotProps: slotProps?.inputAdornment,
+ additionalProps: {
+ position: 'end' as const,
+ },
+ ownerState: { ...ownerState, position: 'end' },
+ });
+
+ const OpenPickerButton = pickerFieldUIContext.slots.openPickerButton ?? MuiIconButton;
+ // We don't want to forward the `ownerState` to the `` component, see mui/material-ui#34056
+ const {
+ ownerState: openPickerButtonOwnerState,
+ ...openPickerButtonProps
+ }: IconButtonProps & { ownerState: any } = useSlotProps({
+ elementType: OpenPickerButton,
+ externalSlotProps: pickerFieldUIContext.slotProps.openPickerButton,
+ additionalProps: {
+ disabled: triggerStatus === 'disabled',
+ onClick: handleClickOpeningButton,
+ 'aria-label': openPickerAriaLabel,
+ edge:
+ clearButtonPosition === 'start' && openPickerButtonPosition === 'start'
+ ? undefined
+ : openPickerButtonPosition,
+ },
+ ownerState,
+ });
+
+ const OpenPickerIcon = pickerFieldUIContext.slots.openPickerIcon ?? defaultOpenPickerIcon;
+ const openPickerIconProps = useSlotProps({
+ elementType: OpenPickerIcon,
+ externalSlotProps: pickerFieldUIContext.slotProps.openPickerIcon,
+ ownerState,
+ });
+
+ const ClearButton = slots?.clearButton ?? pickerFieldUIContext.slots.clearButton ?? MuiIconButton;
+ // We don't want to forward the `ownerState` to the `` component, see mui/material-ui#34056
+ const { ownerState: clearButtonOwnerState, ...clearButtonProps } = useSlotProps({
+ elementType: ClearButton,
+ externalSlotProps: mergeSlotProps(
+ pickerFieldUIContext.slotProps.clearButton,
+ slotProps?.clearButton,
+ ),
+ className: 'clearButton',
+ additionalProps: {
+ title: translations.fieldClearLabel,
+ tabIndex: -1,
+ onClick: onClear,
+ disabled: fieldResponse.disabled || fieldResponse.readOnly,
+ edge:
+ clearButtonPosition === 'end' && openPickerButtonPosition === 'end'
+ ? undefined
+ : clearButtonPosition,
+ },
+ ownerState,
+ });
+
+ const ClearIcon = slots?.clearIcon ?? pickerFieldUIContext.slots.clearIcon ?? MuiClearIcon;
+ const clearIconProps = useSlotProps({
+ elementType: ClearIcon,
+ externalSlotProps: mergeSlotProps(
+ pickerFieldUIContext.slotProps.clearIcon,
+ slotProps?.clearIcon,
+ ),
+ additionalProps: {
+ fontSize: 'small',
+ },
+ ownerState,
+ });
+
+ if (!textFieldProps.InputProps) {
+ textFieldProps.InputProps = {};
+ }
+
+ if (pickerContext) {
+ textFieldProps.InputProps.ref = pickerContext.triggerRef;
+ }
+
+ if (
+ !textFieldProps.InputProps?.startAdornment &&
+ (clearButtonPosition === 'start' || openPickerButtonPosition === 'start')
+ ) {
+ textFieldProps.InputProps.startAdornment = (
+
+ {openPickerButtonPosition === 'start' && (
+
+
+
+ )}
+ {clearButtonPosition === 'start' && (
+
+
+
+ )}
+
+ );
+ }
+
+ if (
+ !textFieldProps.InputProps?.endAdornment &&
+ (clearButtonPosition === 'end' || openPickerButtonPosition === 'end')
+ ) {
+ textFieldProps.InputProps.endAdornment = (
+
+ {clearButtonPosition === 'end' && (
+
+
+
+ )}
+ {openPickerButtonPosition === 'end' && (
+
+
+
+ )}
+
+ );
+ }
+
+ if (clearButtonPosition != null) {
+ textFieldProps.sx = [
+ {
+ '& .clearButton': {
+ opacity: 1,
+ },
+ '@media (pointer: fine)': {
+ '& .clearButton': {
+ opacity: 0,
+ },
+ '&:hover, &:focus-within': {
+ '.clearButton': {
+ opacity: 1,
+ },
+ },
+ },
+ },
+ ...(Array.isArray(textFieldProps.sx) ? textFieldProps.sx : [textFieldProps.sx]),
+ ];
+ }
+
+ return ;
+}
+
+export interface ExportedPickerFieldUIProps {
+ /**
+ * If `true`, a clear button will be shown in the field allowing value clearing.
+ * @default false
+ */
+ clearable?: boolean;
+ /**
+ * Callback fired when the clear button is clicked.
+ */
+ onClear?: React.MouseEventHandler;
+ /**
+ * The position at which the clear button is placed.
+ * If the field is not clearable, the button is not rendered.
+ * @default 'end'
+ */
+ clearButtonPosition?: 'start' | 'end';
+ /**
+ * The position at which the opening button is placed.
+ * If there is no picker to open, the button is not rendered
+ * @default 'end'
+ */
+ openPickerButtonPosition?: 'start' | 'end';
+}
+
+export interface PickerFieldUIProps {
+ /**
+ * Overridable component slots.
+ * @default {}
+ */
+ slots?: PickerFieldUISlots;
+ /**
+ * The props used for each component slot.
+ * @default {}
+ */
+ slotProps?: PickerFieldUISlotProps;
+ /**
+ * Object returned by the `useField` hook or one of its wrapper (for example `useDateField`).
+ */
+ fieldResponse: UseFieldResponse;
+ /**
+ * The component to use to render the picker opening icon if none is provided in the picker's slots.
+ */
+ defaultOpenPickerIcon: React.ElementType;
+}
+
+export interface PickerFieldUISlots {
+ /**
+ * Form control with an input to render the value.
+ * @default , or from '@mui/material' if `enableAccessibleFieldDOMStructure` is `false`.
+ */
+ textField?: React.ElementType;
+ /**
+ * Component displayed on the start or end input adornment used to open the picker on desktop.
+ * @default InputAdornment
+ */
+ inputAdornment?: React.ElementType;
+ /**
+ * Icon to display inside the clear button.
+ * @default ClearIcon
+ */
+ clearIcon?: React.ElementType;
+ /**
+ * Button to clear the value.
+ * @default IconButton
+ */
+ clearButton?: React.ElementType;
+}
+
+export interface PickerFieldUISlotsFromContext extends PickerFieldUISlots {
+ /**
+ * Button to open the picker on desktop.
+ * @default IconButton
+ */
+ openPickerButton?: React.ElementType;
+ /**
+ * Icon displayed in the open picker button on desktop.
+ */
+ openPickerIcon?: React.ElementType;
+}
+
+export interface PickerFieldUISlotProps {
+ textField?: SlotComponentPropsFromProps<
+ PickersTextFieldProps | TextFieldProps,
+ {},
+ FieldOwnerState
+ >;
+ inputAdornment?: SlotComponentPropsFromProps<
+ InputAdornmentProps,
+ {},
+ FieldInputAdornmentOwnerState
+ >;
+ clearIcon?: SlotComponentPropsFromProps;
+ clearButton?: SlotComponentPropsFromProps;
+}
+
+export interface PickerFieldUISlotPropsFromContext extends PickerFieldUISlotProps {
+ openPickerButton?: SlotComponentPropsFromProps;
+ openPickerIcon?: SlotComponentPropsFromProps;
+}
+
+interface FieldInputAdornmentOwnerState extends FieldOwnerState {
+ position: 'start' | 'end';
+}
+
+interface PickerFieldUIContextValue {
+ slots: PickerFieldUISlotsFromContext;
+ slotProps: PickerFieldUISlotPropsFromContext;
+}
+
+function mergeSlotProps(
+ slotPropsA: SlotComponentPropsFromProps | undefined,
+ slotPropsB: SlotComponentPropsFromProps | undefined,
+) {
+ if (!slotPropsA) {
+ return slotPropsB;
+ }
+
+ if (!slotPropsB) {
+ return slotPropsA;
+ }
+
+ return (ownerState: TOwnerState) => {
+ return {
+ ...resolveComponentProps(slotPropsB, ownerState),
+ ...resolveComponentProps(slotPropsA, ownerState),
+ };
+ };
+}
+
+/**
+ * The `textField` slot props cannot be handled inside `PickerFieldUI` because it would be a breaking change to not pass the enriched props to `useField`.
+ * Once the non-accessible DOM structure will be removed, we will be able to remove the `textField` slot and clean this logic.
+ */
+export function useFieldTextFieldProps<
+ TProps extends UseFieldOwnerStateParameters & { inputProps?: {}; InputProps?: {} },
+>(parameters: UseFieldTextFieldPropsParameters) {
+ const { ref, externalForwardedProps, slotProps } = parameters;
+ const pickerFieldUIContext = React.useContext(PickerFieldUIContext);
+ const ownerState = useFieldOwnerState(externalForwardedProps);
+
+ const { InputProps, inputProps, ...otherExternalForwardedProps } = externalForwardedProps;
+
+ const textFieldProps = useSlotProps({
+ elementType: PickersTextField,
+ externalSlotProps: mergeSlotProps(
+ pickerFieldUIContext.slotProps.textField as any,
+ slotProps?.textField as any,
+ ),
+ externalForwardedProps: otherExternalForwardedProps,
+ additionalProps: {
+ ref,
+ },
+ ownerState,
+ }) as any as TProps;
+
+ // TODO: Remove when mui/material-ui#35088 will be merged
+ textFieldProps.inputProps = { ...inputProps, ...textFieldProps.inputProps };
+ textFieldProps.InputProps = { ...InputProps, ...textFieldProps.InputProps };
+
+ return textFieldProps;
+}
+
+interface UseFieldTextFieldPropsParameters {
+ slotProps:
+ | {
+ textField?: SlotComponentPropsFromProps<
+ PickersTextFieldProps | TextFieldProps,
+ {},
+ FieldOwnerState
+ >;
+ }
+ | undefined;
+ ref: React.Ref;
+ externalForwardedProps: any;
+}
+
+export function PickerFieldUIContextProvider(props: PickerFieldUIContextProviderProps) {
+ const { slots = {}, slotProps = {}, children } = props;
+
+ const contextValue = React.useMemo(
+ () => ({
+ slots: {
+ openPickerButton: slots.openPickerButton,
+ openPickerIcon: slots.openPickerIcon,
+ textField: slots.textField,
+ inputAdornment: slots.inputAdornment,
+ clearIcon: slots.clearIcon,
+ clearButton: slots.clearButton,
+ },
+ slotProps: {
+ openPickerButton: slotProps.openPickerButton,
+ openPickerIcon: slotProps.openPickerIcon,
+ textField: slotProps.textField,
+ inputAdornment: slotProps.inputAdornment,
+ clearIcon: slotProps.clearIcon,
+ clearButton: slotProps.clearButton,
+ },
+ }),
+ [
+ slots.openPickerButton,
+ slots.openPickerIcon,
+ slots.textField,
+ slots.inputAdornment,
+ slots.clearIcon,
+ slots.clearButton,
+ slotProps.openPickerButton,
+ slotProps.openPickerIcon,
+ slotProps.textField,
+ slotProps.inputAdornment,
+ slotProps.clearIcon,
+ slotProps.clearButton,
+ ],
+ );
+
+ return (
+ {children}
+ );
+}
+
+interface PickerFieldUIContextProviderProps {
+ children: React.ReactNode;
+ slots: PickerFieldUISlotsFromContext | undefined;
+ slotProps: PickerFieldUISlotPropsFromContext | undefined;
+}
diff --git a/packages/x-date-pickers/src/internals/components/PickerProvider.tsx b/packages/x-date-pickers/src/internals/components/PickerProvider.tsx
index 16997021db476..30d4aa9be6735 100644
--- a/packages/x-date-pickers/src/internals/components/PickerProvider.tsx
+++ b/packages/x-date-pickers/src/internals/components/PickerProvider.tsx
@@ -118,6 +118,18 @@ export interface PickerContextValue<
* Is always equal to "portrait" if the component you are accessing the context from is not wrapped by a picker.
*/
orientation: PickerOrientation;
+ /**
+ * The ref that should be attached to the element that triggers the Picker opening.
+ * When using a built-in field component, this property is automatically handled.
+ */
+ triggerRef: React.RefObject;
+ /**
+ * The status of the element that triggers the Picker opening.
+ * If it is "hidden", the field should not render the UI to open the Picker.
+ * If it is "disabled", the field should render a disabled UI to open the Picker.
+ * If it is "enabled", the field should render an interactive UI to open the Picker.
+ */
+ triggerStatus: 'hidden' | 'disabled' | 'enabled';
/**
* Format that should be used to render the value in the field.
* Is equal to `props.format` on the picker component if defined.
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 eed6bbba05946..8749e675362ee 100644
--- a/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx
+++ b/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx
@@ -1,7 +1,5 @@
import * as React from 'react';
import useSlotProps from '@mui/utils/useSlotProps';
-import MuiInputAdornment from '@mui/material/InputAdornment';
-import IconButton from '@mui/material/IconButton';
import useForkRef from '@mui/utils/useForkRef';
import useId from '@mui/utils/useId';
import { PickersPopper } from '../../components/PickersPopper';
@@ -11,6 +9,7 @@ import { PickersLayout } from '../../../PickersLayout';
import { FieldRef, InferError } from '../../../models';
import { DateOrTimeViewWithMeridiem, BaseSingleInputFieldProps, PickerValue } from '../../models';
import { PickerProvider } from '../../components/PickerProvider';
+import { PickerFieldUIContextProvider } from '../../components/PickerFieldUI';
/**
* Hook managing all the single-date desktop pickers:
@@ -29,7 +28,6 @@ export const useDesktopPicker = <
>,
>({
props,
- getOpenDialogAriaText,
...pickerParams
}: UseDesktopPickerParams) => {
const {
@@ -41,19 +39,17 @@ export const useDesktopPicker = <
label,
inputRef,
readOnly,
- disabled,
autoFocus,
localeText,
reduceAnimations,
} = props;
- const containerRef = React.useRef(null);
const fieldRef = React.useRef>(null);
const labelId = useId();
const isToolbarHidden = innerSlotProps?.toolbar?.hidden ?? false;
- const { hasUIView, providerProps, renderCurrentView, shouldRestoreFocus, ownerState } = usePicker<
+ const { providerProps, renderCurrentView, shouldRestoreFocus, ownerState } = usePicker<
PickerValue,
TView,
TExternalProps
@@ -66,40 +62,6 @@ export const useDesktopPicker = <
variant: 'desktop',
});
- const InputAdornment = slots.inputAdornment ?? MuiInputAdornment;
- const { ownerState: inputAdornmentOwnerState, ...inputAdornmentProps } = useSlotProps({
- elementType: InputAdornment,
- externalSlotProps: innerSlotProps?.inputAdornment,
- additionalProps: {
- position: 'end' as const,
- },
- ownerState,
- });
-
- const OpenPickerButton = slots.openPickerButton ?? IconButton;
- const { ownerState: openPickerButtonOwnerState, ...openPickerButtonProps } = useSlotProps({
- elementType: OpenPickerButton,
- externalSlotProps: innerSlotProps?.openPickerButton,
- additionalProps: {
- disabled: disabled || readOnly,
- // This direct access to `providerProps` will go away in https://github.com/mui/mui-x/pull/15671
- onClick: (event: React.UIEvent) => {
- event.preventDefault();
- providerProps.contextValue.setOpen((prevOpen) => !prevOpen);
- },
- 'aria-label': getOpenDialogAriaText(providerProps.contextValue.value),
- edge: inputAdornmentProps.position,
- },
- ownerState,
- });
-
- const OpenPickerIcon = slots.openPickerIcon;
- const openPickerIconProps = useSlotProps({
- elementType: OpenPickerIcon,
- externalSlotProps: innerSlotProps?.openPickerIcon,
- ownerState,
- });
-
const Field = slots.field;
const fieldProps: BaseSingleInputFieldProps<
PickerValue,
@@ -125,30 +87,6 @@ export const useDesktopPicker = <
ownerState,
});
- // TODO: Move to `useSlotProps` when https://github.com/mui/material-ui/pull/35088 will be merged
- if (hasUIView) {
- fieldProps.InputProps = {
- ...fieldProps.InputProps,
- ref: containerRef,
- ...(!props.disableOpenPicker && {
- [`${inputAdornmentProps.position}Adornment`]: (
-
-
-
-
-
- ),
- }),
- } as typeof fieldProps.InputProps;
- }
-
- const slotsForField = {
- textField: slots.textField,
- clearIcon: slots.clearIcon,
- clearButton: slots.clearButton,
- ...fieldProps.slots,
- };
-
const Layout = slots.layout ?? PickersLayout;
let labelledById = labelId;
@@ -175,25 +113,22 @@ export const useDesktopPicker = <
const renderPicker = () => (
-
-
-
- {renderCurrentView()}
-
-
+
+
+
+
+ {renderCurrentView()}
+
+
+
);
diff --git a/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.types.ts b/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.types.ts
index b8a7f5d0d8e07..58d97811de6cc 100644
--- a/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.types.ts
+++ b/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.types.ts
@@ -1,7 +1,4 @@
import * as React from 'react';
-import type { IconButtonProps } from '@mui/material/IconButton';
-import type { InputAdornmentProps } from '@mui/material/InputAdornment';
-import type { TextFieldProps } from '@mui/material/TextField';
import { MakeRequired, SlotComponentPropsFromProps } from '@mui/x-internals/types';
import {
BasePickerProps,
@@ -9,12 +6,7 @@ import {
} from '../../models/props/basePickerProps';
import { PickersPopperSlots, PickersPopperSlotProps } from '../../components/PickersPopper';
import { UsePickerParams } from '../usePicker';
-import {
- FieldOwnerState,
- PickerFieldSlotProps,
- PickerOwnerState,
- PickerValidDate,
-} from '../../../models';
+import { PickerFieldSlotProps, PickerOwnerState } from '../../../models';
import {
ExportedPickersLayoutSlots,
ExportedPickersLayoutSlotProps,
@@ -24,10 +16,9 @@ import { UsePickerValueNonStaticProps } from '../usePicker/usePickerValue.types'
import { UsePickerViewsProps } from '../usePicker/usePickerViews';
import { DateOrTimeViewWithMeridiem, PickerValue } from '../../models';
import {
- UseClearableFieldSlots,
- UseClearableFieldSlotProps,
-} from '../../../hooks/useClearableField';
-import { PickersTextFieldProps } from '../../../PickersTextField';
+ PickerFieldUISlotsFromContext,
+ PickerFieldUISlotPropsFromContext,
+} from '../../components/PickerFieldUI';
import { UsePickerProviderNonStaticProps } from '../usePicker/usePickerProvider';
export interface UseDesktopPickerSlots
@@ -36,7 +27,7 @@ export interface UseDesktopPickerSlots
'desktopPaper' | 'desktopTransition' | 'desktopTrapFocus' | 'popper'
>,
ExportedPickersLayoutSlots,
- UseClearableFieldSlots {
+ PickerFieldUISlotsFromContext {
/**
* Component used to enter the date with the keyboard.
*/
@@ -46,46 +37,24 @@ export interface UseDesktopPickerSlots
* @default TextField from '@mui/material' or PickersTextField if `enableAccessibleFieldDOMStructure` is `true`.
*/
textField?: React.ElementType;
- /**
- * Component displayed on the start or end input adornment used to open the picker on desktop.
- * @default InputAdornment
- */
- inputAdornment?: React.ElementType;
- /**
- * Button to open the picker on desktop.
- * @default IconButton
- */
- openPickerButton?: React.ElementType;
- /**
- * Icon displayed in the open picker button on desktop.
- */
- openPickerIcon: React.ElementType;
}
-export interface UseDesktopPickerSlotProps
- extends ExportedUseDesktopPickerSlotProps,
- Pick, 'toolbar'> {}
-
export interface ExportedUseDesktopPickerSlotProps<
TEnableAccessibleFieldDOMStructure extends boolean,
> extends PickersPopperSlotProps,
ExportedPickersLayoutSlotProps,
- UseClearableFieldSlotProps {
+ PickerFieldUISlotPropsFromContext {
field?: SlotComponentPropsFromProps<
PickerFieldSlotProps,
{},
PickerOwnerState
>;
- textField?: SlotComponentPropsFromProps<
- PickersTextFieldProps | TextFieldProps,
- {},
- FieldOwnerState
- >;
- inputAdornment?: SlotComponentPropsFromProps;
- openPickerButton?: SlotComponentPropsFromProps;
- openPickerIcon?: SlotComponentPropsFromProps, {}, PickerOwnerState>;
}
+export interface UseDesktopPickerSlotProps
+ extends ExportedUseDesktopPickerSlotProps,
+ Pick, 'toolbar'> {}
+
export interface DesktopOnlyPickerProps
extends BaseNonRangeNonStaticPickerProps,
UsePickerValueNonStaticProps,
@@ -130,5 +99,4 @@ export interface UseDesktopPickerParams<
'valueManager' | 'valueType' | 'validator' | 'rendererInterceptor'
> {
props: TExternalProps;
- getOpenDialogAriaText: (date: PickerValidDate | null) => string;
}
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 8bec0c4e9afa6..3f92bef606d7b 100644
--- a/packages/x-date-pickers/src/internals/hooks/useField/useField.ts
+++ b/packages/x-date-pickers/src/internals/hooks/useField/useField.ts
@@ -3,7 +3,7 @@ import useEnhancedEffect from '@mui/utils/useEnhancedEffect';
import useEventCallback from '@mui/utils/useEventCallback';
import { useRtl } from '@mui/system/RtlProvider';
import { useValidation } from '../../../validation';
-import { useUtils } from '../useUtils';
+import { useLocalizationContext, useUtils } from '../useUtils';
import {
UseFieldParams,
UseFieldResponse,
@@ -52,6 +52,7 @@ export const useField = <
fieldValueManager,
valueManager,
validator,
+ getOpenPickerButtonAriaLabel: getOpenDialogAriaText,
} = params;
const isRtl = useRtl();
@@ -279,9 +280,16 @@ export const useField = <
clearable: Boolean(clearable && !areAllSectionsEmpty && !readOnly && !disabled),
};
+ const localizationContext = useLocalizationContext();
+ const openPickerAriaLabel = React.useMemo(
+ () => getOpenDialogAriaText({ ...localizationContext, value: state.value }),
+ [getOpenDialogAriaText, state.value, localizationContext],
+ );
+
const commonAdditionalProps: UseFieldCommonAdditionalProps = {
disabled,
readOnly,
+ openPickerAriaLabel,
};
return {
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 e286389178177..2b9dbd7d3598c 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
@@ -18,8 +18,9 @@ import type { Validator } from '../../../validation';
import type { UseFieldStateResponse } from './useFieldState';
import type { UseFieldCharacterEditingResponse } from './useFieldCharacterEditing';
import { PickersSectionElement, PickersSectionListRef } from '../../../PickersSectionList';
-import { ExportedUseClearableFieldProps } from '../../../hooks/useClearableField';
import { FormProps, InferNonNullablePickerValue, PickerValidValue } from '../../models';
+import type { ExportedPickerFieldUIProps } from '../../components/PickerFieldUI';
+import { UseLocalizationContextReturnValue } from '../useUtils';
export interface UseFieldParams<
TValue extends PickerValidValue,
@@ -34,6 +35,9 @@ export interface UseFieldParams<
fieldValueManager: FieldValueManager;
validator: Validator, TInternalProps>;
valueType: PickerValueType;
+ getOpenPickerButtonAriaLabel: (
+ parameters: UseLocalizationContextReturnValue & { value: TValue },
+ ) => string;
}
export interface UseFieldInternalProps<
@@ -122,9 +126,15 @@ export interface UseFieldInternalProps<
}
export interface UseFieldCommonAdditionalProps
- extends Required, 'disabled' | 'readOnly'>> {}
+ extends Required, 'disabled' | 'readOnly'>> {
+ /**
+ * The aria label to set on the button that opens the picker.
+ */
+ openPickerAriaLabel: string;
+}
-export interface UseFieldCommonForwardedProps extends ExportedUseClearableFieldProps {
+export interface UseFieldCommonForwardedProps
+ extends Pick {
onKeyDown?: React.KeyboardEventHandler;
error?: boolean;
}
diff --git a/packages/x-date-pickers/src/internals/hooks/useFieldOwnerState.ts b/packages/x-date-pickers/src/internals/hooks/useFieldOwnerState.ts
index 9ec96ac340d73..16f9c3ce49905 100644
--- a/packages/x-date-pickers/src/internals/hooks/useFieldOwnerState.ts
+++ b/packages/x-date-pickers/src/internals/hooks/useFieldOwnerState.ts
@@ -20,6 +20,6 @@ export function useFieldOwnerState(parameters: UseFieldOwnerStateParameters) {
);
}
-interface UseFieldOwnerStateParameters extends FormProps {
+export interface UseFieldOwnerStateParameters extends FormProps {
required?: boolean;
}
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 495276ff0aa50..f484f461a0075 100644
--- a/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.tsx
+++ b/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.tsx
@@ -5,11 +5,11 @@ import useId from '@mui/utils/useId';
import { PickersModalDialog } from '../../components/PickersModalDialog';
import { UseMobilePickerParams, UseMobilePickerProps } from './useMobilePicker.types';
import { usePicker } from '../usePicker';
-import { onSpaceOrEnter } from '../../utils/utils';
import { PickersLayout } from '../../../PickersLayout';
import { FieldRef, InferError } from '../../../models';
import { BaseSingleInputFieldProps, DateOrTimeViewWithMeridiem, PickerValue } from '../../models';
import { PickerProvider } from '../../components/PickerProvider';
+import { PickerFieldUIContextProvider } from '../../components/PickerFieldUI';
/**
* Hook managing all the single-date mobile pickers:
@@ -28,7 +28,6 @@ export const useMobilePicker = <
>,
>({
props,
- getOpenDialogAriaText,
...pickerParams
}: UseMobilePickerParams) => {
const {
@@ -40,7 +39,7 @@ export const useMobilePicker = <
label,
inputRef,
readOnly,
- disabled,
+ autoFocus,
localeText,
} = props;
@@ -72,38 +71,21 @@ export const useMobilePicker = <
externalSlotProps: innerSlotProps?.field,
additionalProps: {
// Internal props
- readOnly: readOnly ?? true,
+ readOnly,
+ autoFocus: autoFocus && !props.open,
// Forwarded props
className,
sx,
label,
name,
+ focused: providerProps.contextValue.open ? true : undefined,
...(isToolbarHidden && { id: labelId }),
- ...(!(disabled || readOnly) && {
- // These direct access to `providerProps` will go away in https://github.com/mui/mui-x/pull/15671
- onClick: (event: React.UIEvent) => {
- event.preventDefault();
- providerProps.contextValue.setOpen(true);
- },
- onKeyDown: onSpaceOrEnter(() => providerProps.contextValue.setOpen(true)),
- }),
...(!!inputRef && { inputRef }),
},
ownerState,
});
- // TODO: Move to `useSlotProps` when https://github.com/mui/material-ui/pull/35088 will be merged
- fieldProps.inputProps = {
- ...fieldProps.inputProps,
- 'aria-label': getOpenDialogAriaText(providerProps.contextValue.value),
- } as typeof fieldProps.inputProps;
-
- const slotsForField = {
- textField: slots.textField,
- ...fieldProps.slots,
- };
-
const Layout = slots.layout ?? PickersLayout;
let labelledById = labelId;
@@ -130,17 +112,14 @@ export const useMobilePicker = <
const renderPicker = () => (
-
-
-
- {renderCurrentView()}
-
-
+
+
+
+
+ {renderCurrentView()}
+
+
+
);
diff --git a/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.types.ts b/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.types.ts
index c169f6713f5be..3398e2d1dfdb2 100644
--- a/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.types.ts
+++ b/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.types.ts
@@ -1,5 +1,4 @@
import * as React from 'react';
-import type { TextFieldProps } from '@mui/material/TextField';
import { MakeRequired, SlotComponentPropsFromProps } from '@mui/x-internals/types';
import {
BasePickerProps,
@@ -10,12 +9,7 @@ import {
PickersModalDialogSlotProps,
} from '../../components/PickersModalDialog';
import { UsePickerParams } from '../usePicker';
-import {
- FieldOwnerState,
- PickerFieldSlotProps,
- PickerOwnerState,
- PickerValidDate,
-} from '../../../models';
+import { PickerFieldSlotProps, PickerOwnerState } from '../../../models';
import {
ExportedPickersLayoutSlots,
ExportedPickersLayoutSlotProps,
@@ -24,37 +18,32 @@ import {
import { UsePickerValueNonStaticProps } from '../usePicker/usePickerValue.types';
import { UsePickerViewsProps } from '../usePicker/usePickerViews';
import { DateOrTimeViewWithMeridiem, PickerValue } from '../../models';
-import { PickersTextFieldProps } from '../../../PickersTextField';
import { UsePickerProviderNonStaticProps } from '../usePicker/usePickerProvider';
+import {
+ PickerFieldUISlotsFromContext,
+ PickerFieldUISlotPropsFromContext,
+} from '../../components/PickerFieldUI';
export interface UseMobilePickerSlots
extends PickersModalDialogSlots,
- ExportedPickersLayoutSlots {
+ ExportedPickersLayoutSlots,
+ PickerFieldUISlotsFromContext {
/**
* Component used to enter the date with the keyboard.
*/
field: React.ElementType;
- /**
- * Form control with an input to render the value inside the default field.
- * @default TextField from '@mui/material' or PickersTextField if `enableAccessibleFieldDOMStructure` is `true`.
- */
- textField?: React.ElementType;
}
export interface ExportedUseMobilePickerSlotProps<
TEnableAccessibleFieldDOMStructure extends boolean,
> extends PickersModalDialogSlotProps,
- ExportedPickersLayoutSlotProps {
+ ExportedPickersLayoutSlotProps,
+ PickerFieldUISlotPropsFromContext {
field?: SlotComponentPropsFromProps<
PickerFieldSlotProps,
{},
PickerOwnerState
>;
- textField?: SlotComponentPropsFromProps<
- PickersTextFieldProps | TextFieldProps,
- {},
- FieldOwnerState
- >;
}
export interface UseMobilePickerSlotProps
@@ -99,5 +88,4 @@ export interface UseMobilePickerParams<
'valueManager' | 'valueType' | 'validator'
> {
props: TExternalProps;
- getOpenDialogAriaText: (date: PickerValidDate | null) => string;
}
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 06f6408ab5c1e..a27c1306f22a2 100644
--- a/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.ts
+++ b/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.ts
@@ -56,7 +56,6 @@ export const usePicker = <
return {
// Picker views
renderCurrentView: pickerViewsResponse.renderCurrentView,
- hasUIView: pickerViewsResponse.provider.hasUIView,
shouldRestoreFocus: pickerViewsResponse.shouldRestoreFocus,
// Picker provider
diff --git a/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.types.ts b/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.types.ts
index 96bf2f4673a65..4bf6424a9de93 100644
--- a/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.types.ts
+++ b/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.types.ts
@@ -63,6 +63,4 @@ export interface UsePickerResponse<
> extends Pick, 'shouldRestoreFocus' | 'renderCurrentView'> {
ownerState: PickerOwnerState;
providerProps: UsePickerProviderReturnValue;
- // TODO v8: Remove in https://github.com/mui/mui-x/pull/15671
- hasUIView: boolean;
}
diff --git a/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerProvider.ts b/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerProvider.ts
index 97be050ea82f9..efbbae1fb221b 100644
--- a/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerProvider.ts
+++ b/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerProvider.ts
@@ -79,6 +79,7 @@ export function usePickerProvider<
const utils = useUtils();
const orientation = usePickerOrientation(paramsFromUsePickerViews.views, props.orientation);
+ const triggerRef = React.useRef(null);
const ownerState = React.useMemo(
() => ({
@@ -105,6 +106,18 @@ export function usePickerProvider<
],
);
+ const triggerStatus = React.useMemo(() => {
+ if (props.disableOpenPicker || !paramsFromUsePickerViews.hasUIView) {
+ return 'hidden';
+ }
+
+ if (props.disabled || props.readOnly) {
+ return 'disabled';
+ }
+
+ return 'enabled';
+ }, [props.disableOpenPicker, paramsFromUsePickerViews.hasUIView, props.disabled, props.readOnly]);
+
const contextValue = React.useMemo>(
() => ({
...paramsFromUsePickerValue.contextValue,
@@ -113,6 +126,8 @@ export function usePickerProvider<
readOnly: props.readOnly ?? false,
variant,
orientation,
+ triggerRef,
+ triggerStatus,
fieldFormat: props.format ?? '',
}),
[
@@ -122,6 +137,8 @@ export function usePickerProvider<
orientation,
props.disabled,
props.readOnly,
+ triggerRef,
+ triggerStatus,
props.format,
],
);
diff --git a/packages/x-date-pickers/src/internals/hooks/useUtils.ts b/packages/x-date-pickers/src/internals/hooks/useUtils.ts
index 814918372fcc6..d7e4aeb94179e 100644
--- a/packages/x-date-pickers/src/internals/hooks/useUtils.ts
+++ b/packages/x-date-pickers/src/internals/hooks/useUtils.ts
@@ -38,9 +38,7 @@ export const useLocalizationContext = () => {
({
...localization,
localeText,
- }) as Omit & {
- localeText: PickersLocaleText;
- },
+ }) as UseLocalizationContextReturnValue,
[localization, localeText],
);
};
@@ -59,3 +57,8 @@ export const useNow = (timezone: PickersTimezone): PickerValidDate => {
return now.current!;
};
+
+export interface UseLocalizationContextReturnValue
+ extends Omit {
+ localeText: PickersLocaleText;
+}
diff --git a/packages/x-date-pickers/src/internals/index.ts b/packages/x-date-pickers/src/internals/index.ts
index 2bbb31ef4487a..85394bf31fdd6 100644
--- a/packages/x-date-pickers/src/internals/index.ts
+++ b/packages/x-date-pickers/src/internals/index.ts
@@ -4,6 +4,17 @@ export type {
PickersArrowSwitcherSlots,
PickersArrowSwitcherSlotProps,
} from './components/PickersArrowSwitcher';
+export {
+ PickerFieldUI,
+ PickerFieldUIContextProvider,
+ cleanFieldResponse,
+ useFieldTextFieldProps,
+} from './components/PickerFieldUI';
+export type {
+ ExportedPickerFieldUIProps,
+ PickerFieldUISlots,
+ PickerFieldUISlotProps,
+} from './components/PickerFieldUI';
export { PickerProvider } from './components/PickerProvider';
export type { PickerContextValue } from './components/PickerProvider';
export { PickersModalDialog } from './components/PickersModalDialog';
@@ -132,7 +143,6 @@ export type {
PickerValidValue,
} from './models/value';
-export { convertFieldResponseIntoMuiTextFieldProps } from './utils/convertFieldResponseIntoMuiTextFieldProps';
export {
applyDefaultDate,
replaceInvalidDateByNull,
diff --git a/packages/x-date-pickers/src/internals/models/fields.ts b/packages/x-date-pickers/src/internals/models/fields.ts
index 016c94b661d5f..44f0baa740eb0 100644
--- a/packages/x-date-pickers/src/internals/models/fields.ts
+++ b/packages/x-date-pickers/src/internals/models/fields.ts
@@ -1,19 +1,16 @@
import { SxProps } from '@mui/material/styles';
-import type {
- ExportedUseClearableFieldProps,
- UseClearableFieldSlotProps,
- UseClearableFieldSlots,
-} from '../../hooks/useClearableField';
import type { FieldSection, PickerOwnerState } from '../../models';
import type { UseFieldInternalProps } from '../hooks/useField';
import { RangePosition } from './pickers';
import { PickerValidValue } from './value';
+import type { ExportedPickerFieldUIProps } from '../components/PickerFieldUI';
export interface FieldRangeSection extends FieldSection {
dateName: RangePosition;
}
-export interface BaseForwardedSingleInputFieldProps extends ExportedUseClearableFieldProps {
+export interface BaseForwardedSingleInputFieldProps
+ extends Pick {
className: string | undefined;
sx: SxProps | undefined;
label: React.ReactNode | undefined;
@@ -24,18 +21,6 @@ export interface BaseForwardedSingleInputFieldProps extends ExportedUseClearable
onBlur?: React.FocusEventHandler;
ref?: React.Ref;
inputRef?: React.Ref;
- InputProps?: {
- ref?: React.Ref;
- endAdornment?: React.ReactNode;
- startAdornment?: React.ReactNode;
- };
- inputProps?: {
- 'aria-label'?: string;
- };
- slots?: UseClearableFieldSlots;
- slotProps?: UseClearableFieldSlotProps & {
- textField?: {};
- };
ownerState: PickerOwnerState;
}
diff --git a/packages/x-date-pickers/src/internals/utils/convertFieldResponseIntoMuiTextFieldProps.ts b/packages/x-date-pickers/src/internals/utils/convertFieldResponseIntoMuiTextFieldProps.ts
deleted file mode 100644
index 7124c455941ab..0000000000000
--- a/packages/x-date-pickers/src/internals/utils/convertFieldResponseIntoMuiTextFieldProps.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { TextFieldProps } from '@mui/material/TextField';
-import { UseFieldResponse } from '../hooks/useField';
-
-export const convertFieldResponseIntoMuiTextFieldProps = <
- TFieldResponse extends UseFieldResponse,
->({
- enableAccessibleFieldDOMStructure,
- ...fieldResponse
-}: TFieldResponse): TextFieldProps => {
- if (enableAccessibleFieldDOMStructure) {
- const { InputProps, readOnly, ...other } = fieldResponse;
-
- return {
- ...other,
- InputProps: { ...(InputProps ?? {}), readOnly },
- } as any;
- }
-
- const { onPaste, onKeyDown, inputMode, readOnly, InputProps, inputProps, inputRef, ...other } =
- fieldResponse;
-
- return {
- ...other,
- InputProps: { ...(InputProps ?? {}), readOnly },
- inputProps: { ...(inputProps ?? {}), inputMode, onPaste, onKeyDown, ref: inputRef },
- } as any;
-};
diff --git a/packages/x-date-pickers/src/locales/utils/getPickersLocalization.ts b/packages/x-date-pickers/src/locales/utils/getPickersLocalization.ts
index 40ab3fe2714ae..a6cf8e68da86c 100644
--- a/packages/x-date-pickers/src/locales/utils/getPickersLocalization.ts
+++ b/packages/x-date-pickers/src/locales/utils/getPickersLocalization.ts
@@ -1,4 +1,3 @@
-import { AdapterFormats, MuiPickersAdapter, PickerValidDate } from '../../models';
import { PickersLocaleText } from './pickersLocaleTextApi';
export const getPickersLocalization = (pickersTranslations: Partial) => {
@@ -12,18 +11,3 @@ export const getPickersLocalization = (pickersTranslations: Partial string;
- propsTranslation: ((formattedValue: string | null) => string) | undefined;
-}) => {
- const { utils, formatKey, contextTranslation, propsTranslation } = params;
-
- return (value: PickerValidDate | null) => {
- const formattedValue = utils.isValid(value) ? utils.format(value, formatKey) : null;
- const translation = propsTranslation ?? contextTranslation;
- return translation(formattedValue);
- };
-};
diff --git a/packages/x-date-pickers/src/managers/useDateManager.ts b/packages/x-date-pickers/src/managers/useDateManager.ts
index aec21fd0379bf..b99686fbd4d85 100644
--- a/packages/x-date-pickers/src/managers/useDateManager.ts
+++ b/packages/x-date-pickers/src/managers/useDateManager.ts
@@ -34,6 +34,10 @@ export function useDateManager {
+ const formattedValue = utils.isValid(value) ? utils.format(value, 'fullDate') : null;
+ return localeText.openDatePickerDialogue(formattedValue);
+ },
}),
[enableAccessibleFieldDOMStructure],
);
diff --git a/packages/x-date-pickers/src/managers/useDateTimeManager.ts b/packages/x-date-pickers/src/managers/useDateTimeManager.ts
index 965333a10b704..dc8b37c307152 100644
--- a/packages/x-date-pickers/src/managers/useDateTimeManager.ts
+++ b/packages/x-date-pickers/src/managers/useDateTimeManager.ts
@@ -35,6 +35,10 @@ export function useDateTimeManager {
+ const formattedValue = utils.isValid(value) ? utils.format(value, 'fullDate') : null;
+ return localeText.openDatePickerDialogue(formattedValue);
+ },
}),
[enableAccessibleFieldDOMStructure],
);
diff --git a/packages/x-date-pickers/src/managers/useTimeManager.ts b/packages/x-date-pickers/src/managers/useTimeManager.ts
index cfbccbabbfe33..5ce9df999af2c 100644
--- a/packages/x-date-pickers/src/managers/useTimeManager.ts
+++ b/packages/x-date-pickers/src/managers/useTimeManager.ts
@@ -34,6 +34,10 @@ export function useTimeManager {
+ const formattedValue = utils.isValid(value) ? utils.format(value, 'fullTime') : null;
+ return localeText.openTimePickerDialogue(formattedValue);
+ },
}),
[enableAccessibleFieldDOMStructure],
);
diff --git a/packages/x-date-pickers/src/models/fields.ts b/packages/x-date-pickers/src/models/fields.ts
index eac8aedd90db1..2002619cb4e25 100644
--- a/packages/x-date-pickers/src/models/fields.ts
+++ b/packages/x-date-pickers/src/models/fields.ts
@@ -1,9 +1,5 @@
import * as React from 'react';
import { TextFieldProps } from '@mui/material/TextField';
-import type {
- ExportedUseClearableFieldProps,
- UseClearableFieldResponse,
-} from '../hooks/useClearableField';
import type { ExportedPickersSectionListProps } from '../PickersSectionList';
import type { UseFieldInternalProps, UseFieldResponse } from '../internals/hooks/useField';
import type { PickersTextFieldProps } from '../PickersTextField';
@@ -14,6 +10,7 @@ import {
PickerValidValue,
} from '../internals/models';
import { PickerOwnerState } from './pickers';
+import type { ExportedPickerFieldUIProps } from '../internals/components/PickerFieldUI';
// Update PickersComponentAgnosticLocaleText -> viewNames when adding new entries
export type FieldSectionType =
@@ -160,7 +157,7 @@ export interface FieldOwnerState extends PickerOwnerState {
export type PickerFieldSlotProps<
TValue extends PickerValidValue,
TEnableAccessibleFieldDOMStructure extends boolean,
-> = ExportedUseClearableFieldProps &
+> = ExportedPickerFieldUIProps &
Pick<
UseFieldInternalProps,
'shouldRespectLeadingZeros' | 'readOnly'
@@ -170,13 +167,20 @@ export type PickerFieldSlotProps<
};
/**
- * Props the text field receives when used with a single input picker.
+ * Props the text field receives when used inside a single input picker.
* Only contains what the MUI components are passing to the text field, not what users can pass using the `props.slotProps.field` and `props.slotProps.textField`.
*/
export type BaseSingleInputPickersTextFieldProps<
TEnableAccessibleFieldDOMStructure extends boolean,
-> = UseClearableFieldResponse<
- UseFieldResponse
+> = Omit<
+ UseFieldResponse,
+ | 'slots'
+ | 'slotProps'
+ | 'clearable'
+ | 'onClear'
+ | 'openPickerButtonPosition'
+ | 'clearButtonPosition'
+ | 'openPickerAriaLabel'
>;
/**
diff --git a/packages/x-date-pickers/src/models/manager.ts b/packages/x-date-pickers/src/models/manager.ts
index dd2346f71e953..c7a0c42db75ec 100644
--- a/packages/x-date-pickers/src/models/manager.ts
+++ b/packages/x-date-pickers/src/models/manager.ts
@@ -1,7 +1,7 @@
import type { FieldValueManager, UseFieldInternalProps } from '../internals/hooks/useField';
import type { PickerValueManager } from '../internals/hooks/usePicker';
+import type { UseLocalizationContextReturnValue } from '../internals/hooks/useUtils';
import type { PickerValidValue } from '../internals/models';
-import type { MuiPickersAdapterContextValue } from '../LocalizationProvider/LocalizationProvider';
import type { Validator } from '../validation';
import type { PickerValueType } from './common';
@@ -78,15 +78,28 @@ export interface PickerManager<
* - a default format to display the value in the field
* - some default validation props that are needed to validate the value (e.g: minDate, maxDate)
* This property is not part of the public API and should not be used directly.
- * @param {ApplyDefaultsToFieldInternalPropsParameters} parameters The parameters to apply the defaults.
+ * @param {ApplyDefaultsToFieldInternalPropsParameters} parameters The parameters to apply the defaults.
* @returns {TFieldInternalPropsWithDefaults} The field internal props with the defaults applied.
*/
internal_applyDefaultsToFieldInternalProps: (
parameters: ApplyDefaultsToFieldInternalPropsParameters,
) => TFieldInternalPropsWithDefaults;
+ /**
+ * Returns the aria-label to apply on the button that opens the picker.
+ * @param {GetOpenPickerButtonAriaLabelParameters} params The parameters to get the aria-label.
+ * @returns {string} The aria-label to apply on the button that opens the picker.
+ */
+ internal_getOpenPickerButtonAriaLabel: (
+ params: GetOpenPickerButtonAriaLabelParameters,
+ ) => string;
}
interface ApplyDefaultsToFieldInternalPropsParameters
- extends MuiPickersAdapterContextValue {
+ extends UseLocalizationContextReturnValue {
internalProps: TFieldInternalProps;
}
+
+interface GetOpenPickerButtonAriaLabelParameters
+ extends UseLocalizationContextReturnValue {
+ value: TValue;
+}
diff --git a/scripts/x-charts-pro.exports.json b/scripts/x-charts-pro.exports.json
index 922530b85e437..b457362dd31ec 100644
--- a/scripts/x-charts-pro.exports.json
+++ b/scripts/x-charts-pro.exports.json
@@ -326,7 +326,7 @@
{ "name": "useYAxis", "kind": "Function" },
{ "name": "useYColorScale", "kind": "Function" },
{ "name": "useYScale", "kind": "Function" },
- { "name": "useZColorScale", "kind": "Function" },
- { "name": "ZAxisContextProvider", "kind": "Function" },
- { "name": "ZAxisContextProviderProps", "kind": "TypeAlias" }
+ { "name": "useZAxes", "kind": "Function" },
+ { "name": "useZAxis", "kind": "Function" },
+ { "name": "useZColorScale", "kind": "Function" }
]
diff --git a/scripts/x-charts.exports.json b/scripts/x-charts.exports.json
index 28e649df3dd20..d23e021e2ab54 100644
--- a/scripts/x-charts.exports.json
+++ b/scripts/x-charts.exports.json
@@ -319,7 +319,7 @@
{ "name": "useYAxis", "kind": "Function" },
{ "name": "useYColorScale", "kind": "Function" },
{ "name": "useYScale", "kind": "Function" },
- { "name": "useZColorScale", "kind": "Function" },
- { "name": "ZAxisContextProvider", "kind": "Function" },
- { "name": "ZAxisContextProviderProps", "kind": "TypeAlias" }
+ { "name": "useZAxes", "kind": "Function" },
+ { "name": "useZAxis", "kind": "Function" },
+ { "name": "useZColorScale", "kind": "Function" }
]
diff --git a/test/e2e/index.test.ts b/test/e2e/index.test.ts
index a60c3ecfe0cb4..1616748cefe1e 100644
--- a/test/e2e/index.test.ts
+++ b/test/e2e/index.test.ts
@@ -13,7 +13,6 @@ import {
WebError,
Locator,
} from '@playwright/test';
-import { pickersTextFieldClasses } from '@mui/x-date-pickers/PickersTextField';
import { pickersSectionListClasses } from '@mui/x-date-pickers/PickersSectionList';
function sleep(timeoutMS: number): Promise {
@@ -799,19 +798,13 @@ async function initializeEnvironment(
it('should allow selecting a value', async () => {
await renderFixture('DatePicker/BasicMobileDatePicker');
- // Old selector: await page.getByRole('textbox').click({ position: { x: 10, y: 2 } });
- await page
- .locator(`.${pickersTextFieldClasses.root}`)
- .click({ position: { x: 10, y: 2 } });
+ await page.getByRole('button').click();
await page.getByRole('gridcell', { name: '11' }).click();
await page.getByRole('button', { name: 'OK' }).click();
- await waitFor(async () => {
- // assert that the dialog has been closed and the focused element is the input
- expect(await page.evaluate(() => document.activeElement?.className)).to.contain(
- pickersSectionListClasses.sectionContent,
- );
- });
+ // assert that the dialog closes after selection is complete
+ // could run into race condition otherwise
+ await page.waitForSelector('[role="dialog"]', { state: 'detached' });
expect(await page.getByRole('textbox', { includeHidden: true }).inputValue()).to.equal(
'04/11/2022',
);
@@ -824,7 +817,7 @@ async function initializeEnvironment(
const input = page.getByRole('textbox');
- await input.click({ position: { x: 10, y: 2 } });
+ await page.getByRole('button').click();
await page.getByRole('button', { name: 'Clear' }).click();
await input.blur();
diff --git a/test/utils/pickers/describePicker/describePicker.tsx b/test/utils/pickers/describePicker/describePicker.tsx
index 5362abab6eaa5..53a3b203855bf 100644
--- a/test/utils/pickers/describePicker/describePicker.tsx
+++ b/test/utils/pickers/describePicker/describePicker.tsx
@@ -56,11 +56,7 @@ function innerDescribePicker(ElementToTest: React.ElementType, options: Describe
/>,
);
- const shouldRenderOpenPickerIcon = !hasNoView && variant !== 'mobile';
-
- expect(queryAllByTestId('component-test')).to.have.length(
- shouldRenderOpenPickerIcon ? 1 : 0,
- );
+ expect(queryAllByTestId('component-test')).to.have.length(hasNoView ? 0 : 1);
},
);
});
diff --git a/test/utils/pickers/describeValue/describeValue.types.ts b/test/utils/pickers/describeValue/describeValue.types.ts
index c823c418143a2..93b8857a59da0 100644
--- a/test/utils/pickers/describeValue/describeValue.types.ts
+++ b/test/utils/pickers/describeValue/describeValue.types.ts
@@ -29,6 +29,7 @@ export type DescribeValueOptions<
> = DescribeValueBaseOptions &
(C extends 'picker'
? OpenPickerParams & {
+ variant: 'desktop' | 'mobile';
setNewValue: (
value: InferNonNullablePickerValue,
options: {
diff --git a/test/utils/pickers/describeValue/testControlledUnControlled.tsx b/test/utils/pickers/describeValue/testControlledUnControlled.tsx
index 272360c609c0c..77be94a9414aa 100644
--- a/test/utils/pickers/describeValue/testControlledUnControlled.tsx
+++ b/test/utils/pickers/describeValue/testControlledUnControlled.tsx
@@ -157,11 +157,13 @@ export const testControlledUnControlled: DescribeValueTestSuite = (
});
});
- it('should not allow editing with keyboard in mobile pickers', () => {
+ it('should allow editing in field on non-range mobile pickers', () => {
if (componentFamily !== 'picker' || params.variant !== 'mobile') {
return;
}
+ const hasMobileFieldEditing = ['time', 'date', 'date-time'].includes(params.type);
+
const handleChange = spy();
const v7Response = renderWithProps({
@@ -170,7 +172,7 @@ export const testControlledUnControlled: DescribeValueTestSuite = (
});
v7Response.selectSection(undefined);
fireUserEvent.keyPress(v7Response.getActiveSection(0), { key: 'ArrowUp' });
- expect(handleChange.callCount).to.equal(0);
+ expect(handleChange.callCount).to.equal(hasMobileFieldEditing ? 1 : 0);
});
it('should have correct labelledby relationship when toolbar is shown', () => {
diff --git a/test/utils/pickers/misc.ts b/test/utils/pickers/misc.ts
index 76e9ba1d50689..8fe2f45957bbb 100644
--- a/test/utils/pickers/misc.ts
+++ b/test/utils/pickers/misc.ts
@@ -24,7 +24,7 @@ const getChangeCountForComponentFamily = (componentFamily: PickerComponentFamily
export const getExpectedOnChangeCount = (
componentFamily: PickerComponentFamily,
- params: OpenPickerParams,
+ params: OpenPickerParams & { variant: 'desktop' | 'mobile' },
) => {
if (componentFamily === 'digital-clock') {
return getChangeCountForComponentFamily(componentFamily);
diff --git a/test/utils/pickers/openPicker.ts b/test/utils/pickers/openPicker.ts
index b34715ce5f8b6..1a4285de7b396 100644
--- a/test/utils/pickers/openPicker.ts
+++ b/test/utils/pickers/openPicker.ts
@@ -5,11 +5,9 @@ import { pickersInputBaseClasses } from '@mui/x-date-pickers/PickersTextField';
export type OpenPickerParams =
| {
type: 'date' | 'date-time' | 'time';
- variant: 'mobile' | 'desktop';
}
| {
type: 'date-range' | 'date-time-range';
- variant: 'mobile' | 'desktop';
initialFocus: 'start' | 'end';
/**
* @default false
@@ -36,12 +34,6 @@ export const openPicker = (params: OpenPickerParams) => {
return true;
}
- if (params.variant === 'mobile') {
- fireEvent.click(fieldSectionsContainer);
-
- return true;
- }
-
const target =
params.type === 'time'
? screen.getByLabelText(/choose time/i)