diff --git a/superset-frontend/src/components/ListView/Filters/DateRange.tsx b/superset-frontend/src/components/ListView/Filters/DateRange.tsx index b7816dcdb8ec9..bf03012eedf4a 100644 --- a/superset-frontend/src/components/ListView/Filters/DateRange.tsx +++ b/superset-frontend/src/components/ListView/Filters/DateRange.tsx @@ -29,6 +29,9 @@ import { RangePicker } from 'src/components/DatePicker'; import { FormLabel } from 'src/components/Form'; import { extendedDayjs } from 'src/utils/dates'; import { Dayjs } from 'dayjs'; +import Loading from 'src/components/Loading'; +import { AntdThemeProvider } from 'src/components/AntdThemeProvider'; +import { useLocale } from 'src/hooks/useLocale'; import { BaseFilter, FilterHandler } from './Base'; interface DateRangeFilterProps extends BaseFilter { @@ -56,6 +59,8 @@ function DateRangeFilter( return [extendedDayjs(value[0]), extendedDayjs(value[1])]; }, [value]); + const locale = useLocale(); + useImperativeHandle(ref, () => ({ clearFilter: () => { setValue(null); @@ -63,28 +68,33 @@ function DateRangeFilter( }, })); + if (locale === null) { + return ; + } return ( - - {Header} - { - if (!dayjsRange?.[0]?.valueOf() || !dayjsRange?.[1]?.valueOf()) { - setValue(null); - onSubmit([]); - return; - } - const changeValue = [ - dayjsRange[0]?.valueOf() ?? 0, - dayjsRange[1]?.valueOf() ?? 0, - ] as ValueState; - setValue(changeValue); - onSubmit(changeValue); - }} - /> - + + + {Header} + { + if (!dayjsRange?.[0]?.valueOf() || !dayjsRange?.[1]?.valueOf()) { + setValue(null); + onSubmit([]); + return; + } + const changeValue = [ + dayjsRange[0]?.valueOf() ?? 0, + dayjsRange[1]?.valueOf() ?? 0, + ] as ValueState; + setValue(changeValue); + onSubmit(changeValue); + }} + /> + + ); } diff --git a/superset-frontend/src/explore/components/controls/DateFilterControl/components/CustomFrame.tsx b/superset-frontend/src/explore/components/controls/DateFilterControl/components/CustomFrame.tsx index 4ac8cfc4e0d6e..ec558c0909890 100644 --- a/superset-frontend/src/explore/components/controls/DateFilterControl/components/CustomFrame.tsx +++ b/superset-frontend/src/explore/components/controls/DateFilterControl/components/CustomFrame.tsx @@ -16,8 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -import { useEffect, useState } from 'react'; -import { useSelector } from 'react-redux'; import { isInteger } from 'lodash'; import { t, customTimeRangeDecode } from '@superset-ui/core'; import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls'; @@ -35,23 +33,19 @@ import { MIDNIGHT, customTimeRangeEncode, dttmToDayjs, - LOCALE_MAPPING, } from 'src/explore/components/controls/DateFilterControl/utils'; import { CustomRangeKey, FrameComponentProps, } from 'src/explore/components/controls/DateFilterControl/types'; -import { ExplorePageState } from 'src/explore/types'; import Loading from 'src/components/Loading'; -import dayjs, { Dayjs } from 'dayjs'; +import { Dayjs } from 'dayjs'; import { AntdThemeProvider } from 'src/components/AntdThemeProvider'; -import { Locale } from 'antd-v5/es/locale'; +import { useLocale } from 'src/hooks/useLocale'; export function CustomFrame(props: FrameComponentProps) { const { customRange, matchedFlag } = customTimeRangeDecode(props.value); - const [datePickerLocale, setDatePickerLocale] = useState< - Locale | undefined | null - >(null); + const datePickerLocale = useLocale(); if (!matchedFlag) { props.onChange(customTimeRangeEncode(customRange)); } @@ -113,31 +107,6 @@ export function CustomFrame(props: FrameComponentProps) { } } - // check if there is a locale defined for explore - const localFromFlaskBabel = useSelector( - (state: ExplorePageState) => state?.common?.locale, - ); - - // An undefined datePickerLocale is acceptable if no match is found in the LOCALE_MAPPING[localFromFlaskBabel] lookup - // and will fall back to antd's default locale when the antd DataPicker's prop locale === undefined - // This also protects us from the case where state is populated with a locale that antd locales does not recognize - useEffect(() => { - if (datePickerLocale === null) { - if (localFromFlaskBabel && LOCALE_MAPPING[localFromFlaskBabel]) { - LOCALE_MAPPING[localFromFlaskBabel]() - .then((locale: { default: Locale }) => { - setDatePickerLocale(locale.default); - import(`dayjs/locale/${localFromFlaskBabel}`).then(() => { - dayjs.locale(localFromFlaskBabel); - }); - }) - .catch(() => setDatePickerLocale(undefined)); - } else { - setDatePickerLocale(undefined); - } - } - }, [datePickerLocale, localFromFlaskBabel]); - if (datePickerLocale === null) { return ; } diff --git a/superset-frontend/src/explore/components/controls/DateFilterControl/utils/constants.ts b/superset-frontend/src/explore/components/controls/DateFilterControl/utils/constants.ts index 0d37ae69133f1..4a116fb65e444 100644 --- a/superset-frontend/src/explore/components/controls/DateFilterControl/utils/constants.ts +++ b/superset-frontend/src/explore/components/controls/DateFilterControl/utils/constants.ts @@ -141,23 +141,6 @@ export const MIDNIGHT = extendedDayjs() .startOf('day') .format(DAYJS_FORMAT); -export const LOCALE_MAPPING = { - en: () => import('antd-v5/locale/en_US'), - fr: () => import('antd-v5/locale/fr_FR'), - es: () => import('antd-v5/locale/es_ES'), - it: () => import('antd-v5/locale/it_IT'), - zh: () => import('antd-v5/locale/zh_CN'), - ja: () => import('antd-v5/locale/ja_JP'), - de: () => import('antd-v5/locale/de_DE'), - pt: () => import('antd-v5/locale/pt_PT'), - pt_BR: () => import('antd-v5/locale/pt_BR'), - ru: () => import('antd-v5/locale/ru_RU'), - ko: () => import('antd-v5/locale/ko_KR'), - sk: () => import('antd-v5/locale/sk_SK'), - sl: () => import('antd-v5/locale/sl_SI'), - nl: () => import('antd-v5/locale/nl_NL'), -}; - export enum DateFilterTestKey { CommonFrame = 'common-frame', ModalOverlay = 'modal-overlay', diff --git a/superset-frontend/src/hooks/useLocale.ts b/superset-frontend/src/hooks/useLocale.ts new file mode 100644 index 0000000000000..43b7505b18ff5 --- /dev/null +++ b/superset-frontend/src/hooks/useLocale.ts @@ -0,0 +1,71 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Locale } from 'antd-v5/es/locale'; +import dayjs from 'dayjs'; +import { useEffect, useState } from 'react'; +import { useSelector } from 'react-redux'; +import { ExplorePageState } from 'src/explore/types'; + +export const LOCALE_MAPPING = { + en: () => import('antd-v5/locale/en_US'), + fr: () => import('antd-v5/locale/fr_FR'), + es: () => import('antd-v5/locale/es_ES'), + it: () => import('antd-v5/locale/it_IT'), + zh: () => import('antd-v5/locale/zh_CN'), + ja: () => import('antd-v5/locale/ja_JP'), + de: () => import('antd-v5/locale/de_DE'), + pt: () => import('antd-v5/locale/pt_PT'), + pt_BR: () => import('antd-v5/locale/pt_BR'), + ru: () => import('antd-v5/locale/ru_RU'), + ko: () => import('antd-v5/locale/ko_KR'), + sk: () => import('antd-v5/locale/sk_SK'), + sl: () => import('antd-v5/locale/sl_SI'), + nl: () => import('antd-v5/locale/nl_NL'), +}; + +export const useLocale = (): Locale | undefined | null => { + const [datePickerLocale, setDatePickerLocale] = useState< + Locale | undefined | null + >(null); + + // Retrieve the locale from Redux store + const localFromFlaskBabel = useSelector( + (state: ExplorePageState) => state?.common?.locale, + ); + + useEffect(() => { + if (datePickerLocale === null) { + if (localFromFlaskBabel && LOCALE_MAPPING[localFromFlaskBabel]) { + LOCALE_MAPPING[localFromFlaskBabel]() + .then((locale: { default: Locale }) => { + setDatePickerLocale(locale.default); + import(`dayjs/locale/${localFromFlaskBabel}.js`).then(() => { + dayjs.locale(localFromFlaskBabel); + }); + }) + .catch(() => setDatePickerLocale(undefined)); + } else { + setDatePickerLocale(undefined); + } + } + }, [datePickerLocale, localFromFlaskBabel]); + + return datePickerLocale; +}; diff --git a/superset-frontend/webpack.config.js b/superset-frontend/webpack.config.js index 14b2866876a2b..4384e0638b2c5 100644 --- a/superset-frontend/webpack.config.js +++ b/superset-frontend/webpack.config.js @@ -160,11 +160,6 @@ const plugins = [ chunks: [], filename: '500.html', }), - // Dayjs locales to keep - new webpack.ContextReplacementPlugin( - /dayjs[/\\]locale$/, - new RegExp(`^./(${getAvailableTranslationCodes().join('|')})$`), - ), ]; if (!process.env.CI) {