]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-11140 Don't automatically select first file in project measures page
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>
Wed, 15 Aug 2018 13:52:22 +0000 (15:52 +0200)
committerSonarTech <sonartech@sonarsource.com>
Thu, 16 Aug 2018 18:20:52 +0000 (20:20 +0200)
Also:
* Fix hidden selection when selecting a hidden file (best value)
* Fix load more spinner

22 files changed:
server/sonar-web/src/main/js/api/measures.ts
server/sonar-web/src/main/js/app/types.ts
server/sonar-web/src/main/js/apps/code/types.ts
server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx
server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.js [deleted file]
server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/component-measures/config/bubbles.ts
server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsList.tsx
server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.tsx
server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.tsx
server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentList-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/FilesView-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentList-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/FilesView-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/apps/component-measures/utils.ts
server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx
server/sonar-web/src/main/js/apps/overview/main/enhance.tsx
server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx
server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx
server/sonar-web/src/main/js/components/SourceViewer/components/MeasuresOverlay.tsx
server/sonar-web/src/main/js/components/measure/utils.ts
server/sonar-web/src/main/js/helpers/measures.ts

index b5d1e6495256b90dc3fb0653badaf78d72e918e3..1102c39870c20fa8016b29affb6b3628460169eb 100644 (file)
  */
 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(
index 8a1da4194d61a0ec481e2390cd0f46453147e9b1..62ff786b5318d2b1d16c0267344c90759ed1b962 100644 (file)
@@ -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 {
index a0b6459a3d3aa2596cf0137fde14b763efd2ccc4..f8fbc5af202d93445ed8b236c1080c7f294fdf22 100644 (file)
@@ -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;
index 6bdd4d6f1909ef99e14862714106ca672a82d949..9472307bc7edaa8c7c7e5a91abeb8ab7371c5fb0 100644 (file)
@@ -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.js
deleted file mode 100644 (file)
index 160bf1d..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * 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.
- */
-// @flow
-import React from 'react';
-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'; */
-
-/*:: type Props = {|
-  branchLike?: { id?: string; name: string },
-  className?: string,
-  currentUser: { isLoggedIn: boolean },
-  rootComponent: Component,
-  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
-|}; */
-
-/*:: type State = {
-  component: ?Component,
-  loading: {
-    measure: boolean,
-    components: boolean
-  },
-  measure: ?MeasureEnhanced,
-  secondaryMeasure: ?MeasureEnhanced
-}; */
-
-export default class MeasureContentContainer extends React.PureComponent {
-  /*:: mounted: boolean; */
-  /*:: props: Props; */
-  state /*: State */ = {
-    component: null,
-    loading: {
-      measure: false,
-      components: false
-    },
-    measure: null,
-    secondaryMeasure: null
-  };
-
-  componentDidMount() {
-    this.mounted = true;
-    this.fetchMeasure(this.props);
-  }
-
-  componentWillReceiveProps(nextProps /*: Props */) {
-    const { component } = this.state;
-    const componentChanged =
-      !component ||
-      nextProps.rootComponent.key !== component.key ||
-      nextProps.selected !== component.key;
-    if (componentChanged || nextProps.metric !== this.props.metric) {
-      this.fetchMeasure(nextProps);
-    }
-  }
-
-  componentWillUnmount() {
-    this.mounted = false;
-  }
-
-  fetchMeasure = ({ branchLike, rootComponent, fetchMeasures, metric, selected } /*: Props */) => {
-    this.updateLoading({ measure: true });
-
-    const metricKeys = [metric.key];
-    if (metric.key === 'ncloc') {
-      metricKeys.push('ncloc_language_distribution');
-    }
-
-    fetchMeasures(selected || rootComponent.key, metricKeys, branchLike).then(
-      ({ component, measures }) => {
-        if (this.mounted) {
-          const measure = measures.find(measure => measure.metric.key === metric.key);
-          const secondaryMeasure = measures.find(measure => measure.metric.key !== metric.key);
-          this.setState({ component, measure, secondaryMeasure });
-          this.updateLoading({ measure: false });
-        }
-      },
-      () => this.updateLoading({ measure: false })
-    );
-  };
-
-  updateLoading = (loading /*: { [string]: boolean } */) => {
-    if (this.mounted) {
-      this.setState(state => ({ loading: { ...state.loading, ...loading } }));
-    }
-  };
-
-  updateSelected = (component /*: string */) =>
-    this.props.updateQuery({
-      selected: component !== this.props.rootComponent.key ? component : null
-    });
-
-  updateView = (view /*: string */) => this.props.updateQuery({ view });
-
-  render() {
-    if (!this.state.component) {
-      return null;
-    }
-
-    return (
-      <MeasureContent
-        branchLike={this.props.branchLike}
-        className={this.props.className}
-        component={this.state.component}
-        currentUser={this.props.currentUser}
-        leakPeriod={this.props.leakPeriod}
-        loading={this.state.loading.measure || this.state.loading.components}
-        measure={this.state.measure}
-        metric={this.props.metric}
-        metrics={this.props.metrics}
-        rootComponent={this.props.rootComponent}
-        router={this.props.router}
-        secondaryMeasure={this.state.secondaryMeasure}
-        updateLoading={this.updateLoading}
-        updateSelected={this.updateSelected}
-        updateView={this.updateView}
-        view={this.props.view}
-      />
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.tsx
new file mode 100644 (file)
index 0000000..3c3a636
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+import * as React from 'react';
+import { InjectedRouter } from 'react-router';
+import MeasureContent from './MeasureContent';
+import { Query } from '../utils';
+import {
+  ComponentMeasure,
+  Metric,
+  BranchLike,
+  CurrentUser,
+  MeasureEnhanced
+} from '../../../app/types';
+import { Period } from '../../../helpers/periods';
+
+interface Props {
+  branchLike?: BranchLike;
+  className?: string;
+  currentUser: CurrentUser;
+  rootComponent: ComponentMeasure;
+  fetchMeasures: (
+    component: 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;
+}
+
+interface LoadingState {
+  measure: boolean;
+  components: boolean;
+  moreComponents: boolean;
+}
+
+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) {
+    const { component } = this.state;
+    const componentChanged =
+      !component ||
+      nextProps.rootComponent.key !== component.key ||
+      nextProps.selected !== component.key;
+    if (componentChanged || nextProps.metric !== this.props.metric) {
+      this.fetchMeasure(nextProps);
+    }
+  }
+
+  componentWillUnmount() {
+    this.mounted = false;
+  }
+
+  fetchMeasure = ({ branchLike, rootComponent, fetchMeasures, metric, selected }: Props) => {
+    this.updateLoading({ measure: true });
+
+    const metricKeys = [metric.key];
+    if (metric.key === 'ncloc') {
+      metricKeys.push('ncloc_language_distribution');
+    }
+
+    fetchMeasures(selected || rootComponent.key, metricKeys, branchLike).then(
+      ({ component, measures }) => {
+        if (this.mounted) {
+          const measure = measures.find(measure => measure.metric.key === metric.key);
+          const secondaryMeasure = measures.find(measure => measure.metric.key !== metric.key);
+          this.setState({ component, measure, secondaryMeasure });
+          this.updateLoading({ measure: false });
+        }
+      },
+      () => this.updateLoading({ measure: false })
+    );
+  };
+
+  updateLoading = (loading: Partial<LoadingState>) => {
+    if (this.mounted) {
+      this.setState(state => ({ loading: { ...state.loading, ...loading } }));
+    }
+  };
+
+  updateSelected = (component: string) => {
+    this.props.updateQuery({
+      selected: component !== this.props.rootComponent.key ? component : undefined
+    });
+  };
+
+  updateView = (view: string) => this.props.updateQuery({ view });
+
+  render() {
+    if (!this.state.component) {
+      return null;
+    }
+
+    return (
+      <MeasureContent
+        branchLike={this.props.branchLike}
+        className={this.props.className}
+        component={this.state.component}
+        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}
+        rootComponent={this.props.rootComponent}
+        router={this.props.router}
+        secondaryMeasure={this.state.secondaryMeasure}
+        updateLoading={this.updateLoading}
+        updateSelected={this.updateSelected}
+        updateView={this.updateView}
+        view={this.props.view}
+      />
+    );
+  }
+}
index d59bb1d91e1aa816b6324ca9213f3cf6e7c1b040..99c1b0e8e8c67e1bee4e2fb587f272f748cfa3d4 100644 (file)
@@ -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',
index ec49ffb6e9cfdadded4c9454fc370aae2184d371..957fdc7b3e5dc3e25653816df62c92816d2ae4c5 100644 (file)
@@ -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>
+  );
 }
index a4b0144ba08d6e89d27026a31abf171a5ed41a89..ce19d48ae3f7fb5d45f2a1b5be8ffbf65da09354 100644 (file)
@@ -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}
             />
           )}
index 828cab940d7dd29f67b988df733bbcc4944dedef..61a35f44ace09bd990ea4bb96536efcf96dd30e1 100644 (file)
@@ -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 (file)
index 0000000..38c5bcc
--- /dev/null
@@ -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 (file)
index 0000000..b9cce00
--- /dev/null
@@ -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 (file)
index 0000000..5633f37
--- /dev/null
@@ -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 (file)
index 0000000..b63601b
--- /dev/null
@@ -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>
+`;
index 553edee41f492da10618a96bc11f832540d091a6..a02181c7e95c1c1d854a78fc5e281f050dda4a76 100644 (file)
@@ -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';
index dcf365a66151f470355f1279d39defbfb90d2018..63aed5899de3605d0d3092dd0fcda3a23031ea09 100644 (file)
@@ -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';
 
index 2ff27976bf8660d7ae029b0367d18f304a510979..b5509cbd7383fa2464c519bf14b4158a3a991604 100644 (file)
@@ -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';
 
index 64a0a9e4ea6f70c7cf1a55c6b78ed7276547e373..cd93c3a9fbd76892fea9884e0a02373bd87e1533 100644 (file)
@@ -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,
index f1b25609103266194589336ea5786ad8d9759320..4fdc6e23ebf45d91b3d1b82ac68bfba2d37945ea 100644 (file)
@@ -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;
index dc71ec4d2aac249a753e3d1dad024b9d5f45285e..cf25dc909b56a3a2f265d6572a5eb237a01f91e4 100644 (file)
@@ -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';
index 626c65f552f3c2e120eed33bd0d688b00b97dd02..e45496fbd1ae3436efaac290590cc0369637a2ea 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 {
-  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'];
 
index 171e04121f2eb4fbe694150514bccd85a1b0dc84..78256f280783d48397ebf928c320554e627eb1bd 100644 (file)
  * 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;
 }