Skip to content

Commit

Permalink
[core] Fix default props behavior on all pickers (#4451)
Browse files Browse the repository at this point in the history
  • Loading branch information
flaviendelangle committed Apr 12, 2022
1 parent dc424ed commit b2b9420
Show file tree
Hide file tree
Showing 18 changed files with 295 additions and 579 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ module.exports = {
'useDatePickerDefaultizedProps',
'useTimePickerDefaultizedProps',
'useDateTimePickerDefaultizedProps',
'useDateRangePickerDefaultizedProps',
],
},
],
Expand Down
182 changes: 39 additions & 143 deletions packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,90 +2,24 @@ import PropTypes from 'prop-types';
import * as React from 'react';
import { useThemeProps } from '@mui/material/styles';
import { useLicenseVerifier } from '@mui/x-license-pro';
import {
ResponsiveTooltipWrapper,
ResponsiveWrapperProps,
useDefaultDates,
useUtils,
ValidationProps,
usePickerState,
PickerStateValueManager,
DateInputPropsLike,
} from '@mui/x-date-pickers/internals';
import { DateRangePickerView, ExportedDateRangePickerViewProps } from './DateRangePickerView';
import { DateRangePickerInput, ExportedDateRangePickerInputProps } from './DateRangePickerInput';
import { RangeInput, DateRange } from '../internal/models/dateRange';
import {
useDateRangeValidation,
DateRangeValidationError,
} from '../internal/hooks/validation/useDateRangeValidation';
import { parseRangeInputValue } from '../internal/utils/date-utils';
import useMediaQuery from '@mui/material/useMediaQuery';
import { getReleaseInfo } from '../internal/utils/releaseInfo';
import { DesktopDateRangePicker, DesktopDateRangePickerProps } from '../DesktopDateRangePicker';
import { MobileDateRangePicker, MobileDateRangePickerProps } from '../MobileDateRangePicker';

const releaseInfo = getReleaseInfo();

interface BaseDateRangePickerProps<TDate>
extends ExportedDateRangePickerViewProps<TDate>,
ValidationProps<DateRangeValidationError, RangeInput<TDate>>,
ExportedDateRangePickerInputProps {
/**
* The components used for each slot.
* Either a string to use an HTML element or a component.
* @default {}
*/
components?: ExportedDateRangePickerViewProps<TDate>['components'] &
ExportedDateRangePickerInputProps['components'];
/**
* Text for end input label and toolbar placeholder.
* @default 'End'
*/
endText?: React.ReactNode;
/**
* Custom mask. Can be used to override generate from format. (e.g. `__/__/____ __:__` or `__/__/____ __:__ _M`).
* @default '__/__/____'
*/
mask?: ExportedDateRangePickerInputProps['mask'];
/**
* Min selectable date. @DateIOType
* @default defaultMinDate
*/
minDate?: TDate;
/**
* Max selectable date. @DateIOType
* @default defaultMaxDate
*/
maxDate?: TDate;
/**
* Callback fired when the value (the selected date range) changes @DateIOType.
* @template TDate
* @param {DateRange<TDate>} date The new parsed date range.
* @param {string} keyboardInputValue The current value of the keyboard input.
*/
onChange: (date: DateRange<TDate>, keyboardInputValue?: string) => void;
/**
* Text for start input label and toolbar placeholder.
* @default 'Start'
*/
startText?: React.ReactNode;
export interface DateRangePickerProps<TDate>
extends DesktopDateRangePickerProps<TDate>,
MobileDateRangePickerProps<TDate> {
/**
* The value of the date range picker.
* CSS media query when `Mobile` mode will be changed to `Desktop`.
* @default '@media (pointer: fine)'
* @example '@media (min-width: 720px)' or theme.breakpoints.up("sm")
*/
value: RangeInput<TDate>;
desktopModeMediaQuery?: string;
}

const KeyboardDateInputComponent = DateRangePickerInput as unknown as React.FC<DateInputPropsLike>;
const PureDateInputComponent = DateRangePickerInput as unknown as React.FC<DateInputPropsLike>;

const rangePickerValueManager: PickerStateValueManager<any, any> = {
emptyValue: [null, null],
parseInput: parseRangeInputValue,
areValuesEqual: (utils, a, b) => utils.isEqual(a[0], b[0]) && utils.isEqual(a[1], b[1]),
};

export interface DateRangePickerProps<TDate>
extends BaseDateRangePickerProps<TDate>,
ResponsiveWrapperProps {}

type DateRangePickerComponent = (<TDate>(
props: DateRangePickerProps<TDate> & React.RefAttributes<HTMLDivElement>,
) => JSX.Element) & { propTypes?: any };
Expand All @@ -108,78 +42,40 @@ export const DateRangePicker = React.forwardRef(function DateRangePicker<TDate>(
useLicenseVerifier('x-date-pickers-pro', releaseInfo);

const {
calendars = 2,
value,
onChange,
mask = '__/__/____',
startText = 'Start',
endText = 'End',
inputFormat: passedInputFormat,
minDate: minDateProp,
maxDate: maxDateProp,
cancelText,
desktopModeMediaQuery = '@media (pointer: fine)',
DialogProps,
okText,
PopperProps,
showTodayButton,
todayText,
TransitionComponent,
...other
} = props;

const utils = useUtils<TDate>();
const defaultDates = useDefaultDates<TDate>();
const minDate = minDateProp ?? defaultDates.minDate;
const maxDate = maxDateProp ?? defaultDates.maxDate;
const [currentlySelectingRangeEnd, setCurrentlySelectingRangeEnd] = React.useState<
'start' | 'end'
>('start');

const pickerStateProps = {
...other,
value,
onChange,
};
const isDesktop = useMediaQuery(desktopModeMediaQuery);

const restProps = {
...other,
minDate,
maxDate,
};

const { pickerProps, inputProps, wrapperProps } = usePickerState<
RangeInput<TDate>,
DateRange<TDate>
>(pickerStateProps, rangePickerValueManager);

const validationError = useDateRangeValidation(props);

const DateInputProps = {
...inputProps,
...restProps,
currentlySelectingRangeEnd,
inputFormat: passedInputFormat || utils.formats.keyboardDate,
setCurrentlySelectingRangeEnd,
startText,
endText,
mask,
validationError,
ref,
};
if (isDesktop) {
return (
<DesktopDateRangePicker
ref={ref}
PopperProps={PopperProps}
TransitionComponent={TransitionComponent}
{...other}
/>
);
}

return (
<ResponsiveTooltipWrapper
{...restProps}
{...wrapperProps}
DateInputProps={DateInputProps}
KeyboardDateInputComponent={KeyboardDateInputComponent}
PureDateInputComponent={PureDateInputComponent}
>
<DateRangePickerView<any>
open={wrapperProps.open}
DateInputProps={DateInputProps}
calendars={calendars}
currentlySelectingRangeEnd={currentlySelectingRangeEnd}
setCurrentlySelectingRangeEnd={setCurrentlySelectingRangeEnd}
startText={startText}
endText={endText}
{...pickerProps}
{...restProps}
/>
</ResponsiveTooltipWrapper>
<MobileDateRangePicker
ref={ref}
cancelText={cancelText}
DialogProps={DialogProps}
okText={okText}
showTodayButton={showTodayButton}
todayText={todayText}
{...other}
/>
);
}) as DateRangePickerComponent;

Expand Down Expand Up @@ -242,7 +138,7 @@ DateRangePicker.propTypes = {
/**
* CSS media query when `Mobile` mode will be changed to `Desktop`.
* @default '@media (pointer: fine)'
* @example '@media (min-width: 720px)' or theme.breakpoints.up('sm')
* @example '@media (min-width: 720px)' or theme.breakpoints.up("sm")
*/
desktopModeMediaQuery: PropTypes.string,
/**
Expand Down
90 changes: 90 additions & 0 deletions packages/x-date-pickers-pro/src/DateRangePicker/shared.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import * as React from 'react';
import { useDefaultDates, useUtils, ValidationProps } from '@mui/x-date-pickers/internals';
import { useThemeProps } from '@mui/material/styles';
import { ExportedDateRangePickerViewProps } from './DateRangePickerView';
import { DateRangeValidationError } from '../internal/hooks/validation/useDateRangeValidation';
import { DateRange, RangeInput } from '../internal/models';
import { ExportedDateRangePickerInputProps } from './DateRangePickerInput';

export interface BaseDateRangePickerProps<TDate>
extends ExportedDateRangePickerViewProps<TDate>,
ValidationProps<DateRangeValidationError, RangeInput<TDate>>,
ExportedDateRangePickerInputProps {
/**
* The components used for each slot.
* Either a string to use an HTML element or a component.
* @default {}
*/
components?: ExportedDateRangePickerViewProps<TDate>['components'] &
ExportedDateRangePickerInputProps['components'];
/**
* Text for end input label and toolbar placeholder.
* @default 'End'
*/
endText?: React.ReactNode;
/**
* Custom mask. Can be used to override generate from format. (e.g. `__/__/____ __:__` or `__/__/____ __:__ _M`).
* @default '__/__/____'
*/
mask?: ExportedDateRangePickerInputProps['mask'];
/**
* Min selectable date. @DateIOType
* @default defaultMinDate
*/
minDate?: TDate;
/**
* Max selectable date. @DateIOType
* @default defaultMaxDate
*/
maxDate?: TDate;
/**
* Callback fired when the value (the selected date range) changes @DateIOType.
* @template TDate
* @param {DateRange<TDate>} date The new parsed date range.
* @param {string} keyboardInputValue The current value of the keyboard input.
*/
onChange: (date: DateRange<TDate>, keyboardInputValue?: string) => void;
/**
* Text for start input label and toolbar placeholder.
* @default 'Start'
*/
startText?: React.ReactNode;
/**
* The value of the date range picker.
*/
value: RangeInput<TDate>;
}

export type DefaultizedProps<Props> = Props & { inputFormat: string };

export function useDateRangePickerDefaultizedProps<
TDate,
Props extends BaseDateRangePickerProps<TDate>,
>(
props: Props,
name: string,
): DefaultizedProps<Props> &
Required<
Pick<BaseDateRangePickerProps<unknown>, 'calendars' | 'mask' | 'startText' | 'endText'>
> {
const utils = useUtils<TDate>();
const defaultDates = useDefaultDates();

// This is technically unsound if the type parameters appear in optional props.
// Optional props can be filled by `useThemeProps` with types that don't match the type parameters.
const themeProps = useThemeProps({
props,
name,
});

return {
calendars: 2,
mask: '__/__/____',
startText: 'Start',
endText: 'End',
inputFormat: utils.formats.keyboardDate,
minDate: defaultDates.minDate,
maxDate: defaultDates.maxDate,
...themeProps,
};
}
Loading

0 comments on commit b2b9420

Please sign in to comment.