From 89759fbe4e5400a0d90f54c528b11e94c091666f Mon Sep 17 00:00:00 2001 From: Lukas Date: Wed, 10 May 2023 14:51:08 +0300 Subject: [PATCH 01/27] Add a combined `dateTimeVIewRenderer` and adapt typing --- .../src/DateTimePicker/DateTimePicker.tsx | 30 +++- .../DateTimePicker/DateTimePicker.types.ts | 7 +- .../src/DateTimePicker/DateTimePickerTabs.tsx | 11 +- .../DateTimePicker/DateTimePickerToolbar.tsx | 9 +- .../src/DateTimePicker/shared.tsx | 24 +-- .../DesktopDateTimePicker.tsx | 76 +++++++-- .../DesktopDateTimePicker.types.ts | 15 +- .../MobileDateTimePicker.tsx | 1 + .../MobileDateTimePicker.types.ts | 31 ++-- .../StaticDateTimePicker.tsx | 1 + .../StaticDateTimePicker.types.ts | 2 +- .../dateTimeViewRenderers.tsx | 161 ++++++++++++++++++ .../src/dateTimeViewRenderers/index.ts | 2 + .../dateViewRenderers/dateViewRenderers.tsx | 4 +- .../DateTimeViewWrapper.tsx | 6 + .../components/DateTimeViewWrapper/index.ts | 1 + .../src/internals/utils/date-utils.ts | 4 + .../src/internals/utils/time-utils.ts | 3 + 18 files changed, 327 insertions(+), 61 deletions(-) create mode 100644 packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx create mode 100644 packages/x-date-pickers/src/dateTimeViewRenderers/index.ts create mode 100644 packages/x-date-pickers/src/internals/components/DateTimeViewWrapper/DateTimeViewWrapper.tsx create mode 100644 packages/x-date-pickers/src/internals/components/DateTimeViewWrapper/index.ts diff --git a/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.tsx b/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.tsx index 9f50915f0c65..02e4eabd3f01 100644 --- a/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.tsx @@ -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'; @@ -26,7 +26,7 @@ const DateTimePicker = React.forwardRef(function DateTimePicker( return ; } - return ; + return )} />; }) as DateTimePickerComponent; DateTimePicker.propTypes = { @@ -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. */ @@ -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 {} @@ -382,6 +387,22 @@ DateTimePicker.propTypes = { PropTypes.func, PropTypes.object, ]), + /** + * Amount of time options below or at which the single column time renderer is used. + * @default 24 + */ + thresholdToRenderTimeInASingleColumn: PropTypes.number, + /** + * 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. @@ -392,7 +413,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. @@ -401,6 +422,7 @@ DateTimePicker.propTypes = { viewRenderers: PropTypes.shape({ day: PropTypes.func, hours: PropTypes.func, + meridiem: PropTypes.func, minutes: PropTypes.func, month: PropTypes.func, seconds: PropTypes.func, diff --git a/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.types.ts b/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.types.ts index cf9e900c257f..3ed5b62e344d 100644 --- a/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.types.ts +++ b/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.types.ts @@ -3,6 +3,7 @@ import { DesktopDateTimePickerSlotsComponent, DesktopDateTimePickerSlotsComponentsProps, } from '../DesktopDateTimePicker'; +import { DateOrTimeViewWithMeridiem } from '../internals/models'; import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; import { MobileDateTimePickerProps, @@ -12,15 +13,15 @@ import { export interface DateTimePickerSlotsComponents extends DesktopDateTimePickerSlotsComponent, - MobileDateTimePickerSlotsComponent {} + MobileDateTimePickerSlotsComponent {} export interface DateTimePickerSlotsComponentsProps extends DesktopDateTimePickerSlotsComponentsProps, - MobileDateTimePickerSlotsComponentsProps {} + MobileDateTimePickerSlotsComponentsProps {} export interface DateTimePickerProps extends DesktopDateTimePickerProps, - MobileDateTimePickerProps { + Omit, 'views'> { /** * CSS media query when `Mobile` mode will be changed to `Desktop`. * @default '@media (pointer: fine)' diff --git a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerTabs.tsx b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerTabs.tsx index c04807a2e7c9..e3c430e3f70a 100644 --- a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerTabs.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerTabs.tsx @@ -5,7 +5,7 @@ import Tabs, { tabsClasses } from '@mui/material/Tabs'; import { styled, useThemeProps } from '@mui/material/styles'; import { unstable_composeClasses as composeClasses } from '@mui/utils'; import { Time, DateRange } from '../internals/components/icons'; -import { DateOrTimeView } from '../models'; +import { DateOrTimeViewWithMeridiem } from '../internals/models'; import { useLocaleText } from '../internals/hooks/useUtils'; import { DateTimePickerTabsClasses, @@ -15,7 +15,7 @@ import { BaseTabsProps, ExportedBaseTabsProps } from '../internals/models/props/ type TabValue = 'date' | 'time'; -const viewToTab = (view: DateOrTimeView): TabValue => { +const viewToTab = (view: DateOrTimeViewWithMeridiem): TabValue => { if (['day', 'month', 'year'].includes(view)) { return 'date'; } @@ -23,7 +23,7 @@ const viewToTab = (view: DateOrTimeView): TabValue => { return 'time'; }; -const tabToView = (tab: TabValue): DateOrTimeView => { +const tabToView = (tab: TabValue): DateOrTimeViewWithMeridiem => { if (tab === 'date') { return 'day'; } @@ -51,7 +51,7 @@ export interface ExportedDateTimePickerTabsProps extends ExportedBaseTabsProps { export interface DateTimePickerTabsProps extends ExportedDateTimePickerTabsProps, - BaseTabsProps { + BaseTabsProps { /** * Override or extend the styles applied to the component. */ @@ -158,7 +158,8 @@ DateTimePickerTabs.propTypes = { /** * Currently visible picker view. */ - view: PropTypes.oneOf(['day', 'hours', 'minutes', 'month', 'seconds', 'year']).isRequired, + view: PropTypes.oneOf(['day', 'hours', 'meridiem', 'minutes', 'month', 'seconds', 'year']) + .isRequired, } as any; export { DateTimePickerTabs }; diff --git a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx index d720dcd43f81..1cb2464f3a55 100644 --- a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx @@ -13,7 +13,7 @@ import { DateTimePickerToolbarClasses, getDateTimePickerToolbarUtilityClass, } from './dateTimePickerToolbarClasses'; -import { DateOrTimeView } from '../models'; +import { DateOrTimeViewWithMeridiem } from '../internals/models'; import { useMeridiemMode } from '../internals/hooks/date-helpers-hooks'; export interface ExportedDateTimePickerToolbarProps extends ExportedBaseToolbarProps { @@ -23,7 +23,7 @@ export interface ExportedDateTimePickerToolbarProps extends ExportedBaseToolbarP export interface DateTimePickerToolbarProps extends ExportedDateTimePickerToolbarProps, - BaseToolbarProps { + BaseToolbarProps { /** * Override or extend the styles applied to the component. */ @@ -366,9 +366,10 @@ DateTimePickerToolbar.propTypes = { /** * Currently visible picker view. */ - view: PropTypes.oneOf(['day', 'hours', 'minutes', 'month', 'seconds', 'year']).isRequired, + view: PropTypes.oneOf(['day', 'hours', 'meridiem', 'minutes', 'month', 'seconds', 'year']) + .isRequired, views: PropTypes.arrayOf( - PropTypes.oneOf(['day', 'hours', 'minutes', 'month', 'seconds', 'year']).isRequired, + PropTypes.oneOf(['day', 'hours', 'meridiem', 'minutes', 'month', 'seconds', 'year']).isRequired, ).isRequired, } as any; diff --git a/packages/x-date-pickers/src/DateTimePicker/shared.tsx b/packages/x-date-pickers/src/DateTimePicker/shared.tsx index 970fac3ced05..729cf69aba5f 100644 --- a/packages/x-date-pickers/src/DateTimePicker/shared.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/shared.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { useThemeProps } from '@mui/material/styles'; import { DefaultizedProps } from '../internals/models/helpers'; -import { DateOrTimeView, DateTimeValidationError } from '../models'; +import { DateTimeValidationError } from '../models'; import { useDefaultDates, useUtils } from '../internals/hooks/useUtils'; import { DateCalendarSlotsComponent, @@ -36,7 +36,7 @@ import { TimeViewRendererProps } from '../timeViewRenderers'; import { applyDefaultViewProps } from '../internals/utils/views'; import { uncapitalizeObjectKeys, UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; import { BaseClockProps, ExportedBaseClockProps } from '../internals/models/props/clock'; -import { TimeViewWithMeridiem } from '../internals/models'; +import { DateOrTimeViewWithMeridiem, TimeViewWithMeridiem } from '../internals/models'; export interface BaseDateTimePickerSlotsComponent extends DateCalendarSlotsComponent, @@ -66,8 +66,8 @@ export interface BaseDateTimePickerSlotsComponentsProps toolbar?: ExportedDateTimePickerToolbarProps; } -export interface BaseDateTimePickerProps - extends BasePickerInputProps, +export interface BaseDateTimePickerProps + extends BasePickerInputProps, Omit, 'onViewChange'>, ExportedBaseClockProps, DateTimeValidationProps { @@ -106,8 +106,8 @@ export interface BaseDateTimePickerProps viewRenderers?: Partial< PickerViewRendererLookup< TDate | null, - DateOrTimeView, - DateViewRendererProps & + TView, + DateViewRendererProps & TimeViewRendererProps>, {} > @@ -116,7 +116,8 @@ export interface BaseDateTimePickerProps type UseDateTimePickerDefaultizedProps< TDate, - Props extends BaseDateTimePickerProps, + TView extends DateOrTimeViewWithMeridiem, + Props extends BaseDateTimePickerProps, > = LocalizedComponent< TDate, DefaultizedProps< @@ -132,11 +133,12 @@ type UseDateTimePickerDefaultizedProps< export function useDateTimePickerDefaultizedProps< TDate, - Props extends BaseDateTimePickerProps, + TView extends DateOrTimeViewWithMeridiem, + Props extends BaseDateTimePickerProps, >( props: Props, name: string, -): Omit, 'components' | 'componentsProps'> { +): Omit, 'components' | 'componentsProps'> { const utils = useUtils(); const defaultDates = useDefaultDates(); const themeProps = useThemeProps({ @@ -164,8 +166,8 @@ export function useDateTimePickerDefaultizedProps< ...applyDefaultViewProps({ views: themeProps.views, openTo: themeProps.openTo, - defaultViews: ['year', 'day', 'hours', 'minutes'], - defaultOpenTo: 'day', + defaultViews: ['year', 'day', 'hours', 'minutes'] as TView[], + defaultOpenTo: 'day' as TView, }), ampm, localeText, diff --git a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx index b8be1a94f432..382f851cf9de 100644 --- a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx +++ b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx @@ -5,13 +5,15 @@ import { singleItemValueManager } from '../internals/utils/valueManagers'; import { DateTimeField } from '../DateTimeField'; import { DesktopDateTimePickerProps } from './DesktopDateTimePicker.types'; import { useDateTimePickerDefaultizedProps } from '../DateTimePicker/shared'; -import { renderDateViewCalendar } from '../dateViewRenderers'; +import { renderDesktopDateTimeView } from '../dateTimeViewRenderers'; import { useLocaleText, validateDateTime } from '../internals'; -import { DateOrTimeView } from '../models'; +import { DateOrTimeViewWithMeridiem } from '../internals/models'; import { Calendar } from '../internals/components/icons'; import { useDesktopPicker } from '../internals/hooks/useDesktopPicker'; import { extractValidationProps } from '../internals/utils/validation/extractValidationProps'; import { PickerViewRendererLookup } from '../internals/hooks/usePicker/usePickerViews'; +import { PickersActionBarAction } from '../PickersActionBar'; +import { isDatePickerView } from '../internals/utils/date-utils'; type DesktopDateTimePickerComponent = (( props: DesktopDateTimePickerProps & React.RefAttributes, @@ -26,24 +28,44 @@ const DesktopDateTimePicker = React.forwardRef(function DesktopDateTimePicker >(inProps, 'MuiDesktopDateTimePicker'); - const viewRenderers: PickerViewRendererLookup = { - day: renderDateViewCalendar, - month: renderDateViewCalendar, - year: renderDateViewCalendar, - hours: null, - minutes: null, - seconds: null, - ...defaultizedProps.viewRenderers, - }; + const thresholdToRenderTimeInASingleColumn = + defaultizedProps.thresholdToRenderTimeInASingleColumn ?? 24; + const timeSteps = { hours: 1, minutes: 5, seconds: 5, ...defaultizedProps.timeSteps }; + const shouldRenderTimeInASingleColumn = + (24 * 60) / (timeSteps.hours * timeSteps.minutes) <= thresholdToRenderTimeInASingleColumn; + + const viewRenderers: PickerViewRendererLookup = + { + day: renderDesktopDateTimeView, + month: renderDesktopDateTimeView, + year: renderDesktopDateTimeView, + hours: renderDesktopDateTimeView, + minutes: renderDesktopDateTimeView, + seconds: renderDesktopDateTimeView, + meridiem: renderDesktopDateTimeView, + ...defaultizedProps.viewRenderers, + }; const ampmInClock = defaultizedProps.ampmInClock ?? true; + const actionBarActions: PickersActionBarAction[] = shouldRenderTimeInASingleColumn + ? [] + : ['accept']; + const views: readonly DateOrTimeViewWithMeridiem[] = defaultizedProps.ampm + ? [...defaultizedProps.views, 'meridiem'] + : defaultizedProps.views; // Props with the default values specific to the desktop variant const props = { ...defaultizedProps, viewRenderers, + // Setting only `hours` time view in case of single column time picker + // Allows for easy view lifecycle management + views: shouldRenderTimeInASingleColumn + ? ([...views.filter(isDatePickerView), 'hours'] as DateOrTimeViewWithMeridiem[]) + : views, yearsPerRow: defaultizedProps.yearsPerRow ?? 4, ampmInClock, slots: { @@ -68,10 +90,14 @@ const DesktopDateTimePicker = React.forwardRef(function DesktopDateTimePicker({ + const { renderPicker } = useDesktopPicker({ props, valueManager: singleItemValueManager, valueType: 'date-time', @@ -319,7 +345,7 @@ DesktopDateTimePicker.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. */ @@ -411,6 +437,11 @@ DesktopDateTimePicker.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 {} @@ -429,6 +460,22 @@ DesktopDateTimePicker.propTypes = { PropTypes.func, PropTypes.object, ]), + /** + * Amount of time options below or at which the single column time renderer is used. + * @default 24 + */ + thresholdToRenderTimeInASingleColumn: PropTypes.number, + /** + * 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. @@ -439,7 +486,7 @@ DesktopDateTimePicker.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. @@ -448,6 +495,7 @@ DesktopDateTimePicker.propTypes = { viewRenderers: PropTypes.shape({ day: PropTypes.func, hours: PropTypes.func, + meridiem: PropTypes.func, minutes: PropTypes.func, month: PropTypes.func, seconds: PropTypes.func, diff --git a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.types.ts b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.types.ts index 45c68f08a00d..9edff68ba7c2 100644 --- a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.types.ts +++ b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.types.ts @@ -11,21 +11,28 @@ import { import { MakeOptional } from '../internals/models/helpers'; import { DateOrTimeView } from '../models'; import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; +import { DesktopOnlyTimePickerProps } from '../internals/models/props/clock'; +import { DateOrTimeViewWithMeridiem } from '../internals/models'; export interface DesktopDateTimePickerSlotsComponent extends BaseDateTimePickerSlotsComponent, MakeOptional< - UseDesktopPickerSlotsComponent, + UseDesktopPickerSlotsComponent, 'Field' | 'OpenPickerIcon' > {} export interface DesktopDateTimePickerSlotsComponentsProps extends BaseDateTimePickerSlotsComponentsProps, - ExportedUseDesktopPickerSlotsComponentsProps {} + ExportedUseDesktopPickerSlotsComponentsProps {} export interface DesktopDateTimePickerProps - extends BaseDateTimePickerProps, - DesktopOnlyPickerProps { + extends BaseDateTimePickerProps, + DesktopOnlyPickerProps, + DesktopOnlyTimePickerProps { + /** + * Available views. + */ + views?: readonly DateOrTimeView[]; /** * Years rendered per row. * @default 4 diff --git a/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx b/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx index 05bd59b683f6..8de7fa1fabd7 100644 --- a/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx +++ b/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx @@ -25,6 +25,7 @@ const MobileDateTimePicker = React.forwardRef(function MobileDateTimePicker >(inProps, 'MuiMobileDateTimePicker'); diff --git a/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.types.ts b/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.types.ts index 29e4e02019f7..61be2967f7a7 100644 --- a/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.types.ts +++ b/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.types.ts @@ -11,38 +11,45 @@ import { import { MakeOptional } from '../internals/models/helpers'; import { DateOrTimeView } from '../models'; import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; +import { DateOrTimeViewWithMeridiem } from '../internals/models'; -export interface MobileDateTimePickerSlotsComponent - extends BaseDateTimePickerSlotsComponent, - MakeOptional, 'Field'> {} +export interface MobileDateTimePickerSlotsComponent< + TDate, + TView extends DateOrTimeViewWithMeridiem = DateOrTimeView, +> extends BaseDateTimePickerSlotsComponent, + MakeOptional, 'Field'> {} -export interface MobileDateTimePickerSlotsComponentsProps - extends BaseDateTimePickerSlotsComponentsProps, - ExportedUseMobilePickerSlotsComponentsProps {} +export interface MobileDateTimePickerSlotsComponentsProps< + TDate, + TView extends DateOrTimeViewWithMeridiem = DateOrTimeView, +> extends BaseDateTimePickerSlotsComponentsProps, + ExportedUseMobilePickerSlotsComponentsProps {} -export interface MobileDateTimePickerProps - extends BaseDateTimePickerProps, +export interface MobileDateTimePickerProps< + TDate, + TView extends DateOrTimeViewWithMeridiem = DateOrTimeView, +> extends BaseDateTimePickerProps, MobileOnlyPickerProps { /** * Overridable components. * @default {} * @deprecated Please use `slots`. */ - components?: MobileDateTimePickerSlotsComponent; + components?: MobileDateTimePickerSlotsComponent; /** * The props used for each component slot. * @default {} * @deprecated Please use `slotProps`. */ - componentsProps?: MobileDateTimePickerSlotsComponentsProps; + componentsProps?: MobileDateTimePickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: UncapitalizeObjectKeys>; /** * The props used for each component slot. * @default {} */ - slotProps?: MobileDateTimePickerSlotsComponentsProps; + slotProps?: MobileDateTimePickerSlotsComponentsProps; } diff --git a/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.tsx b/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.tsx index 22bb0b0cd4b0..439865c30d8d 100644 --- a/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.tsx +++ b/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.tsx @@ -20,6 +20,7 @@ const StaticDateTimePicker = React.forwardRef(function StaticDateTimePicker >(inProps, 'MuiStaticDateTimePicker'); diff --git a/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.types.ts b/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.types.ts index f32150d6c35e..8ac7948fd2e4 100644 --- a/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.types.ts +++ b/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.types.ts @@ -20,7 +20,7 @@ export interface StaticDateTimePickerSlotsComponentsProps UseStaticPickerSlotsComponentsProps {} export interface StaticDateTimePickerProps - extends BaseDateTimePickerProps, + extends BaseDateTimePickerProps, MakeOptional { /** * Overridable components. diff --git a/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx b/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx new file mode 100644 index 000000000000..4bbdf753dc2f --- /dev/null +++ b/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx @@ -0,0 +1,161 @@ +import * as React from 'react'; +import Divider from '@mui/material/Divider'; +import { DateCalendar, DateCalendarProps } from '../DateCalendar'; +import { DateOrTimeViewWithMeridiem } from '../internals/models'; +import { + MultiSectionDigitalClock, + MultiSectionDigitalClockProps, + multiSectionDigitalClockSectionClasses, +} from '../MultiSectionDigitalClock'; +import { DateTimeViewWrapper } from '../internals/components/DateTimeViewWrapper'; +import { isInternalTimeView } from '../internals/utils/time-utils'; +import { isDatePickerView } from '../internals/utils/date-utils'; + +export interface DateTimeViewRendererProps + extends Omit< + DateCalendarProps & MultiSectionDigitalClockProps, + 'views' | 'openTo' | 'view' | 'onViewChange' | 'focusedView' + > { + view: TView; + onViewChange?: (view: TView) => void; + views: readonly TView[]; + focusedView: TView | null; +} + +export const renderDesktopDateTimeView = ({ + view, + onViewChange, + views, + focusedView, + onFocusedViewChange, + value, + defaultValue, + onChange, + className, + classes, + disableFuture, + disablePast, + minDate, + minTime, + maxDate, + maxTime, + shouldDisableDate, + shouldDisableMonth, + shouldDisableYear, + shouldDisableTime, + shouldDisableClock, + reduceAnimations, + minutesStep, + ampm, + onMonthChange, + monthsPerRow, + onYearChange, + yearsPerRow, + defaultCalendarMonth, + components, + componentsProps, + slots, + slotProps, + loading, + renderLoading, + disableHighlightToday, + readOnly, + disabled, + showDaysOutsideCurrentMonth, + dayOfWeekFormatter, + sx, + autoFocus, + fixedWeekNumber, + displayWeekNumber, + disableIgnoringDatePartForTimeValidation, + timeSteps, + skipDisabled, +}: DateTimeViewRendererProps) => { + return ( + + + + + + + + + + ); +}; diff --git a/packages/x-date-pickers/src/dateTimeViewRenderers/index.ts b/packages/x-date-pickers/src/dateTimeViewRenderers/index.ts new file mode 100644 index 000000000000..d909595c47ae --- /dev/null +++ b/packages/x-date-pickers/src/dateTimeViewRenderers/index.ts @@ -0,0 +1,2 @@ +export { renderDesktopDateTimeView } from './dateTimeViewRenderers'; +export type { DateTimeViewRendererProps } from './dateTimeViewRenderers'; diff --git a/packages/x-date-pickers/src/dateViewRenderers/dateViewRenderers.tsx b/packages/x-date-pickers/src/dateViewRenderers/dateViewRenderers.tsx index f5e73ed1d6ec..f852e2bf344a 100644 --- a/packages/x-date-pickers/src/dateViewRenderers/dateViewRenderers.tsx +++ b/packages/x-date-pickers/src/dateViewRenderers/dateViewRenderers.tsx @@ -2,9 +2,7 @@ import * as React from 'react'; import { DateCalendar, DateCalendarProps } from '../DateCalendar'; import { DateView } from '../models'; import { DateOrTimeViewWithMeridiem } from '../internals/models'; - -const isDatePickerView = (view: unknown): view is DateView => - view === 'year' || view === 'month' || view === 'day'; +import { isDatePickerView } from '../internals/utils/date-utils'; export interface DateViewRendererProps extends Omit< diff --git a/packages/x-date-pickers/src/internals/components/DateTimeViewWrapper/DateTimeViewWrapper.tsx b/packages/x-date-pickers/src/internals/components/DateTimeViewWrapper/DateTimeViewWrapper.tsx new file mode 100644 index 000000000000..0f4af5e432ad --- /dev/null +++ b/packages/x-date-pickers/src/internals/components/DateTimeViewWrapper/DateTimeViewWrapper.tsx @@ -0,0 +1,6 @@ +import { styled } from '@mui/material/styles'; + +export const DateTimeViewWrapper = styled('div')({ + display: 'flex', + margin: '0 auto', +}); diff --git a/packages/x-date-pickers/src/internals/components/DateTimeViewWrapper/index.ts b/packages/x-date-pickers/src/internals/components/DateTimeViewWrapper/index.ts new file mode 100644 index 000000000000..12cc5b6252d4 --- /dev/null +++ b/packages/x-date-pickers/src/internals/components/DateTimeViewWrapper/index.ts @@ -0,0 +1 @@ +export { DateTimeViewWrapper } from './DateTimeViewWrapper'; diff --git a/packages/x-date-pickers/src/internals/utils/date-utils.ts b/packages/x-date-pickers/src/internals/utils/date-utils.ts index 2ea3e7562a8e..a910d929988b 100644 --- a/packages/x-date-pickers/src/internals/utils/date-utils.ts +++ b/packages/x-date-pickers/src/internals/utils/date-utils.ts @@ -1,4 +1,5 @@ import { MuiPickersAdapter } from '../../models'; +import { DateOrTimeViewWithMeridiem } from '../models'; interface FindClosestDateParams { date: TDate; @@ -134,3 +135,6 @@ export const mergeDateAndTime = ( return mergedDate; }; + +const dateViews = ['year', 'month', 'day']; +export const isDatePickerView = (view: DateOrTimeViewWithMeridiem) => dateViews.includes(view); diff --git a/packages/x-date-pickers/src/internals/utils/time-utils.ts b/packages/x-date-pickers/src/internals/utils/time-utils.ts index 7710220e78ab..50da6d47b101 100644 --- a/packages/x-date-pickers/src/internals/utils/time-utils.ts +++ b/packages/x-date-pickers/src/internals/utils/time-utils.ts @@ -4,6 +4,9 @@ import { DateOrTimeViewWithMeridiem } from '../models'; const timeViews = ['hours', 'minutes', 'seconds']; export const isTimeView = (view: DateOrTimeViewWithMeridiem) => timeViews.includes(view); +export const isInternalTimeView = (view: DateOrTimeViewWithMeridiem) => + timeViews.includes(view) || view === 'meridiem'; + export type Meridiem = 'am' | 'pm'; export const getMeridiem = ( From 003ce582819ee1f6280b573411eda2071318bf04 Mon Sep 17 00:00:00 2001 From: Lukas Date: Wed, 10 May 2023 15:51:37 +0300 Subject: [PATCH 02/27] Avoid type casting --- .../src/dateViewRenderers/dateViewRenderers.tsx | 6 +++--- .../src/timeViewRenderers/timeViewRenderers.tsx | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/x-date-pickers/src/dateViewRenderers/dateViewRenderers.tsx b/packages/x-date-pickers/src/dateViewRenderers/dateViewRenderers.tsx index f852e2bf344a..c2c45b40c41b 100644 --- a/packages/x-date-pickers/src/dateViewRenderers/dateViewRenderers.tsx +++ b/packages/x-date-pickers/src/dateViewRenderers/dateViewRenderers.tsx @@ -54,12 +54,12 @@ export const renderDateViewCalendar = ({ autoFocus, fixedWeekNumber, displayWeekNumber, -}: DateViewRendererProps) => ( +}: DateViewRendererProps) => ( ({ autoFocus, showViewSwitcher, disableIgnoringDatePartForTimeValidation, -}: TimeViewRendererProps>) => ( +}: TimeViewRendererProps>) => ( - view={view as TimeView} + view={view} onViewChange={onViewChange} - focusedView={focusedView as TimeView} + focusedView={focusedView} onFocusedViewChange={onFocusedViewChange} - views={views.filter(isTimeView) as TimeView[]} + views={views.filter(isTimeView)} value={value} defaultValue={defaultValue} onChange={onChange} From 4d5d90a4b97a6e92b9f047cb29fd9c1bfd9a28cc Mon Sep 17 00:00:00 2001 From: Lukas Date: Wed, 10 May 2023 16:18:25 +0300 Subject: [PATCH 03/27] Use util --- .../x-date-pickers/src/DateTimePicker/DateTimePickerTabs.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerTabs.tsx b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerTabs.tsx index e3c430e3f70a..36628651d59a 100644 --- a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerTabs.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerTabs.tsx @@ -12,11 +12,12 @@ import { getDateTimePickerTabsUtilityClass, } from './dateTimePickerTabsClasses'; import { BaseTabsProps, ExportedBaseTabsProps } from '../internals/models/props/tabs'; +import { isDatePickerView } from '../internals/utils/date-utils'; type TabValue = 'date' | 'time'; const viewToTab = (view: DateOrTimeViewWithMeridiem): TabValue => { - if (['day', 'month', 'year'].includes(view)) { + if (isDatePickerView(view)) { return 'date'; } From 77d5ad42ba14b7cf779c04d0e9f1e663a2bd24e5 Mon Sep 17 00:00:00 2001 From: Lukas Date: Wed, 10 May 2023 17:01:01 +0300 Subject: [PATCH 04/27] Avoid passing incorrect `focusedView` --- .../x-date-pickers/src/dateViewRenderers/dateViewRenderers.tsx | 2 +- .../x-date-pickers/src/timeViewRenderers/timeViewRenderers.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/x-date-pickers/src/dateViewRenderers/dateViewRenderers.tsx b/packages/x-date-pickers/src/dateViewRenderers/dateViewRenderers.tsx index c2c45b40c41b..0a6a23a64507 100644 --- a/packages/x-date-pickers/src/dateViewRenderers/dateViewRenderers.tsx +++ b/packages/x-date-pickers/src/dateViewRenderers/dateViewRenderers.tsx @@ -59,7 +59,7 @@ export const renderDateViewCalendar = ({ view={view} onViewChange={onViewChange} views={views.filter(isDatePickerView)} - focusedView={focusedView} + focusedView={focusedView && isDatePickerView(focusedView) ? focusedView : null} onFocusedViewChange={onFocusedViewChange} value={value} defaultValue={defaultValue} diff --git a/packages/x-date-pickers/src/timeViewRenderers/timeViewRenderers.tsx b/packages/x-date-pickers/src/timeViewRenderers/timeViewRenderers.tsx index 616fc4c873e3..539511d8c35e 100644 --- a/packages/x-date-pickers/src/timeViewRenderers/timeViewRenderers.tsx +++ b/packages/x-date-pickers/src/timeViewRenderers/timeViewRenderers.tsx @@ -54,7 +54,7 @@ export const renderTimeViewClock = ({ view={view} onViewChange={onViewChange} - focusedView={focusedView} + focusedView={focusedView && isTimeView(focusedView) ? focusedView : null} onFocusedViewChange={onFocusedViewChange} views={views.filter(isTimeView)} value={value} From f3a73a64b0bb5fd5d1a196397a89ff903d91f51e Mon Sep 17 00:00:00 2001 From: Lukas Date: Wed, 10 May 2023 17:05:29 +0300 Subject: [PATCH 05/27] Avoid setting view twice and re-setting it in case of DateTimePicker --- packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx b/packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx index 49091644462c..02c9f953cc1d 100644 --- a/packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx +++ b/packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx @@ -243,10 +243,10 @@ export const DateCalendar = React.forwardRef(function DateCalendar( 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(() => { From 914f45151d2f6a12d5a145b100f1d8897ad788f4 Mon Sep 17 00:00:00 2001 From: Lukas Date: Thu, 11 May 2023 14:23:45 +0300 Subject: [PATCH 06/27] Improve typing --- .../DesktopDateTimePicker.types.ts | 10 ++++-- .../dateTimeViewRenderers.tsx | 32 ++++++++++++------- .../src/internals/utils/date-utils.ts | 5 +-- .../src/internals/utils/time-utils.ts | 7 ++-- 4 files changed, 36 insertions(+), 18 deletions(-) diff --git a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.types.ts b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.types.ts index 9edff68ba7c2..1cbb33469b50 100644 --- a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.types.ts +++ b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.types.ts @@ -13,17 +13,23 @@ import { DateOrTimeView } from '../models'; import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; import { DesktopOnlyTimePickerProps } from '../internals/models/props/clock'; import { DateOrTimeViewWithMeridiem } from '../internals/models'; +import { + MultiSectionDigitalClockSlotsComponent, + MultiSectionDigitalClockSlotsComponentsProps, +} from '../MultiSectionDigitalClock'; export interface DesktopDateTimePickerSlotsComponent extends BaseDateTimePickerSlotsComponent, MakeOptional< UseDesktopPickerSlotsComponent, 'Field' | 'OpenPickerIcon' - > {} + >, + MultiSectionDigitalClockSlotsComponent {} export interface DesktopDateTimePickerSlotsComponentsProps extends BaseDateTimePickerSlotsComponentsProps, - ExportedUseDesktopPickerSlotsComponentsProps {} + ExportedUseDesktopPickerSlotsComponentsProps, + MultiSectionDigitalClockSlotsComponentsProps {} export interface DesktopDateTimePickerProps extends BaseDateTimePickerProps, diff --git a/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx b/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx index 4bbdf753dc2f..f2e17d3d6024 100644 --- a/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx +++ b/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx @@ -10,16 +10,26 @@ import { import { DateTimeViewWrapper } from '../internals/components/DateTimeViewWrapper'; import { isInternalTimeView } from '../internals/utils/time-utils'; import { isDatePickerView } from '../internals/utils/date-utils'; +import type { DateTimePickerProps } from '../DateTimePicker/DateTimePicker.types'; -export interface DateTimeViewRendererProps +export interface DateTimeViewRendererProps extends Omit< - DateCalendarProps & MultiSectionDigitalClockProps, - 'views' | 'openTo' | 'view' | 'onViewChange' | 'focusedView' - > { - view: TView; - onViewChange?: (view: TView) => void; - views: readonly TView[]; - focusedView: TView | null; + DateCalendarProps & MultiSectionDigitalClockProps, + | 'views' + | 'openTo' + | 'view' + | 'onViewChange' + | 'focusedView' + | 'components' + | 'componentsProps' + | 'slots' + | 'slotProps' + >, + Pick, 'components' | 'componentsProps' | 'slots' | 'slotProps'> { + view: DateOrTimeViewWithMeridiem; + onViewChange?: (view: DateOrTimeViewWithMeridiem) => void; + views: readonly DateOrTimeViewWithMeridiem[]; + focusedView: DateOrTimeViewWithMeridiem | null; } export const renderDesktopDateTimeView = ({ @@ -70,7 +80,7 @@ export const renderDesktopDateTimeView = ({ disableIgnoringDatePartForTimeValidation, timeSteps, skipDisabled, -}: DateTimeViewRendererProps) => { +}: DateTimeViewRendererProps) => { return ( @@ -79,7 +89,7 @@ export const renderDesktopDateTimeView = ({ view={isDatePickerView(view) ? view : 'day'} onViewChange={onViewChange} views={views.filter(isDatePickerView)} - focusedView={isDatePickerView(focusedView) ? focusedView : null} + focusedView={focusedView && isDatePickerView(focusedView) ? focusedView : null} onFocusedViewChange={onFocusedViewChange} value={value} defaultValue={defaultValue} @@ -119,7 +129,7 @@ export const renderDesktopDateTimeView = ({ { @@ -137,4 +137,5 @@ export const mergeDateAndTime = ( }; const dateViews = ['year', 'month', 'day']; -export const isDatePickerView = (view: DateOrTimeViewWithMeridiem) => dateViews.includes(view); +export const isDatePickerView = (view: DateOrTimeViewWithMeridiem): view is DateView => + dateViews.includes(view); diff --git a/packages/x-date-pickers/src/internals/utils/time-utils.ts b/packages/x-date-pickers/src/internals/utils/time-utils.ts index 50da6d47b101..877632c33769 100644 --- a/packages/x-date-pickers/src/internals/utils/time-utils.ts +++ b/packages/x-date-pickers/src/internals/utils/time-utils.ts @@ -1,11 +1,12 @@ import { MuiPickersAdapter } from '../../models'; -import { DateOrTimeViewWithMeridiem } from '../models'; +import { DateOrTimeViewWithMeridiem, TimeViewWithMeridiem } from '../models'; const timeViews = ['hours', 'minutes', 'seconds']; export const isTimeView = (view: DateOrTimeViewWithMeridiem) => timeViews.includes(view); -export const isInternalTimeView = (view: DateOrTimeViewWithMeridiem) => - timeViews.includes(view) || view === 'meridiem'; +export const isInternalTimeView = ( + view: DateOrTimeViewWithMeridiem, +): view is TimeViewWithMeridiem => timeViews.includes(view) || view === 'meridiem'; export type Meridiem = 'am' | 'pm'; From f34b6d250cb3e048bfc3877ed65811c237c5bd0a Mon Sep 17 00:00:00 2001 From: Lukas Date: Thu, 11 May 2023 15:24:42 +0300 Subject: [PATCH 07/27] Avoid adding a divider when there are no actions --- .../src/dateTimeViewRenderers/dateTimeViewRenderers.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx b/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx index f2e17d3d6024..b397bbf7a3e6 100644 --- a/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx +++ b/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import Divider from '@mui/material/Divider'; +import { resolveComponentProps } from '@mui/base/utils'; import { DateCalendar, DateCalendarProps } from '../DateCalendar'; import { DateOrTimeViewWithMeridiem } from '../internals/models'; import { @@ -81,6 +82,10 @@ export const renderDesktopDateTimeView = ({ timeSteps, skipDisabled, }: DateTimeViewRendererProps) => { + const isActionBarVisible = !!resolveComponentProps( + slotProps?.actionBar ?? componentsProps?.actionBar, + {} as any, + )?.actions?.length; return ( @@ -165,7 +170,7 @@ export const renderDesktopDateTimeView = ({ skipDisabled={skipDisabled} /> - + {isActionBarVisible && } ); }; From 89a0cdad08902e30b64ccb4a8834ff07c8eb8a04 Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 12 May 2023 18:03:29 +0300 Subject: [PATCH 08/27] Add custom `DesktopDateTimePickerToolbar` styling and layout --- .../DateTimePicker/DateTimePickerToolbar.tsx | 60 ++++++++++++++----- .../MultiSectionDigitalClockSection.tsx | 7 ++- .../dateTimeViewRenderers.tsx | 1 - .../components/PickersToolbarButton.tsx | 5 +- .../src/internals/constants/dimensions.ts | 1 + 5 files changed, 55 insertions(+), 19 deletions(-) diff --git a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx index 1cb2464f3a55..d7e93a57b6a1 100644 --- a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx @@ -13,8 +13,9 @@ import { DateTimePickerToolbarClasses, getDateTimePickerToolbarUtilityClass, } from './dateTimePickerToolbarClasses'; -import { DateOrTimeViewWithMeridiem } from '../internals/models'; +import { DateOrTimeViewWithMeridiem, WrapperVariant } from '../internals/models'; import { useMeridiemMode } from '../internals/hooks/date-helpers-hooks'; +import { MULTI_SECTION_CLOCK_SECTION_WIDTH } from '../internals/constants/dimensions'; export interface ExportedDateTimePickerToolbarProps extends ExportedBaseToolbarProps { ampm?: boolean; @@ -28,6 +29,7 @@ export interface DateTimePickerToolbarProps * Override or extend the styles applied to the component. */ classes?: Partial; + wrapperVariant?: WrapperVariant; } const useUtilityClasses = (ownerState: DateTimePickerToolbarProps & { theme: Theme }) => { @@ -49,9 +51,13 @@ const DateTimePickerToolbarRoot = styled(PickersToolbar, { name: 'MuiDateTimePickerToolbar', slot: 'Root', overridesResolver: (props, styles) => styles.root, -})<{ ownerState: DateTimePickerToolbarProps }>(({ theme }) => ({ +})<{ ownerState: DateTimePickerToolbarProps }>(({ theme, ownerState }) => ({ paddingLeft: 16, - paddingRight: 16, + paddingRight: ownerState.wrapperVariant === 'desktop' ? 0 : 16, + borderBottom: + ownerState.wrapperVariant === 'desktop' + ? `1px solid ${(theme.vars || theme).palette.divider}` + : undefined, justifyContent: 'space-around', position: 'relative', [`& .${pickersToolbarClasses.penIconButton}`]: { @@ -102,6 +108,11 @@ const DateTimePickerToolbarTimeContainer = styled('div', { return { display: 'flex', flexDirection: direction, + ...(ownerState.wrapperVariant === 'desktop' && { + gap: 9, + alignSelf: 'flex-end', + marginRight: 4, + }), ...(theme.direction === 'rtl' && { flexDirection: `${direction}-reverse`, }), @@ -112,8 +123,9 @@ const DateTimePickerToolbarTimeDigitsContainer = styled('div', { name: 'MuiDateTimePickerToolbar', slot: 'TimeDigitsContainer', overridesResolver: (props, styles) => styles.timeDigitsContainer, -})(({ theme }) => ({ +})<{ ownerState: DateTimePickerToolbarProps }>(({ theme, ownerState }) => ({ display: 'flex', + ...(ownerState.wrapperVariant === 'desktop' && { gap: 1.5 }), ...(theme.direction === 'rtl' && { flexDirection: 'row-reverse', }), @@ -139,10 +151,10 @@ const DateTimePickerToolbarSeparator = styled(PickersToolbarText, { overridesResolver: (props, styles) => styles.separator, })<{ ownerState: DateTimePickerToolbarProps; -}>({ - margin: '0 4px 0 2px', +}>(({ ownerState }) => ({ + margin: ownerState.wrapperVariant === 'desktop' ? 0 : '0 4px 0 2px', cursor: 'default', -}); +})); // Taken from TimePickerToolbar const DateTimePickerToolbarAmPmSelection = styled('div', { @@ -186,6 +198,7 @@ function DateTimePickerToolbar(inProps: DateTimePickerToo views, disabled, readOnly, + wrapperVariant = 'mobile', ...other } = props; const ownerState = props; @@ -193,6 +206,7 @@ function DateTimePickerToolbar(inProps: DateTimePickerToo const { meridiemMode, handleMeridiemChange } = useMeridiemMode(value, ampm, onChange); const showAmPmControl = Boolean(ampm && !ampmInClock); + const isDesktop = wrapperVariant === 'desktop'; const localeText = useLocaleText(); const theme = useTheme(); @@ -236,7 +250,7 @@ function DateTimePickerToolbar(inProps: DateTimePickerToo {views.includes('day') && ( onViewChange('day')} selected={view === 'day'} @@ -245,10 +259,14 @@ function DateTimePickerToolbar(inProps: DateTimePickerToo )} - + {views.includes('hours') && ( onViewChange('hours')} selected={view === 'hours'} @@ -259,13 +277,14 @@ function DateTimePickerToolbar(inProps: DateTimePickerToo {views.includes('minutes') && ( onViewChange('minutes')} selected={view === 'minutes'} @@ -277,13 +296,14 @@ function DateTimePickerToolbar(inProps: DateTimePickerToo {views.includes('seconds') && ( onViewChange('seconds')} selected={view === 'seconds'} @@ -292,7 +312,7 @@ function DateTimePickerToolbar(inProps: DateTimePickerToo )} - {showAmPmControl && ( + {showAmPmControl && !isDesktop && ( (inProps: DateTimePickerToo /> )} + {ampm && isDesktop && ( + onViewChange('meridiem')} + selected={view === 'meridiem'} + value={value && meridiemMode ? utils.getMeridiemText(meridiemMode) : '--'} + width={MULTI_SECTION_CLOCK_SECTION_WIDTH} + /> + )} ); diff --git a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx index f6b032929f39..c2aed7d904ed 100644 --- a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx +++ b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx @@ -15,7 +15,10 @@ import type { MultiSectionDigitalClockSlotsComponentsProps, } from './MultiSectionDigitalClock.types'; import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; -import { DIGITAL_CLOCK_VIEW_HEIGHT } from '../internals/constants/dimensions'; +import { + DIGITAL_CLOCK_VIEW_HEIGHT, + MULTI_SECTION_CLOCK_SECTION_WIDTH, +} from '../internals/constants/dimensions'; export interface ExportedMultiSectionDigitalClockSectionProps { className?: string; @@ -78,7 +81,7 @@ const MultiSectionDigitalClockSectionItem = styled(MenuItem, { })(({ theme }) => ({ padding: 8, margin: '2px 4px', - width: 48, + width: MULTI_SECTION_CLOCK_SECTION_WIDTH, justifyContent: 'center', '&:first-of-type': { marginTop: 4, diff --git a/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx b/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx index b397bbf7a3e6..193787315ca1 100644 --- a/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx +++ b/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx @@ -88,7 +88,6 @@ export const renderDesktopDateTimeView = ({ )?.actions?.length; return ( - ; + width?: number; } const useUtilityClasses = (ownerState: PickersToolbarButtonProps) => { @@ -40,7 +41,8 @@ const PickersToolbarButtonRoot = styled(Button, { export const PickersToolbarButton: React.FunctionComponent = React.forwardRef(function PickersToolbarButton(inProps, ref) { const props = useThemeProps({ props: inProps, name: 'MuiPickersToolbarButton' }); - const { align, className, selected, typographyClassName, value, variant, ...other } = props; + const { align, className, selected, typographyClassName, value, variant, width, ...other } = + props; const classes = useUtilityClasses(props); @@ -50,6 +52,7 @@ export const PickersToolbarButton: React.FunctionComponent Date: Mon, 15 May 2023 14:06:27 +0300 Subject: [PATCH 09/27] Support desktop toolbar in landscape mode --- .../DateTimePicker/DateTimePickerToolbar.tsx | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx index d7e93a57b6a1..94feb8c83efe 100644 --- a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx @@ -53,9 +53,13 @@ const DateTimePickerToolbarRoot = styled(PickersToolbar, { overridesResolver: (props, styles) => styles.root, })<{ ownerState: DateTimePickerToolbarProps }>(({ theme, ownerState }) => ({ paddingLeft: 16, - paddingRight: ownerState.wrapperVariant === 'desktop' ? 0 : 16, + paddingRight: ownerState.wrapperVariant === 'desktop' && !ownerState.isLandscape ? 0 : 16, borderBottom: - ownerState.wrapperVariant === 'desktop' + ownerState.wrapperVariant === 'desktop' && !ownerState.isLandscape + ? `1px solid ${(theme.vars || theme).palette.divider}` + : undefined, + borderRight: + ownerState.wrapperVariant === 'desktop' && ownerState.isLandscape ? `1px solid ${(theme.vars || theme).palette.divider}` : undefined, justifyContent: 'space-around', @@ -104,14 +108,17 @@ const DateTimePickerToolbarTimeContainer = styled('div', { slot: 'TimeContainer', overridesResolver: (props, styles) => styles.timeContainer, })<{ ownerState: DateTimePickerToolbarProps }>(({ theme, ownerState }) => { - const direction = ownerState.isLandscape ? 'column' : 'row'; + const direction = + ownerState.isLandscape && ownerState.wrapperVariant !== 'desktop' ? 'column' : 'row'; return { display: 'flex', flexDirection: direction, ...(ownerState.wrapperVariant === 'desktop' && { - gap: 9, - alignSelf: 'flex-end', - marginRight: 4, + ...(!ownerState.isLandscape && { + gap: 9, + marginRight: 4, + alignSelf: 'flex-end', + }), }), ...(theme.direction === 'rtl' && { flexDirection: `${direction}-reverse`, @@ -266,7 +273,7 @@ function DateTimePickerToolbar(inProps: DateTimePickerToo {views.includes('hours') && ( onViewChange('hours')} selected={view === 'hours'} @@ -284,7 +291,7 @@ function DateTimePickerToolbar(inProps: DateTimePickerToo /> onViewChange('minutes')} selected={view === 'minutes'} @@ -303,7 +310,7 @@ function DateTimePickerToolbar(inProps: DateTimePickerToo /> onViewChange('seconds')} selected={view === 'seconds'} From 141e61969b57a00fc2fde7cc6031ac2729bbdb8c Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 15 May 2023 14:58:50 +0300 Subject: [PATCH 10/27] Specify desktop `wrapperVariant` --- .../src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx | 1 + 1 file changed, 1 insertion(+) 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 970cc59a9080..3dcf0e47c446 100644 --- a/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx +++ b/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx @@ -153,6 +153,7 @@ export const useDesktopPicker = < toolbar: { ...innerSlotProps?.toolbar, titleId: labelId, + wrapperVariant: 'desktop', }, popper: { 'aria-labelledby': labelledById, From b872592a35aa6403782f9446ce1883428f8d097e Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 15 May 2023 15:16:29 +0300 Subject: [PATCH 11/27] Keep focus and view in sync. --- .../src/internals/hooks/useViews.tsx | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/x-date-pickers/src/internals/hooks/useViews.tsx b/packages/x-date-pickers/src/internals/hooks/useViews.tsx index fc8733b69e9a..c1e14afc64bd 100644 --- a/packages/x-date-pickers/src/internals/hooks/useViews.tsx +++ b/packages/x-date-pickers/src/internals/hooks/useViews.tsx @@ -145,17 +145,6 @@ export function useViews({ const previousView: TView | null = views[viewIndex - 1] ?? null; const nextView: TView | null = views[viewIndex + 1] ?? null; - const handleChangeView = useEventCallback((newView: TView) => { - if (newView === view) { - return; - } - setView(newView); - - if (onViewChange) { - onViewChange(newView); - } - }); - const handleFocusedViewChange = useEventCallback((viewToFocus: TView, hasFocus: boolean) => { if (hasFocus) { // Focus event @@ -170,6 +159,17 @@ export function useViews({ onFocusedViewChange?.(viewToFocus, hasFocus); }); + const handleChangeView = useEventCallback((newView: TView) => { + if (newView === view) { + return; + } + setView(newView); + handleFocusedViewChange(newView, true); + + if (onViewChange) { + onViewChange(newView); + } + }); const goToNextView = useEventCallback(() => { if (nextView) { handleChangeView(nextView); From 9be28283371710672c4a7b34566079c98e7cdd76 Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 15 May 2023 16:41:26 +0300 Subject: [PATCH 12/27] Fix border --- .../x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx index 94feb8c83efe..0962e21d177d 100644 --- a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx @@ -55,7 +55,7 @@ const DateTimePickerToolbarRoot = styled(PickersToolbar, { paddingLeft: 16, paddingRight: ownerState.wrapperVariant === 'desktop' && !ownerState.isLandscape ? 0 : 16, borderBottom: - ownerState.wrapperVariant === 'desktop' && !ownerState.isLandscape + ownerState.wrapperVariant === 'desktop' ? `1px solid ${(theme.vars || theme).palette.divider}` : undefined, borderRight: From 72691df97befc6fdea131323da6429b1f7be6e31 Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 15 May 2023 16:43:26 +0300 Subject: [PATCH 13/27] Avoid rendering `MultiSectionDigitalClock` when there are no `viewRenderers` for time views --- .../dateTimeViewRenderers.tsx | 84 ++++++++++--------- .../hooks/usePicker/usePickerViews.ts | 23 ++--- 2 files changed, 57 insertions(+), 50 deletions(-) diff --git a/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx b/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx index 193787315ca1..de38812ef2b9 100644 --- a/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx +++ b/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx @@ -31,6 +31,7 @@ export interface DateTimeViewRendererProps onViewChange?: (view: DateOrTimeViewWithMeridiem) => void; views: readonly DateOrTimeViewWithMeridiem[]; focusedView: DateOrTimeViewWithMeridiem | null; + timeViewsCount: number; } export const renderDesktopDateTimeView = ({ @@ -81,6 +82,7 @@ export const renderDesktopDateTimeView = ({ disableIgnoringDatePartForTimeValidation, timeSteps, skipDisabled, + timeViewsCount, }: DateTimeViewRendererProps) => { const isActionBarVisible = !!resolveComponentProps( slotProps?.actionBar ?? componentsProps?.actionBar, @@ -129,45 +131,49 @@ export const renderDesktopDateTimeView = ({ fixedWeekNumber={fixedWeekNumber} displayWeekNumber={displayWeekNumber} /> - - + {timeViewsCount > 0 && ( + + + + + )} {isActionBarVisible && } diff --git a/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerViews.ts b/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerViews.ts index c02cb92a22f1..d1ca22ca7702 100644 --- a/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerViews.ts +++ b/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerViews.ts @@ -181,16 +181,16 @@ export const usePickerViews = < [disableOpenPicker, viewRenderers, views], ); - const hasMultipleUITimeView = React.useMemo(() => { - const numberUITimeViews = views.reduce((acc, viewForReduce) => { - if (viewRenderers[viewForReduce] != null && isTimeView(viewForReduce)) { - return acc + 1; - } - return acc; - }, 0); - - return numberUITimeViews > 1; - }, [viewRenderers, views]); + const timeViewsCount = React.useMemo( + () => + views.reduce((acc, viewForReduce) => { + if (viewRenderers[viewForReduce] != null && isTimeView(viewForReduce)) { + return acc + 1; + } + return acc; + }, 0), + [viewRenderers, views], + ); const currentViewMode = viewModeLookup[view]; const shouldRestoreFocus = useEventCallback(() => currentViewMode === 'UI'); @@ -270,7 +270,8 @@ export const usePickerViews = < onViewChange: setView, focusedView, onFocusedViewChange: setFocusedView, - showViewSwitcher: hasMultipleUITimeView, + showViewSwitcher: timeViewsCount > 1, + timeViewsCount, }); }, }; From db355ec6002f6dfdfa442a8201c75e53b09e7d0a Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 15 May 2023 16:50:34 +0300 Subject: [PATCH 14/27] Hiding focus state is evil --- .../x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx index 0962e21d177d..012d27bf2e48 100644 --- a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx @@ -325,7 +325,6 @@ function DateTimePickerToolbar(inProps: DateTimePickerToo ownerState={ownerState} > (inProps: DateTimePickerToo disabled={disabled} /> Date: Tue, 16 May 2023 13:29:59 +0300 Subject: [PATCH 15/27] Fix tests on `DesktopDateTimePicker` --- .../tests/DesktopDateTimePicker.test.tsx | 15 ++- .../describes.DesktopDateTimePicker.test.tsx | 46 +++++++- .../testDayViewValidation.tsx | 108 +++++++++--------- .../testControlledUnControlled.tsx | 4 +- .../describeValue/testPickerActionBar.tsx | 27 +++-- .../testPickerOpenCloseLifeCycle.tsx | 20 ++-- test/utils/pickers-utils.tsx | 17 ++- 7 files changed, 154 insertions(+), 83 deletions(-) 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 a93e76a74a5a..aac8ac0dfd51 100644 --- a/packages/x-date-pickers/src/DesktopDateTimePicker/tests/DesktopDateTimePicker.test.tsx +++ b/packages/x-date-pickers/src/DesktopDateTimePicker/tests/DesktopDateTimePicker.test.tsx @@ -33,7 +33,7 @@ describe('', () => { onChange={onChange} onAccept={onAccept} onClose={onClose} - defaultValue={adapterToUse.date(new Date(2018, 0, 1, 11, 53))} + defaultValue={adapterToUse.date(new Date(2018, 0, 1, 11, 55))} openTo="year" />, ); @@ -43,14 +43,23 @@ describe('', () => { // Select year userEvent.mousePress(screen.getByRole('button', { name: '2025' })); expect(onChange.callCount).to.equal(1); - expect(onChange.lastCall.args[0]).toEqualDateTime(new Date(2025, 0, 1, 11, 53)); + expect(onChange.lastCall.args[0]).toEqualDateTime(new Date(2025, 0, 1, 11, 55)); expect(onAccept.callCount).to.equal(0); expect(onClose.callCount).to.equal(0); // Change the date (same value) userEvent.mousePress(screen.getByRole('gridcell', { name: '1' })); expect(onChange.callCount).to.equal(1); // Don't call onChange again since the value did not change - expect(onAccept.callCount).to.equal(0); + // Change the hours (same value) + userEvent.mousePress(screen.getByRole('option', { name: '11 hours' })); + expect(onChange.callCount).to.equal(1); // Don't call onChange again since the value did not change + // Change the minutes (same value) + userEvent.mousePress(screen.getByRole('option', { name: '55 minutes' })); + expect(onChange.callCount).to.equal(1); // Don't call onChange again since the value did not change + // Change the meridiem (same value) + userEvent.mousePress(screen.getByRole('option', { name: 'AM' })); + expect(onChange.callCount).to.equal(1); // Don't call onChange again since the value did not change + expect(onAccept.callCount).to.equal(1); expect(onClose.callCount).to.equal(1); }); }); 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 37ad17ccd4aa..fef2b76962d4 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,11 +36,10 @@ describe(' - Describes', () => { componentFamily: 'picker', type: 'date-time', variant: 'desktop', - defaultProps: { - views: ['day'], - openTo: 'day', - }, - values: [adapterToUse.date(new Date(2018, 0, 1)), adapterToUse.date(new Date(2018, 0, 2))], + values: [ + adapterToUse.date(new Date(2018, 0, 1, 11, 30)), + adapterToUse.date(new Date(2018, 0, 2, 12, 35)), + ], emptyValue: null, clock, assertRenderedValue: (expectedValue: any) => { @@ -59,16 +58,51 @@ describe(' - Describes', () => { expectInputValue(input, expectedValueStr); }, setNewValue: (value, { isOpened, applySameValue } = {}) => { - const newValue = applySameValue ? value : adapterToUse.addDays(value, 1); + const newValue = applySameValue + ? value + : adapterToUse.addMinutes(adapterToUse.addHours(adapterToUse.addDays(value, 1), 1), 5); if (isOpened) { userEvent.mousePress( screen.getByRole('gridcell', { name: adapterToUse.getDate(newValue).toString() }), ); + const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale(); + const hours = adapterToUse.format(newValue, hasMeridiem ? 'hours12h' : 'hours24h'); + const hoursNumber = adapterToUse.getHours(newValue); + userEvent.mousePress(screen.getByRole('option', { name: `${parseInt(hours, 10)} hours` })); + userEvent.mousePress( + screen.getByRole('option', { name: `${adapterToUse.getMinutes(newValue)} minutes` }), + ); + if (hasMeridiem) { + // meridiem is an extra view on `DesktopDateTimePicker` + // we need to click it to finish selection + userEvent.mousePress( + screen.getByRole('option', { name: hoursNumber >= 12 ? 'PM' : 'AM' }), + ); + } } else { const input = getTextbox(); clickOnInput(input, 5); // Update the day userEvent.keyPress(input, { key: 'ArrowUp' }); + // move to the hours section + userEvent.keyPress(input, { key: 'ArrowRight' }); + userEvent.keyPress(input, { key: 'ArrowRight' }); + userEvent.keyPress(input, { key: 'ArrowUp' }); + // move to the minutes section + userEvent.keyPress(input, { key: 'ArrowRight' }); + // increment by 5 minutes + userEvent.keyPress(input, { key: 'PageUp' }); + const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale(); + if (hasMeridiem) { + // move to the meridiem section + userEvent.keyPress(input, { key: 'ArrowRight' }); + const previousHours = adapterToUse.getHours(value); + const newHours = adapterToUse.getHours(newValue); + // update meridiem section if it changed + if ((previousHours < 12 && newHours >= 12) || (previousHours >= 12 && newHours < 12)) { + userEvent.keyPress(input, { key: 'ArrowUp' }); + } + } } return newValue; diff --git a/packages/x-date-pickers/src/tests/describeValidation/testDayViewValidation.tsx b/packages/x-date-pickers/src/tests/describeValidation/testDayViewValidation.tsx index 555445d09467..f6d45a3e17d0 100644 --- a/packages/x-date-pickers/src/tests/describeValidation/testDayViewValidation.tsx +++ b/packages/x-date-pickers/src/tests/describeValidation/testDayViewValidation.tsx @@ -35,10 +35,10 @@ export const testDayViewValidation: DescribeValidationTestSuite = (ElementToTest />, ); - expect(screen.getByText('9')).not.to.have.attribute('disabled'); - expect(screen.getByText('10')).not.to.have.attribute('disabled'); - expect(screen.getByText('11')).to.have.attribute('disabled'); - expect(screen.getByText('12')).to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '9' })).not.to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '10' })).not.to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '11' })).to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '12' })).to.have.attribute('disabled'); }); it('should apply shouldDisableYear', function test() { @@ -50,16 +50,16 @@ export const testDayViewValidation: DescribeValidationTestSuite = (ElementToTest />, ); - expect(screen.getByText('1')).to.have.attribute('disabled'); - expect(screen.getByText('15')).to.have.attribute('disabled'); - expect(screen.getByText('30')).to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '1' })).to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '15' })).to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '30' })).to.have.attribute('disabled'); setProps({ value: adapterToUse.date(new Date(2019, 0, 1)) }); clock.runToLast(); - expect(screen.getByText('1')).not.to.have.attribute('disabled'); - expect(screen.getByText('15')).not.to.have.attribute('disabled'); - expect(screen.getByText('30')).not.to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '1' })).not.to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '15' })).not.to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '30' })).not.to.have.attribute('disabled'); }); it('should apply shouldDisableMonth', function test() { @@ -71,16 +71,16 @@ export const testDayViewValidation: DescribeValidationTestSuite = (ElementToTest />, ); - expect(screen.getByText('1')).to.have.attribute('disabled'); - expect(screen.getByText('15')).to.have.attribute('disabled'); - expect(screen.getByText('30')).to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '1' })).to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '15' })).to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '30' })).to.have.attribute('disabled'); setProps({ value: adapterToUse.date(new Date(2018, 1, 1)) }); clock.runToLast(); - expect(screen.getByText('1')).not.to.have.attribute('disabled'); - expect(screen.getByText('15')).not.to.have.attribute('disabled'); - expect(screen.getByText('28')).not.to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '1' })).not.to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '15' })).not.to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '28' })).not.to.have.attribute('disabled'); }); it('should apply disablePast', function test() { @@ -94,25 +94,25 @@ export const testDayViewValidation: DescribeValidationTestSuite = (ElementToTest const tomorrow = adapterToUse.addDays(now, 1); const yesterday = adapterToUse.addDays(now, -1); - expect(screen.getByText(adapterToUse.format(now, 'dayOfMonth'))).not.to.have.attribute( - 'disabled', - ); + expect( + screen.getByRole('gridcell', { name: adapterToUse.format(now, 'dayOfMonth') }), + ).not.to.have.attribute('disabled'); if (!adapterToUse.isSameMonth(now, tomorrow)) { setProps({ value: tomorrow }); clock.runToLast(); } - expect(screen.getByText(adapterToUse.format(tomorrow, 'dayOfMonth'))).not.to.have.attribute( - 'disabled', - ); + expect( + screen.getByRole('gridcell', { name: adapterToUse.format(tomorrow, 'dayOfMonth') }), + ).not.to.have.attribute('disabled'); if (!adapterToUse.isSameMonth(yesterday, tomorrow)) { setProps({ value: yesterday }); clock.runToLast(); } - expect(screen.getByText(adapterToUse.format(yesterday, 'dayOfMonth'))).to.have.attribute( - 'disabled', - ); + expect( + screen.getByRole('gridcell', { name: adapterToUse.format(yesterday, 'dayOfMonth') }), + ).to.have.attribute('disabled'); }); it('should apply disableFuture', function test() { @@ -126,25 +126,25 @@ export const testDayViewValidation: DescribeValidationTestSuite = (ElementToTest const tomorrow = adapterToUse.addDays(now, 1); const yesterday = adapterToUse.addDays(now, -1); - expect(screen.getByText(adapterToUse.format(now, 'dayOfMonth'))).not.to.have.attribute( - 'disabled', - ); + expect( + screen.getByRole('gridcell', { name: adapterToUse.format(now, 'dayOfMonth') }), + ).not.to.have.attribute('disabled'); if (!adapterToUse.isSameMonth(now, tomorrow)) { setProps({ value: tomorrow }); clock.runToLast(); } - expect(screen.getByText(adapterToUse.format(tomorrow, 'dayOfMonth'))).to.have.attribute( - 'disabled', - ); + expect( + screen.getByRole('gridcell', { name: adapterToUse.format(tomorrow, 'dayOfMonth') }), + ).to.have.attribute('disabled'); if (!adapterToUse.isSameMonth(yesterday, tomorrow)) { setProps({ value: yesterday }); clock.runToLast(); } - expect(screen.getByText(adapterToUse.format(yesterday, 'dayOfMonth'))).not.to.have.attribute( - 'disabled', - ); + expect( + screen.getByRole('gridcell', { name: adapterToUse.format(yesterday, 'dayOfMonth') }), + ).not.to.have.attribute('disabled'); }); it('should apply minDate', function test() { @@ -155,11 +155,11 @@ export const testDayViewValidation: DescribeValidationTestSuite = (ElementToTest minDate={adapterToUse.date(new Date(2019, 5, 4))} />, ); - expect(screen.getByText('1')).to.have.attribute('disabled'); - expect(screen.getByText('3')).to.have.attribute('disabled'); - expect(screen.getByText('4')).not.to.have.attribute('disabled'); - expect(screen.getByText('5')).not.to.have.attribute('disabled'); - expect(screen.getByText('30')).not.to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '1' })).to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '3' })).to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '4' })).not.to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '5' })).not.to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '30' })).not.to.have.attribute('disabled'); expect(screen.getByLabelText('Previous month')).to.have.attribute('disabled'); expect(screen.getByLabelText('Next month')).not.to.have.attribute('disabled'); }); @@ -172,11 +172,11 @@ export const testDayViewValidation: DescribeValidationTestSuite = (ElementToTest maxDate={adapterToUse.date(new Date(2019, 5, 4))} />, ); - expect(screen.getByText('1')).not.to.have.attribute('disabled'); - expect(screen.getByText('3')).not.to.have.attribute('disabled'); - expect(screen.getByText('4')).not.to.have.attribute('disabled'); - expect(screen.getByText('5')).to.have.attribute('disabled'); - expect(screen.getByText('30')).to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '1' })).not.to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '3' })).not.to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '4' })).not.to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '5' })).to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '30' })).to.have.attribute('disabled'); expect(screen.getByLabelText('Previous month')).not.to.have.attribute('disabled'); expect(screen.getByLabelText('Next month')).to.have.attribute('disabled'); }); @@ -194,11 +194,11 @@ export const testDayViewValidation: DescribeValidationTestSuite = (ElementToTest maxDateTime={adapterToUse.date(new Date(2019, 5, 4, 12, 0, 0))} />, ); - expect(screen.getByText('1')).not.to.have.attribute('disabled'); - expect(screen.getByText('3')).not.to.have.attribute('disabled'); - expect(screen.getByText('4')).not.to.have.attribute('disabled'); - expect(screen.getByText('5')).to.have.attribute('disabled'); - expect(screen.getByText('30')).to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '1' })).not.to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '3' })).not.to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '4' })).not.to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '5' })).to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '30' })).to.have.attribute('disabled'); }); it('should apply minDateTime', function test() { @@ -214,11 +214,11 @@ export const testDayViewValidation: DescribeValidationTestSuite = (ElementToTest minDateTime={adapterToUse.date(new Date(2019, 5, 4, 12, 0, 0))} />, ); - expect(screen.getByText('1')).to.have.attribute('disabled'); - expect(screen.getByText('3')).to.have.attribute('disabled'); - expect(screen.getByText('4')).not.to.have.attribute('disabled'); - expect(screen.getByText('5')).not.to.have.attribute('disabled'); - expect(screen.getByText('30')).not.to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '1' })).to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '3' })).to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '4' })).not.to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '5' })).not.to.have.attribute('disabled'); + expect(screen.getByRole('gridcell', { name: '30' })).not.to.have.attribute('disabled'); }); }); }; diff --git a/packages/x-date-pickers/src/tests/describeValue/testControlledUnControlled.tsx b/packages/x-date-pickers/src/tests/describeValue/testControlledUnControlled.tsx index 195f09ca9fc6..474c06fcb4f3 100644 --- a/packages/x-date-pickers/src/tests/describeValue/testControlledUnControlled.tsx +++ b/packages/x-date-pickers/src/tests/describeValue/testControlledUnControlled.tsx @@ -52,7 +52,7 @@ export const testControlledUnControlled: DescribeValueTestSuite = ( assertRenderedValue(newValue); // TODO: Clean this exception or change the clock behavior - expect(onChange.callCount).to.equal(getExpectedOnChangeCount(componentFamily)); + expect(onChange.callCount).to.equal(getExpectedOnChangeCount(componentFamily, params)); if (Array.isArray(newValue)) { newValue.forEach((value, index) => { expect(onChange.lastCall.args[0][index]).toEqualDateTime(value); @@ -68,7 +68,7 @@ export const testControlledUnControlled: DescribeValueTestSuite = ( render(); const newValue = setNewValue(values[0]); - expect(onChange.callCount).to.equal(getExpectedOnChangeCount(componentFamily)); + expect(onChange.callCount).to.equal(getExpectedOnChangeCount(componentFamily, params)); if (Array.isArray(newValue)) { newValue.forEach((value, index) => { expect(onChange.lastCall.args[0][index]).toEqualDateTime(value); diff --git a/packages/x-date-pickers/src/tests/describeValue/testPickerActionBar.tsx b/packages/x-date-pickers/src/tests/describeValue/testPickerActionBar.tsx index e62a725995eb..cbfc47537323 100644 --- a/packages/x-date-pickers/src/tests/describeValue/testPickerActionBar.tsx +++ b/packages/x-date-pickers/src/tests/describeValue/testPickerActionBar.tsx @@ -2,14 +2,15 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; import { screen, userEvent } from '@mui/monorepo/test/utils'; -import { adapterToUse } from 'test/utils/pickers-utils'; +import { adapterToUse, getExpectedOnChangeCount } from 'test/utils/pickers-utils'; import { DescribeValueTestSuite } from './describeValue.types'; export const testPickerActionBar: DescribeValueTestSuite = ( ElementToTest, getOptions, ) => { - const { componentFamily, render, values, emptyValue, setNewValue, type } = getOptions(); + const { componentFamily, render, values, emptyValue, setNewValue, ...pickerParams } = + getOptions(); if (componentFamily !== 'picker') { return; @@ -36,7 +37,7 @@ export const testPickerActionBar: DescribeValueTestSuite = ( // Clear the date userEvent.mousePress(screen.getByText(/clear/i)); expect(onChange.callCount).to.equal(1); - if (type === 'date-range') { + if (pickerParams.type === 'date-range') { onChange.lastCall.args[0].forEach((value, index) => { expect(value).to.deep.equal(emptyValue[index]); }); @@ -44,7 +45,7 @@ export const testPickerActionBar: DescribeValueTestSuite = ( expect(onChange.lastCall.args[0]).to.deep.equal(emptyValue); } expect(onAccept.callCount).to.equal(1); - if (type === 'date-range') { + if (pickerParams.type === 'date-range') { onAccept.lastCall.args[0].forEach((value, index) => { expect(value).to.deep.equal(emptyValue[index]); }); @@ -101,8 +102,10 @@ export const testPickerActionBar: DescribeValueTestSuite = ( // Cancel the modifications userEvent.mousePress(screen.getByText(/cancel/i)); - expect(onChange.callCount).to.equal(2); - if (type === 'date-range') { + expect(onChange.callCount).to.equal( + getExpectedOnChangeCount(componentFamily, pickerParams) + 1, + ); + if (pickerParams.type === 'date-range') { values[0].forEach((value, index) => { expect(onChange.lastCall.args[0][index]).toEqualDateTime(value); }); @@ -161,7 +164,9 @@ export const testPickerActionBar: DescribeValueTestSuite = ( // Accept the modifications userEvent.mousePress(screen.getByText(/ok/i)); - expect(onChange.callCount).to.equal(1); // The accepted value as already been committed, don't call onChange again + expect(onChange.callCount).to.equal( + getExpectedOnChangeCount(componentFamily, pickerParams), + ); // The accepted value as already been committed, don't call onChange again expect(onAccept.callCount).to.equal(1); expect(onClose.callCount).to.equal(1); }); @@ -235,10 +240,12 @@ export const testPickerActionBar: DescribeValueTestSuite = ( userEvent.mousePress(screen.getByText(/today/i)); const startOfToday = - type === 'date' ? adapterToUse.startOfDay(adapterToUse.date()) : adapterToUse.date(); + pickerParams.type === 'date' + ? adapterToUse.startOfDay(adapterToUse.date()) + : adapterToUse.date(); expect(onChange.callCount).to.equal(1); - if (type === 'date-range') { + if (pickerParams.type === 'date-range') { onChange.lastCall.args[0].forEach((value) => { expect(value).toEqualDateTime(startOfToday); }); @@ -246,7 +253,7 @@ export const testPickerActionBar: DescribeValueTestSuite = ( expect(onChange.lastCall.args[0]).toEqualDateTime(startOfToday); } expect(onAccept.callCount).to.equal(1); - if (type === 'date-range') { + if (pickerParams.type === 'date-range') { onAccept.lastCall.args[0].forEach((value) => { expect(value).toEqualDateTime(startOfToday); }); diff --git a/packages/x-date-pickers/src/tests/describeValue/testPickerOpenCloseLifeCycle.tsx b/packages/x-date-pickers/src/tests/describeValue/testPickerOpenCloseLifeCycle.tsx index 63efe7b35849..eed3af685beb 100644 --- a/packages/x-date-pickers/src/tests/describeValue/testPickerOpenCloseLifeCycle.tsx +++ b/packages/x-date-pickers/src/tests/describeValue/testPickerOpenCloseLifeCycle.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; import { screen, userEvent } from '@mui/monorepo/test/utils'; -import { getTextbox, openPicker } from 'test/utils/pickers-utils'; +import { getExpectedOnChangeCount, getTextbox, openPicker } from 'test/utils/pickers-utils'; import { DescribeValueTestSuite } from './describeValue.types'; export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite = ( @@ -66,7 +66,7 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite // Change the value let newValue = setNewValue(values[0], { isOpened: true }); - expect(onChange.callCount).to.equal(1); + expect(onChange.callCount).to.equal(getExpectedOnChangeCount(componentFamily, pickerParams)); if (pickerParams.type === 'date-range') { newValue = setNewValue(newValue, { isOpened: true, setEndDate: true }); newValue.forEach((value, index) => { @@ -118,7 +118,7 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite // Change the value let newValue = setNewValue(values[0], { isOpened: true }); - expect(onChange.callCount).to.equal(1); + expect(onChange.callCount).to.equal(getExpectedOnChangeCount(componentFamily, pickerParams)); if (pickerParams.type === 'date-range') { newValue = setNewValue(newValue, { isOpened: true, setEndDate: true }); newValue.forEach((value, index) => { @@ -176,7 +176,8 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite // Change the value let newValue = setNewValue(values[0], { isOpened: true }); - expect(onChange.callCount).to.equal(1); + const initialChangeCount = getExpectedOnChangeCount(componentFamily, pickerParams); + expect(onChange.callCount).to.equal(initialChangeCount); if (pickerParams.type === 'date-range') { newValue = setNewValue(newValue, { isOpened: true, setEndDate: true }); newValue.forEach((value, index) => { @@ -197,7 +198,12 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite expect(onChange.lastCall.args[0][index]).toEqualDateTime(value); }); } else { - expect(onChange.callCount).to.equal(2); + expect(onChange.callCount).to.equal( + initialChangeCount + + getExpectedOnChangeCount(componentFamily, pickerParams) - + // meridiem does not change this time in case of multi section digital clock + 1, + ); expect(onChange.lastCall.args[0]).toEqualDateTime(newValueBis as any); } expect(onAccept.callCount).to.equal(0); @@ -225,7 +231,7 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite // Dismiss the picker userEvent.keyPress(document.activeElement!, { key: 'Escape' }); - expect(onChange.callCount).to.equal(1); + expect(onChange.callCount).to.equal(getExpectedOnChangeCount(componentFamily, pickerParams)); expect(onAccept.callCount).to.equal(1); if (pickerParams.type === 'date-range') { newValue.forEach((value, index) => { @@ -291,7 +297,7 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite // Dismiss the picker userEvent.mousePress(document.body); - expect(onChange.callCount).to.equal(1); + expect(onChange.callCount).to.equal(getExpectedOnChangeCount(componentFamily, pickerParams)); expect(onAccept.callCount).to.equal(1); expect(onAccept.lastCall.args[0]).toEqualDateTime(newValue as any); expect(onClose.callCount).to.equal(1); diff --git a/test/utils/pickers-utils.tsx b/test/utils/pickers-utils.tsx index 22f29ba0fca0..73ff44bf5fc8 100644 --- a/test/utils/pickers-utils.tsx +++ b/test/utils/pickers-utils.tsx @@ -569,7 +569,7 @@ export class MockedDataTransfer implements DataTransfer { } } -export const getExpectedOnChangeCount = (componentFamily: PickerComponentFamily) => { +const getChangeCountForComponentFamily = (componentFamily: PickerComponentFamily) => { switch (componentFamily) { case 'clock': return 2; @@ -579,3 +579,18 @@ export const getExpectedOnChangeCount = (componentFamily: PickerComponentFamily) return 1; } }; + +export const getExpectedOnChangeCount = ( + componentFamily: PickerComponentFamily, + params: OpenPickerParams, +) => { + if (params.type !== 'date-time') { + return getChangeCountForComponentFamily(componentFamily); + } + return ( + getChangeCountForComponentFamily(componentFamily) + + getChangeCountForComponentFamily( + params.variant === 'desktop' ? 'multi-section-digital-clock' : 'clock', + ) + ); +}; From 36ab70d1c7c93adb1edd55e4e0d113a3062504b7 Mon Sep 17 00:00:00 2001 From: Lukas Date: Tue, 16 May 2023 18:08:01 +0300 Subject: [PATCH 16/27] Fix time related tests and use more `views` in them --- .../describes.DesktopTimePicker.test.tsx | 29 +++++++++++--- .../tests/describes.DigitalClock.test.tsx | 5 ++- .../describes.MobileDateTimePicker.test.tsx | 39 +++++++++++++++---- .../tests/describes.MobileTimePicker.test.tsx | 33 ++++++++++++---- ...escribes.MultiSectionDigitalClock.test.tsx | 15 +++---- .../tests/describes.TimeClock.test.tsx | 31 +++++++++++---- .../testControlledUnControlled.tsx | 17 +++++++- .../testPickerOpenCloseLifeCycle.tsx | 2 +- test/utils/pickers-utils.tsx | 26 +++++++++---- 9 files changed, 149 insertions(+), 48 deletions(-) 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 9421131ada3f..413b949540e4 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 @@ -57,12 +57,9 @@ describe(' - Describes', () => { componentFamily: 'picker', type: 'time', variant: 'desktop', - defaultProps: { - views: ['hours'], - }, values: [ - adapterToUse.date(new Date(2018, 0, 1, 15, 30)), - adapterToUse.date(new Date(2018, 0, 1, 18, 30)), + adapterToUse.date(new Date(2018, 0, 1, 11, 30)), + adapterToUse.date(new Date(2018, 0, 1, 12, 35)), ], emptyValue: null, clock, @@ -80,13 +77,18 @@ describe(' - Describes', () => { ); }, setNewValue: (value, { isOpened, applySameValue } = {}) => { - const newValue = applySameValue ? value : adapterToUse.addHours(value, 1); + const newValue = applySameValue + ? value + : adapterToUse.addMinutes(adapterToUse.addHours(value, 1), 5); if (isOpened) { const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale(); const hours = adapterToUse.format(newValue, hasMeridiem ? 'hours12h' : 'hours24h'); const hoursNumber = adapterToUse.getHours(newValue); userEvent.mousePress(screen.getByRole('option', { name: `${parseInt(hours, 10)} hours` })); + userEvent.mousePress( + screen.getByRole('option', { name: `${adapterToUse.getMinutes(newValue)} minutes` }), + ); if (hasMeridiem) { // meridiem is an extra view on `DesktopTimePicker` // we need to click it to finish selection @@ -98,6 +100,21 @@ describe(' - Describes', () => { const input = getTextbox(); clickOnInput(input, 1); // Update the hour userEvent.keyPress(input, { key: 'ArrowUp' }); + // move to the minutes section + userEvent.keyPress(input, { key: 'ArrowRight' }); + // increment by 5 minutes + userEvent.keyPress(input, { key: 'PageUp' }); + const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale(); + if (hasMeridiem) { + // move to the meridiem section + userEvent.keyPress(input, { key: 'ArrowRight' }); + const previousHours = adapterToUse.getHours(value); + const newHours = adapterToUse.getHours(newValue); + // update meridiem section if it changed + if ((previousHours < 12 && newHours >= 12) || (previousHours >= 12 && newHours < 12)) { + userEvent.keyPress(input, { key: 'ArrowUp' }); + } + } } return newValue; 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 b3c358217efb..ecc047d8aea4 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 @@ -62,14 +62,15 @@ describe(' - Describes', () => { } }, setNewValue: (value) => { + const newValue = adapterToUse.addMinutes(adapterToUse.addHours(value, 1), 30); const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale(); const formattedLabel = adapterToUse.format( - value, + newValue, hasMeridiem ? 'fullTime12h' : 'fullTime24h', ); userEvent.mousePress(screen.getByRole('option', { name: formattedLabel })); - return value; + return newValue; }, })); }); 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 ba4590ae84c7..e58924b0210f 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 @@ -34,11 +34,11 @@ describe(' - Describes', () => { componentFamily: 'picker', type: 'date-time', variant: 'mobile', - defaultProps: { - openTo: 'minutes', - }, clock, - values: [adapterToUse.date(new Date(2018, 0, 1)), adapterToUse.date(new Date(2018, 0, 2))], + values: [ + adapterToUse.date(new Date(2018, 0, 1, 11, 30)), + adapterToUse.date(new Date(2018, 0, 2, 12, 35)), + ], emptyValue: null, assertRenderedValue: (expectedValue: any) => { const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale(); @@ -57,18 +57,41 @@ describe(' - Describes', () => { }, setNewValue: (value, { isOpened, applySameValue } = {}) => { if (!isOpened) { - openPicker({ type: 'time', variant: 'mobile' }); + openPicker({ type: 'date-time', variant: 'mobile' }); } - const newValue = applySameValue ? value : adapterToUse.addMinutes(value, 1); - const hourClockEvent = getClockTouchEvent(adapterToUse.getMinutes(newValue), 'minutes'); + const newValue = applySameValue + ? value + : adapterToUse.addMinutes(adapterToUse.addHours(adapterToUse.addDays(value, 1), 1), 5); + userEvent.mousePress( + screen.getByRole('gridcell', { name: adapterToUse.getDate(newValue).toString() }), + ); + const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale(); + // change hours + const hourClockEvent = getClockTouchEvent( + adapterToUse.getHours(newValue), + hasMeridiem ? '12hours' : '24hours', + ); fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchmove', hourClockEvent); fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchend', hourClockEvent); + // change minutes + const minutesClockEvent = getClockTouchEvent(adapterToUse.getMinutes(newValue), 'minutes'); + fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchmove', minutesClockEvent); + fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchend', minutesClockEvent); + + if (hasMeridiem) { + const newHours = adapterToUse.getHours(newValue); + // select appropriate meridiem + userEvent.mousePress(screen.getByRole('button', { name: newHours >= 12 ? 'PM' : 'AM' })); + } - // Close the picker to return to the initial state + // Close the picker if (!isOpened) { userEvent.keyPress(document.activeElement!, { key: 'Escape' }); clock.runToLast(); + } else { + // return to the date view in case we'd like to repeat the selection process + userEvent.mousePress(screen.getByRole('tab', { name: 'pick date' })); } return newValue; 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 2fa25b2e1114..dc0b55dd58a5 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 @@ -61,13 +61,10 @@ describe(' - Describes', () => { type: 'time', variant: 'mobile', values: [ - adapterToUse.date(new Date(2018, 0, 1, 15, 30)), - adapterToUse.date(new Date(2018, 0, 1, 18, 30)), + adapterToUse.date(new Date(2018, 0, 1, 11, 30)), + adapterToUse.date(new Date(2018, 0, 1, 12, 35)), ], emptyValue: null, - defaultProps: { - openTo: 'minutes', - }, clock, assertRenderedValue: (expectedValue: any) => { const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale(); @@ -86,15 +83,35 @@ describe(' - Describes', () => { openPicker({ type: 'time', variant: 'mobile' }); } - const newValue = applySameValue ? value : adapterToUse.addMinutes(value, 1); - const hourClockEvent = getClockTouchEvent(adapterToUse.getMinutes(newValue), 'minutes'); + const newValue = applySameValue + ? value + : adapterToUse.addMinutes(adapterToUse.addHours(value, 1), 5); + const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale(); + // change hours + const hourClockEvent = getClockTouchEvent( + adapterToUse.getHours(newValue), + hasMeridiem ? '12hours' : '24hours', + ); fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchmove', hourClockEvent); fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchend', hourClockEvent); + // change minutes + const minutesClockEvent = getClockTouchEvent(adapterToUse.getMinutes(newValue), 'minutes'); + fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchmove', minutesClockEvent); + fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchend', minutesClockEvent); + + if (hasMeridiem) { + const newHours = adapterToUse.getHours(newValue); + // select appropriate meridiem + userEvent.mousePress(screen.getByRole('button', { name: newHours >= 12 ? 'PM' : 'AM' })); + } - // Close the picker to return to the initial state + // Close the picker if (!isOpened) { userEvent.keyPress(document.activeElement!, { key: 'Escape' }); clock.runToLast(); + } else { + // return to the hours view in case we'd like to repeat the selection process + userEvent.mousePress(screen.getByRole('button', { name: 'open previous view' })); } return newValue; 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 02cb357fb8cb..205470e0d858 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 @@ -42,8 +42,8 @@ describe(' - Describes', () => { type: 'time', variant: 'desktop', values: [ - adapterToUse.date(new Date(2018, 0, 1, 15, 30)), - adapterToUse.date(new Date(2018, 0, 1, 16, 15)), + adapterToUse.date(new Date(2018, 0, 1, 11, 30)), + adapterToUse.date(new Date(2018, 0, 1, 12, 35)), ], emptyValue: null, clock, @@ -62,29 +62,30 @@ describe(' - Describes', () => { expect(selectedItems[1]).to.have.text(minutesLabel); if (hasMeridiem) { expect(selectedItems[2]).to.have.text( - adapterToUse.getMeridiemText(adapterToUse.getHours(expectedValue) > 12 ? 'pm' : 'am'), + adapterToUse.getMeridiemText(adapterToUse.getHours(expectedValue) >= 12 ? 'pm' : 'am'), ); } } }, setNewValue: (value) => { + const newValue = adapterToUse.addMinutes(adapterToUse.addHours(value, 1), 5); const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale(); const hoursLabel = parseInt( - adapterToUse.format(value, hasMeridiem ? 'hours12h' : 'hours24h'), + adapterToUse.format(newValue, hasMeridiem ? 'hours12h' : 'hours24h'), 10, ); - const minutesLabel = adapterToUse.getMinutes(value).toString(); + const minutesLabel = adapterToUse.getMinutes(newValue).toString(); userEvent.mousePress(screen.getByRole('option', { name: `${hoursLabel} hours` })); userEvent.mousePress(screen.getByRole('option', { name: `${minutesLabel} minutes` })); if (hasMeridiem) { userEvent.mousePress( screen.getByRole('option', { - name: adapterToUse.getMeridiemText(adapterToUse.getHours(value) > 12 ? 'pm' : 'am'), + name: adapterToUse.getMeridiemText(adapterToUse.getHours(newValue) >= 12 ? 'pm' : 'am'), }), ); } - return value; + return newValue; }, })); }); diff --git a/packages/x-date-pickers/src/TimeClock/tests/describes.TimeClock.test.tsx b/packages/x-date-pickers/src/TimeClock/tests/describes.TimeClock.test.tsx index 9938e53d561a..12fb9ce5cf07 100644 --- a/packages/x-date-pickers/src/TimeClock/tests/describes.TimeClock.test.tsx +++ b/packages/x-date-pickers/src/TimeClock/tests/describes.TimeClock.test.tsx @@ -31,13 +31,10 @@ describe(' - Describes', () => { render, componentFamily: 'clock', values: [ - adapterToUse.date(new Date(2018, 0, 1, 15, 30)), - adapterToUse.date(new Date(2018, 0, 1, 18, 30)), + adapterToUse.date(new Date(2018, 0, 1, 12, 30)), + adapterToUse.date(new Date(2018, 0, 1, 13, 35)), ], emptyValue: null, - defaultProps: { - openTo: 'minutes', - }, clock, assertRenderedValue: (expectedValue: any) => { const clockPointer = document.querySelector(`.${clockPointerClasses.root}`); @@ -45,14 +42,32 @@ describe(' - Describes', () => { expect(clockPointer).to.equal(null); } else { const transform = clockPointer?.style?.transform; - expect(transform).to.equal(`rotateZ(${adapterToUse.getMinutes(expectedValue) * 6}deg)`); + const isMinutesView = screen + .getByRole('listbox') + .getAttribute('aria-label') + ?.includes('minutes'); + if (isMinutesView) { + expect(transform).to.equal(`rotateZ(${adapterToUse.getMinutes(expectedValue) * 6}deg)`); + } else { + const hours = adapterToUse.getHours(expectedValue); + expect(transform).to.equal(`rotateZ(${(hours > 12 ? hours % 12 : hours) * 30}deg)`); + } } }, setNewValue: (value) => { - const newValue = adapterToUse.addMinutes(value, 1); - const hourClockEvent = getClockTouchEvent(adapterToUse.getMinutes(newValue), 'minutes'); + const newValue = adapterToUse.addMinutes(adapterToUse.addHours(value, 1), 5); + const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale(); + // change hours + const hourClockEvent = getClockTouchEvent( + adapterToUse.getHours(newValue), + hasMeridiem ? '12hours' : '24hours', + ); fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchmove', hourClockEvent); fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchend', hourClockEvent); + // change minutes + const minutesClockEvent = getClockTouchEvent(adapterToUse.getMinutes(newValue), 'minutes'); + fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchmove', minutesClockEvent); + fireTouchChangedEvent(screen.getByMuiTest('clock'), 'touchend', minutesClockEvent); return newValue; }, diff --git a/packages/x-date-pickers/src/tests/describeValue/testControlledUnControlled.tsx b/packages/x-date-pickers/src/tests/describeValue/testControlledUnControlled.tsx index 474c06fcb4f3..d3d2cdb8b0b2 100644 --- a/packages/x-date-pickers/src/tests/describeValue/testControlledUnControlled.tsx +++ b/packages/x-date-pickers/src/tests/describeValue/testControlledUnControlled.tsx @@ -65,7 +65,22 @@ export const testControlledUnControlled: DescribeValueTestSuite = ( it('should call onChange when updating a value defined with `props.value`', () => { const onChange = spy(); - render(); + function ControlledElementToTest(props) { + const [value, setValue] = React.useState(props?.value || null); + + return ( + { + setValue(newValue); + props?.onChange(newValue); + }} + /> + ); + } + + render(); const newValue = setNewValue(values[0]); expect(onChange.callCount).to.equal(getExpectedOnChangeCount(componentFamily, params)); diff --git a/packages/x-date-pickers/src/tests/describeValue/testPickerOpenCloseLifeCycle.tsx b/packages/x-date-pickers/src/tests/describeValue/testPickerOpenCloseLifeCycle.tsx index eed3af685beb..0e0670166c1a 100644 --- a/packages/x-date-pickers/src/tests/describeValue/testPickerOpenCloseLifeCycle.tsx +++ b/packages/x-date-pickers/src/tests/describeValue/testPickerOpenCloseLifeCycle.tsx @@ -202,7 +202,7 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite initialChangeCount + getExpectedOnChangeCount(componentFamily, pickerParams) - // meridiem does not change this time in case of multi section digital clock - 1, + (pickerParams.type === 'time' || pickerParams.type === 'date-time' ? 1 : 0), ); expect(onChange.lastCall.args[0]).toEqualDateTime(newValueBis as any); } diff --git a/test/utils/pickers-utils.tsx b/test/utils/pickers-utils.tsx index 73ff44bf5fc8..e85b6f1c5b70 100644 --- a/test/utils/pickers-utils.tsx +++ b/test/utils/pickers-utils.tsx @@ -572,7 +572,6 @@ export class MockedDataTransfer implements DataTransfer { const getChangeCountForComponentFamily = (componentFamily: PickerComponentFamily) => { switch (componentFamily) { case 'clock': - return 2; case 'multi-section-digital-clock': return 3; default: @@ -584,13 +583,26 @@ export const getExpectedOnChangeCount = ( componentFamily: PickerComponentFamily, params: OpenPickerParams, ) => { - if (params.type !== 'date-time') { + if (componentFamily === 'digital-clock') { return getChangeCountForComponentFamily(componentFamily); } - return ( - getChangeCountForComponentFamily(componentFamily) + - getChangeCountForComponentFamily( + if (params.type === 'date-time') { + return ( + getChangeCountForComponentFamily(componentFamily) + + getChangeCountForComponentFamily( + params.variant === 'desktop' ? 'multi-section-digital-clock' : 'clock', + ) + ); + } + if (componentFamily === 'picker' && params.type === 'time') { + return getChangeCountForComponentFamily( params.variant === 'desktop' ? 'multi-section-digital-clock' : 'clock', - ) - ); + ); + } + if (componentFamily === 'clock') { + // the `TimeClock` fires change for both touch move and touch end + // but does not have meridiem control + return (getChangeCountForComponentFamily(componentFamily) - 1) * 2; + } + return getChangeCountForComponentFamily(componentFamily); }; From 73f378f677bce4c2c31b3d86596dce4f4a1d5d99 Mon Sep 17 00:00:00 2001 From: Lukas Date: Wed, 17 May 2023 08:58:33 +0300 Subject: [PATCH 17/27] proptypes --- .../x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx index 012d27bf2e48..e962b89e85a0 100644 --- a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx @@ -342,6 +342,7 @@ function DateTimePickerToolbar(inProps: DateTimePickerToo /> )} + {ampm && isDesktop && ( Date: Wed, 17 May 2023 09:08:55 +0300 Subject: [PATCH 18/27] Remove no longer relevant condition --- .../src/tests/describeValidation/testDayViewValidation.tsx | 6 +----- .../tests/describeValidation/testMinutesViewValidation.tsx | 6 +----- .../tests/describeValidation/testMonthViewValidation.tsx | 6 +----- .../src/tests/describeValidation/testYearViewValidation.tsx | 6 +----- 4 files changed, 4 insertions(+), 20 deletions(-) diff --git a/packages/x-date-pickers/src/tests/describeValidation/testDayViewValidation.tsx b/packages/x-date-pickers/src/tests/describeValidation/testDayViewValidation.tsx index f6d45a3e17d0..1268da250106 100644 --- a/packages/x-date-pickers/src/tests/describeValidation/testDayViewValidation.tsx +++ b/packages/x-date-pickers/src/tests/describeValidation/testDayViewValidation.tsx @@ -17,11 +17,7 @@ export const testDayViewValidation: DescribeValidationTestSuite = (ElementToTest open: true, view: 'day', reduceAnimations: true, - ...(componentFamily.includes('legacy-') - ? { - componentsProps: { toolbar: { hidden: true } }, - } - : { slotProps: { toolbar: { hidden: true } } }), + slotProps: { toolbar: { hidden: true } }, }; it('should apply shouldDisableDate', function test() { diff --git a/packages/x-date-pickers/src/tests/describeValidation/testMinutesViewValidation.tsx b/packages/x-date-pickers/src/tests/describeValidation/testMinutesViewValidation.tsx index 7450de4d9614..5d81ffdcac67 100644 --- a/packages/x-date-pickers/src/tests/describeValidation/testMinutesViewValidation.tsx +++ b/packages/x-date-pickers/src/tests/describeValidation/testMinutesViewValidation.tsx @@ -29,11 +29,7 @@ export const testMinutesViewValidation: DescribeValidationTestSuite = ( view: 'minutes', openTo: 'minutes', reduceAnimations: true, - ...(componentFamily.includes('legacy-') - ? { - componentsProps: { toolbar: { hidden: true } }, - } - : { slotProps: { toolbar: { hidden: true } } }), + slotProps: { toolbar: { hidden: true } }, }; it('should apply shouldDisableTime', function test() { diff --git a/packages/x-date-pickers/src/tests/describeValidation/testMonthViewValidation.tsx b/packages/x-date-pickers/src/tests/describeValidation/testMonthViewValidation.tsx index 49d55aec623b..1dee3a03455a 100644 --- a/packages/x-date-pickers/src/tests/describeValidation/testMonthViewValidation.tsx +++ b/packages/x-date-pickers/src/tests/describeValidation/testMonthViewValidation.tsx @@ -22,11 +22,7 @@ export const testMonthViewValidation: DescribeValidationTestSuite = (ElementToTe ...(componentFamily !== 'calendar' && { open: true, reduceAnimations: true, - ...(componentFamily.includes('legacy-') - ? { - componentsProps: { toolbar: { hidden: true } }, - } - : { slotProps: { toolbar: { hidden: true } } }), + slotProps: { toolbar: { hidden: true } }, }), }; diff --git a/packages/x-date-pickers/src/tests/describeValidation/testYearViewValidation.tsx b/packages/x-date-pickers/src/tests/describeValidation/testYearViewValidation.tsx index 45bedd32abb4..43c5b19dd86d 100644 --- a/packages/x-date-pickers/src/tests/describeValidation/testYearViewValidation.tsx +++ b/packages/x-date-pickers/src/tests/describeValidation/testYearViewValidation.tsx @@ -22,11 +22,7 @@ export const testYearViewValidation: DescribeValidationTestSuite = (ElementToTes ...(componentFamily !== 'calendar' && { open: true, reduceAnimations: true, - ...(componentFamily.includes('legacy-') - ? { - componentsProps: { toolbar: { hidden: true } }, - } - : { slotProps: { toolbar: { hidden: true } } }), + slotProps: { toolbar: { hidden: true } }, }), }; From 0a5b99e9eef4f9960fe0dded330a1997157ab377 Mon Sep 17 00:00:00 2001 From: Lukas Date: Wed, 17 May 2023 10:27:34 +0300 Subject: [PATCH 19/27] Update docs --- .../date-pickers/date-time-picker/date-time-picker.md | 11 +++-------- .../migration-pickers-v5/migration-pickers-v5.md | 4 ++-- 2 files changed, 5 insertions(+), 10 deletions(-) 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 e68eed836e4c..354e74149778 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 @@ -11,25 +11,20 @@ materialDesign: https://m2.material.io/components/date-pickers

The Date Time Picker component lets the user select a date and time.

-:::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/) ## Uncontrolled vs. Controlled diff --git a/docs/data/migration/migration-pickers-v5/migration-pickers-v5.md b/docs/data/migration/migration-pickers-v5/migration-pickers-v5.md index 50a33d4af7b9..f47b39308f10 100644 --- a/docs/data/migration/migration-pickers-v5/migration-pickers-v5.md +++ b/docs/data/migration/migration-pickers-v5/migration-pickers-v5.md @@ -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. From 21e5131645b4ed7b74175cb61d2f3c4e889982f6 Mon Sep 17 00:00:00 2001 From: Lukas Date: Wed, 17 May 2023 10:45:54 +0300 Subject: [PATCH 20/27] docs:api --- .../date-pickers/date-time-picker-tabs.json | 2 +- .../date-time-picker-toolbar.json | 2 +- .../x/api/date-pickers/date-time-picker.json | 19 ++++++++++++++++--- .../desktop-date-time-picker.json | 19 ++++++++++++++++--- .../date-pickers/date-time-picker.json | 4 ++++ .../desktop-date-time-picker.json | 4 ++++ 6 files changed, 42 insertions(+), 8 deletions(-) diff --git a/docs/pages/x/api/date-pickers/date-time-picker-tabs.json b/docs/pages/x/api/date-pickers/date-time-picker-tabs.json index 7dd217ab815b..dd0d6cf36bc9 100644 --- a/docs/pages/x/api/date-pickers/date-time-picker-tabs.json +++ b/docs/pages/x/api/date-pickers/date-time-picker-tabs.json @@ -4,7 +4,7 @@ "view": { "type": { "name": "enum", - "description": "'day'
| 'hours'
| 'minutes'
| 'month'
| 'seconds'
| 'year'" + "description": "'day'
| 'hours'
| 'meridiem'
| 'minutes'
| 'month'
| 'seconds'
| 'year'" }, "required": true }, diff --git a/docs/pages/x/api/date-pickers/date-time-picker-toolbar.json b/docs/pages/x/api/date-pickers/date-time-picker-toolbar.json index 7cab1cc72b15..7828faf86303 100644 --- a/docs/pages/x/api/date-pickers/date-time-picker-toolbar.json +++ b/docs/pages/x/api/date-pickers/date-time-picker-toolbar.json @@ -4,7 +4,7 @@ "view": { "type": { "name": "enum", - "description": "'day'
| 'hours'
| 'minutes'
| 'month'
| 'seconds'
| 'year'" + "description": "'day'
| 'hours'
| 'meridiem'
| 'minutes'
| 'month'
| 'seconds'
| 'year'" }, "required": true }, diff --git a/docs/pages/x/api/date-pickers/date-time-picker.json b/docs/pages/x/api/date-pickers/date-time-picker.json index 01413cac9dc6..99b3d0fe72b3 100644 --- a/docs/pages/x/api/date-pickers/date-time-picker.json +++ b/docs/pages/x/api/date-pickers/date-time-picker.json @@ -73,7 +73,7 @@ "openTo": { "type": { "name": "enum", - "description": "'day'
| 'hours'
| 'minutes'
| 'month'
| 'seconds'
| 'year'" + "description": "'day'
| 'hours'
| 'meridiem'
| 'minutes'
| 'month'
| 'seconds'
| 'year'" } }, "orientation": { @@ -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": { @@ -111,17 +112,25 @@ "description": "Array<func
| object
| bool>
| func
| object" } }, + "thresholdToRenderTimeInASingleColumn": { "type": { "name": "number" }, "default": "24" }, + "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'
| 'hours'
| 'minutes'
| 'month'
| 'seconds'
| 'year'" + "description": "'day'
| 'hours'
| 'meridiem'
| 'minutes'
| 'month'
| 'seconds'
| '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": { @@ -148,6 +157,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" } }, diff --git a/docs/pages/x/api/date-pickers/desktop-date-time-picker.json b/docs/pages/x/api/date-pickers/desktop-date-time-picker.json index 7e3b606d75a8..d974ac48e45e 100644 --- a/docs/pages/x/api/date-pickers/desktop-date-time-picker.json +++ b/docs/pages/x/api/date-pickers/desktop-date-time-picker.json @@ -69,7 +69,7 @@ "openTo": { "type": { "name": "enum", - "description": "'day'
| 'hours'
| 'minutes'
| 'month'
| 'seconds'
| 'year'" + "description": "'day'
| 'hours'
| 'meridiem'
| 'minutes'
| 'month'
| 'seconds'
| 'year'" } }, "orientation": { @@ -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": { @@ -107,17 +108,25 @@ "description": "Array<func
| object
| bool>
| func
| object" } }, + "thresholdToRenderTimeInASingleColumn": { "type": { "name": "number" }, "default": "24" }, + "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'
| 'hours'
| 'minutes'
| 'month'
| 'seconds'
| 'year'" + "description": "'day'
| 'hours'
| 'meridiem'
| 'minutes'
| 'month'
| 'seconds'
| '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": { @@ -143,6 +152,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" } }, diff --git a/docs/translations/api-docs/date-pickers/date-time-picker.json b/docs/translations/api-docs/date-pickers/date-time-picker.json index 98b82dc82ad5..d50115756822 100644 --- a/docs/translations/api-docs/date-pickers/date-time-picker.json +++ b/docs/translations/api-docs/date-pickers/date-time-picker.json @@ -55,9 +55,12 @@ "shouldDisableTime": "Disable specific time.

Signature:
function(value: TDate, view: TimeView) => boolean
value: The value to check.
view: The clock type of the timeValue.
returns (boolean): If true the time will be disabled.", "shouldDisableYear": "Disable specific year.

Signature:
function(year: TDate) => boolean
year: The year to test.
returns (boolean): If true, the year will be disabled.", "showDaysOutsideCurrentMonth": "If true, days outside the current month are rendered:
- if fixedWeekNumber is defined, renders days to have the weeks requested.
- if fixedWeekNumber is not defined, renders day to fill the first and last week of the current month.
- ignored if calendars equals more than 1 on range pickers.", + "skipDisabled": "If true, 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 `sx` page for more details.", + "thresholdToRenderTimeInASingleColumn": "Amount of time options below or at which the single column time renderer is used.", + "timeSteps": "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.", "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 views list.", "viewRenderers": "Define custom view renderers for each section. If null, the section will only have field editing. If undefined, internally defined view will be the used.", @@ -72,6 +75,7 @@ "DesktopTransition": "Custom component for the desktop popper Transition.", "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.", diff --git a/docs/translations/api-docs/date-pickers/desktop-date-time-picker.json b/docs/translations/api-docs/date-pickers/desktop-date-time-picker.json index 5bb756c4707f..47a39abe7cb6 100644 --- a/docs/translations/api-docs/date-pickers/desktop-date-time-picker.json +++ b/docs/translations/api-docs/date-pickers/desktop-date-time-picker.json @@ -54,9 +54,12 @@ "shouldDisableTime": "Disable specific time.

Signature:
function(value: TDate, view: TimeView) => boolean
value: The value to check.
view: The clock type of the timeValue.
returns (boolean): If true the time will be disabled.", "shouldDisableYear": "Disable specific year.

Signature:
function(year: TDate) => boolean
year: The year to test.
returns (boolean): If true, the year will be disabled.", "showDaysOutsideCurrentMonth": "If true, days outside the current month are rendered:
- if fixedWeekNumber is defined, renders days to have the weeks requested.
- if fixedWeekNumber is not defined, renders day to fill the first and last week of the current month.
- ignored if calendars equals more than 1 on range pickers.", + "skipDisabled": "If true, 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 `sx` page for more details.", + "thresholdToRenderTimeInASingleColumn": "Amount of time options below or at which the single column time renderer is used.", + "timeSteps": "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.", "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 views list.", "viewRenderers": "Define custom view renderers for each section. If null, the section will only have field editing. If undefined, internally defined view will be the used.", @@ -70,6 +73,7 @@ "DesktopPaper": "Custom component for the paper rendered inside the desktop picker's Popper.", "DesktopTransition": "Custom component for the desktop popper Transition.", "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.", From b308f7d4be87fa7f31040912d296af22bdfc1c91 Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 19 May 2023 16:29:39 +0300 Subject: [PATCH 21/27] Use new renderer only if no custom renderers are provided --- .../DateTimePicker/DateTimePickerToolbar.tsx | 20 +++---- .../DesktopDateTimePicker.tsx | 55 +++++++++++-------- .../DesktopDateTimePicker.types.ts | 2 +- .../useDesktopPicker/useDesktopPicker.tsx | 1 - 4 files changed, 42 insertions(+), 36 deletions(-) diff --git a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx index e962b89e85a0..26bd814cbf35 100644 --- a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx @@ -29,7 +29,7 @@ export interface DateTimePickerToolbarProps * Override or extend the styles applied to the component. */ classes?: Partial; - wrapperVariant?: WrapperVariant; + toolbarVariant?: WrapperVariant; } const useUtilityClasses = (ownerState: DateTimePickerToolbarProps & { theme: Theme }) => { @@ -53,13 +53,13 @@ const DateTimePickerToolbarRoot = styled(PickersToolbar, { overridesResolver: (props, styles) => styles.root, })<{ ownerState: DateTimePickerToolbarProps }>(({ theme, ownerState }) => ({ paddingLeft: 16, - paddingRight: ownerState.wrapperVariant === 'desktop' && !ownerState.isLandscape ? 0 : 16, + paddingRight: ownerState.toolbarVariant === 'desktop' && !ownerState.isLandscape ? 0 : 16, borderBottom: - ownerState.wrapperVariant === 'desktop' + ownerState.toolbarVariant === 'desktop' ? `1px solid ${(theme.vars || theme).palette.divider}` : undefined, borderRight: - ownerState.wrapperVariant === 'desktop' && ownerState.isLandscape + ownerState.toolbarVariant === 'desktop' && ownerState.isLandscape ? `1px solid ${(theme.vars || theme).palette.divider}` : undefined, justifyContent: 'space-around', @@ -109,11 +109,11 @@ const DateTimePickerToolbarTimeContainer = styled('div', { overridesResolver: (props, styles) => styles.timeContainer, })<{ ownerState: DateTimePickerToolbarProps }>(({ theme, ownerState }) => { const direction = - ownerState.isLandscape && ownerState.wrapperVariant !== 'desktop' ? 'column' : 'row'; + ownerState.isLandscape && ownerState.toolbarVariant !== 'desktop' ? 'column' : 'row'; return { display: 'flex', flexDirection: direction, - ...(ownerState.wrapperVariant === 'desktop' && { + ...(ownerState.toolbarVariant === 'desktop' && { ...(!ownerState.isLandscape && { gap: 9, marginRight: 4, @@ -132,7 +132,7 @@ const DateTimePickerToolbarTimeDigitsContainer = styled('div', { overridesResolver: (props, styles) => styles.timeDigitsContainer, })<{ ownerState: DateTimePickerToolbarProps }>(({ theme, ownerState }) => ({ display: 'flex', - ...(ownerState.wrapperVariant === 'desktop' && { gap: 1.5 }), + ...(ownerState.toolbarVariant === 'desktop' && { gap: 1.5 }), ...(theme.direction === 'rtl' && { flexDirection: 'row-reverse', }), @@ -159,7 +159,7 @@ const DateTimePickerToolbarSeparator = styled(PickersToolbarText, { })<{ ownerState: DateTimePickerToolbarProps; }>(({ ownerState }) => ({ - margin: ownerState.wrapperVariant === 'desktop' ? 0 : '0 4px 0 2px', + margin: ownerState.toolbarVariant === 'desktop' ? 0 : '0 4px 0 2px', cursor: 'default', })); @@ -205,7 +205,7 @@ function DateTimePickerToolbar(inProps: DateTimePickerToo views, disabled, readOnly, - wrapperVariant = 'mobile', + toolbarVariant = 'mobile', ...other } = props; const ownerState = props; @@ -213,7 +213,7 @@ function DateTimePickerToolbar(inProps: DateTimePickerToo const { meridiemMode, handleMeridiemChange } = useMeridiemMode(value, ampm, onChange); const showAmPmControl = Boolean(ampm && !ampmInClock); - const isDesktop = wrapperVariant === 'desktop'; + const isDesktop = toolbarVariant === 'desktop'; const localeText = useLocaleText(); const theme = useTheme(); diff --git a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx index 3c45e2aee343..87271d8f17f6 100644 --- a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx +++ b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx @@ -5,6 +5,7 @@ import { singleItemValueManager } from '../internals/utils/valueManagers'; import { DateTimeField } from '../DateTimeField'; import { DesktopDateTimePickerProps } from './DesktopDateTimePicker.types'; import { useDateTimePickerDefaultizedProps } from '../DateTimePicker/shared'; +import { renderDateViewCalendar } from '../dateViewRenderers/dateViewRenderers'; import { renderDesktopDateTimeView } from '../dateTimeViewRenderers'; import { useLocaleText, validateDateTime } from '../internals'; import { DateOrTimeViewWithMeridiem } from '../internals/models'; @@ -13,7 +14,6 @@ import { useDesktopPicker } from '../internals/hooks/useDesktopPicker'; import { extractValidationProps } from '../internals/utils/validation/extractValidationProps'; import { PickerViewRendererLookup } from '../internals/hooks/usePicker/usePickerViews'; import { PickersActionBarAction } from '../PickersActionBar'; -import { isDatePickerView } from '../internals/utils/date-utils'; type DesktopDateTimePickerComponent = (( props: DesktopDateTimePickerProps & React.RefAttributes, @@ -32,42 +32,48 @@ const DesktopDateTimePicker = React.forwardRef(function DesktopDateTimePicker >(inProps, 'MuiDesktopDateTimePicker'); - const thresholdToRenderTimeInASingleColumn = - defaultizedProps.thresholdToRenderTimeInASingleColumn ?? 24; const timeSteps = { hours: 1, minutes: 5, seconds: 5, ...defaultizedProps.timeSteps }; - const shouldRenderTimeInASingleColumn = - (24 * 60) / (timeSteps.hours * timeSteps.minutes) <= thresholdToRenderTimeInASingleColumn; + const shouldUseNewRenderer = + !defaultizedProps.viewRenderers || Object.keys(defaultizedProps.viewRenderers).length === 0; const viewRenderers: PickerViewRendererLookup = - { - day: renderDesktopDateTimeView, - month: renderDesktopDateTimeView, - year: renderDesktopDateTimeView, - hours: renderDesktopDateTimeView, - minutes: renderDesktopDateTimeView, - seconds: renderDesktopDateTimeView, - meridiem: renderDesktopDateTimeView, - ...defaultizedProps.viewRenderers, - }; + // we can only ensure the expected two-column layout if none of the renderers are overridden + shouldUseNewRenderer + ? { + day: renderDesktopDateTimeView, + month: renderDesktopDateTimeView, + year: renderDesktopDateTimeView, + hours: renderDesktopDateTimeView, + minutes: renderDesktopDateTimeView, + seconds: renderDesktopDateTimeView, + meridiem: renderDesktopDateTimeView, + } + : { + day: renderDateViewCalendar, + month: renderDateViewCalendar, + year: renderDateViewCalendar, + hours: null, + minutes: null, + seconds: null, + meridiem: null, + ...defaultizedProps.viewRenderers, + }; const ampmInClock = defaultizedProps.ampmInClock ?? true; - const actionBarActions: PickersActionBarAction[] = shouldRenderTimeInASingleColumn + // add "accept" action only when the new date time view renderers are used + const actionBarActions: PickersActionBarAction[] = defaultizedProps.viewRenderers ? [] : ['accept']; - const views: readonly DateOrTimeViewWithMeridiem[] = defaultizedProps.ampm - ? [...defaultizedProps.views, 'meridiem'] - : defaultizedProps.views; // Props with the default values specific to the desktop variant const props = { ...defaultizedProps, viewRenderers, - // Setting only `hours` time view in case of single column time picker - // Allows for easy view lifecycle management - views: shouldRenderTimeInASingleColumn - ? ([...views.filter(isDatePickerView), 'hours'] as DateOrTimeViewWithMeridiem[]) - : views, + views: (defaultizedProps.ampm + ? [...defaultizedProps.views, 'meridiem'] + : defaultizedProps.views) as DateOrTimeViewWithMeridiem[], yearsPerRow: defaultizedProps.yearsPerRow ?? 4, ampmInClock, + timeSteps, slots: { field: DateTimeField, openPickerIcon: Calendar, @@ -83,6 +89,7 @@ const DesktopDateTimePicker = React.forwardRef(function DesktopDateTimePicker export interface DesktopDateTimePickerProps extends BaseDateTimePickerProps, DesktopOnlyPickerProps, - DesktopOnlyTimePickerProps { + Omit, 'thresholdToRenderTimeInASingleColumn'> { /** * Available views. */ 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 3dcf0e47c446..970cc59a9080 100644 --- a/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx +++ b/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx @@ -153,7 +153,6 @@ export const useDesktopPicker = < toolbar: { ...innerSlotProps?.toolbar, titleId: labelId, - wrapperVariant: 'desktop', }, popper: { 'aria-labelledby': labelledById, From e532ca3d279e786dd1f70af4e04bc6f8cc8dcbda Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 19 May 2023 16:31:22 +0300 Subject: [PATCH 22/27] proptypes --- .../x-date-pickers/src/DateTimePicker/DateTimePicker.tsx | 5 ----- .../src/DateTimePicker/DateTimePickerToolbar.tsx | 2 +- .../src/DesktopDateTimePicker/DesktopDateTimePicker.tsx | 5 ----- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.tsx b/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.tsx index 02e4eabd3f01..2ec62fb77d98 100644 --- a/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.tsx @@ -387,11 +387,6 @@ DateTimePicker.propTypes = { PropTypes.func, PropTypes.object, ]), - /** - * Amount of time options below or at which the single column time renderer is used. - * @default 24 - */ - thresholdToRenderTimeInASingleColumn: PropTypes.number, /** * 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]`. diff --git a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx index 26bd814cbf35..4a8320bf1954 100644 --- a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx @@ -398,6 +398,7 @@ DateTimePickerToolbar.propTypes = { * @default "––" */ toolbarPlaceholder: PropTypes.node, + toolbarVariant: PropTypes.oneOf(['desktop', 'mobile']), value: PropTypes.any, /** * Currently visible picker view. @@ -407,7 +408,6 @@ DateTimePickerToolbar.propTypes = { views: PropTypes.arrayOf( PropTypes.oneOf(['day', 'hours', 'meridiem', 'minutes', 'month', 'seconds', 'year']).isRequired, ).isRequired, - wrapperVariant: PropTypes.oneOf(['desktop', 'mobile']), } as any; export { DateTimePickerToolbar }; diff --git a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx index 87271d8f17f6..b9d7d16b1dc2 100644 --- a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx +++ b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx @@ -466,11 +466,6 @@ DesktopDateTimePicker.propTypes = { PropTypes.func, PropTypes.object, ]), - /** - * Amount of time options below or at which the single column time renderer is used. - * @default 24 - */ - thresholdToRenderTimeInASingleColumn: PropTypes.number, /** * 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]`. From 24e70ab00b2048c46ef1e01143f50266c932b67e Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 19 May 2023 16:31:53 +0300 Subject: [PATCH 23/27] docs:api --- docs/pages/x/api/date-pickers/date-time-picker.json | 1 - docs/pages/x/api/date-pickers/desktop-date-time-picker.json | 1 - docs/translations/api-docs/date-pickers/date-time-picker.json | 1 - .../api-docs/date-pickers/desktop-date-time-picker.json | 1 - 4 files changed, 4 deletions(-) diff --git a/docs/pages/x/api/date-pickers/date-time-picker.json b/docs/pages/x/api/date-pickers/date-time-picker.json index 99b3d0fe72b3..03f5ac43418b 100644 --- a/docs/pages/x/api/date-pickers/date-time-picker.json +++ b/docs/pages/x/api/date-pickers/date-time-picker.json @@ -112,7 +112,6 @@ "description": "Array<func
| object
| bool>
| func
| object" } }, - "thresholdToRenderTimeInASingleColumn": { "type": { "name": "number" }, "default": "24" }, "timeSteps": { "type": { "name": "shape", diff --git a/docs/pages/x/api/date-pickers/desktop-date-time-picker.json b/docs/pages/x/api/date-pickers/desktop-date-time-picker.json index d974ac48e45e..47d90379bde2 100644 --- a/docs/pages/x/api/date-pickers/desktop-date-time-picker.json +++ b/docs/pages/x/api/date-pickers/desktop-date-time-picker.json @@ -108,7 +108,6 @@ "description": "Array<func
| object
| bool>
| func
| object" } }, - "thresholdToRenderTimeInASingleColumn": { "type": { "name": "number" }, "default": "24" }, "timeSteps": { "type": { "name": "shape", diff --git a/docs/translations/api-docs/date-pickers/date-time-picker.json b/docs/translations/api-docs/date-pickers/date-time-picker.json index d50115756822..cc71e0dbb581 100644 --- a/docs/translations/api-docs/date-pickers/date-time-picker.json +++ b/docs/translations/api-docs/date-pickers/date-time-picker.json @@ -59,7 +59,6 @@ "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 `sx` page for more details.", - "thresholdToRenderTimeInASingleColumn": "Amount of time options below or at which the single column time renderer is used.", "timeSteps": "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.", "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 views list.", diff --git a/docs/translations/api-docs/date-pickers/desktop-date-time-picker.json b/docs/translations/api-docs/date-pickers/desktop-date-time-picker.json index 47a39abe7cb6..cd92daddf64d 100644 --- a/docs/translations/api-docs/date-pickers/desktop-date-time-picker.json +++ b/docs/translations/api-docs/date-pickers/desktop-date-time-picker.json @@ -58,7 +58,6 @@ "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 `sx` page for more details.", - "thresholdToRenderTimeInASingleColumn": "Amount of time options below or at which the single column time renderer is used.", "timeSteps": "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.", "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 views list.", From 632d14da5f9e188da56c1e5b16bfa40a27b0ae51 Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 22 May 2023 09:58:58 +0300 Subject: [PATCH 24/27] Code review: Flavien --- .../src/DesktopDateTimePicker/DesktopDateTimePicker.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx index b9d7d16b1dc2..e067f964db91 100644 --- a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx +++ b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx @@ -60,9 +60,7 @@ const DesktopDateTimePicker = React.forwardRef(function DesktopDateTimePicker Date: Thu, 25 May 2023 12:03:39 +0300 Subject: [PATCH 25/27] Adjust "desktop" toolbar left padding --- .../x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx index 4a8320bf1954..83bfd879d007 100644 --- a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx @@ -52,7 +52,7 @@ const DateTimePickerToolbarRoot = styled(PickersToolbar, { slot: 'Root', overridesResolver: (props, styles) => styles.root, })<{ ownerState: DateTimePickerToolbarProps }>(({ theme, ownerState }) => ({ - paddingLeft: 16, + paddingLeft: ownerState.toolbarVariant === 'desktop' && !ownerState.isLandscape ? 24 : 16, paddingRight: ownerState.toolbarVariant === 'desktop' && !ownerState.isLandscape ? 0 : 16, borderBottom: ownerState.toolbarVariant === 'desktop' From b33ca8961a2dcb8d4f7a4dd4c7ef8892659de040 Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 26 May 2023 15:06:18 +0300 Subject: [PATCH 26/27] Calculate the scroll offset container height based on the parent height --- .../MultiSectionDigitalClockSection.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx index c2aed7d904ed..7d52f4466f9a 100644 --- a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx +++ b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx @@ -69,7 +69,8 @@ const MultiSectionDigitalClockSectionRoot = styled(MenuList, { '&:after': { display: 'block', content: '""', - height: 188, + // subtracting the height of one item, extra margin and borders to make sure the max height is correct + height: 'calc(100% - 40px - 6px)', }, }), ); From c7894f24e1600362ec30eea0c58246c4e783b632 Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 26 May 2023 15:19:18 +0300 Subject: [PATCH 27/27] Fix proptypes --- packages/x-charts/src/BarChart/BarChart.tsx | 9 +++++++++ packages/x-charts/src/LineChart/LineChart.tsx | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/packages/x-charts/src/BarChart/BarChart.tsx b/packages/x-charts/src/BarChart/BarChart.tsx index a9c2a528a2df..d0e4ae2c3123 100644 --- a/packages/x-charts/src/BarChart/BarChart.tsx +++ b/packages/x-charts/src/BarChart/BarChart.tsx @@ -157,6 +157,15 @@ BarChart.propTypes = { id: PropTypes.string, label: PropTypes.string, stack: PropTypes.string, + stackOffset: PropTypes.oneOf(['diverging', 'expand', 'none', 'silhouette', 'wiggle']), + stackOrder: PropTypes.oneOf([ + 'appearance', + 'ascending', + 'descending', + 'insideOut', + 'none', + 'reverse', + ]), type: PropTypes.oneOf(['bar']), xAxisKey: PropTypes.string, yAxisKey: PropTypes.string, diff --git a/packages/x-charts/src/LineChart/LineChart.tsx b/packages/x-charts/src/LineChart/LineChart.tsx index 5317ef0681b7..d67e83b01d95 100644 --- a/packages/x-charts/src/LineChart/LineChart.tsx +++ b/packages/x-charts/src/LineChart/LineChart.tsx @@ -167,6 +167,15 @@ LineChart.propTypes = { id: PropTypes.string, label: PropTypes.string, stack: PropTypes.string, + stackOffset: PropTypes.oneOf(['diverging', 'expand', 'none', 'silhouette', 'wiggle']), + stackOrder: PropTypes.oneOf([ + 'appearance', + 'ascending', + 'descending', + 'insideOut', + 'none', + 'reverse', + ]), type: PropTypes.oneOf(['line']), xAxisKey: PropTypes.string, yAxisKey: PropTypes.string,