From 90f7ed3daefd0f54569ccc8aa3beaac183267095 Mon Sep 17 00:00:00 2001 From: Noah Overcash Date: Thu, 13 Apr 2023 19:11:45 -0500 Subject: [PATCH 1/7] STCOM-1150: Remove unchecked call to Moment.locale --- lib/Timepicker/TimeDropdown.js | 4 ---- lib/Timepicker/Timepicker.js | 1 - 2 files changed, 5 deletions(-) diff --git a/lib/Timepicker/TimeDropdown.js b/lib/Timepicker/TimeDropdown.js index 6ad159034..2104947b6 100644 --- a/lib/Timepicker/TimeDropdown.js +++ b/lib/Timepicker/TimeDropdown.js @@ -23,7 +23,6 @@ const propTypes = { intl: PropTypes.shape({ formatMessage: PropTypes.func.isRequired }).isRequired, - locale: PropTypes.string, mainControl: PropTypes.object, minuteIncrement: PropTypes.number, onBlur: PropTypes.func, @@ -41,7 +40,6 @@ const propTypes = { const defaultProps = { hoursFormat: '24', - locale: 'en', minuteIncrement: 1, onSetTime: () => null, }; @@ -50,8 +48,6 @@ class TimeDropdown extends React.Component { constructor(props) { super(props); - moment.locale(this.props.locale); - // handle existing value... let initMoment; if (typeof props.selectedTime === 'undefined' || props.selectedTime === '') { // handle blank or cleared time... diff --git a/lib/Timepicker/Timepicker.js b/lib/Timepicker/Timepicker.js index c5ef328de..20fe59cff 100644 --- a/lib/Timepicker/Timepicker.js +++ b/lib/Timepicker/Timepicker.js @@ -616,7 +616,6 @@ class Timepicker extends React.Component { mainControl={this.textfieldRef.current} onKeyDown={this.handleKeyDown} onBlur={this.hideTimepicker} - locale={this.locale} onNavigation={this.handleDateNavigation} id={this.testId} onClose={this.hideTimepicker} From da58483e8f153a238260112cbc71b4e385b4a0d6 Mon Sep 17 00:00:00 2001 From: Noah Overcash Date: Fri, 14 Apr 2023 02:55:35 -0500 Subject: [PATCH 2/7] Fix dateTimeUtils test failure with Chrome v110 --- util/dateTimeUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/dateTimeUtils.js b/util/dateTimeUtils.js index 5f90f7bc0..007ef1f57 100644 --- a/util/dateTimeUtils.js +++ b/util/dateTimeUtils.js @@ -48,7 +48,7 @@ export const getLocaleDateFormat = ({ intl }) => { format = getMomentLocalizedFormat(intl); } - return format; + return format.replace('\u202f', ' '); }; // getLocalizedTimeFormatInfo() - From b6a14b5c12b45d14916ed731fed190b40acbc2cb Mon Sep 17 00:00:00 2001 From: Noah Overcash Date: Fri, 14 Apr 2023 03:17:33 -0500 Subject: [PATCH 3/7] Fix getLocalizedTimeInfo --- util/dateTimeUtils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/dateTimeUtils.js b/util/dateTimeUtils.js index 007ef1f57..e8ee44f23 100644 --- a/util/dateTimeUtils.js +++ b/util/dateTimeUtils.js @@ -48,7 +48,7 @@ export const getLocaleDateFormat = ({ intl }) => { format = getMomentLocalizedFormat(intl); } - return format.replace('\u202f', ' '); + return format.replaceAll('\u202f', ' '); }; // getLocalizedTimeFormatInfo() - @@ -128,7 +128,7 @@ export function getLocalizedTimeFormatInfo(locale) { return { ...formatInfo, - timeFormat, + timeFormat: timeFormat.replaceAll('\u202f', ' '), dayPeriods: [...dpOptions], }; } From 093f62c5bb9150b11ffc95b7cf83ff905d75fe0d Mon Sep 17 00:00:00 2001 From: Noah Overcash Date: Sun, 16 Apr 2023 23:05:25 -0500 Subject: [PATCH 4/7] Conditionally call moment.locale --- lib/Timepicker/TimeDropdown.js | 6 ++++++ lib/Timepicker/Timepicker.js | 1 + 2 files changed, 7 insertions(+) diff --git a/lib/Timepicker/TimeDropdown.js b/lib/Timepicker/TimeDropdown.js index 2104947b6..91f01551c 100644 --- a/lib/Timepicker/TimeDropdown.js +++ b/lib/Timepicker/TimeDropdown.js @@ -23,6 +23,7 @@ const propTypes = { intl: PropTypes.shape({ formatMessage: PropTypes.func.isRequired }).isRequired, + locale: PropTypes.string, mainControl: PropTypes.object, minuteIncrement: PropTypes.number, onBlur: PropTypes.func, @@ -40,6 +41,7 @@ const propTypes = { const defaultProps = { hoursFormat: '24', + locale: 'en', minuteIncrement: 1, onSetTime: () => null, }; @@ -48,6 +50,10 @@ class TimeDropdown extends React.Component { constructor(props) { super(props); + if (moment.locales().includes(this.props.locale)) { + moment.locale(this.props.locale); + } + // handle existing value... let initMoment; if (typeof props.selectedTime === 'undefined' || props.selectedTime === '') { // handle blank or cleared time... diff --git a/lib/Timepicker/Timepicker.js b/lib/Timepicker/Timepicker.js index 20fe59cff..c5ef328de 100644 --- a/lib/Timepicker/Timepicker.js +++ b/lib/Timepicker/Timepicker.js @@ -616,6 +616,7 @@ class Timepicker extends React.Component { mainControl={this.textfieldRef.current} onKeyDown={this.handleKeyDown} onBlur={this.hideTimepicker} + locale={this.locale} onNavigation={this.handleDateNavigation} id={this.testId} onClose={this.hideTimepicker} From 3a115c93a73fd32e54b51aabe4ac009286851e32 Mon Sep 17 00:00:00 2001 From: Noah Overcash Date: Tue, 18 Apr 2023 21:20:53 -0500 Subject: [PATCH 5/7] Add moment.locale pre-check for Calendar, too --- lib/Datepicker/Calendar.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/Datepicker/Calendar.js b/lib/Datepicker/Calendar.js index e013d392d..117251dee 100644 --- a/lib/Datepicker/Calendar.js +++ b/lib/Datepicker/Calendar.js @@ -124,7 +124,10 @@ class Calendar extends React.Component { constructor(props) { super(props); - moment.locale(this.props.intl.locale || this.props.locale); + const computedLocale = this.props.intl.locale || this.props.locale; + if (moment.locales().includes(computedLocale)) { + moment.locale(computedLocale); + } const { selectedDate, dateFormat } = this.props; From b514712fd518ccec15a30cb58f8aa1098998f492 Mon Sep 17 00:00:00 2001 From: Noah Overcash Date: Tue, 18 Apr 2023 22:47:09 -0500 Subject: [PATCH 6/7] Add tests for unknown moment locales --- lib/Datepicker/tests/Datepicker-test.js | 2 ++ lib/Timepicker/tests/Timepicker-test.js | 21 +++++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/Datepicker/tests/Datepicker-test.js b/lib/Datepicker/tests/Datepicker-test.js index fbeb181a3..67704ba08 100644 --- a/lib/Datepicker/tests/Datepicker-test.js +++ b/lib/Datepicker/tests/Datepicker-test.js @@ -594,6 +594,8 @@ describe('Datepicker', () => { await datepicker.openCalendar(); }); + it('does not poison moment locale cache', () => expect(moment.locales()).to.not.include('en-se')); + it('renders Monday as the first day of the week', () => Weekday('Mon', { index: 0 }).exists()); it('renders weekday and month days in correct alignment', async () => { diff --git a/lib/Timepicker/tests/Timepicker-test.js b/lib/Timepicker/tests/Timepicker-test.js index ef03ea5be..ae695d9fb 100644 --- a/lib/Timepicker/tests/Timepicker-test.js +++ b/lib/Timepicker/tests/Timepicker-test.js @@ -1,6 +1,8 @@ +import { expect } from 'chai'; import React from 'react'; import { describe, beforeEach, it } from 'mocha'; -import moment from 'moment-timezone'; +import moment from 'moment'; +import momentTz from 'moment-timezone'; import { runAxeTest, HTML, @@ -107,6 +109,21 @@ describe('Timepicker', () => { it('emits an event with the time formatted as displayed', () => converge(() => (timeOutput === '05:00 PM'))); }); + describe('correctly handles unknown locales', () => { + beforeEach(async () => { + await mountWithContext( + + ); + + await timepicker.clickInput(); + }); + + it('does not poison moment locale cache', () => expect(moment.locales()).to.not.include('en-se')); + }); + describe('selecting a time with timeZone prop', () => { let timeOutput; @@ -166,7 +183,7 @@ describe('Timepicker', () => { await timepicker.fillIn('05:00 PM'); }); - it('returns an ISO 8601 time string for specific time zone', () => converge(() => (timeOutput === moment().tz(tz).isDST() ? '00:00:00.000Z' : '01:00:00.000Z'))); + it('returns an ISO 8601 time string for specific time zone', () => converge(() => (timeOutput === momentTz().tz(tz).isDST() ? '00:00:00.000Z' : '01:00:00.000Z'))); }); }); From bcb15b80cffaf27c64546b843f0d365908afbb0d Mon Sep 17 00:00:00 2001 From: Noah Overcash Date: Wed, 19 Apr 2023 12:07:49 -0500 Subject: [PATCH 7/7] Make changes more descriptive --- lib/Timepicker/tests/Timepicker-test.js | 2 +- util/dateTimeUtils.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Timepicker/tests/Timepicker-test.js b/lib/Timepicker/tests/Timepicker-test.js index ae695d9fb..86ce92f13 100644 --- a/lib/Timepicker/tests/Timepicker-test.js +++ b/lib/Timepicker/tests/Timepicker-test.js @@ -109,7 +109,7 @@ describe('Timepicker', () => { it('emits an event with the time formatted as displayed', () => converge(() => (timeOutput === '05:00 PM'))); }); - describe('correctly handles unknown locales', () => { + describe('correctly handles unknown locales (en-SE)', () => { beforeEach(async () => { await mountWithContext( { format = getMomentLocalizedFormat(intl); } + // replace narrow non-breaking space introduced in ICU 72.1 + // see https://github.com/nodejs/node/issues/46123 return format.replaceAll('\u202f', ' '); };