From 6c79656af2024aa02a68d5130ebd38504828d9a8 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Wed, 18 Jan 2023 11:02:46 +0100 Subject: [PATCH 1/4] Fix DatePicker issues --- packages/toolpad-components/package.json | 2 + .../toolpad-components/src/DatePicker.tsx | 61 +++++++++++++------ yarn.lock | 18 ++++++ 3 files changed, 64 insertions(+), 17 deletions(-) diff --git a/packages/toolpad-components/package.json b/packages/toolpad-components/package.json index c5e4c8c1857..7b41d517936 100644 --- a/packages/toolpad-components/package.json +++ b/packages/toolpad-components/package.json @@ -33,6 +33,8 @@ "@mui/material": "^5.11.5", "@mui/toolpad-core": "^0.0.35", "@mui/x-data-grid-pro": "^5.17.19", + "@mui/x-date-pickers": "^5.0.14", + "dayjs": "^1.11.7", "react-markdown": "^8.0.4" }, "devDependencies": { diff --git a/packages/toolpad-components/src/DatePicker.tsx b/packages/toolpad-components/src/DatePicker.tsx index 16f5ddf6fa3..b6577659e47 100644 --- a/packages/toolpad-components/src/DatePicker.tsx +++ b/packages/toolpad-components/src/DatePicker.tsx @@ -1,15 +1,22 @@ import * as React from 'react'; - import { TextField } from '@mui/material'; - import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; import { DesktopDatePicker, DesktopDatePickerProps } from '@mui/x-date-pickers/DesktopDatePicker'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import { createComponent } from '@mui/toolpad-core'; -import { Dayjs } from 'dayjs'; +import * as dayjs from 'dayjs'; import { SX_PROP_HELPER_TEXT } from './constants'; -export interface DatePickerProps extends DesktopDatePickerProps { +const LOCALE_LOADERS = new Map([ + ['nl', () => import('dayjs/locale/nl')], + ['fr', () => import('dayjs/locale/fr')], + // TODO... +]); + +export interface DatePickerProps + extends Omit, 'value' | 'onChange'> { + value: string; + onChange: (newValue: string) => void; format: string; fullWidth: boolean; variant: 'outlined' | 'filled' | 'standard'; @@ -18,19 +25,43 @@ export interface DatePickerProps extends DesktopDatePickerProps { defaultValue: string; } -function DatePicker(props: DatePickerProps) { - const customProps: any = {}; +function DatePicker({ format, onChange, ...props }: DatePickerProps) { + const handleChange = React.useCallback( + (value: dayjs.Dayjs | null) => { + const stringValue = value?.format('YYYY-MM-DD') || ''; + onChange(stringValue); + }, + [onChange], + ); + + const [adapterLocale, setAdapterLocale] = React.useState(); - if (props.format) { - // If inputFormat receives undefined prop, datepicker throws error - customProps.inputFormat = props.format; - } + React.useEffect(() => { + const languages = navigator.languages; + for (const language of languages) { + const loader = LOCALE_LOADERS.get(language); + if (loader) { + loader().then( + () => { + setAdapterLocale(language); + }, + () => { + console.error(`Failed to load locale "${language}", proceeding without localization`); + }, + ); + return; + } + } + }, []); return ( - + // @ts-expect-error This seems to be a dependencies issue. Recreating the yarn.lock file solves this. + // TODO: recreate yarn.lock, or find less drastic solution + ( { - // date-only form of ISO8601. See https://tc39.es/ecma262/#sec-date-time-string-format - return newValue.format('YYYY-MM-DD'); - }, defaultValue: '', defaultValueProp: 'defaultValue', }, diff --git a/yarn.lock b/yarn.lock index c495c059a3b..f2ca11c97f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2112,6 +2112,24 @@ react-transition-group "^4.4.5" rifm "^0.12.1" +"@mui/x-date-pickers@^5.0.14": + version "5.0.14" + resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-5.0.14.tgz#acfc3ef9be914e2f0127484442063f7300deeebf" + integrity sha512-+k+YOL++wEwuF6XRhF3uMLOXlHkUjMHmbXOXWWZ9wJppaGFolj9fNmUvydCp3Z0E9dPRt8J5Vv0z+upZ9KyQZg== + dependencies: + "@babel/runtime" "^7.18.9" + "@date-io/core" "^2.15.0" + "@date-io/date-fns" "^2.15.0" + "@date-io/dayjs" "^2.15.0" + "@date-io/luxon" "^2.15.0" + "@date-io/moment" "^2.15.0" + "@mui/utils" "^5.10.3" + "@types/react-transition-group" "^4.4.5" + clsx "^1.2.1" + prop-types "^15.7.2" + react-transition-group "^4.4.5" + rifm "^0.12.1" + "@mui/x-license-pro@5.17.12": version "5.17.12" resolved "https://registry.yarnpkg.com/@mui/x-license-pro/-/x-license-pro-5.17.12.tgz#d069416b7191f9f5b77b2bf7375d2c51aead497c" From c0db3823899313670b79837d13f476b95a5e0b04 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Wed, 18 Jan 2023 11:09:57 +0100 Subject: [PATCH 2/4] preserve comment --- packages/toolpad-components/src/DatePicker.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/toolpad-components/src/DatePicker.tsx b/packages/toolpad-components/src/DatePicker.tsx index b6577659e47..27d9e2a6acc 100644 --- a/packages/toolpad-components/src/DatePicker.tsx +++ b/packages/toolpad-components/src/DatePicker.tsx @@ -28,6 +28,7 @@ export interface DatePickerProps function DatePicker({ format, onChange, ...props }: DatePickerProps) { const handleChange = React.useCallback( (value: dayjs.Dayjs | null) => { + // date-only form of ISO8601. See https://tc39.es/ecma262/#sec-date-time-string-format const stringValue = value?.format('YYYY-MM-DD') || ''; onChange(stringValue); }, From 3063dc670a489cf40fa7d33315d12acf585e601c Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Wed, 18 Jan 2023 11:42:33 +0100 Subject: [PATCH 3/4] bring this back --- packages/toolpad-components/src/DatePicker.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/toolpad-components/src/DatePicker.tsx b/packages/toolpad-components/src/DatePicker.tsx index 27d9e2a6acc..dd5969d3764 100644 --- a/packages/toolpad-components/src/DatePicker.tsx +++ b/packages/toolpad-components/src/DatePicker.tsx @@ -4,7 +4,7 @@ import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; import { DesktopDatePicker, DesktopDatePickerProps } from '@mui/x-date-pickers/DesktopDatePicker'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import { createComponent } from '@mui/toolpad-core'; -import * as dayjs from 'dayjs'; +import { Dayjs } from 'dayjs'; import { SX_PROP_HELPER_TEXT } from './constants'; const LOCALE_LOADERS = new Map([ @@ -14,7 +14,7 @@ const LOCALE_LOADERS = new Map([ ]); export interface DatePickerProps - extends Omit, 'value' | 'onChange'> { + extends Omit, 'value' | 'onChange'> { value: string; onChange: (newValue: string) => void; format: string; @@ -27,7 +27,7 @@ export interface DatePickerProps function DatePicker({ format, onChange, ...props }: DatePickerProps) { const handleChange = React.useCallback( - (value: dayjs.Dayjs | null) => { + (value: Dayjs | null) => { // date-only form of ISO8601. See https://tc39.es/ecma262/#sec-date-time-string-format const stringValue = value?.format('YYYY-MM-DD') || ''; onChange(stringValue); From 8729f50d3a1cb4e5dc490b26cc35cd17f4981e82 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Wed, 18 Jan 2023 12:08:47 +0100 Subject: [PATCH 4/4] use better loading mechanism --- .../toolpad-components/src/DatePicker.tsx | 69 ++++++++++++++----- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/packages/toolpad-components/src/DatePicker.tsx b/packages/toolpad-components/src/DatePicker.tsx index dd5969d3764..ceb877b907c 100644 --- a/packages/toolpad-components/src/DatePicker.tsx +++ b/packages/toolpad-components/src/DatePicker.tsx @@ -8,11 +8,60 @@ import { Dayjs } from 'dayjs'; import { SX_PROP_HELPER_TEXT } from './constants'; const LOCALE_LOADERS = new Map([ + ['en', () => import('dayjs/locale/en')], ['nl', () => import('dayjs/locale/nl')], ['fr', () => import('dayjs/locale/fr')], // TODO... ]); +interface LoadableLocale { + locale: string; + load: () => Promise; +} + +const handlers = new Set<() => void>(); +let loadedLocale: undefined | string; + +function trygetLoadableLocale(locale: string): LoadableLocale | null { + const load = LOCALE_LOADERS.get(locale); + if (load) { + return { locale, load }; + } + return null; +} + +function getLoadableLocale(): LoadableLocale | null { + if (typeof window === 'undefined') { + return null; + } + const languages = window.navigator.languages; + for (const locale of languages) { + const { language } = new Intl.Locale(locale); + const result = trygetLoadableLocale(locale) || trygetLoadableLocale(language); + if (result) { + return result; + } + } + return null; +} + +const loadableLocale = getLoadableLocale(); +if (loadableLocale) { + loadableLocale.load().then(() => { + loadedLocale = loadableLocale.locale; + handlers.forEach((handler) => handler()); + }); +} + +function subscribeLocaleLoader(cb: () => void) { + handlers.add(cb); + return () => handlers.delete(cb); +} + +function getSnapshot() { + return loadedLocale; +} + export interface DatePickerProps extends Omit, 'value' | 'onChange'> { value: string; @@ -35,25 +84,7 @@ function DatePicker({ format, onChange, ...props }: DatePickerProps) { [onChange], ); - const [adapterLocale, setAdapterLocale] = React.useState(); - - React.useEffect(() => { - const languages = navigator.languages; - for (const language of languages) { - const loader = LOCALE_LOADERS.get(language); - if (loader) { - loader().then( - () => { - setAdapterLocale(language); - }, - () => { - console.error(`Failed to load locale "${language}", proceeding without localization`); - }, - ); - return; - } - } - }, []); + const adapterLocale = React.useSyncExternalStore(subscribeLocaleLoader, getSnapshot); return ( // @ts-expect-error This seems to be a dependencies issue. Recreating the yarn.lock file solves this.