From 93e877a85ab54922efdeaef5fdce89f17ba8882c Mon Sep 17 00:00:00 2001 From: "Mr.Dr.Professor Patrick" Date: Fri, 7 Jul 2023 11:23:45 +0200 Subject: [PATCH] feat!: change moment to date-utils (#198) * feat!: remove moment * fix(Highcharts plugin): fix lang changing * chore: add @gravity-ui/eslint-config/prettier to eslintrc --- .eslintrc | 6 +++- .storybook/decorators/withLang.tsx | 18 ++++++---- package-lock.json | 26 ++++++++------- package.json | 3 +- src/libs/settings/__tests__/settings.test.ts | 13 ++------ src/libs/settings/eventEmitter.ts | 30 +++++++++++++++++ src/libs/settings/settings.ts | 33 +++++++++---------- .../components/HighchartsComponent.tsx | 7 ++++ .../renderer/helpers/config/config.js | 6 ++-- .../config/utils/setNavigatorDefaultPeriod.ts | 4 +-- .../renderer/helpers/highcharts/highcharts.js | 8 +++-- .../renderer/helpers/prepare-data.js | 6 ++-- .../yagr/renderer/tooltip/renderTooltip.ts | 4 +-- src/plugins/yagr/renderer/utils.ts | 6 ++-- 14 files changed, 107 insertions(+), 63 deletions(-) create mode 100644 src/libs/settings/eventEmitter.ts diff --git a/.eslintrc b/.eslintrc index 09c73a92..63913b76 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,4 +1,8 @@ { - "extends": ["@gravity-ui/eslint-config", "@gravity-ui/eslint-config/client"], + "extends": [ + "@gravity-ui/eslint-config", + "@gravity-ui/eslint-config/client", + "@gravity-ui/eslint-config/prettier" + ], "root": true } diff --git a/.storybook/decorators/withLang.tsx b/.storybook/decorators/withLang.tsx index ad2c72e2..f1ecb299 100644 --- a/.storybook/decorators/withLang.tsx +++ b/.storybook/decorators/withLang.tsx @@ -1,13 +1,19 @@ import React from 'react'; -import type {DecoratorFn} from '@storybook/react'; +import type {Decorator} from '@storybook/react'; import {Lang, configure} from '@gravity-ui/uikit'; +import {settings as dateUtilsSettings} from '@gravity-ui/date-utils'; +import {settings as chartkitSettings} from '../../src/libs'; -export const withLang: DecoratorFn = (Story, context) => { - const lang = context.globals.lang; +const setDateUtilsLocale = async (lang: string) => { + await dateUtilsSettings.loadLocale(lang); + dateUtilsSettings.setLocale(lang); +}; - configure({ - lang: lang as Lang, - }); +export const withLang: Decorator = (Story, context) => { + const lang = context.globals.lang; + chartkitSettings.set({lang}); + setDateUtilsLocale(lang); + configure({lang: lang as Lang}); return ; }; diff --git a/package-lock.json b/package-lock.json index 164f4bef..5e1ff01b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "2.13.0", "license": "MIT", "dependencies": { + "@gravity-ui/date-utils": "^1.4.1", "@gravity-ui/yagr": "^3.3.4", "bem-cn-lite": "^4.1.0", "highcharts": "^8.2.2", @@ -50,7 +51,6 @@ "jest": "^28.1.3", "jest-environment-jsdom": "^28.1.2", "lint-staged": "^10.2.7", - "moment": "^2.29.4", "npm-run-all": "^4.1.5", "prettier": "^2.6.0", "react": "^17.0.2", @@ -68,7 +68,6 @@ }, "peerDependencies": { "@gravity-ui/uikit": "^4.0.0", - "moment": "^2.19.3", "react": "^16.0.0 || ^17.0.0 || ^18.0.0" } }, @@ -2601,6 +2600,15 @@ "@floating-ui/core": "^1.3.1" } }, + "node_modules/@gravity-ui/date-utils": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@gravity-ui/date-utils/-/date-utils-1.4.1.tgz", + "integrity": "sha512-gjgpNlt69ogCS38JVOZUijTHF+ZTgh52z1GBURLX2hUJCbeP9SCw0+6cozBeigk5+HH9FZYPAPKrWXTWpBXuYA==", + "dependencies": { + "dayjs": "^1.11.7", + "lodash": "^4.17.0" + } + }, "node_modules/@gravity-ui/eslint-config": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@gravity-ui/eslint-config/-/eslint-config-1.0.2.tgz", @@ -11393,6 +11401,11 @@ "node": ">=12" } }, + "node_modules/dayjs": { + "version": "1.11.9", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.9.tgz", + "integrity": "sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -20979,15 +20992,6 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "dev": true }, - "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", diff --git a/package.json b/package.json index 10cbb649..712c0788 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "access": "public" }, "dependencies": { + "@gravity-ui/date-utils": "^1.4.1", "@gravity-ui/yagr": "^3.3.4", "bem-cn-lite": "^4.1.0", "highcharts": "^8.2.2", @@ -54,7 +55,6 @@ "jest": "^28.1.3", "jest-environment-jsdom": "^28.1.2", "lint-staged": "^10.2.7", - "moment": "^2.29.4", "npm-run-all": "^4.1.5", "prettier": "^2.6.0", "react": "^17.0.2", @@ -72,7 +72,6 @@ }, "peerDependencies": { "@gravity-ui/uikit": "^4.0.0", - "moment": "^2.19.3", "react": "^16.0.0 || ^17.0.0 || ^18.0.0" }, "scripts": { diff --git a/src/libs/settings/__tests__/settings.test.ts b/src/libs/settings/__tests__/settings.test.ts index 6238cf50..e84c05d0 100644 --- a/src/libs/settings/__tests__/settings.test.ts +++ b/src/libs/settings/__tests__/settings.test.ts @@ -1,17 +1,8 @@ -import {DEFAULT_LOCALE_SPECIFICATION, settings} from '../settings'; +import {settings} from '../settings'; -const resetSettings = () => - settings.set({ - lang: 'en', - locale: DEFAULT_LOCALE_SPECIFICATION, - }); +const resetSettings = () => settings.set({lang: 'en'}); describe('libs/settings', () => { - it('Default locale should be equal DEFAULT_LOCALE_SPECIFICATION', () => { - const result = settings.get('locale'); - expect(result).toStrictEqual(DEFAULT_LOCALE_SPECIFICATION); - }); - it('Default lang should be equal to en', () => { const result = settings.get('lang'); diff --git a/src/libs/settings/eventEmitter.ts b/src/libs/settings/eventEmitter.ts new file mode 100644 index 00000000..ed63218a --- /dev/null +++ b/src/libs/settings/eventEmitter.ts @@ -0,0 +1,30 @@ +type EventObject = { + id: string; + action: (args: T) => void; +}; + +export class EventEmitter { + private events = {} as Record[]>; + + on(type: MapKey, event: EventObject) { + if (this.events[type]) { + this.events[type].push(event); + } else { + this.events[type] = [event]; + } + } + + off(type: MapKey, eventId: string) { + if (this.events[type]) { + this.events[type] = this.events[type].filter(({id}) => id !== eventId); + } + } + + dispatch(type: MapKey, args: EventsMap[MapKey]) { + if (this.events[type]) { + this.events[type].forEach(({action}) => { + action(args); + }); + } + } +} diff --git a/src/libs/settings/settings.ts b/src/libs/settings/settings.ts index 7f660bc0..a8f90ca3 100644 --- a/src/libs/settings/settings.ts +++ b/src/libs/settings/settings.ts @@ -1,22 +1,24 @@ -import moment from 'moment'; import get from 'lodash/get'; import merge from 'lodash/merge'; import {configure} from '@gravity-ui/uikit'; import {i18nFactory} from '../../i18n'; import type {ChartKitPlugin, ChartKitLang, ChartKitHolidays} from '../../types'; +import {EventEmitter} from './eventEmitter'; interface Settings { plugins: ChartKitPlugin[]; lang: ChartKitLang; - locale?: moment.LocaleSpecification; extra?: { holidays?: ChartKitHolidays; }; } type SettingKey = keyof Settings; +type SettingsEventsMap = { + 'change-lang': ChartKitLang; +}; -export const DEFAULT_LOCALE_SPECIFICATION: moment.LocaleSpecification = {week: {dow: 1, doy: 7}}; +export const settingsEventEmitter = new EventEmitter(); const removeUndefinedValues = >(data: T) => { return Object.entries(data).reduce((acc, [key, value]) => { @@ -28,14 +30,7 @@ const removeUndefinedValues = >(data: T) => { }, {} as T); }; -const updateLocale = (args: {lang: ChartKitLang; locale?: moment.LocaleSpecification}) => { - const {lang, locale} = args; - - if (locale) { - moment.updateLocale(lang, locale); - } - - moment.locale(lang); +const updateLang = (lang: ChartKitLang) => { configure({lang}); i18nFactory.setLang(lang); }; @@ -46,6 +41,10 @@ class ChartKitSettings { lang: 'en', }; + constructor() { + updateLang(this.get('lang')); + } + get(key: T) { return get(this.settings, key); } @@ -53,16 +52,14 @@ class ChartKitSettings { set(updates: Partial) { const filteredUpdates = removeUndefinedValues(updates); - if (filteredUpdates.lang || filteredUpdates.locale) { + this.settings = merge(this.settings, filteredUpdates); + + if (filteredUpdates.lang) { const lang = filteredUpdates.lang || this.get('lang'); - const locale = filteredUpdates.locale || this.get('locale'); - updateLocale({lang, locale}); + updateLang(lang); + settingsEventEmitter.dispatch('change-lang', lang); } - - this.settings = merge(this.settings, filteredUpdates); } } export const settings = new ChartKitSettings(); - -settings.set({locale: DEFAULT_LOCALE_SPECIFICATION}); diff --git a/src/plugins/highcharts/renderer/components/HighchartsComponent.tsx b/src/plugins/highcharts/renderer/components/HighchartsComponent.tsx index 8708342c..ced0f529 100644 --- a/src/plugins/highcharts/renderer/components/HighchartsComponent.tsx +++ b/src/plugins/highcharts/renderer/components/HighchartsComponent.tsx @@ -4,10 +4,12 @@ import HighchartsReact from 'highcharts-react-official'; import get from 'lodash/get'; import type {ChartKitProps} from '../../../../types'; import {settings} from '../../../../libs'; +import {settingsEventEmitter} from '../../../../libs/settings/settings'; import {markChartPerformance, getChartPerformanceDuration, getRandomCKId} from '../../../../utils'; import type {HighchartsWidgetData, StringParams} from '../../types'; import {getGraph} from '../helpers/graph'; import {initHighchartsModules} from '../helpers/init-highcharts-modules'; +import {initHighchartsLangOptions} from '../helpers/highcharts/highcharts'; import {withSplitPane} from './withSplitPane/withSplitPane'; import './HighchartsComponent.scss'; @@ -23,6 +25,11 @@ type State = { isError: boolean; }; +settingsEventEmitter.on('change-lang', { + id: 'hc-lang-handler', + action: initHighchartsLangOptions, +}); + initHighchartsModules(); export class HighchartsComponent extends React.PureComponent { diff --git a/src/plugins/highcharts/renderer/helpers/config/config.js b/src/plugins/highcharts/renderer/helpers/config/config.js index e0f4c186..89568c85 100644 --- a/src/plugins/highcharts/renderer/helpers/config/config.js +++ b/src/plugins/highcharts/renderer/helpers/config/config.js @@ -11,7 +11,7 @@ import isNumber from 'lodash/isNumber'; import throttle from 'lodash/throttle'; import pick from 'lodash/pick'; import debounce from 'lodash/debounce'; -import moment from 'moment'; +import {dateTime} from '@gravity-ui/date-utils'; import {i18n} from '../../../../../i18n'; import {formatNumber} from '../../../../shared'; import { @@ -1412,7 +1412,9 @@ function drillOnClick(event, {options, chartType}) { chartType === 'scatter' ? drillDownFilter - 180 * 60 * 1000 : drillDownFilter; } - return isDateTime ? moment(drillDownFilter).format('YYYY-MM-DD') : drillDownFilter; + return isDateTime + ? dateTime({input: drillDownFilter}).format('YYYY-MM-DD') + : drillDownFilter; } return filter; diff --git a/src/plugins/highcharts/renderer/helpers/config/utils/setNavigatorDefaultPeriod.ts b/src/plugins/highcharts/renderer/helpers/config/utils/setNavigatorDefaultPeriod.ts index 8ee7779c..44c7c02c 100644 --- a/src/plugins/highcharts/renderer/helpers/config/utils/setNavigatorDefaultPeriod.ts +++ b/src/plugins/highcharts/renderer/helpers/config/utils/setNavigatorDefaultPeriod.ts @@ -1,4 +1,4 @@ -import moment from 'moment'; +import {dateTime} from '@gravity-ui/date-utils'; import type {Highcharts} from '../../../../types'; import type {NavigatorPeriod} from '../types'; import {getXAxisThresholdValue} from './getXAxisThresholdValue'; @@ -39,7 +39,7 @@ export const getDefaultPeriodInMS = ( return null; } - const minXValue = moment(maxXValue).subtract(value, period); + const minXValue = dateTime({input: maxXValue}).subtract(value, period); const range = maxXValue - minXValue.valueOf(); return { diff --git a/src/plugins/highcharts/renderer/helpers/highcharts/highcharts.js b/src/plugins/highcharts/renderer/helpers/highcharts/highcharts.js index ea441d75..b783e8d2 100644 --- a/src/plugins/highcharts/renderer/helpers/highcharts/highcharts.js +++ b/src/plugins/highcharts/renderer/helpers/highcharts/highcharts.js @@ -131,7 +131,7 @@ Highcharts.setOptions({ }, }); -function initHighcharts({isMobile}) { +function initHighchartsLangOptions() { Highcharts.setOptions({ lang: { resetZoom: '⟲', @@ -178,6 +178,10 @@ function initHighcharts({isMobile}) { thousandsSep: i18n('highcharts', 'thousands-sep'), }, }); +} + +function initHighcharts({isMobile}) { + initHighchartsLangOptions(); // https://github.com/highcharts/highcharts/issues/11494 (function (H) { @@ -428,4 +432,4 @@ function initHighchartsMap() { }); } -export {initHighcharts, initHighchartsMap}; +export {initHighcharts, initHighchartsMap, initHighchartsLangOptions}; diff --git a/src/plugins/highcharts/renderer/helpers/prepare-data.js b/src/plugins/highcharts/renderer/helpers/prepare-data.js index 2aaf1067..0467be7a 100644 --- a/src/plugins/highcharts/renderer/helpers/prepare-data.js +++ b/src/plugins/highcharts/renderer/helpers/prepare-data.js @@ -1,5 +1,5 @@ -import moment from 'moment'; import lodashMin from 'lodash/min'; +import {dateTime} from '@gravity-ui/date-utils'; import {i18n} from '../../../../i18n'; import {ChartKitError, CHARTKIT_ERROR_CODE} from '../../../../libs'; import {DEFAULT_LINES_LIMIT} from './constants'; @@ -80,9 +80,9 @@ function removeHolidays(data, options, holidays) { }); data.categories_ms.forEach((ts, i) => { - const datetime = moment(ts).format('YYYYMMDD'); + const key = dateTime({input: ts}).format('YYYYMMDD'); const region = (options.region && options.region.toLowerCase()) || 'tot'; - const holiday = holidays.holiday[region][datetime] || holidays.weekend[region][datetime]; + const holiday = holidays.holiday[region][key] || holidays.weekend[region][key]; if (!holiday) { timeline.push(ts); diff --git a/src/plugins/yagr/renderer/tooltip/renderTooltip.ts b/src/plugins/yagr/renderer/tooltip/renderTooltip.ts index 929d7719..df3d8168 100644 --- a/src/plugins/yagr/renderer/tooltip/renderTooltip.ts +++ b/src/plugins/yagr/renderer/tooltip/renderTooltip.ts @@ -1,4 +1,4 @@ -import moment from 'moment'; +import {dateTime} from '@gravity-ui/date-utils'; import type {TooltipRow, TooltipRenderOptions, ValueFormatter} from '../../types'; import type {TooltipData, TooltipLine} from './types'; import {formatTooltip} from './tooltip'; @@ -50,7 +50,7 @@ export const renderTooltip = (data: TooltipRenderOptions) => { const tooltipFormatOptions: TooltipData = { activeRowAlwaysFirstInTooltip: rows.length > 1, - tooltipHeader: moment(x / timeMultiplier).format('DD MMMM YYYY HH:mm:ss'), + tooltipHeader: dateTime({input: x / timeMultiplier}).format('DD MMMM YYYY HH:mm:ss'), shared: true, lines: rows.map( (row, i) => diff --git a/src/plugins/yagr/renderer/utils.ts b/src/plugins/yagr/renderer/utils.ts index 7c7fa1c8..ba0ee60b 100644 --- a/src/plugins/yagr/renderer/utils.ts +++ b/src/plugins/yagr/renderer/utils.ts @@ -1,5 +1,5 @@ -import moment from 'moment'; import merge from 'lodash/merge'; +import {dateTime} from '@gravity-ui/date-utils'; import {defaults} from '@gravity-ui/yagr'; import {settings} from '../../../libs'; import type {Yagr, YagrWidgetData, YagrTheme, YagrChartOptions, MinimalValidConfig} from '../types'; @@ -120,9 +120,9 @@ const getXAxisFormatter = (_: unknown, ticks: number[]) => { const range = (ticks[ticks.length - 1] - ticks[0]) / msm; return ticks.map((rawValue) => { - const d = moment(rawValue / msm); + const d = dateTime({input: rawValue / msm}); - if (d.hour() === 0 && d.minutes() === 0 && d.seconds() === 0) { + if (d.hour() === 0 && d.minute() === 0 && d.second() === 0) { return d.format('DD.MM.YY'); }