diff options
author | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-08-18 17:47:37 +0200 |
---|---|---|
committer | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-08-25 11:05:36 +0200 |
commit | f6276b3b6fecce2b160ed8bdc62a3e87439249e4 (patch) | |
tree | d1c69e7f786b8693fb13dd816624794eb6b07ae7 /server/sonar-web/src/main/js/helpers | |
parent | 1ddf3ee7dbf26116afb767003a8a0698965c4f70 (diff) | |
download | sonarqube-f6276b3b6fecce2b160ed8bdc62a3e87439249e4.tar.gz sonarqube-f6276b3b6fecce2b160ed8bdc62a3e87439249e4.zip |
SONAR-9385 SONAR-9436 Replace moment with react-intl
Diffstat (limited to 'server/sonar-web/src/main/js/helpers')
-rw-r--r-- | server/sonar-web/src/main/js/helpers/__tests__/dates-test.ts | 69 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/helpers/__tests__/l10n-test.ts (renamed from server/sonar-web/src/main/js/helpers/__tests__/l10n-test.js) | 0 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/helpers/__tests__/query-test.js | 3 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/helpers/dates.ts | 90 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/helpers/handlebars/d.js | 8 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/helpers/handlebars/dt.js | 10 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/helpers/handlebars/fromNow.js | 4 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/helpers/l10n.ts (renamed from server/sonar-web/src/main/js/helpers/l10n.js) | 69 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/helpers/periods.js | 3 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/helpers/query.js | 12 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/helpers/testUtils.ts | 9 |
11 files changed, 230 insertions, 47 deletions
diff --git a/server/sonar-web/src/main/js/helpers/__tests__/dates-test.ts b/server/sonar-web/src/main/js/helpers/__tests__/dates-test.ts new file mode 100644 index 00000000000..455886d15a8 --- /dev/null +++ b/server/sonar-web/src/main/js/helpers/__tests__/dates-test.ts @@ -0,0 +1,69 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as dates from '../dates'; + +const recentDate = new Date('2017-08-16T12:00:00.000Z'); +const recentDate2 = new Date('2016-12-16T12:00:00.000Z'); +const oldDate = new Date('2014-01-12T12:00:00.000Z'); + +it('toShortNotSoISOString', () => { + expect(dates.toShortNotSoISOString(recentDate)).toBe('2017-08-16'); +}); + +it('toNotSoISOString', () => { + expect(dates.toNotSoISOString(recentDate)).toBe('2017-08-16T12:00:00+0000'); +}); + +it('startOfDay', () => { + expect(dates.startOfDay(recentDate).toTimeString()).toContain('00:00:00'); + expect(dates.startOfDay(recentDate)).not.toBe(recentDate); +}); + +it('isValidDate', () => { + expect(dates.isValidDate(recentDate)).toBeTruthy(); + expect(dates.isValidDate(new Date())).toBeTruthy(); + expect(dates.isValidDate(new Date('foo'))).toBeFalsy(); +}); + +it('isSameDay', () => { + expect(dates.isSameDay(recentDate, new Date(recentDate))).toBeTruthy(); + expect(dates.isSameDay(recentDate, recentDate2)).toBeFalsy(); + expect(dates.isSameDay(recentDate, oldDate)).toBeFalsy(); + expect(dates.isSameDay(recentDate, new Date('2016-08-16T12:00:00.000Z'))).toBeFalsy(); +}); + +it('differenceInYears', () => { + expect(dates.differenceInYears(recentDate, recentDate2)).toBe(0); + expect(dates.differenceInYears(recentDate, oldDate)).toBe(3); + expect(dates.differenceInYears(oldDate, recentDate)).toBe(-3); +}); + +it('differenceInDays', () => { + expect(dates.differenceInDays(recentDate, new Date('2017-08-01T12:00:00.000Z'))).toBe(15); + expect(dates.differenceInDays(recentDate, new Date('2017-08-15T23:00:00.000Z'))).toBe(0); + expect(dates.differenceInDays(recentDate, recentDate2)).toBe(243); + expect(dates.differenceInDays(recentDate, oldDate)).toBe(1312); +}); + +it('differenceInSeconds', () => { + expect(dates.differenceInSeconds(recentDate, new Date('2017-08-16T10:00:00.000Z'))).toBe(7200); + expect(dates.differenceInSeconds(recentDate, new Date('2017-08-16T12:00:00.500Z'))).toBe(0); + expect(dates.differenceInSeconds(recentDate, oldDate)).toBe(113356800); +}); diff --git a/server/sonar-web/src/main/js/helpers/__tests__/l10n-test.js b/server/sonar-web/src/main/js/helpers/__tests__/l10n-test.ts index 3763be42db6..3763be42db6 100644 --- a/server/sonar-web/src/main/js/helpers/__tests__/l10n-test.js +++ b/server/sonar-web/src/main/js/helpers/__tests__/l10n-test.ts diff --git a/server/sonar-web/src/main/js/helpers/__tests__/query-test.js b/server/sonar-web/src/main/js/helpers/__tests__/query-test.js index 982f9375a36..11d7b289cae 100644 --- a/server/sonar-web/src/main/js/helpers/__tests__/query-test.js +++ b/server/sonar-web/src/main/js/helpers/__tests__/query-test.js @@ -17,7 +17,6 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import moment from 'moment'; import * as query from '../query'; describe('queriesEqual', () => { @@ -79,7 +78,7 @@ describe('parseAsDate', () => { }); describe('serializeDate', () => { - const date = moment.utc('2016-06-20T13:09:48.256Z'); + const date = new Date('2016-06-20T13:09:48.256Z'); it('should serialize string correctly', () => { expect(query.serializeDate(date)).toBe('2016-06-20T13:09:48+0000'); expect(query.serializeDate('')).toBeUndefined(); diff --git a/server/sonar-web/src/main/js/helpers/dates.ts b/server/sonar-web/src/main/js/helpers/dates.ts new file mode 100644 index 00000000000..5bbb50b3bff --- /dev/null +++ b/server/sonar-web/src/main/js/helpers/dates.ts @@ -0,0 +1,90 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +const MILLISECONDS_IN_MINUTE = 60 * 1000; +const MILLISECONDS_IN_DAY = MILLISECONDS_IN_MINUTE * 60 * 24; + +function pad(number: number) { + if (number < 10) { + return '0' + number; + } + return number; +} + +function compareDateAsc(dateLeft: Date, dateRight: Date): number { + var timeLeft = dateLeft.getTime(); + var timeRight = dateRight.getTime(); + + if (timeLeft < timeRight) { + return -1; + } else if (timeLeft > timeRight) { + return 1; + } else { + return 0; + } +} + +export function toShortNotSoISOString(date: Date): string { + return date.getFullYear() + '-' + pad(date.getMonth() + 1) + '-' + pad(date.getDate()); +} + +export function toNotSoISOString(date: Date): string { + return date.toISOString().replace(/\..+Z$/, '+0000'); +} + +export function startOfDay(date: Date): Date { + const startDay = new Date(date); + startDay.setHours(0, 0, 0, 0); + return startDay; +} + +export function isValidDate(date: Date): boolean { + return !isNaN(date.getTime()); +} + +export function isSameDay(dateLeft: Date, dateRight: Date): boolean { + const startDateLeft = startOfDay(dateLeft); + const startDateRight = startOfDay(dateRight); + return startDateLeft.getTime() === startDateRight.getTime(); +} + +export function differenceInYears(dateLeft: Date, dateRight: Date): number { + const sign = compareDateAsc(dateLeft, dateRight); + const diff = Math.abs(dateLeft.getFullYear() - dateRight.getFullYear()); + const tmpLeftDate = new Date(dateLeft); + tmpLeftDate.setFullYear(dateLeft.getFullYear() - sign * diff); + const isLastYearNotFull = compareDateAsc(tmpLeftDate, dateRight) === -sign; + return sign * (diff - (isLastYearNotFull ? 1 : 0)); +} + +export function differenceInDays(dateLeft: Date, dateRight: Date): number { + const startDateLeft = startOfDay(dateLeft); + const startDateRight = startOfDay(dateRight); + const timestampLeft = + startDateLeft.getTime() - startDateLeft.getTimezoneOffset() * MILLISECONDS_IN_MINUTE; + const timestampRight = + startDateRight.getTime() - startDateRight.getTimezoneOffset() * MILLISECONDS_IN_MINUTE; + return Math.round((timestampLeft - timestampRight) / MILLISECONDS_IN_DAY); +} + +export function differenceInSeconds(dateLeft: Date, dateRight: Date): number { + const diff = (dateLeft.getTime() - dateRight.getTime()) / 1000; + return diff > 0 ? Math.floor(diff) : Math.ceil(diff); +} diff --git a/server/sonar-web/src/main/js/helpers/handlebars/d.js b/server/sonar-web/src/main/js/helpers/handlebars/d.js index d457edd9fdb..ef43101b332 100644 --- a/server/sonar-web/src/main/js/helpers/handlebars/d.js +++ b/server/sonar-web/src/main/js/helpers/handlebars/d.js @@ -17,8 +17,10 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -const moment = require('moment'); - module.exports = function(date) { - return moment(date).format('LL'); + return new Intl.DateTimeFormat(localStorage.getItem('l10n.locale') || 'en', { + year: 'numeric', + month: 'long', + day: 'numeric' + }).format(new Date(date)); }; diff --git a/server/sonar-web/src/main/js/helpers/handlebars/dt.js b/server/sonar-web/src/main/js/helpers/handlebars/dt.js index 708be097e33..3af77ae1d6c 100644 --- a/server/sonar-web/src/main/js/helpers/handlebars/dt.js +++ b/server/sonar-web/src/main/js/helpers/handlebars/dt.js @@ -17,8 +17,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -const moment = require('moment'); - module.exports = function(date) { - return moment(date).format('LLL'); + return new Intl.DateTimeFormat(localStorage.getItem('l10n.locale') || 'en', { + year: 'numeric', + month: 'long', + day: 'numeric', + hour: 'numeric', + minute: 'numeric' + }).format(new Date(date)); }; diff --git a/server/sonar-web/src/main/js/helpers/handlebars/fromNow.js b/server/sonar-web/src/main/js/helpers/handlebars/fromNow.js index dc607b8dca2..ea25726d79f 100644 --- a/server/sonar-web/src/main/js/helpers/handlebars/fromNow.js +++ b/server/sonar-web/src/main/js/helpers/handlebars/fromNow.js @@ -17,8 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -const moment = require('moment'); +const IntlRelativeFormat = require('intl-relativeformat'); module.exports = function(date) { - return moment(date).fromNow(); + return new IntlRelativeFormat(localStorage.getItem('l10n.locale') || 'en').format(date); }; diff --git a/server/sonar-web/src/main/js/helpers/l10n.js b/server/sonar-web/src/main/js/helpers/l10n.ts index 1f5ebda6796..57f51949074 100644 --- a/server/sonar-web/src/main/js/helpers/l10n.js +++ b/server/sonar-web/src/main/js/helpers/l10n.ts @@ -17,21 +17,36 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/* @flow */ -import moment from 'moment'; import { getJSON } from './request'; +import { toNotSoISOString } from './dates'; -let messages = {}; +interface LanguageBundle { + [name: string]: string; +} + +interface BundleRequestParams { + locale?: string; + ts?: string; +} + +interface BundleRequestResponse { + effectiveLocale: string; + messages: LanguageBundle; +} -export function translate(...keys /*: string[] */) { +let messages: LanguageBundle = {}; + +export const DEFAULT_LANGUAGE = 'en'; + +export function translate(...keys: string[]): string { const messageKey = keys.join('.'); return messages[messageKey] || messageKey; } export function translateWithParameters( - messageKey /*: string */, - ...parameters /*: Array<string | number> */ -) { + messageKey: string, + ...parameters: Array<string | number> +): string { const message = messages[messageKey]; if (message) { return parameters @@ -42,20 +57,16 @@ export function translateWithParameters( } } -export function hasMessage(...keys /*: string[] */) { +export function hasMessage(...keys: string[]): boolean { const messageKey = keys.join('.'); return messages[messageKey] != null; } -export function configureMoment(language /*: ?string */) { - moment.locale(language || getPreferredLanguage()); -} - -function getPreferredLanguage() { +function getPreferredLanguage(): string | undefined { return window.navigator.languages ? window.navigator.languages[0] : window.navigator.language; } -function checkCachedBundle() { +function checkCachedBundle(): boolean { const cached = localStorage.getItem('l10n.bundle'); if (!cached) { @@ -70,20 +81,20 @@ function checkCachedBundle() { } } -function getL10nBundle(params) { +function getL10nBundle(params: BundleRequestParams): Promise<BundleRequestResponse> { const url = '/api/l10n/index'; return getJSON(url, params); } -export function requestMessages() { +export function requestMessages(): Promise<string> { const browserLocale = getPreferredLanguage(); const cachedLocale = localStorage.getItem('l10n.locale'); - const params = {}; + const params: BundleRequestParams = {}; if (browserLocale) { params.locale = browserLocale; - if (browserLocale.startsWith(cachedLocale)) { + if (cachedLocale && browserLocale.startsWith(cachedLocale)) { const bundleTimestamp = localStorage.getItem('l10n.timestamp'); if (bundleTimestamp !== null && checkCachedBundle()) { params.ts = bundleTimestamp; @@ -92,52 +103,52 @@ export function requestMessages() { } return getL10nBundle(params).then( - ({ effectiveLocale, messages }) => { + ({ effectiveLocale, messages }: BundleRequestResponse) => { try { - const currentTimestamp = moment().format('YYYY-MM-DDTHH:mm:ssZZ'); + const currentTimestamp = toNotSoISOString(new Date()); localStorage.setItem('l10n.timestamp', currentTimestamp); localStorage.setItem('l10n.locale', effectiveLocale); localStorage.setItem('l10n.bundle', JSON.stringify(messages)); } catch (e) { // do nothing } - configureMoment(effectiveLocale); resetBundle(messages); + return effectiveLocale || browserLocale || DEFAULT_LANGUAGE; }, ({ response }) => { if (response && response.status === 304) { - configureMoment(cachedLocale || browserLocale); resetBundle(JSON.parse(localStorage.getItem('l10n.bundle') || '{}')); } else { throw new Error('Unexpected status code: ' + response.status); } + return cachedLocale || browserLocale || DEFAULT_LANGUAGE; } ); } -export function resetBundle(bundle /*: Object */) { +export function resetBundle(bundle: LanguageBundle) { messages = bundle; } export function installGlobal() { - window.t = translate; - window.tp = translateWithParameters; - window.requestMessages = requestMessages; + (window as any).t = translate; + (window as any).tp = translateWithParameters; + (window as any).requestMessages = requestMessages; } -export function getLocalizedDashboardName(baseName /*: string */) { +export function getLocalizedDashboardName(baseName: string) { const l10nKey = `dashboard.${baseName}.name`; const l10nLabel = translate(l10nKey); return l10nLabel !== l10nKey ? l10nLabel : baseName; } -export function getLocalizedMetricName(metric /*: { key: string, name: string } */) { +export function getLocalizedMetricName(metric: { key: string; name: string }) { const bundleKey = `metric.${metric.key}.name`; const fromBundle = translate(bundleKey); return fromBundle !== bundleKey ? fromBundle : metric.name; } -export function getLocalizedMetricDomain(domainName /*: string */) { +export function getLocalizedMetricDomain(domainName: string) { const bundleKey = `metric_domain.${domainName}`; const fromBundle = translate(bundleKey); return fromBundle !== bundleKey ? fromBundle : domainName; diff --git a/server/sonar-web/src/main/js/helpers/periods.js b/server/sonar-web/src/main/js/helpers/periods.js index 0677d81c13c..4c5ac1c876d 100644 --- a/server/sonar-web/src/main/js/helpers/periods.js +++ b/server/sonar-web/src/main/js/helpers/periods.js @@ -17,7 +17,6 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import moment from 'moment'; import { translate, translateWithParameters } from './l10n'; export function getPeriod(periods, index) { @@ -51,7 +50,7 @@ export function getPeriodDate(period) { return null; } - return moment(period.date).toDate(); + return new Date(period.date); } export function getLeakPeriodLabel(periods) { diff --git a/server/sonar-web/src/main/js/helpers/query.js b/server/sonar-web/src/main/js/helpers/query.js index f7c0f2b6a9c..a87eefe5e3f 100644 --- a/server/sonar-web/src/main/js/helpers/query.js +++ b/server/sonar-web/src/main/js/helpers/query.js @@ -19,7 +19,7 @@ */ // @flow import { isNil, omitBy } from 'lodash'; -import moment from 'moment'; +import { isValidDate, toNotSoISOString } from './dates'; /*:: export type RawQuery = { [string]: any }; @@ -65,9 +65,11 @@ export function parseAsBoolean( } export function parseAsDate(value /*: ?string */) /*: Date | void */ { - const date = moment(value); - if (value && date) { - return date.toDate(); + if (value) { + const date = new Date(value); + if (isValidDate(date)) { + return date; + } } } @@ -85,7 +87,7 @@ export function parseAsArray(value /*: ?string */, itemParser /*: string => * */ export function serializeDate(value /*: ?Date */) /*: string | void */ { if (value != null && value.toISOString) { - return moment(value).format('YYYY-MM-DDTHH:mm:ssZZ'); + return toNotSoISOString(value); } } diff --git a/server/sonar-web/src/main/js/helpers/testUtils.ts b/server/sonar-web/src/main/js/helpers/testUtils.ts index deed3501e74..a0931769355 100644 --- a/server/sonar-web/src/main/js/helpers/testUtils.ts +++ b/server/sonar-web/src/main/js/helpers/testUtils.ts @@ -17,7 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { ShallowWrapper } from 'enzyme'; +import { shallow, ShallowWrapper } from 'enzyme'; +import { IntlProvider } from 'react-intl'; export const mockEvent = { target: { blur() {} }, @@ -69,3 +70,9 @@ export function doAsync(fn: Function): Promise<void> { }, 0); }); } + +const intlProvider = new IntlProvider({ locale: 'en' }, {}); +const { intl } = intlProvider.getChildContext(); +export function shallowWithIntl(node, options = {}) { + return shallow(node, { ...options, context: { intl, ...options.context } }); +} |