Skip to content

Commit b1a8dd4

Browse files
[docs] New recipe of a read-only field (#14606)
Signed-off-by: Flavien DELANGLE <[email protected]> Co-authored-by: Lukas Tyla <[email protected]>
1 parent c2cff49 commit b1a8dd4

File tree

26 files changed

+331
-146
lines changed

26 files changed

+331
-146
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import * as React from 'react';
2+
3+
import TextField from '@mui/material/TextField';
4+
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
5+
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
6+
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
7+
import { useValidation, validateDate } from '@mui/x-date-pickers/validation';
8+
import {
9+
useSplitFieldProps,
10+
useParsedFormat,
11+
usePickersContext,
12+
} from '@mui/x-date-pickers/hooks';
13+
import { CalendarIcon } from '@mui/x-date-pickers/icons';
14+
15+
function ReadOnlyDateField(props) {
16+
const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');
17+
const { value, timezone, format } = internalProps;
18+
const { InputProps, slotProps, slots, ...other } = forwardedProps;
19+
20+
const pickersContext = usePickersContext();
21+
22+
const parsedFormat = useParsedFormat(internalProps);
23+
const { hasValidationError } = useValidation({
24+
validator: validateDate,
25+
value,
26+
timezone,
27+
props: internalProps,
28+
});
29+
30+
const handleTogglePicker = (event) => {
31+
if (pickersContext.open) {
32+
pickersContext.onClose(event);
33+
} else {
34+
pickersContext.onOpen(event);
35+
}
36+
};
37+
38+
return (
39+
<TextField
40+
{...other}
41+
value={value == null ? '' : value.format(format)}
42+
placeholder={parsedFormat}
43+
InputProps={{
44+
...InputProps,
45+
readOnly: true,
46+
endAdornment: <CalendarIcon color="action" />,
47+
sx: { cursor: 'pointer', '& *': { cursor: 'inherit' } },
48+
}}
49+
error={hasValidationError}
50+
onClick={handleTogglePicker}
51+
/>
52+
);
53+
}
54+
55+
function ReadOnlyFieldDatePicker(props) {
56+
return (
57+
<DatePicker {...props} slots={{ ...props.slots, field: ReadOnlyDateField }} />
58+
);
59+
}
60+
61+
export default function ReadOnlyMaterialTextField() {
62+
return (
63+
<LocalizationProvider dateAdapter={AdapterDayjs}>
64+
<ReadOnlyFieldDatePicker />
65+
</LocalizationProvider>
66+
);
67+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import * as React from 'react';
2+
import { Dayjs } from 'dayjs';
3+
import TextField from '@mui/material/TextField';
4+
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
5+
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
6+
import {
7+
DatePicker,
8+
DatePickerProps,
9+
DatePickerFieldProps,
10+
} from '@mui/x-date-pickers/DatePicker';
11+
import { useValidation, validateDate } from '@mui/x-date-pickers/validation';
12+
import {
13+
useSplitFieldProps,
14+
useParsedFormat,
15+
usePickersContext,
16+
} from '@mui/x-date-pickers/hooks';
17+
import { CalendarIcon } from '@mui/x-date-pickers/icons';
18+
19+
function ReadOnlyDateField(props: DatePickerFieldProps<Dayjs, false>) {
20+
const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');
21+
const { value, timezone, format } = internalProps;
22+
const { InputProps, slotProps, slots, ...other } = forwardedProps;
23+
24+
const pickersContext = usePickersContext();
25+
26+
const parsedFormat = useParsedFormat(internalProps);
27+
const { hasValidationError } = useValidation({
28+
validator: validateDate,
29+
value,
30+
timezone,
31+
props: internalProps,
32+
});
33+
34+
const handleTogglePicker = (event: React.UIEvent) => {
35+
if (pickersContext.open) {
36+
pickersContext.onClose(event);
37+
} else {
38+
pickersContext.onOpen(event);
39+
}
40+
};
41+
42+
return (
43+
<TextField
44+
{...other}
45+
value={value == null ? '' : value.format(format)}
46+
placeholder={parsedFormat}
47+
InputProps={{
48+
...InputProps,
49+
readOnly: true,
50+
endAdornment: <CalendarIcon color="action" />,
51+
sx: { cursor: 'pointer', '& *': { cursor: 'inherit' } },
52+
}}
53+
error={hasValidationError}
54+
onClick={handleTogglePicker}
55+
/>
56+
);
57+
}
58+
59+
function ReadOnlyFieldDatePicker(props: DatePickerProps<Dayjs>) {
60+
return (
61+
<DatePicker {...props} slots={{ ...props.slots, field: ReadOnlyDateField }} />
62+
);
63+
}
64+
65+
export default function ReadOnlyMaterialTextField() {
66+
return (
67+
<LocalizationProvider dateAdapter={AdapterDayjs}>
68+
<ReadOnlyFieldDatePicker />
69+
</LocalizationProvider>
70+
);
71+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<ReadOnlyFieldDatePicker />

docs/data/date-pickers/custom-field/custom-field.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,17 @@ you can replace the field with an `Autocomplete` listing those dates:
158158

159159
{{"demo": "PickerWithAutocompleteField.js", "defaultCodeOpen": false}}
160160

161+
### Using a read-only `TextField`
162+
163+
If you want users to select a value exclusively through the views
164+
but you still want the UI to look like a `TextField`, you can replace the field with a read-only `TextField`:
165+
166+
{{"demo": "custom-behavior/ReadOnlyMaterialTextField.js", "defaultCodeOpen": false}}
167+
161168
### Using a `Button`
162169

163-
If you only want to allow the user to pick a value through the views,
164-
you can replace the field with a `Button`:
170+
If you want users to select a value exclusively through the views
171+
and you don't want the UI to look like a `TextField`, you can replace the field with a `Button`:
165172

166173
{{"demo": "PickerWithButtonField.js", "defaultCodeOpen": false}}
167174

docs/data/date-pickers/experimentation/CustomField.js

Lines changed: 0 additions & 47 deletions
This file was deleted.

docs/data/date-pickers/experimentation/CustomField.tsx

Lines changed: 0 additions & 47 deletions
This file was deleted.

docs/data/date-pickers/experimentation/CustomField.tsx.preview

Lines changed: 0 additions & 6 deletions
This file was deleted.

docs/data/date-pickers/experimentation/experimentation.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,3 @@ productId: x-date-pickers
55
# Date and Time Pickers experimentation
66

77
<p class="description">Demos not accessible through the navbar of the doc</p>
8-
9-
## Custom field
10-
11-
{{"demo": "CustomField.js"}}

packages/x-date-pickers-pro/src/internals/hooks/useDesktopRangePicker/useDesktopRangePicker.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import * as React from 'react';
22
import useSlotProps from '@mui/utils/useSlotProps';
33
import { useLicenseVerifier } from '@mui/x-license';
4-
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
54
import { PickersLayout, PickersLayoutSlotProps } from '@mui/x-date-pickers/PickersLayout';
65
import {
76
executeInTheNextEventLoopTick,
@@ -11,6 +10,7 @@ import {
1110
ExportedBaseToolbarProps,
1211
DateOrTimeViewWithMeridiem,
1312
ExportedBaseTabsProps,
13+
PickersProvider,
1414
} from '@mui/x-date-pickers/internals';
1515
import { PickerValidDate, FieldRef, InferError } from '@mui/x-date-pickers/models';
1616
import {
@@ -93,6 +93,7 @@ export const useDesktopRangePicker = <
9393
renderCurrentView,
9494
shouldRestoreFocus,
9595
fieldProps: pickerFieldProps,
96+
contextValue,
9697
} = usePicker<
9798
DateRange<TDate>,
9899
TDate,
@@ -209,7 +210,7 @@ export const useDesktopRangePicker = <
209210
const Layout = slots?.layout ?? PickersLayout;
210211

211212
const renderPicker = () => (
212-
<LocalizationProvider localeText={localeText}>
213+
<PickersProvider contextValue={contextValue} localeText={localeText}>
213214
<Field {...enrichedFieldProps} />
214215
<PickersPopper
215216
role="tooltip"
@@ -233,7 +234,7 @@ export const useDesktopRangePicker = <
233234
{renderCurrentView()}
234235
</Layout>
235236
</PickersPopper>
236-
</LocalizationProvider>
237+
</PickersProvider>
237238
);
238239

239240
return {

packages/x-date-pickers-pro/src/internals/hooks/useMobileRangePicker/useMobileRangePicker.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import * as React from 'react';
22
import useSlotProps from '@mui/utils/useSlotProps';
33
import { useLicenseVerifier } from '@mui/x-license';
4-
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
54
import { PickersLayout, PickersLayoutSlotProps } from '@mui/x-date-pickers/PickersLayout';
65
import {
76
usePicker,
87
PickersModalDialog,
98
ExportedBaseToolbarProps,
109
DateOrTimeViewWithMeridiem,
1110
ExportedBaseTabsProps,
11+
PickersProvider,
1212
} from '@mui/x-date-pickers/internals';
1313
import { usePickersTranslations } from '@mui/x-date-pickers/hooks';
1414
import { PickerValidDate, FieldRef, InferError } from '@mui/x-date-pickers/models';
@@ -88,6 +88,7 @@ export const useMobileRangePicker = <
8888
layoutProps,
8989
renderCurrentView,
9090
fieldProps: pickerFieldProps,
91+
contextValue,
9192
} = usePicker<
9293
DateRange<TDate>,
9394
TDate,
@@ -214,7 +215,7 @@ export const useMobileRangePicker = <
214215
};
215216

216217
const renderPicker = () => (
217-
<LocalizationProvider localeText={localeText}>
218+
<PickersProvider contextValue={contextValue} localeText={localeText}>
218219
<Field {...enrichedFieldProps} />
219220
<PickersModalDialog {...actions} open={open} slots={slots} slotProps={slotProps}>
220221
<Layout
@@ -226,7 +227,7 @@ export const useMobileRangePicker = <
226227
{renderCurrentView()}
227228
</Layout>
228229
</PickersModalDialog>
229-
</LocalizationProvider>
230+
</PickersProvider>
230231
);
231232

232233
return {

0 commit comments

Comments
 (0)