Skip to content

Commit

Permalink
[pickers] Allow custom fields to validate the value (mui#14486)
Browse files Browse the repository at this point in the history
  • Loading branch information
flaviendelangle authored and Arthur Balduini committed Sep 30, 2024
1 parent 3967d21 commit 5666dd6
Show file tree
Hide file tree
Showing 64 changed files with 584 additions and 371 deletions.
47 changes: 47 additions & 0 deletions docs/data/date-pickers/experimentation/CustomField.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import * as React from 'react';
import dayjs from 'dayjs';
import TextField from '@mui/material/TextField';
import { DemoContainer } from '@mui/x-date-pickers/internals/demo';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { useValidation, validateDate } from '@mui/x-date-pickers/validation';
import { useSplitFieldProps } from '@mui/x-date-pickers/hooks';

function ReadOnlyField(props) {
const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');

const { value, timezone, format } = internalProps;
const { InputProps, slotProps, slots, ...other } = forwardedProps;

const { hasValidationError } = useValidation({
validator: validateDate,
value,
timezone,
props: internalProps,
});

return (
<TextField
{...other}
value={value == null ? '' : value.format(format)}
InputProps={{ ...InputProps, readOnly: true }}
error={hasValidationError}
/>
);
}

export default function CustomField() {
return (
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DemoContainer components={['DatePicker']}>
<DatePicker
label="Date Picker"
slots={{ field: ReadOnlyField }}
maxDate={dayjs('2022-04-17')}
defaultValue={dayjs('2022-04-18')}
/>
</DemoContainer>
</LocalizationProvider>
);
}
48 changes: 48 additions & 0 deletions docs/data/date-pickers/experimentation/CustomField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import * as React from 'react';
import dayjs, { Dayjs } from 'dayjs';
import TextField from '@mui/material/TextField';
import { DemoContainer } from '@mui/x-date-pickers/internals/demo';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { useValidation, validateDate } from '@mui/x-date-pickers/validation';
import { useSplitFieldProps } from '@mui/x-date-pickers/hooks';
import { DateFieldInPickerProps } from '@mui/x-date-pickers/DateField';

function ReadOnlyField(props: DateFieldInPickerProps<Dayjs, false>) {
const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');

const { value, timezone, format } = internalProps;
const { InputProps, slotProps, slots, ...other } = forwardedProps;

const { hasValidationError } = useValidation({
validator: validateDate,
value,
timezone,
props: internalProps,
});

return (
<TextField
{...other}
value={value == null ? '' : value.format(format)}
InputProps={{ ...InputProps, readOnly: true }}
error={hasValidationError}
/>
);
}

export default function CustomField() {
return (
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DemoContainer components={['DatePicker']}>
<DatePicker
label="Date Picker"
slots={{ field: ReadOnlyField }}
maxDate={dayjs('2022-04-17')}
defaultValue={dayjs('2022-04-18')}
/>
</DemoContainer>
</LocalizationProvider>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<DatePicker
label="Date Picker"
slots={{ field: ReadOnlyField }}
maxDate={dayjs('2022-04-17')}
defaultValue={dayjs('2022-04-18')}
/>
11 changes: 11 additions & 0 deletions docs/data/date-pickers/experimentation/experimentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
productId: x-date-pickers
---

# Date and Time Pickers experimentation

<p class="description">Demos not accessible through the navbar of the doc</p>

## Custom field

{{"demo": "CustomField.js"}}
7 changes: 7 additions & 0 deletions docs/pages/x/react-date-pickers/experimentation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import * as React from 'react';
import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs';
import * as pageProps from 'docsx/data/date-pickers/experimentation/experimentation.md?muiMarkdown';

export default function Page() {
return <MarkdownDocs {...pageProps} />;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { extractValidationProps, PickerViewRendererLookup } from '@mui/x-date-pickers/internals';
import { PickerViewRendererLookup } from '@mui/x-date-pickers/internals';
import { extractValidationProps } from '@mui/x-date-pickers/validation';
import { PickerValidDate } from '@mui/x-date-pickers/models';
import resolveComponentProps from '@mui/utils/resolveComponentProps';
import { refType } from '@mui/utils';
Expand All @@ -10,7 +11,7 @@ import { useDateRangePickerDefaultizedProps } from '../DateRangePicker/shared';
import { renderDateRangeViewCalendar } from '../dateRangeViewRenderers';
import { MultiInputDateRangeField } from '../MultiInputDateRangeField';
import { useDesktopRangePicker } from '../internals/hooks/useDesktopRangePicker';
import { validateDateRange } from '../internals/utils/validation/validateDateRange';
import { validateDateRange } from '../validation';
import { DateRange } from '../models';

type DesktopDateRangePickerComponent = (<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import * as React from 'react';
import PropTypes from 'prop-types';
import {
DefaultizedProps,
extractValidationProps,
isDatePickerView,
isInternalTimeView,
PickerViewRenderer,
PickerViewsRendererProps,
resolveDateTimeFormat,
useUtils,
} from '@mui/x-date-pickers/internals';
import { extractValidationProps } from '@mui/x-date-pickers/validation';
import { PickerValidDate } from '@mui/x-date-pickers/models';
import resolveComponentProps from '@mui/utils/resolveComponentProps';
import { refType } from '@mui/utils';
Expand All @@ -32,7 +32,7 @@ import {
useDesktopRangePicker,
UseDesktopRangePickerProps,
} from '../internals/hooks/useDesktopRangePicker';
import { validateDateTimeRange } from '../internals/utils/validation/validateDateTimeRange';
import { validateDateTimeRange } from '../validation';
import { DateTimeRangePickerView } from '../internals/models';
import { DateRange } from '../models';
import {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { extractValidationProps, PickerViewRendererLookup } from '@mui/x-date-pickers/internals';
import { PickerViewRendererLookup } from '@mui/x-date-pickers/internals';
import { extractValidationProps } from '@mui/x-date-pickers/validation';
import { PickerValidDate } from '@mui/x-date-pickers/models';
import resolveComponentProps from '@mui/utils/resolveComponentProps';
import { refType } from '@mui/utils';
Expand All @@ -10,7 +11,7 @@ import { useDateRangePickerDefaultizedProps } from '../DateRangePicker/shared';
import { renderDateRangeViewCalendar } from '../dateRangeViewRenderers';
import { MultiInputDateRangeField } from '../MultiInputDateRangeField';
import { useMobileRangePicker } from '../internals/hooks/useMobileRangePicker';
import { validateDateRange } from '../internals/utils/validation/validateDateRange';
import { validateDateRange } from '../validation';
import { DateRange } from '../models';

type MobileDateRangePickerComponent = (<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { refType } from '@mui/utils';
import {
DIALOG_WIDTH,
VIEW_HEIGHT,
extractValidationProps,
isInternalTimeView,
isDatePickerView,
PickerViewRenderer,
Expand All @@ -14,6 +13,7 @@ import {
resolveDateTimeFormat,
useUtils,
} from '@mui/x-date-pickers/internals';
import { extractValidationProps } from '@mui/x-date-pickers/validation';
import { PickerValidDate } from '@mui/x-date-pickers/models';
import resolveComponentProps from '@mui/utils/resolveComponentProps';
import {
Expand All @@ -32,7 +32,7 @@ import {
UseMobileRangePickerProps,
useMobileRangePicker,
} from '../internals/hooks/useMobileRangePicker';
import { validateDateTimeRange } from '../internals/utils/validation/validateDateTimeRange';
import { validateDateTimeRange } from '../validation';
import { DateTimeRangePickerView } from '../internals/models';
import { DateRange } from '../models';
import {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useSplitFieldProps } from '@mui/x-date-pickers/hooks';
import { PickerValidDate } from '@mui/x-date-pickers/models';
import { UseSingleInputDateRangeFieldProps } from './SingleInputDateRangeField.types';
import { rangeValueManager, getRangeFieldValueManager } from '../internals/utils/valueManagers';
import { validateDateRange } from '../internals/utils/validation/validateDateRange';
import { validateDateRange } from '../validation';
import { RangeFieldSection, DateRange } from '../models';

export const useSingleInputDateRangeField = <
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useSplitFieldProps } from '@mui/x-date-pickers/hooks';
import { PickerValidDate } from '@mui/x-date-pickers/models';
import { UseSingleInputDateTimeRangeFieldProps } from './SingleInputDateTimeRangeField.types';
import { rangeValueManager, getRangeFieldValueManager } from '../internals/utils/valueManagers';
import { validateDateTimeRange } from '../internals/utils/validation/validateDateTimeRange';
import { validateDateTimeRange } from '../validation';
import { RangeFieldSection, DateRange } from '../models';

export const useSingleInputDateTimeRangeField = <
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useSplitFieldProps } from '@mui/x-date-pickers/hooks';
import { PickerValidDate } from '@mui/x-date-pickers/models';
import { UseSingleInputTimeRangeFieldProps } from './SingleInputTimeRangeField.types';
import { rangeValueManager, getRangeFieldValueManager } from '../internals/utils/valueManagers';
import { validateTimeRange } from '../internals/utils/validation/validateTimeRange';
import { validateTimeRange } from '../validation';
import { RangeFieldSection, DateRange } from '../models';

export const useSingleInputTimeRangeField = <
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { StaticDateRangePickerProps } from './StaticDateRangePicker.types';
import { useDateRangePickerDefaultizedProps } from '../DateRangePicker/shared';
import { renderDateRangeViewCalendar } from '../dateRangeViewRenderers';
import { rangeValueManager } from '../internals/utils/valueManagers';
import { validateDateRange } from '../internals/utils/validation/validateDateRange';
import { validateDateRange } from '../validation';
import { DateRange } from '../models';

type StaticDateRangePickerComponent = (<TDate extends PickerValidDate>(
Expand Down
1 change: 1 addition & 0 deletions packages/x-date-pickers-pro/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ export * from './MobileDateTimeRangePicker';
export * from './dateRangeViewRenderers';

export * from './models';
export * from './validation';
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ import {
getActiveElement,
usePicker,
PickersPopper,
InferError,
ExportedBaseToolbarProps,
DateOrTimeViewWithMeridiem,
ExportedBaseTabsProps,
} from '@mui/x-date-pickers/internals';
import { PickerValidDate, FieldRef, BaseSingleInputFieldProps } from '@mui/x-date-pickers/models';
import {
PickerValidDate,
FieldRef,
BaseSingleInputFieldProps,
InferError,
} from '@mui/x-date-pickers/models';
import {
DesktopRangePickerAdditionalViewProps,
UseDesktopRangePickerParams,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import { PickersLayout, PickersLayoutSlotProps } from '@mui/x-date-pickers/Picke
import {
usePicker,
PickersModalDialog,
InferError,
ExportedBaseToolbarProps,
DateOrTimeViewWithMeridiem,
ExportedBaseTabsProps,
} from '@mui/x-date-pickers/internals';
import { usePickersTranslations } from '@mui/x-date-pickers/hooks';
import { PickerValidDate, FieldRef, BaseSingleInputFieldProps } from '@mui/x-date-pickers/models';
import {
PickerValidDate,
FieldRef,
BaseSingleInputFieldProps,
InferError,
} from '@mui/x-date-pickers/models';
import useId from '@mui/utils/useId';
import {
MobileRangePickerAdditionalViewProps,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,19 @@ import {
UseDateFieldComponentProps,
} from '@mui/x-date-pickers/DateField';
import {
useLocalizationContext,
useValidation,
FieldChangeHandler,
FieldChangeHandlerContext,
UseFieldResponse,
useControlledValueWithTimezone,
useDefaultizedDateField,
} from '@mui/x-date-pickers/internals';
import { useValidation } from '@mui/x-date-pickers/validation';
import { DateValidationError, PickerValidDate } from '@mui/x-date-pickers/models';
import {
UseMultiInputDateRangeFieldParams,
UseMultiInputDateRangeFieldProps,
} from '../../../MultiInputDateRangeField/MultiInputDateRangeField.types';
import {
DateRangeComponentValidationProps,
validateDateRange,
} from '../../utils/validation/validateDateRange';
import { validateDateRange } from '../../../validation';
import { rangeValueManager } from '../../utils/valueManagers';
import type { UseMultiInputRangeFieldResponse } from './useMultiInputRangeField.types';
import { DateRangeValidationError, DateRange } from '../../../models';
Expand Down Expand Up @@ -48,8 +44,6 @@ export const useMultiInputDateRangeField = <
typeof inSharedProps
>(inSharedProps);

const adapter = useLocalizationContext<TDate>();

const {
value: valueProp,
defaultValue,
Expand All @@ -75,6 +69,14 @@ export const useMultiInputDateRangeField = <
valueManager: rangeValueManager,
});

const { validationError, getValidationErrorForNewValue } = useValidation({
props: sharedProps,
value,
timezone,
validator: validateDateRange,
onError: sharedProps.onError,
});

// TODO: Maybe export utility from `useField` instead of copy/pasting the logic
const buildChangeHandler = (
index: 0 | 1,
Expand All @@ -85,11 +87,7 @@ export const useMultiInputDateRangeField = <

const context: FieldChangeHandlerContext<DateRangeValidationError> = {
...rawContext,
validationError: validateDateRange({
adapter,
value: newDateRange,
props: { ...sharedProps, timezone },
}),
validationError: getValidationErrorForNewValue(newDateRange),
};

handleValueChange(newDateRange, context);
Expand All @@ -99,18 +97,6 @@ export const useMultiInputDateRangeField = <
const handleStartDateChange = useEventCallback(buildChangeHandler(0));
const handleEndDateChange = useEventCallback(buildChangeHandler(1));

const validationError = useValidation<
DateRange<TDate>,
TDate,
DateRangeValidationError,
DateRangeComponentValidationProps<TDate>
>(
{ ...sharedProps, value, timezone },
validateDateRange,
rangeValueManager.isSameError,
rangeValueManager.defaultErrorState,
);

const selectedSectionsResponse = useMultiInputFieldSelectedSections({
selectedSections,
onSelectedSectionsChange,
Expand Down
Loading

0 comments on commit 5666dd6

Please sign in to comment.