@@ -94,7 +94,6 @@ | |||
"no-array-constructor": "warn", | |||
"no-bitwise": "warn", | |||
"no-lonely-if": "error", | |||
"no-mixed-operators": "warn", | |||
"no-multi-assign": "warn", | |||
"no-nested-ternary": "warn", | |||
"no-new-object": "warn", |
@@ -24,7 +24,6 @@ | |||
"jquery": "2.2.0", | |||
"keymaster": "1.6.2", | |||
"lodash": "4.17.4", | |||
"numeral": "1.5.3", | |||
"prop-types": "15.6.0", | |||
"rc-tooltip": "3.5.0", | |||
"react": "15.6.2", | |||
@@ -53,7 +52,6 @@ | |||
"@types/jest": "21.1.5", | |||
"@types/jquery": "3.2.11", | |||
"@types/lodash": "4.14.79", | |||
"@types/numeral": "0.0.22", | |||
"@types/prop-types": "15.5.2", | |||
"@types/react": "16.0.2", | |||
"@types/react-dom": "15.5.2", |
@@ -4,7 +4,7 @@ | |||
{{#each values}} | |||
<a class="facet search-navigator-facet js-facet" data-value="{{val}}" title="{{default label val}}"> | |||
<span class="facet-name">{{default label val}}{{#if extra}} <span class="note">{{extra}}</span>{{/if}}</span> | |||
<span class="facet-stat">{{numberShort count}}</span> | |||
<span class="facet-stat">{{formatMeasure count 'SHORT_INT'}}</span> | |||
</a> | |||
{{/each}} | |||
</div> |
@@ -4,7 +4,7 @@ | |||
{{#each values}} | |||
<a class="facet search-navigator-facet js-facet" data-value="{{val}}" title="{{default label val}}"> | |||
<span class="facet-name">{{default label val}}{{#if extra}} <span class="note">{{extra}}</span>{{/if}}</span> | |||
<span class="facet-stat">{{numberShort count}}</span> | |||
<span class="facet-stat">{{formatMeasure count 'SHORT_INT'}}</span> | |||
</a> | |||
{{/each}} | |||
@@ -4,7 +4,7 @@ | |||
{{#each values}} | |||
<a class="facet search-navigator-facet search-navigator-facet-half js-facet" data-value="{{val}}" title="{{t 'severity' val}}"> | |||
<span class="facet-name">{{severityIcon val}} {{t 'severity' val}}</span> | |||
<span class="facet-stat">{{numberShort count}}</span> | |||
<span class="facet-stat">{{formatMeasure count 'SHORT_INT'}}</span> | |||
</a> | |||
{{/each}} | |||
</div> |
@@ -5,9 +5,7 @@ | |||
<a class="facet search-navigator-facet js-facet" | |||
data-value="{{val}}"> | |||
<span class="facet-name">{{issueTypeIcon val}} {{t 'issue.type' val}}</span> | |||
<span class="facet-stat"> | |||
{{numberShort count}} | |||
</span> | |||
<span class="facet-stat">{{formatMeasure count 'SHORT_INT'}}</span> | |||
</a> | |||
{{/each}} | |||
</div> |
@@ -17,7 +17,7 @@ exports[`should render correctly 1`] = ` | |||
className="project-activity-graph-tooltip-value text-right spacer-right thin" | |||
colSpan="2" | |||
> | |||
10k | |||
10short_number_suffix.k | |||
</td> | |||
<td> | |||
metric.uncovered_lines.name | |||
@@ -56,7 +56,7 @@ exports[`should render correctly when data is missing 1`] = ` | |||
className="project-activity-graph-tooltip-value text-right spacer-right thin" | |||
colSpan="2" | |||
> | |||
41k | |||
41short_number_suffix.k | |||
</td> | |||
<td> | |||
metric.uncovered_lines.name |
@@ -23,7 +23,7 @@ exports[`renders 1`] = ` | |||
}, | |||
}, | |||
"size": 1734, | |||
"tooltip": "<div class=\\"text-left\\"><div class=\\"little-spacer-bottom\\"><strong>Foo</strong></div><div>metric.reliability_rating.name: –</div><div>metric.security_rating.name: –</div><div>metric.coverage.name: 53.5%</div><div>metric.sqale_index.name: –</div><div>metric.ncloc.name: 1.7k</div></div>", | |||
"tooltip": "<div class=\\"text-left\\"><div class=\\"little-spacer-bottom\\"><strong>Foo</strong></div><div>metric.reliability_rating.name: –</div><div>metric.security_rating.name: –</div><div>metric.coverage.name: 53.5%</div><div>metric.sqale_index.name: –</div><div>metric.ncloc.name: 1.7short_number_suffix.k</div></div>", | |||
"x": 0, | |||
"y": 53.5, | |||
}, |
@@ -38,7 +38,7 @@ exports[`renders 1`] = ` | |||
} | |||
yValues={ | |||
Array [ | |||
"1.7k", | |||
"1.7short_number_suffix.k", | |||
"845", | |||
"73", | |||
"15", |
@@ -33,7 +33,10 @@ beforeEach(() => { | |||
'work_duration.about': '~ {0}', | |||
'metric.level.ERROR': 'Error', | |||
'metric.level.WARN': 'Warning', | |||
'metric.level.OK': 'Ok' | |||
'metric.level.OK': 'Ok', | |||
'short_number_suffix.k': 'k', | |||
'short_number_suffix.m': 'm', | |||
'short_number_suffix.b': 'b' | |||
}); | |||
}); | |||
@@ -18,13 +18,13 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
const { parseDate } = require('../../helpers/dates'); | |||
const { DEFAULT_LANGUAGE } = require('../../helpers/l10n'); | |||
const { getCurrentLocale } = require('../../helpers/l10n'); | |||
module.exports = function(date) { | |||
if (!date) { | |||
return ''; | |||
} | |||
return new Intl.DateTimeFormat(localStorage.getItem('l10n.locale') || DEFAULT_LANGUAGE, { | |||
return new Intl.DateTimeFormat(getCurrentLocale(), { | |||
year: 'numeric', | |||
month: 'long', | |||
day: 'numeric' |
@@ -18,13 +18,13 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
const { parseDate } = require('../../helpers/dates'); | |||
const { DEFAULT_LANGUAGE } = require('../../helpers/l10n'); | |||
const { getCurrentLocale } = require('../../helpers/l10n'); | |||
module.exports = function(date) { | |||
if (!date) { | |||
return ''; | |||
} | |||
return new Intl.DateTimeFormat(localStorage.getItem('l10n.locale') || DEFAULT_LANGUAGE, { | |||
return new Intl.DateTimeFormat(getCurrentLocale(), { | |||
year: 'numeric', | |||
month: 'long', | |||
day: 'numeric', |
@@ -19,10 +19,8 @@ | |||
*/ | |||
const IntlRelativeFormat = require('intl-relativeformat'); | |||
const { parseDate } = require('../../helpers/dates'); | |||
const { DEFAULT_LANGUAGE } = require('../../helpers/l10n'); | |||
const { getCurrentLocale } = require('../../helpers/l10n'); | |||
module.exports = function(date) { | |||
return new IntlRelativeFormat(localStorage.getItem('l10n.locale') || DEFAULT_LANGUAGE).format( | |||
parseDate(date) | |||
); | |||
return new IntlRelativeFormat(getCurrentLocale()).format(parseDate(date)); | |||
}; |
@@ -1,31 +0,0 @@ | |||
/* | |||
* 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 numeral = require('numeral'); | |||
module.exports = function(number) { | |||
let format = '0,0'; | |||
if (number >= 10000) { | |||
format = '0.[0]a'; | |||
} | |||
if (number >= 100000) { | |||
format = '0a'; | |||
} | |||
return numeral(number).format(format); | |||
}; |
@@ -175,3 +175,11 @@ export function getLocalizedMetricDomain(domainName: string) { | |||
const fromBundle = translate(bundleKey); | |||
return fromBundle !== bundleKey ? fromBundle : domainName; | |||
} | |||
export function getCurrentLocale() { | |||
// check `window && window.localStorage` for tests | |||
return ( | |||
(window && window.localStorage && window.localStorage.getItem('l10n.locale')) || | |||
DEFAULT_LANGUAGE | |||
); | |||
} |
@@ -17,8 +17,7 @@ | |||
* 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 numeral from 'numeral'; | |||
import { translate, translateWithParameters } from './l10n'; | |||
import { translate, translateWithParameters, getCurrentLocale } from './l10n'; | |||
const HOURS_IN_DAY = 8; | |||
@@ -134,8 +133,16 @@ function getVariationFormatter(type: string): Formatter { | |||
return FORMATTERS[type] || noFormatter; | |||
} | |||
function genericNumberFormatter(value: number, formatValue?: string): string { | |||
return numeral(value).format(formatValue); | |||
function numberFormatter( | |||
value: number, | |||
minimumFractionDigits = 0, | |||
maximumFractionDigits = minimumFractionDigits | |||
) { | |||
const { format } = new Intl.NumberFormat(getCurrentLocale(), { | |||
minimumFractionDigits, | |||
maximumFractionDigits | |||
}); | |||
return format(value); | |||
} | |||
function noFormatter(value: string | number): string | number { | |||
@@ -147,22 +154,26 @@ function emptyFormatter(): string { | |||
} | |||
function intFormatter(value: number): string { | |||
return genericNumberFormatter(value, '0,0'); | |||
return numberFormatter(value); | |||
} | |||
function intVariationFormatter(value: number): string { | |||
return genericNumberFormatter(value, '+0,0'); | |||
const prefix = value < 0 ? '-' : '+'; | |||
return prefix + intFormatter(Math.abs(value)); | |||
} | |||
function shortIntFormatter(value: number): string { | |||
let format = '0,0'; | |||
if (value >= 1000) { | |||
format = '0.[0]a'; | |||
} | |||
if (value >= 10000) { | |||
format = '0a'; | |||
if (value >= 1e9) { | |||
return numberFormatter(value / 1e9) + translate('short_number_suffix.b'); | |||
} else if (value >= 1e6) { | |||
return numberFormatter(value / 1e6) + translate('short_number_suffix.m'); | |||
} else if (value >= 1e4) { | |||
return numberFormatter(value / 1e3) + translate('short_number_suffix.k'); | |||
} else if (value >= 1e3) { | |||
return numberFormatter(value / 1e3, 0, 1) + translate('short_number_suffix.k'); | |||
} else { | |||
return numberFormatter(value); | |||
} | |||
return genericNumberFormatter(value, format); | |||
} | |||
function shortIntVariationFormatter(value: number): string { | |||
@@ -171,11 +182,12 @@ function shortIntVariationFormatter(value: number): string { | |||
} | |||
function floatFormatter(value: number): string { | |||
return genericNumberFormatter(value, '0,0.0[0000]'); | |||
return numberFormatter(value, 1, 5); | |||
} | |||
function floatVariationFormatter(value: number): string { | |||
return value === 0 ? '+0.0' : genericNumberFormatter(value, '+0,0.0[0000]'); | |||
const prefix = value < 0 ? '-' : '+'; | |||
return prefix + floatFormatter(Math.abs(value)); | |||
} | |||
function percentFormatter(value: string | number, options: { decimals?: number } = {}): string { | |||
@@ -183,9 +195,9 @@ function percentFormatter(value: string | number, options: { decimals?: number } | |||
value = parseFloat(value); | |||
} | |||
if (options.decimals) { | |||
return genericNumberFormatter(value / 100, `0,0.${'0'.repeat(options.decimals)}%`); | |||
return numberFormatter(value, options.decimals) + '%'; | |||
} | |||
return value === 100 ? '100%' : genericNumberFormatter(value / 100, '0,0.0%'); | |||
return value === 100 ? '100%' : numberFormatter(value, 1) + '%'; | |||
} | |||
function percentVariationFormatter( | |||
@@ -195,10 +207,8 @@ function percentVariationFormatter( | |||
if (typeof value === 'string') { | |||
value = parseFloat(value); | |||
} | |||
if (options.decimals) { | |||
return genericNumberFormatter(value / 100, `+0,0.${'0'.repeat(options.decimals)}%`); | |||
} | |||
return value === 0 ? '+0.0%' : genericNumberFormatter(value / 100, '+0,0.0%'); | |||
const prefix = value < 0 ? '-' : '+'; | |||
return prefix + percentFormatter(Math.abs(value), options); | |||
} | |||
function ratingFormatter(value: string | number): string { |
@@ -61,10 +61,6 @@ | |||
version "8.0.22" | |||
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.22.tgz#9c6bfee1f45f5e9952ff6b487e657ecca48c7777" | |||
"@types/numeral@0.0.22": | |||
version "0.0.22" | |||
resolved "https://registry.yarnpkg.com/@types/numeral/-/numeral-0.0.22.tgz#86bef1f0a2d743afdc2ef3168d45f2905e1a0b93" | |||
"@types/prop-types@15.5.2": | |||
version "15.5.2" | |||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.2.tgz#3c6b8dceb2906cc87fe4358e809f9d20c8d59be1" | |||
@@ -5258,10 +5254,6 @@ number-is-nan@^1.0.0: | |||
version "1.0.1" | |||
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" | |||
numeral@1.5.3: | |||
version "1.5.3" | |||
resolved "https://registry.yarnpkg.com/numeral/-/numeral-1.5.3.tgz#a4c3eba68239580509f818267c77243bce43ff62" | |||
"nwmatcher@>= 1.3.9 < 2.0.0": | |||
version "1.4.1" | |||
resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.1.tgz#7ae9b07b0ea804db7e25f05cb5fe4097d4e4949f" |
@@ -217,6 +217,9 @@ no_results_search.favorites.2=Would you like to search among {url} projects? | |||
page_extension_failed=Page extension failed. | |||
set_as_default=Set as Default | |||
unset_as_default=Unset as Default | |||
short_number_suffix.k=k | |||
short_number_suffix.m=m | |||
short_number_suffix.b=b | |||
show_more=Show More | |||
should_be_unique=Should be unique | |||
since_x=since {0} |