aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-web/src/main/js/api/measures.ts10
-rw-r--r--server/sonar-web/src/main/js/app/types.ts53
-rw-r--r--server/sonar-web/src/main/js/apps/code/types.ts3
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx36
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.js)101
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/config/bubbles.ts2
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsList.tsx124
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.tsx79
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentList-test.tsx82
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/FilesView-test.tsx79
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentList-test.tsx.snap140
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/FilesView-test.tsx.snap110
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/utils.ts8
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/enhance.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx4
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/MeasuresOverlay.tsx3
-rw-r--r--server/sonar-web/src/main/js/components/measure/utils.ts9
-rw-r--r--server/sonar-web/src/main/js/helpers/measures.ts23
21 files changed, 650 insertions, 238 deletions
diff --git a/server/sonar-web/src/main/js/api/measures.ts b/server/sonar-web/src/main/js/api/measures.ts
index b5d1e649525..1102c39870c 100644
--- a/server/sonar-web/src/main/js/api/measures.ts
+++ b/server/sonar-web/src/main/js/api/measures.ts
@@ -19,8 +19,14 @@
*/
import { getJSON, RequestData, postJSON, post } from '../helpers/request';
import throwGlobalError from '../app/utils/throwGlobalError';
-import { Measure, MeasurePeriod } from '../helpers/measures';
-import { Metric, CustomMeasure, Paging, BranchParameters } from '../app/types';
+import {
+ Metric,
+ CustomMeasure,
+ Paging,
+ BranchParameters,
+ Measure,
+ MeasurePeriod
+} from '../app/types';
import { Period } from '../helpers/periods';
export function getMeasures(
diff --git a/server/sonar-web/src/main/js/app/types.ts b/server/sonar-web/src/main/js/app/types.ts
index 8a1da4194d6..62ff786b531 100644
--- a/server/sonar-web/src/main/js/app/types.ts
+++ b/server/sonar-web/src/main/js/app/types.ts
@@ -1,5 +1,3 @@
-import { Measure, MeasureEnhanced } from '../helpers/measures';
-
/*
* SonarQube
* Copyright (C) 2009-2018 SonarSource SA
@@ -101,6 +99,28 @@ export interface ComponentQualityProfile {
name: string;
}
+interface ComponentMeasureIntern {
+ isFavorite?: boolean;
+ isRecentlyBrowsed?: boolean;
+ key: string;
+ match?: string;
+ name: string;
+ organization?: string;
+ project?: string;
+ qualifier: string;
+ refKey?: string;
+}
+
+export interface ComponentMeasure extends ComponentMeasureIntern {
+ measures?: Measure[];
+}
+
+export interface ComponentMeasureEnhanced extends ComponentMeasureIntern {
+ value?: string;
+ leak?: string;
+ measures: MeasureEnhanced[];
+}
+
export interface Condition {
error: string;
id: number;
@@ -342,26 +362,25 @@ export interface MainBranch extends Branch {
status?: { qualityGateStatus: string };
}
-interface ComponentMeasureIntern {
- isFavorite?: boolean;
- isRecentlyBrowsed?: boolean;
- key: string;
- match?: string;
- name: string;
- organization?: string;
- project?: string;
- qualifier: string;
- refKey?: string;
+export interface MeasurePeriod {
+ bestValue?: boolean;
+ index: number;
+ value: string;
}
-export interface ComponentMeasure extends ComponentMeasureIntern {
- measures?: Measure[];
+interface MeasureIntern {
+ bestValue?: boolean;
+ periods?: MeasurePeriod[];
+ value?: string;
}
-export interface ComponentMeasureEnhanced extends ComponentMeasureIntern {
- value?: string;
+export interface Measure extends MeasureIntern {
+ metric: string;
+}
+
+export interface MeasureEnhanced extends MeasureIntern {
+ metric: Metric;
leak?: string;
- measures: MeasureEnhanced[];
}
export interface Metric {
diff --git a/server/sonar-web/src/main/js/apps/code/types.ts b/server/sonar-web/src/main/js/apps/code/types.ts
index a0b6459a3d3..f8fbc5af202 100644
--- a/server/sonar-web/src/main/js/apps/code/types.ts
+++ b/server/sonar-web/src/main/js/apps/code/types.ts
@@ -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 { Measure } from '../../helpers/measures';
+import { Measure } from '../../app/types';
export interface Component extends Breadcrumb {
branch?: string;
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx
index 6bdd4d6f190..9472307bc7e 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx
@@ -33,15 +33,18 @@ import { getComponentTree } from '../../../api/components';
import { complementary } from '../config/complementary';
import { enhanceComponent, isFileType, isViewType } from '../utils';
import { getProjectUrl } from '../../../helpers/urls';
-import { isDiffMetric, MeasureEnhanced } from '../../../helpers/measures';
+import { isDiffMetric } from '../../../helpers/measures';
import { isSameBranchLike, getBranchLikeQuery } from '../../../helpers/branches';
import DeferredSpinner from '../../../components/common/DeferredSpinner';
import {
+ BranchLike,
ComponentMeasure,
ComponentMeasureEnhanced,
- BranchLike,
+ CurrentUser,
+ isLoggedIn,
Metric,
- Paging
+ Paging,
+ MeasureEnhanced
} from '../../../app/types';
import { RequestData } from '../../../helpers/request';
import { Period } from '../../../helpers/periods';
@@ -50,8 +53,9 @@ interface Props {
branchLike?: BranchLike;
className?: string;
component: ComponentMeasure;
- currentUser: { isLoggedIn: boolean };
+ currentUser: CurrentUser;
loading: boolean;
+ loadingMore: boolean;
leakPeriod?: Period;
measure?: MeasureEnhanced;
metric: Metric;
@@ -66,7 +70,6 @@ interface Props {
}
interface State {
- bestValue?: string;
components: ComponentMeasureEnhanced[];
metric?: Metric;
paging?: Paging;
@@ -147,16 +150,15 @@ export default class MeasureContent extends React.PureComponent<Props, State> {
if (metric === this.props.metric) {
if (this.mounted) {
this.setState(({ selected }: State) => ({
- bestValue: r.metrics[0].bestValue,
components: r.components.map(component =>
enhanceComponent(component, metric, metrics)
),
- metric,
+ metric: { ...metric, ...r.metrics.find(m => m.key === metric.key) },
paging: r.paging,
selected:
- r.components.length > 0 && !r.components.find(c => c.key === selected)
- ? r.components[0].key
- : selected,
+ r.components.length > 0 && r.components.find(c => c.key === selected)
+ ? selected
+ : undefined,
view
}));
}
@@ -176,26 +178,25 @@ export default class MeasureContent extends React.PureComponent<Props, State> {
const { metricKeys, opts, strategy } = this.getComponentRequestParams(view, metric, {
p: paging.pageIndex + 1
});
- this.props.updateLoading({ components: true });
+ this.props.updateLoading({ moreComponents: true });
getComponentTree(strategy, component.key, metricKeys, opts).then(
r => {
if (metric === this.props.metric) {
if (this.mounted) {
this.setState(state => ({
- bestValue: r.metrics[0].bestValue,
components: [
...state.components,
...r.components.map(component => enhanceComponent(component, metric, metrics))
],
- metric,
+ metric: { ...metric, ...r.metrics.find(m => m.key === metric.key) },
paging: r.paging,
view
}));
}
- this.props.updateLoading({ components: false });
+ this.props.updateLoading({ moreComponents: false });
}
},
- () => this.props.updateLoading({ components: false })
+ () => this.props.updateLoading({ moreComponents: false })
);
};
@@ -243,12 +244,12 @@ export default class MeasureContent extends React.PureComponent<Props, State> {
const selectedIdx = this.getSelectedIndex();
return (
<FilesView
- bestValue={this.state.bestValue}
branchLike={this.props.branchLike}
components={this.state.components}
fetchMore={this.fetchMoreComponents}
handleOpen={this.onOpenComponent}
handleSelect={this.onSelectComponent}
+ loadingMore={this.props.loadingMore}
metric={metric}
metrics={this.props.metrics}
paging={this.state.paging}
@@ -276,7 +277,6 @@ export default class MeasureContent extends React.PureComponent<Props, State> {
render() {
const { branchLike, component, currentUser, measure, metric, rootComponent, view } = this.props;
- const isLoggedIn = currentUser && currentUser.isLoggedIn;
const isFile = isFileType(component);
const selectedIdx = this.getSelectedIndex();
return (
@@ -295,7 +295,7 @@ export default class MeasureContent extends React.PureComponent<Props, State> {
rootComponent={rootComponent}
/>
{component.key !== rootComponent.key &&
- isLoggedIn && (
+ isLoggedIn(currentUser) && (
<MeasureFavoriteContainer
branchLike={branchLike}
className="measure-favorite spacer-right"
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.js b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.tsx
index 160bf1d74b6..3c3a636538f 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.tsx
@@ -17,64 +17,61 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
+import { InjectedRouter } from 'react-router';
import MeasureContent from './MeasureContent';
-/*:: import type { Component, Period, Query } from '../types'; */
-/*:: import type { MeasureEnhanced } from '../../../components/measure/types'; */
-/*:: import type { Metric } from '../../../store/metrics/actions'; */
-/*:: import type { RawQuery } from '../../../helpers/query'; */
+import { Query } from '../utils';
+import {
+ ComponentMeasure,
+ Metric,
+ BranchLike,
+ CurrentUser,
+ MeasureEnhanced
+} from '../../../app/types';
+import { Period } from '../../../helpers/periods';
-/*:: type Props = {|
- branchLike?: { id?: string; name: string },
- className?: string,
- currentUser: { isLoggedIn: boolean },
- rootComponent: Component,
+interface Props {
+ branchLike?: BranchLike;
+ className?: string;
+ currentUser: CurrentUser;
+ rootComponent: ComponentMeasure;
fetchMeasures: (
component: string,
- metricsKey: Array<string>,
- branchLike?: { id?: string; name: string }
- ) => Promise<{ component: Component, measures: Array<MeasureEnhanced> }>,
- leakPeriod?: Period,
- metric: Metric,
- metrics: { [string]: Metric },
- router: {
- push: ({ pathname: string, query?: RawQuery }) => void
- },
- selected: ?string,
- updateQuery: Query => void,
- view: string
-|}; */
+ metricsKey: string[],
+ branchLike?: BranchLike
+ ) => Promise<{ component: ComponentMeasure; measures: MeasureEnhanced[] }>;
+ leakPeriod?: Period;
+ metric: Metric;
+ metrics: { [metric: string]: Metric };
+ router: InjectedRouter;
+ selected?: string;
+ updateQuery: (query: Partial<Query>) => void;
+ view: string;
+}
-/*:: type State = {
- component: ?Component,
- loading: {
- measure: boolean,
- components: boolean
- },
- measure: ?MeasureEnhanced,
- secondaryMeasure: ?MeasureEnhanced
-}; */
+interface LoadingState {
+ measure: boolean;
+ components: boolean;
+ moreComponents: boolean;
+}
-export default class MeasureContentContainer extends React.PureComponent {
- /*:: mounted: boolean; */
- /*:: props: Props; */
- state /*: State */ = {
- component: null,
- loading: {
- measure: false,
- components: false
- },
- measure: null,
- secondaryMeasure: null
- };
+interface State {
+ component?: ComponentMeasure;
+ loading: LoadingState;
+ measure?: MeasureEnhanced;
+ secondaryMeasure?: MeasureEnhanced;
+}
+
+export default class MeasureContentContainer extends React.PureComponent<Props, State> {
+ mounted = false;
+ state: State = { loading: { measure: false, components: false, moreComponents: false } };
componentDidMount() {
this.mounted = true;
this.fetchMeasure(this.props);
}
- componentWillReceiveProps(nextProps /*: Props */) {
+ componentWillReceiveProps(nextProps: Props) {
const { component } = this.state;
const componentChanged =
!component ||
@@ -89,7 +86,7 @@ export default class MeasureContentContainer extends React.PureComponent {
this.mounted = false;
}
- fetchMeasure = ({ branchLike, rootComponent, fetchMeasures, metric, selected } /*: Props */) => {
+ fetchMeasure = ({ branchLike, rootComponent, fetchMeasures, metric, selected }: Props) => {
this.updateLoading({ measure: true });
const metricKeys = [metric.key];
@@ -110,18 +107,19 @@ export default class MeasureContentContainer extends React.PureComponent {
);
};
- updateLoading = (loading /*: { [string]: boolean } */) => {
+ updateLoading = (loading: Partial<LoadingState>) => {
if (this.mounted) {
this.setState(state => ({ loading: { ...state.loading, ...loading } }));
}
};
- updateSelected = (component /*: string */) =>
+ updateSelected = (component: string) => {
this.props.updateQuery({
- selected: component !== this.props.rootComponent.key ? component : null
+ selected: component !== this.props.rootComponent.key ? component : undefined
});
+ };
- updateView = (view /*: string */) => this.props.updateQuery({ view });
+ updateView = (view: string) => this.props.updateQuery({ view });
render() {
if (!this.state.component) {
@@ -136,6 +134,7 @@ export default class MeasureContentContainer extends React.PureComponent {
currentUser={this.props.currentUser}
leakPeriod={this.props.leakPeriod}
loading={this.state.loading.measure || this.state.loading.components}
+ loadingMore={this.state.loading.moreComponents}
measure={this.state.measure}
metric={this.props.metric}
metrics={this.props.metrics}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/config/bubbles.ts b/server/sonar-web/src/main/js/apps/component-measures/config/bubbles.ts
index d59bb1d91e1..99c1b0e8e8c 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/config/bubbles.ts
+++ b/server/sonar-web/src/main/js/apps/component-measures/config/bubbles.ts
@@ -46,7 +46,7 @@ export const bubbles: {
},
Coverage: { x: 'complexity', y: 'coverage', size: 'uncovered_lines', yDomain: [100, 0] },
Duplications: { x: 'ncloc', y: 'duplicated_lines', size: 'duplicated_blocks' },
- // eslint-disable-next-line
+ // eslint-disable-next-line camelcase
project_overview: {
x: 'sqale_index',
y: 'coverage',
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsList.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsList.tsx
index ec49ffb6e9c..957fdc7b3e5 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsList.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsList.tsx
@@ -21,13 +21,10 @@ import * as React from 'react';
import ComponentsListRow from './ComponentsListRow';
import EmptyResult from './EmptyResult';
import { complementary } from '../config/complementary';
-import { getLocalizedMetricName, translate, translateWithParameters } from '../../../helpers/l10n';
-import { formatMeasure, isDiffMetric, isPeriodBestValue } from '../../../helpers/measures';
+import { getLocalizedMetricName } from '../../../helpers/l10n';
import { ComponentMeasure, ComponentMeasureEnhanced, Metric, BranchLike } from '../../../app/types';
-import { Button } from '../../../components/ui/buttons';
interface Props {
- bestValue?: string;
branchLike?: BranchLike;
components: ComponentMeasureEnhanced[];
onClick: (component: string) => void;
@@ -37,93 +34,44 @@ interface Props {
selectedComponent?: string;
}
-interface State {
- hideBest: boolean;
-}
-
-export default class ComponentsList extends React.PureComponent<Props, State> {
- state: State = { hideBest: true };
-
- componentWillReceiveProps(nextProps: Props) {
- if (nextProps.metric !== this.props.metric) {
- this.setState({ hideBest: true });
- }
+export default function ComponentsList({ components, metric, metrics, ...props }: Props) {
+ if (!components.length) {
+ return <EmptyResult />;
}
- displayAll = () => {
- this.setState({ hideBest: false });
- };
-
- hasBestValue = (component: ComponentMeasureEnhanced) => {
- const { metric } = this.props;
- const focusedMeasure = component.measures.find(measure => measure.metric.key === metric.key);
- if (focusedMeasure && isDiffMetric(focusedMeasure.metric.key)) {
- return isPeriodBestValue(focusedMeasure, 1);
- }
- return Boolean(focusedMeasure && focusedMeasure.bestValue);
- };
-
- renderHiddenLink = (hiddenCount: number) => {
- return (
- <div className="alert alert-info spacer-top">
- {translateWithParameters(
- 'component_measures.hidden_best_score_metrics',
- hiddenCount,
- formatMeasure(this.props.bestValue, this.props.metric.type)
- )}
- <Button className="button-link spacer-left" onClick={this.displayAll}>
- {translate('show_all')}
- </Button>
- </div>
- );
- };
-
- render() {
- const { components, metric, metrics } = this.props;
- if (!components.length) {
- return <EmptyResult />;
- }
-
- const otherMetrics = (complementary[metric.key] || []).map(key => metrics[key]);
- const notBestComponents = components.filter(component => !this.hasBestValue(component));
- const hiddenCount = components.length - notBestComponents.length;
- const shouldHideBest = this.state.hideBest && hiddenCount !== components.length;
- return (
- <React.Fragment>
- <table className="data zebra zebra-hover">
- {otherMetrics.length > 0 && (
- <thead>
- <tr>
- <th>&nbsp;</th>
- <th className="text-right">
+ const otherMetrics = (complementary[metric.key] || []).map(key => metrics[key]);
+ return (
+ <React.Fragment>
+ <table className="data zebra zebra-hover">
+ {otherMetrics.length > 0 && (
+ <thead>
+ <tr>
+ <th>&nbsp;</th>
+ <th className="text-right">
+ <span className="small">{getLocalizedMetricName(metric)}</span>
+ </th>
+ {otherMetrics.map(metric => (
+ <th className="text-right" key={metric.key}>
<span className="small">{getLocalizedMetricName(metric)}</span>
</th>
- {otherMetrics.map(metric => (
- <th className="text-right" key={metric.key}>
- <span className="small">{getLocalizedMetricName(metric)}</span>
- </th>
- ))}
- </tr>
- </thead>
- )}
+ ))}
+ </tr>
+ </thead>
+ )}
- <tbody>
- {(shouldHideBest ? notBestComponents : components).map(component => (
- <ComponentsListRow
- branchLike={this.props.branchLike}
- component={component}
- isSelected={component.key === this.props.selectedComponent}
- key={component.key}
- metric={metric}
- onClick={this.props.onClick}
- otherMetrics={otherMetrics}
- rootComponent={this.props.rootComponent}
- />
- ))}
- </tbody>
- </table>
- {shouldHideBest && hiddenCount > 0 && this.renderHiddenLink(hiddenCount)}
- </React.Fragment>
- );
- }
+ <tbody>
+ {components.map(component => (
+ <ComponentsListRow
+ component={component}
+ isSelected={component.key === props.selectedComponent}
+ key={component.key}
+ metric={metric}
+ otherMetrics={otherMetrics}
+ {...props}
+ />
+ ))}
+ </tbody>
+ </table>
+ </React.Fragment>
+ );
}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.tsx
index a4b0144ba08..ce19d48ae3f 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.tsx
@@ -22,7 +22,7 @@ import * as key from 'keymaster';
import { throttle } from 'lodash';
import ComponentsList from './ComponentsList';
import ListFooter from '../../../components/controls/ListFooter';
-import { scrollToElement } from '../../../helpers/scrolling';
+import { Button } from '../../../components/ui/buttons';
import {
ComponentMeasure,
ComponentMeasureEnhanced,
@@ -30,14 +30,17 @@ import {
Paging,
BranchLike
} from '../../../app/types';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { isPeriodBestValue, isDiffMetric, formatMeasure } from '../../../helpers/measures';
+import { scrollToElement } from '../../../helpers/scrolling';
interface Props {
- bestValue?: string;
branchLike?: BranchLike;
components: ComponentMeasureEnhanced[];
fetchMore: () => void;
handleSelect: (component: string) => void;
handleOpen: (component: string) => void;
+ loadingMore: boolean;
metric: Metric;
metrics: { [metric: string]: Metric };
paging?: Paging;
@@ -46,11 +49,16 @@ interface Props {
selectedIdx?: number;
}
-export default class ListView extends React.PureComponent<Props> {
+interface State {
+ showBestMeasures: boolean;
+}
+
+export default class ListView extends React.PureComponent<Props, State> {
listContainer?: HTMLElement | null;
constructor(props: Props) {
super(props);
+ this.state = { showBestMeasures: false };
this.selectNext = throttle(this.selectNext, 100);
this.selectPrevious = throttle(this.selectPrevious, 100);
}
@@ -66,6 +74,9 @@ export default class ListView extends React.PureComponent<Props> {
if (this.props.selectedKey !== undefined && prevProps.selectedKey !== this.props.selectedKey) {
this.scrollToElement();
}
+ if (prevProps.metric.key !== this.props.metric.key) {
+ this.setState({ showBestMeasures: false });
+ }
}
componentWillUnmount() {
@@ -91,6 +102,30 @@ export default class ListView extends React.PureComponent<Props> {
['up', 'down', 'right'].forEach(action => key.unbind(action, 'measures-files'));
}
+ getVisibleComponents = (components: ComponentMeasureEnhanced[], showBestMeasures: boolean) => {
+ if (showBestMeasures) {
+ return components;
+ }
+ const filtered = components.filter(component => !this.hasBestValue(component));
+ if (filtered.length === 0) {
+ return components;
+ }
+ return filtered;
+ };
+
+ handleShowBestMeasures = () => {
+ this.setState({ showBestMeasures: true });
+ };
+
+ hasBestValue = (component: ComponentMeasureEnhanced) => {
+ const { metric } = this.props;
+ const focusedMeasure = component.measures.find(measure => measure.metric.key === metric.key);
+ if (focusedMeasure && isDiffMetric(metric.key)) {
+ return isPeriodBestValue(focusedMeasure, 1);
+ }
+ return Boolean(focusedMeasure && focusedMeasure.bestValue);
+ };
+
openSelected = () => {
if (this.props.selectedKey !== undefined) {
this.props.handleOpen(this.props.selectedKey);
@@ -98,20 +133,22 @@ export default class ListView extends React.PureComponent<Props> {
};
selectPrevious = () => {
- const { selectedIdx } = this.props;
+ const { components, selectedIdx } = this.props;
+ const visibleComponents = this.getVisibleComponents(components, this.state.showBestMeasures);
if (selectedIdx !== undefined && selectedIdx > 0) {
- this.props.handleSelect(this.props.components[selectedIdx - 1].key);
+ this.props.handleSelect(visibleComponents[selectedIdx - 1].key);
} else {
- this.props.handleSelect(this.props.components[this.props.components.length - 1].key);
+ this.props.handleSelect(visibleComponents[visibleComponents.length - 1].key);
}
};
selectNext = () => {
- const { selectedIdx } = this.props;
- if (selectedIdx !== undefined && selectedIdx < this.props.components.length - 1) {
- this.props.handleSelect(this.props.components[selectedIdx + 1].key);
+ const { components, selectedIdx } = this.props;
+ const visibleComponents = this.getVisibleComponents(components, this.state.showBestMeasures);
+ if (selectedIdx !== undefined && selectedIdx < visibleComponents.length - 1) {
+ this.props.handleSelect(visibleComponents[selectedIdx + 1].key);
} else {
- this.props.handleSelect(this.props.components[0].key);
+ this.props.handleSelect(visibleComponents[0].key);
}
};
@@ -125,23 +162,39 @@ export default class ListView extends React.PureComponent<Props> {
};
render() {
+ const { components } = this.props;
+ const filteredComponents = this.getVisibleComponents(components, this.state.showBestMeasures);
+ const hidingBestMeasures = filteredComponents.length < components.length;
return (
<div ref={elem => (this.listContainer = elem)}>
<ComponentsList
- bestValue={this.props.bestValue}
branchLike={this.props.branchLike}
- components={this.props.components}
+ components={filteredComponents}
metric={this.props.metric}
metrics={this.props.metrics}
onClick={this.props.handleOpen}
rootComponent={this.props.rootComponent}
selectedComponent={this.props.selectedKey}
/>
- {this.props.paging &&
+ {hidingBestMeasures && (
+ <div className="alert alert-info spacer-top">
+ {translateWithParameters(
+ 'component_measures.hidden_best_score_metrics',
+ components.length - filteredComponents.length,
+ formatMeasure(this.props.metric.bestValue, this.props.metric.type)
+ )}
+ <Button className="button-link spacer-left" onClick={this.handleShowBestMeasures}>
+ {translate('show_all')}
+ </Button>
+ </div>
+ )}
+ {!hidingBestMeasures &&
+ this.props.paging &&
this.props.components.length > 0 && (
<ListFooter
count={this.props.components.length}
loadMore={this.props.fetchMore}
+ loading={this.props.loadingMore}
total={this.props.paging.total}
/>
)}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.tsx
index 828cab940d7..61a35f44ace 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.tsx
@@ -72,13 +72,14 @@ export default class TreeMapView extends React.PureComponent<Props, State> {
}
const colorValue =
colorMeasure && (isDiffMetric(metric.key) ? colorMeasure.leak : colorMeasure.value);
- const sizeValue = Number(
- isDiffMetric(sizeMeasure.metric.key) ? sizeMeasure.leak : sizeMeasure.value
- );
- if (isNaN(sizeValue)) {
+ const rawSizeValue = isDiffMetric(sizeMeasure.metric.key)
+ ? sizeMeasure.leak
+ : sizeMeasure.value;
+ if (rawSizeValue === undefined) {
return undefined;
}
+ const sizeValue = Number(rawSizeValue);
return {
color:
colorValue !== undefined ? (colorScale as Function)(colorValue) : theme.secondFontColor,
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentList-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentList-test.tsx
new file mode 100644
index 00000000000..38c5bccbcac
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentList-test.tsx
@@ -0,0 +1,82 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+/* eslint-disable camelcase */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import ComponentsList from '../ComponentsList';
+
+const COMPONENTS = [
+ {
+ key: 'foo',
+ measures: [],
+ name: 'Foo',
+ organization: 'foo',
+ qualifier: 'TRK'
+ }
+];
+
+const METRICS = {
+ coverage: { id: '1', key: 'coverage', type: 'PERCENT', name: 'Coverage' },
+ new_bugs: { id: '2', key: 'new_bugs', type: 'INT', name: 'New Bugs' },
+ uncovered_lines: { id: '3', key: 'uncovered_lines', type: 'INT', name: 'Lines' },
+ uncovered_conditions: { id: '4', key: 'uncovered_conditions', type: 'INT', name: 'Conditions' }
+};
+
+it('should renders correctly', () => {
+ expect(
+ shallow(
+ <ComponentsList
+ components={COMPONENTS}
+ metric={METRICS.new_bugs}
+ metrics={METRICS}
+ onClick={jest.fn()}
+ rootComponent={COMPONENTS[0]}
+ />
+ )
+ ).toMatchSnapshot();
+});
+
+it('should renders empty', () => {
+ expect(
+ shallow(
+ <ComponentsList
+ components={[]}
+ metric={METRICS.new_bugs}
+ metrics={METRICS}
+ onClick={jest.fn()}
+ rootComponent={COMPONENTS[0]}
+ />
+ )
+ ).toMatchSnapshot();
+});
+
+it('should renders with multiple measures', () => {
+ expect(
+ shallow(
+ <ComponentsList
+ components={COMPONENTS}
+ metric={METRICS.coverage}
+ metrics={METRICS}
+ onClick={jest.fn()}
+ rootComponent={COMPONENTS[0]}
+ />
+ )
+ ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/FilesView-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/FilesView-test.tsx
new file mode 100644
index 00000000000..b9cce0022db
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/FilesView-test.tsx
@@ -0,0 +1,79 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+/* eslint-disable camelcase */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import FilesView from '../FilesView';
+
+const COMPONENTS = [
+ {
+ key: 'foo',
+ measures: [],
+ name: 'Foo',
+ organization: 'foo',
+ qualifier: 'TRK'
+ }
+];
+
+const METRICS = { coverage: { id: '1', key: 'coverage', type: 'PERCENT', name: 'Coverage' } };
+
+it('should renders correctly', () => {
+ expect(getWrapper()).toMatchSnapshot();
+});
+
+it('should render with best values hidden', () => {
+ expect(
+ getWrapper({
+ components: [
+ ...COMPONENTS,
+ {
+ key: 'bar',
+ measures: [{ bestValue: true, metric: { key: 'coverage' } }],
+ name: 'Bar',
+ organization: 'foo',
+ qualifier: 'TRK'
+ }
+ ]
+ })
+ ).toMatchSnapshot();
+});
+
+function getWrapper(props = {}) {
+ return shallow(
+ <FilesView
+ components={COMPONENTS}
+ fetchMore={jest.fn()}
+ handleOpen={jest.fn()}
+ handleSelect={jest.fn()}
+ loadingMore={false}
+ metric={METRICS.coverage}
+ metrics={METRICS}
+ paging={{ pageIndex: 0, pageSize: 5, total: 10 }}
+ rootComponent={{
+ key: 'parent',
+ measures: [],
+ name: 'Parent',
+ organization: 'foo',
+ qualifier: 'TRK'
+ }}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentList-test.tsx.snap b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentList-test.tsx.snap
new file mode 100644
index 00000000000..5633f3760fe
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentList-test.tsx.snap
@@ -0,0 +1,140 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should renders correctly 1`] = `
+<React.Fragment>
+ <table
+ className="data zebra zebra-hover"
+ >
+ <tbody>
+ <ComponentsListRow
+ component={
+ Object {
+ "key": "foo",
+ "measures": Array [],
+ "name": "Foo",
+ "organization": "foo",
+ "qualifier": "TRK",
+ }
+ }
+ isSelected={false}
+ key="foo"
+ metric={
+ Object {
+ "id": "2",
+ "key": "new_bugs",
+ "name": "New Bugs",
+ "type": "INT",
+ }
+ }
+ onClick={[MockFunction]}
+ otherMetrics={Array []}
+ rootComponent={
+ Object {
+ "key": "foo",
+ "measures": Array [],
+ "name": "Foo",
+ "organization": "foo",
+ "qualifier": "TRK",
+ }
+ }
+ />
+ </tbody>
+ </table>
+</React.Fragment>
+`;
+
+exports[`should renders empty 1`] = `<EmptyResult />`;
+
+exports[`should renders with multiple measures 1`] = `
+<React.Fragment>
+ <table
+ className="data zebra zebra-hover"
+ >
+ <thead>
+ <tr>
+ <th>
+  
+ </th>
+ <th
+ className="text-right"
+ >
+ <span
+ className="small"
+ >
+ Coverage
+ </span>
+ </th>
+ <th
+ className="text-right"
+ key="uncovered_lines"
+ >
+ <span
+ className="small"
+ >
+ Lines
+ </span>
+ </th>
+ <th
+ className="text-right"
+ key="uncovered_conditions"
+ >
+ <span
+ className="small"
+ >
+ Conditions
+ </span>
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <ComponentsListRow
+ component={
+ Object {
+ "key": "foo",
+ "measures": Array [],
+ "name": "Foo",
+ "organization": "foo",
+ "qualifier": "TRK",
+ }
+ }
+ isSelected={false}
+ key="foo"
+ metric={
+ Object {
+ "id": "1",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ }
+ }
+ onClick={[MockFunction]}
+ otherMetrics={
+ Array [
+ Object {
+ "id": "3",
+ "key": "uncovered_lines",
+ "name": "Lines",
+ "type": "INT",
+ },
+ Object {
+ "id": "4",
+ "key": "uncovered_conditions",
+ "name": "Conditions",
+ "type": "INT",
+ },
+ ]
+ }
+ rootComponent={
+ Object {
+ "key": "foo",
+ "measures": Array [],
+ "name": "Foo",
+ "organization": "foo",
+ "qualifier": "TRK",
+ }
+ }
+ />
+ </tbody>
+ </table>
+</React.Fragment>
+`;
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/FilesView-test.tsx.snap b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/FilesView-test.tsx.snap
new file mode 100644
index 00000000000..b63601b1b71
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/FilesView-test.tsx.snap
@@ -0,0 +1,110 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render with best values hidden 1`] = `
+<div>
+ <ComponentsList
+ components={
+ Array [
+ Object {
+ "key": "foo",
+ "measures": Array [],
+ "name": "Foo",
+ "organization": "foo",
+ "qualifier": "TRK",
+ },
+ ]
+ }
+ metric={
+ Object {
+ "id": "1",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ }
+ }
+ metrics={
+ Object {
+ "coverage": Object {
+ "id": "1",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ }
+ }
+ onClick={[MockFunction]}
+ rootComponent={
+ Object {
+ "key": "parent",
+ "measures": Array [],
+ "name": "Parent",
+ "organization": "foo",
+ "qualifier": "TRK",
+ }
+ }
+ />
+ <div
+ className="alert alert-info spacer-top"
+ >
+ component_measures.hidden_best_score_metrics.1.
+ <Button
+ className="button-link spacer-left"
+ onClick={[Function]}
+ >
+ show_all
+ </Button>
+ </div>
+</div>
+`;
+
+exports[`should renders correctly 1`] = `
+<div>
+ <ComponentsList
+ components={
+ Array [
+ Object {
+ "key": "foo",
+ "measures": Array [],
+ "name": "Foo",
+ "organization": "foo",
+ "qualifier": "TRK",
+ },
+ ]
+ }
+ metric={
+ Object {
+ "id": "1",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ }
+ }
+ metrics={
+ Object {
+ "coverage": Object {
+ "id": "1",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ }
+ }
+ onClick={[MockFunction]}
+ rootComponent={
+ Object {
+ "key": "parent",
+ "measures": Array [],
+ "name": "Parent",
+ "organization": "foo",
+ "qualifier": "TRK",
+ }
+ }
+ />
+ <ListFooter
+ count={1}
+ loadMore={[MockFunction]}
+ loading={false}
+ total={10}
+ />
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/component-measures/utils.ts b/server/sonar-web/src/main/js/apps/component-measures/utils.ts
index 553edee41f4..a02181c7e95 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/utils.ts
+++ b/server/sonar-web/src/main/js/apps/component-measures/utils.ts
@@ -21,10 +21,14 @@ import { groupBy, memoize, sortBy, toPairs } from 'lodash';
import { domains } from './config/domains';
import { bubbles } from './config/bubbles';
import { getLocalizedMetricName } from '../../helpers/l10n';
-import { ComponentMeasure, ComponentMeasureEnhanced, Metric } from '../../app/types';
+import {
+ ComponentMeasure,
+ ComponentMeasureEnhanced,
+ Metric,
+ MeasureEnhanced
+} from '../../app/types';
import { enhanceMeasure } from '../../components/measure/utils';
import { cleanQuery, parseAsString, RawQuery, serializeString } from '../../helpers/query';
-import { MeasureEnhanced } from '../../helpers/measures';
export const PROJECT_OVERVEW = 'project_overview';
export const DEFAULT_VIEW = 'list';
diff --git a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx
index dcf365a6615..63aed5899de 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx
@@ -31,7 +31,7 @@ import throwGlobalError from '../../../app/utils/throwGlobalError';
import { getMeasuresAndMeta } from '../../../api/measures';
import { getAllTimeMachineData, History } from '../../../api/time-machine';
import { parseDate } from '../../../helpers/dates';
-import { enhanceMeasuresWithMetrics, MeasureEnhanced } from '../../../helpers/measures';
+import { enhanceMeasuresWithMetrics } from '../../../helpers/measures';
import { getLeakPeriod, Period } from '../../../helpers/periods';
import { get } from '../../../helpers/storage';
import { METRICS, HISTORY_METRICS_LIST } from '../utils';
@@ -48,7 +48,7 @@ import {
} from '../../../helpers/branches';
import { fetchMetrics } from '../../../store/rootActions';
import { getMetrics } from '../../../store/rootReducer';
-import { BranchLike, Component, Metric } from '../../../app/types';
+import { BranchLike, Component, Metric, MeasureEnhanced } from '../../../app/types';
import { translate } from '../../../helpers/l10n';
import '../styles.css';
diff --git a/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx b/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx
index 2ff27976bf8..b5509cbd738 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx
@@ -30,8 +30,7 @@ import {
isDiffMetric,
getPeriodValue,
getShortType,
- getRatingTooltip,
- MeasureEnhanced
+ getRatingTooltip
} from '../../../helpers/measures';
import { getLocalizedMetricName } from '../../../helpers/l10n';
import { getPeriodDate } from '../../../helpers/periods';
@@ -40,7 +39,7 @@ import {
getComponentIssuesUrl,
getMeasureHistoryUrl
} from '../../../helpers/urls';
-import { Component, BranchLike } from '../../../app/types';
+import { Component, BranchLike, MeasureEnhanced } from '../../../app/types';
import { History } from '../../../api/time-machine';
import { getBranchLikeQuery } from '../../../helpers/branches';
diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx b/server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx
index 64a0a9e4ea6..cd93c3a9fbd 100644
--- a/server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx
@@ -35,11 +35,11 @@ import {
Metric,
BranchLike,
CurrentUser,
- Organization
+ Organization,
+ MeasureEnhanced
} from '../../../app/types';
import { History } from '../../../api/time-machine';
import { translate } from '../../../helpers/l10n';
-import { MeasureEnhanced } from '../../../helpers/measures';
import { hasPrivateAccess } from '../../../helpers/organizations';
import {
getCurrentUser,
diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx b/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx
index f1b25609103..4fdc6e23ebf 100644
--- a/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx
@@ -22,10 +22,10 @@ import * as classNames from 'classnames';
import DrilldownLink from '../../../components/shared/DrilldownLink';
import LanguageDistributionContainer from '../../../components/charts/LanguageDistributionContainer';
import SizeRating from '../../../components/ui/SizeRating';
-import { formatMeasure, MeasureEnhanced } from '../../../helpers/measures';
+import { formatMeasure } from '../../../helpers/measures';
import { getMetricName } from '../helpers/metrics';
import { translate } from '../../../helpers/l10n';
-import { LightComponent, BranchLike } from '../../../app/types';
+import { LightComponent, BranchLike, MeasureEnhanced } from '../../../app/types';
interface Props {
branchLike?: BranchLike;
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/MeasuresOverlay.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/MeasuresOverlay.tsx
index dc71ec4d2aa..cf25dc909b5 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/components/MeasuresOverlay.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/components/MeasuresOverlay.tsx
@@ -26,7 +26,7 @@ import { Button } from '../../ui/buttons';
import { getFacets } from '../../../api/issues';
import { getMeasures } from '../../../api/measures';
import { getAllMetrics } from '../../../api/metrics';
-import { FacetValue, SourceViewerFile, BranchLike } from '../../../app/types';
+import { FacetValue, SourceViewerFile, BranchLike, MeasureEnhanced } from '../../../app/types';
import Modal from '../../controls/Modal';
import Measure from '../../measure/Measure';
import QualifierIcon from '../../icons-components/QualifierIcon';
@@ -38,7 +38,6 @@ import { SEVERITIES, TYPES } from '../../../helpers/constants';
import { translate, getLocalizedMetricName } from '../../../helpers/l10n';
import {
formatMeasure,
- MeasureEnhanced,
getDisplayMetrics,
enhanceMeasuresWithMetrics
} from '../../../helpers/measures';
diff --git a/server/sonar-web/src/main/js/components/measure/utils.ts b/server/sonar-web/src/main/js/components/measure/utils.ts
index 626c65f552f..e45496fbd1a 100644
--- a/server/sonar-web/src/main/js/components/measure/utils.ts
+++ b/server/sonar-web/src/main/js/components/measure/utils.ts
@@ -17,13 +17,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import {
- getRatingTooltip as nextGetRatingTooltip,
- isDiffMetric,
- Measure,
- MeasureEnhanced
-} from '../../helpers/measures';
-import { Metric } from '../../app/types';
+import { getRatingTooltip as nextGetRatingTooltip, isDiffMetric } from '../../helpers/measures';
+import { Metric, Measure, MeasureEnhanced } from '../../app/types';
const KNOWN_RATINGS = ['sqale_rating', 'reliability_rating', 'security_rating'];
diff --git a/server/sonar-web/src/main/js/helpers/measures.ts b/server/sonar-web/src/main/js/helpers/measures.ts
index 171e04121f2..78256f28078 100644
--- a/server/sonar-web/src/main/js/helpers/measures.ts
+++ b/server/sonar-web/src/main/js/helpers/measures.ts
@@ -18,31 +18,10 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { translate, translateWithParameters, getCurrentLocale } from './l10n';
-import { Metric } from '../app/types';
+import { Metric, Measure, MeasureEnhanced } from '../app/types';
const HOURS_IN_DAY = 8;
-export interface MeasurePeriod {
- bestValue?: boolean;
- index: number;
- value: string;
-}
-
-export interface MeasureIntern {
- bestValue?: boolean;
- periods?: MeasurePeriod[];
- value?: string;
-}
-
-export interface Measure extends MeasureIntern {
- metric: string;
-}
-
-export interface MeasureEnhanced extends MeasureIntern {
- metric: Metric;
- leak?: string;
-}
-
interface Formatter {
(value: string | number, options?: any): string;
}