From 424544ad96c22f35203f698b3f4e61b96adaff3a Mon Sep 17 00:00:00 2001 From: Jean-Michel FRANCOIS Date: Fri, 8 Dec 2023 16:34:30 +0100 Subject: [PATCH] chore: upgrade date-fns to 2.x and fix (#4510) * chore :upgrade and see * chore: continue migration * fix: build faceted * fix: update tests and upgrade code * Add changeset --------- Co-authored-by: Alexandre Amalric --- .changeset/great-ears-pretend.md | 9 ++ packages/a11y/package.json | 4 +- packages/a11y/src/__mocks__/day-calendar.tsx | 7 +- packages/components/__mocks__/day-calendar.js | 4 +- packages/components/package.json | 3 +- .../DateTimePickers/Date/date-extraction.js | 11 +- .../Date/date-extraction.test.js | 4 +- .../DateRange/Manager/Manager.component.js | 2 +- .../DateTime/datetime-extraction.js | 12 ++- .../Manager/Manager.component.js | 4 +- .../InputDatePicker/DatePicker.stories.js | 4 +- .../DateTime/Manager/Manager.component.js | 2 +- .../DateTime/date-extraction.js | 39 ++++--- .../DateTime/date-extraction.test.js | 2 +- .../LegacyDateTimePickers/generator.js | 22 ++-- .../DatePicker/DatePicker.component.js | 27 ++--- .../pickers/DatePicker/DatePicker.test.js | 6 +- .../DateTimePicker.component.js | 6 +- .../DateTimePicker/DateTimePicker.test.js | 2 +- .../HeaderTitle/HeaderTitle.component.js | 18 ++-- .../src/DateTimePickers/generator.js | 21 ++-- .../CalendarPicker.component.js | 6 +- .../CalendarPicker/CalendarPicker.test.js | 2 +- .../DatePicker/DatePicker.component.js | 41 +++---- .../pickers/DatePicker/DatePicker.test.js | 6 +- .../HeaderTitle/HeaderTitle.component.js | 18 ++-- .../Resource/Resource.component.js | 14 +-- .../ResourceList/Resource/Resource.test.js | 4 +- .../CellDatetime/CellDatetime.component.js | 43 +++++--- .../CellDatetime/CellDatetime.test.js | 44 +++++--- .../src/i18n/DateFnsLocale/locale.js | 70 ++++-------- .../src/i18n/DateFnsLocale/locale.test.js | 101 ++++++++---------- packages/components/src/translate.js | 3 +- packages/design-system/package.json | 2 +- packages/faceted-search/package.json | 2 +- .../Badges/BadgeDate/BadgeDate.component.js | 8 +- .../BadgeDate/BadgeDateForm.component.js | 7 +- packages/utils/package.json | 4 +- packages/utils/src/date/date.test.ts | 7 +- packages/utils/src/date/generator.ts | 4 +- packages/utils/src/date/index.ts | 35 +++++- versions/dependencies.json | 2 +- yarn.lock | 55 +++++----- 43 files changed, 370 insertions(+), 317 deletions(-) create mode 100644 .changeset/great-ears-pretend.md diff --git a/.changeset/great-ears-pretend.md b/.changeset/great-ears-pretend.md new file mode 100644 index 0000000000..d9c1ccbdcc --- /dev/null +++ b/.changeset/great-ears-pretend.md @@ -0,0 +1,9 @@ +--- +'@talend/react-faceted-search': minor +'@talend/design-system': minor +'@talend/react-components': minor +'@talend/utils': minor +'@talend/react-a11y': minor +--- + +chore: upgrade date-fns to 2.x and fix diff --git a/packages/a11y/package.json b/packages/a11y/package.json index 7e3bd60ae0..3a8dec78c6 100644 --- a/packages/a11y/package.json +++ b/packages/a11y/package.json @@ -33,11 +33,11 @@ "@talend/scripts-config-typescript": "^11.2.0", "@testing-library/react": "^14.1.2", "@testing-library/user-event": "^14.5.1", - "@types/date-fns": "^0.0.2", + "@types/date-fns": "^2.6.0", "@types/jest": "^29.5.10", "@types/react": "^18.2.39", "@types/react-dom": "^18.2.17", - "date-fns": "^1.30.1", + "date-fns": "^2.30.0", "jest-in-case": "^1.0.2", "react": "^18.2.0", "react-dom": "^18.2.0" diff --git a/packages/a11y/src/__mocks__/day-calendar.tsx b/packages/a11y/src/__mocks__/day-calendar.tsx index f22350474d..13ff911bcd 100644 --- a/packages/a11y/src/__mocks__/day-calendar.tsx +++ b/packages/a11y/src/__mocks__/day-calendar.tsx @@ -1,8 +1,11 @@ /* eslint-disable import/no-extraneous-dependencies */ import { useRef } from 'react'; -import getDate from 'date-fns/get_date'; -import getMonth from 'date-fns/get_month'; + +import getDate from 'date-fns/getDate'; +import getMonth from 'date-fns/getMonth'; + import { date } from '@talend/utils'; + import { WithCalendarGestureInjectedProps } from '../Gesture/propTypes'; const buildWeeks = date.buildWeeks; diff --git a/packages/components/__mocks__/day-calendar.js b/packages/components/__mocks__/day-calendar.js index 80b507a331..4c94d1674a 100644 --- a/packages/components/__mocks__/day-calendar.js +++ b/packages/components/__mocks__/day-calendar.js @@ -1,7 +1,7 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import getDate from 'date-fns/get_date'; -import getMonth from 'date-fns/get_month'; +import getDate from 'date-fns/getDate'; +import getMonth from 'date-fns/getMonth'; import { buildWeeks } from '../src/DateTimePickers/generator'; class DayCalendar extends Component { diff --git a/packages/components/package.json b/packages/components/package.json index b4492909e0..46663518e8 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -44,7 +44,7 @@ "ally.js": "^1.4.1", "classnames": "^2.3.2", "d3": "^7.8.5", - "date-fns": "^1.30.1", + "date-fns": "^2.30.0", "dom-helpers": "^3.4.0", "focus-outline-manager": "^1.0.2", "immutable": "^3.8.2", @@ -88,7 +88,6 @@ "@testing-library/react-hooks": "^8.0.1", "@types/classnames": "^2.3.1", "@types/d3": "^7.4.3", - "@types/date-fns": "^0.0.2", "@types/jest": "^29.5.10", "@types/lodash": "^4.14.202", "@types/prop-types": "^15.7.11", diff --git a/packages/components/src/DateTimePickers/Date/date-extraction.js b/packages/components/src/DateTimePickers/Date/date-extraction.js index 58c1cecccb..917ea5d999 100644 --- a/packages/components/src/DateTimePickers/Date/date-extraction.js +++ b/packages/components/src/DateTimePickers/Date/date-extraction.js @@ -1,7 +1,8 @@ import format from 'date-fns/format'; -import getDate from 'date-fns/get_date'; -import lastDayOfMonth from 'date-fns/last_day_of_month'; -import setDate from 'date-fns/set_date'; +import getDate from 'date-fns/getDate'; +import lastDayOfMonth from 'date-fns/lastDayOfMonth'; +import setDate from 'date-fns/setDate'; + import { date as dateUtils } from '@talend/utils'; import getErrorMessage from '../shared/error-messages'; @@ -47,7 +48,7 @@ function isDateValid(date, options) { * @param {Object} options */ function dateToStr(date, { dateFormat }) { - return format(date, dateFormat); + return format(date, dateUtils.formatToUnicode(dateFormat)); } function convertDateToTimezone(date, { useUTC, timezone }) { @@ -248,7 +249,7 @@ function extractFromDate(date, options) { return { localDate: date, date: convertDateToTimezone(date, options), - textInput: format(date, options.dateFormat), + textInput: format(date, dateUtils.formatToUnicode(options.dateFormat)), errors: [], errorMessage: null, }; diff --git a/packages/components/src/DateTimePickers/Date/date-extraction.test.js b/packages/components/src/DateTimePickers/Date/date-extraction.test.js index e012616377..96c09b9f3d 100644 --- a/packages/components/src/DateTimePickers/Date/date-extraction.test.js +++ b/packages/components/src/DateTimePickers/Date/date-extraction.test.js @@ -1,5 +1,5 @@ -import isAfter from 'date-fns/is_after'; -import subHours from 'date-fns/sub_hours'; +import isAfter from 'date-fns/isAfter'; +import subHours from 'date-fns/subHours'; import { checkSupportedDateFormat, checkSupportedTimezone, diff --git a/packages/components/src/DateTimePickers/DateRange/Manager/Manager.component.js b/packages/components/src/DateTimePickers/DateRange/Manager/Manager.component.js index c88056f72e..2a0fcb14a9 100644 --- a/packages/components/src/DateTimePickers/DateRange/Manager/Manager.component.js +++ b/packages/components/src/DateTimePickers/DateRange/Manager/Manager.component.js @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; -import isBefore from 'date-fns/is_before'; +import isBefore from 'date-fns/isBefore'; import { DateRangeContext } from '../Context'; import getErrorMessage from '../../shared/error-messages'; diff --git a/packages/components/src/DateTimePickers/DateTime/datetime-extraction.js b/packages/components/src/DateTimePickers/DateTime/datetime-extraction.js index 1ae823c619..cf5a0b59b5 100644 --- a/packages/components/src/DateTimePickers/DateTime/datetime-extraction.js +++ b/packages/components/src/DateTimePickers/DateTime/datetime-extraction.js @@ -1,10 +1,11 @@ import format from 'date-fns/format'; -import setSeconds from 'date-fns/set_seconds'; +import setSeconds from 'date-fns/setSeconds'; + import { date as dateUtils } from '@talend/utils'; -import getErrorMessage from '../shared/error-messages'; import { convertDateToTimezone, extractDateOnly } from '../Date/date-extraction'; -import { checkTime, pad, timeToStr, strToTime } from '../Time/time-extraction'; +import getErrorMessage from '../shared/error-messages'; +import { checkTime, pad, strToTime, timeToStr } from '../Time/time-extraction'; const INTERNAL_INVALID_DATE = new Date('INTERNAL_INVALID_DATE'); @@ -85,7 +86,8 @@ function dateAndTimeToDateTime(date, time, options) { } function dateAndTimeToStr(date = '', time = '', options) { - const dateStr = date instanceof Date ? format(date, options.dateFormat) : date; + const dateStr = + date instanceof Date ? format(date, dateUtils.formatToUnicode(options.dateFormat)) : date; const timeStr = typeof time === 'string' ? time : timeToStr(time, options.useSeconds); return `${dateStr} ${timeStr}`.trim(); @@ -147,7 +149,7 @@ function extractPartsFromTextInput(textInput, options) { const splitMatches = textInput.split(/\s/); date = splitMatches[0]; time = splitMatches[1]; - datetime = dateAndTimeToDateTime(date, time, options); + datetime = dateAndTimeToDateTime(new Date(date), time, options); } catch (error) { datetime = INTERNAL_INVALID_DATE; errors = [error]; diff --git a/packages/components/src/DateTimePickers/DateTimeRange/Manager/Manager.component.js b/packages/components/src/DateTimePickers/DateTimeRange/Manager/Manager.component.js index d1314f18fc..fe88b8b3f2 100644 --- a/packages/components/src/DateTimePickers/DateTimeRange/Manager/Manager.component.js +++ b/packages/components/src/DateTimePickers/DateTimeRange/Manager/Manager.component.js @@ -1,7 +1,7 @@ import { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; -import isBefore from 'date-fns/is_before'; -import isEqual from 'date-fns/is_equal'; +import isBefore from 'date-fns/isBefore'; +import isEqual from 'date-fns/isEqual'; import { DateTimeRangeContext } from '../Context'; import getErrorMessage from '../../shared/error-messages'; diff --git a/packages/components/src/DateTimePickers/InputDatePicker/DatePicker.stories.js b/packages/components/src/DateTimePickers/InputDatePicker/DatePicker.stories.js index 8443b8dc96..ce67bbcc2e 100644 --- a/packages/components/src/DateTimePickers/InputDatePicker/DatePicker.stories.js +++ b/packages/components/src/DateTimePickers/InputDatePicker/DatePicker.stories.js @@ -1,6 +1,6 @@ import { action } from '@storybook/addon-actions'; -import isBefore from 'date-fns/is_before'; -import startOfDay from 'date-fns/start_of_day'; +import isBefore from 'date-fns/isBefore'; +import startOfDay from 'date-fns/startOfDay'; import InputDatePicker from './InputDatePicker.component'; diff --git a/packages/components/src/DateTimePickers/LegacyDateTimePickers/DateTime/Manager/Manager.component.js b/packages/components/src/DateTimePickers/LegacyDateTimePickers/DateTime/Manager/Manager.component.js index a502ed595c..20bcdddd69 100644 --- a/packages/components/src/DateTimePickers/LegacyDateTimePickers/DateTime/Manager/Manager.component.js +++ b/packages/components/src/DateTimePickers/LegacyDateTimePickers/DateTime/Manager/Manager.component.js @@ -1,6 +1,6 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import isSameSecond from 'date-fns/is_same_second'; +import isSameSecond from 'date-fns/isSameSecond'; import { DateTimeContext } from '../Context'; import { diff --git a/packages/components/src/DateTimePickers/LegacyDateTimePickers/DateTime/date-extraction.js b/packages/components/src/DateTimePickers/LegacyDateTimePickers/DateTime/date-extraction.js index ffc9757cf3..cd38e76420 100644 --- a/packages/components/src/DateTimePickers/LegacyDateTimePickers/DateTime/date-extraction.js +++ b/packages/components/src/DateTimePickers/LegacyDateTimePickers/DateTime/date-extraction.js @@ -1,9 +1,10 @@ import format from 'date-fns/format'; -import getDate from 'date-fns/get_date'; -import lastDayOfMonth from 'date-fns/last_day_of_month'; -import setSeconds from 'date-fns/set_seconds'; -import setDate from 'date-fns/set_date'; -import startOfSecond from 'date-fns/start_of_second'; +import getDate from 'date-fns/getDate'; +import lastDayOfMonth from 'date-fns/lastDayOfMonth'; +import setDate from 'date-fns/setDate'; +import setSeconds from 'date-fns/setSeconds'; +import startOfSecond from 'date-fns/startOfSecond'; + import { date as dateUtils } from '@talend/utils'; import getErrorMessage from './error-messages'; @@ -249,27 +250,35 @@ function dateTimeToStr(date, time, options) { const { dateFormat, useTime } = options; if (time === undefined || useTime === false) { - return format(date, dateFormat); + return format(date, dateUtils.formatToUnicode(dateFormat)); } + const typedDate = date instanceof Date ? date : new Date(date); try { const timeInSeconds = timeToSeconds(hours, minutes, seconds, options); - const fullDate = setSeconds(date, timeInSeconds); + const fullDate = setSeconds(typedDate, timeInSeconds); if (hybridMode && isTimeEmpty(time)) { - return format(fullDate, getFullDateFormat({ ...options, useTime: false })); + return format( + fullDate, + dateUtils.formatToUnicode(getFullDateFormat({ ...options, useTime: false })), + ); } - return format(fullDate, getFullDateFormat(options)); + return format(fullDate, dateUtils.formatToUnicode(getFullDateFormat(options))); } catch (e) { - const dateStr = format(date, dateFormat); - if (hours !== '' && minutes !== '') { - if (options.useSeconds && seconds !== '') { - return `${dateStr} ${hours}:${minutes}:${seconds}`; + try { + const dateStr = format(typedDate, dateUtils.formatToUnicode(dateFormat)); + if (hours !== '' && minutes !== '') { + if (options.useSeconds && seconds !== '') { + return `${dateStr} ${hours}:${minutes}:${seconds}`; + } + return `${dateStr} ${hours}:${minutes}`; } - return `${dateStr} ${hours}:${minutes}`; + return dateStr; + } catch (_) { + return 'Invalid Date'; } - return dateStr; } } diff --git a/packages/components/src/DateTimePickers/LegacyDateTimePickers/DateTime/date-extraction.test.js b/packages/components/src/DateTimePickers/LegacyDateTimePickers/DateTime/date-extraction.test.js index e1ba81c13e..b0e1e004d0 100644 --- a/packages/components/src/DateTimePickers/LegacyDateTimePickers/DateTime/date-extraction.test.js +++ b/packages/components/src/DateTimePickers/LegacyDateTimePickers/DateTime/date-extraction.test.js @@ -1,11 +1,11 @@ import { + check, checkSupportedDateFormat, extractParts, extractPartsFromDateAndTime, extractPartsFromDateTime, extractPartsFromTextInput, getFullDateFormat, - check, } from './date-extraction'; describe('Date extraction', () => { diff --git a/packages/components/src/DateTimePickers/LegacyDateTimePickers/generator.js b/packages/components/src/DateTimePickers/LegacyDateTimePickers/generator.js index e6d4b865b7..819b83e331 100644 --- a/packages/components/src/DateTimePickers/LegacyDateTimePickers/generator.js +++ b/packages/components/src/DateTimePickers/LegacyDateTimePickers/generator.js @@ -1,16 +1,16 @@ -import addDays from 'date-fns/add_days'; -import chunk from 'lodash/chunk'; -import addMonths from 'date-fns/add_months'; +import addDays from 'date-fns/addDays'; +import addMonths from 'date-fns/addMonths'; import format from 'date-fns/format'; -import getYear from 'date-fns/get_year'; -import setDay from 'date-fns/set_day'; -import startOfWeek from 'date-fns/start_of_week'; +import getYear from 'date-fns/getYear'; +import setDay from 'date-fns/setDay'; +import startOfWeek from 'date-fns/startOfWeek'; +import chunk from 'lodash/chunk'; import memoize from 'lodash/memoize'; + import getLocale from '../../i18n/DateFnsLocale/locale'; -import getDefaultT from '../../translate'; -function buildDateFnsLocale(t) { - return { locale: getLocale(t || getDefaultT()) }; +function buildDateFnsLocale() { + return { locale: getLocale() }; } export const getPickerLocale = memoize(buildDateFnsLocale); @@ -25,8 +25,8 @@ export function buildDayNames(firstDayOfweek = 1, t) { .map((_, i) => (i + firstDayOfweek) % 7) .map(dayOfWeek => setDay(new Date(0), dayOfWeek)) .map(headerDate => ({ - abbr: format(headerDate, 'd', pickerLocale), - full: format(headerDate, 'dddd', pickerLocale), + abbr: format(headerDate, 'EEEEE', pickerLocale), + full: format(headerDate, 'EEEE', pickerLocale), })); } diff --git a/packages/components/src/DateTimePickers/LegacyDateTimePickers/pickers/DatePicker/DatePicker.component.js b/packages/components/src/DateTimePickers/LegacyDateTimePickers/pickers/DatePicker/DatePicker.component.js index 461ce1c655..695107fd48 100644 --- a/packages/components/src/DateTimePickers/LegacyDateTimePickers/pickers/DatePicker/DatePicker.component.js +++ b/packages/components/src/DateTimePickers/LegacyDateTimePickers/pickers/DatePicker/DatePicker.component.js @@ -1,20 +1,23 @@ import { PureComponent } from 'react'; -import PropTypes from 'prop-types'; + import classNames from 'classnames'; -import memoize from 'lodash/memoize'; -import isToday from 'date-fns/is_today'; -import getDate from 'date-fns/get_date'; -import getMonth from 'date-fns/get_month'; -import getYear from 'date-fns/get_year'; -import isSameDay from 'date-fns/is_same_day'; -import setMonth from 'date-fns/set_month'; import format from 'date-fns/format'; -import startOfMonth from 'date-fns/start_of_month'; +import getDate from 'date-fns/getDate'; +import getMonth from 'date-fns/getMonth'; +import getYear from 'date-fns/getYear'; +import isSameDay from 'date-fns/isSameDay'; +import isToday from 'date-fns/isToday'; +import setMonth from 'date-fns/setMonth'; +import startOfMonth from 'date-fns/startOfMonth'; +import memoize from 'lodash/memoize'; +import PropTypes from 'prop-types'; -import theme from './DatePicker.module.scss'; -import { buildDayNames, buildWeeks, getPickerLocale } from '../../generator'; import { Gesture } from '@talend/react-a11y'; + import getDefaultT from '../../../../translate'; +import { buildDayNames, buildWeeks, getPickerLocale } from '../../generator'; + +import theme from './DatePicker.module.scss'; const getDayNames = memoize(buildDayNames); @@ -124,7 +127,7 @@ class DatePicker extends PureComponent { 'btn-default', ); - let ariaLabel = format(date, 'dddd DD MMMM YYYY', pickerLocale); + let ariaLabel = format(date, 'EEEE dd MMMM yyyy', pickerLocale); const tdProps = { key: j, className: theme['calendar-col'], diff --git a/packages/components/src/DateTimePickers/LegacyDateTimePickers/pickers/DatePicker/DatePicker.test.js b/packages/components/src/DateTimePickers/LegacyDateTimePickers/pickers/DatePicker/DatePicker.test.js index 9ff77d0eff..8e890f646f 100644 --- a/packages/components/src/DateTimePickers/LegacyDateTimePickers/pickers/DatePicker/DatePicker.test.js +++ b/packages/components/src/DateTimePickers/LegacyDateTimePickers/pickers/DatePicker/DatePicker.test.js @@ -1,11 +1,11 @@ import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import isSameDay from 'date-fns/is_same_day'; -import isToday from 'date-fns/is_today'; +import isSameDay from 'date-fns/isSameDay'; +import isToday from 'date-fns/isToday'; import DatePicker from './DatePicker.component'; -jest.mock('date-fns/is_today'); +jest.mock('date-fns/isToday'); function mockIsTodayWith(newToday) { isToday.mockImplementation(date => isSameDay(date, newToday)); diff --git a/packages/components/src/DateTimePickers/LegacyDateTimePickers/pickers/DateTimePicker/DateTimePicker.component.js b/packages/components/src/DateTimePickers/LegacyDateTimePickers/pickers/DateTimePicker/DateTimePicker.component.js index d098aceb8b..c724d9d798 100644 --- a/packages/components/src/DateTimePickers/LegacyDateTimePickers/pickers/DateTimePicker/DateTimePicker.component.js +++ b/packages/components/src/DateTimePickers/LegacyDateTimePickers/pickers/DateTimePicker/DateTimePicker.component.js @@ -1,8 +1,8 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import getMonth from 'date-fns/get_month'; -import getYear from 'date-fns/get_year'; -import startOfDay from 'date-fns/start_of_day'; +import getMonth from 'date-fns/getMonth'; +import getYear from 'date-fns/getYear'; +import startOfDay from 'date-fns/startOfDay'; import classNames from 'classnames'; import theme from './DateTimePicker.module.scss'; diff --git a/packages/components/src/DateTimePickers/LegacyDateTimePickers/pickers/DateTimePicker/DateTimePicker.test.js b/packages/components/src/DateTimePickers/LegacyDateTimePickers/pickers/DateTimePicker/DateTimePicker.test.js index d4654a5cf9..8aab083cd4 100644 --- a/packages/components/src/DateTimePickers/LegacyDateTimePickers/pickers/DateTimePicker/DateTimePicker.test.js +++ b/packages/components/src/DateTimePickers/LegacyDateTimePickers/pickers/DateTimePicker/DateTimePicker.test.js @@ -1,7 +1,7 @@ /* eslint-disable testing-library/no-container */ import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import startOfDay from 'date-fns/start_of_day'; +import startOfDay from 'date-fns/startOfDay'; import dateMock from '../../../../../../../mocks/dateMock'; import DateTimePicker from './DateTimePicker.component'; diff --git a/packages/components/src/DateTimePickers/LegacyDateTimePickers/views/HeaderTitle/HeaderTitle.component.js b/packages/components/src/DateTimePickers/LegacyDateTimePickers/views/HeaderTitle/HeaderTitle.component.js index 1eb36d9269..b93c9126b9 100644 --- a/packages/components/src/DateTimePickers/LegacyDateTimePickers/views/HeaderTitle/HeaderTitle.component.js +++ b/packages/components/src/DateTimePickers/LegacyDateTimePickers/views/HeaderTitle/HeaderTitle.component.js @@ -1,13 +1,15 @@ -import PropTypes from 'prop-types'; import classNames from 'classnames'; -import setYear from 'date-fns/set_year'; -import setMonth from 'date-fns/set_month'; import format from 'date-fns/format'; -import theme from './HeaderTitle.module.scss'; +import setMonth from 'date-fns/setMonth'; +import setYear from 'date-fns/setYear'; +import PropTypes from 'prop-types'; + +import { Action, ActionDropdown } from '../../../../Actions'; import { getPickerLocale } from '../../generator'; -import { ActionDropdown, Action } from '../../../../Actions'; import YearPicker from '../../pickers/YearPicker'; +import theme from './HeaderTitle.module.scss'; + function HeaderTitle(props) { const isButton = !!props.button; const className = classNames( @@ -21,10 +23,10 @@ function HeaderTitle(props) { ...(isButton ? props.button : {}), }; - const pickerLocale = getPickerLocale(props.t); + const pickerLocale = getPickerLocale(); const date = setYear(setMonth(new Date(0), props.monthIndex), props.year); - const label = format(date, 'MMMM YYYY', pickerLocale); - const yearLabel = format(date, 'YYYY', pickerLocale); + const label = format(date, 'MMMM yyyy', pickerLocale); + const yearLabel = format(date, 'yyyy', pickerLocale); const monthLabel = format(date, 'MMMM', pickerLocale); if (isButton) { diff --git a/packages/components/src/DateTimePickers/generator.js b/packages/components/src/DateTimePickers/generator.js index 8e15a0de9a..f375516312 100644 --- a/packages/components/src/DateTimePickers/generator.js +++ b/packages/components/src/DateTimePickers/generator.js @@ -1,15 +1,16 @@ -import { date } from '@talend/utils'; -import chunk from 'lodash/chunk'; -import addMonths from 'date-fns/add_months'; +import addMonths from 'date-fns/addMonths'; import format from 'date-fns/format'; -import getYear from 'date-fns/get_year'; -import setDay from 'date-fns/set_day'; +import getYear from 'date-fns/getYear'; +import setDay from 'date-fns/setDay'; +import chunk from 'lodash/chunk'; import memoize from 'lodash/memoize'; + +import { date } from '@talend/utils'; + import getLocale from '../i18n/DateFnsLocale/locale'; -import getDefaultT from '../translate'; -function buildDateFnsLocale(t) { - return { locale: getLocale(t || getDefaultT()) }; +function buildDateFnsLocale() { + return { locale: getLocale() }; } export const getPickerLocale = memoize(buildDateFnsLocale); @@ -24,8 +25,8 @@ export function buildDayNames(firstDayOfweek = 1, t) { .map((_, i) => (i + firstDayOfweek) % 7) .map(dayOfWeek => setDay(new Date(0), dayOfWeek)) .map(headerDate => ({ - abbr: format(headerDate, 'd', pickerLocale), - full: format(headerDate, 'dddd', pickerLocale), + abbr: format(headerDate, 'EEEEE', pickerLocale), + full: format(headerDate, 'EEEE', pickerLocale), })); } diff --git a/packages/components/src/DateTimePickers/pickers/CalendarPicker/CalendarPicker.component.js b/packages/components/src/DateTimePickers/pickers/CalendarPicker/CalendarPicker.component.js index edfcb7d158..1dd62bef38 100644 --- a/packages/components/src/DateTimePickers/pickers/CalendarPicker/CalendarPicker.component.js +++ b/packages/components/src/DateTimePickers/pickers/CalendarPicker/CalendarPicker.component.js @@ -1,8 +1,8 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import getMonth from 'date-fns/get_month'; -import getYear from 'date-fns/get_year'; -import startOfDay from 'date-fns/start_of_day'; +import getMonth from 'date-fns/getMonth'; +import getYear from 'date-fns/getYear'; +import startOfDay from 'date-fns/startOfDay'; import classNames from 'classnames'; import theme from './CalendarPicker.module.scss'; diff --git a/packages/components/src/DateTimePickers/pickers/CalendarPicker/CalendarPicker.test.js b/packages/components/src/DateTimePickers/pickers/CalendarPicker/CalendarPicker.test.js index b0f4b776f0..bf241e9243 100644 --- a/packages/components/src/DateTimePickers/pickers/CalendarPicker/CalendarPicker.test.js +++ b/packages/components/src/DateTimePickers/pickers/CalendarPicker/CalendarPicker.test.js @@ -4,7 +4,7 @@ // rewrite using rtl import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import startOfDay from 'date-fns/start_of_day'; +import startOfDay from 'date-fns/startOfDay'; import dateMock from '../../../../../../mocks/dateMock'; import CalendarPicker from './CalendarPicker.component'; diff --git a/packages/components/src/DateTimePickers/pickers/DatePicker/DatePicker.component.js b/packages/components/src/DateTimePickers/pickers/DatePicker/DatePicker.component.js index bc16e4826f..5b99592f0c 100644 --- a/packages/components/src/DateTimePickers/pickers/DatePicker/DatePicker.component.js +++ b/packages/components/src/DateTimePickers/pickers/DatePicker/DatePicker.component.js @@ -1,24 +1,27 @@ import { PureComponent } from 'react'; -import PropTypes from 'prop-types'; + import classNames from 'classnames'; -import memoize from 'lodash/memoize'; -import isAfter from 'date-fns/is_after'; -import isBefore from 'date-fns/is_before'; -import isSameDay from 'date-fns/is_same_day'; -import isToday from 'date-fns/is_today'; -import isWithinRange from 'date-fns/is_within_range'; import format from 'date-fns/format'; -import getDate from 'date-fns/get_date'; -import getMonth from 'date-fns/get_month'; -import getYear from 'date-fns/get_year'; -import setMonth from 'date-fns/set_month'; -import startOfDay from 'date-fns/start_of_day'; -import startOfMonth from 'date-fns/start_of_month'; +import getDate from 'date-fns/getDate'; +import getMonth from 'date-fns/getMonth'; +import getYear from 'date-fns/getYear'; +import isAfter from 'date-fns/isAfter'; +import isBefore from 'date-fns/isBefore'; +import isSameDay from 'date-fns/isSameDay'; +import isToday from 'date-fns/isToday'; +import isWithinRange from 'date-fns/isWithinInterval'; +import setMonth from 'date-fns/setMonth'; +import startOfDay from 'date-fns/startOfDay'; +import startOfMonth from 'date-fns/startOfMonth'; +import memoize from 'lodash/memoize'; +import PropTypes from 'prop-types'; -import theme from './DatePicker.module.scss'; -import { buildDayNames, buildWeeks, getPickerLocale } from '../../generator'; import { Gesture } from '@talend/react-a11y'; + import getDefaultT from '../../../translate'; +import { buildDayNames, buildWeeks, getPickerLocale } from '../../generator'; + +import theme from './DatePicker.module.scss'; const getDayNames = memoize(buildDayNames); @@ -65,15 +68,15 @@ class DatePicker extends PureComponent { const { calendar } = this.props; const { year, monthIndex } = calendar; const weeks = this.getWeeks(year, monthIndex, 1); - return isWithinRange(date, weeks[0][0], weeks[5][6]); + return isWithinRange(date, { start: weeks[0][0], end: weeks[5][6] }); } isDateWithinRange(date) { const { selectedDate, startDate, endDate } = this.props; if (startDate && isAfter(selectedDate, startDate)) { - return isWithinRange(date, startOfDay(startDate), selectedDate); + return isWithinRange(date, { start: startOfDay(startDate), end: selectedDate }); } else if (endDate && isBefore(selectedDate, endDate)) { - return isWithinRange(date, selectedDate, endDate); + return isWithinRange(date, { start: selectedDate, end: endDate }); } return false; } @@ -184,7 +187,7 @@ class DatePicker extends PureComponent { className: classNames(theme['calendar-col'], cellTheme), }; - let ariaLabel = format(date, 'dddd DD MMMM YYYY', pickerLocale); + let ariaLabel = format(date, 'EEEE dd MMMM yyyy', pickerLocale); if (isInRange) { if (isStart) { ariaLabel = t('DATEPICKER_DAY_RANGE_START', { diff --git a/packages/components/src/DateTimePickers/pickers/DatePicker/DatePicker.test.js b/packages/components/src/DateTimePickers/pickers/DatePicker/DatePicker.test.js index 9fb9597680..3bb135b7ce 100644 --- a/packages/components/src/DateTimePickers/pickers/DatePicker/DatePicker.test.js +++ b/packages/components/src/DateTimePickers/pickers/DatePicker/DatePicker.test.js @@ -1,11 +1,11 @@ import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import isSameDay from 'date-fns/is_same_day'; -import isToday from 'date-fns/is_today'; +import isSameDay from 'date-fns/isSameDay'; +import isToday from 'date-fns/isToday'; import DatePicker from './DatePicker.component'; -jest.mock('date-fns/is_today'); +jest.mock('date-fns/isToday'); function mockIsTodayWith(newToday) { isToday.mockImplementation(date => isSameDay(date, newToday)); diff --git a/packages/components/src/DateTimePickers/views/HeaderTitle/HeaderTitle.component.js b/packages/components/src/DateTimePickers/views/HeaderTitle/HeaderTitle.component.js index ecda5142e7..948fcafa7c 100644 --- a/packages/components/src/DateTimePickers/views/HeaderTitle/HeaderTitle.component.js +++ b/packages/components/src/DateTimePickers/views/HeaderTitle/HeaderTitle.component.js @@ -1,13 +1,15 @@ -import PropTypes from 'prop-types'; import classNames from 'classnames'; -import setYear from 'date-fns/set_year'; -import setMonth from 'date-fns/set_month'; import format from 'date-fns/format'; -import theme from './HeaderTitle.module.scss'; +import setMonth from 'date-fns/setMonth'; +import setYear from 'date-fns/setYear'; +import PropTypes from 'prop-types'; + +import { Action, ActionDropdown } from '../../../Actions'; import { getPickerLocale } from '../../generator'; -import { ActionDropdown, Action } from '../../../Actions'; import YearPicker from '../../pickers/YearPicker'; +import theme from './HeaderTitle.module.scss'; + function HeaderTitle(props) { const isButton = !!props.button; const className = classNames( @@ -21,10 +23,10 @@ function HeaderTitle(props) { ...(isButton ? props.button : {}), }; - const pickerLocale = getPickerLocale(props.t); + const pickerLocale = getPickerLocale(); const date = setYear(setMonth(new Date(0), props.monthIndex), props.year); - const label = format(date, 'MMMM YYYY', pickerLocale); - const yearLabel = format(date, 'YYYY', pickerLocale); + const label = format(date, 'MMMM yyyy', pickerLocale); + const yearLabel = format(date, 'yyyy', pickerLocale); const monthLabel = format(date, 'MMMM', pickerLocale); if (isButton) { diff --git a/packages/components/src/ResourceList/Resource/Resource.component.js b/packages/components/src/ResourceList/Resource/Resource.component.js index 7ed7143a15..964524a90e 100644 --- a/packages/components/src/ResourceList/Resource/Resource.component.js +++ b/packages/components/src/ResourceList/Resource/Resource.component.js @@ -1,13 +1,15 @@ import { Fragment } from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import distanceInWordsToNow from 'date-fns/distance_in_words_to_now'; import { withTranslation } from 'react-i18next'; -import { getRowData } from '../../VirtualizedList/utils/gridrow'; + +import classNames from 'classnames'; +import formatDistanceToNow from 'date-fns/formatDistanceToNow'; +import PropTypes from 'prop-types'; + import I18N_DOMAIN_COMPONENTS from '../../constants'; -import getDefaultT from '../../translate'; import getLocale from '../../i18n/DateFnsLocale/locale'; import Icon from '../../Icon'; +import getDefaultT from '../../translate'; +import { getRowData } from '../../VirtualizedList/utils/gridrow'; import theme from './Resource.module.scss'; @@ -17,7 +19,7 @@ const FLAGS = { }; function getDateLabel(t, date) { - return distanceInWordsToNow(date, { + return formatDistanceToNow(new Date(date), { addSuffix: true, locale: getLocale(t), }); diff --git a/packages/components/src/ResourceList/Resource/Resource.test.js b/packages/components/src/ResourceList/Resource/Resource.test.js index 60ed2289a6..b56e4fea36 100644 --- a/packages/components/src/ResourceList/Resource/Resource.test.js +++ b/packages/components/src/ResourceList/Resource/Resource.test.js @@ -1,11 +1,11 @@ import { render } from '@testing-library/react'; import Resource from './Resource.component'; -jest.mock('date-fns/distance_in_words_to_now', () => () => 'over 2 years ago'); +jest.mock('date-fns/formatDistanceToNow', () => () => 'over 2 years ago'); describe('Resource component snaps', () => { afterAll(() => { - jest.unmock('date-fns/distance_in_words_to_now'); + jest.unmock('date-fns/formatDistanceToNow'); }); describe('renderers', () => { diff --git a/packages/components/src/VirtualizedList/CellDatetime/CellDatetime.component.js b/packages/components/src/VirtualizedList/CellDatetime/CellDatetime.component.js index f05458b6bb..d26c93ab66 100644 --- a/packages/components/src/VirtualizedList/CellDatetime/CellDatetime.component.js +++ b/packages/components/src/VirtualizedList/CellDatetime/CellDatetime.component.js @@ -1,40 +1,47 @@ -import PropTypes from 'prop-types'; import { Component } from 'react'; +import { withTranslation } from 'react-i18next'; + import classnames from 'classnames'; +import format from 'date-fns/format'; +import formatDistanceToNow from 'date-fns/formatDistanceToNow'; +import isValid from 'date-fns/isValid'; +import parseISO from 'date-fns/parseISO'; import isEqual from 'lodash/isEqual'; import pick from 'lodash/pick'; -import distanceInWordsToNow from 'date-fns/distance_in_words_to_now'; -import format from 'date-fns/format'; -import isValid from 'date-fns/is_valid'; -import parse from 'date-fns/parse'; -import { withTranslation } from 'react-i18next'; +import PropTypes from 'prop-types'; + import { date as dateUtils } from '@talend/utils'; import I18N_DOMAIN_COMPONENTS from '../../constants'; -import getDefaultT from '../../translate'; import getLocale from '../../i18n/DateFnsLocale/locale'; -import styles from './CellDatetime.module.scss'; import TooltipTrigger from '../../TooltipTrigger'; +import getDefaultT from '../../translate'; + +import styles from './CellDatetime.module.scss'; const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; export function computeValue(cellData, columnData, t) { - const isDateValid = isValid(parse(cellData)); + const date = new Date(cellData); + const isDateValid = isValid(date); if (isDateValid) { - if (cellData && columnData.mode === 'ago') { - return distanceInWordsToNow(cellData, { + const dateFNS = parseISO(date.toISOString()); + if (dateFNS && columnData.mode === 'ago') { + return formatDistanceToNow(dateFNS, { addSuffix: true, locale: getLocale(t), }); } else if (columnData.mode === 'format') { if (columnData.timeZone) { - return dateUtils.formatToTimeZone(cellData, columnData.pattern || DATE_TIME_FORMAT, { + return dateUtils.formatToTimeZone(dateFNS, columnData.pattern || DATE_TIME_FORMAT, { timeZone: columnData.timeZone, locale: getLocale(t), }); } - return format(cellData, columnData.pattern || DATE_TIME_FORMAT, { locale: getLocale(t) }); + return format(dateFNS, dateUtils.formatToUnicode(columnData.pattern || DATE_TIME_FORMAT), { + locale: getLocale(t), + }); } } @@ -53,9 +60,13 @@ export function getTooltipLabel(cellData, columnData, t) { locale: getLocale(t), }); } else { - tooltipLabel = format(cellData, columnData.pattern || DATE_TIME_FORMAT, { - locale: getLocale(t), - }); + tooltipLabel = format( + cellData, + dateUtils.formatToUnicode(columnData.pattern || DATE_TIME_FORMAT), + { + locale: getLocale(t), + }, + ); } return tooltipLabel; } diff --git a/packages/components/src/VirtualizedList/CellDatetime/CellDatetime.test.js b/packages/components/src/VirtualizedList/CellDatetime/CellDatetime.test.js index 43581196f6..d4e1a2ae66 100644 --- a/packages/components/src/VirtualizedList/CellDatetime/CellDatetime.test.js +++ b/packages/components/src/VirtualizedList/CellDatetime/CellDatetime.test.js @@ -1,17 +1,19 @@ /* eslint-disable react/prop-types */ + /* eslint-disable react/display-name */ import { render, screen } from '@testing-library/react'; -import distanceInWordsToNow from 'date-fns/distance_in_words_to_now'; import format from 'date-fns/format'; +import distanceInWordsToNow from 'date-fns/formatDistanceToNow'; + import { date as dateUtils } from '@talend/utils'; -import { computeValue, CellDatetimeComponent } from './CellDatetime.component'; -import getDefaultT from '../../translate'; import getLocale from '../../i18n/DateFnsLocale/locale'; +import getDefaultT from '../../translate'; +import { CellDatetimeComponent, computeValue } from './CellDatetime.component'; jest.mock('../../i18n/DateFnsLocale/locale'); -jest.mock('date-fns/distance_in_words_to_now', () => ({ +jest.mock('date-fns/formatDistanceToNow', () => ({ __esModule: true, default: jest.fn(() => 'about 1 month ago'), })); @@ -49,10 +51,14 @@ describe('CellDatetime', () => { afterAll(() => { jest.unmock('../../i18n/DateFnsLocale/locale'); - jest.unmock('date-fns/distance_in_words_to_now'); + jest.unmock('date-fns/formatDistanceToNow'); jest.unmock('date-fns/format'); }); + afterEach(() => { + jest.clearAllMocks(); + }); + it('should render CellDatetime', () => { // when const columnData = { @@ -63,7 +69,7 @@ describe('CellDatetime', () => { , ); // then - expect(distanceInWordsToNow).toHaveBeenCalledWith(1474495200000, { + expect(distanceInWordsToNow).toHaveBeenCalledWith(new Date(1474495200000), { addSuffix: true, locale: 'getLocale', }); @@ -83,7 +89,7 @@ describe('CellDatetime', () => { render(); // then - expect(distanceInWordsToNow).toHaveBeenCalled(); + expect(distanceInWordsToNow).not.toHaveBeenCalled(); expect(format).toHaveBeenCalled(); expect(document.querySelector('.cell-datetime-container')).toBeEmptyDOMElement(); }); @@ -125,14 +131,18 @@ describe('CellDatetime', () => { const cellData = 1474495200000 + 3600 * 11 * 1000; const timezoneOffset = new Date(cellData).getTimezoneOffset(); const cellDataWithOffset = cellData + timezoneOffset * 60 * 1000; - const hours = 11 + timezoneOffset / 60; + const expectedStrDate = '2016-09-22 09:00:00'; const computedStrOffset = computeValue(cellDataWithOffset, columnData, t); // then expect(computedStrOffset).toEqual(expectedStrDate); - expect(format).toHaveBeenCalledWith(cellDataWithOffset, columnData.pattern, { - locale: getLocale(t), - }); + expect(format).toHaveBeenCalledWith( + new Date(cellDataWithOffset), + dateUtils.formatToUnicode(columnData.pattern), + { + locale: getLocale(t), + }, + ); }); it('should render CellDatetime with tooltip in ago mode', () => { @@ -162,9 +172,13 @@ describe('CellDatetime', () => { computeValue(cellData, columnData, t); // then - expect(dateUtils.formatToTimeZone).toHaveBeenCalledWith(cellData, columnData.pattern, { - timeZone: columnData.timeZone, - locale: getLocale(t), - }); + expect(dateUtils.formatToTimeZone).toHaveBeenCalledWith( + new Date(cellData), + columnData.pattern, + { + timeZone: columnData.timeZone, + locale: getLocale(t), + }, + ); }); }); diff --git a/packages/components/src/i18n/DateFnsLocale/locale.js b/packages/components/src/i18n/DateFnsLocale/locale.js index bf09809a25..86ff69b1cb 100644 --- a/packages/components/src/i18n/DateFnsLocale/locale.js +++ b/packages/components/src/i18n/DateFnsLocale/locale.js @@ -1,61 +1,31 @@ -import { getCurrentLanguage } from '../../translate'; -import buildFormatLocale from './formatters'; - -export function buildDistanceInWordsLocale(t) { - function localize(token, count, options = {}) { - const distanceInWordsLocale = { - lessThanXSeconds: t('DATE_FNS_LESS_THAN_SECOND', { - defaultValue: 'less than {{count}} second', - count, - }), - xSeconds: t('DATE_FNS_SECOND', { defaultValue: '{{count}} second', count }), - halfAMinute: t('DATE_FNS_HALF_A_MINUTE', { defaultValue: 'half a minute' }), - lessThanXMinutes: t('DATE_FNS_LESS_THAN_MINUTE', { - defaultValue: 'less than {{count}} minute', - count, - }), - xMinutes: t('DATE_FNS_ABOUT_MINUTE', { defaultValue: '{{count}} minute', count }), - aboutXHours: t('DATE_FNS_ABOUT_HOUR', { defaultValue: 'about {{count}} hour', count }), - xHours: t('DATE_FNS_HOUR', { defaultValue: '{{count}} hour', count }), - xDays: t('DATE_FNS_DAY', { defaultValue: '{{count}} day', count }), - aboutXMonths: t('DATE_FNS_ABOUT_MONTH', { defaultValue: 'about {{count}} month', count }), - xMonths: t('DATE_FNS_MONTH', { defaultValue: '{{count}} month', count }), - aboutXYears: t('DATE_FNS_ABOUT_YEAR', { defaultValue: 'about {{count}} year', count }), - xYears: t('DATE_FNS_YEAR', { defaultValue: '{{count}} year', count }), - overXYears: t('DATE_FNS_OVER_YEAR', { defaultValue: 'over {{count}} year', count }), - almostXYears: t('DATE_FNS_ALMOST_YEAR', { defaultValue: 'almost {{count}} year', count }), - }; - - const result = distanceInWordsLocale[token]; - - if (!options.addSuffix) { - return result; - } - - if (options.comparison > 0) { - return t('DATE_FNS_IN', { defaultValue: 'in {{value}}', value: result }); - } - - return t('DATE_FNS_AGO', { defaultValue: '{{value}} ago', value: result }); - } +import * as dateFNSLocales from 'date-fns/locale'; +import i18next from 'i18next'; - return { - localize, - }; -} +import { getCurrentLanguage } from '../../translate'; let language; let locale; -export default function getLocale(t) { +export default function getLocale() { const currentlanguage = getCurrentLanguage(); if (language !== currentlanguage) { - locale = { - distanceInWords: buildDistanceInWordsLocale(t), - format: buildFormatLocale(t), - }; - language = getCurrentLanguage(); + // See https://github.com/date-fns/date-fns/blob/main/docs/upgradeGuide.md + // Locales renamed: + // en → en-US + // zh_cn → zh-CN + // zh_tw → zh-TW + let fnsLanguage = currentlanguage; + if (fnsLanguage === 'en') { + fnsLanguage = 'enUS'; + } else if (fnsLanguage === 'zh_cn') { + fnsLanguage = 'zhCN'; + } else if (fnsLanguage === 'zh_tw') { + fnsLanguage = 'zhTW'; + } + locale = dateFNSLocales[fnsLanguage || i18next.language]; + language = currentlanguage; } return locale; } +// formatDistanceToNow diff --git a/packages/components/src/i18n/DateFnsLocale/locale.test.js b/packages/components/src/i18n/DateFnsLocale/locale.test.js index 0320072634..bc676a1ee3 100644 --- a/packages/components/src/i18n/DateFnsLocale/locale.test.js +++ b/packages/components/src/i18n/DateFnsLocale/locale.test.js @@ -1,113 +1,106 @@ import i18next from 'i18next'; -import getDefaultT from '../../translate'; -import getLocale, { buildDistanceInWordsLocale } from './locale'; -describe('buildDistanceInWordsLocale', () => { +import getLocale from './locale'; + +describe('getLocale', () => { + let originalLang; + beforeEach(() => { + originalLang = i18next.language; + }); + afterEach(() => { + i18next.language = originalLang; + }); + it('should return the formatDistance with ', () => { - const distanceInWords = buildDistanceInWordsLocale(getDefaultT()); + const locale = getLocale(); + expect(locale.formatDistance).toBeDefined(); + expect( - distanceInWords.localize('lessThanXSeconds', 5, { + locale.formatDistance('lessThanXSeconds', 5, { addSuffix: true, }), - ).toBe('less than 5 second ago'); + ).toBe('less than 5 seconds ago'); expect( - distanceInWords.localize('xSeconds', 5, { + locale.formatDistance('xSeconds', 5, { addSuffix: true, }), - ).toBe('5 second ago'); + ).toBe('5 seconds ago'); expect( - distanceInWords.localize('halfAMinute', 5, { + locale.formatDistance('halfAMinute', 5, { addSuffix: true, }), ).toBe('half a minute ago'); expect( - distanceInWords.localize('lessThanXMinutes', 5, { + locale.formatDistance('lessThanXMinutes', 5, { addSuffix: true, }), - ).toBe('less than 5 minute ago'); + ).toBe('less than 5 minutes ago'); expect( - distanceInWords.localize('xMinutes', 5, { + locale.formatDistance('xMinutes', 5, { addSuffix: true, }), - ).toBe('5 minute ago'); + ).toBe('5 minutes ago'); expect( - distanceInWords.localize('aboutXHours', 5, { + locale.formatDistance('aboutXHours', 5, { addSuffix: true, }), - ).toBe('about 5 hour ago'); + ).toBe('about 5 hours ago'); expect( - distanceInWords.localize('xHours', 5, { + locale.formatDistance('xHours', 5, { addSuffix: true, }), - ).toBe('5 hour ago'); + ).toBe('5 hours ago'); expect( - distanceInWords.localize('xDays', 5, { + locale.formatDistance('xDays', 5, { addSuffix: true, }), - ).toBe('5 day ago'); + ).toBe('5 days ago'); expect( - distanceInWords.localize('aboutXMonths', 5, { + locale.formatDistance('aboutXMonths', 5, { addSuffix: true, }), - ).toBe('about 5 month ago'); + ).toBe('about 5 months ago'); expect( - distanceInWords.localize('xMonths', 5, { + locale.formatDistance('xMonths', 5, { addSuffix: true, }), - ).toBe('5 month ago'); + ).toBe('5 months ago'); expect( - distanceInWords.localize('aboutXYears', 5, { + locale.formatDistance('aboutXYears', 5, { addSuffix: true, }), - ).toBe('about 5 year ago'); + ).toBe('about 5 years ago'); expect( - distanceInWords.localize('xYears', 5, { + locale.formatDistance('xYears', 5, { addSuffix: true, }), - ).toBe('5 year ago'); + ).toBe('5 years ago'); expect( - distanceInWords.localize('overXYears', 5, { + locale.formatDistance('overXYears', 5, { addSuffix: true, }), - ).toBe('over 5 year ago'); + ).toBe('over 5 years ago'); expect( - distanceInWords.localize('almostXYears', 5, { + locale.formatDistance('almostXYears', 5, { addSuffix: true, }), - ).toBe('almost 5 year ago'); + ).toBe('almost 5 years ago'); expect( - distanceInWords.localize('lessThanXSeconds', 5, { + locale.formatDistance('lessThanXSeconds', 5, { addSuffix: true, comparison: 1, }), - ).toBe('in less than 5 second'); - - expect(distanceInWords.localize('lessThanXSeconds', 5)).toBe('less than 5 second'); - }); -}); - -describe('getLocale', () => { - let originalLang; - beforeEach(() => { - originalLang = i18next.language; - }); - afterEach(() => { - i18next.language = originalLang; - }); - it('should return the locale', () => { - const locale = getLocale(getDefaultT()); + ).toBe('in less than 5 seconds'); - expect(locale.distanceInWords).toBeDefined(); - expect(locale.distanceInWords.localize).toBeDefined(); - expect(locale.distanceInWords.localize('lessThanXSeconds', 2)).toBe('less than 2 second'); + expect(locale.formatDistance('lessThanXSeconds', 5)).toBe('less than 5 seconds'); }); it('should return a locale different when we change the i18next language', () => { - const locale = getLocale(getDefaultT()); - const secondLocale = getLocale(getDefaultT()); + const locale = getLocale(); + const secondLocale = getLocale(); expect(locale).toBe(secondLocale); i18next.language = 'fr'; - const thirdLocale = getLocale(getDefaultT()); + const thirdLocale = getLocale(); expect(thirdLocale).not.toBe(secondLocale); }); }); diff --git a/packages/components/src/translate.js b/packages/components/src/translate.js index 4dc5dfcce0..1ec9c44935 100644 --- a/packages/components/src/translate.js +++ b/packages/components/src/translate.js @@ -1,5 +1,6 @@ +import { getI18n, setI18n } from 'react-i18next'; + import i18next from 'i18next'; -import { setI18n, getI18n } from 'react-i18next'; if (!getI18n()) { // eslint-disable-next-line no-console diff --git a/packages/design-system/package.json b/packages/design-system/package.json index 981a9996d6..f44881a676 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -83,7 +83,7 @@ "@types/react-virtualized": "^9.21.28", "browser-sync": "^2.29.3", "browser-sync-webpack-plugin": "^2.3.0", - "concurrently": "^7.6.0", + "concurrently": "^8.2.2", "core-js": "^3.33.3", "cypress": "^13.6.0", "focus-outline-manager": "^1.0.2", diff --git a/packages/faceted-search/package.json b/packages/faceted-search/package.json index 7486bdbcd9..017ccc243f 100644 --- a/packages/faceted-search/package.json +++ b/packages/faceted-search/package.json @@ -38,7 +38,7 @@ "@talend/utils": "^2.8.0", "@talend/design-tokens": "^2.10.1", "classnames": "^2.3.2", - "date-fns": "^1.30.1", + "date-fns": "^2.30.0", "invariant": "^2.2.4", "lodash": "^4.17.21" }, diff --git a/packages/faceted-search/src/components/Badges/BadgeDate/BadgeDate.component.js b/packages/faceted-search/src/components/Badges/BadgeDate/BadgeDate.component.js index 0cf585bdae..8d262ec2e2 100644 --- a/packages/faceted-search/src/components/Badges/BadgeDate/BadgeDate.component.js +++ b/packages/faceted-search/src/components/Badges/BadgeDate/BadgeDate.component.js @@ -1,14 +1,16 @@ import { useMemo } from 'react'; + import format from 'date-fns/format'; import PropTypes from 'prop-types'; + import Badge from '@talend/react-components/lib/Badge'; +import { createOperatorsDict, operatorNames } from '../../../dictionary/operator.dictionary'; +import { operatorPropTypes, operatorsPropTypes } from '../../facetedSearch.propTypes'; import { BadgeFaceted } from '../BadgeFaceted'; import { BadgeDateForm } from './BadgeDateForm.component'; -import { operatorPropTypes, operatorsPropTypes } from '../../facetedSearch.propTypes'; -import { createOperatorsDict, operatorNames } from '../../../dictionary/operator.dictionary'; -const DATE_FORMAT = 'YYYY-MM-DD'; +const DATE_FORMAT = 'yyyy-MM-dd'; const defaultOperatorsName = [ operatorNames.notEquals, diff --git a/packages/faceted-search/src/components/Badges/BadgeDate/BadgeDateForm.component.js b/packages/faceted-search/src/components/Badges/BadgeDate/BadgeDateForm.component.js index 9278693b74..b17d42367e 100644 --- a/packages/faceted-search/src/components/Badges/BadgeDate/BadgeDateForm.component.js +++ b/packages/faceted-search/src/components/Badges/BadgeDate/BadgeDateForm.component.js @@ -1,7 +1,10 @@ import { useMemo } from 'react'; -import startOfDay from 'date-fns/start_of_day'; + +import startOfDay from 'date-fns/startOfDay'; import PropTypes from 'prop-types'; -import { Action, getTheme, Rich, DatePicker } from '@talend/react-components'; + +import { Action, DatePicker, getTheme, Rich } from '@talend/react-components'; + import { getApplyDataFeature, getDataAttributesFrom } from '../../../helpers/usage.helpers'; import cssModule from './BadgeDate.module.scss'; diff --git a/packages/utils/package.json b/packages/utils/package.json index 0f958b1e15..59acd93451 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -26,12 +26,12 @@ "@talend/scripts-config-jest": "^14.0.0", "@talend/scripts-config-stylelint": "^4.1.0", "@talend/scripts-config-typescript": "^11.2.0", - "@types/date-fns": "^0.0.2", + "@types/date-fns": "^2.6.0", "@types/lodash": "^4.14.202", "cross-env": "^7.0.3" }, "dependencies": { - "date-fns": "^1.30.1", + "date-fns": "^2.30.0", "lodash": "^4.17.21" }, "publishConfig": { diff --git a/packages/utils/src/date/date.test.ts b/packages/utils/src/date/date.test.ts index 6317c38ff4..48a3be52e4 100644 --- a/packages/utils/src/date/date.test.ts +++ b/packages/utils/src/date/date.test.ts @@ -1,4 +1,6 @@ import dateFnsFormat from 'date-fns/format'; +import enGB from 'date-fns/locale/en-GB'; + import { convertToLocalTime, convertToTimeZone, @@ -137,12 +139,11 @@ describe('date', () => { }); it('should pass locale to datefns format method', () => { // given - const mockLocal = { format: () => {} }; const dateObj = new Date('2020-12-20, 20:00'); const formatString = 'ddd YYYY-MM-DD HH:mm:ss'; const options = { timeZone: timeZones['UTC+5'], - locale: mockLocal, + locale: enGB, }; // when @@ -153,7 +154,7 @@ describe('date', () => { expect.anything(), expect.anything(), expect.objectContaining({ - locale: mockLocal, + locale: enGB, }), ); }); diff --git a/packages/utils/src/date/generator.ts b/packages/utils/src/date/generator.ts index a3edb3ee62..090dd9ac10 100644 --- a/packages/utils/src/date/generator.ts +++ b/packages/utils/src/date/generator.ts @@ -1,5 +1,5 @@ -import startOfWeek from 'date-fns/start_of_week'; -import addDays from 'date-fns/add_days'; +import addDays from 'date-fns/addDays'; +import startOfWeek from 'date-fns/startOfWeek'; import chunk from 'lodash/chunk'; /** diff --git a/packages/utils/src/date/index.ts b/packages/utils/src/date/index.ts index 6e0b06ca59..41075388c0 100644 --- a/packages/utils/src/date/index.ts +++ b/packages/utils/src/date/index.ts @@ -1,5 +1,6 @@ +import { parseISO } from 'date-fns'; import dateFnsFormat from 'date-fns/format'; -import parse from 'date-fns/parse'; + import * as generator from './generator'; type DateFnsFormatInput = Date | number | string; @@ -100,7 +101,7 @@ function formatTimeZoneTokens(dateFormat: string, timeZone: string): string { * @see https://github.com/prantlf/date-fns-timezone/blob/HEAD/docs/API.md#converttolocaltime */ export function convertToLocalTime(date: DateFnsFormatInput, options: ConversionOptions): Date { - const parsedDate = parse(date); + const parsedDate = parseISO(new Date(date).toISOString()); const offset = getUTCOffset(options.timeZone) + parsedDate.getTimezoneOffset(); return new Date(parsedDate.getTime() - offset * 60 * 1000); @@ -118,7 +119,7 @@ export function convertToLocalTime(date: DateFnsFormatInput, options: Conversion export function convertToTimeZone(date: DateFnsFormatInput, options: ConversionOptions): Date { const { timeZone, sourceTimeZone } = options; - const parsedDate = parse(date); + const parsedDate = parseISO(new Date(date).toISOString()); let offset = getUTCOffset(timeZone, parsedDate) + parsedDate.getTimezoneOffset(); @@ -130,6 +131,22 @@ export function convertToTimeZone(date: DateFnsFormatInput, options: ConversionO return new Date(parsedDate.getTime() + offset * 60 * 1000); } +export function formatToUnicode(formatString: string): string { + // See: https://date-fns.org/v2.16.1/docs/format + return ( + formatString + // Characters are now escaped using single quote symbols (') instead of square brackets. + .replace(/\[T\]/g, () => "'T'") + // See: https://date-fns.org/docs/Unicode-Tokens + // Replace years YYYY to yyyy + .replace(/YYYY/g, () => 'yyyy') + // Replace days of the month DD to dd + .replace(/DD/g, () => 'dd') + // Replace days of the week ddd to EEE + .replace(/ddd/g, () => 'EEE') + ); +} + /** * Returns the formatted date string in the given format, after converting it to the given time zone. * @param {DateFnsFormatInput} date Date to format @@ -146,8 +163,15 @@ export function formatToTimeZone( ): string { const dateConvertedToTimezone = convertToTimeZone(date, options); + const unicodeFormatedString = formatToUnicode(formatString); + // Replace timezone token(s) in the string format with timezone values, since format() will use local timezone - const dateFnsFormatWithTimeZoneValue = formatTimeZoneTokens(formatString, options.timeZone); + const dateFnsFormatWithTimeZoneValue = formatTimeZoneTokens( + unicodeFormatedString, + options.timeZone, + ) + // Avoid error: "Format string contains an unescaped latin alphabet character `Z`" + .replace(/\[Z\]/g, () => "'Z'"); return dateFnsFormat(dateConvertedToTimezone, dateFnsFormatWithTimeZoneValue, options); } @@ -219,7 +243,8 @@ const options = { * @returns The formated date */ export function format(date: DateFnsFormatInput, dateOption: string, lang: string): string { - return new Intl.DateTimeFormat(lang, options[dateOption]).format(parse(date)); + const parsedDate = parseISO(new Date(date).toISOString()); + return new Intl.DateTimeFormat(lang, options[dateOption]).format(parsedDate); } export const buildWeeks = generator.buildWeeks; diff --git a/versions/dependencies.json b/versions/dependencies.json index abec618d2c..08048d0c9c 100644 --- a/versions/dependencies.json +++ b/versions/dependencies.json @@ -41,7 +41,7 @@ "bootstrap-sass": "3.4.1", "bson-objectid": "^1.3.1", "classnames": "^2.2.5", - "date-fns": "^1.27.2", + "date-fns": "^2.30.0", "date-fns-timezone": "^0.1.4", "hoist-non-react-statics": "^2.5.5", "immutable": "^3.8.1", diff --git a/yarn.lock b/yarn.lock index 201c4aa22a..1c84c77493 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4436,10 +4436,12 @@ "@types/d3-transition" "*" "@types/d3-zoom" "*" -"@types/date-fns@^0.0.2": - version "0.0.2" - resolved "https://registry.yarnpkg.com/@types/date-fns/-/date-fns-0.0.2.tgz#a71d2e0b2ab57bb8de81c843e5ecf6e161779b8c" - integrity sha512-oaNPSNlQvNmenCWCw5fHBpRUpltjZCBw3hwZkYKgSyNuCuD2w60jpIgMUa1h/BrKuw4L+0BvoiXhwmNKUFQLHw== +"@types/date-fns@^2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@types/date-fns/-/date-fns-2.6.0.tgz#b062ca46562002909be0c63a6467ed173136acc1" + integrity sha512-9DSw2ZRzV0Tmpa6PHHJbMcZn79HHus+BBBohcOaDzkK/G3zMjDUDYjJIWBFLbkh+1+/IOS0A59BpQfdr37hASg== + dependencies: + date-fns "*" "@types/debug@^4.0.0", "@types/debug@^4.1.7": version "4.1.12" @@ -7211,20 +7213,20 @@ concat-stream@^2.0.0: readable-stream "^3.0.2" typedarray "^0.0.6" -concurrently@^7.6.0: - version "7.6.0" - resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-7.6.0.tgz#531a6f5f30cf616f355a4afb8f8fcb2bba65a49a" - integrity sha512-BKtRgvcJGeZ4XttiDiNcFiRlxoAeZOseqUvyYRUp/Vtd+9p1ULmeoSqGsDA+2ivdeDFpqrJvGvmI+StKfKl5hw== +concurrently@^8.2.2: + version "8.2.2" + resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-8.2.2.tgz#353141985c198cfa5e4a3ef90082c336b5851784" + integrity sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg== dependencies: - chalk "^4.1.0" - date-fns "^2.29.1" + chalk "^4.1.2" + date-fns "^2.30.0" lodash "^4.17.21" - rxjs "^7.0.0" - shell-quote "^1.7.3" - spawn-command "^0.0.2-1" - supports-color "^8.1.0" + rxjs "^7.8.1" + shell-quote "^1.8.1" + spawn-command "0.0.2" + supports-color "^8.1.1" tree-kill "^1.2.2" - yargs "^17.3.1" + yargs "^17.7.2" confusing-browser-globals@^1.0.10: version "1.0.11" @@ -8066,12 +8068,7 @@ data-urls@^3.0.2: whatwg-mimetype "^3.0.0" whatwg-url "^11.0.0" -date-fns@^1.30.1: - version "1.30.1" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" - integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== - -date-fns@^2.29.1, date-fns@^2.30.0: +date-fns@*, date-fns@^2.30.0: version "2.30.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== @@ -16616,7 +16613,7 @@ rxjs@^6.6.0: dependencies: tslib "^1.9.0" -rxjs@^7.0.0, rxjs@^7.5.1, rxjs@^7.5.5: +rxjs@^7.5.1, rxjs@^7.5.5, rxjs@^7.8.1: version "7.8.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== @@ -16960,7 +16957,7 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@^1.7.1, shell-quote@^1.7.3, shell-quote@^1.8.1: +shell-quote@^1.7.1, shell-quote@^1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== @@ -17229,10 +17226,10 @@ space-separated-tokens@^1.0.0: resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== -spawn-command@^0.0.2-1: - version "0.0.2-1" - resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" - integrity sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg== +spawn-command@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2.tgz#9544e1a43ca045f8531aac1a48cb29bdae62338e" + integrity sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ== spawndamnit@^2.0.0: version "2.0.0" @@ -17807,7 +17804,7 @@ supports-color@^7.0.0, supports-color@^7.1.0, supports-color@^7.2.0: dependencies: has-flag "^4.0.0" -supports-color@^8.0.0, supports-color@^8.1.0, supports-color@^8.1.1: +supports-color@^8.0.0, supports-color@^8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== @@ -19729,7 +19726,7 @@ yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.3.1, yargs@^17.7.1: +yargs@^17.3.1, yargs@^17.7.1, yargs@^17.7.2: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==