Skip to content

Commit

Permalink
[pickers] Add DigitalClock to DesktopDateTimePicker (#8946)
Browse files Browse the repository at this point in the history
  • Loading branch information
LukasTy committed May 26, 2023
1 parent 41ef63a commit 030a8d0
Show file tree
Hide file tree
Showing 50 changed files with 820 additions and 288 deletions.
11 changes: 3 additions & 8 deletions docs/data/date-pickers/date-time-picker/date-time-picker.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,20 @@ materialDesign: https://m2.material.io/components/date-pickers

<p class="description">The Date Time Picker component lets the user select a date and time.</p>

:::info
The component by default currently does not ship with **time** picker view experience on **desktop**.
It was a conscious decision and a first step towards having a more user friendly desktop experience [discussed in #4483](https://github.com/mui/mui-x/issues/4483).
If a desktop view experience is essential, you can revert to it by following the suggestion [in the migration guide](/x/migration/migration-pickers-v5/#stop-rendering-a-clock-on-desktop).
:::

## Basic usage

{{"demo": "BasicDateTimePicker.js"}}

## Component composition

The component is built using the `DateTimeField` for the keyboard editing, the `DateCalendar` for the date view editing and the `TimeClock` for the time view editing.
All the documented props of those three components can also be passed to the Date Time Picker component.
The component is built using the `DateTimeField` for the keyboard editing, the `DateCalendar` for the date view editing, the `DigitalClock` for the desktop view editing, and the `TimeClock` for the mobile time view editing.
All the documented props of those four components can also be passed to the Date Time Picker component.

Check-out their documentation page for more information:

- [Date Field](/x/react-date-pickers/date-field/)
- [Date Calendar](/x/react-date-pickers/date-calendar/)
- [Digital Clock](/x/react-date-pickers/digital-clock/)
- [Time Clock](/x/react-date-pickers/time-clock/)

The value of the component can be uncontrolled or controlled.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ import { DateTime } from 'luxon';
### Stop rendering a clock on desktop

In desktop mode, the `DateTimePicker` and `TimePicker` components will no longer render the [`TimeClock`](/x/react-date-pickers/time-clock/) component.
The `DateTimePicker` component currently has no replacement, but on `TimePicker` a new [`DigitalClock`](/x/react-date-pickers/digital-clock/) component has been introduced instead.
The behavior on mobile mode is still the same.
The `TimeClock` component has been replaced with a new [`DigitalClock`](/x/react-date-pickers/digital-clock/) component instead.
The behavior on `Mobile` and `Static` variants is still the same.
If you were relying on Clock Picker in desktop mode for tests—make sure to check [testing caveats](/x/react-date-pickers/base-concepts/#testing-caveats) to choose the best replacement for it.

You can manually re-enable the previous clock component using the new `viewRenderers` prop.
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/x/api/date-pickers/date-time-picker-tabs.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"view": {
"type": {
"name": "enum",
"description": "'day'<br>&#124;&nbsp;'hours'<br>&#124;&nbsp;'minutes'<br>&#124;&nbsp;'month'<br>&#124;&nbsp;'seconds'<br>&#124;&nbsp;'year'"
"description": "'day'<br>&#124;&nbsp;'hours'<br>&#124;&nbsp;'meridiem'<br>&#124;&nbsp;'minutes'<br>&#124;&nbsp;'month'<br>&#124;&nbsp;'seconds'<br>&#124;&nbsp;'year'"
},
"required": true
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"view": {
"type": {
"name": "enum",
"description": "'day'<br>&#124;&nbsp;'hours'<br>&#124;&nbsp;'minutes'<br>&#124;&nbsp;'month'<br>&#124;&nbsp;'seconds'<br>&#124;&nbsp;'year'"
"description": "'day'<br>&#124;&nbsp;'hours'<br>&#124;&nbsp;'meridiem'<br>&#124;&nbsp;'minutes'<br>&#124;&nbsp;'month'<br>&#124;&nbsp;'seconds'<br>&#124;&nbsp;'year'"
},
"required": true
},
Expand Down
18 changes: 15 additions & 3 deletions docs/pages/x/api/date-pickers/date-time-picker.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"openTo": {
"type": {
"name": "enum",
"description": "'day'<br>&#124;&nbsp;'hours'<br>&#124;&nbsp;'minutes'<br>&#124;&nbsp;'month'<br>&#124;&nbsp;'seconds'<br>&#124;&nbsp;'year'"
"description": "'day'<br>&#124;&nbsp;'hours'<br>&#124;&nbsp;'meridiem'<br>&#124;&nbsp;'minutes'<br>&#124;&nbsp;'month'<br>&#124;&nbsp;'seconds'<br>&#124;&nbsp;'year'"
}
},
"orientation": {
Expand Down Expand Up @@ -103,6 +103,7 @@
"shouldDisableTime": { "type": { "name": "func" } },
"shouldDisableYear": { "type": { "name": "func" } },
"showDaysOutsideCurrentMonth": { "type": { "name": "bool" } },
"skipDisabled": { "type": { "name": "bool" } },
"slotProps": { "type": { "name": "object" }, "default": "{}" },
"slots": { "type": { "name": "object" }, "default": "{}" },
"sx": {
Expand All @@ -111,17 +112,24 @@
"description": "Array&lt;func<br>&#124;&nbsp;object<br>&#124;&nbsp;bool&gt;<br>&#124;&nbsp;func<br>&#124;&nbsp;object"
}
},
"timeSteps": {
"type": {
"name": "shape",
"description": "{ hours?: number, minutes?: number, seconds?: number }"
},
"default": "{ hours: 1, minutes: 5, seconds: 5 }"
},
"value": { "type": { "name": "any" } },
"view": {
"type": {
"name": "enum",
"description": "'day'<br>&#124;&nbsp;'hours'<br>&#124;&nbsp;'minutes'<br>&#124;&nbsp;'month'<br>&#124;&nbsp;'seconds'<br>&#124;&nbsp;'year'"
"description": "'day'<br>&#124;&nbsp;'hours'<br>&#124;&nbsp;'meridiem'<br>&#124;&nbsp;'minutes'<br>&#124;&nbsp;'month'<br>&#124;&nbsp;'seconds'<br>&#124;&nbsp;'year'"
}
},
"viewRenderers": {
"type": {
"name": "shape",
"description": "{ day?: func, hours?: func, minutes?: func, month?: func, seconds?: func, year?: func }"
"description": "{ day?: func, hours?: func, meridiem?: func, minutes?: func, month?: func, seconds?: func, year?: func }"
}
},
"views": {
Expand All @@ -148,6 +156,10 @@
"type": { "name": "elementType" }
},
"Dialog": { "default": "PickersModalDialogRoot", "type": { "name": "elementType" } },
"DigitalClockSectionItem": {
"default": "MenuItem from '@mui/material'",
"type": { "name": "elementType" }
},
"Field": { "type": { "name": "elementType" } },
"InputAdornment": { "default": "InputAdornment", "type": { "name": "elementType" } },
"Layout": { "type": { "name": "elementType" } },
Expand Down
18 changes: 15 additions & 3 deletions docs/pages/x/api/date-pickers/desktop-date-time-picker.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"openTo": {
"type": {
"name": "enum",
"description": "'day'<br>&#124;&nbsp;'hours'<br>&#124;&nbsp;'minutes'<br>&#124;&nbsp;'month'<br>&#124;&nbsp;'seconds'<br>&#124;&nbsp;'year'"
"description": "'day'<br>&#124;&nbsp;'hours'<br>&#124;&nbsp;'meridiem'<br>&#124;&nbsp;'minutes'<br>&#124;&nbsp;'month'<br>&#124;&nbsp;'seconds'<br>&#124;&nbsp;'year'"
}
},
"orientation": {
Expand Down Expand Up @@ -99,6 +99,7 @@
"shouldDisableTime": { "type": { "name": "func" } },
"shouldDisableYear": { "type": { "name": "func" } },
"showDaysOutsideCurrentMonth": { "type": { "name": "bool" } },
"skipDisabled": { "type": { "name": "bool" } },
"slotProps": { "type": { "name": "object" }, "default": "{}" },
"slots": { "type": { "name": "object" }, "default": "{}" },
"sx": {
Expand All @@ -107,17 +108,24 @@
"description": "Array&lt;func<br>&#124;&nbsp;object<br>&#124;&nbsp;bool&gt;<br>&#124;&nbsp;func<br>&#124;&nbsp;object"
}
},
"timeSteps": {
"type": {
"name": "shape",
"description": "{ hours?: number, minutes?: number, seconds?: number }"
},
"default": "{ hours: 1, minutes: 5, seconds: 5 }"
},
"value": { "type": { "name": "any" } },
"view": {
"type": {
"name": "enum",
"description": "'day'<br>&#124;&nbsp;'hours'<br>&#124;&nbsp;'minutes'<br>&#124;&nbsp;'month'<br>&#124;&nbsp;'seconds'<br>&#124;&nbsp;'year'"
"description": "'day'<br>&#124;&nbsp;'hours'<br>&#124;&nbsp;'meridiem'<br>&#124;&nbsp;'minutes'<br>&#124;&nbsp;'month'<br>&#124;&nbsp;'seconds'<br>&#124;&nbsp;'year'"
}
},
"viewRenderers": {
"type": {
"name": "shape",
"description": "{ day?: func, hours?: func, minutes?: func, month?: func, seconds?: func, year?: func }"
"description": "{ day?: func, hours?: func, meridiem?: func, minutes?: func, month?: func, seconds?: func, year?: func }"
}
},
"views": {
Expand All @@ -143,6 +151,10 @@
"default": "TrapFocus from @mui/material",
"type": { "name": "elementType" }
},
"DigitalClockSectionItem": {
"default": "MenuItem from '@mui/material'",
"type": { "name": "elementType" }
},
"Field": { "type": { "name": "elementType" } },
"InputAdornment": { "default": "InputAdornment", "type": { "name": "elementType" } },
"Layout": { "type": { "name": "elementType" } },
Expand Down
3 changes: 3 additions & 0 deletions docs/translations/api-docs/date-pickers/date-time-picker.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@
"shouldDisableTime": "Disable specific time.<br><br><strong>Signature:</strong><br><code>function(value: TDate, view: TimeView) =&gt; boolean</code><br><em>value:</em> The value to check.<br><em>view:</em> The clock type of the timeValue.<br> <em>returns</em> (boolean): If <code>true</code> the time will be disabled.",
"shouldDisableYear": "Disable specific year.<br><br><strong>Signature:</strong><br><code>function(year: TDate) =&gt; boolean</code><br><em>year:</em> The year to test.<br> <em>returns</em> (boolean): If <code>true</code>, the year will be disabled.",
"showDaysOutsideCurrentMonth": "If <code>true</code>, days outside the current month are rendered:<br>- if <code>fixedWeekNumber</code> is defined, renders days to have the weeks requested.<br>- if <code>fixedWeekNumber</code> is not defined, renders day to fill the first and last week of the current month.<br>- ignored if <code>calendars</code> equals more than <code>1</code> on range pickers.",
"skipDisabled": "If <code>true</code>, disabled digital clock items will not be rendered.",
"slotProps": "The props used for each component slot.",
"slots": "Overridable component slots.",
"sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the <a href=\"/system/getting-started/the-sx-prop/\">`sx` page</a> for more details.",
"timeSteps": "The time steps between two time unit options. For example, if <code>timeStep.minutes = 8</code>, then the available minute options will be <code>[0, 8, 16, 24, 32, 40, 48, 56]</code>. When single column time renderer is used, only <code>timeStep.minutes</code> will be used.",
"value": "The selected value. Used when the component is controlled.",
"view": "The visible view. Used when the component view is controlled. Must be a valid option from <code>views</code> list.",
"viewRenderers": "Define custom view renderers for each section. If <code>null</code>, the section will only have field editing. If <code>undefined</code>, internally defined view will be the used.",
Expand All @@ -72,6 +74,7 @@
"DesktopTransition": "Custom component for the desktop popper <a href=\"https://mui.com/material-ui/transitions/\">Transition</a>.",
"DesktopTrapFocus": "Custom component for trapping the focus inside the views on desktop.",
"Dialog": "Custom component for the dialog inside which the views are rendered on mobile.",
"DigitalClockSectionItem": "Component responsible for rendering a single multi section digital clock section item.",
"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.\nIt wraps the toolbar, views, action bar, and shortcuts.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@
"shouldDisableTime": "Disable specific time.<br><br><strong>Signature:</strong><br><code>function(value: TDate, view: TimeView) =&gt; boolean</code><br><em>value:</em> The value to check.<br><em>view:</em> The clock type of the timeValue.<br> <em>returns</em> (boolean): If <code>true</code> the time will be disabled.",
"shouldDisableYear": "Disable specific year.<br><br><strong>Signature:</strong><br><code>function(year: TDate) =&gt; boolean</code><br><em>year:</em> The year to test.<br> <em>returns</em> (boolean): If <code>true</code>, the year will be disabled.",
"showDaysOutsideCurrentMonth": "If <code>true</code>, days outside the current month are rendered:<br>- if <code>fixedWeekNumber</code> is defined, renders days to have the weeks requested.<br>- if <code>fixedWeekNumber</code> is not defined, renders day to fill the first and last week of the current month.<br>- ignored if <code>calendars</code> equals more than <code>1</code> on range pickers.",
"skipDisabled": "If <code>true</code>, disabled digital clock items will not be rendered.",
"slotProps": "The props used for each component slot.",
"slots": "Overridable component slots.",
"sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the <a href=\"/system/getting-started/the-sx-prop/\">`sx` page</a> for more details.",
"timeSteps": "The time steps between two time unit options. For example, if <code>timeStep.minutes = 8</code>, then the available minute options will be <code>[0, 8, 16, 24, 32, 40, 48, 56]</code>. When single column time renderer is used, only <code>timeStep.minutes</code> will be used.",
"value": "The selected value. Used when the component is controlled.",
"view": "The visible view. Used when the component view is controlled. Must be a valid option from <code>views</code> list.",
"viewRenderers": "Define custom view renderers for each section. If <code>null</code>, the section will only have field editing. If <code>undefined</code>, internally defined view will be the used.",
Expand All @@ -70,6 +72,7 @@
"DesktopPaper": "Custom component for the paper rendered inside the desktop picker&#39;s Popper.",
"DesktopTransition": "Custom component for the desktop popper <a href=\"https://mui.com/material-ui/transitions/\">Transition</a>.",
"DesktopTrapFocus": "Custom component for trapping the focus inside the views on desktop.",
"DigitalClockSectionItem": "Component responsible for rendering a single multi section digital clock section item.",
"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.\nIt wraps the toolbar, views, action bar, and shortcuts.",
Expand Down
4 changes: 2 additions & 2 deletions packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -243,10 +243,10 @@ export const DateCalendar = React.forwardRef(function DateCalendar<TDate>(
const handleSelectedDayChange = useEventCallback((day: TDate | null) => {
if (value && day) {
// If there is a date already selected, then we want to keep its time
return setValueAndGoToNextView(mergeDateAndTime(utils, day, value), 'finish');
return handleValueChange(mergeDateAndTime(utils, day, value), 'finish');
}

return setValueAndGoToNextView(day, 'finish');
return handleValueChange(day, 'finish');
});

React.useEffect(() => {
Expand Down
25 changes: 21 additions & 4 deletions packages/x-date-pickers/src/DateTimePicker/DateTimePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import useMediaQuery from '@mui/material/useMediaQuery';
import { useThemeProps } from '@mui/material/styles';
import { DesktopDateTimePicker } from '../DesktopDateTimePicker';
import { MobileDateTimePicker } from '../MobileDateTimePicker';
import { MobileDateTimePicker, MobileDateTimePickerProps } from '../MobileDateTimePicker';
import { DateTimePickerProps } from './DateTimePicker.types';
import { DEFAULT_DESKTOP_MODE_MEDIA_QUERY } from '../internals/utils/utils';

Expand All @@ -26,7 +26,7 @@ const DateTimePicker = React.forwardRef(function DateTimePicker<TDate>(
return <DesktopDateTimePicker ref={ref} {...other} />;
}

return <MobileDateTimePicker ref={ref} {...other} />;
return <MobileDateTimePicker ref={ref} {...(other as MobileDateTimePickerProps<TDate>)} />;
}) as DateTimePickerComponent;

DateTimePicker.propTypes = {
Expand Down Expand Up @@ -272,7 +272,7 @@ DateTimePicker.propTypes = {
* Used when the component view is not controlled.
* Must be a valid option from `views` list.
*/
openTo: PropTypes.oneOf(['day', 'hours', 'minutes', 'month', 'seconds', 'year']),
openTo: PropTypes.oneOf(['day', 'hours', 'meridiem', 'minutes', 'month', 'seconds', 'year']),
/**
* Force rendering in particular orientation.
*/
Expand Down Expand Up @@ -364,6 +364,11 @@ DateTimePicker.propTypes = {
* @default false
*/
showDaysOutsideCurrentMonth: PropTypes.bool,
/**
* If `true`, disabled digital clock items will not be rendered.
* @default false
*/
skipDisabled: PropTypes.bool,
/**
* The props used for each component slot.
* @default {}
Expand All @@ -382,6 +387,17 @@ DateTimePicker.propTypes = {
PropTypes.func,
PropTypes.object,
]),
/**
* The time steps between two time unit options.
* For example, if `timeStep.minutes = 8`, then the available minute options will be `[0, 8, 16, 24, 32, 40, 48, 56]`.
* When single column time renderer is used, only `timeStep.minutes` will be used.
* @default{ hours: 1, minutes: 5, seconds: 5 }
*/
timeSteps: PropTypes.shape({
hours: PropTypes.number,
minutes: PropTypes.number,
seconds: PropTypes.number,
}),
/**
* The selected value.
* Used when the component is controlled.
Expand All @@ -392,7 +408,7 @@ DateTimePicker.propTypes = {
* Used when the component view is controlled.
* Must be a valid option from `views` list.
*/
view: PropTypes.oneOf(['day', 'hours', 'minutes', 'month', 'seconds', 'year']),
view: PropTypes.oneOf(['day', 'hours', 'meridiem', 'minutes', 'month', 'seconds', 'year']),
/**
* Define custom view renderers for each section.
* If `null`, the section will only have field editing.
Expand All @@ -401,6 +417,7 @@ DateTimePicker.propTypes = {
viewRenderers: PropTypes.shape({
day: PropTypes.func,
hours: PropTypes.func,
meridiem: PropTypes.func,
minutes: PropTypes.func,
month: PropTypes.func,
seconds: PropTypes.func,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
DesktopDateTimePickerSlotsComponent,
DesktopDateTimePickerSlotsComponentsProps,
} from '../DesktopDateTimePicker';
import { DateOrTimeViewWithMeridiem } from '../internals/models';
import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration';
import {
MobileDateTimePickerProps,
Expand All @@ -12,15 +13,15 @@ import {

export interface DateTimePickerSlotsComponents<TDate>
extends DesktopDateTimePickerSlotsComponent<TDate>,
MobileDateTimePickerSlotsComponent<TDate> {}
MobileDateTimePickerSlotsComponent<TDate, DateOrTimeViewWithMeridiem> {}

export interface DateTimePickerSlotsComponentsProps<TDate>
extends DesktopDateTimePickerSlotsComponentsProps<TDate>,
MobileDateTimePickerSlotsComponentsProps<TDate> {}
MobileDateTimePickerSlotsComponentsProps<TDate, DateOrTimeViewWithMeridiem> {}

export interface DateTimePickerProps<TDate>
extends DesktopDateTimePickerProps<TDate>,
MobileDateTimePickerProps<TDate> {
Omit<MobileDateTimePickerProps<TDate, DateOrTimeViewWithMeridiem>, 'views'> {
/**
* CSS media query when `Mobile` mode will be changed to `Desktop`.
* @default '@media (pointer: fine)'
Expand Down
Loading

0 comments on commit 030a8d0

Please sign in to comment.