aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/helpers
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2017-08-18 17:47:37 +0200
committerGrégoire Aubert <gregoire.aubert@sonarsource.com>2017-08-25 11:05:36 +0200
commitf6276b3b6fecce2b160ed8bdc62a3e87439249e4 (patch)
treed1c69e7f786b8693fb13dd816624794eb6b07ae7 /server/sonar-web/src/main/js/helpers
parent1ddf3ee7dbf26116afb767003a8a0698965c4f70 (diff)
downloadsonarqube-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.ts69
-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.js3
-rw-r--r--server/sonar-web/src/main/js/helpers/dates.ts90
-rw-r--r--server/sonar-web/src/main/js/helpers/handlebars/d.js8
-rw-r--r--server/sonar-web/src/main/js/helpers/handlebars/dt.js10
-rw-r--r--server/sonar-web/src/main/js/helpers/handlebars/fromNow.js4
-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.js3
-rw-r--r--server/sonar-web/src/main/js/helpers/query.js12
-rw-r--r--server/sonar-web/src/main/js/helpers/testUtils.ts9
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 } });
+}