From 17729127ca9448a096c49d076cda0fb58d5fac91 Mon Sep 17 00:00:00 2001 From: flavien Date: Mon, 7 Oct 2024 11:33:10 +0200 Subject: [PATCH] Review Lukas + JSDoc for useParsedFormat --- .../ReadOnlyMaterialTextField.js | 6 +- .../ReadOnlyMaterialTextField.tsx | 6 +- .../useDesktopRangePicker.tsx | 4 +- .../useMobileRangePicker.tsx | 4 +- packages/x-date-pickers/src/hooks/index.tsx | 2 +- .../src/hooks/useFieldPlaceholder.ts | 56 ----------------- .../src/hooks/useParsedFormat.ts | 61 +++++++++++++++++++ .../internals/components/PickersProvider.tsx | 8 +-- .../useDesktopPicker/useDesktopPicker.tsx | 4 +- .../hooks/useField/buildSectionsFromFormat.ts | 10 +-- .../hooks/useField/useField.types.ts | 2 +- .../hooks/useField/useField.utils.ts | 10 ++- .../hooks/useMobilePicker/useMobilePicker.tsx | 4 +- .../internals/hooks/usePicker/usePicker.ts | 2 +- .../hooks/usePicker/usePickerValue.ts | 4 +- .../hooks/usePicker/usePickerValue.types.ts | 2 +- scripts/x-date-pickers-pro.exports.json | 2 +- scripts/x-date-pickers.exports.json | 2 +- 18 files changed, 94 insertions(+), 95 deletions(-) delete mode 100644 packages/x-date-pickers/src/hooks/useFieldPlaceholder.ts create mode 100644 packages/x-date-pickers/src/hooks/useParsedFormat.ts diff --git a/docs/data/date-pickers/custom-field/custom-behavior/ReadOnlyMaterialTextField.js b/docs/data/date-pickers/custom-field/custom-behavior/ReadOnlyMaterialTextField.js index 844d92176838f..35d02256eea58 100644 --- a/docs/data/date-pickers/custom-field/custom-behavior/ReadOnlyMaterialTextField.js +++ b/docs/data/date-pickers/custom-field/custom-behavior/ReadOnlyMaterialTextField.js @@ -7,7 +7,7 @@ import { DatePicker } from '@mui/x-date-pickers/DatePicker'; import { useValidation, validateDate } from '@mui/x-date-pickers/validation'; import { useSplitFieldProps, - useFieldPlaceholder, + useParsedFormat, usePickersContext, } from '@mui/x-date-pickers/hooks'; @@ -18,7 +18,7 @@ function ReadOnlyDateField(props) { const pickersContext = usePickersContext(); - const placeholder = useFieldPlaceholder(internalProps); + const parsedFormat = useParsedFormat(internalProps); const { hasValidationError } = useValidation({ validator: validateDate, value, @@ -30,7 +30,7 @@ function ReadOnlyDateField(props) { ) { const pickersContext = usePickersContext(); - const placeholder = useFieldPlaceholder(internalProps); + const parsedFormat = useParsedFormat(internalProps); const { hasValidationError } = useValidation({ validator: validateDate, value, @@ -34,7 +34,7 @@ function ReadOnlyDateField(props: DatePickerFieldProps) { , TDate, @@ -210,7 +210,7 @@ export const useDesktopRangePicker = < const Layout = slots?.layout ?? PickersLayout; const renderPicker = () => ( - + , TDate, @@ -215,7 +215,7 @@ export const useMobileRangePicker = < }; const renderPicker = () => ( - + ({ - format, - formatDensity = 'dense', - timezone, - shouldRespectLeadingZeros = false, -}: UseFieldPlaceholderParams) => { - const utils = useUtils(); - const isRtl = useRtl(); - const translations = usePickersTranslations(); - 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, - ]); -}; diff --git a/packages/x-date-pickers/src/hooks/useParsedFormat.ts b/packages/x-date-pickers/src/hooks/useParsedFormat.ts new file mode 100644 index 0000000000000..579356224ebef --- /dev/null +++ b/packages/x-date-pickers/src/hooks/useParsedFormat.ts @@ -0,0 +1,61 @@ +'use client'; +import * as React from 'react'; +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 { PickerValidDate } from '../models'; +import { usePickersTranslations } from './usePickersTranslations'; +import type { UseFieldInternalProps } from '../internals/hooks/useField'; + +interface UseParsedFormatParameters + extends Pick< + UseFieldInternalProps, + 'format' | 'formatDensity' | 'shouldRespectLeadingZeros' + > {} + +/** + * Returns the parsed format to be rendered in the field when their is no value or in other parts of the picker. + * This format is localized (e.g: `AAAA` for the year with the French locale) and cannot be parsed by your date library. + * @param {object} The parameters needed to build the placeholder. + * @param {string} params.format Format of the date to use. + * @param {'dense' | 'spacious'} params.formatDensity Density of the format (setting `formatDensity` to `"spacious"` will add a space before and after each `/`, `-` and `.` character). + * @param {boolean} params.shouldRespectLeadingZeros If `true`, the format will respect the leading zeroes, if `false`, the format will always add leading zeroes. + * @returns + */ +export const useParsedFormat = ( + parameters: UseParsedFormatParameters, +) => { + const { format, formatDensity = 'dense', shouldRespectLeadingZeros = false } = parameters; + const utils = useUtils(); + const isRtl = useRtl(); + const translations = usePickersTranslations(); + const localizedDigits = React.useMemo(() => getLocalizedDigits(utils), [utils]); + + return React.useMemo(() => { + const sections = buildSectionsFromFormat({ + utils, + format, + formatDensity, + isRtl, + shouldRespectLeadingZeros, + localeText: translations, + localizedDigits, + date: null, + // TODO v9: Make sure we still don't reverse in `buildSectionsFromFormat` when using `useParsedFormat`. + enableAccessibleFieldDOMStructure: false, + }); + + return sections + .map((section) => `${section.startSeparator}${section.placeholder}${section.endSeparator}`) + .join(''); + }, [ + utils, + isRtl, + translations, + localizedDigits, + format, + formatDensity, + shouldRespectLeadingZeros, + ]); +}; diff --git a/packages/x-date-pickers/src/internals/components/PickersProvider.tsx b/packages/x-date-pickers/src/internals/components/PickersProvider.tsx index 9cdfbf83c7908..faeea0b8da854 100644 --- a/packages/x-date-pickers/src/internals/components/PickersProvider.tsx +++ b/packages/x-date-pickers/src/internals/components/PickersProvider.tsx @@ -7,7 +7,7 @@ export const PickersContext = React.createContext(nu /** * Provides the context for the various parts of a picker component: - * - fieldContextValue: the context for the field + * - contextValue: the context for the picker sub-components. * - localizationProvider: the translations passed through the props and through a parent LocalizationProvider. * * @ignore - do not document. @@ -15,17 +15,17 @@ export const PickersContext = React.createContext(nu export function PickersProvider( props: PickersFieldProviderProps, ) { - const { fieldContextValue, localeText, children } = props; + const { contextValue, localeText, children } = props; return ( - + {children} ); } interface PickersFieldProviderProps { - fieldContextValue: PickersContextValue; + contextValue: PickersContextValue; localeText: PickersInputLocaleText | undefined; children: React.ReactNode; } 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 62897088a7915..bd77cbc4d0e98 100644 --- a/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx +++ b/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx @@ -80,7 +80,7 @@ export const useDesktopPicker = < renderCurrentView, shouldRestoreFocus, fieldProps: pickerFieldProps, - fieldContextValue, + contextValue, } = usePicker({ ...pickerParams, props, @@ -213,7 +213,7 @@ export const useDesktopPicker = < const handleFieldRef = useForkRef(fieldRef, fieldProps.unstableFieldRef); const renderPicker = () => ( - + { format: string; formatDensity: 'dense' | 'spacious'; isRtl: boolean; - timezone: PickersTimezone; shouldRespectLeadingZeros: boolean; localeText: PickersLocaleText; localizedDigits: string[]; @@ -64,7 +63,6 @@ const getEscapedPartsFromFormat = ({ const getSectionPlaceholder = ( utils: MuiPickersAdapter, - timezone: PickersTimezone, localeText: PickersLocaleText, sectionConfig: Pick, sectionFormat: string, @@ -72,7 +70,7 @@ const getSectionPlaceholder = ( switch (sectionConfig.type) { case 'year': { return localeText.fieldYearPlaceholder({ - digitAmount: utils.formatByString(utils.date(undefined, timezone), sectionFormat).length, + digitAmount: utils.formatByString(utils.date(undefined, 'default'), sectionFormat).length, format: sectionFormat, }); } @@ -119,7 +117,6 @@ const getSectionPlaceholder = ( const createSection = ({ utils, - timezone, date, shouldRespectLeadingZeros, localeText, @@ -140,7 +137,6 @@ const createSection = ({ const hasLeadingZerosInFormat = doesSectionFormatHaveLeadingZeros( utils, - timezone, sectionConfig.contentType, sectionConfig.type, token, @@ -181,7 +177,7 @@ const createSection = ({ format: token, maxLength, value: sectionValue, - placeholder: getSectionPlaceholder(utils, timezone, localeText, sectionConfig, token), + placeholder: getSectionPlaceholder(utils, localeText, sectionConfig, token), hasLeadingZerosInFormat, hasLeadingZerosInInput, startSeparator, diff --git a/packages/x-date-pickers/src/internals/hooks/useField/useField.types.ts b/packages/x-date-pickers/src/internals/hooks/useField/useField.types.ts index e558a7b3b7ee9..589d5cfd3fcde 100644 --- a/packages/x-date-pickers/src/internals/hooks/useField/useField.types.ts +++ b/packages/x-date-pickers/src/internals/hooks/useField/useField.types.ts @@ -74,7 +74,7 @@ export interface UseFieldInternalProps< */ onChange?: FieldChangeHandler; /** - * Format of the date when rendered in the input(s). + * Format of the date when rendered in the field. */ format: string; /** diff --git a/packages/x-date-pickers/src/internals/hooks/useField/useField.utils.ts b/packages/x-date-pickers/src/internals/hooks/useField/useField.utils.ts index 71b5465dce556..395bd15302843 100644 --- a/packages/x-date-pickers/src/internals/hooks/useField/useField.utils.ts +++ b/packages/x-date-pickers/src/internals/hooks/useField/useField.utils.ts @@ -393,13 +393,11 @@ export const changeSectionValueFormat = ( const isFourDigitYearFormat = ( utils: MuiPickersAdapter, - timezone: PickersTimezone, format: string, -) => utils.formatByString(utils.date(undefined, timezone), format).length === 4; +) => utils.formatByString(utils.date(undefined, 'system'), format).length === 4; export const doesSectionFormatHaveLeadingZeros = ( utils: MuiPickersAdapter, - timezone: PickersTimezone, contentType: FieldSectionContentType, sectionType: FieldSectionType, format: string, @@ -408,12 +406,12 @@ export const doesSectionFormatHaveLeadingZeros = return false; } - const now = utils.date(undefined, timezone); + const now = utils.date(undefined, 'default'); switch (sectionType) { // We can't use `changeSectionValueFormat`, because `utils.parse('1', 'YYYY')` returns `1971` instead of `1`. case 'year': { - if (isFourDigitYearFormat(utils, timezone, format)) { + if (isFourDigitYearFormat(utils, format)) { const formatted0001 = utils.formatByString(utils.setYear(now, 1), format); return formatted0001 === '0001'; } @@ -547,7 +545,7 @@ export const getSectionsBoundaries = ( return { year: ({ format }) => ({ minimum: 0, - maximum: isFourDigitYearFormat(utils, timezone, format) ? 9999 : 99, + maximum: isFourDigitYearFormat(utils, format) ? 9999 : 99, }), month: () => ({ minimum: 1, diff --git a/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.tsx b/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.tsx index 63f734258bb44..89108c2cb6211 100644 --- a/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.tsx +++ b/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.tsx @@ -73,7 +73,7 @@ export const useMobilePicker = < layoutProps, renderCurrentView, fieldProps: pickerFieldProps, - fieldContextValue, + contextValue, } = usePicker({ ...pickerParams, props, @@ -160,7 +160,7 @@ export const useMobilePicker = < const handleFieldRef = useForkRef(fieldRef, fieldProps.unstableFieldRef); const renderPicker = () => ( - + ( + const contextValue = React.useMemo( () => ({ onToggleView: (event) => { if (isOpen) { @@ -477,6 +477,6 @@ export const usePickerValue = < viewProps: viewResponse, layoutProps: layoutResponse, actions, - fieldContextValue, + contextValue, }; }; diff --git a/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerValue.types.ts b/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerValue.types.ts index 81ca78cfe8723..5b5952a1d3d04 100644 --- a/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerValue.types.ts +++ b/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerValue.types.ts @@ -328,5 +328,5 @@ export interface UsePickerValueResponse; fieldProps: UsePickerValueFieldResponse; layoutProps: UsePickerValueLayoutResponse; - fieldContextValue: PickersContextValue; + contextValue: PickersContextValue; } diff --git a/scripts/x-date-pickers-pro.exports.json b/scripts/x-date-pickers-pro.exports.json index 3901b785a96a5..c0c3e0d39b253 100644 --- a/scripts/x-date-pickers-pro.exports.json +++ b/scripts/x-date-pickers-pro.exports.json @@ -413,13 +413,13 @@ { "name": "UseDateRangeFieldProps", "kind": "Interface" }, { "name": "UseDateTimeFieldComponentProps", "kind": "TypeAlias" }, { "name": "UseDateTimeFieldProps", "kind": "Interface" }, - { "name": "useFieldPlaceholder", "kind": "Variable" }, { "name": "UseMultiInputDateRangeFieldComponentProps", "kind": "TypeAlias" }, { "name": "UseMultiInputDateRangeFieldProps", "kind": "Interface" }, { "name": "UseMultiInputDateTimeRangeFieldComponentProps", "kind": "TypeAlias" }, { "name": "UseMultiInputDateTimeRangeFieldProps", "kind": "Interface" }, { "name": "UseMultiInputTimeRangeFieldComponentProps", "kind": "TypeAlias" }, { "name": "UseMultiInputTimeRangeFieldProps", "kind": "Interface" }, + { "name": "useParsedFormat", "kind": "Variable" }, { "name": "usePickerLayout", "kind": "ExportAssignment" }, { "name": "usePickersContext", "kind": "Variable" }, { "name": "usePickersTranslations", "kind": "Variable" }, diff --git a/scripts/x-date-pickers.exports.json b/scripts/x-date-pickers.exports.json index b2f7126714e49..e432b56a60afc 100644 --- a/scripts/x-date-pickers.exports.json +++ b/scripts/x-date-pickers.exports.json @@ -304,7 +304,7 @@ { "name": "UseDateFieldProps", "kind": "Interface" }, { "name": "UseDateTimeFieldComponentProps", "kind": "TypeAlias" }, { "name": "UseDateTimeFieldProps", "kind": "Interface" }, - { "name": "useFieldPlaceholder", "kind": "Variable" }, + { "name": "useParsedFormat", "kind": "Variable" }, { "name": "usePickerLayout", "kind": "ExportAssignment" }, { "name": "usePickersContext", "kind": "Variable" }, { "name": "usePickersTranslations", "kind": "Variable" },