]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-22049 Align formatMeasure
authorViktor Vorona <viktor.vorona@sonarsource.com>
Tue, 23 Apr 2024 14:41:37 +0000 (16:41 +0200)
committerMatteo Mara <matteo.mara@sonarsource.com>
Tue, 30 Apr 2024 08:59:03 +0000 (10:59 +0200)
66 files changed:
server/sonar-web/src/main/js/app/components/nav/component/branch-like/QualityGateStatus.tsx
server/sonar-web/src/main/js/apps/account/projects/ProjectCard.tsx
server/sonar-web/src/main/js/apps/background-tasks/components/StatPendingTime.tsx
server/sonar-web/src/main/js/apps/code/components/ComponentMeasure.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/BulkChangeModal.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/Facet.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsIssues.tsx
server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.tsx
server/sonar-web/src/main/js/apps/component-measures/drilldown/BubbleChartView.tsx
server/sonar-web/src/main/js/apps/component-measures/drilldown/ColorRatingsLegend.tsx
server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.tsx
server/sonar-web/src/main/js/apps/component-measures/drilldown/MeasureCell.tsx
server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.tsx
server/sonar-web/src/main/js/apps/component-measures/sidebar/SubnavigationMeasureValue.tsx
server/sonar-web/src/main/js/apps/issues/components/TotalEffort.tsx
server/sonar-web/src/main/js/apps/issues/sidebar/CreationDateFacet.tsx
server/sonar-web/src/main/js/apps/issues/sidebar/ListStyleFacet.tsx
server/sonar-web/src/main/js/apps/issues/sidebar/ListStyleFacetFooter.tsx
server/sonar-web/src/main/js/apps/issues/utils.ts
server/sonar-web/src/main/js/apps/overview/branches/AnalysisVariations.tsx
server/sonar-web/src/main/js/apps/overview/branches/BranchMetaTopBar.tsx
server/sonar-web/src/main/js/apps/overview/branches/NewCodeMeasuresPanel.tsx
server/sonar-web/src/main/js/apps/overview/branches/OverallCodeMeasuresPanel.tsx
server/sonar-web/src/main/js/apps/overview/branches/QualityGateCondition.tsx
server/sonar-web/src/main/js/apps/overview/branches/QualityGateSimplifiedCondition.tsx
server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureBreakdownCard.tsx
server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureCard.tsx
server/sonar-web/src/main/js/apps/overview/components/MeasuresCardNumber.tsx
server/sonar-web/src/main/js/apps/overview/components/MeasuresCardPercent.tsx
server/sonar-web/src/main/js/apps/overview/pullRequests/BranchQualityGateConditions.tsx
server/sonar-web/src/main/js/apps/overview/pullRequests/IssueMeasuresCard.tsx
server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestMetaTopBar.tsx
server/sonar-web/src/main/js/apps/overview/utils.tsx
server/sonar-web/src/main/js/apps/projectBranches/components/LifetimeInformationRenderer.tsx
server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaSize.tsx
server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx
server/sonar-web/src/main/js/apps/projects/filters/RatingFacet.tsx
server/sonar-web/src/main/js/apps/projects/filters/SecurityReviewFilter.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/CaycCondition.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/ConditionValue.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/ConditionValueDescription.tsx
server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesRow.tsx
server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/StatusUpdateSuccessModal.tsx
server/sonar-web/src/main/js/apps/system/utils.ts
server/sonar-web/src/main/js/apps/webhooks/components/DeliveryItem.tsx
server/sonar-web/src/main/js/components/SourceViewer/SourceViewerHeader.tsx
server/sonar-web/src/main/js/components/activity-graph/DataTableModal.tsx
server/sonar-web/src/main/js/components/activity-graph/GraphHistory.tsx
server/sonar-web/src/main/js/components/activity-graph/GraphsTooltipsContentCoverage.tsx
server/sonar-web/src/main/js/components/activity-graph/GraphsTooltipsContentDuplication.tsx
server/sonar-web/src/main/js/components/charts/ColorBoxLegend.tsx
server/sonar-web/src/main/js/components/charts/LanguageDistribution.tsx
server/sonar-web/src/main/js/components/common/PageCounter.tsx
server/sonar-web/src/main/js/components/controls/ListFooter.tsx
server/sonar-web/src/main/js/components/issue/components/IssueChangelogDiff.tsx
server/sonar-web/src/main/js/components/issue/components/__tests__/IssueChangelogDiff-test.tsx
server/sonar-web/src/main/js/components/measure/Measure.tsx
server/sonar-web/src/main/js/components/measure/MeasureIndicator.tsx
server/sonar-web/src/main/js/components/measure/RatingTooltipContent.tsx
server/sonar-web/src/main/js/components/ui/FilesCounter.tsx
server/sonar-web/src/main/js/components/ui/Rating.tsx
server/sonar-web/src/main/js/helpers/__tests__/measures-test.ts
server/sonar-web/src/main/js/helpers/measures.ts
server/sonar-web/src/main/js/sonar-aligned/helpers/__tests__/measures-test.ts [new file with mode: 0644]
server/sonar-web/src/main/js/sonar-aligned/helpers/measures.ts [new file with mode: 0644]

index 534262f9d620fa649bfa17cc92ee6c13f4de74cd..4b6cad57e4aad128c3745e75c8a0f4c760f56a70 100644 (file)
@@ -21,7 +21,7 @@ import classNames from 'classnames';
 import { QualityGateIndicator } from 'design-system';
 import React from 'react';
 import { translateWithParameters } from '../../../../../helpers/l10n';
-import { formatMeasure } from '../../../../../helpers/measures';
+import { formatMeasure } from '../../../../../sonar-aligned/helpers/measures';
 import { BranchLike } from '../../../../../types/branch-like';
 import { MetricType } from '../../../../../types/metrics';
 
index ecb186bf184d8fe8f11d607d4351dee50b2dcf96..1cbd7c8ca538fd6703ab10ddb67937a18f75725f 100644 (file)
@@ -31,9 +31,9 @@ import MetaLink from '../../../components/common/MetaLink';
 import Tooltip from '../../../components/controls/Tooltip';
 import DateFromNow from '../../../components/intl/DateFromNow';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { formatMeasure } from '../../../helpers/measures';
 import { orderLinks } from '../../../helpers/projectLinks';
 import { getProjectUrl } from '../../../helpers/urls';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { MetricType } from '../../../types/metrics';
 import { MyProject, ProjectLink, Status } from '../../../types/types';
 
index d52332bbc76ac6f2d577fca317058ae375cdaf9f..7b4fc52c0f1d899fe6544cb657e1ccc8048ef7cf 100644 (file)
@@ -20,8 +20,8 @@
 import { HelperHintIcon } from 'design-system';
 import * as React from 'react';
 import { translate } from '../../../helpers/l10n';
-import { formatMeasure } from '../../../helpers/measures';
 import HelpTooltip from '../../../sonar-aligned/components/controls/HelpTooltip';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 
 // Do not display the pending time for values smaller than this threshold (in ms)
 const MIN_PENDING_TIME_THRESHOLD = 1000;
index b4b21a0fab794ea07697c793f477a2ea34023aa2..a4a3a41824ab45c2c32c6c5b2400ac93a50793a2 100644 (file)
@@ -35,9 +35,9 @@ import {
 import { translateWithParameters } from '../../../helpers/l10n';
 import {
   areCCTMeasuresComputed as areCCTMeasuresComputedFn,
-  formatMeasure,
   isDiffMetric,
 } from '../../../helpers/measures';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { isApplication, isProject } from '../../../types/component';
 import { MetricKey, MetricType } from '../../../types/metrics';
 import { Metric, Status, ComponentMeasure as TypeComponentMeasure } from '../../../types/types';
index 8f1343a4efc6f9d20872ae7ad684d9265dafd4f6..9137773c91e11cb0c197cbbd3014fc291b79bd33 100644 (file)
@@ -22,7 +22,7 @@ import * as React from 'react';
 import { Profile, bulkActivateRules, bulkDeactivateRules } from '../../../api/quality-profiles';
 import withLanguagesContext from '../../../app/components/languages/withLanguagesContext';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { formatMeasure } from '../../../helpers/measures';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { Languages } from '../../../types/languages';
 import { MetricType } from '../../../types/metrics';
 import { Dict } from '../../../types/types';
index 485e8261e07f9bdbe0f974bf637a10b713f03ef2..4156a90e8abb0473a16ba414054e03f7353d647b 100644 (file)
@@ -23,7 +23,7 @@ import { orderBy, sortBy, without } from 'lodash';
 import * as React from 'react';
 import Tooltip from '../../../components/controls/Tooltip';
 import { translate } from '../../../helpers/l10n';
-import { formatMeasure } from '../../../helpers/measures';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { MetricType } from '../../../types/metrics';
 import { Dict } from '../../../types/types';
 import { FacetItemsList } from '../../issues/sidebar/FacetItemsList';
index f723b214a48f8baad3955b8ec0e4cbbc6924c729..dbfd31d4765b0f93a6041672eb36c09fb48d6802 100644 (file)
@@ -29,8 +29,8 @@ import withAvailableFeatures, {
 import Tooltip from '../../../components/controls/Tooltip';
 import { DEFAULT_ISSUES_QUERY } from '../../../components/shared/utils';
 import { translate } from '../../../helpers/l10n';
-import { formatMeasure } from '../../../helpers/measures';
 import { getIssuesUrl } from '../../../helpers/urls';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { Feature } from '../../../types/features';
 import { FacetName } from '../../../types/issues';
 import { MetricType } from '../../../types/metrics';
index 4fd76d7d88b44fde056fbddf931930d68023462d..b772df0a85022c5d0dea72d32b5c87e6969e9e7f 100644 (file)
@@ -25,8 +25,9 @@ import LanguageDistribution from '../../../components/charts/LanguageDistributio
 import Tooltip from '../../../components/controls/Tooltip';
 import Measure from '../../../components/measure/Measure';
 import { getLocalizedMetricName, translate, translateWithParameters } from '../../../helpers/l10n';
-import { formatMeasure, isDiffMetric } from '../../../helpers/measures';
+import { isDiffMetric } from '../../../helpers/measures';
 import { getMeasureHistoryUrl } from '../../../helpers/urls';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { BranchLike } from '../../../types/branch-like';
 import { ComponentQualifier } from '../../../types/component';
 import { MetricKey, MetricType } from '../../../types/metrics';
index 28bee63698c2db2b24168b020ae6c6f01403c896..0dd396ef30dca5127a8f8356c8b524f5165a5a15 100644 (file)
@@ -33,10 +33,11 @@ import {
   translate,
   translateWithParameters,
 } from '../../../helpers/l10n';
-import { formatMeasure, isDiffMetric } from '../../../helpers/measures';
+import { isDiffMetric } from '../../../helpers/measures';
 import { isDefined } from '../../../helpers/types';
 import { getComponentDrilldownUrl } from '../../../helpers/urls';
 import HelpTooltip from '../../../sonar-aligned/components/controls/HelpTooltip';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { BranchLike } from '../../../types/branch-like';
 import { isProject, isView } from '../../../types/component';
 import { MetricKey } from '../../../types/metrics';
index 7b521886e71bf74653d4a70383d289c5ec22b963..9eecb2e95ca1ff605719517f55c6c28add84643c 100644 (file)
@@ -20,7 +20,7 @@
 import { ColorFilterOption, ColorsLegend } from 'design-system';
 import * as React from 'react';
 import { translateWithParameters } from '../../../helpers/l10n';
-import { formatMeasure } from '../../../helpers/measures';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { MetricType } from '../../../types/metrics';
 
 export interface ColorRatingsLegendProps {
index 6db5c021d3a5645e8c244199b22c21f8864b5a71..d7370ee781fe2ff98b6ac32da1f85c6605f403c9 100644 (file)
@@ -24,7 +24,8 @@ import ListFooter from '../../../components/controls/ListFooter';
 import { isInput, isShortcut } from '../../../helpers/keyboardEventHelpers';
 import { KeyboardKeys } from '../../../helpers/keycodes';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { formatMeasure, isDiffMetric, isPeriodBestValue } from '../../../helpers/measures';
+import { isDiffMetric, isPeriodBestValue } from '../../../helpers/measures';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { BranchLike } from '../../../types/branch-like';
 import { MeasurePageView } from '../../../types/measures';
 import { MetricType } from '../../../types/metrics';
index e4354dbf8c90d3e3816a3eb0804474d656e0ff4e..ff45c4a3d6d1a76e38e930936b27ac57f68bd105 100644 (file)
@@ -21,7 +21,8 @@ import { MetricsRatingBadge, NumericalCell, RatingLabel } from 'design-system';
 import * as React from 'react';
 import Measure from '../../../components/measure/Measure';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { formatMeasure, getCCTMeasureValue, isDiffMetric } from '../../../helpers/measures';
+import { getCCTMeasureValue, isDiffMetric } from '../../../helpers/measures';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { MetricType } from '../../../types/metrics';
 import { ComponentMeasureEnhanced, MeasureEnhanced, Metric } from '../../../types/types';
 
index d26a3a160b76c4569d7de8afe19b7058c14d180c..b00b48be0c634b67594de2eaaa165e24efe3b72b 100644 (file)
@@ -36,8 +36,9 @@ import ColorBoxLegend from '../../../components/charts/ColorBoxLegend';
 import ColorGradientLegend from '../../../components/charts/ColorGradientLegend';
 import { getComponentMeasureUniqueKey } from '../../../helpers/component';
 import { getLocalizedMetricName, translate } from '../../../helpers/l10n';
-import { formatMeasure, isDiffMetric } from '../../../helpers/measures';
+import { isDiffMetric } from '../../../helpers/measures';
 import { isDefined } from '../../../helpers/types';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { MetricKey, MetricType } from '../../../types/metrics';
 import { ComponentMeasureEnhanced, ComponentMeasureIntern, Metric } from '../../../types/types';
 import EmptyResult from './EmptyResult';
index ab344f0ff6b07f97ae68e852a272f7a72962d8a0..0b9a0e545c8006838e0c27dc57b3540c632602aa 100644 (file)
@@ -21,7 +21,8 @@ import { MetricsRatingBadge, Note, RatingLabel } from 'design-system';
 import React from 'react';
 import Measure from '../../../components/measure/Measure';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { formatMeasure, isDiffMetric } from '../../../helpers/measures';
+import { isDiffMetric } from '../../../helpers/measures';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { MetricType } from '../../../types/metrics';
 import { MeasureEnhanced } from '../../../types/types';
 
index 2b7fc276ccc8634c543351fe3d35988849f7372a..34fc771e66747759f44e9dd56ae9dcadcf6e6258 100644 (file)
@@ -20,7 +20,7 @@
 import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
 import { translate } from '../../../helpers/l10n';
-import { formatMeasure } from '../../../helpers/measures';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 
 export default function TotalEffort({ effort }: { effort: number }) {
   return (
index 829e23792f4056ec3db5e925ad439a2bf5681662..edd5ace2cd706d18d0f35acccebe5c9dd9c6a820 100644 (file)
@@ -27,7 +27,7 @@ import DateFromNow from '../../../components/intl/DateFromNow';
 import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
 import { parseDate } from '../../../helpers/dates';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { formatMeasure } from '../../../helpers/measures';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { MetricType } from '../../../types/metrics';
 import { Component, Dict } from '../../../types/types';
 import { Query } from '../utils';
index 4552da2afc08326b6879bad81de710bcd9a38a97..36cf186598c0e7b239b3b765fda9e109e15daa08 100644 (file)
@@ -23,9 +23,9 @@ import * as React from 'react';
 import ListFooter from '../../../components/controls/ListFooter';
 import Tooltip from '../../../components/controls/Tooltip';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { formatMeasure } from '../../../helpers/measures';
 import { queriesEqual } from '../../../helpers/query';
 import { isDefined } from '../../../helpers/types';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { MetricType } from '../../../types/metrics';
 import { Dict, Paging, RawQuery } from '../../../types/types';
 import { FacetItemsList } from './FacetItemsList';
index 635394cc2d8dd97d0e4a0e6e8c5daaac9c24627a..14a054bf641d20aa646ce4ad3c7ec2490b6bd0f6 100644 (file)
@@ -21,7 +21,7 @@ import { useTheme } from '@emotion/react';
 import { DiscreetLink, Theme, themeColor } from 'design-system';
 import * as React from 'react';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { formatMeasure } from '../../../helpers/measures';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { MetricType } from '../../../types/metrics';
 
 export interface Props {
index 67e936bce8b6d53e1362b2d091d34d486dc06ff8..1e0da76fa239cdf02ba50353c5400eff4bf71d6c 100644 (file)
@@ -20,7 +20,6 @@
 import { intersection, isArray, uniq } from 'lodash';
 import { getUsers } from '../../api/users';
 import { DEFAULT_ISSUES_QUERY } from '../../components/shared/utils';
-import { formatMeasure } from '../../helpers/measures';
 import {
   cleanQuery,
   parseAsArray,
@@ -35,6 +34,7 @@ import {
 } from '../../helpers/query';
 import { get, save } from '../../helpers/storage';
 import { isDefined } from '../../helpers/types';
+import { formatMeasure } from '../../sonar-aligned/helpers/measures';
 import {
   CleanCodeAttributeCategory,
   SoftwareImpactSeverity,
index d15d4413a7b47944d5f351cbbad532e569f73a2c..6a372a5c18f2852e21907d2b44ca3132f84dd4f0 100644 (file)
@@ -21,7 +21,7 @@ import styled from '@emotion/styled';
 import { TrendDirection, TrendIcon, TrendType, themeColor } from 'design-system';
 import React from 'react';
 import { FormattedMessage } from 'react-intl';
-import { formatMeasure } from '../../../helpers/measures';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { MetricType } from '../../../types/metrics';
 import { AnalysisMeasuresVariations } from '../../../types/project-activity';
 
index ab91aa2661c0e857ee4142da9138c932396d2dc1..f3df27076acd14e4a07ee70161a49623157b3f92 100644 (file)
@@ -23,7 +23,8 @@ import { useIntl } from 'react-intl';
 import { getCurrentPage } from '../../../app/components/nav/component/utils';
 import ComponentReportActions from '../../../components/controls/ComponentReportActions';
 import HomePageSelect from '../../../components/controls/HomePageSelect';
-import { findMeasure, formatMeasure } from '../../../helpers/measures';
+import { findMeasure } from '../../../helpers/measures';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { Branch } from '../../../types/branch-like';
 import { MetricKey, MetricType } from '../../../types/metrics';
 import { Component, MeasureEnhanced } from '../../../types/types';
index 94aae3bb9870edfc223c3c93e8110eebbb61fe5b..3bf4823d83fc02cbbb4741457c27b64926e19dab 100644 (file)
@@ -34,13 +34,14 @@ import React from 'react';
 import { useIntl } from 'react-intl';
 import { getLeakValue } from '../../../components/measure/utils';
 import { DEFAULT_ISSUES_QUERY } from '../../../components/shared/utils';
-import { findMeasure, formatMeasure, formatRating } from '../../../helpers/measures';
+import { findMeasure, formatRating } from '../../../helpers/measures';
 import {
   CodeScope,
   getComponentIssuesUrl,
   getComponentSecurityHotspotsUrl,
 } from '../../../helpers/urls';
 import { getBranchLikeQuery } from '../../../sonar-aligned/helpers/branch-like';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { Branch } from '../../../types/branch-like';
 import { isApplication } from '../../../types/component';
 import { IssueStatus } from '../../../types/issues';
index a594bfbf0edd00b51a9880a30e1a7f3d615ce6c4..e803855e9370d240ef9f76e0bb6e85e5208db19e 100644 (file)
@@ -26,13 +26,14 @@ import {
 } from 'design-system';
 import * as React from 'react';
 import { useIntl } from 'react-intl';
-import { findMeasure, formatMeasure, formatRating } from '../../../helpers/measures';
+import { findMeasure, formatRating } from '../../../helpers/measures';
 import {
   CodeScope,
   getComponentIssuesUrl,
   getComponentSecurityHotspotsUrl,
 } from '../../../helpers/urls';
 import { getBranchLikeQuery } from '../../../sonar-aligned/helpers/branch-like';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { Branch } from '../../../types/branch-like';
 import { SoftwareQuality } from '../../../types/clean-code-taxonomy';
 import { isApplication } from '../../../types/component';
index d07769a9c80db7c2b73e982d278cad2f495ebbac..182b19781c6b88a6f48d2bafc1a36716175f9aa7 100644 (file)
@@ -28,7 +28,7 @@ import {
   propsToIssueParams,
 } from '../../../components/shared/utils';
 import { translate } from '../../../helpers/l10n';
-import { formatMeasure, isDiffMetric, localizeMetric } from '../../../helpers/measures';
+import { isDiffMetric, localizeMetric } from '../../../helpers/measures';
 import { getOperatorLabel } from '../../../helpers/qualityGates';
 import {
   getComponentDrilldownUrl,
@@ -36,6 +36,7 @@ import {
   getComponentSecurityHotspotsUrl,
 } from '../../../helpers/urls';
 import { getBranchLikeQuery } from '../../../sonar-aligned/helpers/branch-like';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { BranchLike } from '../../../types/branch-like';
 import { IssueType } from '../../../types/issues';
 import { MetricKey, MetricType } from '../../../types/metrics';
@@ -139,7 +140,7 @@ export default class QualityGateCondition extends React.PureComponent<Props> {
     if (metric.type !== MetricType.Rating) {
       const actual = (condition.period ? measure.period?.value : measure.value) as string;
       const formattedValue = formatMeasure(actual, metric.type, {
-        decimal: 2,
+        decimals: 1,
         omitExtraDecimalZeros: metric.type === MetricType.Percent,
       });
       return `${formattedValue} ${subText}`;
index 4c1b3e9ba6d892024d7d1099bfc678726809ab74..e31e94ea4ee0376094a25bd1d3468838b512c744 100644 (file)
@@ -21,9 +21,10 @@ import { Highlight, LinkBox } from 'design-system';
 import * as React from 'react';
 import { propsToIssueParams } from '../../../components/shared/utils';
 import { translate } from '../../../helpers/l10n';
-import { formatMeasure, isDiffMetric, localizeMetric } from '../../../helpers/measures';
+import { isDiffMetric, localizeMetric } from '../../../helpers/measures';
 import { getComponentIssuesUrl } from '../../../helpers/urls';
 import { getBranchLikeQuery } from '../../../sonar-aligned/helpers/branch-like';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { BranchLike } from '../../../types/branch-like';
 import { MetricKey, MetricType } from '../../../types/metrics';
 import { QualityGateStatusConditionEnhanced } from '../../../types/quality-gates';
index 4e6799fd95d64fcc5e334b83a550fd1eb02899d6..0f3620e3d90fce8c15016d7f41e338b42ba4a059 100644 (file)
@@ -23,8 +23,8 @@ import { DiscreetLinkBox, Tooltip, themeColor, themeContrast } from 'design-syst
 import * as React from 'react';
 import { useIntl } from 'react-intl';
 import { DEFAULT_ISSUES_QUERY } from '../../../components/shared/utils';
-import { formatMeasure } from '../../../helpers/measures';
 import { getComponentIssuesUrl } from '../../../helpers/urls';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { Branch } from '../../../types/branch-like';
 import { SoftwareImpactSeverity, SoftwareQuality } from '../../../types/clean-code-taxonomy';
 import { MetricType } from '../../../types/metrics';
index 0da8bccbde3a6cba425893a931bb164107521ac3..40814c2a15e710bf8d4ece2d4d7be39b8a4c7bbc 100644 (file)
@@ -28,9 +28,9 @@ import {
   SOFTWARE_QUALITIES_METRIC_KEYS_MAP,
   getIssueTypeBySoftwareQuality,
 } from '../../../helpers/issues';
-import { formatMeasure } from '../../../helpers/measures';
 import { isDefined } from '../../../helpers/types';
 import { getComponentIssuesUrl } from '../../../helpers/urls';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { Branch } from '../../../types/branch-like';
 import {
   SoftwareImpactMeasureData,
index feea138e269ab003c0e233e746c756af1cdbe3eb..7d186fe1fd6aa0f1b40da91d669f8d5b5b9f6c4b 100644 (file)
@@ -21,7 +21,7 @@ import { LightLabel, TextError } from 'design-system';
 import * as React from 'react';
 import { useIntl } from 'react-intl';
 import { To } from 'react-router-dom';
-import { formatMeasure } from '../../../helpers/measures';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { MetricKey, MetricType } from '../../../types/metrics';
 import { QualityGateStatusConditionEnhanced } from '../../../types/quality-gates';
 import { Status, getConditionRequiredLabel } from '../utils';
index 8bdb401d6f4d4b31e800e00e65bea894761b6714..4875e5a078852559bc781c61d8db2ea0f915642f 100644 (file)
@@ -30,8 +30,9 @@ import { FormattedMessage, useIntl } from 'react-intl';
 import { To } from 'react-router-dom';
 import { duplicationRatingConverter, getLeakValue } from '../../../components/measure/utils';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { findMeasure, formatMeasure, localizeMetric } from '../../../helpers/measures';
+import { findMeasure, localizeMetric } from '../../../helpers/measures';
 import { getComponentDrilldownUrl } from '../../../helpers/urls';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { BranchLike } from '../../../types/branch-like';
 import { MetricKey, MetricType } from '../../../types/metrics';
 import { QualityGateStatusConditionEnhanced } from '../../../types/quality-gates';
index 9fe6fc70bb2735b19c46f699df7e5855cc1ea678..f2586295e7e6f8a1895fb288b68f2c0ce1b387f8 100644 (file)
@@ -27,13 +27,14 @@ import {
   propsToIssueParams,
 } from '../../../components/shared/utils';
 import { getLocalizedMetricName, translate } from '../../../helpers/l10n';
-import { formatMeasure, getShortType, isDiffMetric } from '../../../helpers/measures';
+import { getShortType, isDiffMetric } from '../../../helpers/measures';
 import {
   getComponentDrilldownUrl,
   getComponentIssuesUrl,
   getComponentSecurityHotspotsUrl,
 } from '../../../helpers/urls';
 import { getBranchLikeQuery } from '../../../sonar-aligned/helpers/branch-like';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { BranchLike } from '../../../types/branch-like';
 import { IssueType } from '../../../types/issues';
 import { MetricType } from '../../../types/metrics';
index aaed573861a641f4b03bd884c2a7e4c75ba1236e..613c243c5d4d8c451ae922602143628864d38f23 100644 (file)
@@ -35,9 +35,10 @@ import * as React from 'react';
 import { useIntl } from 'react-intl';
 import { getLeakValue } from '../../../components/measure/utils';
 import { DEFAULT_ISSUES_QUERY } from '../../../components/shared/utils';
-import { findMeasure, formatMeasure } from '../../../helpers/measures';
+import { findMeasure } from '../../../helpers/measures';
 import { getComponentIssuesUrl } from '../../../helpers/urls';
 import { getBranchLikeQuery } from '../../../sonar-aligned/helpers/branch-like';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { PullRequest } from '../../../types/branch-like';
 import { MetricKey, MetricType } from '../../../types/metrics';
 import { QualityGateStatusConditionEnhanced } from '../../../types/quality-gates';
index 0ce4a8aae1fab5c11952bef6f520993fb68db881..aae9b023155c10d1416baa5613f1549b1b6affce 100644 (file)
@@ -22,7 +22,8 @@ import React from 'react';
 import { useIntl } from 'react-intl';
 import CurrentBranchLikeMergeInformation from '../../../app/components/nav/component/branch-like/CurrentBranchLikeMergeInformation';
 import { getLeakValue } from '../../../components/measure/utils';
-import { findMeasure, formatMeasure } from '../../../helpers/measures';
+import { findMeasure } from '../../../helpers/measures';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { PullRequest } from '../../../types/branch-like';
 import { MetricKey, MetricType } from '../../../types/metrics';
 import { MeasureEnhanced } from '../../../types/types';
index bcb1431ffab361a2475cc4886b1dac434daf1d55..eb0c21128072dab7174779d7b66367e5d6a8253d 100644 (file)
@@ -22,8 +22,8 @@ import React from 'react';
 import { IntlShape } from 'react-intl';
 import { ISSUETYPE_METRIC_KEYS_MAP } from '../../helpers/issues';
 import { translate } from '../../helpers/l10n';
-import { formatMeasure } from '../../helpers/measures';
 import { parseAsString } from '../../helpers/query';
+import { formatMeasure } from '../../sonar-aligned/helpers/measures';
 import { SoftwareQuality } from '../../types/clean-code-taxonomy';
 import { IssueType } from '../../types/issues';
 import { MetricKey, MetricType } from '../../types/metrics';
index 7ab9e1ecca23ae37d5fddb696ce73f95944f63df..566a9332e3fb1a8b77b2db9f6b1f7036c3a502c2 100644 (file)
@@ -21,7 +21,7 @@ import { Link, Spinner } from 'design-system';
 import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
 import { translate } from '../../../helpers/l10n';
-import { formatMeasure } from '../../../helpers/measures';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 
 export interface LifetimeInformationRendererProps {
   branchAndPullRequestLifeTimeInDays?: string;
index 7593c2a9b32499ac1d8b9c99cffc13ab7ebc157e..3172136ad9c314665543ce59dece54e8c0054068 100644 (file)
@@ -20,8 +20,9 @@
 import { DrilldownLink, Note, SizeIndicator, SubHeading } from 'design-system';
 import * as React from 'react';
 import { translate, translateWithParameters } from '../../../../helpers/l10n';
-import { formatMeasure, localizeMetric } from '../../../../helpers/measures';
+import { localizeMetric } from '../../../../helpers/measures';
 import { getComponentDrilldownUrl } from '../../../../helpers/urls';
+import { formatMeasure } from '../../../../sonar-aligned/helpers/measures';
 import { ComponentQualifier } from '../../../../types/component';
 import { MetricKey, MetricType } from '../../../../types/metrics';
 import { Component, Measure } from '../../../../types/types';
index 1c254aec9c406ba59837ee7a4b82f2b0a91a28fb..71896a6256776513921dfff39e07269ca3701680 100644 (file)
@@ -43,9 +43,9 @@ import DateFromNow from '../../../../components/intl/DateFromNow';
 import DateTimeFormatter from '../../../../components/intl/DateTimeFormatter';
 import Measure from '../../../../components/measure/Measure';
 import { translate, translateWithParameters } from '../../../../helpers/l10n';
-import { formatMeasure } from '../../../../helpers/measures';
 import { isDefined } from '../../../../helpers/types';
 import { getProjectUrl } from '../../../../helpers/urls';
+import { formatMeasure } from '../../../../sonar-aligned/helpers/measures';
 import { ComponentQualifier } from '../../../../types/component';
 import { MetricKey, MetricType } from '../../../../types/metrics';
 import { Status } from '../../../../types/types';
index 6dac1d3015d07317a44aad67c7ddf69d89776e3c..f33656bbd5fb89e8dea32cde9ae6c9f9239f92c7 100644 (file)
@@ -20,7 +20,7 @@
 import { MetricsRatingBadge, RatingEnum } from 'design-system';
 import * as React from 'react';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { formatMeasure } from '../../../helpers/measures';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { MetricType } from '../../../types/metrics';
 import { RawQuery } from '../../../types/types';
 import { Facet } from '../types';
index 9764cea8aa66013901880cb26a8fd5b0ec063868..b84363490dea6e179e090b547e1edec358224623 100644 (file)
@@ -20,7 +20,7 @@
 import { MetricsRatingBadge, RatingEnum } from 'design-system';
 import * as React from 'react';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { formatMeasure } from '../../../helpers/measures';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { MetricType } from '../../../types/metrics';
 import { Dict, RawQuery } from '../../../types/types';
 import { Facet } from '../types';
index 4d523433faa6eee8648b4a506a5ed59b71e981ac..ad7f681dfb35448b55d3d325169e6123f5c64888 100644 (file)
@@ -23,8 +23,8 @@ import React from 'react';
 import { FormattedMessage } from 'react-intl';
 import withMetricsContext from '../../../app/components/metrics/withMetricsContext';
 import { translate } from '../../../helpers/l10n';
-import { formatMeasure } from '../../../helpers/measures';
 import DocHelpTooltip from '../../../sonar-aligned/components/controls/DocHelpTooltip';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { MetricKey } from '../../../types/metrics';
 import { Condition, Dict, Metric } from '../../../types/types';
 import { getCaycConditionMetadata, getLocalizedMetricNameNoDiffMetric } from '../utils';
index 82d94ed1f16aedb7b77ffb55998c105377fdf1f9..703830027cc64b4f85dd6d1050441a88426b0128 100644 (file)
@@ -21,7 +21,7 @@ import styled from '@emotion/styled';
 import classNames from 'classnames';
 import { themeColor } from 'design-system';
 import * as React from 'react';
-import { formatMeasure } from '../../../helpers/measures';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { Condition, Metric } from '../../../types/types';
 import { getCorrectCaycCondition } from '../utils';
 import ConditionValueDescription from './ConditionValueDescription';
index a93ee48e1319268ac8cd9e136332d43cd851dc06..e03131227e3bdb63260a161909930cd40cd1ac8c 100644 (file)
@@ -19,7 +19,7 @@
  */
 import * as React from 'react';
 import { translate } from '../../../helpers/l10n';
-import { formatMeasure } from '../../../helpers/measures';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { MetricKey } from '../../../types/metrics';
 import { Condition, Metric } from '../../../types/types';
 import { GreenColorText } from './ConditionValue';
index 8cc304097cd4491b0ffa88c584bdfaa10d582e58..c9a9bd316a57381f5d837a33f4e2282266c9fd8f 100644 (file)
@@ -20,9 +20,9 @@
 import { ContentCell, Link, Note, NumericalCell, TableRow } from 'design-system';
 import * as React from 'react';
 import { translateWithParameters } from '../../../helpers/l10n';
-import { formatMeasure } from '../../../helpers/measures';
 import { isDefined } from '../../../helpers/types';
 import { getRulesUrl } from '../../../helpers/urls';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { MetricType } from '../../../types/metrics';
 import { RulesFacetName } from '../../../types/rules';
 
index 45e15ddabb845b32ee5db170cab78791ebed6735..5db045746483a6b6f5ff2243526208ef6dbaa9ae 100644 (file)
@@ -24,8 +24,8 @@ import { useIntl } from 'react-intl';
 import { listRules } from '../../../api/rules';
 import { toShortISO8601String } from '../../../helpers/dates';
 import { translateWithParameters } from '../../../helpers/l10n';
-import { formatMeasure } from '../../../helpers/measures';
 import { getRulesUrl } from '../../../helpers/urls';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { MetricType } from '../../../types/metrics';
 import { Rule, RuleActivation } from '../../../types/types';
 
index 1ee9b32e65146829f68eff97197569c582585920..7f081e17679f2ac3f6e09cb7ef9ae1ede16f6ec4 100644 (file)
@@ -21,8 +21,8 @@ import { ButtonPrimary, ButtonSecondary, Checkbox, Modal, Note } from 'design-sy
 import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { formatMeasure } from '../../../helpers/measures';
 import { save } from '../../../helpers/storage';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { HotspotStatusOption } from '../../../types/security-hotspots';
 import { SHOW_STATUS_DIALOG_STORAGE_KEY } from '../constants';
 
index 350b250031bfa35f062db1af4fa607224135a22b..5168c128f3b6c2feb801f3db865d981afaa112de 100644 (file)
@@ -18,8 +18,8 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import { each, memoize, omit, omitBy, pickBy, sortBy } from 'lodash';
-import { formatMeasure } from '../../helpers/measures';
 import { cleanQuery, parseAsArray, parseAsString, serializeStringArray } from '../../helpers/query';
+import { formatMeasure } from '../../sonar-aligned/helpers/measures';
 import {
   RawQuery,
   SysInfoAppNode,
index cd77dae024bfb383f5b07b0b606273e04531ea9c..5d03123739f9399bed62b02435d1b605712e6cf1 100644 (file)
@@ -20,7 +20,7 @@
 import { CodeSnippet, Spinner } from 'design-system';
 import * as React from 'react';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { formatMeasure } from '../../../helpers/measures';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { WebhookDelivery } from '../../../types/webhook';
 import { formatPayload } from '../utils';
 
index fbe658763a64295ad0d631350586e81541a6fbb4..c2372d53b3c803ee69e7670513e0db0e4364256b 100644 (file)
@@ -44,10 +44,7 @@ import {
   SOFTWARE_QUALITIES_METRIC_KEYS_MAP,
   getIssueTypeBySoftwareQuality,
 } from '../../helpers/issues';
-import {
-  areCCTMeasuresComputed as areCCTMeasuresComputedFn,
-  formatMeasure,
-} from '../../helpers/measures';
+import { areCCTMeasuresComputed as areCCTMeasuresComputedFn } from '../../helpers/measures';
 import { collapsedDirFromPath, fileFromPath } from '../../helpers/path';
 import { omitNil } from '../../helpers/request';
 import { getBaseUrl } from '../../helpers/system';
@@ -59,6 +56,7 @@ import {
   getComponentSecurityHotspotsUrl,
 } from '../../helpers/urls';
 import { getBranchLikeQuery } from '../../sonar-aligned/helpers/branch-like';
+import { formatMeasure } from '../../sonar-aligned/helpers/measures';
 import type { BranchLike } from '../../types/branch-like';
 import { ComponentQualifier } from '../../types/component';
 import { IssueType } from '../../types/issues';
index 732e8b18dd7ac142c83156108e4689ad6938c07b..d95c51a2f28ff7cf2d9340b243a770b1873de904 100644 (file)
@@ -23,7 +23,7 @@ import { filter, slice, sortBy } from 'lodash';
 import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
 import { translate, translateWithParameters } from '../../helpers/l10n';
-import { formatMeasure } from '../../helpers/measures';
+import { formatMeasure } from '../../sonar-aligned/helpers/measures';
 import { ParsedAnalysis, Serie } from '../../types/project-activity';
 import DateFormatter from '../intl/DateFormatter';
 import TimeFormatter from '../intl/TimeFormatter';
index c715da2013b4e1e37602b206cf3d2b79e78aee7e..675e2b0895edd96581c86dc11c4307bf10a709dc 100644 (file)
@@ -23,7 +23,8 @@ import * as React from 'react';
 import { AutoSizer } from 'react-virtualized/dist/commonjs/AutoSizer';
 import { AdvancedTimeline } from '../../components/charts/AdvancedTimeline';
 import { translate } from '../../helpers/l10n';
-import { formatMeasure, getShortType } from '../../helpers/measures';
+import { getShortType } from '../../helpers/measures';
+import { formatMeasure } from '../../sonar-aligned/helpers/measures';
 import { MeasureHistory, ParsedAnalysis, Serie } from '../../types/project-activity';
 import ModalButton from '../controls/ModalButton';
 import DataTableModal from './DataTableModal';
index 8f289f9605a82eeaf812a3beddfae08e10c11267..65a080ae412c06f96137997607d613d81feba256 100644 (file)
@@ -20,7 +20,7 @@
 import { TableSeparator } from 'design-system';
 import * as React from 'react';
 import { translate } from '../../helpers/l10n';
-import { formatMeasure } from '../../helpers/measures';
+import { formatMeasure } from '../../sonar-aligned/helpers/measures';
 import { MetricKey, MetricType } from '../../types/metrics';
 import { MeasureHistory } from '../../types/project-activity';
 
index 4c1a88bfe6c61451374e3e1c9cd42ab46a71e8ba..2d14453306f7e78314466ccbbd19c0873602a67b 100644 (file)
@@ -20,7 +20,7 @@
 import { TableSeparator } from 'design-system';
 import * as React from 'react';
 import { translate } from '../../helpers/l10n';
-import { formatMeasure } from '../../helpers/measures';
+import { formatMeasure } from '../../sonar-aligned/helpers/measures';
 import { MetricKey, MetricType } from '../../types/metrics';
 import { MeasureHistory } from '../../types/project-activity';
 
index d700ae06b8ccb445314933e6d44e8155bc06a019..633ab7637989181924b23ea231f866d2d750b62c 100644 (file)
@@ -20,7 +20,7 @@
 import styled from '@emotion/styled';
 import { ScaleLinear, ScaleOrdinal } from 'd3-scale';
 import * as React from 'react';
-import { formatMeasure } from '../../helpers/measures';
+import { formatMeasure } from '../../sonar-aligned/helpers/measures';
 
 interface Props {
   colorNA?: string;
index c8b3a8bd9c2f776f36c3b444fd9b80064bd731fa..d3fe35ae3a653a6c6b4657f2a53af4d33a18a23a 100644 (file)
@@ -22,7 +22,7 @@ import { sortBy } from 'lodash';
 import * as React from 'react';
 import withLanguagesContext from '../../app/components/languages/withLanguagesContext';
 import { translate } from '../../helpers/l10n';
-import { formatMeasure } from '../../helpers/measures';
+import { formatMeasure } from '../../sonar-aligned/helpers/measures';
 import { Languages } from '../../types/languages';
 import { MetricType } from '../../types/metrics';
 
index d48ddc16bcc4817059c9cc6840db982136f1e078..2f20c7216fc56b2cdd4cdc9f90499f44d11d0197 100644 (file)
@@ -18,7 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import { formatMeasure } from '../../helpers/measures';
+import { formatMeasure } from '../../sonar-aligned/helpers/measures';
 import { MetricType } from '../../types/metrics';
 
 export interface PageCounterProps {
index 70071fb047d09ee141ecb5d39661957c84937a28..b393b36ada3e9e4c5f7e93a82928f9ecb1d7c5f5 100644 (file)
@@ -22,7 +22,7 @@ import classNames from 'classnames';
 import { ButtonSecondary, Spinner, themeColor } from 'design-system';
 import * as React from 'react';
 import { translate, translateWithParameters } from '../../helpers/l10n';
-import { formatMeasure } from '../../helpers/measures';
+import { formatMeasure } from '../../sonar-aligned/helpers/measures';
 import { MetricType } from '../../types/metrics';
 
 export interface ListFooterProps {
index 037798b92f177e1572d3089ce98372090ef6810c..638c8e3349c65cd29ba961c0bc9b816ef6c3cb2b 100644 (file)
@@ -19,7 +19,7 @@
  */
 import * as React from 'react';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { formatMeasure } from '../../../helpers/measures';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
 import { IssueChangelogDiff as TypeIssueChangelogDiff } from '../../../types/types';
 
 export interface IssueChangelogDiffProps {
index f533304d5acd2d309728816fc2390cb3314fb36a..de956d27a58b132aa8e829d5235615a62f6373d6 100644 (file)
@@ -23,7 +23,7 @@ import { mockIssueChangelogDiff } from '../../../../helpers/mocks/issues';
 import { renderComponent } from '../../../../helpers/testReactTestingUtils';
 import IssueChangelogDiff, { IssueChangelogDiffProps } from '../IssueChangelogDiff';
 
-jest.mock('../../../../helpers/measures', () => ({
+jest.mock('../../../../sonar-aligned/helpers/measures', () => ({
   formatMeasure: jest
     .fn()
     .mockImplementation((value: string, type: string) => `formatted.${value}.as.${type}`),
index 0e0f1f0e1408832de2fe6f01371362c713129ac8..6f9a7c647b283580b885c0bd27246df44291a69b 100644 (file)
@@ -21,14 +21,14 @@ import { MetricsRatingBadge, QualityGateIndicator, RatingLabel } from 'design-sy
 import * as React from 'react';
 import Tooltip from '../../components/controls/Tooltip';
 import { translate, translateWithParameters } from '../../helpers/l10n';
-import { formatMeasure } from '../../helpers/measures';
+import { formatMeasure } from '../../sonar-aligned/helpers/measures';
 import { MetricType } from '../../types/metrics';
 import { Status } from '../../types/types';
 import RatingTooltipContent from './RatingTooltipContent';
 
 interface Props {
   className?: string;
-  decimals?: number | null;
+  decimals?: number;
   metricKey: string;
   metricType: string;
   small?: boolean;
index 9889e1393482aa3dad6329ce925395296c3d6bbe..2e384775ab3590ab85929d0334bd17adfe3c6df5 100644 (file)
@@ -24,14 +24,14 @@ import {
   RatingEnum,
 } from 'design-system';
 import * as React from 'react';
-import { formatMeasure } from '../../helpers/measures';
+import { formatMeasure } from '../../sonar-aligned/helpers/measures';
 import { MetricKey, MetricType } from '../../types/metrics';
 import Measure from './Measure';
 import { duplicationRatingConverter } from './utils';
 
 interface Props {
   className?: string;
-  decimals?: number | null;
+  decimals?: number;
   metricKey: string;
   metricType: string;
   small?: boolean;
index 26dd0f4b542a9e5eedf644dd8086cdc223398061..d83276ea6bf0dd141fd9ae5c5c701d355d908de7 100644 (file)
 import * as React from 'react';
 import withAppStateContext from '../../app/components/app-state/withAppStateContext';
 import { translate, translateWithParameters } from '../../helpers/l10n';
-import { formatMeasure, isDiffMetric } from '../../helpers/measures';
+import { isDiffMetric } from '../../helpers/measures';
 import {
   DIFF_METRIC_PREFIX_LENGTH,
   GRID_INDEX_OFFSET,
   PERCENT_MULTIPLIER,
   getMaintainabilityGrid,
 } from '../../helpers/ratings';
+import { formatMeasure } from '../../sonar-aligned/helpers/measures';
 import { AppState } from '../../types/appstate';
 import { MetricKey, MetricType } from '../../types/metrics';
 import { GlobalSettingKeys } from '../../types/settings';
index 6916b574d5e1a0fe581f688b97f8731d35253371..37ed3bd3d995f117db1ac428ac474e686cde713d 100644 (file)
@@ -22,8 +22,8 @@ import classNames from 'classnames';
 import { Note, themeColor } from 'design-system';
 import React from 'react';
 import { translate } from '../../helpers/l10n';
-import { formatMeasure } from '../../helpers/measures';
 import { isDefined } from '../../helpers/types';
+import { formatMeasure } from '../../sonar-aligned/helpers/measures';
 import { MetricType } from '../../types/metrics';
 
 interface Props {
index 449a268d995d236361f20f277de34f5965d2e801..055e99dc9eb59050dc951e4c86b52d08cfcbc2a1 100644 (file)
@@ -20,7 +20,7 @@
 import classNames from 'classnames';
 import * as React from 'react';
 import { translate, translateWithParameters } from '../../helpers/l10n';
-import { formatMeasure } from '../../helpers/measures';
+import { formatMeasure } from '../../sonar-aligned/helpers/measures';
 import { MetricType } from '../../types/metrics';
 import './Rating.css';
 
index 820f4a6636bed2a54e7e2f3117dd6adb65babf60..06083a1ee2324296fa306ee10db23d366c88e19c 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { MetricKey, MetricType } from '../../types/metrics';
-import { Dict } from '../../types/types';
+import { MetricKey } from '../../types/metrics';
 import { CCT_SOFTWARE_QUALITY_METRICS } from '../constants';
-import { getMessages } from '../l10nBundle';
 import {
   areCCTMeasuresComputed,
   enhanceConditionWithMeasure,
-  formatMeasure,
   isPeriodBestValue,
 } from '../measures';
 import { mockQualityGateStatusCondition } from '../mocks/quality-gates';
 import { mockMeasure, mockMeasureEnhanced, mockMetric } from '../testMocks';
 
-jest.unmock('../l10n');
-
-jest.mock('../l10nBundle', () => ({
-  getCurrentLocale: jest.fn().mockReturnValue('us'),
-  getMessages: jest.fn().mockReturnValue({}),
-}));
-
-const resetMessages = (messages: Dict<string>) =>
-  (getMessages as jest.Mock).mockReturnValue(messages);
-
-beforeAll(() => {
-  resetMessages({
-    'work_duration.x_days': '{0}d',
-    'work_duration.x_hours': '{0}h',
-    'work_duration.x_minutes': '{0}min',
-    'work_duration.about': '~ {0}',
-    'metric.level.ERROR': 'Error',
-    'metric.level.WARN': 'Warning',
-    'metric.level.OK': 'Ok',
-    'short_number_suffix.g': 'G',
-    'short_number_suffix.k': 'k',
-    'short_number_suffix.m': 'M',
-  });
-});
-
-const HOURS_IN_DAY = 8;
-const ONE_MINUTE = 1;
-const ONE_HOUR = ONE_MINUTE * 60;
-const ONE_DAY = HOURS_IN_DAY * ONE_HOUR;
-
 describe('enhanceConditionWithMeasure', () => {
   it('should correctly map enhance conditions with measure data', () => {
     const measures = [
@@ -112,160 +79,6 @@ describe('isPeriodBestValue', () => {
   });
 });
 
-describe('#formatMeasure()', () => {
-  it('should format INT', () => {
-    expect(formatMeasure(0, MetricType.Integer)).toBe('0');
-    expect(formatMeasure(1, MetricType.Integer)).toBe('1');
-    expect(formatMeasure(-5, MetricType.Integer)).toBe('-5');
-    expect(formatMeasure(999, MetricType.Integer)).toBe('999');
-    expect(formatMeasure(1000, MetricType.Integer)).toBe('1,000');
-    expect(formatMeasure(1529, MetricType.Integer)).toBe('1,529');
-    expect(formatMeasure(10000, MetricType.Integer)).toBe('10,000');
-    expect(formatMeasure(1234567890, MetricType.Integer)).toBe('1,234,567,890');
-  });
-
-  it('should format SHORT_INT', () => {
-    expect(formatMeasure(0, MetricType.ShortInteger)).toBe('0');
-    expect(formatMeasure(1, MetricType.ShortInteger)).toBe('1');
-    expect(formatMeasure(999, MetricType.ShortInteger)).toBe('999');
-    expect(formatMeasure(1000, MetricType.ShortInteger)).toBe('1k');
-    expect(formatMeasure(1529, MetricType.ShortInteger)).toBe('1.5k');
-    expect(formatMeasure(10000, MetricType.ShortInteger)).toBe('10k');
-    expect(formatMeasure(10678, MetricType.ShortInteger)).toBe('11k');
-    expect(formatMeasure(9467890, MetricType.ShortInteger)).toBe('9.5M');
-    expect(formatMeasure(994567890, MetricType.ShortInteger)).toBe('995M');
-    expect(formatMeasure(999000001, MetricType.ShortInteger)).toBe('999M');
-    expect(formatMeasure(999567890, MetricType.ShortInteger)).toBe('1G');
-    expect(formatMeasure(1234567890, MetricType.ShortInteger)).toBe('1.2G');
-    expect(formatMeasure(11234567890, MetricType.ShortInteger)).toBe('11G');
-  });
-
-  it('should format FLOAT', () => {
-    expect(formatMeasure(0.0, 'FLOAT')).toBe('0.0');
-    expect(formatMeasure(1.0, 'FLOAT')).toBe('1.0');
-    expect(formatMeasure(1.3, 'FLOAT')).toBe('1.3');
-    expect(formatMeasure(1.34, 'FLOAT')).toBe('1.34');
-    expect(formatMeasure(50.89, 'FLOAT')).toBe('50.89');
-    expect(formatMeasure(100.0, 'FLOAT')).toBe('100.0');
-    expect(formatMeasure(123.456, 'FLOAT')).toBe('123.456');
-    expect(formatMeasure(123456.7, 'FLOAT')).toBe('123,456.7');
-    expect(formatMeasure(1234567890.0, 'FLOAT')).toBe('1,234,567,890.0');
-  });
-
-  it('should respect FLOAT precision', () => {
-    expect(formatMeasure(0.1, 'FLOAT')).toBe('0.1');
-    expect(formatMeasure(0.12, 'FLOAT')).toBe('0.12');
-    expect(formatMeasure(0.12345, 'FLOAT')).toBe('0.12345');
-    expect(formatMeasure(0.123456, 'FLOAT')).toBe('0.12346');
-  });
-
-  it('should format PERCENT', () => {
-    expect(formatMeasure(0.0, MetricType.Percent)).toBe('0.0%');
-    expect(formatMeasure(1.0, MetricType.Percent)).toBe('1.0%');
-    expect(formatMeasure(1.3, MetricType.Percent)).toBe('1.3%');
-    expect(formatMeasure(1.34, MetricType.Percent)).toBe('1.3%');
-    expect(formatMeasure(50.89, MetricType.Percent)).toBe('50.9%');
-    expect(formatMeasure(100.0, MetricType.Percent)).toBe('100%');
-    expect(formatMeasure(50.89, MetricType.Percent, { decimals: 0 })).toBe('50.9%');
-    expect(formatMeasure(50.89, MetricType.Percent, { decimals: 1 })).toBe('50.9%');
-    expect(formatMeasure(50.89, MetricType.Percent, { decimals: 2 })).toBe('50.89%');
-    expect(formatMeasure(50.89, MetricType.Percent, { decimals: 3 })).toBe('50.890%');
-    expect(
-      formatMeasure(50, MetricType.Percent, { decimals: 0, omitExtraDecimalZeros: true }),
-    ).toBe('50.0%');
-    expect(
-      formatMeasure(50, MetricType.Percent, { decimals: 1, omitExtraDecimalZeros: true }),
-    ).toBe('50.0%');
-    expect(
-      formatMeasure(50, MetricType.Percent, { decimals: 3, omitExtraDecimalZeros: true }),
-    ).toBe('50.0%');
-    expect(
-      formatMeasure(50.89, MetricType.Percent, { decimals: 3, omitExtraDecimalZeros: true }),
-    ).toBe('50.89%');
-  });
-
-  it('should format WORK_DUR', () => {
-    expect(formatMeasure(0, 'WORK_DUR')).toBe('0');
-    expect(formatMeasure(5 * ONE_DAY, 'WORK_DUR')).toBe('5d');
-    expect(formatMeasure(2 * ONE_HOUR, 'WORK_DUR')).toBe('2h');
-    expect(formatMeasure(40 * ONE_MINUTE, 'WORK_DUR')).toBe('40min');
-    expect(formatMeasure(ONE_MINUTE, 'WORK_DUR')).toBe('1min');
-    expect(formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR, 'WORK_DUR')).toBe('5d 2h');
-    expect(formatMeasure(2 * ONE_HOUR + ONE_MINUTE, 'WORK_DUR')).toBe('2h 1min');
-    expect(formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, 'WORK_DUR')).toBe('5d 2h');
-    expect(formatMeasure(15 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, 'WORK_DUR')).toBe('15d');
-    expect(formatMeasure(-5 * ONE_DAY, 'WORK_DUR')).toBe('-5d');
-    expect(formatMeasure(-2 * ONE_HOUR, 'WORK_DUR')).toBe('-2h');
-    expect(formatMeasure(-1 * ONE_MINUTE, 'WORK_DUR')).toBe('-1min');
-  });
-
-  it('should format SHORT_WORK_DUR', () => {
-    expect(formatMeasure(0, MetricType.ShortWorkDuration)).toBe('0');
-    expect(formatMeasure(5 * ONE_DAY, MetricType.ShortWorkDuration)).toBe('5d');
-    expect(formatMeasure(2 * ONE_HOUR, MetricType.ShortWorkDuration)).toBe('2h');
-    expect(formatMeasure(ONE_MINUTE, MetricType.ShortWorkDuration)).toBe('1min');
-    expect(formatMeasure(40 * ONE_MINUTE, MetricType.ShortWorkDuration)).toBe('40min');
-    expect(formatMeasure(58 * ONE_MINUTE, MetricType.ShortWorkDuration)).toBe('1h');
-    expect(formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR, MetricType.ShortWorkDuration)).toBe('5d');
-    expect(formatMeasure(2 * ONE_HOUR + ONE_MINUTE, MetricType.ShortWorkDuration)).toBe('2h');
-    expect(formatMeasure(ONE_HOUR + 55 * ONE_MINUTE, MetricType.ShortWorkDuration)).toBe('2h');
-    expect(formatMeasure(3 * ONE_DAY + 6 * ONE_HOUR, MetricType.ShortWorkDuration)).toBe('4d');
-    expect(formatMeasure(7 * ONE_HOUR + 59 * ONE_MINUTE, MetricType.ShortWorkDuration)).toBe('1d');
-    expect(
-      formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, MetricType.ShortWorkDuration),
-    ).toBe('5d');
-    expect(
-      formatMeasure(15 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, MetricType.ShortWorkDuration),
-    ).toBe('15d');
-    expect(formatMeasure(7 * ONE_MINUTE, MetricType.ShortWorkDuration)).toBe('7min');
-    expect(formatMeasure(-5 * ONE_DAY, MetricType.ShortWorkDuration)).toBe('-5d');
-    expect(formatMeasure(-2 * ONE_HOUR, MetricType.ShortWorkDuration)).toBe('-2h');
-    expect(formatMeasure(-1 * ONE_MINUTE, MetricType.ShortWorkDuration)).toBe('-1min');
-
-    expect(formatMeasure(1529 * ONE_DAY, MetricType.ShortWorkDuration)).toBe('1.5kd');
-    expect(formatMeasure(1234567 * ONE_DAY, MetricType.ShortWorkDuration)).toBe('1.2Md');
-    expect(formatMeasure(12345670 * ONE_DAY + 4 * ONE_HOUR, MetricType.ShortWorkDuration)).toBe(
-      '12Md',
-    );
-  });
-
-  it('should format RATING', () => {
-    expect(formatMeasure(1, MetricType.Rating)).toBe('A');
-    expect(formatMeasure(2, MetricType.Rating)).toBe('B');
-    expect(formatMeasure(3, MetricType.Rating)).toBe('C');
-    expect(formatMeasure(4, MetricType.Rating)).toBe('D');
-    expect(formatMeasure(5, MetricType.Rating)).toBe('E');
-  });
-
-  it('should format LEVEL', () => {
-    expect(formatMeasure('ERROR', MetricType.Level)).toBe('Error');
-    expect(formatMeasure('WARN', MetricType.Level)).toBe('Warning');
-    expect(formatMeasure('OK', MetricType.Level)).toBe('Ok');
-    expect(formatMeasure('UNKNOWN', MetricType.Level)).toBe('UNKNOWN');
-  });
-
-  it('should format MILLISEC', () => {
-    expect(formatMeasure(0, 'MILLISEC')).toBe('0ms');
-    expect(formatMeasure(1, 'MILLISEC')).toBe('1ms');
-    expect(formatMeasure(173, 'MILLISEC')).toBe('173ms');
-    expect(formatMeasure(3649, 'MILLISEC')).toBe('4s');
-    expect(formatMeasure(893481, 'MILLISEC')).toBe('15min');
-    expect(formatMeasure(17862325, 'MILLISEC')).toBe('298min');
-  });
-
-  it('should not format unknown type', () => {
-    expect(formatMeasure('random value', 'RANDOM_TYPE')).toBe('random value');
-  });
-
-  it('should return null if value is empty string', () => {
-    expect(formatMeasure('', MetricType.Percent)).toBe('');
-  });
-
-  it('should not fail with undefined', () => {
-    expect(formatMeasure(undefined, MetricType.Integer)).toBe('');
-  });
-});
-
 describe('areCCTMeasuresComputed', () => {
   it('returns true when measures include maintainability_,security_,reliability_issues', () => {
     expect(
index 1f5b075904fd1afe813429c9775a15024ccb59fa..2ac1984bf76aff242b52e64c9ea60ea9d930bee3 100644 (file)
@@ -22,15 +22,13 @@ import {
   QualityGateStatusCondition,
   QualityGateStatusConditionEnhanced,
 } from '../types/quality-gates';
-import { Dict, Measure, MeasureEnhanced, Metric } from '../types/types';
+import { Measure, MeasureEnhanced, Metric } from '../types/types';
 import {
   CCT_SOFTWARE_QUALITY_METRICS,
   LEAK_CCT_SOFTWARE_QUALITY_METRICS,
   LEAK_OLD_TAXONOMY_METRICS,
-  ONE_SECOND,
 } from './constants';
-import { translate, translateWithParameters } from './l10n';
-import { getCurrentLocale } from './l10nBundle';
+import { translate } from './l10n';
 import { isDefined } from './types';
 
 export const MEASURES_REDIRECTION: Partial<Record<MetricKey, MetricKey>> = {
@@ -137,24 +135,6 @@ export const getCCTMeasureValue = (key: string, value?: string) => {
   return value;
 };
 
-const HOURS_IN_DAY = 8;
-
-type Formatter = (value: string | number, options?: Dict<unknown>) => string;
-
-/**
- * Format a measure value for a given type
- * ! For Ratings, use formatRating instead
- */
-export function formatMeasure(
-  value: string | number | undefined,
-  type: string,
-  options?: Dict<unknown>,
-): string {
-  const formatter = getFormatter(type);
-  // eslint-disable-next-line react-hooks/rules-of-hooks
-  return useFormatter(value, formatter, options);
-}
-
 type RatingValue = 'A' | 'B' | 'C' | 'D' | 'E';
 const RATING_VALUES: RatingValue[] = ['A', 'B', 'C', 'D', 'E'];
 export function formatRating(value: string | number | undefined): RatingValue | undefined {
@@ -184,266 +164,3 @@ export function getShortType(type: string): string {
   }
   return type;
 }
-
-function useFormatter(
-  value: string | number | undefined,
-  formatter: Formatter,
-  options?: Dict<unknown>,
-): string {
-  return value !== undefined && value !== '' ? formatter(value, options) : '';
-}
-
-function getFormatter(type: string): Formatter {
-  const FORMATTERS: Dict<Formatter> = {
-    INT: intFormatter,
-    SHORT_INT: shortIntFormatter,
-    FLOAT: floatFormatter,
-    PERCENT: percentFormatter,
-    WORK_DUR: durationFormatter,
-    SHORT_WORK_DUR: shortDurationFormatter,
-    RATING: ratingFormatter,
-    LEVEL: levelFormatter,
-    MILLISEC: millisecondsFormatter,
-  };
-  return FORMATTERS[type] || noFormatter;
-}
-
-function numberFormatter(
-  value: string | number,
-  minimumFractionDigits = 0,
-  maximumFractionDigits = minimumFractionDigits,
-) {
-  const { format } = new Intl.NumberFormat(getCurrentLocale(), {
-    minimumFractionDigits,
-    maximumFractionDigits,
-  });
-  if (typeof value === 'string') {
-    return format(parseFloat(value));
-  }
-  return format(value);
-}
-
-function noFormatter(value: string | number): string | number {
-  return value;
-}
-
-function intFormatter(value: string | number): string {
-  return numberFormatter(value);
-}
-
-const shortIntFormats = [
-  { unit: 1e10, formatUnit: 1e9, fraction: 0, suffix: 'short_number_suffix.g' },
-  { unit: 1e9, formatUnit: 1e9, fraction: 1, suffix: 'short_number_suffix.g' },
-  { unit: 1e7, formatUnit: 1e6, fraction: 0, suffix: 'short_number_suffix.m' },
-  { unit: 1e6, formatUnit: 1e6, fraction: 1, suffix: 'short_number_suffix.m' },
-  { unit: 1e4, formatUnit: 1e3, fraction: 0, suffix: 'short_number_suffix.k' },
-  { unit: 1e3, formatUnit: 1e3, fraction: 1, suffix: 'short_number_suffix.k' },
-];
-
-function shortIntFormatter(
-  value: string | number,
-  option?: { roundingFunc?: (x: number) => number },
-): string {
-  const roundingFunc = option?.roundingFunc;
-  if (typeof value === 'string') {
-    value = parseFloat(value);
-  }
-  for (let i = 0; i < shortIntFormats.length; i++) {
-    const { unit, formatUnit, fraction, suffix } = shortIntFormats[i];
-    const nextFraction = unit / (shortIntFormats[i + 1] ? shortIntFormats[i + 1].unit / 10 : 1);
-    const roundedValue = numberRound(value / unit, nextFraction, roundingFunc);
-    if (roundedValue >= 1) {
-      return (
-        numberFormatter(
-          numberRound(value / formatUnit, Math.pow(10, fraction), roundingFunc),
-          0,
-          fraction,
-        ) + translate(suffix)
-      );
-    }
-  }
-
-  return numberFormatter(value);
-}
-
-function numberRound(
-  value: number,
-  fraction: number = 1000,
-  roundingFunc: (x: number) => number = Math.round,
-) {
-  return roundingFunc(value * fraction) / fraction;
-}
-
-function floatFormatter(value: string | number): string {
-  return numberFormatter(value, 1, 5);
-}
-
-function percentFormatter(
-  value: string | number,
-  { decimals, omitExtraDecimalZeros }: { decimals?: number; omitExtraDecimalZeros?: boolean } = {},
-): string {
-  if (typeof value === 'string') {
-    value = parseFloat(value);
-  }
-  if (value === 100) {
-    return '100%';
-  } else if (omitExtraDecimalZeros && decimals) {
-    // If omitExtraDecimalZeros is true, all trailing decimal 0s will be removed,
-    // except for the first decimal.
-    // E.g. for decimals=3:
-    // - omitExtraDecimalZeros: false, value: 45.450 => 45.450
-    // - omitExtraDecimalZeros: true, value: 45.450 => 45.45
-    // - omitExtraDecimalZeros: false, value: 85 => 85.000
-    // - omitExtraDecimalZeros: true, value: 85 => 85.0
-    return `${numberFormatter(value, 1, decimals)}%`;
-  }
-  return `${numberFormatter(value, decimals || 1)}%`;
-}
-
-function ratingFormatter(value: string | number): string {
-  if (typeof value === 'string') {
-    value = parseInt(value, 10);
-  }
-  return String.fromCharCode(97 + value - 1).toUpperCase();
-}
-
-function levelFormatter(value: string | number): string {
-  if (typeof value === 'number') {
-    value = value.toString();
-  }
-  const l10nKey = `metric.level.${value}`;
-  const result = translate(l10nKey);
-
-  // if couldn't translate, return the initial value
-  return l10nKey !== result ? result : value;
-}
-
-function millisecondsFormatter(value: string | number): string {
-  if (typeof value === 'string') {
-    value = parseInt(value, 10);
-  }
-  const ONE_MINUTE = 60 * ONE_SECOND;
-  if (value >= ONE_MINUTE) {
-    const minutes = Math.round(value / ONE_MINUTE);
-    return `${minutes}min`;
-  } else if (value >= ONE_SECOND) {
-    const seconds = Math.round(value / ONE_SECOND);
-    return `${seconds}s`;
-  }
-  return `${value}ms`;
-}
-
-/*
- * Debt Formatters
- */
-
-function shouldDisplayDays(days: number): boolean {
-  return days > 0;
-}
-
-function shouldDisplayDaysInShortFormat(days: number): boolean {
-  return days > 0.9;
-}
-
-function shouldDisplayHours(days: number, hours: number): boolean {
-  return hours > 0 && days < 10;
-}
-
-function shouldDisplayHoursInShortFormat(hours: number): boolean {
-  return hours > 0.9;
-}
-
-function shouldDisplayMinutes(days: number, hours: number, minutes: number): boolean {
-  return minutes > 0 && hours < 10 && days === 0;
-}
-
-function addSpaceIfNeeded(value: string): string {
-  return value.length > 0 ? `${value} ` : value;
-}
-
-function formatDuration(isNegative: boolean, days: number, hours: number, minutes: number): string {
-  let formatted = '';
-  if (shouldDisplayDays(days)) {
-    formatted += translateWithParameters('work_duration.x_days', isNegative ? -1 * days : days);
-  }
-  if (shouldDisplayHours(days, hours)) {
-    formatted = addSpaceIfNeeded(formatted);
-    formatted += translateWithParameters(
-      'work_duration.x_hours',
-      isNegative && formatted.length === 0 ? -1 * hours : hours,
-    );
-  }
-  if (shouldDisplayMinutes(days, hours, minutes)) {
-    formatted = addSpaceIfNeeded(formatted);
-    formatted += translateWithParameters(
-      'work_duration.x_minutes',
-      isNegative && formatted.length === 0 ? -1 * minutes : minutes,
-    );
-  }
-  return formatted;
-}
-
-function formatDurationShort(
-  isNegative: boolean,
-  days: number,
-  hours: number,
-  minutes: number,
-): string {
-  if (shouldDisplayDaysInShortFormat(days)) {
-    const roundedDays = Math.round(days);
-    const formattedDays = formatMeasure(
-      isNegative ? -1 * roundedDays : roundedDays,
-      MetricType.ShortInteger,
-    );
-    return translateWithParameters('work_duration.x_days', formattedDays);
-  }
-
-  if (shouldDisplayHoursInShortFormat(hours)) {
-    const roundedHours = Math.round(hours);
-    const formattedHours = formatMeasure(
-      isNegative ? -1 * roundedHours : roundedHours,
-      MetricType.ShortInteger,
-    );
-    return translateWithParameters('work_duration.x_hours', formattedHours);
-  }
-
-  const formattedMinutes = formatMeasure(
-    isNegative ? -1 * minutes : minutes,
-    MetricType.ShortInteger,
-  );
-  return translateWithParameters('work_duration.x_minutes', formattedMinutes);
-}
-
-function durationFormatter(value: string | number): string {
-  if (typeof value === 'string') {
-    value = parseInt(value, 10);
-  }
-  if (value === 0) {
-    return '0';
-  }
-  const hoursInDay = HOURS_IN_DAY;
-  const isNegative = value < 0;
-  const absValue = Math.abs(value);
-  const days = Math.floor(absValue / hoursInDay / 60);
-  let remainingValue = absValue - days * hoursInDay * 60;
-  const hours = Math.floor(remainingValue / 60);
-  remainingValue -= hours * 60;
-  return formatDuration(isNegative, days, hours, remainingValue);
-}
-
-function shortDurationFormatter(value: string | number): string {
-  if (typeof value === 'string') {
-    value = parseInt(value, 10);
-  }
-  if (value === 0) {
-    return '0';
-  }
-  const hoursInDay = HOURS_IN_DAY;
-  const isNegative = value < 0;
-  const absValue = Math.abs(value);
-  const days = absValue / hoursInDay / 60;
-  let remainingValue = absValue - Math.floor(days) * hoursInDay * 60;
-  const hours = remainingValue / 60;
-  remainingValue -= Math.floor(hours) * 60;
-  return formatDurationShort(isNegative, days, hours, remainingValue);
-}
diff --git a/server/sonar-web/src/main/js/sonar-aligned/helpers/__tests__/measures-test.ts b/server/sonar-web/src/main/js/sonar-aligned/helpers/__tests__/measures-test.ts
new file mode 100644 (file)
index 0000000..8680dbb
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 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 { getMessages } from '../../../helpers/l10nBundle';
+import { MetricType } from '../../../types/metrics';
+import { Dict } from '../../../types/types';
+import { formatMeasure } from '../measures';
+
+const HOURS_IN_DAY = 8;
+const ONE_MINUTE = 1;
+const ONE_HOUR = ONE_MINUTE * 60;
+const ONE_DAY = HOURS_IN_DAY * ONE_HOUR;
+
+jest.unmock('../../../helpers/l10n');
+
+jest.mock('../../../helpers/l10nBundle', () => ({
+  getCurrentLocale: jest.fn().mockReturnValue('us'),
+  getMessages: jest.fn().mockReturnValue({}),
+}));
+
+const resetMessages = (messages: Dict<string>) =>
+  jest.mocked(getMessages).mockReturnValue(messages);
+
+beforeAll(() => {
+  resetMessages({
+    'work_duration.x_days': '{0}d',
+    'work_duration.x_hours': '{0}h',
+    'work_duration.x_minutes': '{0}min',
+    'work_duration.about': '~ {0}',
+    'metric.level.ERROR': 'Error',
+    'metric.level.WARN': 'Warning',
+    'metric.level.OK': 'Ok',
+    'short_number_suffix.g': 'G',
+    'short_number_suffix.k': 'k',
+    'short_number_suffix.m': 'M',
+  });
+});
+
+describe('#formatMeasure()', () => {
+  it('should format INT', () => {
+    expect(formatMeasure(0, MetricType.Integer)).toBe('0');
+    expect(formatMeasure(1, MetricType.Integer)).toBe('1');
+    expect(formatMeasure(-5, MetricType.Integer)).toBe('-5');
+    expect(formatMeasure(999, MetricType.Integer)).toBe('999');
+    expect(formatMeasure(1000, MetricType.Integer)).toBe('1,000');
+    expect(formatMeasure(1529, MetricType.Integer)).toBe('1,529');
+    expect(formatMeasure(10000, MetricType.Integer)).toBe('10,000');
+    expect(formatMeasure(1234567890, MetricType.Integer)).toBe('1,234,567,890');
+  });
+
+  it('should format SHORT_INT', () => {
+    expect(formatMeasure(0, MetricType.ShortInteger)).toBe('0');
+    expect(formatMeasure(1, MetricType.ShortInteger)).toBe('1');
+    expect(formatMeasure(999, MetricType.ShortInteger)).toBe('999');
+    expect(formatMeasure(1000, MetricType.ShortInteger)).toBe('1k');
+    expect(formatMeasure(1529, MetricType.ShortInteger)).toBe('1.5k');
+    expect(formatMeasure(10000, MetricType.ShortInteger)).toBe('10k');
+    expect(formatMeasure(10678, MetricType.ShortInteger)).toBe('11k');
+    expect(formatMeasure(9467890, MetricType.ShortInteger)).toBe('9.5M');
+    expect(formatMeasure(994567890, MetricType.ShortInteger)).toBe('995M');
+    expect(formatMeasure(999000001, MetricType.ShortInteger)).toBe('999M');
+    expect(formatMeasure(999567890, MetricType.ShortInteger)).toBe('1G');
+    expect(formatMeasure(1234567890, MetricType.ShortInteger)).toBe('1.2G');
+    expect(formatMeasure(11234567890, MetricType.ShortInteger)).toBe('11G');
+  });
+
+  it('should format FLOAT', () => {
+    expect(formatMeasure(0, 'FLOAT')).toBe('0.0');
+    expect(formatMeasure(1, 'FLOAT')).toBe('1.0');
+    expect(formatMeasure(1.3, 'FLOAT')).toBe('1.3');
+    expect(formatMeasure(1.34, 'FLOAT')).toBe('1.34');
+    expect(formatMeasure(50.89, 'FLOAT')).toBe('50.89');
+    expect(formatMeasure(100, 'FLOAT')).toBe('100.0');
+    expect(formatMeasure(123.456, 'FLOAT')).toBe('123.456');
+    expect(formatMeasure(123456.7, 'FLOAT')).toBe('123,456.7');
+    expect(formatMeasure(1234567890, 'FLOAT')).toBe('1,234,567,890.0');
+  });
+
+  it('should respect FLOAT precision', () => {
+    expect(formatMeasure(0.1, 'FLOAT')).toBe('0.1');
+    expect(formatMeasure(0.12, 'FLOAT')).toBe('0.12');
+    expect(formatMeasure(0.12345, 'FLOAT')).toBe('0.12345');
+    expect(formatMeasure(0.123456, 'FLOAT')).toBe('0.12346');
+  });
+
+  it('should format PERCENT', () => {
+    expect(formatMeasure(0, MetricType.Percent)).toBe('0.0%');
+    expect(formatMeasure(1, MetricType.Percent)).toBe('1.0%');
+    expect(formatMeasure(1.3, MetricType.Percent)).toBe('1.3%');
+    expect(formatMeasure(1.34, MetricType.Percent)).toBe('1.3%');
+    expect(formatMeasure(50.89, MetricType.Percent)).toBe('50.9%');
+    expect(formatMeasure(100, MetricType.Percent)).toBe('100%');
+    expect(formatMeasure(50.89, MetricType.Percent, { decimals: 0 })).toBe('50.9%');
+    expect(formatMeasure(50.89, MetricType.Percent, { decimals: 1 })).toBe('50.9%');
+    expect(formatMeasure(50.89, MetricType.Percent, { decimals: 2 })).toBe('50.89%');
+    expect(formatMeasure(50.89, MetricType.Percent, { decimals: 3 })).toBe('50.890%');
+    expect(
+      formatMeasure(50, MetricType.Percent, { decimals: 0, omitExtraDecimalZeros: true }),
+    ).toBe('50.0%');
+    expect(
+      formatMeasure(50, MetricType.Percent, { decimals: 1, omitExtraDecimalZeros: true }),
+    ).toBe('50.0%');
+    expect(
+      formatMeasure(50, MetricType.Percent, { decimals: 3, omitExtraDecimalZeros: true }),
+    ).toBe('50.0%');
+    expect(
+      formatMeasure(50.89, MetricType.Percent, { decimals: 3, omitExtraDecimalZeros: true }),
+    ).toBe('50.89%');
+  });
+
+  it('should format WORK_DUR', () => {
+    expect(formatMeasure(0, 'WORK_DUR')).toBe('0');
+    expect(formatMeasure(5 * ONE_DAY, 'WORK_DUR')).toBe('5d');
+    expect(formatMeasure(2 * ONE_HOUR, 'WORK_DUR')).toBe('2h');
+    expect(formatMeasure(40 * ONE_MINUTE, 'WORK_DUR')).toBe('40min');
+    expect(formatMeasure(ONE_MINUTE, 'WORK_DUR')).toBe('1min');
+    expect(formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR, 'WORK_DUR')).toBe('5d 2h');
+    expect(formatMeasure(2 * ONE_HOUR + ONE_MINUTE, 'WORK_DUR')).toBe('2h 1min');
+    expect(formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, 'WORK_DUR')).toBe('5d 2h');
+    expect(formatMeasure(15 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, 'WORK_DUR')).toBe('15d');
+    expect(formatMeasure(-5 * ONE_DAY, 'WORK_DUR')).toBe('-5d');
+    expect(formatMeasure(-2 * ONE_HOUR, 'WORK_DUR')).toBe('-2h');
+    expect(formatMeasure(-1 * ONE_MINUTE, 'WORK_DUR')).toBe('-1min');
+  });
+
+  it('should format SHORT_WORK_DUR', () => {
+    expect(formatMeasure(0, MetricType.ShortWorkDuration)).toBe('0');
+    expect(formatMeasure(5 * ONE_DAY, MetricType.ShortWorkDuration)).toBe('5d');
+    expect(formatMeasure(2 * ONE_HOUR, MetricType.ShortWorkDuration)).toBe('2h');
+    expect(formatMeasure(ONE_MINUTE, MetricType.ShortWorkDuration)).toBe('1min');
+    expect(formatMeasure(40 * ONE_MINUTE, MetricType.ShortWorkDuration)).toBe('40min');
+    expect(formatMeasure(58 * ONE_MINUTE, MetricType.ShortWorkDuration)).toBe('1h');
+    expect(formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR, MetricType.ShortWorkDuration)).toBe('5d');
+    expect(formatMeasure(2 * ONE_HOUR + ONE_MINUTE, MetricType.ShortWorkDuration)).toBe('2h');
+    expect(formatMeasure(ONE_HOUR + 55 * ONE_MINUTE, MetricType.ShortWorkDuration)).toBe('2h');
+    expect(formatMeasure(3 * ONE_DAY + 6 * ONE_HOUR, MetricType.ShortWorkDuration)).toBe('4d');
+    expect(formatMeasure(7 * ONE_HOUR + 59 * ONE_MINUTE, MetricType.ShortWorkDuration)).toBe('1d');
+    expect(
+      formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, MetricType.ShortWorkDuration),
+    ).toBe('5d');
+    expect(
+      formatMeasure(15 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, MetricType.ShortWorkDuration),
+    ).toBe('15d');
+    expect(formatMeasure(7 * ONE_MINUTE, MetricType.ShortWorkDuration)).toBe('7min');
+    expect(formatMeasure(-5 * ONE_DAY, MetricType.ShortWorkDuration)).toBe('-5d');
+    expect(formatMeasure(-2 * ONE_HOUR, MetricType.ShortWorkDuration)).toBe('-2h');
+    expect(formatMeasure(-1 * ONE_MINUTE, MetricType.ShortWorkDuration)).toBe('-1min');
+
+    expect(formatMeasure(1529 * ONE_DAY, MetricType.ShortWorkDuration)).toBe('1.5kd');
+    expect(formatMeasure(1234567 * ONE_DAY, MetricType.ShortWorkDuration)).toBe('1.2Md');
+    expect(formatMeasure(12345670 * ONE_DAY + 4 * ONE_HOUR, MetricType.ShortWorkDuration)).toBe(
+      '12Md',
+    );
+  });
+
+  it('should format RATING', () => {
+    expect(formatMeasure(1, MetricType.Rating)).toBe('A');
+    expect(formatMeasure(2, MetricType.Rating)).toBe('B');
+    expect(formatMeasure(3, MetricType.Rating)).toBe('C');
+    expect(formatMeasure(4, MetricType.Rating)).toBe('D');
+    expect(formatMeasure(5, MetricType.Rating)).toBe('E');
+  });
+
+  it('should format LEVEL', () => {
+    expect(formatMeasure('ERROR', MetricType.Level)).toBe('Error');
+    expect(formatMeasure('WARN', MetricType.Level)).toBe('Warning');
+    expect(formatMeasure('OK', MetricType.Level)).toBe('Ok');
+    expect(formatMeasure('UNKNOWN', MetricType.Level)).toBe('UNKNOWN');
+  });
+
+  it('should format MILLISEC', () => {
+    expect(formatMeasure(0, 'MILLISEC')).toBe('0ms');
+    expect(formatMeasure(1, 'MILLISEC')).toBe('1ms');
+    expect(formatMeasure(173, 'MILLISEC')).toBe('173ms');
+    expect(formatMeasure(3649, 'MILLISEC')).toBe('4s');
+    expect(formatMeasure(893481, 'MILLISEC')).toBe('15min');
+    expect(formatMeasure(17862325, 'MILLISEC')).toBe('298min');
+  });
+
+  it('should not format unknown type', () => {
+    expect(formatMeasure('random value', 'RANDOM_TYPE')).toBe('random value');
+  });
+
+  it('should return null if value is empty string', () => {
+    expect(formatMeasure('', MetricType.Percent)).toBe('');
+  });
+
+  it('should not fail with undefined', () => {
+    expect(formatMeasure(undefined, MetricType.Integer)).toBe('');
+  });
+});
diff --git a/server/sonar-web/src/main/js/sonar-aligned/helpers/measures.ts b/server/sonar-web/src/main/js/sonar-aligned/helpers/measures.ts
new file mode 100644 (file)
index 0000000..1969f3d
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 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 { ONE_SECOND } from '../../helpers/constants';
+import { translate, translateWithParameters } from '../../helpers/l10n';
+import { getCurrentLocale } from '../../helpers/l10nBundle';
+import { MetricType } from '../../types/metrics';
+
+import { Dict } from '../../types/types';
+
+const HOURS_IN_DAY = 8;
+
+type FormatterOption =
+  | { roundingFunc?: (x: number) => number }
+  | { decimals?: number; omitExtraDecimalZeros?: boolean };
+
+type Formatter = (value: string | number, options?: FormatterOption) => string;
+/**
+ * Format a measure value for a given type
+ * ! For Ratings, use formatRating instead
+ */
+
+export function formatMeasure(
+  value: string | number | undefined,
+  type: string,
+  options?: FormatterOption,
+): string {
+  const formatter = getFormatter(type);
+  // eslint-disable-next-line react-hooks/rules-of-hooks
+  return useFormatter(value, formatter, options);
+}
+
+function useFormatter(
+  value: string | number | undefined,
+  formatter: Formatter,
+  options?: FormatterOption,
+): string {
+  return value !== undefined && value !== '' ? formatter(value, options) : '';
+}
+
+function getFormatter(type: string): Formatter {
+  const FORMATTERS: Dict<Formatter> = {
+    INT: intFormatter,
+    SHORT_INT: shortIntFormatter,
+    FLOAT: floatFormatter,
+    PERCENT: percentFormatter,
+    WORK_DUR: durationFormatter,
+    SHORT_WORK_DUR: shortDurationFormatter,
+    RATING: ratingFormatter,
+    LEVEL: levelFormatter,
+    MILLISEC: millisecondsFormatter,
+  };
+  return FORMATTERS[type] || noFormatter;
+}
+
+function noFormatter(value: string | number): string | number {
+  return value;
+}
+
+function numberFormatter(
+  value: string | number,
+  minimumFractionDigits = 0,
+  maximumFractionDigits = minimumFractionDigits,
+) {
+  const { format } = new Intl.NumberFormat(getCurrentLocale(), {
+    minimumFractionDigits,
+    maximumFractionDigits,
+  });
+  if (typeof value === 'string') {
+    return format(parseFloat(value));
+  }
+  return format(value);
+}
+
+function intFormatter(value: string | number): string {
+  return numberFormatter(value);
+}
+
+function shortIntFormatter(
+  value: string | number,
+  option?: { roundingFunc?: (x: number) => number },
+): string {
+  const shortIntFormats = [
+    { unit: 10000000000, formatUnit: 1000000000, fraction: 0, suffix: 'short_number_suffix.g' },
+    { unit: 1000000000, formatUnit: 1000000000, fraction: 1, suffix: 'short_number_suffix.g' },
+    { unit: 10000000, formatUnit: 1000000, fraction: 0, suffix: 'short_number_suffix.m' },
+    { unit: 1000000, formatUnit: 1000000, fraction: 1, suffix: 'short_number_suffix.m' },
+    { unit: 10000, formatUnit: 1000, fraction: 0, suffix: 'short_number_suffix.k' },
+    { unit: 1000, formatUnit: 1000, fraction: 1, suffix: 'short_number_suffix.k' },
+  ];
+
+  const roundingFunc = option?.roundingFunc;
+  if (typeof value === 'string') {
+    value = parseFloat(value);
+  }
+  for (let i = 0; i < shortIntFormats.length; i++) {
+    const { unit, formatUnit, fraction, suffix } = shortIntFormats[i];
+    const nextFraction = unit / (shortIntFormats[i + 1] ? shortIntFormats[i + 1].unit / 10 : 1);
+    const roundedValue = numberRound(value / unit, nextFraction, roundingFunc);
+    if (roundedValue >= 1) {
+      return (
+        numberFormatter(
+          numberRound(value / formatUnit, Math.pow(10, fraction), roundingFunc),
+          0,
+          fraction,
+        ) + translate(suffix)
+      );
+    }
+  }
+
+  return numberFormatter(value);
+}
+
+function numberRound(
+  value: number,
+  fraction = 1000,
+  roundingFunc: (x: number) => number = Math.round,
+) {
+  return roundingFunc(value * fraction) / fraction;
+}
+
+function floatFormatter(value: string | number): string {
+  return numberFormatter(value, 1, 5);
+}
+
+function percentFormatter(
+  value: string | number,
+  { decimals, omitExtraDecimalZeros }: { decimals?: number; omitExtraDecimalZeros?: boolean } = {},
+): string {
+  if (typeof value === 'string') {
+    value = parseFloat(value);
+  }
+  if (value === 100) {
+    return '100%';
+  } else if (omitExtraDecimalZeros && decimals) {
+    // If omitExtraDecimalZeros is true, all trailing decimal 0s will be removed,
+    // except for the first decimal.
+    // E.g. for decimals=3:
+    // - omitExtraDecimalZeros: false, value: 45.450 => 45.450
+    // - omitExtraDecimalZeros: true, value: 45.450 => 45.45
+    // - omitExtraDecimalZeros: false, value: 85 => 85.000
+    // - omitExtraDecimalZeros: true, value: 85 => 85.0
+    return `${numberFormatter(value, 1, decimals)}%`;
+  }
+  return `${numberFormatter(value, decimals || 1)}%`;
+}
+
+function ratingFormatter(value: string | number): string {
+  if (typeof value === 'string') {
+    value = parseInt(value, 10);
+  }
+  return String.fromCharCode(97 + value - 1).toUpperCase();
+}
+
+function levelFormatter(value: string | number): string {
+  if (typeof value === 'number') {
+    value = value.toString();
+  }
+  const l10nKey = `metric.level.${value}`;
+  const result = translate(l10nKey);
+
+  // if couldn't translate, return the initial value
+  return l10nKey !== result ? result : value;
+}
+
+function millisecondsFormatter(value: string | number): string {
+  if (typeof value === 'string') {
+    value = parseInt(value, 10);
+  }
+  const ONE_MINUTE = 60 * ONE_SECOND;
+  if (value >= ONE_MINUTE) {
+    const minutes = Math.round(value / ONE_MINUTE);
+    return `${minutes}min`;
+  } else if (value >= ONE_SECOND) {
+    const seconds = Math.round(value / ONE_SECOND);
+    return `${seconds}s`;
+  }
+  return `${value}ms`;
+}
+
+function formatDuration(isNegative: boolean, days: number, hours: number, minutes: number): string {
+  let formatted = '';
+  if (shouldDisplayDays(days)) {
+    formatted += translateWithParameters('work_duration.x_days', isNegative ? -1 * days : days);
+  }
+  if (shouldDisplayHours(days, hours)) {
+    formatted = addSpaceIfNeeded(formatted);
+    formatted += translateWithParameters(
+      'work_duration.x_hours',
+      isNegative && formatted.length === 0 ? -1 * hours : hours,
+    );
+  }
+  if (shouldDisplayMinutes(days, hours, minutes)) {
+    formatted = addSpaceIfNeeded(formatted);
+    formatted += translateWithParameters(
+      'work_duration.x_minutes',
+      isNegative && formatted.length === 0 ? -1 * minutes : minutes,
+    );
+  }
+  return formatted;
+}
+
+function formatDurationShort(
+  isNegative: boolean,
+  days: number,
+  hours: number,
+  minutes: number,
+): string {
+  if (shouldDisplayDaysInShortFormat(days)) {
+    const roundedDays = Math.round(days);
+    const formattedDays = formatMeasure(
+      isNegative ? -1 * roundedDays : roundedDays,
+      MetricType.ShortInteger,
+    );
+    return translateWithParameters('work_duration.x_days', formattedDays);
+  }
+
+  if (shouldDisplayHoursInShortFormat(hours)) {
+    const roundedHours = Math.round(hours);
+    const formattedHours = formatMeasure(
+      isNegative ? -1 * roundedHours : roundedHours,
+      MetricType.ShortInteger,
+    );
+    return translateWithParameters('work_duration.x_hours', formattedHours);
+  }
+
+  const formattedMinutes = formatMeasure(
+    isNegative ? -1 * minutes : minutes,
+    MetricType.ShortInteger,
+  );
+  return translateWithParameters('work_duration.x_minutes', formattedMinutes);
+}
+
+function durationFormatter(value: string | number): string {
+  if (typeof value === 'string') {
+    value = parseInt(value, 10);
+  }
+  if (value === 0) {
+    return '0';
+  }
+  const hoursInDay = HOURS_IN_DAY;
+  const isNegative = value < 0;
+  const absValue = Math.abs(value);
+  const days = Math.floor(absValue / hoursInDay / 60);
+  let remainingValue = absValue - days * hoursInDay * 60;
+  const hours = Math.floor(remainingValue / 60);
+  remainingValue -= hours * 60;
+  return formatDuration(isNegative, days, hours, remainingValue);
+}
+
+function shortDurationFormatter(value: string | number): string {
+  if (typeof value === 'string') {
+    value = parseInt(value, 10);
+  }
+  if (value === 0) {
+    return '0';
+  }
+  const hoursInDay = HOURS_IN_DAY;
+  const isNegative = value < 0;
+  const absValue = Math.abs(value);
+  const days = absValue / hoursInDay / 60;
+  let remainingValue = absValue - Math.floor(days) * hoursInDay * 60;
+  const hours = remainingValue / 60;
+  remainingValue -= Math.floor(hours) * 60;
+  return formatDurationShort(isNegative, days, hours, remainingValue);
+}
+
+/*
+ * Debt Formatters
+ */
+function shouldDisplayDays(days: number): boolean {
+  return days > 0;
+}
+
+function shouldDisplayDaysInShortFormat(days: number): boolean {
+  return days > 0.9;
+}
+
+function shouldDisplayHours(days: number, hours: number): boolean {
+  return hours > 0 && days < 10;
+}
+
+function shouldDisplayHoursInShortFormat(hours: number): boolean {
+  return hours > 0.9;
+}
+
+function shouldDisplayMinutes(days: number, hours: number, minutes: number): boolean {
+  return minutes > 0 && hours < 10 && days === 0;
+}
+
+function addSpaceIfNeeded(value: string): string {
+  return value.length > 0 ? `${value} ` : value;
+}