aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2018-08-20 17:28:52 +0200
committersonartech <sonartech@sonarsource.com>2018-09-19 10:51:41 +0200
commit0ba8c56c9a7cddf225720f224e66b267f9bbf528 (patch)
tree70bde7296671e7a6fe172b7c1160314eedec9ce8 /server
parent19107a98d5248a1556ac76b3d9263f1e69fbcefc (diff)
downloadsonarqube-0ba8c56c9a7cddf225720f224e66b267f9bbf528.tar.gz
sonarqube-0ba8c56c9a7cddf225720f224e66b267f9bbf528.zip
SONAR-11165 Migrate rest of component measures page to TS
Diffstat (limited to 'server')
-rw-r--r--server/sonar-web/src/main/js/api/components.ts10
-rw-r--r--server/sonar-web/src/main/js/api/measures.ts10
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx6
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap18
-rw-r--r--server/sonar-web/src/main/js/app/styles/components/modals.css1
-rw-r--r--server/sonar-web/src/main/js/app/types.ts42
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/App.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/components/App.js)120
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/AppContainer.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/components/AppContainer.js)78
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/FilesCounter.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/components/FilesCounter.js)17
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/LeakPeriodLegend.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.js)31
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.js)72
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverviewContainer.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverviewContainer.js)76
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/MetricNotFound.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/components/MetricNotFound.js)5
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/PageActions.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/components/PageActions.js)23
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.js)20
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/__tests__/FilesCounter-test.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/components/__tests__/FilesCounter-test.js)4
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/__tests__/LeakPeriodLegend-test.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureHeader-test.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureHeader-test.js)34
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/__tests__/PageActions-test.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/components/__tests__/PageActions-test.js)12
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.js.snap)38
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/FilesCounter-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/FilesCounter-test.js.snap)0
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureHeader-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureHeader-test.js.snap)51
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/PageActions-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/PageActions-test.js.snap)0
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/config/bubbles.ts2
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/config/domains.ts7
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/BubbleChart.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/drilldown/BubbleChart.js)93
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/CodeView.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.js)77
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsListRow.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsListRow.js)28
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/MeasureCell.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/drilldown/MeasureCell.js)21
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/MeasureCell-test.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/MeasureCell-test.js)8
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainFacet.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainFacet.js)40
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetMeasureValue.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetMeasureValue.js)11
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/sidebar/ProjectOverviewFacet.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/sidebar/ProjectOverviewFacet.js)22
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.js)49
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/DomainFacet-test.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/DomainFacet-test.js)9
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/FacetMeasureValue-test.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/FacetMeasureValue-test.js)5
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/Sidebar-test.tsx (renamed from server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/Sidebar-test.js)9
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/DomainFacet-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/DomainFacet-test.js.snap)16
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/FacetMeasureValue-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/FacetMeasureValue-test.js.snap)0
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/Sidebar-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/Sidebar-test.js.snap)3
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/types.js58
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/utils.ts9
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/LeakPeriodLegend.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/__tests__/LeakPeriodLegend-test.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/enhance.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeak-test.tsx1
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.tsx1
-rw-r--r--server/sonar-web/src/main/js/components/charts/BubbleChart.tsx28
-rw-r--r--server/sonar-web/src/main/js/components/charts/__tests__/BubbleChart-test.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/BubbleChart-test.tsx.snap4
-rw-r--r--server/sonar-web/src/main/js/components/measure/utils.ts3
-rw-r--r--server/sonar-web/src/main/js/helpers/path.ts16
-rw-r--r--server/sonar-web/src/main/js/helpers/periods.ts22
58 files changed, 601 insertions, 652 deletions
diff --git a/server/sonar-web/src/main/js/api/components.ts b/server/sonar-web/src/main/js/api/components.ts
index 39805e25649..df793ee8a87 100644
--- a/server/sonar-web/src/main/js/api/components.ts
+++ b/server/sonar-web/src/main/js/api/components.ts
@@ -114,14 +114,14 @@ export function getComponentTree(
metricKeys: metrics.join(','),
strategy
});
- return getJSON(url, data);
+ return getJSON(url, data).catch(throwGlobalError);
}
export function getChildren(
componentKey: string,
metrics: string[] = [],
additional: RequestData = {}
-): Promise<any> {
+) {
return getComponentTree('children', componentKey, metrics, additional);
}
@@ -129,14 +129,14 @@ export function getComponentLeaves(
componentKey: string,
metrics: string[] = [],
additional: RequestData = {}
-): Promise<any> {
+) {
return getComponentTree('leaves', componentKey, metrics, additional);
}
export function getComponent(
data: { componentKey: string; metricKeys: string } & BranchParameters
): Promise<any> {
- return getJSON('/api/measures/component', data).then(r => r.component);
+ return getJSON('/api/measures/component', data).then(r => r.component, throwGlobalError);
}
export interface TreeComponent extends LightComponent {
@@ -165,7 +165,7 @@ export function getTree(data: {
}
export function getComponentShow(data: { component: string } & BranchParameters): Promise<any> {
- return getJSON('/api/components/show', data);
+ return getJSON('/api/components/show', data).catch(throwGlobalError);
}
export function getParents(component: string): Promise<any> {
diff --git a/server/sonar-web/src/main/js/api/measures.ts b/server/sonar-web/src/main/js/api/measures.ts
index 1102c39870c..378c5079242 100644
--- a/server/sonar-web/src/main/js/api/measures.ts
+++ b/server/sonar-web/src/main/js/api/measures.ts
@@ -20,14 +20,14 @@
import { getJSON, RequestData, postJSON, post } from '../helpers/request';
import throwGlobalError from '../app/utils/throwGlobalError';
import {
- Metric,
CustomMeasure,
- Paging,
BranchParameters,
Measure,
- MeasurePeriod
+ Metric,
+ Paging,
+ Period,
+ PeriodMeasure
} from '../app/types';
-import { Period } from '../helpers/periods';
export function getMeasures(
data: { componentKey: string; metricKeys: string } & BranchParameters
@@ -55,7 +55,7 @@ export function getMeasuresAndMeta(
interface MeasuresForProjects {
component: string;
metric: string;
- periods?: MeasurePeriod[];
+ periods?: PeriodMeasure[];
value?: string;
}
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx
index 31983bc3913..267ec368cb1 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx
@@ -152,12 +152,6 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
}
renderComponentMeasuresLink() {
- const { branchLike } = this.props;
-
- if (isShortLivingBranch(branchLike) || isPullRequest(branchLike)) {
- return null;
- }
-
return (
<li>
<Link
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap
index 0b2bf02dab9..71afa0c4716 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap
@@ -1104,6 +1104,24 @@ exports[`should work for short-living branches 1`] = `
style={Object {}}
to={
Object {
+ "pathname": "/component_measures",
+ "query": Object {
+ "branch": "feature",
+ "id": "foo",
+ },
+ }
+ }
+ >
+ layout.measures
+ </Link>
+ </li>
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
"pathname": "/code",
"query": Object {
"branch": "feature",
diff --git a/server/sonar-web/src/main/js/app/styles/components/modals.css b/server/sonar-web/src/main/js/app/styles/components/modals.css
index a89dbc9a621..dd8be688094 100644
--- a/server/sonar-web/src/main/js/app/styles/components/modals.css
+++ b/server/sonar-web/src/main/js/app/styles/components/modals.css
@@ -275,7 +275,6 @@
}
.modal-foot {
- line-height: var(--controlHeight);
padding: 10px;
border-top: 1px solid #ccc;
background-color: var(--gray94);
diff --git a/server/sonar-web/src/main/js/app/types.ts b/server/sonar-web/src/main/js/app/types.ts
index 237d4d6e981..954eecdd305 100644
--- a/server/sonar-web/src/main/js/app/types.ts
+++ b/server/sonar-web/src/main/js/app/types.ts
@@ -107,12 +107,14 @@ export interface ComponentQualityProfile {
}
interface ComponentMeasureIntern {
+ branch?: string;
isFavorite?: boolean;
isRecentlyBrowsed?: boolean;
key: string;
match?: string;
name: string;
organization?: string;
+ path?: string;
project?: string;
qualifier: string;
refKey?: string;
@@ -376,18 +378,6 @@ export interface MainBranch extends Branch {
status?: { qualityGateStatus: string };
}
-export interface MeasurePeriod {
- bestValue?: boolean;
- index: number;
- value: string;
-}
-
-interface MeasureIntern {
- bestValue?: boolean;
- periods?: MeasurePeriod[];
- value?: string;
-}
-
export interface Measure extends MeasureIntern {
metric: string;
}
@@ -397,6 +387,12 @@ export interface MeasureEnhanced extends MeasureIntern {
leak?: string;
}
+interface MeasureIntern {
+ bestValue?: boolean;
+ periods?: PeriodMeasure[];
+ value?: string;
+}
+
export interface Metric {
bestValue?: string;
custom?: boolean;
@@ -478,6 +474,28 @@ export interface Paging {
total: number;
}
+export interface Period {
+ date: string;
+ index: number;
+ mode: PeriodMode;
+ modeParam?: string;
+ parameter?: string;
+}
+
+export interface PeriodMeasure {
+ bestValue?: boolean;
+ index: number;
+ value: string;
+}
+
+export enum PeriodMode {
+ Days = 'days',
+ Date = 'date',
+ Version = 'version',
+ PreviousAnalysis = 'previous_analysis',
+ PreviousVersion = 'previous_version'
+}
+
export interface PermissionTemplate {
defaultFor: string[];
id: string;
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/App.js b/server/sonar-web/src/main/js/apps/component-measures/components/App.tsx
index f866fd0dd60..ff78d3a724a 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/App.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/App.tsx
@@ -17,15 +17,15 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
+import * as key from 'keymaster';
+import { InjectedRouter } from 'react-router';
import Helmet from 'react-helmet';
-import key from 'keymaster';
import MeasureContentContainer from './MeasureContentContainer';
import MeasureOverviewContainer from './MeasureOverviewContainer';
import Sidebar from '../sidebar/Sidebar';
import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
-import { isProjectOverview, hasBubbleChart, parseQuery, serializeQuery } from '../utils';
+import { isProjectOverview, hasBubbleChart, parseQuery, serializeQuery, Query } from '../utils';
import { isSameBranchLike, getBranchLikeQuery } from '../../../helpers/branches';
import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
import {
@@ -34,67 +34,64 @@ import {
translate
} from '../../../helpers/l10n';
import { getDisplayMetrics } from '../../../helpers/measures';
-/*:: import type { Component, Query, Period } from '../types'; */
-/*:: import type { RawQuery } from '../../../helpers/query'; */
-/*:: import type { Metric } from '../../../app/flow-types'; */
-/*:: import type { MeasureEnhanced } from '../../../components/measure/types'; */
+import { RawQuery } from '../../../helpers/query';
+import {
+ BranchLike,
+ ComponentMeasure,
+ MeasureEnhanced,
+ Metric,
+ CurrentUser,
+ Period
+} from '../../../app/types';
import '../../../components/search-navigator.css';
import '../style.css';
-/*:: type Props = {|
- branchLike?: { id?: string; name: string },
- component: Component,
- currentUser: { isLoggedIn: boolean },
- location: { pathname: string, query: RawQuery },
+interface Props {
+ branchLike?: BranchLike;
+ component: ComponentMeasure;
+ currentUser: CurrentUser;
+ location: { pathname: string; query: RawQuery };
fetchMeasures: (
component: string,
- metricsKey: Array<string>,
- branchLike?: { id?: string; name: string }
- ) => Promise<{ component: Component, measures: Array<MeasureEnhanced>, leakPeriod: ?Period }>,
- fetchMetrics: () => void,
- metrics: { [string]: Metric },
- metricsKey: Array<string>,
- router: {
- push: ({ pathname: string, query?: RawQuery }) => void
- }
-|}; */
+ metricsKey: string[],
+ branchLike?: BranchLike
+ ) => Promise<{ component: ComponentMeasure; measures: MeasureEnhanced[]; leakPeriod?: Period }>;
+ fetchMetrics: () => void;
+ metrics: { [metric: string]: Metric };
+ metricsKey: string[];
+ router: InjectedRouter;
+}
-/*:: type State = {|
- loading: boolean,
- measures: Array<MeasureEnhanced>,
- leakPeriod: ?Period
-|}; */
+interface State {
+ loading: boolean;
+ measures: MeasureEnhanced[];
+ leakPeriod?: Period;
+}
-export default class App extends React.PureComponent {
- /*:: mounted: boolean; */
- /*:: props: Props; */
- /*:: state: State; */
+export default class App extends React.PureComponent<Props, State> {
+ mounted = false;
- constructor(props /*: Props */) {
+ constructor(props: Props) {
super(props);
- this.state = {
- loading: true,
- measures: [],
- leakPeriod: null
- };
+ this.state = { loading: true, measures: [] };
}
componentDidMount() {
this.mounted = true;
- // $FlowFixMe
+
document.body.classList.add('white-page');
- // $FlowFixMe
document.documentElement.classList.add('white-page');
- this.props.fetchMetrics();
- this.fetchMeasures(this.props);
- key.setScope('measures-files');
const footer = document.getElementById('footer');
if (footer) {
footer.classList.add('page-footer-with-sidebar');
}
+
+ key.setScope('measures-files');
+ this.props.fetchMetrics();
+ this.fetchMeasures(this.props);
}
- componentWillReceiveProps(nextProps /*: Props */) {
+ componentWillReceiveProps(nextProps: Props) {
if (
!isSameBranchLike(nextProps.branchLike, this.props.branchLike) ||
nextProps.component.key !== this.props.component.key ||
@@ -106,27 +103,31 @@ export default class App extends React.PureComponent {
componentWillUnmount() {
this.mounted = false;
- // $FlowFixMe
+
document.body.classList.remove('white-page');
- // $FlowFixMe
document.documentElement.classList.remove('white-page');
- key.deleteScope('measures-files');
+
const footer = document.getElementById('footer');
if (footer) {
footer.classList.remove('page-footer-with-sidebar');
}
+
+ key.deleteScope('measures-files');
}
- fetchMeasures = ({ branchLike, component, fetchMeasures, metrics } /*: Props */) => {
+ fetchMeasures = ({ branchLike, component, fetchMeasures, metrics }: Props) => {
this.setState({ loading: true });
const filteredKeys = getDisplayMetrics(Object.values(metrics)).map(metric => metric.key);
+
fetchMeasures(component.key, filteredKeys, branchLike).then(
({ measures, leakPeriod }) => {
if (this.mounted) {
this.setState({
loading: false,
leakPeriod,
- measures: measures.filter(measure => measure.value != null || measure.leak != null)
+ measures: measures.filter(
+ measure => measure.value !== undefined || measure.leak !== undefined
+ )
});
}
},
@@ -138,7 +139,7 @@ export default class App extends React.PureComponent {
);
};
- updateQuery = (newQuery /*: Query */) => {
+ updateQuery = (newQuery: Partial<Query>) => {
const query = serializeQuery({
...parseQuery(this.props.location.query),
...newQuery
@@ -153,19 +154,16 @@ export default class App extends React.PureComponent {
});
};
- getHelmetTitle = (
- metric /*: Metric */,
- query /*: {metric: string, selected: string, view: string }*/
- ) => {
- if (metric == null && hasBubbleChart(query.metric)) {
- return isProjectOverview(query.metric)
+ getHelmetTitle = (metric?: Metric) => {
+ if (metric && hasBubbleChart(metric.key)) {
+ return isProjectOverview(metric.key)
? translate('component_measures.overview.project_overview.facet')
: translateWithParameters(
'component_measures.domain_x_overview',
- getLocalizedMetricDomain(query.metric)
+ getLocalizedMetricDomain(metric.key)
);
}
- return metric != null ? metric.name : translate('layout.measures');
+ return metric ? metric.name : translate('layout.measures');
};
render() {
@@ -180,7 +178,7 @@ export default class App extends React.PureComponent {
return (
<div className="layout-page" id="component-measures">
<Suggestions suggestions="component_measures" />
- <Helmet title={this.getHelmetTitle(metric, query)} />
+ <Helmet title={this.getHelmetTitle(metric)} />
<ScreenPositionHelper className="layout-page-side-outer">
{({ top }) => (
@@ -198,7 +196,7 @@ export default class App extends React.PureComponent {
)}
</ScreenPositionHelper>
- {metric != null && (
+ {metric && (
<MeasureContentContainer
branchLike={branchLike}
className="layout-page-main"
@@ -214,7 +212,7 @@ export default class App extends React.PureComponent {
view={query.view}
/>
)}
- {metric == null &&
+ {!metric &&
hasBubbleChart(query.metric) && (
<MeasureOverviewContainer
branchLike={branchLike}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/AppContainer.js b/server/sonar-web/src/main/js/apps/component-measures/components/AppContainer.tsx
index b02d379358c..86c347c7be4 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/AppContainer.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/AppContainer.tsx
@@ -17,9 +17,9 @@
* 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 { Dispatch } from 'redux';
import { connect } from 'react-redux';
-import { withRouter } from 'react-router';
+import { withRouter, WithRouterProps } from 'react-router';
import App from './App';
import throwGlobalError from '../../../app/utils/throwGlobalError';
import { getCurrentUser, getMetrics, getMetricsKey } from '../../../store/rootReducer';
@@ -28,31 +28,57 @@ import { getMeasuresAndMeta } from '../../../api/measures';
import { getLeakPeriod } from '../../../helpers/periods';
import { enhanceMeasure } from '../../../components/measure/utils';
import { getBranchLikeQuery } from '../../../helpers/branches';
-/*:: import type { Component, Period } from '../types'; */
-/*:: import type { Measure, MeasureEnhanced } from '../../../components/measure/types'; */
+import {
+ BranchLike,
+ ComponentMeasure,
+ CurrentUser,
+ Measure,
+ MeasureEnhanced,
+ Metric,
+ Period
+} from '../../../app/types';
-const mapStateToProps = state => ({
+interface StateToProps {
+ currentUser: CurrentUser;
+ metrics: { [metric: string]: Metric };
+ metricsKey: string[];
+}
+
+interface DispatchToProps {
+ fetchMeasures: (
+ component: string,
+ metricsKey: string[],
+ branchLike?: BranchLike
+ ) => Promise<{ component: ComponentMeasure; measures: MeasureEnhanced[]; leakPeriod?: Period }>;
+ fetchMetrics: () => void;
+}
+
+interface OwnProps {
+ branchLike?: BranchLike;
+ component: ComponentMeasure;
+}
+
+const mapStateToProps = (state: any): StateToProps => ({
currentUser: getCurrentUser(state),
metrics: getMetrics(state),
metricsKey: getMetricsKey(state)
});
-function banQualityGate(component /*: Component */) /*: Array<Measure> */ {
- const bannedMetrics = [];
- if (!['VW', 'SVW'].includes(component.qualifier)) {
+function banQualityGate({ measures = [], qualifier }: ComponentMeasure): Measure[] {
+ const bannedMetrics: string[] = [];
+ if (!['VW', 'SVW'].includes(qualifier)) {
bannedMetrics.push('alert_status');
}
- if (component.qualifier === 'APP') {
+ if (qualifier === 'APP') {
bannedMetrics.push('releasability_rating', 'releasability_effort');
}
- return component.measures.filter(measure => !bannedMetrics.includes(measure.metric));
+ return measures.filter(measure => !bannedMetrics.includes(measure.metric));
}
-const fetchMeasures = (
- component /*: string */,
- metricsKey /*: Array<string> */,
- branchLike /*: { id?: string; name: string } | void */
-) => (dispatch, getState) => {
+const fetchMeasures = (component: string, metricsKey: string[], branchLike?: BranchLike) => (
+ _dispatch: Dispatch<any>,
+ getState: () => any
+) => {
if (metricsKey.length <= 0) {
return Promise.resolve({ component: {}, measures: [], leakPeriod: null });
}
@@ -60,21 +86,23 @@ const fetchMeasures = (
return getMeasuresAndMeta(component, metricsKey, {
additionalFields: 'periods',
...getBranchLikeQuery(branchLike)
- }).then(r => {
- const measures = banQualityGate(r.component).map(measure =>
+ }).then(({ component, periods }) => {
+ const measures = banQualityGate(component).map(measure =>
enhanceMeasure(measure, getMetrics(getState()))
);
const newBugs = measures.find(measure => measure.metric.key === 'new_bugs');
- const applicationPeriods = newBugs ? [{ index: 1 }] : [];
- const periods = r.component.qualifier === 'APP' ? applicationPeriods : r.periods;
- return { component: r.component, measures, leakPeriod: getLeakPeriod(periods) };
+ const applicationPeriods = newBugs ? [{ index: 1 } as Period] : [];
+ const leakPeriod = getLeakPeriod(component.qualifier === 'APP' ? applicationPeriods : periods);
+ return { component, measures, leakPeriod };
}, throwGlobalError);
};
-const mapDispatchToProps = { fetchMeasures, fetchMetrics };
+const mapDispatchToProps: DispatchToProps = { fetchMeasures: fetchMeasures as any, fetchMetrics };
-export default connect(
- mapStateToProps,
- mapDispatchToProps
-)(withRouter(App));
+export default withRouter<OwnProps>(
+ connect<StateToProps, DispatchToProps, OwnProps & WithRouterProps>(
+ mapStateToProps,
+ mapDispatchToProps
+ )(App)
+);
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/FilesCounter.js b/server/sonar-web/src/main/js/apps/component-measures/components/FilesCounter.tsx
index 94ab8b016f9..37e794a6ee8 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/FilesCounter.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/FilesCounter.tsx
@@ -17,22 +17,21 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import { translate } from '../../../helpers/l10n';
import { formatMeasure } from '../../../helpers/measures';
-/*:: type Props = {
- className?: string,
- current: ?number,
- total: number
-}; */
+interface Props {
+ className?: string;
+ current?: number;
+ total: number;
+}
-export default function FilesCounter({ className, current, total } /*: Props */) {
+export default function FilesCounter({ className, current, total }: Props) {
return (
<span className={className}>
<strong>
- {current != null && (
+ {current !== undefined && (
<span>
{formatMeasure(current, 'INT')}
{' / '}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/LeakPeriodLegend.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/LeakPeriodLegend.tsx
index 8c61f4e87b7..f1d8747a2c8 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/LeakPeriodLegend.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/LeakPeriodLegend.tsx
@@ -24,10 +24,10 @@ import DateFromNow from '../../../components/intl/DateFromNow';
import DateFormatter, { longFormatterOption } from '../../../components/intl/DateFormatter';
import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
import Tooltip from '../../../components/controls/Tooltip';
-import { getPeriodLabel, getPeriodDate, Period, PeriodMode } from '../../../helpers/periods';
+import { getPeriodLabel, getPeriodDate } from '../../../helpers/periods';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { differenceInDays } from '../../../helpers/dates';
-import { ComponentMeasure } from '../../../app/types';
+import { ComponentMeasure, Period, PeriodMode } from '../../../app/types';
interface Props {
className?: string;
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx
index 9472307bc7e..7eee54217b6 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx
@@ -44,10 +44,10 @@ import {
isLoggedIn,
Metric,
Paging,
- MeasureEnhanced
+ MeasureEnhanced,
+ Period
} from '../../../app/types';
import { RequestData } from '../../../helpers/request';
-import { Period } from '../../../helpers/periods';
interface Props {
branchLike?: BranchLike;
@@ -328,10 +328,8 @@ export default class MeasureContent extends React.PureComponent<Props, State> {
<MeasureHeader
branchLike={branchLike}
component={component}
- components={this.state.components}
leakPeriod={this.props.leakPeriod}
- // fall back to `undefined` to be compatible with typescript files where we compare with `=== undefined`
- measure={measure || undefined}
+ measure={measure}
metric={metric}
secondaryMeasure={this.props.secondaryMeasure}
/>
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.tsx
index 3c3a636538f..c8a1301038d 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.tsx
@@ -26,9 +26,9 @@ import {
Metric,
BranchLike,
CurrentUser,
- MeasureEnhanced
+ MeasureEnhanced,
+ Period
} from '../../../app/types';
-import { Period } from '../../../helpers/periods';
interface Props {
branchLike?: BranchLike;
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.js b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.tsx
index c39214fba0b..61840e12d62 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.tsx
@@ -17,8 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import { Link } from 'react-router';
import LeakPeriodLegend from './LeakPeriodLegend';
import HistoryIcon from '../../../components/icons-components/HistoryIcon';
@@ -29,21 +28,18 @@ import Tooltip from '../../../components/controls/Tooltip';
import { getLocalizedMetricName, translate } from '../../../helpers/l10n';
import { getMeasureHistoryUrl } from '../../../helpers/urls';
import { isDiffMetric } from '../../../helpers/measures';
-/*:: import type { Component, Period } from '../types'; */
-/*:: import type { MeasureEnhanced } from '../../../components/measure/types'; */
-/*:: import type { Metric } from '../../../app/flow-types'; */
+import { MeasureEnhanced, Metric, ComponentMeasure, BranchLike, Period } from '../../../app/types';
-/*:: type Props = {|
- branchLike?: { id?: string; name: string },
- component: Component,
- components: Array<Component>,
- leakPeriod?: Period,
- measure?: MeasureEnhanced,
- metric: Metric,
- secondaryMeasure: ?MeasureEnhanced
-|}; */
+interface Props {
+ branchLike?: BranchLike;
+ component: ComponentMeasure;
+ leakPeriod?: Period;
+ measure?: MeasureEnhanced;
+ metric: Metric;
+ secondaryMeasure?: MeasureEnhanced;
+}
-export default function MeasureHeader(props /*: Props*/) {
+export default function MeasureHeader(props: Props) {
const { branchLike, component, leakPeriod, measure, metric, secondaryMeasure } = props;
const isDiff = isDiffMetric(metric.key);
const hasHistory = component.qualifier !== 'FIL' && component.qualifier !== 'UTS';
@@ -83,13 +79,14 @@ export default function MeasureHeader(props /*: Props*/) {
)}
</div>
<div className="measure-details-primary-actions">
- {leakPeriod != null && (
+ {leakPeriod && (
<LeakPeriodLegend className="spacer-left" component={component} period={leakPeriod} />
)}
</div>
</div>
{secondaryMeasure &&
- secondaryMeasure.metric.key === 'ncloc_language_distribution' && (
+ secondaryMeasure.metric.key === 'ncloc_language_distribution' &&
+ secondaryMeasure.value !== undefined && (
<div className="measure-details-secondary">
<LanguageDistributionContainer
alignTicks={true}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.js b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx
index 495626eaede..18c34ea5d4a 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx
@@ -17,8 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import Breadcrumbs from './Breadcrumbs';
import LeakPeriodLegend from './LeakPeriodLegend';
import MeasureFavoriteContainer from './MeasureFavoriteContainer';
@@ -29,44 +28,47 @@ import { getComponentLeaves } from '../../../api/components';
import { enhanceComponent, getBubbleMetrics, isFileType } from '../utils';
import { getBranchLikeQuery } from '../../../helpers/branches';
import DeferredSpinner from '../../../components/common/DeferredSpinner';
-/*:: import type { Component, ComponentEnhanced, Paging, Period } from '../types'; */
-/*:: import type { Metric } from '../../../app/flow-types'; */
+import {
+ BranchLike,
+ ComponentMeasure,
+ ComponentMeasureEnhanced,
+ CurrentUser,
+ Metric,
+ Paging,
+ Period
+} from '../../../app/types';
-/*:: type Props = {|
- branchLike?: { id?: string; name: string },
- className?: string,
- component: Component,
- currentUser: { isLoggedIn: boolean },
- domain: string,
- leakPeriod: Period,
- loading: boolean,
- metrics: { [string]: Metric },
- rootComponent: Component,
- updateLoading: ({ [string]: boolean }) => void,
- updateSelected: string => void
-|}; */
+interface Props {
+ branchLike?: BranchLike;
+ className?: string;
+ component: ComponentMeasure;
+ currentUser: CurrentUser;
+ domain: string;
+ leakPeriod?: Period;
+ loading: boolean;
+ metrics: { [metric: string]: Metric };
+ rootComponent: ComponentMeasure;
+ updateLoading: (param: { [key: string]: boolean }) => void;
+ updateSelected: (component: string) => void;
+}
-/*:: type State = {
- components: Array<ComponentEnhanced>,
- paging?: Paging
-}; */
+interface State {
+ components: ComponentMeasureEnhanced[];
+ paging?: Paging;
+}
const BUBBLES_LIMIT = 500;
-export default class MeasureOverview extends React.PureComponent {
- /*:: mounted: boolean; */
- /*:: props: Props; */
- state /*: State */ = {
- components: [],
- paging: null
- };
+export default class MeasureOverview extends React.PureComponent<Props, State> {
+ mounted = false;
+ state: State = { components: [] };
componentDidMount() {
this.mounted = true;
this.fetchComponents(this.props);
}
- componentWillReceiveProps(nextProps /*: Props */) {
+ componentWillReceiveProps(nextProps: Props) {
if (
nextProps.component !== this.props.component ||
nextProps.metrics !== this.props.metrics ||
@@ -80,16 +82,16 @@ export default class MeasureOverview extends React.PureComponent {
this.mounted = false;
}
- fetchComponents = (props /*: Props */) => {
+ fetchComponents = (props: Props) => {
const { branchLike, component, domain, metrics } = props;
if (isFileType(component)) {
- this.setState({ components: [], paging: null });
+ this.setState({ components: [], paging: undefined });
return;
}
const { x, y, size, colors } = getBubbleMetrics(domain, metrics);
const metricsKey = [x.key, y.key, size.key];
if (colors) {
- metricsKey.push(colors.map(metric => metric.key));
+ metricsKey.push(...colors.map(metric => metric.key));
}
const options = {
...getBranchLikeQuery(branchLike),
@@ -105,7 +107,9 @@ export default class MeasureOverview extends React.PureComponent {
if (domain === this.props.domain) {
if (this.mounted) {
this.setState({
- components: r.components.map(component => enhanceComponent(component, null, metrics)),
+ components: r.components.map(component =>
+ enhanceComponent(component, undefined, metrics)
+ ),
paging: r.paging
});
}
@@ -171,7 +175,7 @@ export default class MeasureOverview extends React.PureComponent {
</div>
<div className="layout-page-main-inner measure-details-content">
<div className="clearfix big-spacer-bottom">
- {leakPeriod != null && (
+ {leakPeriod && (
<LeakPeriodLegend className="pull-right" component={component} period={leakPeriod} />
)}
</div>
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverviewContainer.js b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverviewContainer.tsx
index db4caed4947..642cde71e20 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverviewContainer.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverviewContainer.tsx
@@ -17,49 +17,43 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
+import { InjectedRouter } from 'react-router';
import MeasureOverview from './MeasureOverview';
import { getComponentShow } from '../../../api/components';
import { getProjectUrl } from '../../../helpers/urls';
-import { isViewType } from '../utils';
+import { isViewType, Query } from '../utils';
import { getBranchLikeQuery } from '../../../helpers/branches';
-/*:: import type { Component, Period, Query } from '../types'; */
-/*:: import type { RawQuery } from '../../../helpers/query'; */
-/*:: import type { Metric } from '../../../app/flow-types'; */
+import { BranchLike, ComponentMeasure, CurrentUser, Metric, Period } from '../../../app/types';
-/*:: type Props = {|
- branchLike?: { id?: string; name: string },
- className?: string,
- rootComponent: Component,
- currentUser: { isLoggedIn: boolean },
- domain: string,
- leakPeriod: Period,
- metrics: { [string]: Metric },
- router: {
- push: ({ pathname: string, query?: RawQuery }) => void
- },
- selected: ?string,
- updateQuery: Query => void
-|}; */
+interface Props {
+ branchLike?: BranchLike;
+ className?: string;
+ currentUser: CurrentUser;
+ domain: string;
+ leakPeriod?: Period;
+ metrics: { [metric: string]: Metric };
+ rootComponent: ComponentMeasure;
+ router: InjectedRouter;
+ selected?: string;
+ updateQuery: (query: Partial<Query>) => void;
+}
-/*:: type State = {
- component: ?Component,
- loading: {
- component: boolean,
- bubbles: boolean
- }
-}; */
+interface LoadingState {
+ bubbles: boolean;
+ component: boolean;
+}
-export default class MeasureOverviewContainer extends React.PureComponent {
- /*:: mounted: boolean; */
- /*:: props: Props; */
- state /*: State */ = {
- component: null,
- loading: {
- component: false,
- bubbles: false
- }
+interface State {
+ component?: ComponentMeasure;
+ loading: LoadingState;
+}
+
+export default class MeasureOverviewContainer extends React.PureComponent<Props, State> {
+ mounted = false;
+
+ state: State = {
+ loading: { bubbles: false, component: false }
};
componentDidMount() {
@@ -67,7 +61,7 @@ export default class MeasureOverviewContainer extends React.PureComponent {
this.fetchComponent(this.props);
}
- componentWillReceiveProps(nextProps /*: Props */) {
+ componentWillReceiveProps(nextProps: Props) {
const { component } = this.state;
const componentChanged =
!component ||
@@ -82,7 +76,7 @@ export default class MeasureOverviewContainer extends React.PureComponent {
this.mounted = false;
}
- fetchComponent = ({ branchLike, rootComponent, selected } /*: Props */) => {
+ fetchComponent = ({ branchLike, rootComponent, selected }: Props) => {
if (!selected || rootComponent.key === selected) {
this.setState({ component: rootComponent });
this.updateLoading({ component: false });
@@ -100,18 +94,18 @@ export default class MeasureOverviewContainer extends React.PureComponent {
);
};
- updateLoading = (loading /*: { [string]: boolean } */) => {
+ updateLoading = (loading: Partial<LoadingState>) => {
if (this.mounted) {
this.setState(state => ({ loading: { ...state.loading, ...loading } }));
}
};
- updateSelected = (component /*: string */) => {
+ updateSelected = (component: string) => {
if (this.state.component && isViewType(this.state.component)) {
this.props.router.push(getProjectUrl(component));
} else {
this.props.updateQuery({
- selected: component !== this.props.rootComponent.key ? component : null
+ selected: component !== this.props.rootComponent.key ? component : undefined
});
}
};
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MetricNotFound.js b/server/sonar-web/src/main/js/apps/component-measures/components/MetricNotFound.tsx
index e51270786d1..6009d74a6c9 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/MetricNotFound.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/MetricNotFound.tsx
@@ -17,11 +17,10 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import { translate } from '../../../helpers/l10n';
-export default function MetricNotFound({ className } /*: { className?: string } */) {
+export default function MetricNotFound({ className }: { className?: string }) {
return (
<div className={className}>
<div className="alert alert-danger">{translate('component_measures.not_found')}</div>
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/PageActions.js b/server/sonar-web/src/main/js/apps/component-measures/components/PageActions.tsx
index 4b26994fa81..75078edb21a 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/PageActions.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/PageActions.tsx
@@ -17,23 +17,22 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import FilesCounter from './FilesCounter';
import { translate } from '../../../helpers/l10n';
-/*:: import type { Paging } from '../types'; */
+import { Paging } from '../../../app/types';
-/*:: type Props = {|
- current: ?number,
- isFile: ?boolean,
- paging: ?Paging,
- totalLoadedComponents?: number,
- view?: string
-|}; */
+interface Props {
+ current?: number;
+ isFile?: boolean;
+ paging?: Paging;
+ totalLoadedComponents?: number;
+ view?: string;
+}
-export default function PageActions(props /*: Props */) {
+export default function PageActions(props: Props) {
const { isFile, paging, totalLoadedComponents } = props;
- const showShortcuts = ['list', 'tree'].includes(props.view);
+ const showShortcuts = props.view && ['list', 'tree'].includes(props.view);
return (
<div className="pull-right">
{!isFile && showShortcuts && renderShortcuts()}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.js b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.tsx
index 5b19497408a..f0202595073 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.tsx
@@ -17,36 +17,42 @@
* 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';
+/* eslint-disable camelcase */
+import * as React from 'react';
import { shallow } from 'enzyme';
import App from '../App';
+const COMPONENT = { key: 'foo', name: 'Foo', qualifier: 'TRK' };
+
const METRICS = {
lines_to_cover: {
+ id: '1',
key: 'lines_to_cover',
type: 'INT',
name: 'Lines to Cover',
domain: 'Coverage'
},
- coverage: { key: 'coverage', type: 'PERCENT', name: 'Coverage', domain: 'Coverage' },
+ coverage: { id: '2', key: 'coverage', type: 'PERCENT', name: 'Coverage', domain: 'Coverage' },
duplicated_lines_density: {
+ id: '3',
key: 'duplicated_lines_density',
type: 'PERCENT',
name: 'Duplicated Lines (%)',
domain: 'Duplications'
},
- new_bugs: { key: 'new_bugs', type: 'INT', name: 'New Bugs', domain: 'Reliability' }
+ new_bugs: { id: '4', key: 'new_bugs', type: 'INT', name: 'New Bugs', domain: 'Reliability' }
};
const PROPS = {
branch: { isMain: true, name: 'master' },
- component: { key: 'foo' },
+ component: COMPONENT,
+ currentUser: { isLoggedIn: false },
location: { pathname: '/component_measures', query: { metric: 'coverage' } },
- fetchMeasures: () => Promise.resolve({ measures: [] }),
- fetchMetrics: () => {},
+ fetchMeasures: jest.fn().mockResolvedValue({ component: COMPONENT, measures: [] }),
+ fetchMetrics: jest.fn(),
metrics: METRICS,
metricsKey: ['lines_to_cover', 'coverage', 'duplicated_lines_density', 'new_bugs'],
- router: { push: () => {} }
+ router: { push: jest.fn() } as any
};
it('should render correctly', () => {
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/FilesCounter-test.js b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/FilesCounter-test.tsx
index 1cedf4a4238..8d02bc65a1c 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/FilesCounter-test.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/FilesCounter-test.tsx
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import React from 'react';
+import * as React from 'react';
import { shallow } from 'enzyme';
import FilesCounter from '../FilesCounter';
@@ -26,5 +26,5 @@ it('should display x files on y total', () => {
});
it('should display only total of files', () => {
- expect(shallow(<FilesCounter current={null} total={123455} />)).toMatchSnapshot();
+ expect(shallow(<FilesCounter current={undefined} total={123455} />)).toMatchSnapshot();
});
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/LeakPeriodLegend-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/LeakPeriodLegend-test.tsx
index 475b6e17bc6..be616b62017 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/LeakPeriodLegend-test.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/LeakPeriodLegend-test.tsx
@@ -20,9 +20,8 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import LeakPeriodLegend from '../LeakPeriodLegend';
-import { PeriodMode, Period } from '../../../../helpers/periods';
import { differenceInDays } from '../../../../helpers/dates';
-import { ComponentMeasure } from '../../../../app/types';
+import { ComponentMeasure, Period, PeriodMode } from '../../../../app/types';
jest.mock('../../../../helpers/dates', () => {
const dates = require.requireActual('../../../../helpers/dates');
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureHeader-test.js b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureHeader-test.tsx
index 5582f01e151..965af1b5474 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureHeader-test.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureHeader-test.tsx
@@ -17,11 +17,13 @@
* 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 * as React from 'react';
import { shallow } from 'enzyme';
import MeasureHeader from '../MeasureHeader';
+import { PeriodMode } from '../../../../app/types';
const METRIC = {
+ id: '1',
key: 'reliability_rating',
type: 'RATING',
name: 'Reliability Rating'
@@ -35,6 +37,7 @@ const MEASURE = {
};
const LEAK_METRIC = {
+ id: '2',
key: 'new_reliability_rating',
type: 'RATING',
name: 'Reliability Rating on New Code'
@@ -49,28 +52,23 @@ const LEAK_MEASURE = {
const SECONDARY = {
value: 'java=175123;js=26382',
metric: {
+ id: '3',
key: 'ncloc_language_distribution',
type: 'DATA',
name: 'Lines of Code Per Language'
- },
- leak: null
+ }
};
const PROPS = {
- component: { key: 'foo', qualifier: 'TRK' },
- components: [],
- handleSelect: () => {},
+ component: { key: 'foo', name: 'Foo', qualifier: 'TRK' },
leakPeriod: {
date: '2017-05-16T13:50:02+0200',
index: 1,
- mode: 'previous_version',
+ mode: PeriodMode.PreviousVersion,
parameter: '6,4'
},
measure: MEASURE,
- metric: METRIC,
- paging: null,
- secondaryMeasure: null,
- selectedIdx: null
+ metric: METRIC
};
it('should render correctly', () => {
@@ -109,20 +107,6 @@ it('should display secondary measure too', () => {
expect(wrapper.find('Connect(LanguageDistribution)')).toHaveLength(1);
});
-it('should display correctly for open file', () => {
- const wrapper = shallow(
- <MeasureHeader
- {...PROPS}
- component={{ key: 'bar', qualifier: 'FIL' }}
- components={[{ key: 'foo' }, { key: 'bar' }, { key: 'baz' }]}
- selectedIdx={1}
- />
- );
- expect(wrapper.find('.measure-details-primary-actions')).toMatchSnapshot();
- wrapper.setProps({ components: [{ key: 'foo' }, { key: 'bar' }] });
- expect(wrapper.find('.measure-details-primary-actions')).toMatchSnapshot();
-});
-
it('should work with measure without value', () => {
expect(shallow(<MeasureHeader {...PROPS} measure={undefined} />)).toMatchSnapshot();
});
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/PageActions-test.js b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/PageActions-test.tsx
index 067ecb4d78d..016e36de105 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/PageActions-test.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/PageActions-test.tsx
@@ -17,10 +17,16 @@
* 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 * as React from 'react';
import { shallow } from 'enzyme';
import PageActions from '../PageActions';
+const PAGING = {
+ pageIndex: 1,
+ pageSize: 100,
+ total: 120
+};
+
it('should display correctly for a project', () => {
expect(
shallow(<PageActions isFile={false} totalLoadedComponents={20} view="list" />)
@@ -46,7 +52,7 @@ it('should display the total of files', () => {
<PageActions
current={12}
isFile={false}
- paging={{ total: 120 }}
+ paging={PAGING}
totalLoadedComponents={20}
view="treemap"
/>
@@ -57,7 +63,7 @@ it('should display the total of files', () => {
<PageActions
current={12}
isFile={true}
- paging={{ total: 120 }}
+ paging={PAGING}
totalLoadedComponents={20}
view="list"
/>
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.js.snap b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.tsx.snap
index 70583de970f..b0436ae2851 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.tsx.snap
@@ -18,11 +18,37 @@ exports[`should render correctly 1`] = `
/>
<MeasureContentContainer
className="layout-page-main"
- fetchMeasures={[Function]}
- leakPeriod={null}
+ currentUser={
+ Object {
+ "isLoggedIn": false,
+ }
+ }
+ fetchMeasures={
+ [MockFunction] {
+ "calls": Array [
+ Array [
+ "foo",
+ Array [
+ "lines_to_cover",
+ "coverage",
+ "duplicated_lines_density",
+ "new_bugs",
+ ],
+ undefined,
+ ],
+ ],
+ "results": Array [
+ Object {
+ "isThrow": false,
+ "value": Promise {},
+ },
+ ],
+ }
+ }
metric={
Object {
"domain": "Coverage",
+ "id": "2",
"key": "coverage",
"name": "Coverage",
"type": "PERCENT",
@@ -32,24 +58,28 @@ exports[`should render correctly 1`] = `
Object {
"coverage": Object {
"domain": "Coverage",
+ "id": "2",
"key": "coverage",
"name": "Coverage",
"type": "PERCENT",
},
"duplicated_lines_density": Object {
"domain": "Duplications",
+ "id": "3",
"key": "duplicated_lines_density",
"name": "Duplicated Lines (%)",
"type": "PERCENT",
},
"lines_to_cover": Object {
"domain": "Coverage",
+ "id": "1",
"key": "lines_to_cover",
"name": "Lines to Cover",
"type": "INT",
},
"new_bugs": Object {
"domain": "Reliability",
+ "id": "4",
"key": "new_bugs",
"name": "New Bugs",
"type": "INT",
@@ -59,11 +89,13 @@ exports[`should render correctly 1`] = `
rootComponent={
Object {
"key": "foo",
+ "name": "Foo",
+ "qualifier": "TRK",
}
}
router={
Object {
- "push": [Function],
+ "push": [MockFunction],
}
}
selected=""
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/FilesCounter-test.js.snap b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/FilesCounter-test.tsx.snap
index bb01a6121da..bb01a6121da 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/FilesCounter-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/FilesCounter-test.tsx.snap
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureHeader-test.js.snap b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureHeader-test.tsx.snap
index 49f756bc4a6..2ccafb98328 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureHeader-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureHeader-test.tsx.snap
@@ -1,53 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`should display correctly for open file 1`] = `
-<div
- className="measure-details-primary-actions"
->
- <LeakPeriodLegend
- className="spacer-left"
- component={
- Object {
- "key": "bar",
- "qualifier": "FIL",
- }
- }
- period={
- Object {
- "date": "2017-05-16T13:50:02+0200",
- "index": 1,
- "mode": "previous_version",
- "parameter": "6,4",
- }
- }
- />
-</div>
-`;
-
-exports[`should display correctly for open file 2`] = `
-<div
- className="measure-details-primary-actions"
->
- <LeakPeriodLegend
- className="spacer-left"
- component={
- Object {
- "key": "bar",
- "qualifier": "FIL",
- }
- }
- period={
- Object {
- "date": "2017-05-16T13:50:02+0200",
- "index": 1,
- "mode": "previous_version",
- "parameter": "6,4",
- }
- }
- />
-</div>
-`;
-
exports[`should render correctly 1`] = `
<div
className="measure-details-header big-spacer-bottom"
@@ -104,6 +56,7 @@ exports[`should render correctly 1`] = `
component={
Object {
"key": "foo",
+ "name": "Foo",
"qualifier": "TRK",
}
}
@@ -157,6 +110,7 @@ exports[`should render correctly for leak 1`] = `
component={
Object {
"key": "foo",
+ "name": "Foo",
"qualifier": "TRK",
}
}
@@ -250,6 +204,7 @@ exports[`should work with measure without value 1`] = `
component={
Object {
"key": "foo",
+ "name": "Foo",
"qualifier": "TRK",
}
}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/PageActions-test.js.snap b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/PageActions-test.tsx.snap
index 9849310b4c2..9849310b4c2 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/PageActions-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/PageActions-test.tsx.snap
diff --git a/server/sonar-web/src/main/js/apps/component-measures/config/bubbles.ts b/server/sonar-web/src/main/js/apps/component-measures/config/bubbles.ts
index 99c1b0e8e8c..0b432457a90 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/config/bubbles.ts
+++ b/server/sonar-web/src/main/js/apps/component-measures/config/bubbles.ts
@@ -23,7 +23,7 @@ export const bubbles: {
y: string;
size: string;
colors?: string[];
- yDomain?: number[];
+ yDomain?: [number, number];
};
} = {
Reliability: {
diff --git a/server/sonar-web/src/main/js/apps/component-measures/config/domains.ts b/server/sonar-web/src/main/js/apps/component-measures/config/domains.ts
index d7022b680b4..fe45dd98450 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/config/domains.ts
+++ b/server/sonar-web/src/main/js/apps/component-measures/config/domains.ts
@@ -17,7 +17,12 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-export const domains: { [domain: string]: { categories?: string[]; order: string[] } } = {
+
+interface Domains {
+ [domain: string]: { categories?: string[]; order: string[] };
+}
+
+export const domains: Domains = {
Reliability: {
categories: ['new_code_category', 'overall_category'],
order: [
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/BubbleChart.js b/server/sonar-web/src/main/js/apps/component-measures/drilldown/BubbleChart.tsx
index 7a6fe3264ed..0c07b5a249f 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/BubbleChart.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/BubbleChart.tsx
@@ -17,10 +17,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import EmptyResult from './EmptyResult';
-import OriginalBubbleChart from '../../../components/charts/BubbleChart';
+import OriginalBubbleChart, { BubbleItem } from '../../../components/charts/BubbleChart';
import ColorRatingsLegend from '../../../components/charts/ColorRatingsLegend';
import HelpTooltip from '../../../components/controls/HelpTooltip';
import { formatMeasure, isDiffMetric } from '../../../helpers/measures';
@@ -32,43 +31,31 @@ import {
} from '../../../helpers/l10n';
import { getBubbleMetrics, getBubbleYDomain, isProjectOverview } from '../utils';
import { RATING_COLORS } from '../../../helpers/constants';
-/*:: import type { Component, ComponentEnhanced } from '../types'; */
-/*:: import type { Metric } from '../../../app/flow-types'; */
+import { ComponentMeasure, ComponentMeasureEnhanced, Metric } from '../../../app/types';
const HEIGHT = 500;
-/*:: type Props = {|
- component: Component,
- components: Array<ComponentEnhanced>,
- domain: string,
- metrics: { [string]: Metric },
- updateSelected: string => void
-|}; */
-
-export default class BubbleChart extends React.PureComponent {
- /*:: props: Props; */
+interface Props {
+ component: ComponentMeasure;
+ components: ComponentMeasureEnhanced[];
+ domain: string;
+ metrics: { [metric: string]: Metric };
+ updateSelected: (component: string) => void;
+}
- getMeasureVal = (component /*: ComponentEnhanced */, metric /*: Metric */) => {
+export default class BubbleChart extends React.PureComponent<Props> {
+ getMeasureVal = (component: ComponentMeasureEnhanced, metric: Metric) => {
const measure = component.measures.find(measure => measure.metric.key === metric.key);
- if (measure) {
- return Number(isDiffMetric(metric.key) ? measure.leak : measure.value);
+ if (!measure) {
+ return undefined;
}
+ return Number(isDiffMetric(metric.key) ? measure.leak : measure.value);
};
getTooltip(
- componentName /*: string */,
- values /*: {
- x: number,
- y: number,
- size: number,
- colors: ?Array<?number>
- }*/,
- metrics /*: {
- x: Metric ,
- y: Metric ,
- size: Metric ,
- colors: ?Array<Metric>
- }*/
+ componentName: string,
+ values: { x: number; y: number; size: number; colors?: Array<number | undefined> },
+ metrics: { x: Metric; y: Metric; size: Metric; colors?: Array<Metric> }
) {
const inner = [
componentName,
@@ -76,10 +63,11 @@ export default class BubbleChart extends React.PureComponent {
`${metrics.y.name}: ${formatMeasure(values.y, metrics.y.type)}`,
`${metrics.size.name}: ${formatMeasure(values.size, metrics.size.type)}`
];
- if (values.colors && metrics.colors) {
- metrics.colors.forEach((metric, idx) => {
- // $FlowFixMe colors is always defined at this point
- const colorValue = values.colors[idx];
+ const { colors: valuesColors } = values;
+ const { colors: metricColors } = metrics;
+ if (valuesColors && metricColors) {
+ metricColors.forEach((metric, idx) => {
+ const colorValue = valuesColors[idx];
if (colorValue || colorValue === 0) {
inner.push(`${metric.name}: ${formatMeasure(colorValue, metric.type)}`);
}
@@ -97,10 +85,10 @@ export default class BubbleChart extends React.PureComponent {
);
}
- handleBubbleClick = (component /*: ComponentEnhanced */) =>
+ handleBubbleClick = (component: ComponentMeasureEnhanced) =>
this.props.updateSelected(component.refKey || component.key);
- getDescription(domain /*: string */) {
+ getDescription(domain: string) {
const description = `component_measures.overview.${domain}.description`;
const translatedDescription = translate(description);
if (description === translatedDescription) {
@@ -109,14 +97,7 @@ export default class BubbleChart extends React.PureComponent {
return translatedDescription;
}
- renderBubbleChart(
- metrics /*: {
- x: Metric ,
- y: Metric ,
- size: Metric ,
- colors: ?Array<Metric>
- }*/
- ) {
+ renderBubbleChart(metrics: { x: Metric; y: Metric; size: Metric; colors?: Metric[] }) {
const items = this.props.components
.map(component => {
const x = this.getMeasureVal(component, metrics.x);
@@ -125,25 +106,27 @@ export default class BubbleChart extends React.PureComponent {
const colors =
metrics.colors && metrics.colors.map(metric => this.getMeasureVal(component, metric));
if ((!x && x !== 0) || (!y && y !== 0) || (!size && size !== 0)) {
- return null;
+ return undefined;
}
return {
x,
y,
size,
color:
- colors != null ? RATING_COLORS[Math.max(...colors.filter(Boolean)) - 1] : undefined,
- link: component,
+ colors !== undefined
+ ? RATING_COLORS[Math.max(...colors.filter(Boolean) as number[]) - 1]
+ : undefined,
+ data: component,
tooltip: this.getTooltip(component.name, { x, y, size, colors }, metrics)
};
})
- .filter(Boolean);
+ .filter(Boolean) as BubbleItem<ComponentMeasureEnhanced>[];
- const formatXTick = tick => formatMeasure(tick, metrics.x.type);
- const formatYTick = tick => formatMeasure(tick, metrics.y.type);
+ const formatXTick = (tick: string | number | undefined) => formatMeasure(tick, metrics.x.type);
+ const formatYTick = (tick: string | number | undefined) => formatMeasure(tick, metrics.y.type);
return (
- <OriginalBubbleChart
+ <OriginalBubbleChart<ComponentMeasureEnhanced>
formatXTick={formatXTick}
formatYTick={formatYTick}
height={HEIGHT}
@@ -155,11 +138,7 @@ export default class BubbleChart extends React.PureComponent {
);
}
- renderChartHeader(
- domain /*: string */,
- sizeMetric /*: Metric */,
- colorsMetric /*: ?Array<Metric> */
- ) {
+ renderChartHeader(domain: string, sizeMetric: Metric, colorsMetric?: Metric[]) {
const title = isProjectOverview(domain)
? translate('component_measures.overview', domain, 'title')
: translateWithParameters(
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/CodeView.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/CodeView.tsx
index 6f74390695b..a07eb4710c2 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/CodeView.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/CodeView.tsx
@@ -20,8 +20,13 @@
import * as React from 'react';
import * as key from 'keymaster';
import SourceViewer from '../../../components/SourceViewer/SourceViewer';
-import { BranchLike, ComponentMeasure, ComponentMeasureEnhanced, Metric } from '../../../app/types';
-import { Period } from '../../../helpers/periods';
+import {
+ BranchLike,
+ ComponentMeasure,
+ ComponentMeasureEnhanced,
+ Metric,
+ Period
+} from '../../../app/types';
interface Props {
branchLike?: BranchLike;
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.js b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.tsx
index 6ec4749855e..4ddcd665e7b 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.tsx
@@ -17,8 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import { Link } from 'react-router';
import LinkIcon from '../../../components/icons-components/LinkIcon';
import QualifierIcon from '../../../components/icons-components/QualifierIcon';
@@ -27,80 +26,72 @@ import { splitPath } from '../../../helpers/path';
import {
getPathUrlAsString,
getBranchLikeUrl,
- getLongLivingBranchUrl,
- getComponentDrilldownUrlWithSelection
+ getComponentDrilldownUrlWithSelection,
+ getProjectUrl
} from '../../../helpers/urls';
import { translate } from '../../../helpers/l10n';
-/*:: import type { Component, ComponentEnhanced } from '../types'; */
-/*:: import type { Metric } from '../../../app/flow-types'; */
+import { BranchLike, ComponentMeasure, ComponentMeasureEnhanced, Metric } from '../../../app/types';
-/*:: type Props = {
- branchLike?: { id?: string; name: string },
- component: ComponentEnhanced,
- onClick: string => void,
- metric: Metric,
- rootComponent: Component
-}; */
-
-export default class ComponentCell extends React.PureComponent {
- /*:: props: Props; */
+interface Props {
+ branchLike?: BranchLike;
+ component: ComponentMeasureEnhanced;
+ onClick: (component: string) => void;
+ metric: Metric;
+ rootComponent: ComponentMeasure;
+}
- handleClick = (e /*: MouseEvent */) => {
- const isLeftClickEvent = e.button === 0;
- const isModifiedEvent = !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
+export default class ComponentCell extends React.PureComponent<Props> {
+ handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
+ const isLeftClickEvent = event.button === 0;
+ const isModifiedEvent = !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
if (isLeftClickEvent && !isModifiedEvent) {
- e.preventDefault();
+ event.preventDefault();
this.props.onClick(this.props.component.key);
}
};
- renderInner() {
+ renderInner(componentKey: string) {
const { component } = this.props;
let head = '';
let tail = component.name;
- let branch = null;
+ let branchComponent = null;
- if (['DIR', 'FIL', 'UTS'].includes(component.qualifier)) {
- const parts = splitPath(component.path);
- ({ head, tail } = parts);
+ if (['DIR', 'FIL', 'UTS'].includes(component.qualifier) && component.path) {
+ ({ head, tail } = splitPath(component.path));
}
if (this.props.rootComponent.qualifier === 'APP') {
- branch = (
- <React.Fragment>
+ branchComponent = (
+ <>
{component.branch ? (
- <React.Fragment>
+ <>
<LongLivingBranchIcon className="spacer-left little-spacer-right" />
<span className="note">{component.branch}</span>
- </React.Fragment>
+ </>
) : (
<span className="spacer-left outline-badge">{translate('branches.main_branch')}</span>
)}
- </React.Fragment>
+ </>
);
}
return (
- <span title={component.refKey || component.key}>
+ <span title={componentKey}>
<QualifierIcon qualifier={component.qualifier} />
&nbsp;
{head.length > 0 && <span className="note">{head}/</span>}
<span>{tail}</span>
- {branch}
+ {branchComponent}
</span>
);
}
render() {
const { branchLike, component, metric, rootComponent } = this.props;
- const to =
- this.props.rootComponent.qualifier === 'APP'
- ? getLongLivingBranchUrl(component.refKey, component.branch)
- : getBranchLikeUrl(component.refKey, branchLike);
return (
<td className="measure-details-component-cell">
<div className="text-ellipsis">
- {component.refKey == null ? (
+ {!component.refKey ? (
<a
className="link-no-underline"
href={getPathUrlAsString(
@@ -113,17 +104,21 @@ export default class ComponentCell extends React.PureComponent {
)}
id={'component-measures-component-link-' + component.key}
onClick={this.handleClick}>
- {this.renderInner()}
+ {this.renderInner(component.key)}
</a>
) : (
<Link
className="link-no-underline"
- id={'component-measures-component-link-' + component.key}
- to={to}>
+ id={'component-measures-component-link-' + component.refKey}
+ to={
+ this.props.rootComponent.qualifier === 'APP'
+ ? getProjectUrl(component.refKey, component.branch)
+ : getBranchLikeUrl(component.refKey, branchLike)
+ }>
<span className="big-spacer-right">
<LinkIcon />
</span>
- {this.renderInner()}
+ {this.renderInner(component.refKey)}
</Link>
)}
</div>
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsListRow.js b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsListRow.tsx
index 49024fe6f07..6ae4c80b619 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsListRow.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsListRow.tsx
@@ -17,25 +17,23 @@
* 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 * as React from 'react';
+import * as classNames from 'classnames';
import ComponentCell from './ComponentCell';
import MeasureCell from './MeasureCell';
-/*:: import type { Component, ComponentEnhanced } from '../types'; */
-/*:: import type { Metric } from '../../../app/flow-types'; */
+import { ComponentMeasure, Metric, ComponentMeasureEnhanced, BranchLike } from '../../../app/types';
-/*:: type Props = {|
- branchLike?: { id?: string; name: string },
- component: ComponentEnhanced,
- isSelected: boolean,
- onClick: string => void,
- otherMetrics: Array<Metric>,
- metric: Metric,
- rootComponent: Component
-|}; */
+interface Props {
+ branchLike?: BranchLike;
+ component: ComponentMeasureEnhanced;
+ isSelected: boolean;
+ onClick: (component: string) => void;
+ otherMetrics: Metric[];
+ metric: Metric;
+ rootComponent: ComponentMeasure;
+}
-export default function ComponentsListRow(props /*: Props */) {
+export default function ComponentsListRow(props: Props) {
const { branchLike, component, rootComponent } = props;
const otherMeasures = props.otherMetrics.map(metric => {
const measure = component.measures.find(measure => measure.metric.key === metric.key);
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/MeasureCell.js b/server/sonar-web/src/main/js/apps/component-measures/drilldown/MeasureCell.tsx
index 8a984c5001f..951bb590f62 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/MeasureCell.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/MeasureCell.tsx
@@ -17,22 +17,19 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import Measure from '../../../components/measure/Measure';
import { isDiffMetric } from '../../../helpers/measures';
-/*:: import type { ComponentEnhanced } from '../types'; */
-/*:: import type { MeasureEnhanced } from '../../../components/measure/types'; */
-/*:: import type { Metric } from '../../../app/flow-types'; */
+import { Metric, MeasureEnhanced, ComponentMeasureEnhanced } from '../../../app/types';
-/*:: type Props = {
- component: ComponentEnhanced,
- measure?: MeasureEnhanced,
- metric: Metric
-}; */
+interface Props {
+ component: ComponentMeasureEnhanced;
+ measure?: MeasureEnhanced;
+ metric: Metric;
+}
-export default function MeasureCell({ component, measure, metric } /*: Props */) {
- const getValue = (item /*: { leak?: ?string; value?: string } */) =>
+export default function MeasureCell({ component, measure, metric }: Props) {
+ const getValue = (item: { leak?: string; value?: string }) =>
isDiffMetric(metric.key) ? item.leak : item.value;
const value = getValue(measure || component);
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/MeasureCell-test.js b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/MeasureCell-test.tsx
index 6897ed8a623..cd0772fd03b 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/MeasureCell-test.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/MeasureCell-test.tsx
@@ -17,19 +17,19 @@
* 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 * as React from 'react';
import { shallow } from 'enzyme';
import MeasureCell from '../MeasureCell';
describe('should correctly take the value', () => {
- const renderAndTakeValue = props =>
+ const renderAndTakeValue = (props: any) =>
shallow(<MeasureCell {...props} />)
.find('Measure')
.prop('value');
it('absolute value', () => {
const component = { value: '123' };
- const metric = { key: 'coverage' };
+ const metric = { id: '1', key: 'coverage' };
const measure = { value: '567' };
expect(renderAndTakeValue({ component, metric })).toEqual('123');
@@ -38,7 +38,7 @@ describe('should correctly take the value', () => {
it('leak value', () => {
const component = { leak: '234' };
- const metric = { key: 'new_coverage' };
+ const metric = { id: '1', key: 'new_coverage' };
const measure = { leak: '678' };
expect(renderAndTakeValue({ component, metric })).toEqual('234');
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainFacet.js b/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainFacet.tsx
index 47338d7ac0d..3257089235c 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainFacet.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainFacet.tsx
@@ -17,8 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import FacetMeasureValue from './FacetMeasureValue';
import BubblesIcon from '../../../components/icons-components/BubblesIcon';
import FacetBox from '../../../components/facet/FacetBox';
@@ -38,26 +37,22 @@ import {
getLocalizedMetricName,
translate
} from '../../../helpers/l10n';
-/*:: import type { MeasureEnhanced } from '../../../components/measure/types'; */
+import { MeasureEnhanced } from '../../../app/types';
-/*:: type Props = {|
- onChange: (metric: string) => void,
- onToggle: (property: string) => void,
- open: boolean,
- domain: { name: string, measures: Array<MeasureEnhanced> },
- selected: string
-|}; */
-
-export default class DomainFacet extends React.PureComponent {
- /*:: props: Props; */
+interface Props {
+ domain: { name: string; measures: MeasureEnhanced[] };
+ onChange: (metric: string) => void;
+ onToggle: (property: string) => void;
+ open: boolean;
+ selected: string;
+}
- handleHeaderClick = () => this.props.onToggle(this.props.domain.name);
+export default class DomainFacet extends React.PureComponent<Props> {
+ handleHeaderClick = () => {
+ this.props.onToggle(this.props.domain.name);
+ };
- hasFacetSelected = (
- domain /*: { name: string } */,
- measures /*: Array<MeasureEnhanced> */,
- selected /*: string */
- ) => {
+ hasFacetSelected = (domain: { name: string }, measures: MeasureEnhanced[], selected: string) => {
const measureSelected = measures.find(measure => measure.metric.key === selected);
const overviewSelected = domain.name === selected && hasBubbleChart(domain.name);
return measureSelected || overviewSelected;
@@ -73,8 +68,9 @@ export default class DomainFacet extends React.PureComponent {
return overviewSelected ? [translate('component_measures.domain_overview')] : [];
};
- renderItemFacetStat = (item /*: MeasureEnhanced */) =>
- hasFacetStat(item.metric.key) ? <FacetMeasureValue measure={item} /> : null;
+ renderItemFacetStat = (item: MeasureEnhanced) => {
+ return hasFacetStat(item.metric.key) ? <FacetMeasureValue measure={item} /> : null;
+ };
renderItemsFacet = () => {
const { domain, selected } = this.props;
@@ -110,6 +106,7 @@ export default class DomainFacet extends React.PureComponent {
}
onClick={this.props.onChange}
stat={this.renderItemFacetStat(item)}
+ tooltip={translateMetric(item.metric)}
value={item.metric.key}
/>
)
@@ -133,6 +130,7 @@ export default class DomainFacet extends React.PureComponent {
}
onClick={this.props.onChange}
stat={<BubblesIcon size={14} />}
+ tooltip={translate('component_measures.domain_overview')}
value={domain.name}
/>
);
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetMeasureValue.js b/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetMeasureValue.tsx
index 70953357687..1829547f860 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetMeasureValue.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetMeasureValue.tsx
@@ -17,13 +17,16 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import Measure from '../../../components/measure/Measure';
import { isDiffMetric } from '../../../helpers/measures';
-/*:: import type { MeasureEnhanced } from '../../../components/measure/types'; */
+import { MeasureEnhanced } from '../../../app/types';
-export default function FacetMeasureValue({ measure } /*: { measure: MeasureEnhanced } */) {
+interface Props {
+ measure: MeasureEnhanced;
+}
+
+export default function FacetMeasureValue({ measure }: Props) {
if (isDiffMetric(measure.metric.key)) {
return (
<div
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/ProjectOverviewFacet.js b/server/sonar-web/src/main/js/apps/component-measures/sidebar/ProjectOverviewFacet.tsx
index c0877081176..a91c6583bd2 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/ProjectOverviewFacet.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/ProjectOverviewFacet.tsx
@@ -17,20 +17,19 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import FacetBox from '../../../components/facet/FacetBox';
import FacetItem from '../../../components/facet/FacetItem';
import FacetItemsList from '../../../components/facet/FacetItemsList';
import { translate } from '../../../helpers/l10n';
-/*:: type Props = {|
- onChange: (metric: string) => void,
- selected: string,
- value: string
-|}; */
+interface Props {
+ onChange: (metric: string) => void;
+ selected: string;
+ value: string;
+}
-export default function ProjectOverviewFacet({ value, selected, onChange } /*: Props */) {
+export default function ProjectOverviewFacet({ value, selected, onChange }: Props) {
const facetName = translate('component_measures.overview', value, 'facet');
return (
<FacetBox property={value}>
@@ -39,12 +38,9 @@ export default function ProjectOverviewFacet({ value, selected, onChange } /*: P
active={value === selected}
disabled={false}
key={value}
- name={
- <strong id={`measure-overview-${value}-name`} title={facetName}>
- {facetName}
- </strong>
- }
+ name={<strong id={`measure-overview-${value}-name`}>{facetName}</strong>}
onClick={onChange}
+ tooltip={facetName}
value={value}
/>
</FacetItemsList>
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.js b/server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.tsx
index 183875dc425..027d3593f4f 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.tsx
@@ -17,42 +17,39 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import ProjectOverviewFacet from './ProjectOverviewFacet';
import DomainFacet from './DomainFacet';
-import { getDefaultView, groupByDomains, KNOWN_DOMAINS, PROJECT_OVERVEW } from '../utils';
-/*:: import type { MeasureEnhanced } from '../../../components/measure/types'; */
-/*:: import type { Query } from '../types'; */
+import { getDefaultView, groupByDomains, KNOWN_DOMAINS, PROJECT_OVERVEW, Query } from '../utils';
+import { MeasureEnhanced } from '../../../app/types';
-/*:: type Props = {|
- measures: Array<MeasureEnhanced>,
- selectedMetric: string,
- updateQuery: Query => void
-|}; */
-
-/*:: type State = {|
- openFacets: { [string]: boolean }
-|}; */
+interface Props {
+ measures: MeasureEnhanced[];
+ selectedMetric: string;
+ updateQuery: (query: Query) => void;
+}
-export default class Sidebar extends React.PureComponent {
- /*:: props: Props; */
- /*:: state: State; */
+interface State {
+ openFacets: { [metric: string]: boolean };
+}
- constructor(props /*: Props */) {
+export default class Sidebar extends React.PureComponent<Props, State> {
+ constructor(props: Props) {
super(props);
this.state = { openFacets: this.getOpenFacets({}, props) };
}
- componentWillReceiveProps(nextProps /*: Props */) {
+ componentWillReceiveProps(nextProps: Props) {
if (nextProps.selectedMetric !== this.props.selectedMetric) {
- this.setState(state => this.getOpenFacets(state.openFacets, nextProps));
+ this.setState(({ openFacets }) => ({
+ openFacets: this.getOpenFacets(openFacets, nextProps)
+ }));
}
}
getOpenFacets = (
- openFacets /*: { [string]: boolean } */,
- { measures, selectedMetric } /*: Props */
+ openFacets: { [metric: string]: boolean },
+ { measures, selectedMetric }: Props
) => {
const newOpenFacets = { ...openFacets };
const measure = measures.find(measure => measure.metric.key === selectedMetric);
@@ -64,15 +61,15 @@ export default class Sidebar extends React.PureComponent {
return newOpenFacets;
};
- toggleFacet = (name /*: string */) => {
- this.setState(({ openFacets } /*: State */) => ({
+ toggleFacet = (name: string) => {
+ this.setState(({ openFacets }) => ({
openFacets: { ...openFacets, [name]: !openFacets[name] }
}));
};
- resetSelection = (metric /*: string */) => ({ selected: null, view: getDefaultView(metric) });
+ resetSelection = (metric: string) => ({ selected: undefined, view: getDefaultView(metric) });
- changeMetric = (metric /*: string */) =>
+ changeMetric = (metric: string) =>
this.props.updateQuery({ metric, ...this.resetSelection(metric) });
render() {
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/DomainFacet-test.js b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/DomainFacet-test.tsx
index 551d11831e8..590536b454c 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/DomainFacet-test.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/DomainFacet-test.tsx
@@ -17,8 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import { shallow } from 'enzyme';
import DomainFacet from '../DomainFacet';
@@ -27,6 +26,7 @@ const DOMAIN = {
measures: [
{
metric: {
+ id: '1',
key: 'bugs',
type: 'INT',
name: 'Bugs',
@@ -38,6 +38,7 @@ const DOMAIN = {
},
{
metric: {
+ id: '2',
key: 'new_bugs',
type: 'INT',
name: 'New Bugs',
@@ -75,7 +76,7 @@ it('should not display subtitles of new measures if there is none', () => {
name: 'Reliability',
measures: [
{
- metric: { key: 'bugs', type: 'INT', name: 'Bugs', domain: 'Reliability' },
+ metric: { id: '1', key: 'bugs', type: 'INT', name: 'Bugs', domain: 'Reliability' },
value: '5'
}
]
@@ -99,7 +100,7 @@ it('should not display subtitles of new measures if there is none, even on last
name: 'Reliability',
measures: [
{
- metric: { key: 'new_bugs', type: 'INT', name: 'New Bugs', domain: 'Reliability' },
+ metric: { id: '2', key: 'new_bugs', type: 'INT', name: 'New Bugs', domain: 'Reliability' },
value: '5'
}
]
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/FacetMeasureValue-test.js b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/FacetMeasureValue-test.tsx
index 542fc424aa7..971be2bbf3d 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/FacetMeasureValue-test.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/FacetMeasureValue-test.tsx
@@ -17,13 +17,13 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import { shallow } from 'enzyme';
import FacetMeasureValue from '../FacetMeasureValue';
const MEASURE = {
metric: {
+ id: '1',
key: 'bugs',
type: 'INT',
name: 'Bugs',
@@ -35,6 +35,7 @@ const MEASURE = {
};
const LEAK_MEASURE = {
metric: {
+ id: '2',
key: 'new_bugs',
type: 'INT',
name: 'New Bugs',
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/Sidebar-test.js b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/Sidebar-test.tsx
index e19a46431cf..9b629994ac3 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/Sidebar-test.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/Sidebar-test.tsx
@@ -17,13 +17,14 @@
* 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 * as React from 'react';
import { shallow } from 'enzyme';
import Sidebar from '../Sidebar';
const MEASURES = [
{
metric: {
+ id: '1',
key: 'lines_to_cover',
type: 'INT',
name: 'Lines to Cover',
@@ -35,6 +36,7 @@ const MEASURES = [
},
{
metric: {
+ id: '2',
key: 'coverage',
type: 'PERCENT',
name: 'Coverage',
@@ -46,6 +48,7 @@ const MEASURES = [
},
{
metric: {
+ id: '3',
key: 'duplicated_lines_density',
type: 'PERCENT',
name: 'Duplicated Lines (%)',
@@ -70,8 +73,8 @@ it('should display two facets', () => {
it('should correctly toggle facets', () => {
const wrapper = shallow(<Sidebar {...PROPS} />);
expect(wrapper.state('openFacets').bugs).toBeUndefined();
- wrapper.instance().toggleFacet('bugs');
+ (wrapper.instance() as Sidebar).toggleFacet('bugs');
expect(wrapper.state('openFacets').bugs).toBeTruthy();
- wrapper.instance().toggleFacet('bugs');
+ (wrapper.instance() as Sidebar).toggleFacet('bugs');
expect(wrapper.state('openFacets').bugs).toBeFalsy();
});
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/DomainFacet-test.js.snap b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/DomainFacet-test.tsx.snap
index 64c9678aaef..0a4c85967f1 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/DomainFacet-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/DomainFacet-test.tsx.snap
@@ -30,6 +30,7 @@ exports[`should display facet item list 1`] = `
size={14}
/>
}
+ tooltip="component_measures.domain_overview"
value="Reliability"
/>
<span
@@ -64,6 +65,7 @@ exports[`should display facet item list 1`] = `
"leak": "5",
"metric": Object {
"domain": "Reliability",
+ "id": "2",
"key": "new_bugs",
"name": "New Bugs",
"type": "INT",
@@ -78,6 +80,7 @@ exports[`should display facet item list 1`] = `
}
/>
}
+ tooltip="New Bugs"
value="new_bugs"
/>
<span
@@ -112,6 +115,7 @@ exports[`should display facet item list 1`] = `
"leak": "5",
"metric": Object {
"domain": "Reliability",
+ "id": "1",
"key": "bugs",
"name": "Bugs",
"type": "INT",
@@ -127,6 +131,7 @@ exports[`should display facet item list 1`] = `
}
/>
}
+ tooltip="Bugs"
value="bugs"
/>
</FacetItemsList>
@@ -167,6 +172,7 @@ exports[`should display facet item list with bugs selected 1`] = `
size={14}
/>
}
+ tooltip="component_measures.domain_overview"
value="Reliability"
/>
<span
@@ -201,6 +207,7 @@ exports[`should display facet item list with bugs selected 1`] = `
"leak": "5",
"metric": Object {
"domain": "Reliability",
+ "id": "2",
"key": "new_bugs",
"name": "New Bugs",
"type": "INT",
@@ -215,6 +222,7 @@ exports[`should display facet item list with bugs selected 1`] = `
}
/>
}
+ tooltip="New Bugs"
value="new_bugs"
/>
<span
@@ -249,6 +257,7 @@ exports[`should display facet item list with bugs selected 1`] = `
"leak": "5",
"metric": Object {
"domain": "Reliability",
+ "id": "1",
"key": "bugs",
"name": "Bugs",
"type": "INT",
@@ -264,6 +273,7 @@ exports[`should display facet item list with bugs selected 1`] = `
}
/>
}
+ tooltip="Bugs"
value="bugs"
/>
</FacetItemsList>
@@ -300,6 +310,7 @@ exports[`should not display subtitles of new measures if there is none 1`] = `
size={14}
/>
}
+ tooltip="component_measures.domain_overview"
value="Reliability"
/>
<span
@@ -333,6 +344,7 @@ exports[`should not display subtitles of new measures if there is none 1`] = `
Object {
"metric": Object {
"domain": "Reliability",
+ "id": "1",
"key": "bugs",
"name": "Bugs",
"type": "INT",
@@ -342,6 +354,7 @@ exports[`should not display subtitles of new measures if there is none 1`] = `
}
/>
}
+ tooltip="Bugs"
value="bugs"
/>
</FacetItemsList>
@@ -378,6 +391,7 @@ exports[`should not display subtitles of new measures if there is none, even on
size={14}
/>
}
+ tooltip="component_measures.domain_overview"
value="Reliability"
/>
<span
@@ -411,6 +425,7 @@ exports[`should not display subtitles of new measures if there is none, even on
Object {
"metric": Object {
"domain": "Reliability",
+ "id": "2",
"key": "new_bugs",
"name": "New Bugs",
"type": "INT",
@@ -420,6 +435,7 @@ exports[`should not display subtitles of new measures if there is none, even on
}
/>
}
+ tooltip="New Bugs"
value="new_bugs"
/>
</FacetItemsList>
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/FacetMeasureValue-test.js.snap b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/FacetMeasureValue-test.tsx.snap
index 228f8a3e6d9..228f8a3e6d9 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/FacetMeasureValue-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/FacetMeasureValue-test.tsx.snap
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/Sidebar-test.js.snap b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/Sidebar-test.tsx.snap
index f526bbb3a16..09f7788831c 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/Sidebar-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/Sidebar-test.tsx.snap
@@ -15,6 +15,7 @@ exports[`should display two facets 1`] = `
"leak": "70",
"metric": Object {
"domain": "Coverage",
+ "id": "1",
"key": "lines_to_cover",
"name": "Lines to Cover",
"type": "INT",
@@ -31,6 +32,7 @@ exports[`should display two facets 1`] = `
"leak": "0.0999999999999943",
"metric": Object {
"domain": "Coverage",
+ "id": "2",
"key": "coverage",
"name": "Coverage",
"type": "PERCENT",
@@ -61,6 +63,7 @@ exports[`should display two facets 1`] = `
"leak": "0.0",
"metric": Object {
"domain": "Duplications",
+ "id": "3",
"key": "duplicated_lines_density",
"name": "Duplicated Lines (%)",
"type": "PERCENT",
diff --git a/server/sonar-web/src/main/js/apps/component-measures/types.js b/server/sonar-web/src/main/js/apps/component-measures/types.js
deleted file mode 100644
index 4ce7d76cf75..00000000000
--- a/server/sonar-web/src/main/js/apps/component-measures/types.js
+++ /dev/null
@@ -1,58 +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 type { Measure, MeasureEnhanced } from '../../components/measure/types'; */
-
-/*:: type ComponentIntern = {
- isFavorite?: boolean,
- isRecentlyBrowsed?: boolean,
- key: string,
- match?: string,
- name: string,
- organization?: string,
- project?: string,
- qualifier: string
-}; */
-
-/*:: export type Component = ComponentIntern & { measures?: Array<Measure> }; */
-
-/*:: export type ComponentEnhanced = ComponentIntern & {
- value?: ?string,
- leak?: ?string,
- measures: Array<MeasureEnhanced>
-}; */
-
-/*:: export type Paging = {
- pageIndex: number,
- pageSize: number,
- total: number
-}; */
-
-/*:: export type Period = {
- index: number,
- date: string,
- mode: string,
- parameter?: string
-}; */
-
-/*:: export type Query = {
- metric: ?string,
- selected: ?string,
- view: string
-}; */
diff --git a/server/sonar-web/src/main/js/apps/component-measures/utils.ts b/server/sonar-web/src/main/js/apps/component-measures/utils.ts
index a02181c7e95..74befd184f0 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/utils.ts
+++ b/server/sonar-web/src/main/js/apps/component-measures/utils.ts
@@ -79,10 +79,7 @@ export function sortMeasures(
]);
}
-export function addMeasureCategories(
- domainName: string,
- measures: MeasureEnhanced[]
-) /*: Array<any> */ {
+export function addMeasureCategories(domainName: string, measures: MeasureEnhanced[]) {
const categories = domains[domainName] && domains[domainName].categories;
if (categories && categories.length > 0) {
return [...categories, ...measures];
@@ -121,7 +118,7 @@ export const groupByDomains = memoize((measures: MeasureEnhanced[]) => {
}));
return sortBy(domains, [
- (domain: { name: string; measure: MeasureEnhanced[] }) => {
+ (domain: { name: string; measures: MeasureEnhanced[] }) => {
const idx = KNOWN_DOMAINS.indexOf(domain.name);
return idx >= 0 ? idx : KNOWN_DOMAINS.length;
},
@@ -162,7 +159,7 @@ export function getBubbleMetrics(domain: string, metrics: { [key: string]: Metri
x: metrics[conf.x],
y: metrics[conf.y],
size: metrics[conf.size],
- colors: conf.colors ? conf.colors.map(color => metrics[color]) : null
+ colors: conf.colors && conf.colors.map(color => metrics[color])
};
}
diff --git a/server/sonar-web/src/main/js/apps/overview/components/LeakPeriodLegend.tsx b/server/sonar-web/src/main/js/apps/overview/components/LeakPeriodLegend.tsx
index 90b9b569311..7e1c17777dd 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/LeakPeriodLegend.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/LeakPeriodLegend.tsx
@@ -23,9 +23,10 @@ import DateFromNow from '../../../components/intl/DateFromNow';
import DateFormatter, { longFormatterOption } from '../../../components/intl/DateFormatter';
import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
import Tooltip from '../../../components/controls/Tooltip';
-import { getPeriodDate, getPeriodLabel, Period, PeriodMode } from '../../../helpers/periods';
+import { getPeriodDate, getPeriodLabel } from '../../../helpers/periods';
import { translateWithParameters } from '../../../helpers/l10n';
import { differenceInDays } from '../../../helpers/dates';
+import { Period, PeriodMode } from '../../../app/types';
interface Props {
period: Period;
diff --git a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx
index 3f6982b4696..f6e58af8c0b 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx
@@ -32,7 +32,7 @@ import { getMeasuresAndMeta } from '../../../api/measures';
import { getAllTimeMachineData, History } from '../../../api/time-machine';
import { parseDate } from '../../../helpers/dates';
import { enhanceMeasuresWithMetrics } from '../../../helpers/measures';
-import { getLeakPeriod, Period } from '../../../helpers/periods';
+import { getLeakPeriod } from '../../../helpers/periods';
import { get } from '../../../helpers/storage';
import { METRICS, HISTORY_METRICS_LIST } from '../utils';
import {
@@ -48,7 +48,7 @@ import {
} from '../../../helpers/branches';
import { fetchMetrics } from '../../../store/rootActions';
import { getMetrics, Store } from '../../../store/rootReducer';
-import { BranchLike, Component, Metric, MeasureEnhanced } from '../../../app/types';
+import { BranchLike, Component, Metric, MeasureEnhanced, Period } from '../../../app/types';
import { translate } from '../../../helpers/l10n';
import '../styles.css';
diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/LeakPeriodLegend-test.tsx b/server/sonar-web/src/main/js/apps/overview/components/__tests__/LeakPeriodLegend-test.tsx
index fa6f4c952fe..77a2ddc8cca 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/LeakPeriodLegend-test.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/LeakPeriodLegend-test.tsx
@@ -20,8 +20,8 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import LeakPeriodLegend from '../LeakPeriodLegend';
-import { PeriodMode, Period } from '../../../../helpers/periods';
import { differenceInDays } from '../../../../helpers/dates';
+import { Period, PeriodMode } from '../../../../app/types';
jest.mock('../../../../helpers/dates', () => {
const dates = require.requireActual('../../../../helpers/dates');
diff --git a/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx b/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx
index 44aecab78af..e0306ea0a7e 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx
@@ -33,13 +33,13 @@ import {
getRatingTooltip
} from '../../../helpers/measures';
import { getLocalizedMetricName } from '../../../helpers/l10n';
-import { getPeriodDate, Period } from '../../../helpers/periods';
+import { getPeriodDate } from '../../../helpers/periods';
import {
getComponentDrilldownUrl,
getComponentIssuesUrl,
getMeasureHistoryUrl
} from '../../../helpers/urls';
-import { Component, BranchLike, MeasureEnhanced } from '../../../app/types';
+import { Component, BranchLike, MeasureEnhanced, Period } from '../../../app/types';
import { History } from '../../../api/time-machine';
import { getBranchLikeQuery } from '../../../helpers/branches';
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeak-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeak-test.tsx
index 67d4b26c17a..62c153454d7 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeak-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeak-test.tsx
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+/* eslint-disable camelcase */
import * as React from 'react';
import { shallow } from 'enzyme';
import ProjectCardLeak from '../ProjectCardLeak';
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.tsx b/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.tsx
index 85b04e98266..021f46232bf 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.tsx
@@ -48,7 +48,6 @@ import {
SourceViewerFile
} from '../../app/types';
import { isSameBranchLike, getBranchLikeQuery } from '../../helpers/branches';
-import { parseDate } from '../../helpers/dates';
import { translate } from '../../helpers/l10n';
import './styles.css';
diff --git a/server/sonar-web/src/main/js/components/charts/BubbleChart.tsx b/server/sonar-web/src/main/js/components/charts/BubbleChart.tsx
index f636a57ee14..7810f4503d8 100644
--- a/server/sonar-web/src/main/js/components/charts/BubbleChart.tsx
+++ b/server/sonar-web/src/main/js/components/charts/BubbleChart.tsx
@@ -28,14 +28,16 @@ import { event, select } from 'd3-selection';
import { sortBy, uniq } from 'lodash';
import Tooltip from '../controls/Tooltip';
import { translate } from '../../helpers/l10n';
+import { Location } from '../../helpers/urls';
import './BubbleChart.css';
const TICKS_COUNT = 5;
-interface BubbleProps {
+interface BubbleProps<T> {
color?: string;
- link?: string;
- onClick?: (link?: string) => void;
+ link?: string | Location;
+ onClick?: (ref?: T) => void;
+ data?: T;
r: number;
scale: number;
tooltip?: string | React.ReactNode;
@@ -43,12 +45,12 @@ interface BubbleProps {
y: number;
}
-export class Bubble extends React.PureComponent<BubbleProps> {
+export class Bubble<T> extends React.PureComponent<BubbleProps<T>> {
handleClick = (event: React.MouseEvent<SVGCircleElement>) => {
if (this.props.onClick) {
event.stopPropagation();
event.preventDefault();
- this.props.onClick(this.props.link);
+ this.props.onClick(this.props.data);
}
};
@@ -75,17 +77,18 @@ export class Bubble extends React.PureComponent<BubbleProps> {
}
}
-interface Item {
+export interface BubbleItem<T> {
color?: string;
key?: string;
- link?: any;
+ link?: string | Location;
+ data?: T;
size: number;
tooltip?: React.ReactNode;
x: number;
y: number;
}
-interface Props {
+interface Props<T> {
displayXGrid?: boolean;
displayXTicks?: boolean;
displayYGrid?: boolean;
@@ -93,8 +96,8 @@ interface Props {
formatXTick?: (tick: number) => string;
formatYTick?: (tick: number) => string;
height: number;
- items: Item[];
- onBubbleClick?: (link?: string) => void;
+ items: BubbleItem<T>[];
+ onBubbleClick?: (ref?: T) => void;
padding?: [number, number, number, number];
sizeDomain?: [number, number];
sizeRange?: [number, number];
@@ -108,7 +111,7 @@ interface State {
type Scale = ScaleLinear<number, number>;
-export default class BubbleChart extends React.Component<Props, State> {
+export default class BubbleChart<T> extends React.Component<Props<T>, State> {
node: SVGSVGElement | null = null;
selection: any = null;
transform: any = null;
@@ -122,7 +125,7 @@ export default class BubbleChart extends React.Component<Props, State> {
sizeRange: [5, 45]
};
- constructor(props: Props) {
+ constructor(props: Props<T>) {
super(props);
this.state = { transform: { x: 0, y: 0, k: 1 } };
}
@@ -317,6 +320,7 @@ export default class BubbleChart extends React.Component<Props, State> {
key={item.key || index}
link={item.link}
onClick={this.props.onBubbleClick}
+ data={item.data}
r={sizeScale(item.size)}
scale={1 / transform.k}
tooltip={item.tooltip}
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
index 07c73da2b87..d98be168b61 100644
--- 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
@@ -35,7 +35,7 @@ it('should render bubble links', () => {
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 items = [{ x: 1, y: 10, size: 7, data: 'foo' }, { x: 2, y: 30, size: 5, data: '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__/__snapshots__/BubbleChart-test.tsx.snap b/server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/BubbleChart-test.tsx.snap
index 89b36a4c5ea..9d69488fbd0 100644
--- 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
@@ -130,8 +130,8 @@ exports[`should render bubble links 2`] = `
exports[`should render bubbles with click handlers 1`] = `
<Bubble
+ data="foo"
key="0"
- link="foo"
onClick={[MockFunction]}
r={45}
scale={1}
@@ -159,8 +159,8 @@ exports[`should render bubbles with click handlers 1`] = `
exports[`should render bubbles with click handlers 2`] = `
<Bubble
+ data="bar"
key="1"
- link="bar"
onClick={[MockFunction]}
r={33.57142857142857}
scale={1}
diff --git a/server/sonar-web/src/main/js/components/measure/utils.ts b/server/sonar-web/src/main/js/components/measure/utils.ts
index e45496fbd1a..e5851a5f956 100644
--- a/server/sonar-web/src/main/js/components/measure/utils.ts
+++ b/server/sonar-web/src/main/js/components/measure/utils.ts
@@ -19,6 +19,7 @@
*/
import { getRatingTooltip as nextGetRatingTooltip, isDiffMetric } from '../../helpers/measures';
import { Metric, Measure, MeasureEnhanced } from '../../app/types';
+import { getLeakPeriod } from '../../helpers/periods';
const KNOWN_RATINGS = ['sqale_rating', 'reliability_rating', 'security_rating'];
@@ -37,7 +38,7 @@ export function getLeakValue(measure: Measure | undefined): string | undefined {
if (!measure || !measure.periods) {
return undefined;
}
- const period = measure.periods.find(period => period.index === 1);
+ const period = getLeakPeriod(measure.periods);
return period && period.value;
}
diff --git a/server/sonar-web/src/main/js/helpers/path.ts b/server/sonar-web/src/main/js/helpers/path.ts
index 1c4138e75a5..c9a257f90fb 100644
--- a/server/sonar-web/src/main/js/helpers/path.ts
+++ b/server/sonar-web/src/main/js/helpers/path.ts
@@ -87,16 +87,12 @@ export function fileFromPath(path: string | null): string | null {
}
}
-export function splitPath(path: string): { head: string; tail: string } | null {
- if (typeof path === 'string') {
- const tokens = path.split('/');
- return {
- head: tokens.slice(0, -1).join('/'),
- tail: tokens[tokens.length - 1]
- };
- } else {
- return null;
- }
+export function splitPath(path: string) {
+ const tokens = path.split('/');
+ return {
+ head: tokens.slice(0, -1).join('/'),
+ tail: tokens[tokens.length - 1]
+ };
}
export function limitComponentName(str: string, limit = 30): string {
diff --git a/server/sonar-web/src/main/js/helpers/periods.ts b/server/sonar-web/src/main/js/helpers/periods.ts
index d278a904b9d..baa0651439d 100644
--- a/server/sonar-web/src/main/js/helpers/periods.ts
+++ b/server/sonar-web/src/main/js/helpers/periods.ts
@@ -19,32 +19,16 @@
*/
import { translate, translateWithParameters } from './l10n';
import { parseDate } from './dates';
+import { Period, PeriodMode, PeriodMeasure } from '../app/types';
-export enum PeriodMode {
- Days = 'days',
- Date = 'date',
- Version = 'version',
- PreviousAnalysis = 'previous_analysis',
- PreviousVersion = 'previous_version'
-}
-
-export interface Period {
- date: string;
- index: number;
- mode: PeriodMode;
- modeParam?: string;
- parameter?: string;
-}
-
-function getPeriod(periods: Period[] | undefined, index: number) {
+function getPeriod<T extends Period | PeriodMeasure>(periods: T[] | undefined, index: number) {
if (!Array.isArray(periods)) {
return undefined;
}
-
return periods.find(period => period.index === index);
}
-export function getLeakPeriod(periods: Period[] | undefined) {
+export function getLeakPeriod<T extends Period | PeriodMeasure>(periods: T[] | undefined) {
return getPeriod(periods, 1);
}