]> source.dussan.org Git - sonarqube.git/commitdiff
rewrite some components in ts (#243)
authorStas Vilchik <stas.vilchik@sonarsource.com>
Tue, 15 May 2018 15:10:00 +0000 (17:10 +0200)
committerSonarTech <sonartech@sonarsource.com>
Tue, 15 May 2018 18:20:50 +0000 (20:20 +0200)
82 files changed:
server/sonar-web/package.json
server/sonar-web/src/main/js/@types/json.d.ts [new file with mode: 0644]
server/sonar-web/src/main/js/app/components/search/Search.js
server/sonar-web/src/main/js/app/components/search/SearchResult.js
server/sonar-web/src/main/js/apps/code/components/ComponentPin.tsx
server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.js
server/sonar-web/src/main/js/apps/issues/sidebar/CreationDateFacet.tsx
server/sonar-web/src/main/js/apps/overview/components/Timeline.tsx
server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/Timeline-test.js.snap
server/sonar-web/src/main/js/apps/overview/meta/MetaLink.tsx
server/sonar-web/src/main/js/apps/project-admin/links/LinkRow.tsx
server/sonar-web/src/main/js/apps/quality-profiles/components/App.tsx
server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.tsx
server/sonar-web/src/main/js/components/charts/BarChart.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/charts/BubbleChart.tsx
server/sonar-web/src/main/js/components/charts/ColorBoxLegend.js [deleted file]
server/sonar-web/src/main/js/components/charts/ColorBoxLegend.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/charts/ColorGradientLegend.js [deleted file]
server/sonar-web/src/main/js/components/charts/ColorGradientLegend.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/charts/ColorRatingsLegend.js [deleted file]
server/sonar-web/src/main/js/components/charts/ColorRatingsLegend.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/charts/DonutChart.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/charts/LineChart.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/charts/TreeMap.js [deleted file]
server/sonar-web/src/main/js/components/charts/TreeMap.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/charts/TreeMapRect.js [deleted file]
server/sonar-web/src/main/js/components/charts/TreeMapRect.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/charts/__tests__/BarChart-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/charts/__tests__/BubbleChart-test.js [deleted file]
server/sonar-web/src/main/js/components/charts/__tests__/BubbleChart-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/charts/__tests__/LineChart-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/charts/__tests__/TreeMap-test.js [deleted file]
server/sonar-web/src/main/js/components/charts/__tests__/TreeMap-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/BubbleChart-test.js.snap [deleted file]
server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/BubbleChart-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/charts/__tests__/bar-chart-test.js [deleted file]
server/sonar-web/src/main/js/components/charts/__tests__/line-chart-test.js [deleted file]
server/sonar-web/src/main/js/components/charts/bar-chart.js [deleted file]
server/sonar-web/src/main/js/components/charts/donut-chart.js [deleted file]
server/sonar-web/src/main/js/components/charts/line-chart.js [deleted file]
server/sonar-web/src/main/js/components/common/ClockIcon.js [deleted file]
server/sonar-web/src/main/js/components/common/LocationIndex.js [deleted file]
server/sonar-web/src/main/js/components/common/LocationIndex.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/common/LocationMessage.js [deleted file]
server/sonar-web/src/main/js/components/common/LocationMessage.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/common/OrganizationHelmet.js [deleted file]
server/sonar-web/src/main/js/components/common/OrganizationHelmet.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/common/UpgradeOrganizationBox.js [deleted file]
server/sonar-web/src/main/js/components/common/UpgradeOrganizationBox.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/controls/GlobalMessages.js [deleted file]
server/sonar-web/src/main/js/components/controls/GlobalMessages.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/controls/Toggle.js [deleted file]
server/sonar-web/src/main/js/components/controls/Toggle.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.js [deleted file]
server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/controls/__tests__/Toggle-test.js [deleted file]
server/sonar-web/src/main/js/components/controls/__tests__/Toggle-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/icons-components/BugTrackerIcon.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/icons-components/ClockIcon.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/icons-components/PinIcon.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTitleBar-test.js.snap
server/sonar-web/src/main/js/components/shared/SeverityIcon.js [deleted file]
server/sonar-web/src/main/js/components/shared/SeverityIcon.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/shared/StatusHelper.js [deleted file]
server/sonar-web/src/main/js/components/shared/StatusHelper.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/shared/StatusIcon.js [deleted file]
server/sonar-web/src/main/js/components/shared/StatusIcon.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/shared/WithStore.js [deleted file]
server/sonar-web/src/main/js/components/shared/__tests__/QualifierIcon-test.js [deleted file]
server/sonar-web/src/main/js/components/shared/__tests__/QualifierIcon-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/shared/__tests__/__snapshots__/QualifierIcon-test.js.snap [deleted file]
server/sonar-web/src/main/js/components/shared/__tests__/__snapshots__/QualifierIcon-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/shared/pin-icon.js [deleted file]
server/sonar-web/src/main/js/components/ui/BugTrackerIcon.js [deleted file]
server/sonar-web/src/main/js/components/ui/CoverageRating.tsx
server/sonar-web/src/main/js/components/ui/__tests__/Level-test.js [deleted file]
server/sonar-web/src/main/js/components/ui/__tests__/Level-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/ui/buttons.tsx
server/sonar-web/src/main/js/libs/third-party/VSS.SDK.min.js [deleted file]
server/sonar-web/src/main/js/typings/json.d.ts [deleted file]
server/sonar-web/src/main/js/typings/rc-tooltip.d.ts [deleted file]
server/sonar-web/yarn.lock

index aed2f5263af0a1799364293383833e67a0a7d3a4..283b02f948d59ac2c9da291c92d51d58221dab08 100644 (file)
@@ -42,7 +42,9 @@
     "@types/classnames": "2.2.3",
     "@types/clipboard": "2.0.0",
     "@types/d3-array": "1.2.1",
+    "@types/d3-hierarchy": "1.1.1",
     "@types/d3-scale": "2.0.0",
+    "@types/d3-shape": "1.2.2",
     "@types/enzyme": "3.1.10",
     "@types/jest": "22.2.3",
     "@types/keymaster": "1.6.28",
diff --git a/server/sonar-web/src/main/js/@types/json.d.ts b/server/sonar-web/src/main/js/@types/json.d.ts
new file mode 100644 (file)
index 0000000..0f858f5
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+declare module '*.json' {
+  const value: any;
+  export default value;
+}
index 327844759f8093d18f71b1851fe5126288a639a0..cd315cbfacf0f34c8d8c0b4c9d3cc3312fc75d58 100644 (file)
@@ -29,7 +29,7 @@ import { sortQualifiers } from './utils';
 /*:: import type { Component, More, Results } from './utils'; */
 import RecentHistory from '../../components/RecentHistory';
 import DeferredSpinner from '../../../components/common/DeferredSpinner';
-import ClockIcon from '../../../components/common/ClockIcon';
+import ClockIcon from '../../../components/icons-components/ClockIcon';
 import OutsideClickHandler from '../../../components/controls/OutsideClickHandler';
 import SearchBox from '../../../components/controls/SearchBox';
 import { getSuggestions } from '../../../api/components';
index c4bd954c9bb5091385d98a06d52ffc288bef8af2..02cc378ce09719c401a8b8640e99cf804a6a692d 100644 (file)
@@ -23,7 +23,7 @@ import { Link } from 'react-router';
 /*:: import type { Component } from './utils'; */
 import FavoriteIcon from '../../../components/icons-components/FavoriteIcon';
 import QualifierIcon from '../../../components/shared/QualifierIcon';
-import ClockIcon from '../../../components/common/ClockIcon';
+import ClockIcon from '../../../components/icons-components/ClockIcon';
 import Tooltip from '../../../components/controls/Tooltip';
 import { getProjectUrl } from '../../../helpers/urls';
 
index 3eadf905cbf1391df7cbfef72e2bf4625f64e76f..18d77d03f67bd2a019828e660bb640192d8a7f2d 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import * as PropTypes from 'prop-types';
 import { Component } from '../types';
 import { BranchLike } from '../../../app/types';
-import PinIcon from '../../../components/shared/pin-icon';
+import PinIcon from '../../../components/icons-components/PinIcon';
 import { WorkspaceContext } from '../../../components/workspace/context';
 import { translate } from '../../../helpers/l10n';
 
index 62d23b385b4a0f69f5a890beb539f34f8925a986..6a79f5e3d564c90eea37500590ce214710e8b399 100644 (file)
@@ -205,9 +205,9 @@ export default class TreeMapView extends React.PureComponent {
         <AutoSizer disableHeight={true}>
           {({ width }) => (
             <TreeMap
+              height={HEIGHT}
               items={treemapItems}
               onRectangleClick={this.props.handleSelect}
-              height={HEIGHT}
               width={width}
             />
           )}
index 5ba901f92e619c01e7963046d2d58903e1d5e07c..6096ada13a5b287f1b788f7b666110257d3d14d4 100644 (file)
@@ -28,7 +28,7 @@ import FacetItem from '../../../components/facet/FacetItem';
 import { longFormatterOption } from '../../../components/intl/DateFormatter';
 import DateFromNow from '../../../components/intl/DateFromNow';
 import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
-import { BarChart } from '../../../components/charts/bar-chart';
+import BarChart from '../../../components/charts/BarChart';
 import DateRangeInput from '../../../components/controls/DateRangeInput';
 import { isSameDay, parseDate } from '../../../helpers/dates';
 import { translate } from '../../../helpers/l10n';
index 1b2bd3d04ec7ac21100acd1b9a422a367927b8b3..2b372ce80ddf4ed16058979fad8d543ad940a00e 100644 (file)
@@ -19,7 +19,7 @@
  */
 import * as React from 'react';
 import { max } from 'd3-array';
-import { LineChart } from '../../../components/charts/line-chart';
+import LineChart from '../../../components/charts/LineChart';
 import { HistoryItem } from '../../../api/time-machine';
 
 const HEIGHT = 80;
@@ -49,17 +49,16 @@ export default class Timeline extends React.PureComponent<Props> {
     }
 
     const data = snapshots.map((snapshot, index) => {
-      return { x: index, y: snapshot.value };
+      return { x: index, y: Number(snapshot.value) };
     });
-    const domain = [0, max(this.props.history, d => parseFloat(d.value))];
+    const domain = [0, max(this.props.history, d => parseFloat(d.value))] as [number, number];
     return (
       <LineChart
         data={data}
-        domain={domain}
-        interpolate="basis"
         displayBackdrop={true}
         displayPoints={false}
         displayVerticalGrid={false}
+        domain={domain}
         height={HEIGHT}
         padding={[0, 0, 0, 0]}
       />
index c25e28b7e3f8d51f34c627652869d4e7c235c737..3c01c85264de7fa2456204c1703f80b78d175d82 100644 (file)
@@ -6,11 +6,11 @@ exports[`should render correctly with a "before" range 1`] = `
     Array [
       Object {
         "x": 0,
-        "y": "29.6",
+        "y": 29.6,
       },
       Object {
         "x": 1,
-        "y": "170.8",
+        "y": 170.8,
       },
     ]
   }
@@ -24,7 +24,6 @@ exports[`should render correctly with a "before" range 1`] = `
     ]
   }
   height={80}
-  interpolate="basis"
   padding={
     Array [
       0,
@@ -33,8 +32,6 @@ exports[`should render correctly with a "before" range 1`] = `
       0,
     ]
   }
-  xTicks={Array []}
-  xValues={Array []}
 />
 `;
 
@@ -44,11 +41,11 @@ exports[`should render correctly with an "after" range 1`] = `
     Array [
       Object {
         "x": 0,
-        "y": "360",
+        "y": 360,
       },
       Object {
         "x": 1,
-        "y": "39",
+        "y": 39,
       },
     ]
   }
@@ -62,7 +59,6 @@ exports[`should render correctly with an "after" range 1`] = `
     ]
   }
   height={80}
-  interpolate="basis"
   padding={
     Array [
       0,
@@ -71,7 +67,5 @@ exports[`should render correctly with an "after" range 1`] = `
       0,
     ]
   }
-  xTicks={Array []}
-  xValues={Array []}
 />
 `;
index 4996bae73c013785ab697313f12f262e892cf206..7a3caf2ab7a5bad164c9083e2fbac61b8716aa24 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import { isProvided, getLinkName } from '../../project-admin/links/utils';
 import { ProjectLink } from '../../../app/types';
 import DetachIcon from '../../../components/icons-components/DetachIcon';
-import BugTrackerIcon from '../../../components/ui/BugTrackerIcon';
+import BugTrackerIcon from '../../../components/icons-components/BugTrackerIcon';
 
 interface Props {
   link: ProjectLink;
index 0bd5d10cbd119f0fb5872f609002c57fa41863b9..a39c7cfe903f400f9c6ddb9b91ff94b737c66dc1 100644 (file)
@@ -22,7 +22,7 @@ import { isProvided, getLinkName } from './utils';
 import { ProjectLink } from '../../../app/types';
 import ConfirmButton from '../../../components/controls/ConfirmButton';
 import DetachIcon from '../../../components/icons-components/DetachIcon';
-import BugTrackerIcon from '../../../components/ui/BugTrackerIcon';
+import BugTrackerIcon from '../../../components/icons-components/BugTrackerIcon';
 import { Button } from '../../../components/ui/buttons';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 
index 3a7a834957f299f353a62c94465536f3cb6b6583..35fd9354bff4770dc8bad6a4e487bb5de0dfad6c 100644 (file)
@@ -31,7 +31,7 @@ interface Props {
   children: React.ReactElement<any>;
   languages: Languages;
   onRequestFail: (reasong: any) => void;
-  organization: { name: string; key: string } | null;
+  organization: { name: string; key: string } | undefined;
 }
 
 interface State {
@@ -113,8 +113,8 @@ export default class App extends React.PureComponent<Props, State> {
       <div className="page page-limited">
         <Suggestions suggestions="quality_profiles" />
         <OrganizationHelmet
-          title={translate('quality_profiles.page')}
           organization={this.props.organization}
+          title={translate('quality_profiles.page')}
         />
 
         {this.renderChild()}
index 1f1d6d2f4a5381442c0c8b825c0fdcb12ce65a5a..1da66735a8c76caa4d0dbc07a22d835ca9e8d23f 100644 (file)
@@ -26,7 +26,7 @@ import { Languages } from '../../../store/languages/reducer';
 
 interface StateProps {
   languages: Languages;
-  organization: { name: string; key: string } | null;
+  organization: { name: string; key: string } | undefined;
 }
 
 interface DispatchProps {
@@ -37,7 +37,7 @@ const mapStateToProps = (state: any, ownProps: any) => ({
   languages: getLanguages(state),
   organization: ownProps.params.organizationKey
     ? getOrganizationByKey(state, ownProps.params.organizationKey)
-    : null
+    : undefined
 });
 
 const mapDispatchToProps = (dispatch: any) => ({
diff --git a/server/sonar-web/src/main/js/components/charts/BarChart.tsx b/server/sonar-web/src/main/js/components/charts/BarChart.tsx
new file mode 100644 (file)
index 0000000..7739f1d
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * 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 { max } from 'd3-array';
+import { scaleLinear, scaleBand, ScaleLinear, ScaleBand } from 'd3-scale';
+import Tooltip from '../controls/Tooltip';
+
+interface DataPoint {
+  tooltip?: React.ReactNode;
+  x: number;
+  y: number;
+}
+
+interface Props<T> {
+  barsWidth: number;
+  data: Array<DataPoint & T>;
+  height: number;
+  onBarClick?: (point: DataPoint & T) => void;
+  padding?: [number, number, number, number];
+  width: number;
+  xTicks?: string[];
+  xValues?: string[];
+}
+
+export default class BarChart<T> extends React.PureComponent<Props<T>> {
+  handleClick = (point: DataPoint & T) => {
+    if (this.props.onBarClick) {
+      this.props.onBarClick(point);
+    }
+  };
+
+  renderXTicks = (xScale: ScaleBand<number>, yScale: ScaleLinear<number, number>) => {
+    const { data, xTicks = [] } = this.props;
+
+    if (!xTicks.length) {
+      return null;
+    }
+
+    const ticks = xTicks.map((tick, index) => {
+      const point = data[index];
+      const x = Math.round((xScale(point.x) as number) + xScale.bandwidth() / 2);
+      const y = yScale.range()[0];
+      const d = data[index];
+      const text = (
+        <text
+          className="bar-chart-tick"
+          dy="1.5em"
+          key={index}
+          onClick={() => this.handleClick(point)}
+          style={{ cursor: this.props.onBarClick ? 'pointer' : 'default' }}
+          x={x}
+          y={y}>
+          {tick}
+        </text>
+      );
+      return (
+        <Tooltip key={index} overlay={d.tooltip || undefined}>
+          {text}
+        </Tooltip>
+      );
+    });
+    return <g>{ticks}</g>;
+  };
+
+  renderXValues = (xScale: ScaleBand<number>, yScale: ScaleLinear<number, number>) => {
+    const { data, xValues = [] } = this.props;
+
+    if (!xValues.length) {
+      return null;
+    }
+
+    const ticks = xValues.map((value, index) => {
+      const point = data[index];
+      const x = Math.round((xScale(point.x) as number) + xScale.bandwidth() / 2);
+      const y = yScale(point.y);
+      const text = (
+        <text
+          className="bar-chart-tick"
+          dy="-1em"
+          key={index}
+          onClick={() => this.handleClick(point)}
+          style={{ cursor: this.props.onBarClick ? 'pointer' : 'default' }}
+          x={x}
+          y={y}>
+          {value}
+        </text>
+      );
+      return (
+        <Tooltip key={index} overlay={point.tooltip || undefined}>
+          {text}
+        </Tooltip>
+      );
+    });
+    return <g>{ticks}</g>;
+  };
+
+  renderBars = (xScale: ScaleBand<number>, yScale: ScaleLinear<number, number>) => {
+    const bars = this.props.data.map((point, index) => {
+      const x = Math.round(xScale(point.x) as number);
+      const maxY = yScale.range()[0];
+      const y = Math.round(yScale(point.y)) - /* minimum bar height */ 1;
+      const height = maxY - y;
+      const rect = (
+        <rect
+          className="bar-chart-bar"
+          height={height}
+          key={index}
+          onClick={() => this.handleClick(point)}
+          style={{ cursor: this.props.onBarClick ? 'pointer' : 'default' }}
+          width={this.props.barsWidth}
+          x={x}
+          y={y}
+        />
+      );
+      return (
+        <Tooltip key={index} overlay={point.tooltip || undefined}>
+          {rect}
+        </Tooltip>
+      );
+    });
+    return <g>{bars}</g>;
+  };
+
+  render() {
+    const { barsWidth, data, width, height, padding = [10, 10, 10, 10] } = this.props;
+
+    const availableWidth = width - padding[1] - padding[3];
+    const availableHeight = height - padding[0] - padding[2];
+
+    const innerPadding = (availableWidth - barsWidth * data.length) / (data.length - 1);
+    const relativeInnerPadding = innerPadding / (innerPadding + barsWidth);
+
+    const maxY = max(data, d => d.y) as number;
+    const xScale = scaleBand<number>()
+      .domain(data.map(d => d.x))
+      .range([0, availableWidth])
+      .paddingInner(relativeInnerPadding);
+    const yScale = scaleLinear()
+      .domain([0, maxY])
+      .range([availableHeight, 0]);
+
+    return (
+      <svg className="bar-chart" height={height} width={width}>
+        <g transform={`translate(${padding[3]}, ${padding[0]})`}>
+          {this.renderXTicks(xScale, yScale)}
+          {this.renderXValues(xScale, yScale)}
+          {this.renderBars(xScale, yScale)}
+        </g>
+      </svg>
+    );
+  }
+}
index 40be474bb72164fa43e6b6320d47cf982fa3640e..d280be689228e3f5ade9d568ecdc58d22a83f040 100644 (file)
@@ -86,12 +86,12 @@ interface Props {
   displayXTicks?: boolean;
   displayYGrid?: boolean;
   displayYTicks?: boolean;
-  formatXTick: (tick: number) => string;
-  formatYTick: (tick: number) => string;
+  formatXTick?: (tick: number) => string;
+  formatYTick?: (tick: number) => string;
   height: number;
   items: Item[];
   onBubbleClick?: (link?: string) => void;
-  padding: [number, number, number, number];
+  padding?: [number, number, number, number];
   sizeDomain?: [number, number];
   sizeRange?: [number, number];
   xDomain?: [number, number];
@@ -115,9 +115,6 @@ export default class BubbleChart extends React.Component<Props, State> {
     displayXTicks: true,
     displayYGrid: true,
     displayYTicks: true,
-    formatXTick: (d: number) => d,
-    formatYTick: (d: number) => d,
-    padding: [10, 10, 10, 10],
     sizeRange: [5, 45]
   };
 
@@ -141,6 +138,18 @@ export default class BubbleChart extends React.Component<Props, State> {
     document.removeEventListener('mousemove', this.updateZoomCenter);
   }
 
+  get formatXTick() {
+    return this.props.formatXTick || ((d: number) => String(d));
+  }
+
+  get formatYTick() {
+    return this.props.formatYTick || ((d: number) => String(d));
+  }
+
+  get padding() {
+    return this.props.padding || [10, 10, 10, 10];
+  }
+
   startMoving = (event: React.MouseEvent<SVGSVGElement>) => {
     if (this.node && this.state.zoom > 1) {
       const rect = this.node.getBoundingClientRect();
@@ -176,8 +185,8 @@ export default class BubbleChart extends React.Component<Props, State> {
       event.preventDefault();
 
       const rect = this.node.getBoundingClientRect();
-      const mouseX = event.clientX - rect.left - this.props.padding[1];
-      const mouseY = event.clientY - rect.top - this.props.padding[0];
+      const mouseX = event.clientX - rect.left - this.padding[1];
+      const mouseY = event.clientY - rect.top - this.padding[0];
 
       let delta = event.deltaY;
       if ((event as any).webkitDirectionInvertedFromDevice) {
@@ -308,7 +317,7 @@ export default class BubbleChart extends React.Component<Props, State> {
     const ticks = xTicks.map((tick, index) => {
       const x = xScale(tick);
       const y = yScale.range()[0];
-      const innerText = this.props.formatXTick(tick);
+      const innerText = this.formatXTick(tick);
       return (
         <text
           className="bubble-chart-tick"
@@ -332,7 +341,7 @@ export default class BubbleChart extends React.Component<Props, State> {
     const ticks = yTicks.map((tick, index) => {
       const x = xScale.range()[0];
       const y = yScale(tick);
-      const innerText = this.props.formatYTick(tick);
+      const innerText = this.formatYTick(tick);
       return (
         <text
           className="bubble-chart-tick bubble-chart-tick-y"
@@ -350,8 +359,8 @@ export default class BubbleChart extends React.Component<Props, State> {
   };
 
   renderChart = (width: number) => {
-    const availableWidth = width - this.props.padding[1] - this.props.padding[3];
-    const availableHeight = this.props.height - this.props.padding[0] - this.props.padding[2];
+    const availableWidth = width - this.padding[1] - this.padding[3];
+    const availableHeight = this.props.height - this.padding[0] - this.padding[2];
 
     const xScale = scaleLinear()
       .domain(this.props.xDomain || [0, max(this.props.items, d => d.x) || 0])
@@ -389,8 +398,8 @@ export default class BubbleChart extends React.Component<Props, State> {
       );
     });
 
-    const xTicks = this.getTicks(xScale, this.props.formatXTick);
-    const yTicks = this.getTicks(yScale, this.props.formatYTick);
+    const xTicks = this.getTicks(xScale, this.formatXTick);
+    const yTicks = this.getTicks(yScale, this.formatYTick);
 
     return (
       <svg
@@ -400,9 +409,9 @@ export default class BubbleChart extends React.Component<Props, State> {
         onWheel={this.onWheel}
         ref={node => (this.node = node)}
         width={width}>
-        <g transform={`translate(${this.props.padding[3]}, ${this.props.padding[0]})`}>
+        <g transform={`translate(${this.padding[3]}, ${this.padding[0]})`}>
           <svg
-            height={this.props.height - this.props.padding[0] - this.props.padding[2]}
+            height={this.props.height - this.padding[0] - this.padding[2]}
             style={{ overflow: 'hidden' }}
             width={width}>
             {this.renderXGrid(xTicks, xScale, yScale, centerXDelta)}
diff --git a/server/sonar-web/src/main/js/components/charts/ColorBoxLegend.js b/server/sonar-web/src/main/js/components/charts/ColorBoxLegend.js
deleted file mode 100644 (file)
index 6d21cca..0000000
+++ /dev/null
@@ -1,60 +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 classNames from 'classnames';
-import { formatMeasure } from '../../helpers/measures';
-
-/*:: type Props = {
-  className?: string,
-  colorScale: Object,
-  colorNA?: string,
-  metricType: string
-}; */
-
-export default function ColorBoxLegend(
-  { className, colorScale, colorNA, metricType } /*: Props */
-) {
-  const colorDomain = colorScale.domain();
-  const colorRange = colorScale.range();
-  return (
-    <div className={classNames('color-box-legend', className)}>
-      {colorDomain.map((value, idx) => (
-        <div key={value}>
-          <span className="color-box-legend-rect" style={{ borderColor: colorRange[idx] }}>
-            <span
-              className="color-box-legend-rect-inner"
-              style={{ backgroundColor: colorRange[idx] }}
-            />
-          </span>
-          {formatMeasure(value, metricType)}
-        </div>
-      ))}
-      {colorNA && (
-        <div>
-          <span className="color-box-legend-rect" style={{ borderColor: colorNA }}>
-            <span className="color-box-legend-rect-inner" style={{ backgroundColor: colorNA }} />
-          </span>
-          N/A
-        </div>
-      )}
-    </div>
-  );
-}
diff --git a/server/sonar-web/src/main/js/components/charts/ColorBoxLegend.tsx b/server/sonar-web/src/main/js/components/charts/ColorBoxLegend.tsx
new file mode 100644 (file)
index 0000000..cf2a140
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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 * as classNames from 'classnames';
+import { ScaleLinear, ScaleOrdinal } from 'd3-scale';
+import { formatMeasure } from '../../helpers/measures';
+
+interface Props {
+  className?: string;
+  colorNA?: string;
+  colorScale:
+    | ScaleOrdinal<string, string> // used for LEVEL type
+    | ScaleLinear<number, string | number>; // used for RATING or PERCENT type
+  metricType: string;
+}
+
+export default function ColorBoxLegend({ className, colorScale, colorNA, metricType }: Props) {
+  const colorDomain: Array<number | string> = colorScale.domain();
+  const colorRange = colorScale.range();
+  return (
+    <div className={classNames('color-box-legend', className)}>
+      {colorDomain.map((value, idx) => (
+        <div key={value}>
+          <span className="color-box-legend-rect" style={{ borderColor: colorRange[idx] }}>
+            <span
+              className="color-box-legend-rect-inner"
+              style={{ backgroundColor: colorRange[idx] }}
+            />
+          </span>
+          {formatMeasure(value, metricType)}
+        </div>
+      ))}
+      {colorNA && (
+        <div>
+          <span className="color-box-legend-rect" style={{ borderColor: colorNA }}>
+            <span className="color-box-legend-rect-inner" style={{ backgroundColor: colorNA }} />
+          </span>
+          N/A
+        </div>
+      )}
+    </div>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/charts/ColorGradientLegend.js b/server/sonar-web/src/main/js/components/charts/ColorGradientLegend.js
deleted file mode 100644 (file)
index 06ca596..0000000
+++ /dev/null
@@ -1,98 +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';
-
-/*:: type Props = {
-  className?: string,
-  colorScale: Object,
-  colorNA?: string,
-  direction?: number,
-  padding?: Array<number>,
-  height: number,
-  width: number
-}; */
-
-const NA_SPACING = 4;
-
-export default function ColorGradientLegend(
-  {
-    className,
-    colorScale,
-    colorNA,
-    direction,
-    padding = [12, 24, 0, 0],
-    height,
-    width
-  } /*: Props */
-) {
-  const colorRange = colorScale.range();
-  if (direction === 1) {
-    colorRange.reverse();
-  }
-
-  const colorDomain = colorScale.domain();
-  const lastColorIdx = colorRange.length - 1;
-  const lastDomainIdx = colorDomain.length - 1;
-  const widthNoPadding = width - padding[1];
-  const rectHeight = height - padding[0];
-  return (
-    <svg className={className} width={width} height={height}>
-      <defs>
-        <linearGradient id="gradient-legend">
-          {colorRange.map((color, idx) => (
-            <stop key={idx} offset={idx / lastColorIdx} stopColor={color} />
-          ))}
-        </linearGradient>
-      </defs>
-      <g transform={`translate(${padding[3]}, ${padding[0]})`}>
-        <rect fill="url(#gradient-legend)" x={0} y={0} height={rectHeight} width={widthNoPadding} />
-        {colorDomain.map((d, idx) => (
-          <text
-            className="gradient-legend-text"
-            key={idx}
-            x={widthNoPadding * (idx / lastDomainIdx)}
-            y={0}
-            dy="-2px">
-            {d}
-          </text>
-        ))}
-      </g>
-      {colorNA && (
-        <g transform={`translate(${widthNoPadding}, ${padding[0]})`}>
-          <rect
-            fill={colorNA}
-            x={NA_SPACING}
-            y={0}
-            height={rectHeight}
-            width={padding[1] - NA_SPACING}
-          />
-          <text
-            className="gradient-legend-na"
-            x={NA_SPACING + (padding[1] - NA_SPACING) / 2}
-            y={0}
-            dy="-2px">
-            N/A
-          </text>
-        </g>
-      )}
-    </svg>
-  );
-}
diff --git a/server/sonar-web/src/main/js/components/charts/ColorGradientLegend.tsx b/server/sonar-web/src/main/js/components/charts/ColorGradientLegend.tsx
new file mode 100644 (file)
index 0000000..5ea00b6
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * 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 { ScaleLinear, ScaleOrdinal } from 'd3-scale';
+
+interface Props {
+  className?: string;
+  colorNA?: string;
+  colorScale:
+    | ScaleOrdinal<string, string> // used for LEVEL type
+    | ScaleLinear<number, string | number>; // used for RATING or PERCENT type
+  direction?: number;
+  height: number;
+  padding?: [number, number, number, number];
+  width: number;
+}
+
+const NA_SPACING = 4;
+
+export default function ColorGradientLegend({
+  className,
+  colorScale,
+  colorNA,
+  direction,
+  padding = [12, 24, 0, 0],
+  height,
+  width
+}: Props) {
+  const colorRange: Array<string | number> = colorScale.range();
+  if (direction === 1) {
+    colorRange.reverse();
+  }
+
+  const colorDomain: Array<string | number> = colorScale.domain();
+  const lastColorIdx = colorRange.length - 1;
+  const lastDomainIdx = colorDomain.length - 1;
+  const widthNoPadding = width - padding[1];
+  const rectHeight = height - padding[0];
+  return (
+    <svg className={className} height={height} width={width}>
+      <defs>
+        <linearGradient id="gradient-legend">
+          {colorRange.map((color, idx) => (
+            <stop key={idx} offset={idx / lastColorIdx} stopColor={String(color)} />
+          ))}
+        </linearGradient>
+      </defs>
+      <g transform={`translate(${padding[3]}, ${padding[0]})`}>
+        <rect fill="url(#gradient-legend)" height={rectHeight} width={widthNoPadding} x={0} y={0} />
+        {colorDomain.map((d, idx) => (
+          <text
+            className="gradient-legend-text"
+            dy="-2px"
+            key={idx}
+            x={widthNoPadding * (idx / lastDomainIdx)}
+            y={0}>
+            {d}
+          </text>
+        ))}
+      </g>
+      {colorNA && (
+        <g transform={`translate(${widthNoPadding}, ${padding[0]})`}>
+          <rect
+            fill={colorNA}
+            height={rectHeight}
+            width={padding[1] - NA_SPACING}
+            x={NA_SPACING}
+            y={0}
+          />
+          <text
+            className="gradient-legend-na"
+            dy="-2px"
+            x={NA_SPACING + (padding[1] - NA_SPACING) / 2}
+            y={0}>
+            N/A
+          </text>
+        </g>
+      )}
+    </svg>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/charts/ColorRatingsLegend.js b/server/sonar-web/src/main/js/components/charts/ColorRatingsLegend.js
deleted file mode 100644 (file)
index 3d8a07b..0000000
+++ /dev/null
@@ -1,44 +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 classNames from 'classnames';
-import { formatMeasure } from '../../helpers/measures';
-import { RATING_COLORS } from '../../helpers/constants';
-
-export default function ColorRatingsLegend({ className } /*: { className?: string } */) {
-  return (
-    <div className={classNames('color-box-legend', className)}>
-      {[1, 2, 3, 4, 5].map(rating => (
-        <div key={rating}>
-          <span
-            className="color-box-legend-rect"
-            style={{ borderColor: RATING_COLORS[rating - 1] }}>
-            <span
-              className="color-box-legend-rect-inner"
-              style={{ backgroundColor: RATING_COLORS[rating - 1] }}
-            />
-          </span>
-          {formatMeasure(rating, 'RATING')}
-        </div>
-      ))}
-    </div>
-  );
-}
diff --git a/server/sonar-web/src/main/js/components/charts/ColorRatingsLegend.tsx b/server/sonar-web/src/main/js/components/charts/ColorRatingsLegend.tsx
new file mode 100644 (file)
index 0000000..80e44f7
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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 * as classNames from 'classnames';
+import { formatMeasure } from '../../helpers/measures';
+import { RATING_COLORS } from '../../helpers/constants';
+
+interface Props {
+  className?: string;
+}
+
+export default function ColorRatingsLegend({ className }: Props) {
+  return (
+    <div className={classNames('color-box-legend', className)}>
+      {[1, 2, 3, 4, 5].map(rating => (
+        <div key={rating}>
+          <span
+            className="color-box-legend-rect"
+            style={{ borderColor: RATING_COLORS[rating - 1] }}>
+            <span
+              className="color-box-legend-rect-inner"
+              style={{ backgroundColor: RATING_COLORS[rating - 1] }}
+            />
+          </span>
+          {formatMeasure(rating, 'RATING')}
+        </div>
+      ))}
+    </div>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/charts/DonutChart.tsx b/server/sonar-web/src/main/js/components/charts/DonutChart.tsx
new file mode 100644 (file)
index 0000000..4cc5d03
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * 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 { arc as d3Arc, pie as d3Pie, PieArcDatum } from 'd3-shape';
+
+interface DataPoint {
+  fill: string;
+  value: number;
+}
+
+interface Props {
+  data: DataPoint[];
+  height: number;
+  thickness: number;
+  padding?: [number, number, number, number];
+  width: number;
+}
+
+export default function DonutChart(props: Props) {
+  const { height, padding = [0, 0, 0, 0], width } = props;
+
+  const availableWidth = width - padding[1] - padding[3];
+  const availableHeight = height - padding[0] - padding[2];
+
+  const size = Math.min(availableWidth, availableHeight);
+  const radius = Math.floor(size / 2);
+
+  const pie = d3Pie<any, DataPoint>()
+    .sort(null)
+    .value(d => d.value);
+
+  const sectors = pie(props.data).map((d, i) => {
+    return (
+      <Sector
+        data={d}
+        fill={props.data[i].fill}
+        key={i}
+        radius={radius}
+        thickness={props.thickness}
+      />
+    );
+  });
+
+  return (
+    <svg className="donut-chart" height={height} width={width}>
+      <g transform={`translate(${padding[3]}, ${padding[0]})`}>
+        <g transform={`translate(${radius}, ${radius})`}>{sectors}</g>
+      </g>
+    </svg>
+  );
+}
+
+interface SectorProps {
+  data: PieArcDatum<DataPoint>;
+  fill: string;
+  radius: number;
+  thickness: number;
+}
+
+function Sector(props: SectorProps) {
+  const arc = d3Arc<any, PieArcDatum<DataPoint>>()
+    .outerRadius(props.radius)
+    .innerRadius(props.radius - props.thickness);
+  const d = arc(props.data) as string;
+  return <path d={d} style={{ fill: props.fill }} />;
+}
diff --git a/server/sonar-web/src/main/js/components/charts/LineChart.tsx b/server/sonar-web/src/main/js/components/charts/LineChart.tsx
new file mode 100644 (file)
index 0000000..2d8d5f1
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * 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 { extent, max } from 'd3-array';
+import { scaleLinear, ScaleLinear } from 'd3-scale';
+import { area as d3Area, line as d3Line, curveBasis } from 'd3-shape';
+import { AutoSizer } from 'react-virtualized/dist/commonjs/AutoSizer';
+
+interface DataPoint {
+  x: number;
+  y: number;
+}
+
+interface Props {
+  backdropConstraints?: [number, number];
+  data: DataPoint[];
+  displayBackdrop?: boolean;
+  displayPoints?: boolean;
+  displayVerticalGrid?: boolean;
+  domain?: [number, number];
+  height: number;
+  padding?: [number, number, number, number];
+  width?: number;
+  xTicks?: {}[];
+  xValues?: {}[];
+}
+
+export default class LineChart extends React.PureComponent<Props> {
+  renderBackdrop(xScale: ScaleLinear<number, number>, yScale: ScaleLinear<number, number>) {
+    const { displayBackdrop = true } = this.props;
+
+    if (!displayBackdrop) {
+      return null;
+    }
+
+    const area = d3Area<DataPoint>()
+      .x(d => xScale(d.x))
+      .y0(yScale.range()[0])
+      .y1(d => yScale(d.y))
+      .defined(d => d.y != null)
+      .curve(curveBasis);
+
+    let { data } = this.props;
+    if (this.props.backdropConstraints) {
+      const c = this.props.backdropConstraints;
+      data = data.filter(d => c[0] <= d.x && d.x <= c[1]);
+    }
+
+    return <path className="line-chart-backdrop" d={area(data) as string} />;
+  }
+
+  renderPoints(xScale: ScaleLinear<number, number>, yScale: ScaleLinear<number, number>) {
+    const { displayPoints = true } = this.props;
+
+    if (!displayPoints) {
+      return null;
+    }
+
+    const points = this.props.data.filter(point => point.y != null).map((point, index) => {
+      const x = xScale(point.x);
+      const y = yScale(point.y);
+      return <circle className="line-chart-point" cx={x} cy={y} key={index} r="3" />;
+    });
+    return <g>{points}</g>;
+  }
+
+  renderVerticalGrid(xScale: ScaleLinear<number, number>, yScale: ScaleLinear<number, number>) {
+    const { displayVerticalGrid = true } = this.props;
+
+    if (!displayVerticalGrid) {
+      return null;
+    }
+
+    const lines = this.props.data.map((point, index) => {
+      const x = xScale(point.x);
+      const y1 = yScale.range()[0];
+      const y2 = yScale(point.y);
+      return <line className="line-chart-grid" key={index} x1={x} x2={x} y1={y1} y2={y2} />;
+    });
+    return <g>{lines}</g>;
+  }
+
+  renderXTicks(xScale: ScaleLinear<number, number>, yScale: ScaleLinear<number, number>) {
+    const { xTicks = [] } = this.props;
+
+    if (!xTicks.length) {
+      return null;
+    }
+
+    const ticks = xTicks.map((tick, index) => {
+      const point = this.props.data[index];
+      const x = xScale(point.x);
+      const y = yScale.range()[0];
+      return (
+        <text className="line-chart-tick" dy="1.5em" key={index} x={x} y={y}>
+          {tick}
+        </text>
+      );
+    });
+    return <g>{ticks}</g>;
+  }
+
+  renderXValues(xScale: ScaleLinear<number, number>, yScale: ScaleLinear<number, number>) {
+    const { xValues = [] } = this.props;
+
+    if (!xValues.length) {
+      return null;
+    }
+
+    const ticks = xValues.map((value, index) => {
+      const point = this.props.data[index];
+      const x = xScale(point.x);
+      const y = yScale(point.y);
+      return (
+        <text className="line-chart-tick" dy="-1em" key={index} x={x} y={y}>
+          {value}
+        </text>
+      );
+    });
+    return <g>{ticks}</g>;
+  }
+
+  renderLine(xScale: ScaleLinear<number, number>, yScale: ScaleLinear<number, number>) {
+    const p = d3Line<DataPoint>()
+      .x(d => xScale(d.x))
+      .y(d => yScale(d.y))
+      .defined(d => d.y != null)
+      .curve(curveBasis);
+    return <path className="line-chart-path" d={p(this.props.data) as string} />;
+  }
+
+  renderChart = (width: number) => {
+    const { height, padding = [10, 10, 10, 10] } = this.props;
+
+    if (!width || !height) {
+      return <div />;
+    }
+
+    const availableWidth = width - padding[1] - padding[3];
+    const availableHeight = height - padding[0] - padding[2];
+
+    const xScale = scaleLinear()
+      .domain(extent(this.props.data, d => d.x) as [number, number])
+      .range([0, availableWidth]);
+    const yScale = scaleLinear().range([availableHeight, 0]);
+
+    if (this.props.domain) {
+      yScale.domain(this.props.domain);
+    } else {
+      const maxY = max(this.props.data, d => d.y) as number;
+      yScale.domain([0, maxY]);
+    }
+
+    return (
+      <svg className="line-chart" height={height} width={width}>
+        <g transform={`translate(${padding[3]}, ${padding[0]})`}>
+          {this.renderVerticalGrid(xScale, yScale)}
+          {this.renderBackdrop(xScale, yScale)}
+          {this.renderLine(xScale, yScale)}
+          {this.renderPoints(xScale, yScale)}
+          {this.renderXTicks(xScale, yScale)}
+          {this.renderXValues(xScale, yScale)}
+        </g>
+      </svg>
+    );
+  };
+
+  render() {
+    return this.props.width !== undefined ? (
+      this.renderChart(this.props.width)
+    ) : (
+      <AutoSizer disableHeight={true}>{size => this.renderChart(size.width)}</AutoSizer>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/components/charts/TreeMap.js b/server/sonar-web/src/main/js/components/charts/TreeMap.js
deleted file mode 100644 (file)
index 2b526fd..0000000
+++ /dev/null
@@ -1,115 +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 { treemap as d3Treemap, hierarchy as d3Hierarchy } from 'd3-hierarchy';
-import TreeMapRect from './TreeMapRect';
-import { translate } from '../../helpers/l10n';
-
-/*:: export type TreeMapItem = {
-  key: string,
-  size: number,
-  color: string,
-  icon?: React.Element<*>,
-  tooltip?: string | React.Element<*>,
-  label: string,
-  link?: string
-}; */
-
-/*:: type Props = {|
-  items: Array<TreeMapItem>,
-  onRectangleClick?: string => void,
-  height: number,
-  width: number
-|}; */
-
-export default class TreeMap extends React.PureComponent {
-  /*:: props: Props; */
-
-  mostCommitPrefix = (labels /*: Array<string> */) => {
-    const sortedLabels = labels.slice(0).sort();
-    const firstLabel = sortedLabels[0];
-    const firstLabelLength = firstLabel.length;
-    const lastLabel = sortedLabels[sortedLabels.length - 1];
-    let i = 0;
-    while (i < firstLabelLength && firstLabel.charAt(i) === lastLabel.charAt(i)) {
-      i++;
-    }
-    const prefix = firstLabel.substr(0, i);
-    const prefixTokens = prefix.split(/[\s\\/]/);
-    const lastPrefixPart = prefixTokens[prefixTokens.length - 1];
-    return prefix.substr(0, prefix.length - lastPrefixPart.length);
-  };
-
-  renderNoData() {
-    return (
-      <div className="sonar-d3">
-        <div
-          className="treemap-container"
-          style={{ width: this.props.width, height: this.props.height }}>
-          {translate('no_data')}
-        </div>
-      </div>
-    );
-  }
-
-  render() {
-    const { items, height, width } = this.props;
-    if (items.length <= 0) {
-      return this.renderNoData();
-    }
-
-    const hierarchy = d3Hierarchy({ children: items })
-      .sum(d => d.size)
-      .sort((a, b) => b.value - a.value);
-
-    const treemap = d3Treemap()
-      .round(true)
-      .size([width, height]);
-
-    const nodes = treemap(hierarchy).leaves();
-    const prefix = this.mostCommitPrefix(items.map(item => item.label));
-    const halfWidth = width / 2;
-    return (
-      <div className="sonar-d3">
-        <div className="treemap-container" style={{ width, height }}>
-          {nodes.map(node => (
-            <TreeMapRect
-              key={node.data.key}
-              x={node.x0}
-              y={node.y0}
-              width={node.x1 - node.x0}
-              height={node.y1 - node.y0}
-              fill={node.data.color}
-              label={node.data.label}
-              prefix={prefix}
-              itemKey={node.data.key}
-              icon={node.data.icon}
-              tooltip={node.data.tooltip}
-              link={node.data.link}
-              onClick={this.props.onRectangleClick}
-              placement={node.x0 === 0 || node.x1 < halfWidth ? 'right' : 'left'}
-            />
-          ))}
-        </div>
-      </div>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/components/charts/TreeMap.tsx b/server/sonar-web/src/main/js/components/charts/TreeMap.tsx
new file mode 100644 (file)
index 0000000..9ee5d23
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * 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 { treemap as d3Treemap, hierarchy as d3Hierarchy } from 'd3-hierarchy';
+import TreeMapRect from './TreeMapRect';
+import { translate } from '../../helpers/l10n';
+
+interface TreeMapItem {
+  color: string;
+  icon?: React.ReactNode;
+  key: string;
+  label: string;
+  link?: string;
+  size: number;
+  tooltip?: React.ReactNode;
+}
+
+interface HierarchicalTreemapItem extends TreeMapItem {
+  children?: TreeMapItem[];
+}
+
+interface Props {
+  height: number;
+  items: TreeMapItem[];
+  onRectangleClick?: (item: string) => void;
+  width: number;
+}
+
+export default class TreeMap extends React.PureComponent<Props> {
+  mostCommitPrefix = (labels: string[]) => {
+    const sortedLabels = labels.slice(0).sort();
+    const firstLabel = sortedLabels[0];
+    const firstLabelLength = firstLabel.length;
+    const lastLabel = sortedLabels[sortedLabels.length - 1];
+    let i = 0;
+    while (i < firstLabelLength && firstLabel.charAt(i) === lastLabel.charAt(i)) {
+      i++;
+    }
+    const prefix = firstLabel.substr(0, i);
+    const prefixTokens = prefix.split(/[\s\\/]/);
+    const lastPrefixPart = prefixTokens[prefixTokens.length - 1];
+    return prefix.substr(0, prefix.length - lastPrefixPart.length);
+  };
+
+  renderNoData() {
+    return (
+      <div className="sonar-d3">
+        <div
+          className="treemap-container"
+          style={{ width: this.props.width, height: this.props.height }}>
+          {translate('no_data')}
+        </div>
+      </div>
+    );
+  }
+
+  render() {
+    const { items, height, width } = this.props;
+    if (items.length <= 0) {
+      return this.renderNoData();
+    }
+
+    const hierarchy = d3Hierarchy({ children: items } as HierarchicalTreemapItem)
+      .sum(d => d.size)
+      .sort((a, b) => (b.value || 0) - (a.value || 0));
+
+    const treemap = d3Treemap<TreeMapItem>()
+      .round(true)
+      .size([width, height]);
+
+    const nodes = treemap(hierarchy).leaves();
+    const prefix = this.mostCommitPrefix(items.map(item => item.label));
+    const halfWidth = width / 2;
+    return (
+      <div className="sonar-d3">
+        <div className="treemap-container" style={{ width, height }}>
+          {nodes.map(node => (
+            <TreeMapRect
+              fill={node.data.color}
+              height={node.y1 - node.y0}
+              icon={node.data.icon}
+              itemKey={node.data.key}
+              key={node.data.key}
+              label={node.data.label}
+              link={node.data.link}
+              onClick={this.props.onRectangleClick}
+              placement={node.x0 === 0 || node.x1 < halfWidth ? 'right' : 'left'}
+              prefix={prefix}
+              tooltip={node.data.tooltip}
+              width={node.x1 - node.x0}
+              x={node.x0}
+              y={node.y0}
+            />
+          ))}
+        </div>
+      </div>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/components/charts/TreeMapRect.js b/server/sonar-web/src/main/js/components/charts/TreeMapRect.js
deleted file mode 100644 (file)
index 9cc3c2e..0000000
+++ /dev/null
@@ -1,121 +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 { Link } from 'react-router';
-import classNames from 'classnames';
-import { scaleLinear } from 'd3-scale';
-import LinkIcon from '../icons-components/LinkIcon';
-import Tooltip from '../controls/Tooltip';
-
-const SIZE_SCALE = scaleLinear()
-  .domain([3, 15])
-  .range([11, 18])
-  .clamp(true);
-
-/*:: type Props = {|
-  x: number,
-  y: number,
-  width: number,
-  height: number,
-  fill: string,
-  label: string,
-  prefix: string,
-  icon?: React.Element<*>,
-  tooltip?: string | React.Element<*>,
-  itemKey: string,
-  link?: string,
-  onClick?: string => void,
-  placement?: string
-|}; */
-
-export default class TreeMapRect extends React.PureComponent {
-  /*:: props: Props; */
-
-  handleLinkClick = (e /*: Event */) => e.stopPropagation();
-
-  handleRectClick = () => {
-    if (this.props.onClick != null) {
-      this.props.onClick(this.props.itemKey);
-    }
-  };
-
-  renderLink = () => {
-    const { link, height, width } = this.props;
-    const hasMinSize = width >= 24 && height >= 24 && (width >= 48 || height >= 50);
-    if (!hasMinSize || link == null) {
-      return null;
-    }
-    return (
-      <Link className="treemap-link" to={link} onClick={this.handleLinkClick}>
-        <LinkIcon />
-      </Link>
-    );
-  };
-
-  renderCell = () => {
-    const cellStyles = {
-      left: this.props.x,
-      top: this.props.y,
-      width: this.props.width,
-      height: this.props.height,
-      backgroundColor: this.props.fill,
-      fontSize: SIZE_SCALE(this.props.width / this.props.label.length),
-      lineHeight: `${this.props.height}px`,
-      cursor: this.props.onClick != null ? 'pointer' : 'default'
-    };
-    const isTextVisible = this.props.width >= 40 && this.props.height >= 45;
-    const isIconVisible = this.props.width >= 24 && this.props.height >= 26;
-
-    const label = this.props.prefix
-      ? `${this.props.prefix}<br>${this.props.label.substr(this.props.prefix.length)}`
-      : this.props.label;
-
-    return (
-      <div
-        className="treemap-cell"
-        style={cellStyles}
-        onClick={this.handleRectClick}
-        role="treeitem"
-        tabIndex={0}>
-        <div className="treemap-inner" style={{ maxWidth: this.props.width }}>
-          {isIconVisible && (
-            <span className={classNames('treemap-icon', { 'spacer-right': isTextVisible })}>
-              {this.props.icon}
-            </span>
-          )}
-          {isTextVisible && (
-            <span className="treemap-text" dangerouslySetInnerHTML={{ __html: label }} />
-          )}
-        </div>
-        {this.renderLink()}
-      </div>
-    );
-  };
-
-  render() {
-    const { placement, tooltip } = this.props;
-    return (
-      <Tooltip overlay={tooltip || undefined} placement={placement || 'left'}>
-        {this.renderCell()}
-      </Tooltip>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/components/charts/TreeMapRect.tsx b/server/sonar-web/src/main/js/components/charts/TreeMapRect.tsx
new file mode 100644 (file)
index 0000000..75e8cdc
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * 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 { Link } from 'react-router';
+import * as classNames from 'classnames';
+import { scaleLinear } from 'd3-scale';
+import LinkIcon from '../icons-components/LinkIcon';
+import Tooltip, { Placement } from '../controls/Tooltip';
+
+const SIZE_SCALE = scaleLinear()
+  .domain([3, 15])
+  .range([11, 18])
+  .clamp(true);
+
+interface Props {
+  fill: string;
+  height: number;
+  icon?: React.ReactNode;
+  itemKey: string;
+  label: string;
+  link?: string;
+  onClick?: (item: string) => void;
+  placement?: Placement;
+  prefix: string;
+  tooltip?: React.ReactNode;
+  width: number;
+  x: number;
+  y: number;
+}
+
+export default class TreeMapRect extends React.PureComponent<Props> {
+  handleLinkClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
+    event.stopPropagation();
+  };
+
+  handleRectClick = () => {
+    if (this.props.onClick) {
+      this.props.onClick(this.props.itemKey);
+    }
+  };
+
+  renderLink = () => {
+    const { link, height, width } = this.props;
+    const hasMinSize = width >= 24 && height >= 24 && (width >= 48 || height >= 50);
+    if (!hasMinSize || link == null) {
+      return null;
+    }
+    return (
+      <Link className="treemap-link" onClick={this.handleLinkClick} to={link}>
+        <LinkIcon />
+      </Link>
+    );
+  };
+
+  renderCell = () => {
+    const cellStyles = {
+      left: this.props.x,
+      top: this.props.y,
+      width: this.props.width,
+      height: this.props.height,
+      backgroundColor: this.props.fill,
+      fontSize: SIZE_SCALE(this.props.width / this.props.label.length),
+      lineHeight: `${this.props.height}px`,
+      cursor: this.props.onClick != null ? 'pointer' : 'default'
+    };
+    const isTextVisible = this.props.width >= 40 && this.props.height >= 45;
+    const isIconVisible = this.props.width >= 24 && this.props.height >= 26;
+
+    const label = this.props.prefix
+      ? `${this.props.prefix}<br>${this.props.label.substr(this.props.prefix.length)}`
+      : this.props.label;
+
+    return (
+      <div
+        className="treemap-cell"
+        onClick={this.handleRectClick}
+        role="treeitem"
+        style={cellStyles}
+        tabIndex={0}>
+        <div className="treemap-inner" style={{ maxWidth: this.props.width }}>
+          {isIconVisible && (
+            <span className={classNames('treemap-icon', { 'spacer-right': isTextVisible })}>
+              {this.props.icon}
+            </span>
+          )}
+          {isTextVisible && (
+            <span className="treemap-text" dangerouslySetInnerHTML={{ __html: label }} />
+          )}
+        </div>
+        {this.renderLink()}
+      </div>
+    );
+  };
+
+  render() {
+    const { placement, tooltip } = this.props;
+    return (
+      <Tooltip overlay={tooltip || undefined} placement={placement || 'left'}>
+        {this.renderCell()}
+      </Tooltip>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/BarChart-test.tsx b/server/sonar-web/src/main/js/components/charts/__tests__/BarChart-test.tsx
new file mode 100644 (file)
index 0000000..0b109c1
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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 { shallow } from 'enzyme';
+import BarChart from '../BarChart';
+
+it('should display bars', () => {
+  const data = [{ x: 1, y: 10 }, { x: 2, y: 30 }, { x: 3, y: 20 }];
+  const chart = shallow(<BarChart barsWidth={20} data={data} height={100} width={100} />);
+  expect(chart.find('.bar-chart-bar').length).toBe(3);
+});
+
+it('should display ticks', () => {
+  const data = [{ x: 1, y: 10 }, { x: 2, y: 30 }, { x: 3, y: 20 }];
+  const ticks = ['A', 'B', 'C'];
+  const chart = shallow(
+    <BarChart barsWidth={20} data={data} height={100} width={100} xTicks={ticks} />
+  );
+  expect(chart.find('.bar-chart-tick').length).toBe(3);
+});
+
+it('should display values', () => {
+  const data = [{ x: 1, y: 10 }, { x: 2, y: 30 }, { x: 3, y: 20 }];
+  const values = ['A', 'B', 'C'];
+  const chart = shallow(
+    <BarChart barsWidth={20} data={data} height={100} width={100} xValues={values} />
+  );
+  expect(chart.find('.bar-chart-tick').length).toBe(3);
+});
+
+it('should display bars, ticks and values', () => {
+  const data = [{ x: 1, y: 10 }, { x: 2, y: 30 }, { x: 3, y: 20 }];
+  const ticks = ['A', 'B', 'C'];
+  const values = ['A', 'B', 'C'];
+  const chart = shallow(
+    <BarChart barsWidth={20} data={data} height={100} width={100} xTicks={ticks} xValues={values} />
+  );
+  expect(chart.find('.bar-chart-bar').length).toBe(3);
+  expect(chart.find('.bar-chart-tick').length).toBe(6);
+});
diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/BubbleChart-test.js b/server/sonar-web/src/main/js/components/charts/__tests__/BubbleChart-test.js
deleted file mode 100644 (file)
index 347d1df..0000000
+++ /dev/null
@@ -1,41 +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.
- */
-import React from 'react';
-import { mount } from 'enzyme';
-import BubbleChart, { Bubble } from '../BubbleChart';
-
-it('should display bubbles', () => {
-  const items = [{ x: 1, y: 10, size: 7 }, { x: 2, y: 30, size: 5 }];
-  const chart = mount(<BubbleChart items={items} height={100} />);
-  chart.find(Bubble).forEach(bubble => expect(bubble).toMatchSnapshot());
-});
-
-it('should render bubble links', () => {
-  const items = [{ x: 1, y: 10, size: 7, link: 'foo' }, { x: 2, y: 30, size: 5, link: 'bar' }];
-  const chart = mount(<BubbleChart items={items} height={100} />);
-  chart.find(Bubble).forEach(bubble => expect(bubble).toMatchSnapshot());
-});
-
-it('should render bubbles with click handlers', () => {
-  const onClick = jest.fn();
-  const items = [{ x: 1, y: 10, size: 7, link: 'foo' }, { x: 2, y: 30, size: 5, link: 'bar' }];
-  const chart = mount(<BubbleChart items={items} height={100} onBubbleClick={onClick} />);
-  chart.find(Bubble).forEach(bubble => expect(bubble).toMatchSnapshot());
-});
diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/BubbleChart-test.tsx b/server/sonar-web/src/main/js/components/charts/__tests__/BubbleChart-test.tsx
new file mode 100644 (file)
index 0000000..07c73da
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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 { mount } from 'enzyme';
+import BubbleChart, { Bubble } from '../BubbleChart';
+
+it('should display bubbles', () => {
+  const items = [{ x: 1, y: 10, size: 7 }, { x: 2, y: 30, size: 5 }];
+  const chart = mount(<BubbleChart height={100} items={items} />);
+  chart.find(Bubble).forEach(bubble => expect(bubble).toMatchSnapshot());
+});
+
+it('should render bubble links', () => {
+  const items = [{ x: 1, y: 10, size: 7, link: 'foo' }, { x: 2, y: 30, size: 5, link: 'bar' }];
+  const chart = mount(<BubbleChart height={100} items={items} />);
+  chart.find(Bubble).forEach(bubble => expect(bubble).toMatchSnapshot());
+});
+
+it('should render bubbles with click handlers', () => {
+  const onClick = jest.fn();
+  const items = [{ x: 1, y: 10, size: 7, link: 'foo' }, { x: 2, y: 30, size: 5, link: 'bar' }];
+  const chart = mount(<BubbleChart height={100} items={items} onBubbleClick={onClick} />);
+  chart.find(Bubble).forEach(bubble => expect(bubble).toMatchSnapshot());
+});
diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/LineChart-test.tsx b/server/sonar-web/src/main/js/components/charts/__tests__/LineChart-test.tsx
new file mode 100644 (file)
index 0000000..533bd5a
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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 { shallow } from 'enzyme';
+import LineChart from '../LineChart';
+
+it('should display line', () => {
+  const data = [{ x: 1, y: 10 }, { x: 2, y: 30 }, { x: 3, y: 20 }];
+  const chart = shallow(<LineChart data={data} height={100} width={100} />);
+  expect(chart.find('.line-chart-path').length).toBe(1);
+});
+
+it('should display ticks', () => {
+  const data = [{ x: 1, y: 10 }, { x: 2, y: 30 }, { x: 3, y: 20 }];
+  const ticks = ['A', 'B', 'C'];
+  const chart = shallow(<LineChart data={data} height={100} width={100} xTicks={ticks} />);
+  expect(chart.find('.line-chart-tick').length).toBe(3);
+});
+
+it('should display values', () => {
+  const data = [{ x: 1, y: 10 }, { x: 2, y: 30 }, { x: 3, y: 20 }];
+  const values = ['A', 'B', 'C'];
+  const chart = shallow(<LineChart data={data} height={100} width={100} xValues={values} />);
+  expect(chart.find('.line-chart-tick').length).toBe(3);
+});
diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/TreeMap-test.js b/server/sonar-web/src/main/js/components/charts/__tests__/TreeMap-test.js
deleted file mode 100644 (file)
index 246f3a8..0000000
+++ /dev/null
@@ -1,34 +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.
- */
-import React from 'react';
-import { shallow } from 'enzyme';
-import TreeMap from '../TreeMap';
-
-it('should display', () => {
-  const items = [
-    { key: '1', size: 10, color: '#777', label: 'SonarQube :: Server' },
-    { key: '2', size: 30, color: '#777', label: 'SonarQube :: Web' },
-    { key: '3', size: 20, color: '#777', label: 'SonarQube :: Search' }
-  ];
-  const chart = shallow(
-    <TreeMap items={items} width={100} height={100} onRectangleClick={() => {}} />
-  );
-  expect(chart.find('TreeMapRect')).toHaveLength(3);
-});
diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/TreeMap-test.tsx b/server/sonar-web/src/main/js/components/charts/__tests__/TreeMap-test.tsx
new file mode 100644 (file)
index 0000000..96fbe93
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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 { shallow } from 'enzyme';
+import TreeMap from '../TreeMap';
+
+it('should display', () => {
+  const items = [
+    { key: '1', size: 10, color: '#777', label: 'SonarQube :: Server' },
+    { key: '2', size: 30, color: '#777', label: 'SonarQube :: Web' },
+    { key: '3', size: 20, color: '#777', label: 'SonarQube :: Search' }
+  ];
+  const chart = shallow(
+    <TreeMap height={100} items={items} onRectangleClick={() => {}} width={100} />
+  );
+  expect(chart.find('TreeMapRect')).toHaveLength(3);
+});
diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/BubbleChart-test.js.snap b/server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/BubbleChart-test.js.snap
deleted file mode 100644 (file)
index fbbd766..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should display bubbles 1`] = `
-<Bubble
-  key="0"
-  r={45}
-  x={-10}
-  y={52.3015873015873}
->
-  <Tooltip>
-    <g>
-      <circle
-        className="bubble-chart-bubble"
-        r={45}
-        style={
-          Object {
-            "fill": undefined,
-            "stroke": undefined,
-          }
-        }
-        transform="translate(-10, 52.3015873015873)"
-      />
-    </g>
-  </Tooltip>
-</Bubble>
-`;
-
-exports[`should display bubbles 2`] = `
-<Bubble
-  key="1"
-  r={33.57142857142857}
-  x={-75}
-  y={33.57142857142857}
->
-  <Tooltip>
-    <g>
-      <circle
-        className="bubble-chart-bubble"
-        r={33.57142857142857}
-        style={
-          Object {
-            "fill": undefined,
-            "stroke": undefined,
-          }
-        }
-        transform="translate(-75, 33.57142857142857)"
-      />
-    </g>
-  </Tooltip>
-</Bubble>
-`;
-
-exports[`should render bubble links 1`] = `
-<Bubble
-  key="0"
-  link="foo"
-  r={45}
-  x={-10}
-  y={52.3015873015873}
->
-  <Tooltip>
-    <g>
-      <Link
-        onlyActiveOnIndex={false}
-        style={Object {}}
-        to="foo"
-      >
-        <a
-          onClick={[Function]}
-          style={Object {}}
-        >
-          <circle
-            className="bubble-chart-bubble"
-            r={45}
-            style={
-              Object {
-                "fill": undefined,
-                "stroke": undefined,
-              }
-            }
-            transform="translate(-10, 52.3015873015873)"
-          />
-        </a>
-      </Link>
-    </g>
-  </Tooltip>
-</Bubble>
-`;
-
-exports[`should render bubble links 2`] = `
-<Bubble
-  key="1"
-  link="bar"
-  r={33.57142857142857}
-  x={-75}
-  y={33.57142857142857}
->
-  <Tooltip>
-    <g>
-      <Link
-        onlyActiveOnIndex={false}
-        style={Object {}}
-        to="bar"
-      >
-        <a
-          onClick={[Function]}
-          style={Object {}}
-        >
-          <circle
-            className="bubble-chart-bubble"
-            r={33.57142857142857}
-            style={
-              Object {
-                "fill": undefined,
-                "stroke": undefined,
-              }
-            }
-            transform="translate(-75, 33.57142857142857)"
-          />
-        </a>
-      </Link>
-    </g>
-  </Tooltip>
-</Bubble>
-`;
-
-exports[`should render bubbles with click handlers 1`] = `
-<Bubble
-  key="0"
-  link="foo"
-  onClick={[MockFunction]}
-  r={45}
-  x={-10}
-  y={52.3015873015873}
->
-  <Tooltip>
-    <g>
-      <circle
-        className="bubble-chart-bubble"
-        onClick={[Function]}
-        r={45}
-        style={
-          Object {
-            "fill": undefined,
-            "stroke": undefined,
-          }
-        }
-        transform="translate(-10, 52.3015873015873)"
-      />
-    </g>
-  </Tooltip>
-</Bubble>
-`;
-
-exports[`should render bubbles with click handlers 2`] = `
-<Bubble
-  key="1"
-  link="bar"
-  onClick={[MockFunction]}
-  r={33.57142857142857}
-  x={-75}
-  y={33.57142857142857}
->
-  <Tooltip>
-    <g>
-      <circle
-        className="bubble-chart-bubble"
-        onClick={[Function]}
-        r={33.57142857142857}
-        style={
-          Object {
-            "fill": undefined,
-            "stroke": undefined,
-          }
-        }
-        transform="translate(-75, 33.57142857142857)"
-      />
-    </g>
-  </Tooltip>
-</Bubble>
-`;
diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/BubbleChart-test.tsx.snap b/server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/BubbleChart-test.tsx.snap
new file mode 100644 (file)
index 0000000..fbbd766
--- /dev/null
@@ -0,0 +1,181 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should display bubbles 1`] = `
+<Bubble
+  key="0"
+  r={45}
+  x={-10}
+  y={52.3015873015873}
+>
+  <Tooltip>
+    <g>
+      <circle
+        className="bubble-chart-bubble"
+        r={45}
+        style={
+          Object {
+            "fill": undefined,
+            "stroke": undefined,
+          }
+        }
+        transform="translate(-10, 52.3015873015873)"
+      />
+    </g>
+  </Tooltip>
+</Bubble>
+`;
+
+exports[`should display bubbles 2`] = `
+<Bubble
+  key="1"
+  r={33.57142857142857}
+  x={-75}
+  y={33.57142857142857}
+>
+  <Tooltip>
+    <g>
+      <circle
+        className="bubble-chart-bubble"
+        r={33.57142857142857}
+        style={
+          Object {
+            "fill": undefined,
+            "stroke": undefined,
+          }
+        }
+        transform="translate(-75, 33.57142857142857)"
+      />
+    </g>
+  </Tooltip>
+</Bubble>
+`;
+
+exports[`should render bubble links 1`] = `
+<Bubble
+  key="0"
+  link="foo"
+  r={45}
+  x={-10}
+  y={52.3015873015873}
+>
+  <Tooltip>
+    <g>
+      <Link
+        onlyActiveOnIndex={false}
+        style={Object {}}
+        to="foo"
+      >
+        <a
+          onClick={[Function]}
+          style={Object {}}
+        >
+          <circle
+            className="bubble-chart-bubble"
+            r={45}
+            style={
+              Object {
+                "fill": undefined,
+                "stroke": undefined,
+              }
+            }
+            transform="translate(-10, 52.3015873015873)"
+          />
+        </a>
+      </Link>
+    </g>
+  </Tooltip>
+</Bubble>
+`;
+
+exports[`should render bubble links 2`] = `
+<Bubble
+  key="1"
+  link="bar"
+  r={33.57142857142857}
+  x={-75}
+  y={33.57142857142857}
+>
+  <Tooltip>
+    <g>
+      <Link
+        onlyActiveOnIndex={false}
+        style={Object {}}
+        to="bar"
+      >
+        <a
+          onClick={[Function]}
+          style={Object {}}
+        >
+          <circle
+            className="bubble-chart-bubble"
+            r={33.57142857142857}
+            style={
+              Object {
+                "fill": undefined,
+                "stroke": undefined,
+              }
+            }
+            transform="translate(-75, 33.57142857142857)"
+          />
+        </a>
+      </Link>
+    </g>
+  </Tooltip>
+</Bubble>
+`;
+
+exports[`should render bubbles with click handlers 1`] = `
+<Bubble
+  key="0"
+  link="foo"
+  onClick={[MockFunction]}
+  r={45}
+  x={-10}
+  y={52.3015873015873}
+>
+  <Tooltip>
+    <g>
+      <circle
+        className="bubble-chart-bubble"
+        onClick={[Function]}
+        r={45}
+        style={
+          Object {
+            "fill": undefined,
+            "stroke": undefined,
+          }
+        }
+        transform="translate(-10, 52.3015873015873)"
+      />
+    </g>
+  </Tooltip>
+</Bubble>
+`;
+
+exports[`should render bubbles with click handlers 2`] = `
+<Bubble
+  key="1"
+  link="bar"
+  onClick={[MockFunction]}
+  r={33.57142857142857}
+  x={-75}
+  y={33.57142857142857}
+>
+  <Tooltip>
+    <g>
+      <circle
+        className="bubble-chart-bubble"
+        onClick={[Function]}
+        r={33.57142857142857}
+        style={
+          Object {
+            "fill": undefined,
+            "stroke": undefined,
+          }
+        }
+        transform="translate(-75, 33.57142857142857)"
+      />
+    </g>
+  </Tooltip>
+</Bubble>
+`;
diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/bar-chart-test.js b/server/sonar-web/src/main/js/components/charts/__tests__/bar-chart-test.js
deleted file mode 100644 (file)
index 933ac88..0000000
+++ /dev/null
@@ -1,57 +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.
- */
-import React from 'react';
-import { shallow } from 'enzyme';
-import { BarChart } from '../bar-chart';
-
-it('should display bars', () => {
-  const data = [{ x: 1, y: 10 }, { x: 2, y: 30 }, { x: 3, y: 20 }];
-  const chart = shallow(<BarChart data={data} width={100} height={100} barsWidth={20} />);
-  expect(chart.find('.bar-chart-bar').length).toBe(3);
-});
-
-it('should display ticks', () => {
-  const data = [{ x: 1, y: 10 }, { x: 2, y: 30 }, { x: 3, y: 20 }];
-  const ticks = ['A', 'B', 'C'];
-  const chart = shallow(
-    <BarChart data={data} xTicks={ticks} width={100} height={100} barsWidth={20} />
-  );
-  expect(chart.find('.bar-chart-tick').length).toBe(3);
-});
-
-it('should display values', () => {
-  const data = [{ x: 1, y: 10 }, { x: 2, y: 30 }, { x: 3, y: 20 }];
-  const values = ['A', 'B', 'C'];
-  const chart = shallow(
-    <BarChart data={data} xValues={values} width={100} height={100} barsWidth={20} />
-  );
-  expect(chart.find('.bar-chart-tick').length).toBe(3);
-});
-
-it('should display bars, ticks and values', () => {
-  const data = [{ x: 1, y: 10 }, { x: 2, y: 30 }, { x: 3, y: 20 }];
-  const ticks = ['A', 'B', 'C'];
-  const values = ['A', 'B', 'C'];
-  const chart = shallow(
-    <BarChart data={data} xTicks={ticks} xValues={values} width={100} height={100} barsWidth={20} />
-  );
-  expect(chart.find('.bar-chart-bar').length).toBe(3);
-  expect(chart.find('.bar-chart-tick').length).toBe(6);
-});
diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/line-chart-test.js b/server/sonar-web/src/main/js/components/charts/__tests__/line-chart-test.js
deleted file mode 100644 (file)
index 9b2b001..0000000
+++ /dev/null
@@ -1,42 +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.
- */
-import React from 'react';
-import { shallow } from 'enzyme';
-import { LineChart } from '../line-chart';
-
-it('should display line', () => {
-  const data = [{ x: 1, y: 10 }, { x: 2, y: 30 }, { x: 3, y: 20 }];
-  const chart = shallow(<LineChart data={data} width={100} height={100} />);
-  expect(chart.find('.line-chart-path').length).toBe(1);
-});
-
-it('should display ticks', () => {
-  const data = [{ x: 1, y: 10 }, { x: 2, y: 30 }, { x: 3, y: 20 }];
-  const ticks = ['A', 'B', 'C'];
-  const chart = shallow(<LineChart data={data} xTicks={ticks} width={100} height={100} />);
-  expect(chart.find('.line-chart-tick').length).toBe(3);
-});
-
-it('should display values', () => {
-  const data = [{ x: 1, y: 10 }, { x: 2, y: 30 }, { x: 3, y: 20 }];
-  const values = ['A', 'B', 'C'];
-  const chart = shallow(<LineChart data={data} xValues={values} width={100} height={100} />);
-  expect(chart.find('.line-chart-tick').length).toBe(3);
-});
diff --git a/server/sonar-web/src/main/js/components/charts/bar-chart.js b/server/sonar-web/src/main/js/components/charts/bar-chart.js
deleted file mode 100644 (file)
index c137b2f..0000000
+++ /dev/null
@@ -1,187 +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.
- */
-import React from 'react';
-import createReactClass from 'create-react-class';
-import PropTypes from 'prop-types';
-import { max } from 'd3-array';
-import { scaleLinear, scaleBand } from 'd3-scale';
-import Tooltip from '../controls/Tooltip';
-import { ResizeMixin } from '../mixins/resize-mixin';
-
-export const BarChart = createReactClass({
-  displayName: 'BarChart',
-
-  propTypes: {
-    data: PropTypes.arrayOf(PropTypes.object).isRequired,
-    xTicks: PropTypes.arrayOf(PropTypes.any),
-    xValues: PropTypes.arrayOf(PropTypes.any),
-    height: PropTypes.number,
-    padding: PropTypes.arrayOf(PropTypes.number),
-    barsWidth: PropTypes.number.isRequired,
-    onBarClick: PropTypes.func
-  },
-
-  mixins: [ResizeMixin],
-
-  getDefaultProps() {
-    return {
-      xTicks: [],
-      xValues: [],
-      padding: [10, 10, 10, 10]
-    };
-  },
-
-  getInitialState() {
-    return { width: this.props.width, height: this.props.height };
-  },
-
-  componentDidUpdate(prevProps) {
-    if (this.props.width && prevProps.width !== this.props.width) {
-      this.setState({ width: this.props.width });
-    }
-    if (this.props.height && prevProps.height !== this.props.height) {
-      this.setState({ height: this.props.height });
-    }
-  },
-
-  handleClick(point) {
-    this.props.onBarClick(point);
-  },
-
-  renderXTicks(xScale, yScale) {
-    if (!this.props.xTicks.length) {
-      return null;
-    }
-    const ticks = this.props.xTicks.map((tick, index) => {
-      const point = this.props.data[index];
-      const x = Math.round(xScale(point.x) + xScale.bandwidth() / 2);
-      const y = yScale.range()[0];
-      const d = this.props.data[index];
-      const text = (
-        <text
-          className="bar-chart-tick"
-          dy="1.5em"
-          key={index}
-          onClick={this.props.onBarClick && this.handleClick.bind(this, point)}
-          style={{ cursor: this.props.onBarClick ? 'pointer' : 'default' }}
-          x={x}
-          y={y}>
-          {tick}
-        </text>
-      );
-      return (
-        <Tooltip key={index} overlay={d.tooltip || undefined}>
-          {text}
-        </Tooltip>
-      );
-    });
-    return <g>{ticks}</g>;
-  },
-
-  renderXValues(xScale, yScale) {
-    if (!this.props.xValues.length) {
-      return null;
-    }
-    const ticks = this.props.xValues.map((value, index) => {
-      const point = this.props.data[index];
-      const x = Math.round(xScale(point.x) + xScale.bandwidth() / 2);
-      const y = yScale(point.y);
-      const d = this.props.data[index];
-      const text = (
-        <text
-          className="bar-chart-tick"
-          dy="-1em"
-          key={index}
-          onClick={this.props.onBarClick && this.handleClick.bind(this, point)}
-          style={{ cursor: this.props.onBarClick ? 'pointer' : 'default' }}
-          x={x}
-          y={y}>
-          {value}
-        </text>
-      );
-      return (
-        <Tooltip key={index} overlay={d.tooltip || undefined}>
-          {text}
-        </Tooltip>
-      );
-    });
-    return <g>{ticks}</g>;
-  },
-
-  renderBars(xScale, yScale) {
-    const bars = this.props.data.map((d, index) => {
-      const x = Math.round(xScale(d.x));
-      const maxY = yScale.range()[0];
-      const y = Math.round(yScale(d.y)) - /* minimum bar height */ 1;
-      const height = maxY - y;
-      const rect = (
-        <rect
-          className="bar-chart-bar"
-          height={height}
-          key={index}
-          onClick={this.props.onBarClick && this.handleClick.bind(this, d)}
-          style={{ cursor: this.props.onBarClick ? 'pointer' : 'default' }}
-          width={this.props.barsWidth}
-          x={x}
-          y={y}
-        />
-      );
-      return (
-        <Tooltip key={index} overlay={d.tooltip || undefined}>
-          {rect}
-        </Tooltip>
-      );
-    });
-    return <g>{bars}</g>;
-  },
-
-  render() {
-    if (!this.state.width || !this.state.height) {
-      return <div />;
-    }
-
-    const availableWidth = this.state.width - this.props.padding[1] - this.props.padding[3];
-    const availableHeight = this.state.height - this.props.padding[0] - this.props.padding[2];
-
-    const innerPadding =
-      (availableWidth - this.props.barsWidth * this.props.data.length) /
-      (this.props.data.length - 1);
-    const relativeInnerPadding = innerPadding / (innerPadding + this.props.barsWidth);
-
-    const maxY = max(this.props.data, d => d.y);
-    const xScale = scaleBand()
-      .domain(this.props.data.map(d => d.x))
-      .range([0, availableWidth])
-      .paddingInner(relativeInnerPadding);
-    const yScale = scaleLinear()
-      .domain([0, maxY])
-      .range([availableHeight, 0]);
-
-    return (
-      <svg className="bar-chart" height={this.state.height} width={this.state.width}>
-        <g transform={`translate(${this.props.padding[3]}, ${this.props.padding[0]})`}>
-          {this.renderXTicks(xScale, yScale)}
-          {this.renderXValues(xScale, yScale)}
-          {this.renderBars(xScale, yScale)}
-        </g>
-      </svg>
-    );
-  }
-});
diff --git a/server/sonar-web/src/main/js/components/charts/donut-chart.js b/server/sonar-web/src/main/js/components/charts/donut-chart.js
deleted file mode 100644 (file)
index 08fd9b0..0000000
+++ /dev/null
@@ -1,84 +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.
- */
-import React from 'react';
-import createReactClass from 'create-react-class';
-import PropTypes from 'prop-types';
-import { arc as d3Arc, pie as d3Pie } from 'd3-shape';
-import { ResizeMixin } from './../mixins/resize-mixin';
-
-function Sector(props) {
-  const arc = d3Arc()
-    .outerRadius(props.radius)
-    .innerRadius(props.radius - props.thickness);
-  return <path d={arc(props.data)} style={{ fill: props.fill }} />;
-}
-
-export const DonutChart = createReactClass({
-  displayName: 'DonutChart',
-
-  propTypes: {
-    data: PropTypes.arrayOf(PropTypes.object).isRequired
-  },
-
-  mixins: [ResizeMixin],
-
-  getDefaultProps() {
-    return { thickness: 6, padding: [0, 0, 0, 0] };
-  },
-
-  getInitialState() {
-    return { width: this.props.width, height: this.props.height };
-  },
-
-  render() {
-    if (!this.state.width || !this.state.height) {
-      return <div />;
-    }
-
-    const availableWidth = this.state.width - this.props.padding[1] - this.props.padding[3];
-    const availableHeight = this.state.height - this.props.padding[0] - this.props.padding[2];
-
-    const size = Math.min(availableWidth, availableHeight);
-    const radius = Math.floor(size / 2);
-
-    const pie = d3Pie()
-      .sort(null)
-      .value(d => d.value);
-    const sectors = pie(this.props.data).map((d, i) => {
-      return (
-        <Sector
-          data={d}
-          fill={this.props.data[i].fill}
-          key={i}
-          radius={radius}
-          thickness={this.props.thickness}
-        />
-      );
-    });
-
-    return (
-      <svg className="donut-chart" height={this.state.height} width={this.state.width}>
-        <g transform={`translate(${this.props.padding[3]}, ${this.props.padding[0]})`}>
-          <g transform={`translate(${radius}, ${radius})`}>{sectors}</g>
-        </g>
-      </svg>
-    );
-  }
-});
diff --git a/server/sonar-web/src/main/js/components/charts/line-chart.js b/server/sonar-web/src/main/js/components/charts/line-chart.js
deleted file mode 100644 (file)
index af99622..0000000
+++ /dev/null
@@ -1,183 +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.
- */
-import React from 'react';
-import createReactClass from 'create-react-class';
-import PropTypes from 'prop-types';
-import { extent, max } from 'd3-array';
-import { scaleLinear } from 'd3-scale';
-import { area as d3Area, line as d3Line, curveBasis } from 'd3-shape';
-import { ResizeMixin } from './../mixins/resize-mixin';
-
-export const LineChart = createReactClass({
-  displayName: 'LineChart',
-
-  propTypes: {
-    data: PropTypes.arrayOf(PropTypes.object).isRequired,
-    xTicks: PropTypes.arrayOf(PropTypes.any),
-    xValues: PropTypes.arrayOf(PropTypes.any),
-    padding: PropTypes.arrayOf(PropTypes.number),
-    backdropConstraints: PropTypes.arrayOf(PropTypes.number),
-    displayBackdrop: PropTypes.bool,
-    displayPoints: PropTypes.bool,
-    displayVerticalGrid: PropTypes.bool,
-    height: PropTypes.number
-  },
-
-  mixins: [ResizeMixin],
-
-  getDefaultProps() {
-    return {
-      displayBackdrop: true,
-      displayPoints: true,
-      displayVerticalGrid: true,
-      xTicks: [],
-      xValues: [],
-      padding: [10, 10, 10, 10]
-    };
-  },
-
-  getInitialState() {
-    return { width: this.props.width, height: this.props.height };
-  },
-
-  renderBackdrop(xScale, yScale) {
-    if (!this.props.displayBackdrop) {
-      return null;
-    }
-
-    const area = d3Area()
-      .x(d => xScale(d.x))
-      .y0(yScale.range()[0])
-      .y1(d => yScale(d.y))
-      .defined(d => d.y != null)
-      .curve(curveBasis);
-
-    let data = this.props.data;
-    if (this.props.backdropConstraints) {
-      const c = this.props.backdropConstraints;
-      data = data.filter(d => c[0] <= d.x && d.x <= c[1]);
-    }
-    return <path className="line-chart-backdrop" d={area(data)} />;
-  },
-
-  renderPoints(xScale, yScale) {
-    if (!this.props.displayPoints) {
-      return null;
-    }
-    const points = this.props.data.filter(point => point.y != null).map((point, index) => {
-      const x = xScale(point.x);
-      const y = yScale(point.y);
-      return <circle key={index} className="line-chart-point" r="3" cx={x} cy={y} />;
-    });
-    return <g>{points}</g>;
-  },
-
-  renderVerticalGrid(xScale, yScale) {
-    if (!this.props.displayVerticalGrid) {
-      return null;
-    }
-    const lines = this.props.data.map((point, index) => {
-      const x = xScale(point.x);
-      const y1 = yScale.range()[0];
-      const y2 = yScale(point.y);
-      return <line key={index} className="line-chart-grid" x1={x} x2={x} y1={y1} y2={y2} />;
-    });
-    return <g>{lines}</g>;
-  },
-
-  renderXTicks(xScale, yScale) {
-    if (!this.props.xTicks.length) {
-      return null;
-    }
-    const ticks = this.props.xTicks.map((tick, index) => {
-      const point = this.props.data[index];
-      const x = xScale(point.x);
-      const y = yScale.range()[0];
-      return (
-        <text key={index} className="line-chart-tick" x={x} y={y} dy="1.5em">
-          {tick}
-        </text>
-      );
-    });
-    return <g>{ticks}</g>;
-  },
-
-  renderXValues(xScale, yScale) {
-    if (!this.props.xValues.length) {
-      return null;
-    }
-    const ticks = this.props.xValues.map((value, index) => {
-      const point = this.props.data[index];
-      const x = xScale(point.x);
-      const y = yScale(point.y);
-      return (
-        <text key={index} className="line-chart-tick" x={x} y={y} dy="-1em">
-          {value}
-        </text>
-      );
-    });
-    return <g>{ticks}</g>;
-  },
-
-  renderLine(xScale, yScale) {
-    const p = d3Line()
-      .x(d => xScale(d.x))
-      .y(d => yScale(d.y))
-      .defined(d => d.y != null)
-      .curve(curveBasis);
-    return <path className="line-chart-path" d={p(this.props.data)} />;
-  },
-
-  render() {
-    if (!this.state.width || !this.state.height) {
-      return <div />;
-    }
-
-    const availableWidth = this.state.width - this.props.padding[1] - this.props.padding[3];
-    const availableHeight = this.state.height - this.props.padding[0] - this.props.padding[2];
-
-    let maxY;
-    const xScale = scaleLinear()
-      .domain(extent(this.props.data, d => d.x))
-      .range([0, availableWidth]);
-    const yScale = scaleLinear().range([availableHeight, 0]);
-
-    if (this.props.domain) {
-      maxY = this.props.domain[1];
-      yScale.domain(this.props.domain);
-    } else {
-      maxY = max(this.props.data, d => d.y);
-      yScale.domain([0, maxY]);
-    }
-
-    return (
-      <svg className="line-chart" width={this.state.width} height={this.state.height}>
-        <g transform={`translate(${this.props.padding[3]}, ${this.props.padding[0]})`}>
-          {this.renderVerticalGrid(xScale, yScale, maxY)}
-          {this.renderBackdrop(xScale, yScale)}
-          {this.renderLine(xScale, yScale)}
-          {this.renderPoints(xScale, yScale)}
-          {this.renderXTicks(xScale, yScale)}
-          {this.renderXValues(xScale, yScale)}
-        </g>
-      </svg>
-    );
-  }
-});
diff --git a/server/sonar-web/src/main/js/components/common/ClockIcon.js b/server/sonar-web/src/main/js/components/common/ClockIcon.js
deleted file mode 100644 (file)
index e0e6961..0000000
+++ /dev/null
@@ -1,49 +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 classNames from 'classnames';
-
-/*::
-type Props = {
-  className?: string,
-  size?: number
-};
-*/
-
-export default function ClockIcon(props /*: Props */) {
-  /* eslint max-len: 0 */
-  return (
-    <svg
-      className={classNames('icon-clock', props.className)}
-      viewBox="0 0 16 16"
-      width={props.size}
-      height={props.size}>
-      <g fill="#fff" stroke="#ADADAD" transform="matrix(1.4 0 0 1.4 .3 .7)">
-        <circle cx="5.5" cy="5.2" r="5" />
-        <path fillRule="nonzero" d="M5.6 2.9v2.7l2-.5" />
-      </g>
-    </svg>
-  );
-}
-
-ClockIcon.defaultProps = {
-  size: 16
-};
diff --git a/server/sonar-web/src/main/js/components/common/LocationIndex.js b/server/sonar-web/src/main/js/components/common/LocationIndex.js
deleted file mode 100644 (file)
index f3ae1bf..0000000
+++ /dev/null
@@ -1,51 +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 classNames from 'classnames';
-import './LocationIndex.css';
-
-/*::
-type Props = {
-  children?: React.Element<*>,
-  leading?: boolean,
-  onClick?: () => void,
-  selected?: boolean
-};
-*/
-
-export default function LocationIndex(props /*: Props */) {
-  const { children, leading, onClick, selected, ...other } = props;
-  const clickAttributes = onClick ? { onClick, role: 'button', tabIndex: 0 } : {};
-
-  // put {...others} because Tooltip sets some event handlers
-  return (
-    <div
-      className={classNames('location-index', { 'is-leading': leading, selected })}
-      {...clickAttributes}
-      {...other}>
-      {children}
-    </div>
-  );
-}
-
-LocationIndex.defaultProps = {
-  selected: false
-};
diff --git a/server/sonar-web/src/main/js/components/common/LocationIndex.tsx b/server/sonar-web/src/main/js/components/common/LocationIndex.tsx
new file mode 100644 (file)
index 0000000..15b143e
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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 * as classNames from 'classnames';
+import './LocationIndex.css';
+
+interface Props {
+  children?: React.ReactNode;
+  leading?: boolean;
+  onClick?: () => void;
+  selected?: boolean;
+  [x: string]: any;
+}
+
+export default function LocationIndex(props: Props) {
+  const { children, leading, onClick, selected, ...other } = props;
+  const clickAttributes = onClick ? { onClick, role: 'button', tabIndex: 0 } : {};
+  // put {...others} because Tooltip sets some event handlers
+  return (
+    <div
+      className={classNames('location-index', {
+        'is-leading': leading,
+        selected
+      })}
+      {...clickAttributes}
+      {...other}>
+      {children}
+    </div>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/common/LocationMessage.js b/server/sonar-web/src/main/js/components/common/LocationMessage.js
deleted file mode 100644 (file)
index 491ac9e..0000000
+++ /dev/null
@@ -1,38 +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 classNames from 'classnames';
-import './LocationMessage.css';
-
-/*::
-type Props = {
-  children?: React.Element<*>,
-  selected: boolean
-};
-*/
-
-export default function LocationMessage(props /*: Props */) {
-  return (
-    <div className={classNames('location-message', { selected: props.selected })}>
-      {props.children}
-    </div>
-  );
-}
diff --git a/server/sonar-web/src/main/js/components/common/LocationMessage.tsx b/server/sonar-web/src/main/js/components/common/LocationMessage.tsx
new file mode 100644 (file)
index 0000000..bcee4be
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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 * as classNames from 'classnames';
+import './LocationMessage.css';
+
+interface Props {
+  children?: React.ReactNode;
+  selected: boolean;
+}
+
+export default function LocationMessage(props: Props) {
+  return (
+    <div className={classNames('location-message', { selected: props.selected })}>
+      {props.children}
+    </div>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/common/OrganizationHelmet.js b/server/sonar-web/src/main/js/components/common/OrganizationHelmet.js
deleted file mode 100644 (file)
index d4b5288..0000000
+++ /dev/null
@@ -1,34 +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 Helmet from 'react-helmet';
-
-/*::
-type Props = {
-  title: string,
-  organization?: ?{ name: string }
-};
-*/
-
-export default function OrganizationHelmet({ title, organization } /*: Props */) {
-  const defaultTitle = title + (organization ? ' - ' + organization.name : '');
-  return <Helmet defaultTitle={defaultTitle} titleTemplate={'%s - ' + defaultTitle} />;
-}
diff --git a/server/sonar-web/src/main/js/components/common/OrganizationHelmet.tsx b/server/sonar-web/src/main/js/components/common/OrganizationHelmet.tsx
new file mode 100644 (file)
index 0000000..e842eaa
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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 Helmet from 'react-helmet';
+
+interface Props {
+  organization?: { name: string };
+  title: string;
+}
+
+export default function OrganizationHelmet({ title, organization }: Props) {
+  const defaultTitle = title + (organization ? ' - ' + organization.name : '');
+  return <Helmet defaultTitle={defaultTitle} titleTemplate={'%s - ' + defaultTitle} />;
+}
diff --git a/server/sonar-web/src/main/js/components/common/UpgradeOrganizationBox.js b/server/sonar-web/src/main/js/components/common/UpgradeOrganizationBox.js
deleted file mode 100644 (file)
index b6934ce..0000000
+++ /dev/null
@@ -1,53 +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 { Link } from 'react-router';
-import { translate, hasMessage } from '../../helpers/l10n';
-import './UpgradeOrganizationBox.css';
-
-/*::
-type Props = {
-  organization: string
-};
-*/
-
-export default function UpgradeOrganizationBox(props /*: Props */) {
-  return (
-    <div className="boxed-group boxed-group-inner upgrade-organization-box">
-      <h3 className="spacer-bottom">{translate('billing.upgrade_box.header')}</h3>
-
-      <p>{translate('billing.upgrade_box.text')}</p>
-
-      {hasMessage('billing.upgrade_box.button') && (
-        <div className="big-spacer-top">
-          <Link
-            className="button"
-            to={{
-              pathname: `organizations/${props.organization}/extension/billing/billing`,
-              query: { page: 'upgrade' }
-            }}>
-            {translate('billing.upgrade_box.button')}
-          </Link>
-        </div>
-      )}
-    </div>
-  );
-}
diff --git a/server/sonar-web/src/main/js/components/common/UpgradeOrganizationBox.tsx b/server/sonar-web/src/main/js/components/common/UpgradeOrganizationBox.tsx
new file mode 100644 (file)
index 0000000..baeb9ad
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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 { Link } from 'react-router';
+import { translate, hasMessage } from '../../helpers/l10n';
+import './UpgradeOrganizationBox.css';
+
+interface Props {
+  organization: string;
+}
+
+export default function UpgradeOrganizationBox({ organization }: Props) {
+  return (
+    <div className="boxed-group boxed-group-inner upgrade-organization-box">
+      <h3 className="spacer-bottom">{translate('billing.upgrade_box.header')}</h3>
+
+      <p>{translate('billing.upgrade_box.text')}</p>
+
+      {hasMessage('billing.upgrade_box.button') && (
+        <div className="big-spacer-top">
+          <Link
+            className="button"
+            to={{
+              pathname: `organizations/${organization}/extension/billing/billing`,
+              query: { page: 'upgrade' }
+            }}>
+            {translate('billing.upgrade_box.button')}
+          </Link>
+        </div>
+      )}
+    </div>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/controls/GlobalMessages.js b/server/sonar-web/src/main/js/components/controls/GlobalMessages.js
deleted file mode 100644 (file)
index 0d07820..0000000
+++ /dev/null
@@ -1,64 +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.
- */
-import React from 'react';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
-import { ERROR, SUCCESS } from '../../store/globalMessages/duck';
-import { Button } from '../ui/buttons';
-
-export default class GlobalMessages extends React.PureComponent {
-  static propTypes = {
-    messages: PropTypes.arrayOf(
-      PropTypes.shape({
-        id: PropTypes.string.isRequired,
-        message: PropTypes.string.isRequired,
-        level: PropTypes.oneOf([ERROR, SUCCESS])
-      })
-    ),
-    closeGlobalMessage: PropTypes.func.isRequired
-  };
-
-  renderMessage = message => {
-    const className = classNames('process-spinner', 'shown', {
-      'process-spinner-failed': message.level === ERROR,
-      'process-spinner-success': message.level === SUCCESS
-    });
-    return (
-      <div className={className} key={message.id}>
-        {message.message}
-        <Button
-          className="process-spinner-close"
-          onClick={() => this.props.closeGlobalMessage(message.id)}>
-          <i className="icon-close" />
-        </Button>
-      </div>
-    );
-  };
-
-  render() {
-    const { messages } = this.props;
-
-    if (messages.length === 0) {
-      return null;
-    }
-
-    return <div className="processes-container">{messages.map(this.renderMessage)}</div>;
-  }
-}
diff --git a/server/sonar-web/src/main/js/components/controls/GlobalMessages.tsx b/server/sonar-web/src/main/js/components/controls/GlobalMessages.tsx
new file mode 100644 (file)
index 0000000..95ee65b
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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 * as classNames from 'classnames';
+import { Button } from '../ui/buttons';
+
+interface Message {
+  id: string;
+  level: 'ERROR' | 'SUCCESS';
+  message: string;
+}
+
+interface Props {
+  closeGlobalMessage: (id: string) => void;
+  messages: Message[];
+}
+
+export default class GlobalMessages extends React.PureComponent<Props> {
+  renderMessage = (message: Message) => {
+    const className = classNames('process-spinner', 'shown', {
+      'process-spinner-failed': message.level === 'ERROR',
+      'process-spinner-success': message.level === 'SUCCESS'
+    });
+    return (
+      <div className={className} key={message.id}>
+        {message.message}
+        <Button
+          className="process-spinner-close"
+          onClick={() => this.props.closeGlobalMessage(message.id)}>
+          <i className="icon-close" />
+        </Button>
+      </div>
+    );
+  };
+
+  render() {
+    const { messages } = this.props;
+
+    if (messages.length === 0) {
+      return null;
+    }
+
+    return <div className="processes-container">{messages.map(this.renderMessage)}</div>;
+  }
+}
diff --git a/server/sonar-web/src/main/js/components/controls/Toggle.js b/server/sonar-web/src/main/js/components/controls/Toggle.js
deleted file mode 100644 (file)
index 4cbabfe..0000000
+++ /dev/null
@@ -1,54 +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.
- */
-import React from 'react';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
-import { Button } from '../ui/buttons';
-import './styles.css';
-
-export default class Toggle extends React.PureComponent {
-  static propTypes = {
-    value: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]).isRequired,
-    name: PropTypes.string,
-    onChange: PropTypes.func
-  };
-
-  handleClick = value => {
-    if (this.props.onChange) {
-      this.props.onChange(!value);
-    }
-  };
-
-  render() {
-    const { value } = this.props;
-    const booleanValue = typeof value === 'string' ? value === 'true' : value;
-
-    const className = classNames('boolean-toggle', { 'boolean-toggle-on': booleanValue });
-
-    return (
-      <Button
-        className={className}
-        name={this.props.name}
-        onClick={() => this.handleClick(booleanValue)}>
-        <div className="boolean-toggle-handle" />
-      </Button>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/components/controls/Toggle.tsx b/server/sonar-web/src/main/js/components/controls/Toggle.tsx
new file mode 100644 (file)
index 0000000..b35d121
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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 * as classNames from 'classnames';
+import { Button } from '../ui/buttons';
+import './styles.css';
+
+export interface Props {
+  name?: string;
+  onChange?: (value: boolean) => void;
+  value: boolean | string;
+}
+
+export default class Toggle extends React.PureComponent<Props> {
+  getValue = () => {
+    const { value } = this.props;
+    return typeof value === 'string' ? value === 'true' : value;
+  };
+
+  handleClick = () => {
+    if (this.props.onChange) {
+      const value = this.getValue();
+      this.props.onChange(!value);
+    }
+  };
+
+  render() {
+    const value = this.getValue();
+    const className = classNames('boolean-toggle', { 'boolean-toggle-on': value });
+
+    return (
+      <Button className={className} name={this.props.name} onClick={this.handleClick}>
+        <div className="boolean-toggle-handle" />
+      </Button>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.js b/server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.js
deleted file mode 100644 (file)
index 5c2e745..0000000
+++ /dev/null
@@ -1,47 +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.
- */
-import { shallow } from 'enzyme';
-import React from 'react';
-import ListFooter from '../ListFooter';
-import { click } from '../../../helpers/testUtils';
-
-it('should render "3 of 5 shown"', () => {
-  const listFooter = shallow(<ListFooter count={3} total={5} />);
-  expect(listFooter.text()).toContain('x_of_y_shown.3.5');
-});
-
-it('should not render "show more"', () => {
-  const listFooter = shallow(<ListFooter count={3} total={5} />);
-  expect(listFooter.find('a').length).toBe(0);
-});
-
-it('should not render "show more"', () => {
-  const listFooter = shallow(<ListFooter count={5} total={5} loadMore={jest.fn()} />);
-  expect(listFooter.find('a').length).toBe(0);
-});
-
-it('should "show more"', () => {
-  const loadMore = jest.fn();
-  const listFooter = shallow(<ListFooter count={3} total={5} loadMore={loadMore} />);
-  const link = listFooter.find('a');
-  expect(link.length).toBe(1);
-  click(link);
-  expect(loadMore).toBeCalled();
-});
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.tsx b/server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.tsx
new file mode 100644 (file)
index 0000000..b8de25e
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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 { shallow } from 'enzyme';
+import ListFooter from '../ListFooter';
+import { click } from '../../../helpers/testUtils';
+
+it('should render "3 of 5 shown"', () => {
+  const listFooter = shallow(<ListFooter count={3} total={5} />);
+  expect(listFooter.text()).toContain('x_of_y_shown.3.5');
+});
+
+it('should not render "show more"', () => {
+  const listFooter = shallow(<ListFooter count={3} total={5} />);
+  expect(listFooter.find('a').length).toBe(0);
+});
+
+it('should not render "show more"', () => {
+  const listFooter = shallow(<ListFooter count={5} loadMore={jest.fn()} total={5} />);
+  expect(listFooter.find('a').length).toBe(0);
+});
+
+it('should "show more"', () => {
+  const loadMore = jest.fn();
+  const listFooter = shallow(<ListFooter count={3} loadMore={loadMore} total={5} />);
+  const link = listFooter.find('a');
+  expect(link.length).toBe(1);
+  click(link);
+  expect(loadMore).toBeCalled();
+});
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/Toggle-test.js b/server/sonar-web/src/main/js/components/controls/__tests__/Toggle-test.js
deleted file mode 100644 (file)
index 3245074..0000000
+++ /dev/null
@@ -1,39 +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.
- */
-import { shallow } from 'enzyme';
-import React from 'react';
-import Toggle from '../Toggle';
-import { click } from '../../../helpers/testUtils';
-
-function getSample(props) {
-  return <Toggle onChange={() => true} value={true} {...props} />;
-}
-
-it('should render', () => {
-  const Toggle = shallow(getSample());
-  expect(Toggle.is('Button')).toBe(true);
-});
-
-it('should call onChange', () => {
-  const onChange = jest.fn();
-  const Toggle = shallow(getSample({ onChange }));
-  click(Toggle);
-  expect(onChange).toBeCalledWith(false);
-});
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/Toggle-test.tsx b/server/sonar-web/src/main/js/components/controls/__tests__/Toggle-test.tsx
new file mode 100644 (file)
index 0000000..7d3e8ae
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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 { shallow } from 'enzyme';
+import Toggle, { Props } from '../Toggle';
+import { click } from '../../../helpers/testUtils';
+
+it('should render', () => {
+  const Toggle = shallowRender();
+  expect(Toggle.is('Button')).toBe(true);
+});
+
+it('should call onChange', () => {
+  const onChange = jest.fn();
+  const Toggle = shallowRender({ onChange });
+  click(Toggle);
+  expect(onChange).toBeCalledWith(false);
+});
+
+function shallowRender(props?: Partial<Props>) {
+  return shallow(<Toggle onChange={() => true} value={true} {...props} />);
+}
diff --git a/server/sonar-web/src/main/js/components/icons-components/BugTrackerIcon.tsx b/server/sonar-web/src/main/js/components/icons-components/BugTrackerIcon.tsx
new file mode 100644 (file)
index 0000000..f42e84a
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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 { IconProps } from './types';
+
+export default function BugTrackerIcon({ className, fill = 'currentColor', size = 16 }: IconProps) {
+  return (
+    <svg
+      className={className}
+      height={size}
+      version="1.1"
+      viewBox="0 0 16 16"
+      width={size}
+      xmlSpace="preserve"
+      xmlnsXlink="http://www.w3.org/1999/xlink">
+      <path
+        d="M13.5 9.5c1.003.033 1.466 1.952 0 2h-2.618L9.685 9.107 8 14.162 6.096 8.45l-.832 3.05-2.829-.002c-.984-.097-1.369-1.951.065-1.998h1.236l2.168-7.95L8 7.838l1.315-3.945L12.118 9.5H13.5z"
+        style={{ fill }}
+      />
+    </svg>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/icons-components/ClockIcon.tsx b/server/sonar-web/src/main/js/components/icons-components/ClockIcon.tsx
new file mode 100644 (file)
index 0000000..d5da0ec
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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 * as classNames from 'classnames';
+import { IconProps } from './types';
+
+export default function ClockIcon({ className, size = 16 }: IconProps) {
+  return (
+    <svg
+      className={classNames('icon-clock', className)}
+      height={size}
+      version="1.1"
+      viewBox="0 0 16 16"
+      width={size}
+      xmlSpace="preserve"
+      xmlnsXlink="http://www.w3.org/1999/xlink">
+      <g fill="#fff" stroke="#ADADAD" transform="matrix(1.4 0 0 1.4 .3 .7)">
+        <circle cx="5.5" cy="5.2" r="5" />
+        <path d="M5.6 2.9v2.7l2-.5" fillRule="nonzero" />
+      </g>
+    </svg>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/icons-components/PinIcon.tsx b/server/sonar-web/src/main/js/components/icons-components/PinIcon.tsx
new file mode 100644 (file)
index 0000000..3e83487
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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 { IconProps } from './types';
+
+export default function PinIcon({ className, fill = 'currentColor', size = 16 }: IconProps) {
+  return (
+    <svg
+      className={className}
+      height={size}
+      version="1.1"
+      viewBox="0 0 16 16"
+      width={size}
+      xmlSpace="preserve"
+      xmlnsXlink="http://www.w3.org/1999/xlink">
+      <path
+        d="M7.25 7.25v-3.5a.243.243 0 0 0-.07-.18A.243.243 0 0 0 7 3.5a.243.243 0 0 0-.18.07.243.243 0 0 0-.07.18v3.5c0 .073.023.133.07.18.047.047.107.07.18.07a.243.243 0 0 0 .18-.07.243.243 0 0 0 .07-.18zM12.5 10a.482.482 0 0 1-.148.352.482.482 0 0 1-.352.148H8.648l-.398 3.773a.29.29 0 0 1-.082.161.219.219 0 0 1-.16.066H8c-.141 0-.224-.07-.25-.211L7.156 10.5H4a.482.482 0 0 1-.352-.148A.482.482 0 0 1 3.5 10c0-.641.204-1.217.613-1.73.409-.513.871-.77 1.387-.77v-4a.96.96 0 0 1-.703-.297A.96.96 0 0 1 4.5 2.5a.96.96 0 0 1 .297-.703A.96.96 0 0 1 5.5 1.5h5a.96.96 0 0 1 .703.297.96.96 0 0 1 .297.703.96.96 0 0 1-.297.703.96.96 0 0 1-.703.297v4c.516 0 .978.257 1.387.77.409.513.613 1.089.613 1.73z"
+        style={{ fill }}
+      />;
+    </svg>
+  );
+}
index 8f3694dd8d0e2ba49fa6e72ed7da49d97b6312f5..93d4a3f72d2168b10affcec07ba503e110748dde 100644 (file)
@@ -1,9 +1,7 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`should count all code locations 1`] = `
-<LocationIndex
-  selected={false}
->
+<LocationIndex>
   7
 </LocationIndex>
 `;
diff --git a/server/sonar-web/src/main/js/components/shared/SeverityIcon.js b/server/sonar-web/src/main/js/components/shared/SeverityIcon.js
deleted file mode 100644 (file)
index 4a7575e..0000000
+++ /dev/null
@@ -1,30 +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 classNames from 'classnames';
-
-export default function SeverityIcon(props /*: { severity: ?string, className?: string } */) {
-  if (!props.severity) {
-    return null;
-  }
-  const className = classNames('icon-severity-' + props.severity.toLowerCase(), props.className);
-  return <i className={className} />;
-}
diff --git a/server/sonar-web/src/main/js/components/shared/SeverityIcon.tsx b/server/sonar-web/src/main/js/components/shared/SeverityIcon.tsx
new file mode 100644 (file)
index 0000000..38ee508
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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 * as classNames from 'classnames';
+
+interface Props {
+  className?: string;
+  severity: string | null | undefined;
+}
+
+export default function SeverityIcon(props: Props) {
+  if (!props.severity) {
+    return null;
+  }
+  return (
+    <i className={classNames('icon-severity-' + props.severity.toLowerCase(), props.className)} />
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/shared/StatusHelper.js b/server/sonar-web/src/main/js/components/shared/StatusHelper.js
deleted file mode 100644 (file)
index 2c25326..0000000
+++ /dev/null
@@ -1,41 +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 StatusIcon from './StatusIcon';
-import { translate } from '../../helpers/l10n';
-
-export default function StatusHelper(
-  props /*: {
-  resolution?: string,
-  status: string,
-  className?: string
-} */
-) {
-  const resolution =
-    props.resolution != null && ` (${translate('issue.resolution', props.resolution)})`;
-  return (
-    <span className={props.className}>
-      <StatusIcon className="little-spacer-right" status={props.status} />
-      {translate('issue.status', props.status)}
-      {resolution}
-    </span>
-  );
-}
diff --git a/server/sonar-web/src/main/js/components/shared/StatusHelper.tsx b/server/sonar-web/src/main/js/components/shared/StatusHelper.tsx
new file mode 100644 (file)
index 0000000..b6921ef
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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 StatusIcon from './StatusIcon';
+import { translate } from '../../helpers/l10n';
+
+interface Props {
+  className?: string;
+  resolution: string | undefined;
+  status: string;
+}
+
+export default function StatusHelper(props: Props) {
+  const resolution = props.resolution && ` (${translate('issue.resolution', props.resolution)})`;
+  return (
+    <span className={props.className}>
+      <StatusIcon className="little-spacer-right" status={props.status} />
+      {translate('issue.status', props.status)}
+      {resolution}
+    </span>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/shared/StatusIcon.js b/server/sonar-web/src/main/js/components/shared/StatusIcon.js
deleted file mode 100644 (file)
index ed8c418..0000000
+++ /dev/null
@@ -1,27 +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 classNames from 'classnames';
-
-export default function StatusIcon(props /*: { status: string, className?: string } */) {
-  const className = classNames('icon-status-' + props.status.toLowerCase(), props.className);
-  return <i className={className} />;
-}
diff --git a/server/sonar-web/src/main/js/components/shared/StatusIcon.tsx b/server/sonar-web/src/main/js/components/shared/StatusIcon.tsx
new file mode 100644 (file)
index 0000000..d043b03
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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 * as classNames from 'classnames';
+
+interface Props {
+  className?: string;
+  status: string;
+}
+
+export default function StatusIcon({ className, status }: Props) {
+  return <i className={classNames('icon-status-' + status.toLowerCase(), className)} />;
+}
diff --git a/server/sonar-web/src/main/js/components/shared/WithStore.js b/server/sonar-web/src/main/js/components/shared/WithStore.js
deleted file mode 100644 (file)
index 2cdf9bb..0000000
+++ /dev/null
@@ -1,52 +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 PropTypes from 'prop-types';
-import getStore from '../../app/utils/getStore';
-
-/*::
-type Props = {
-  children: React.Element<*>
-};
-*/
-
-export default class WithStore extends React.PureComponent {
-  /*:: props: Props; */
-  /*:: store: {};
-*/
-
-  static childContextTypes = {
-    store: PropTypes.object
-  };
-
-  constructor(props /*: Props */) {
-    super(props);
-    this.store = getStore();
-  }
-
-  getChildContext() {
-    return { store: this.store };
-  }
-
-  render() {
-    return this.props.children;
-  }
-}
diff --git a/server/sonar-web/src/main/js/components/shared/__tests__/QualifierIcon-test.js b/server/sonar-web/src/main/js/components/shared/__tests__/QualifierIcon-test.js
deleted file mode 100644 (file)
index 32097c9..0000000
+++ /dev/null
@@ -1,35 +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.
- */
-import React from 'react';
-import { shallow } from 'enzyme';
-import QualifierIcon from '../QualifierIcon';
-
-it('should render icon', () => {
-  expect(shallow(<QualifierIcon qualifier="TRK" />)).toMatchSnapshot();
-  expect(shallow(<QualifierIcon qualifier="trk" />)).toMatchSnapshot();
-});
-
-it('should not render icon', () => {
-  expect(shallow(<QualifierIcon qualifier={null} />).type()).toBeNull();
-});
-
-it('should render with custom class', () => {
-  expect(shallow(<QualifierIcon className="spacer-right" qualifier="TRK" />)).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/components/shared/__tests__/QualifierIcon-test.tsx b/server/sonar-web/src/main/js/components/shared/__tests__/QualifierIcon-test.tsx
new file mode 100644 (file)
index 0000000..0616927
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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 { shallow } from 'enzyme';
+import QualifierIcon from '../QualifierIcon';
+
+it('should render icon', () => {
+  expect(shallow(<QualifierIcon qualifier="TRK" />)).toMatchSnapshot();
+  expect(shallow(<QualifierIcon qualifier="trk" />)).toMatchSnapshot();
+});
+
+it('should not render icon', () => {
+  expect(shallow(<QualifierIcon qualifier={null} />).type()).toBeNull();
+});
+
+it('should render with custom class', () => {
+  expect(shallow(<QualifierIcon className="spacer-right" qualifier="TRK" />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/shared/__tests__/__snapshots__/QualifierIcon-test.js.snap b/server/sonar-web/src/main/js/components/shared/__tests__/__snapshots__/QualifierIcon-test.js.snap
deleted file mode 100644 (file)
index 3e1625b..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render icon 1`] = `
-<i
-  className="icon-qualifier-trk"
-/>
-`;
-
-exports[`should render icon 2`] = `
-<i
-  className="icon-qualifier-trk"
-/>
-`;
-
-exports[`should render with custom class 1`] = `
-<i
-  className="icon-qualifier-trk spacer-right"
-/>
-`;
diff --git a/server/sonar-web/src/main/js/components/shared/__tests__/__snapshots__/QualifierIcon-test.tsx.snap b/server/sonar-web/src/main/js/components/shared/__tests__/__snapshots__/QualifierIcon-test.tsx.snap
new file mode 100644 (file)
index 0000000..3e1625b
--- /dev/null
@@ -0,0 +1,19 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render icon 1`] = `
+<i
+  className="icon-qualifier-trk"
+/>
+`;
+
+exports[`should render icon 2`] = `
+<i
+  className="icon-qualifier-trk"
+/>
+`;
+
+exports[`should render with custom class 1`] = `
+<i
+  className="icon-qualifier-trk spacer-right"
+/>
+`;
diff --git a/server/sonar-web/src/main/js/components/shared/pin-icon.js b/server/sonar-web/src/main/js/components/shared/pin-icon.js
deleted file mode 100644 (file)
index 9435d00..0000000
+++ /dev/null
@@ -1,33 +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.
- */
-/* eslint max-len: 0 */
-import React from 'react';
-import * as theme from '../../app/theme';
-
-export default function PinIcon() {
-  return (
-    <svg width="9" height="14" viewBox="0 0 288 448">
-      <path
-        fill={theme.darkBlue}
-        d="M120 216v-112q0-3.5-2.25-5.75t-5.75-2.25-5.75 2.25-2.25 5.75v112q0 3.5 2.25 5.75t5.75 2.25 5.75-2.25 2.25-5.75zM288 304q0 6.5-4.75 11.25t-11.25 4.75h-107.25l-12.75 120.75q-0.5 3-2.625 5.125t-5.125 2.125h-0.25q-6.75 0-8-6.75l-19-121.25h-101q-6.5 0-11.25-4.75t-4.75-11.25q0-30.75 19.625-55.375t44.375-24.625v-128q-13 0-22.5-9.5t-9.5-22.5 9.5-22.5 22.5-9.5h160q13 0 22.5 9.5t9.5 22.5-9.5 22.5-22.5 9.5v128q24.75 0 44.375 24.625t19.625 55.375z"
-      />
-    </svg>
-  );
-}
diff --git a/server/sonar-web/src/main/js/components/ui/BugTrackerIcon.js b/server/sonar-web/src/main/js/components/ui/BugTrackerIcon.js
deleted file mode 100644 (file)
index 45b233b..0000000
+++ /dev/null
@@ -1,41 +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';
-
-export default function BugTrackerIcon() {
-  /* eslint-disable max-len */
-  return (
-    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 14" width="14" height="14">
-      <g transform="matrix(1,0,0,1,0,1)">
-        <path
-          style={{
-            fill: 'none',
-            stroke: 'currentColor',
-            strokeWidth: 2,
-            strokeLinecap: 'round',
-            strokeMiterlimit: '10'
-          }}
-          d="M12 9h-2L8 5 6.5 9.5l-2-6L3 9H1"
-        />
-      </g>
-    </svg>
-  );
-}
index 7439b850ff36a8f063b2123647d80c7bee2876c0..10ffc9e849b62cec48398aaecd6942f1b2516c6b 100644 (file)
@@ -18,7 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import { DonutChart } from '../charts/donut-chart';
+import DonutChart from '../charts/DonutChart';
 import * as theme from '../../app/theme';
 
 const SIZE_TO_WIDTH_MAPPING = { small: 16, normal: 24, big: 40, huge: 60 };
@@ -45,5 +45,5 @@ export default function CoverageRating({ muted = false, size = 'normal', value }
   const width = SIZE_TO_WIDTH_MAPPING[size];
   const thickness = SIZE_TO_THICKNESS_MAPPING[size];
 
-  return <DonutChart data={data} width={width} height={width} thickness={thickness} />;
+  return <DonutChart data={data} height={width} thickness={thickness} width={width} />;
 }
diff --git a/server/sonar-web/src/main/js/components/ui/__tests__/Level-test.js b/server/sonar-web/src/main/js/components/ui/__tests__/Level-test.js
deleted file mode 100644 (file)
index 3a315a0..0000000
+++ /dev/null
@@ -1,27 +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.
- */
-import { shallow } from 'enzyme';
-import React from 'react';
-import Level from '../Level';
-
-it('should render', () => {
-  const rating = shallow(<Level level="ERROR" />);
-  expect(rating.is('.level-ERROR')).toBe(true);
-});
diff --git a/server/sonar-web/src/main/js/components/ui/__tests__/Level-test.tsx b/server/sonar-web/src/main/js/components/ui/__tests__/Level-test.tsx
new file mode 100644 (file)
index 0000000..7706ee2
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * 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 { shallow } from 'enzyme';
+import Level from '../Level';
+
+it('should render', () => {
+  const rating = shallow(<Level level="ERROR" />);
+  expect(rating.is('.level-ERROR')).toBe(true);
+});
index 82992ba0a6478ab2f517a5199c75e64993c84406..1ede3bb8ce13e52e80ad13c2a2b59a5bca339fcb 100644 (file)
@@ -32,6 +32,7 @@ interface ButtonProps {
   disabled?: boolean;
   id?: string;
   innerRef?: (node: HTMLElement | null) => void;
+  name?: string;
   onClick?: (event: React.MouseEvent<HTMLElement>) => void;
   preventDefault?: boolean;
   stopPropagation?: boolean;
diff --git a/server/sonar-web/src/main/js/libs/third-party/VSS.SDK.min.js b/server/sonar-web/src/main/js/libs/third-party/VSS.SDK.min.js
deleted file mode 100644 (file)
index 5f6a766..0000000
+++ /dev/null
@@ -1,957 +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.
- */
-// Copyright (C) Microsoft Corporation. All rights reserved.
-var XDM, VSS;
-(function(n) {
-  function u() {
-    return new o();
-  }
-  function s() {
-    return (
-      Math.floor(Math.random() * (f - t) + t).toString(36) +
-      Math.floor(Math.random() * (f - t) + t).toString(36)
-    );
-  }
-  var i, r, e;
-  n.createDeferred = u;
-  var o = (function() {
-      function n() {
-        var n = this;
-        this._resolveCallbacks = [];
-        this._rejectCallbacks = [];
-        this._isResolved = !1;
-        this._isRejected = !1;
-        this.resolve = function(t) {
-          n._resolve(t);
-        };
-        this.reject = function(t) {
-          n._reject(t);
-        };
-        this.promise = {};
-        this.promise.then = function(t, i) {
-          return n._then(t, i);
-        };
-      }
-      return (
-        (n.prototype._then = function(t, i) {
-          var u = this,
-            r;
-          return (!t && !i) || (this._isResolved && !t) || (this._isRejected && !i)
-            ? this.promise
-            : ((r = new n()),
-              this._resolveCallbacks.push(function(n) {
-                u._wrapCallback(t, n, r, !1);
-              }),
-              this._rejectCallbacks.push(function(n) {
-                u._wrapCallback(i, n, r, !0);
-              }),
-              this._isResolved
-                ? this._resolve(this._resolvedValue)
-                : this._isRejected && this._reject(this._rejectValue),
-              r.promise);
-        }),
-        (n.prototype._wrapCallback = function(n, t, i, r) {
-          if (!n) {
-            r ? i.reject(t) : i.resolve(t);
-            return;
-          }
-          var u;
-          try {
-            u = n(t);
-          } catch (f) {
-            i.reject(f);
-            return;
-          }
-          u === undefined
-            ? i.resolve(t)
-            : u && typeof u.then == 'function'
-              ? u.then(
-                  function(n) {
-                    i.resolve(n);
-                  },
-                  function(n) {
-                    i.reject(n);
-                  }
-                )
-              : i.resolve(u);
-        }),
-        (n.prototype._resolve = function(n) {
-          if (
-            (this._isRejected ||
-              this._isResolved ||
-              ((this._isResolved = !0), (this._resolvedValue = n)),
-            this._isResolved && this._resolveCallbacks.length > 0)
-          ) {
-            var t = this._resolveCallbacks.splice(0);
-            window.setTimeout(function() {
-              for (var i = 0, r = t.length; i < r; i++) t[i](n);
-            });
-          }
-        }),
-        (n.prototype._reject = function(n) {
-          if (
-            (this._isRejected ||
-              this._isResolved ||
-              ((this._isRejected = !0),
-              (this._rejectValue = n),
-              this._rejectCallbacks.length === 0 &&
-                window.console &&
-                window.console.warn &&
-                (console.warn('Rejected XDM promise with no reject callbacks'),
-                n && console.warn(n))),
-            this._isRejected && this._rejectCallbacks.length > 0)
-          ) {
-            var t = this._rejectCallbacks.splice(0);
-            window.setTimeout(function() {
-              for (var i = 0, r = t.length; i < r; i++) t[i](n);
-            });
-          }
-        }),
-        n
-      );
-    })(),
-    t = parseInt('10000000000', 36),
-    f = Number.MAX_SAFE_INTEGER || 9007199254740991;
-  i = (function() {
-    function n() {
-      this._registeredObjects = {};
-    }
-    return (
-      (n.prototype.register = function(n, t) {
-        this._registeredObjects[n] = t;
-      }),
-      (n.prototype.unregister = function(n) {
-        delete this._registeredObjects[n];
-      }),
-      (n.prototype.getInstance = function(n, t) {
-        var i = this._registeredObjects[n];
-        return i ? (typeof i == 'function' ? i(t) : i) : null;
-      }),
-      n
-    );
-  })();
-  n.XDMObjectRegistry = i;
-  n.globalObjectRegistry = new i();
-  r = (function() {
-    function t(n, r) {
-      r === void 0 && (r = null);
-      this._nextMessageId = 1;
-      this._deferreds = {};
-      this._nextProxyFunctionId = 1;
-      this._proxyFunctions = {};
-      this._postToWindow = n;
-      this._targetOrigin = r;
-      this._channelObjectRegistry = new i();
-      this._channelId = t._nextChannelId++;
-      this._targetOrigin || (this._handshakeToken = s());
-    }
-    return (
-      (t.prototype.getObjectRegistry = function() {
-        return this._channelObjectRegistry;
-      }),
-      (t.prototype.invokeRemoteMethod = function(n, t, i, r, f) {
-        var e = {
-            id: this._nextMessageId++,
-            methodName: n,
-            instanceId: t,
-            instanceContext: r,
-            params: this._customSerializeObject(i, f),
-            jsonrpc: '2.0',
-            serializationSettings: f
-          },
-          o;
-        return (
-          this._targetOrigin || (e.handshakeToken = this._handshakeToken),
-          (o = u()),
-          (this._deferreds[e.id] = o),
-          this._sendRpcMessage(e),
-          o.promise
-        );
-      }),
-      (t.prototype.getRemoteObjectProxy = function(n, t) {
-        return this.invokeRemoteMethod(null, n, null, t);
-      }),
-      (t.prototype.invokeMethod = function(n, t) {
-        var f = this,
-          r,
-          u,
-          i;
-        if (!t.methodName) {
-          this._success(t, n, t.handshakeToken);
-          return;
-        }
-        if (((r = n[t.methodName]), typeof r != 'function')) {
-          this._error(t, new Error('RPC method not found: ' + t.methodName), t.handshakeToken);
-          return;
-        }
-        try {
-          u = [];
-          t.params && (u = this._customDeserializeObject(t.params));
-          i = r.apply(n, u);
-          i && i.then && typeof i.then == 'function'
-            ? i.then(
-                function(n) {
-                  f._success(t, n, t.handshakeToken);
-                },
-                function(n) {
-                  f._error(t, n, t.handshakeToken);
-                }
-              )
-            : this._success(t, i, t.handshakeToken);
-        } catch (e) {
-          this._error(t, e, t.handshakeToken);
-        }
-      }),
-      (t.prototype.getRegisteredObject = function(t, i) {
-        if (t === '__proxyFunctions') return this._proxyFunctions;
-        var r = this._channelObjectRegistry.getInstance(t, i);
-        return r || (r = n.globalObjectRegistry.getInstance(t, i)), r;
-      }),
-      (t.prototype.onMessage = function(n) {
-        var u = this,
-          t = n,
-          i,
-          r;
-        if (t.instanceId) {
-          if (((i = this.getRegisteredObject(t.instanceId, t.instanceContext)), !i)) return !1;
-          typeof i.then == 'function'
-            ? i.then(
-                function(n) {
-                  u.invokeMethod(n, t);
-                },
-                function(n) {
-                  u._error(t, n, t.handshakeToken);
-                }
-              )
-            : this.invokeMethod(i, t);
-        } else {
-          if (((r = this._deferreds[t.id]), !r)) return !1;
-          t.error
-            ? r.reject(this._customDeserializeObject([t.error])[0])
-            : r.resolve(this._customDeserializeObject([t.result])[0]);
-          delete this._deferreds[t.id];
-        }
-        return !0;
-      }),
-      (t.prototype.owns = function(n, t, i) {
-        var r = i;
-        if (this._postToWindow === n) {
-          if (this._targetOrigin)
-            return t
-              ? t.toLowerCase() === 'null' ||
-                  this._targetOrigin.toLowerCase().indexOf(t.toLowerCase()) === 0
-              : !1;
-          if (r.handshakeToken && r.handshakeToken === this._handshakeToken)
-            return (this._targetOrigin = t), !0;
-        }
-        return !1;
-      }),
-      (t.prototype.error = function(n, t) {
-        var i = n;
-        this._error(i, t, i.handshakeToken);
-      }),
-      (t.prototype._error = function(n, t, i) {
-        var r = {
-          id: n.id,
-          error: this._customSerializeObject([t], n.serializationSettings)[0],
-          jsonrpc: '2.0',
-          handshakeToken: i
-        };
-        this._sendRpcMessage(r);
-      }),
-      (t.prototype._success = function(n, t, i) {
-        var r = {
-          id: n.id,
-          result: this._customSerializeObject([t], n.serializationSettings)[0],
-          jsonrpc: '2.0',
-          handshakeToken: i
-        };
-        this._sendRpcMessage(r);
-      }),
-      (t.prototype._sendRpcMessage = function(n) {
-        var t = JSON.stringify(n);
-        this._postToWindow.postMessage(t, '*');
-      }),
-      (t.prototype._shouldSkipSerialization = function(n) {
-        for (var r, i = 0, u = t.WINDOW_TYPES_TO_SKIP_SERIALIZATION.length; i < u; i++)
-          if (((r = t.WINDOW_TYPES_TO_SKIP_SERIALIZATION[i]), window[r] && n instanceof window[r]))
-            return !0;
-        if (window.jQuery)
-          for (i = 0, u = t.JQUERY_TYPES_TO_SKIP_SERIALIZATION.length; i < u; i++)
-            if (
-              ((r = t.JQUERY_TYPES_TO_SKIP_SERIALIZATION[i]),
-              window.jQuery[r] && n instanceof window.jQuery[r])
-            )
-              return !0;
-        return !1;
-      }),
-      (t.prototype._customSerializeObject = function(n, i, r, u, f) {
-        var h = this,
-          a,
-          o,
-          l,
-          v,
-          e,
-          c,
-          s;
-        if (
-          (r === void 0 && (r = null),
-          u === void 0 && (u = 1),
-          f === void 0 && (f = 1),
-          !n || f > t.MAX_XDM_DEPTH) ||
-          this._shouldSkipSerialization(n)
-        )
-          return null;
-        if (
-          ((a = function(t, e, o) {
-            var s, c, l, a, v;
-            try {
-              s = t[o];
-            } catch (y) {}
-            ((c = typeof s), c !== 'undefined') &&
-              ((l = -1),
-              c === 'object' && (l = r.originalObjects.indexOf(s)),
-              l >= 0
-                ? ((a = r.newObjects[l]),
-                  a.__circularReferenceId || (a.__circularReferenceId = u++),
-                  (e[o] = { __circularReference: a.__circularReferenceId }))
-                : c === 'function'
-                  ? ((v = h._nextProxyFunctionId++),
-                    (e[o] = {
-                      __proxyFunctionId: h._registerProxyFunction(s, n),
-                      __channelId: h._channelId
-                    }))
-                  : c === 'object'
-                    ? (e[o] =
-                        s && s instanceof Date
-                          ? { __proxyDate: s.getTime() }
-                          : h._customSerializeObject(s, i, r, u, f + 1))
-                    : o !== '__proxyFunctionId' && (e[o] = s));
-          }),
-          r || (r = { newObjects: [], originalObjects: [] }),
-          r.originalObjects.push(n),
-          n instanceof Array)
-        )
-          for (o = [], r.newObjects.push(o), e = 0, c = n.length; e < c; e++) a(n, o, e);
-        else {
-          o = {};
-          r.newObjects.push(o);
-          l = {};
-          try {
-            for (s in n) l[s] = !0;
-            for (v = Object.getOwnPropertyNames(n), e = 0, c = v.length; e < c; e++) l[v[e]] = !0;
-          } catch (y) {}
-          for (s in l) ((s && s[0] !== '_') || (i && i.includeUnderscoreProperties)) && a(n, o, s);
-        }
-        return r.originalObjects.pop(), r.newObjects.pop(), o;
-      }),
-      (t.prototype._registerProxyFunction = function(n, t) {
-        var i = this._nextProxyFunctionId++;
-        return (
-          (this._proxyFunctions['proxy' + i] = function() {
-            return n.apply(t, Array.prototype.slice.call(arguments, 0));
-          }),
-          i
-        );
-      }),
-      (t.prototype._customDeserializeObject = function(n, t) {
-        var e = this,
-          o = this,
-          r,
-          i,
-          u,
-          f;
-        if (!n) return null;
-        if (
-          (t || (t = {}),
-          (r = function(n, i) {
-            var r = n[i],
-              u = typeof r;
-            i === '__circularReferenceId' && u === 'number'
-              ? ((t[r] = n), delete n[i])
-              : u === 'object' &&
-                r &&
-                (r.__proxyFunctionId
-                  ? (n[i] = function() {
-                      return o.invokeRemoteMethod(
-                        'proxy' + r.__proxyFunctionId,
-                        '__proxyFunctions',
-                        Array.prototype.slice.call(arguments, 0),
-                        null,
-                        { includeUnderscoreProperties: !0 }
-                      );
-                    })
-                  : r.__proxyDate
-                    ? (n[i] = new Date(r.__proxyDate))
-                    : r.__circularReference
-                      ? (n[i] = t[r.__circularReference])
-                      : e._customDeserializeObject(r, t));
-          }),
-          n instanceof Array)
-        )
-          for (i = 0, u = n.length; i < u; i++) r(n, i);
-        else if (typeof n == 'object') for (f in n) r(n, f);
-        return n;
-      }),
-      (t._nextChannelId = 1),
-      (t.MAX_XDM_DEPTH = 100),
-      (t.WINDOW_TYPES_TO_SKIP_SERIALIZATION = ['Node', 'Window', 'Event']),
-      (t.JQUERY_TYPES_TO_SKIP_SERIALIZATION = ['jQuery']),
-      t
-    );
-  })();
-  n.XDMChannel = r;
-  e = (function() {
-    function n() {
-      this._channels = [];
-      this._subscribe(window);
-    }
-    return (
-      (n.get = function() {
-        return this._default || (this._default = new n()), this._default;
-      }),
-      (n.prototype.addChannel = function(n, t) {
-        var i = new r(n, t);
-        return this._channels.push(i), i;
-      }),
-      (n.prototype.removeChannel = function(n) {
-        this._channels = this._channels.filter(function(t) {
-          return t !== n;
-        });
-      }),
-      (n.prototype._handleMessageReceived = function(n) {
-        var i, e, r, t, u, f;
-        if (typeof n.data == 'string')
-          try {
-            t = JSON.parse(n.data);
-          } catch (o) {}
-        if (t) {
-          for (u = !1, i = 0, e = this._channels.length; i < e; i++)
-            (r = this._channels[i]),
-              r.owns(n.source, n.origin, t) && ((f = r), (u = r.onMessage(t, n.origin) || u));
-          !f ||
-            u ||
-            (window.console &&
-              console.error('No handler found on any channel for message: ' + JSON.stringify(t)),
-            t.instanceId &&
-              f.error(t, 'The registered object ' + t.instanceId + ' could not be found.'));
-        }
-      }),
-      (n.prototype._subscribe = function(n) {
-        var t = this;
-        n.addEventListener
-          ? n.addEventListener('message', function(n) {
-              t._handleMessageReceived(n);
-            })
-          : n.attachEvent('onmessage', function(n) {
-              t._handleMessageReceived(n);
-            });
-      }),
-      n
-    );
-  })();
-  n.XDMChannelManager = e;
-})(XDM || (XDM = {})),
-  (function(n) {
-    function at() {
-      function r() {
-        n ||
-          (n = setTimeout(function() {
-            n = 0;
-            tt();
-          }, 50));
-      }
-      var n,
-        i = !1,
-        t;
-      try {
-        i = typeof document.cookie == 'string';
-      } catch (f) {}
-      i ||
-        Object.defineProperty(Document.prototype, 'cookie', {
-          get: function() {
-            return '';
-          },
-          set: function() {}
-        });
-      t = !1;
-      try {
-        t = !!window.localStorage;
-      } catch (f) {}
-      t ||
-        (delete window.localStorage,
-        (u = new g(r)),
-        Object.defineProperty(window, 'localStorage', { value: u }),
-        delete window.sessionStorage,
-        Object.defineProperty(window, 'sessionStorage', { value: new g() }));
-    }
-    function nt(f) {
-      r = f || {};
-      e = r.usePlatformScripts;
-      a = r.usePlatformStyles;
-      window.setTimeout(function() {
-        var f = {
-          notifyLoadSucceeded: !r.explicitNotifyLoaded,
-          extensionReusedCallback: r.extensionReusedCallback,
-          vssSDKVersion: n.VssSDKVersion
-        };
-        i.invokeRemoteMethod('initialHandshake', 'VSS.HostControl', [f]).then(function(n) {
-          var f, r, o, h, l, s, v, i;
-          if (
-            ((t = n.pageContext),
-            (b = t.webContext),
-            (k = n.initialConfig || {}),
-            (d = n.contribution),
-            (c = n.extensionContext),
-            n.sandboxedStorage)
-          ) {
-            if (((f = !1), u))
-              if (n.sandboxedStorage.localStorage) {
-                for (
-                  r = n.sandboxedStorage.localStorage, o = 0, h = Object.keys(u);
-                  o < h.length;
-                  o++
-                )
-                  (i = h[o]), (l = u.getItem(i)), l !== r[i] && ((r[i] = l), (f = !0));
-                for (s = 0, v = Object.keys(r); s < v.length; s++) (i = v[s]), u.setItem(i, r[i]);
-              } else u.length > 0 && (f = !0);
-            lt = !0;
-            f && tt();
-          }
-          e || a ? ht() : w();
-        });
-      }, 0);
-    }
-    function tt() {
-      var n = { localStorage: JSON.stringify(u || {}) };
-      i.invokeRemoteMethod('updateSandboxedStorage', 'VSS.HostControl', [n]);
-    }
-    function pt(n, t) {
-      var i;
-      i = typeof n == 'string' ? [n] : n;
-      t || (t = function() {});
-      l
-        ? it(i, t)
-        : (r ? e || ((e = !0), s && ((s = !1), ht())) : nt({ usePlatformScripts: !0 }),
-          rt(function() {
-            it(i, t);
-          }));
-    }
-    function it(n, i) {
-      t.diagnostics.bundlingEnabled
-        ? window.require(['VSS/Bundling'], function(t) {
-            t.requireModules(n).spread(function() {
-              i.apply(this, arguments);
-            });
-          })
-        : window.require(n, i);
-    }
-    function rt(n) {
-      s ? window.setTimeout(n, 0) : (f || (f = []), f.push(n));
-    }
-    function wt() {
-      i.invokeRemoteMethod('notifyLoadSucceeded', 'VSS.HostControl');
-    }
-    function ut(n) {
-      i.invokeRemoteMethod('notifyLoadFailed', 'VSS.HostControl', [n]);
-    }
-    function ft() {
-      return b;
-    }
-    function bt() {
-      return k;
-    }
-    function et() {
-      return c;
-    }
-    function kt() {
-      return d;
-    }
-    function dt(n, t) {
-      return ot(n).then(function(n) {
-        return (
-          t || (t = {}),
-          t.webContext || (t.webContext = ft()),
-          t.extensionContext || (t.extensionContext = et()),
-          n.getInstance(n.id, t)
-        );
-      });
-    }
-    function ot(t) {
-      var r = XDM.createDeferred();
-      return (
-        n.ready(function() {
-          i
-            .invokeRemoteMethod('getServiceContribution', 'vss.hostManagement', [t])
-            .then(function(n) {
-              var t = n;
-              t.getInstance = function(t, i) {
-                return st(n, t, i);
-              };
-              r.resolve(t);
-            }, r.reject);
-        }),
-        r.promise
-      );
-    }
-    function gt(t) {
-      var r = XDM.createDeferred();
-      return (
-        n.ready(function() {
-          i
-            .invokeRemoteMethod('getContributionsForTarget', 'vss.hostManagement', [t])
-            .then(function(n) {
-              var t = [];
-              n.forEach(function(n) {
-                var i = n;
-                i.getInstance = function(t, i) {
-                  return st(n, t, i);
-                };
-                t.push(i);
-              });
-              r.resolve(t);
-            }, r.reject);
-        }),
-        r.promise
-      );
-    }
-    function st(t, r, u) {
-      var f = XDM.createDeferred();
-      return (
-        n.ready(function() {
-          i
-            .invokeRemoteMethod('getBackgroundContributionInstance', 'vss.hostManagement', [
-              t,
-              r,
-              u
-            ])
-            .then(f.resolve, f.reject);
-        }),
-        f.promise
-      );
-    }
-    function ni(n, t) {
-      i.getObjectRegistry().register(n, t);
-    }
-    function ti(n) {
-      i.getObjectRegistry().unregister(n);
-    }
-    function ii(n, t) {
-      return i.getObjectRegistry().getInstance(n, t);
-    }
-    function ri() {
-      return i.invokeRemoteMethod('getAccessToken', 'VSS.HostControl');
-    }
-    function ui() {
-      return i.invokeRemoteMethod('getAppToken', 'VSS.HostControl');
-    }
-    function fi(n, t) {
-      o || (o = document.getElementsByTagName('body').item(0));
-      var r = typeof n == 'number' ? n : o.scrollWidth,
-        u = typeof t == 'number' ? t : o.scrollHeight;
-      i.invokeRemoteMethod('resize', 'VSS.HostControl', [r, u]);
-    }
-    function ht() {
-      var i = si(t.webContext),
-        f,
-        g,
-        n,
-        s,
-        o,
-        b,
-        k,
-        nt,
-        tt,
-        d,
-        u;
-      if (
-        ((window.__vssPageContext = t),
-        (window.__cultureInfo = t.microsoftAjaxConfig.cultureInfo),
-        a !== !1 &&
-          t.coreReferences.stylesheets &&
-          t.coreReferences.stylesheets.forEach(function(n) {
-            if (n.isCoreStylesheet) {
-              var t = document.createElement('link');
-              t.href = h(n.url, i);
-              t.rel = 'stylesheet';
-              p(t, 'head');
-            }
-          }),
-        !e)
-      ) {
-        l = !0;
-        w();
-        return;
-      }
-      if (
-        ((f = []),
-        (g = !1),
-        t.coreReferences.scripts &&
-          (t.coreReferences.scripts.forEach(function(n) {
-            if (n.isCoreModule) {
-              var r = !1,
-                t = window;
-              n.identifier === 'JQuery'
-                ? (r = !!t.jQuery)
-                : n.identifier === 'JQueryUI'
-                  ? (r = !!(t.jQuery && t.jQuery.ui && t.jQuery.ui.version))
-                  : n.identifier === 'AMDLoader' &&
-                    (r = typeof t.define == 'function' && !!t.define.amd);
-              r ? (g = !0) : f.push({ source: h(n.url, i) });
-            }
-          }),
-          t.coreReferences.coreScriptsBundle &&
-            !g &&
-            (f = [{ source: h(t.coreReferences.coreScriptsBundle.url, i) }]),
-          t.coreReferences.extensionCoreReferences &&
-            f.push({ source: h(t.coreReferences.extensionCoreReferences.url, i) })),
-        (n = { baseUrl: c.baseUri, contributionPaths: null, paths: {}, shim: {} }),
-        r.moduleLoaderConfig &&
-          (r.moduleLoaderConfig.baseUrl && (n.baseUrl = r.moduleLoaderConfig.baseUrl),
-          oi(r.moduleLoaderConfig, n),
-          ct(r.moduleLoaderConfig, n)),
-        t.moduleLoaderConfig &&
-          (ct(t.moduleLoaderConfig, n), (s = t.moduleLoaderConfig.contributionPaths), s))
-      )
-        for (o in s)
-          if (
-            s.hasOwnProperty(o) &&
-            !n.paths[o] &&
-            ((b = s[o].value),
-            (n.paths[o] = b.match('^https?://') ? b : i + b),
-            (k = t.moduleLoaderConfig.paths),
-            k)
-          ) {
-            nt = o + '/';
-            tt = v(i, t.moduleLoaderConfig.baseUrl);
-            for (d in k)
-              ei(d, nt) &&
-                ((u = k[d]),
-                u.match('^https?://') || (u = u[0] === '/' ? v(i, u) : v(tt, u)),
-                (n.paths[d] = u));
-          }
-      window.__vssModuleLoaderConfig = n;
-      f.push({ content: 'require.config(' + JSON.stringify(n) + ');' });
-      y(f, 0, function() {
-        l = !0;
-        w();
-      });
-    }
-    function ei(n, t) {
-      return n && n.length >= t.length ? n.substr(0, t.length).localeCompare(t) === 0 : !1;
-    }
-    function v(n, t) {
-      var i = n || '';
-      return i[i.length - 1] !== '/' && (i += '/'), t && (i += t[0] === '/' ? t.substr(1) : t), i;
-    }
-    function oi(n, t, i) {
-      var r, u;
-      if (n.paths) {
-        t.paths || (t.paths = {});
-        for (r in n.paths)
-          n.paths.hasOwnProperty(r) &&
-            ((u = n.paths[r]), i && (u = i(r, n.paths[r])), u && (t.paths[r] = u));
-      }
-    }
-    function ct(n, t) {
-      if (n.shim) {
-        t.shim || (t.shim = {});
-        for (var i in n.shim) n.shim.hasOwnProperty(i) && (t.shim[i] = n.shim[i]);
-      }
-    }
-    function si(n) {
-      var r = n.account || n.host,
-        t = r.uri,
-        i = r.relativeUri;
-      return (
-        t &&
-          i &&
-          (t[t.length - 1] !== '/' && (t += '/'),
-          i[i.length - 1] !== '/' && (i += '/'),
-          (t = t.substr(0, t.length - i.length))),
-        t
-      );
-    }
-    function y(n, t, i) {
-      var f = this,
-        r,
-        u;
-      if (t >= n.length) {
-        i.call(this);
-        return;
-      }
-      r = document.createElement('script');
-      r.type = 'text/javascript';
-      n[t].source
-        ? ((u = n[t].source),
-          (r.src = u),
-          r.addEventListener('load', function() {
-            y.call(f, n, t + 1, i);
-          }),
-          r.addEventListener('error', function() {
-            ut('Failed to load script: ' + u);
-          }),
-          p(r, 'head'))
-        : n[t].content && ((r.textContent = n[t].content), p(r, 'head'), y.call(this, n, t + 1, i));
-    }
-    function p(n, t) {
-      var i = document.getElementsByTagName(t)[0];
-      i || ((i = document.createElement(t)), document.appendChild(i));
-      i.appendChild(n);
-    }
-    function h(n, t) {
-      var i = (n || '').toLowerCase();
-      return (
-        i.substr(0, 2) !== '//' &&
-          i.substr(0, 5) !== 'http:' &&
-          i.substr(0, 6) !== 'https:' &&
-          (n = t + (i[0] === '/' ? '' : '/') + n),
-        n
-      );
-    }
-    function w() {
-      var t = this,
-        n;
-      s = !0;
-      f &&
-        ((n = f),
-        (f = null),
-        n.forEach(function(n) {
-          n.call(t);
-        }));
-    }
-    var yt;
-    n.VssSDKVersion = 2;
-    n.VssSDKRestVersion = '4.0';
-    var o,
-      b,
-      t,
-      c,
-      k,
-      d,
-      r,
-      l = !1,
-      e,
-      a,
-      s = !1,
-      f,
-      i = XDM.XDMChannelManager.get().addChannel(window.parent),
-      u,
-      lt = !1,
-      g = (function() {
-        function n() {
-          t && t.call(this);
-        }
-        function i() {}
-        var t;
-        return (
-          Object.defineProperties(i.prototype, {
-            getItem: {
-              get: function() {
-                return function(n) {
-                  var t = this['' + n];
-                  return typeof t == 'undefined' ? null : t;
-                };
-              }
-            },
-            setItem: {
-              get: function() {
-                return function(t, i) {
-                  t = '' + t;
-                  var u = this[t],
-                    r = '' + i;
-                  u !== r && ((this[t] = r), n());
-                };
-              }
-            },
-            removeItem: {
-              get: function() {
-                return function(t) {
-                  t = '' + t;
-                  typeof this[t] != 'undefined' && (delete this[t], n());
-                };
-              }
-            },
-            clear: {
-              get: function() {
-                return function() {
-                  var r = Object.keys(this),
-                    t,
-                    i,
-                    u;
-                  if (r.length > 0) {
-                    for (t = 0, i = r; t < i.length; t++) (u = i[t]), delete this[u];
-                    n();
-                  }
-                };
-              }
-            },
-            key: {
-              get: function() {
-                return function(n) {
-                  return Object.keys(this)[n];
-                };
-              }
-            },
-            length: {
-              get: function() {
-                return Object.keys(this).length;
-              }
-            }
-          }),
-          i
-        );
-      })();
-    if (!window.__vssNoSandboxShim)
-      try {
-        at();
-      } catch (vt) {
-        window.console &&
-          window.console.warn &&
-          window.console.warn(
-            'Failed to shim support for sandboxed properties: ' +
-              vt.message +
-              '. Set "window.__vssNoSandboxShim = true" in order to bypass the shim of sandboxed properties.'
-          );
-      }
-    (function(n) {
-      n.Dialog = 'ms.vss-web.dialog-service';
-      n.Navigation = 'ms.vss-web.navigation-service';
-      n.ExtensionData = 'ms.vss-web.data-service';
-    })((yt = n.ServiceIds || (n.ServiceIds = {})));
-    n.init = nt;
-    n.require = pt;
-    n.ready = rt;
-    n.notifyLoadSucceeded = wt;
-    n.notifyLoadFailed = ut;
-    n.getWebContext = ft;
-    n.getConfiguration = bt;
-    n.getExtensionContext = et;
-    n.getContribution = kt;
-    n.getService = dt;
-    n.getServiceContribution = ot;
-    n.getServiceContributions = gt;
-    n.register = ni;
-    n.unregister = ti;
-    n.getRegisteredObject = ii;
-    n.getAccessToken = ri;
-    n.getAppToken = ui;
-    n.resize = fi;
-  })(VSS || (VSS = {}));
diff --git a/server/sonar-web/src/main/js/typings/json.d.ts b/server/sonar-web/src/main/js/typings/json.d.ts
deleted file mode 100644 (file)
index 0f858f5..0000000
+++ /dev/null
@@ -1,23 +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.
- */
-declare module '*.json' {
-  const value: any;
-  export default value;
-}
diff --git a/server/sonar-web/src/main/js/typings/rc-tooltip.d.ts b/server/sonar-web/src/main/js/typings/rc-tooltip.d.ts
deleted file mode 100644 (file)
index 999b5b0..0000000
+++ /dev/null
@@ -1,54 +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.
- */
-declare module 'rc-tooltip' {
-  export type Trigger = 'hover' | 'click' | 'focus';
-  export type Placement =
-    | 'left'
-    | 'right'
-    | 'top'
-    | 'bottom'
-    | 'topLeft'
-    | 'topRight'
-    | 'bottomLeft'
-    | 'bottomRight';
-
-  export interface Props extends React.Props<any> {
-    overlayClassName?: string;
-    trigger?: Trigger[];
-    mouseEnterDelay?: number;
-    mouseLeaveDelay?: number;
-    overlayStyle?: React.CSSProperties;
-    prefixCls?: string;
-    transitionName?: string;
-    onVisibleChange?: () => void;
-    visible?: boolean;
-    defaultVisible?: boolean;
-    placement?: Placement | Object;
-    align?: Object;
-    onPopupAlign?: (popupDomNode: Element, align: Object) => void;
-    overlay: React.ReactNode;
-    arrowContent?: React.ReactNode;
-    getTooltipContainer?: () => Element;
-    destroyTooltipOnHide?: boolean;
-  }
-
-  // the next line is crucial, it is absent in the original typings
-  export default class Tooltip extends React.Component<Props> {}
-}
index fed42e3c3507faad3d94fe78fde9774fb901d277..ca97ba90123934a70dd3b065f1c8c03bd5b5c094 100644 (file)
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-1.2.1.tgz#e489605208d46a1c9d980d2e5772fa9c75d9ec65"
 
+"@types/d3-hierarchy@1.1.1":
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/@types/d3-hierarchy/-/d3-hierarchy-1.1.1.tgz#f1b7f4bab0a9ecfb9c372a303ded97d83db3cbbf"
+
+"@types/d3-path@*":
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-1.0.6.tgz#c1a7d2dc07b295fdd1c84dabe4404df991b48693"
+
 "@types/d3-scale@2.0.0":
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-2.0.0.tgz#d40f2ff137f4fe9b13b45c5a8e14f39edd498dcf"
   dependencies:
     "@types/d3-time" "*"
 
+"@types/d3-shape@1.2.2":
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-1.2.2.tgz#f8dcdff7772a7ae37858bf04abd43848a78e590e"
+  dependencies:
+    "@types/d3-path" "*"
+
 "@types/d3-time@*":
   version "1.0.7"
   resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-1.0.7.tgz#4266d7c9be15fa81256a88d1d052d61cd8dc572c"