aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2017-06-21 15:53:31 +0200
committerGrégoire Aubert <gregoire.aubert@sonarsource.com>2017-07-04 14:15:34 +0200
commit7feb62d1317819a82df8dcbc71969d9c1d51bdc7 (patch)
tree2a0937f508e28fc242958661a67ef89c03e2b75d /server/sonar-web/src/main/js/apps
parentcc5e586bcc63ddcb678659d44196cbda482141ed (diff)
downloadsonarqube-7feb62d1317819a82df8dcbc71969d9c1d51bdc7.tar.gz
sonarqube-7feb62d1317819a82df8dcbc71969d9c1d51bdc7.zip
SONAR-9402 Add basic zooming capabilities to the project history graphs
Diffstat (limited to 'server/sonar-web/src/main/js/apps')
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/GraphsZoom.js81
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.js71
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/StaticGraphs.js12
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/utils.js3
4 files changed, 134 insertions, 33 deletions
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/GraphsZoom.js b/server/sonar-web/src/main/js/apps/projectActivity/components/GraphsZoom.js
new file mode 100644
index 00000000000..3dea9f1a188
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/GraphsZoom.js
@@ -0,0 +1,81 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+// @flow
+import React from 'react';
+import { some, throttle } from 'lodash';
+import { AutoSizer } from 'react-virtualized';
+import ZoomTimeLine from '../../../components/charts/ZoomTimeLine';
+import type { RawQuery } from '../../../helpers/query';
+import type { Serie } from '../../../components/charts/AdvancedTimeline';
+
+type Props = {
+ graphEndDate: ?Date,
+ graphStartDate: ?Date,
+ leakPeriodDate: Date,
+ loading: boolean,
+ metricsType: string,
+ series: Array<Serie>,
+ showAreas?: boolean,
+ updateGraphZoom: (from: ?Date, to: ?Date) => void,
+ updateQuery: RawQuery => void
+};
+
+export default class GraphsZoom extends React.PureComponent {
+ props: Props;
+
+ constructor(props: Props) {
+ super(props);
+ this.updateDateRange = throttle(this.updateDateRange, 100);
+ }
+
+ hasHistoryData = () => some(this.props.series, serie => serie.data && serie.data.length > 2);
+
+ updateDateRange = (from: ?Date, to: ?Date) => this.props.updateQuery({ from, to });
+
+ render() {
+ const { loading } = this.props;
+ if (loading || !this.hasHistoryData()) {
+ return null;
+ }
+
+ return (
+ <div className="project-activity-graph-zoom">
+ <AutoSizer disableHeight={true}>
+ {({ width }) => (
+ <ZoomTimeLine
+ endDate={this.props.graphEndDate}
+ height={64}
+ width={width}
+ interpolate="linear"
+ leakPeriodDate={this.props.leakPeriodDate}
+ metricType={this.props.metricsType}
+ padding={[0, 10, 18, 60]}
+ series={this.props.series}
+ showAreas={this.props.showAreas}
+ startDate={this.props.graphStartDate}
+ updateZoom={this.updateDateRange}
+ updateZoomFast={this.props.updateGraphZoom}
+ />
+ )}
+ </AutoSizer>
+ </div>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.js
index 5680eca4306..db1eb841dac 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.js
@@ -20,8 +20,14 @@
// @flow
import React from 'react';
import ProjectActivityGraphsHeader from './ProjectActivityGraphsHeader';
+import GraphsZoom from './GraphsZoom';
import StaticGraphs from './StaticGraphs';
-import { GRAPHS_METRICS, generateCoveredLinesMetric, historyQueryChanged } from '../utils';
+import {
+ GRAPHS_METRICS,
+ datesQueryChanged,
+ generateCoveredLinesMetric,
+ historyQueryChanged
+} from '../utils';
import { translate } from '../../../helpers/l10n';
import type { RawQuery } from '../../../helpers/query';
import type { Analysis, MeasureHistory, Query } from '../types';
@@ -39,7 +45,8 @@ type Props = {
};
type State = {
- filteredSeries: Array<Serie>,
+ graphStartDate: ?Date,
+ graphEndDate: ?Date,
series: Array<Serie>
};
@@ -51,7 +58,8 @@ export default class ProjectActivityGraphs extends React.PureComponent {
super(props);
const series = this.getSeries(props.measuresHistory);
this.state = {
- filteredSeries: this.filterSeries(series, props.query),
+ graphStartDate: props.query.from || null,
+ graphEndDate: props.query.to || null,
series
};
}
@@ -62,10 +70,13 @@ export default class ProjectActivityGraphs extends React.PureComponent {
historyQueryChanged(this.props.query, nextProps.query)
) {
const series = this.getSeries(nextProps.measuresHistory);
- this.setState({
- filteredSeries: this.filterSeries(series, nextProps.query),
- series
- });
+ this.setState({ series });
+ }
+ if (
+ nextProps.query !== this.props.query &&
+ datesQueryChanged(this.props.query, nextProps.query)
+ ) {
+ this.setState({ graphStartDate: nextProps.query.from, graphEndDate: nextProps.query.to });
}
}
@@ -89,35 +100,37 @@ export default class ProjectActivityGraphs extends React.PureComponent {
};
});
- filterSeries = (series: Array<Serie>, query: Query): Array<Serie> => {
- if (!query.from && !query.to) {
- return series;
- }
- return series.map(serie => ({
- ...serie,
- data: serie.data.filter(p => {
- const isAfterFrom = !query.from || p.x >= query.from;
- const isBeforeTo = !query.to || p.x <= query.to;
- return isAfterFrom && isBeforeTo;
- })
- }));
- };
+ updateGraphZoom = (graphStartDate: ?Date, graphEndDate: ?Date) =>
+ this.setState({ graphStartDate, graphEndDate });
render() {
- const { graph, category } = this.props.query;
+ const { leakPeriodDate, loading, metricsType, query } = this.props;
+ const { series } = this.state;
return (
<div className="project-activity-layout-page-main-inner boxed-group boxed-group-inner">
- <ProjectActivityGraphsHeader graph={graph} updateQuery={this.props.updateQuery} />
+ <ProjectActivityGraphsHeader graph={query.graph} updateQuery={this.props.updateQuery} />
<StaticGraphs
analyses={this.props.analyses}
- eventFilter={category}
- filteredSeries={this.state.filteredSeries}
- leakPeriodDate={this.props.leakPeriodDate}
- loading={this.props.loading}
- metricsType={this.props.metricsType}
+ eventFilter={query.category}
+ graphEndDate={this.state.graphEndDate}
+ graphStartDate={this.state.graphStartDate}
+ leakPeriodDate={leakPeriodDate}
+ loading={loading}
+ metricsType={metricsType}
project={this.props.project}
- series={this.state.series}
- showAreas={['coverage', 'duplications'].includes(graph)}
+ series={series}
+ showAreas={['coverage', 'duplications'].includes(query.graph)}
+ />
+ <GraphsZoom
+ graphEndDate={this.state.graphEndDate}
+ graphStartDate={this.state.graphStartDate}
+ leakPeriodDate={leakPeriodDate}
+ loading={loading}
+ metricsType={metricsType}
+ series={series}
+ showAreas={['coverage', 'duplications'].includes(query.graph)}
+ updateGraphZoom={this.updateGraphZoom}
+ updateQuery={this.props.updateQuery}
/>
</div>
);
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/StaticGraphs.js b/server/sonar-web/src/main/js/apps/projectActivity/components/StaticGraphs.js
index 086c9a6e1d1..e14f5f097fb 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/StaticGraphs.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/StaticGraphs.js
@@ -32,11 +32,13 @@ import type { Serie } from '../../../components/charts/AdvancedTimeline';
type Props = {
analyses: Array<Analysis>,
eventFilter: string,
- filteredSeries: Array<Serie>,
+ graphStartDate: ?Date,
leakPeriodDate: Date,
loading: boolean,
metricsType: string,
- series: Array<Serie>
+ series: Array<Serie>,
+ showAreas?: boolean,
+ graphEndDate: ?Date
};
export default class StaticGraphs extends React.PureComponent {
@@ -95,7 +97,7 @@ export default class StaticGraphs extends React.PureComponent {
);
}
- const { filteredSeries, series } = this.props;
+ const { series } = this.props;
return (
<div className="project-activity-graph-container">
<StaticGraphsLegend series={series} />
@@ -103,6 +105,7 @@ export default class StaticGraphs extends React.PureComponent {
<AutoSizer>
{({ height, width }) => (
<AdvancedTimeline
+ endDate={this.props.graphEndDate}
events={this.getEvents()}
height={height}
interpolate="linear"
@@ -110,8 +113,9 @@ export default class StaticGraphs extends React.PureComponent {
formatYTick={this.formatYTick}
leakPeriodDate={this.props.leakPeriodDate}
metricType={this.props.metricsType}
- series={filteredSeries}
+ series={series}
showAreas={this.props.showAreas}
+ startDate={this.props.graphStartDate}
width={width}
/>
)}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/utils.js b/server/sonar-web/src/main/js/apps/projectActivity/utils.js
index 257300e503d..ae651fd891f 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/utils.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/utils.js
@@ -77,6 +77,9 @@ export const activityQueryChanged = (prevQuery: Query, nextQuery: Query): boolea
export const historyQueryChanged = (prevQuery: Query, nextQuery: Query): boolean =>
prevQuery.graph !== nextQuery.graph;
+export const datesQueryChanged = (prevQuery: Query, nextQuery: Query): boolean =>
+ prevQuery.from !== nextQuery.from || prevQuery.to !== nextQuery.to;
+
export const generateCoveredLinesMetric = (
uncoveredLines: MeasureHistory,
measuresHistory: Array<MeasureHistory>,