Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[docs] New recipe of a read-only field #14606

Merged
merged 40 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
ffda378
[docs] New recipe of a read-only field
flaviendelangle Sep 13, 2024
81099d2
Fix
flaviendelangle Sep 13, 2024
95075dd
Fix
flaviendelangle Sep 13, 2024
92da0c7
Fix
flaviendelangle Sep 13, 2024
b79b5f4
Add onOpen prop
flaviendelangle Sep 13, 2024
c7a1032
Fix types
flaviendelangle Sep 13, 2024
66574c3
Fix types
flaviendelangle Sep 13, 2024
00c6329
Fix
flaviendelangle Sep 13, 2024
cb32485
Merge branch 'master' into readonly-field
flaviendelangle Sep 17, 2024
15fbfec
Improve doc
flaviendelangle Sep 17, 2024
5768e56
Merge branch 'master' into readonly-field
flaviendelangle Sep 20, 2024
26a2135
Try to move onOpen to a context
flaviendelangle Sep 24, 2024
676aa65
Remove TS changes
flaviendelangle Sep 24, 2024
9f5f302
Fix
flaviendelangle Sep 24, 2024
9837ebf
Fix
flaviendelangle Sep 24, 2024
c9b0906
Work
flaviendelangle Sep 24, 2024
9ca0721
Move logic to usePicker
flaviendelangle Sep 24, 2024
1f185cf
Improve typing
flaviendelangle Sep 24, 2024
5f7f8e5
Work
flaviendelangle Sep 24, 2024
7bd2d32
Review Arthur
flaviendelangle Sep 30, 2024
f345cba
Merge branch 'master' into readonly-field
flaviendelangle Sep 30, 2024
299a96c
Work
flaviendelangle Sep 30, 2024
164a1de
Work
flaviendelangle Oct 1, 2024
7adf029
Fix
flaviendelangle Oct 1, 2024
77ef242
Review Lukas
flaviendelangle Oct 4, 2024
a1bc87f
[pickers] Move the DateFieldInPickerProps interface to the picker fol…
flaviendelangle Oct 4, 2024
3642e3c
Fix
flaviendelangle Oct 7, 2024
e6ea1a5
Merge branch 'master' into readonly-field
flaviendelangle Oct 7, 2024
cb91ec7
Merge
flaviendelangle Oct 7, 2024
2365f69
Merge
flaviendelangle Oct 7, 2024
07a0285
Review Lukas + JSDoc for useParsedFormat
flaviendelangle Oct 7, 2024
9d5e300
Replace toggling method with onClose and onOpen
flaviendelangle Oct 7, 2024
065dbfc
Merge branch 'master' into readonly-field
flaviendelangle Oct 7, 2024
3e976f8
Update packages/x-date-pickers/src/internals/components/PickersProvid…
flaviendelangle Oct 7, 2024
f5b950e
Update packages/x-date-pickers/src/internals/components/PickersProvid…
flaviendelangle Oct 7, 2024
80e16b4
Update packages/x-date-pickers/src/internals/hooks/usePicker/usePicke…
flaviendelangle Oct 7, 2024
2fd6a5a
Update packages/x-date-pickers/src/hooks/useParsedFormat.ts
flaviendelangle Oct 7, 2024
9443b65
Update packages/x-date-pickers/src/hooks/usePickersContext.ts
flaviendelangle Oct 7, 2024
018158d
Improve demo
flaviendelangle Oct 7, 2024
70b1bef
Fix
flaviendelangle Oct 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import * as React from 'react';

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, useFieldPlaceholder } from '@mui/x-date-pickers/hooks';
import TextField from '@mui/material/TextField';

function ReadonlyField(props) {
const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');
const { value, timezone, format } = internalProps;
const { InputProps, slotProps, slots, ...other } = forwardedProps;

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

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

function ReadonlyFieldDatePicker(props) {
return <DatePicker slots={{ ...props.slots, field: ReadonlyField }} {...props} />;
}

export default function CustomReadonlyBehaviorMaterialTextField() {
return (
<LocalizationProvider dateAdapter={AdapterDayjs}>
<ReadonlyFieldDatePicker />
</LocalizationProvider>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import * as React from 'react';
import { Dayjs } from 'dayjs';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { DatePicker, DatePickerProps } from '@mui/x-date-pickers/DatePicker';
import { DateFieldInPickerProps } from '@mui/x-date-pickers/DateField';
import { useValidation, validateDate } from '@mui/x-date-pickers/validation';
import { useSplitFieldProps, useFieldPlaceholder } from '@mui/x-date-pickers/hooks';
import TextField from '@mui/material/TextField';

function ReadonlyField(props: DateFieldInPickerProps<Dayjs, false>) {
flaviendelangle marked this conversation as resolved.
Show resolved Hide resolved
const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');
const { value, timezone, format } = internalProps;
const { InputProps, slotProps, slots, ...other } = forwardedProps;

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

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

function ReadonlyFieldDatePicker(
props: Omit<DatePickerProps<Dayjs>, 'open' | 'onOpen' | 'onClose'>,
) {
return <DatePicker slots={{ ...props.slots, field: ReadonlyField }} {...props} />;
}

export default function CustomReadonlyBehaviorMaterialTextField() {
return (
<LocalizationProvider dateAdapter={AdapterDayjs}>
<ReadonlyFieldDatePicker />
</LocalizationProvider>
);
}
4 changes: 4 additions & 0 deletions docs/data/date-pickers/custom-field/custom-field.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ Learn more about the accessible DOM structure and its difference compared to the

## Usage with another UI

### Using a read-only `TextField`

{{"demo": "CustomReadonlyBehaviorMaterialTextField.js", "defaultCodeOpen": false}}

### Using an `Autocomplete`

If your user can only select a value in a small list of available dates,
Expand Down
2 changes: 2 additions & 0 deletions packages/x-date-pickers/src/hooks/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ export type {
export { usePickersTranslations } from './usePickersTranslations';

export { useSplitFieldProps } from './useSplitFieldProps';

export { useFieldPlaceholder } from './useFieldPlaceholder';
55 changes: 55 additions & 0 deletions packages/x-date-pickers/src/hooks/useFieldPlaceholder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import * as React from 'react';
flaviendelangle marked this conversation as resolved.
Show resolved Hide resolved
import { useRtl } from '@mui/system/RtlProvider';
import { useUtils } from '../internals/hooks/useUtils';
import { buildSectionsFromFormat } from '../internals/hooks/useField/buildSectionsFromFormat';
import { getLocalizedDigits } from '../internals/hooks/useField/useField.utils';
import { PickersTimezone, PickerValidDate } from '../models';
import { usePickersTranslations } from './usePickersTranslations';

interface UseFieldPlaceholderParams {
format: string;
formatDensity?: 'dense' | 'spacious';
timezone: PickersTimezone;
shouldRespectLeadingZeros?: boolean;
}

export const useFieldPlaceholder = <TDate extends PickerValidDate>({
LukasTy marked this conversation as resolved.
Show resolved Hide resolved
format,
formatDensity = 'dense',
timezone,
shouldRespectLeadingZeros = false,
}: UseFieldPlaceholderParams) => {
const utils = useUtils<TDate>();
const isRtl = useRtl();
const translations = usePickersTranslations<TDate>();
const localizedDigits = React.useMemo(() => getLocalizedDigits(utils), [utils]);

return React.useMemo(() => {
const sections = buildSectionsFromFormat({
utils,
format,
formatDensity,
isRtl,
timezone,
shouldRespectLeadingZeros,
localeText: translations,
localizedDigits,
date: null,
// TODO v9: Make sure we still don't reverse in `buildSectionsFromFormat` when using `useFieldPlaceholder`.
enableAccessibleFieldDOMStructure: false,
});

return sections
.map((section) => `${section.startSeparator}${section.placeholder}${section.endSeparator}`)
.join('');
}, [
utils,
isRtl,
translations,
localizedDigits,
format,
formatDensity,
timezone,
shouldRespectLeadingZeros,
]);
};