From 5a0ff33ea0c8496aa923dba9e64ca642d79898ed Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Mon, 9 Nov 2015 14:07:58 +0100 Subject: SONAR-6361 add detailed "Duplications" panel for the "Overview" main page --- .../main/js/apps/overview/domain/bubble-chart.js | 4 +- .../js/apps/overview/duplications/bubble-chart.js | 12 -- .../overview/duplications/duplications-details.js | 21 ---- .../src/main/js/apps/overview/duplications/main.js | 126 +++++++++++++++++---- .../main/js/apps/overview/duplications/timeline.js | 16 --- .../main/js/apps/overview/duplications/treemap.js | 20 ---- .../src/main/js/apps/overview/main/duplications.js | 2 +- .../src/main/js/apps/overview/overview.js | 9 ++ .../src/main/js/components/charts/bubble-chart.js | 37 +++--- server/sonar-web/src/main/js/helpers/constants.js | 2 + server/sonar-web/src/main/less/pages/overview.less | 40 ++++++- .../WEB-INF/app/views/overview/index.html.erb | 2 +- 12 files changed, 178 insertions(+), 113 deletions(-) delete mode 100644 server/sonar-web/src/main/js/apps/overview/duplications/bubble-chart.js delete mode 100644 server/sonar-web/src/main/js/apps/overview/duplications/duplications-details.js delete mode 100644 server/sonar-web/src/main/js/apps/overview/duplications/timeline.js delete mode 100644 server/sonar-web/src/main/js/apps/overview/duplications/treemap.js diff --git a/server/sonar-web/src/main/js/apps/overview/domain/bubble-chart.js b/server/sonar-web/src/main/js/apps/overview/domain/bubble-chart.js index 58c65c45ad1..e112d8a230d 100644 --- a/server/sonar-web/src/main/js/apps/overview/domain/bubble-chart.js +++ b/server/sonar-web/src/main/js/apps/overview/domain/bubble-chart.js @@ -101,7 +101,7 @@ export class DomainBubbleChart extends React.Component { } render () { - return
+ return

Project Files

    @@ -110,7 +110,7 @@ export class DomainBubbleChart extends React.Component {
  • Size: {this.getSizeMetricsTitle()}
-
+
{this.renderBubbleChart()}
; diff --git a/server/sonar-web/src/main/js/apps/overview/duplications/bubble-chart.js b/server/sonar-web/src/main/js/apps/overview/duplications/bubble-chart.js deleted file mode 100644 index 299a47978f1..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/duplications/bubble-chart.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; -import { DomainBubbleChart } from '../domain/bubble-chart'; - - -export class DuplicationsBubbleChart extends React.Component { - render () { - return ; - } -} diff --git a/server/sonar-web/src/main/js/apps/overview/duplications/duplications-details.js b/server/sonar-web/src/main/js/apps/overview/duplications/duplications-details.js deleted file mode 100644 index b1b9ec79138..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/duplications/duplications-details.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; - -import { DomainMeasuresList } from '../domain/measures-list'; - - -const METRICS = [ - 'duplicated_blocks', - 'duplicated_files', - 'duplicated_lines', - 'duplicated_lines_density' -]; - - -export class DuplicationsDetails extends React.Component { - render () { - return
-

Duplications

- -
; - } -} diff --git a/server/sonar-web/src/main/js/apps/overview/duplications/main.js b/server/sonar-web/src/main/js/apps/overview/duplications/main.js index 02aa9eceecc..a3aa1d482dd 100644 --- a/server/sonar-web/src/main/js/apps/overview/duplications/main.js +++ b/server/sonar-web/src/main/js/apps/overview/duplications/main.js @@ -1,31 +1,117 @@ +import d3 from 'd3'; import React from 'react'; -import { DuplicationsDetails } from './duplications-details'; -import { DuplicationsBubbleChart } from './bubble-chart'; -import { DuplicationsTimeline } from './timeline'; -import { DuplicationsTreemap } from './treemap'; +import { getMeasuresAndVariations } from '../../../api/measures'; +import { DetailedMeasure } from '../common-components'; +import { DomainTimeline } from '../timeline/domain-timeline'; +import { DomainTreemap } from '../domain/treemap'; +import { DomainBubbleChart } from '../domain/bubble-chart'; +import { getPeriodLabel, getPeriodDate } from './../helpers/period-label'; +import { TooltipsMixin } from '../../../components/mixins/tooltips-mixin'; +import { filterMetrics, filterMetricsForDomains } from '../helpers/metrics'; +import { Legend } from '../common-components'; +import { CHART_COLORS_RANGE_PERCENT } from '../../../helpers/constants'; -export default class extends React.Component { - render () { - return
-
-

Duplications

-
+export const DuplicationsMain = React.createClass({ + mixins: [TooltipsMixin], + + getInitialState() { + return { + ready: false, + leakPeriodLabel: getPeriodLabel(this.props.component.periods, this.props.leakPeriodIndex), + leakPeriodDate: getPeriodDate(this.props.component.periods, this.props.leakPeriodIndex) + }; + }, + + componentDidMount() { + this.requestMeasures().then(r => { + let measures = this.getMeasuresValues(r, 'value'); + let leak = this.getMeasuresValues(r, 'var' + this.props.leakPeriodIndex); + this.setState({ ready: true, measures, leak }); + }); + }, + + getMeasuresValues (measures, fieldKey) { + let values = {}; + Object.keys(measures).forEach(measureKey => { + values[measureKey] = measures[measureKey][fieldKey]; + }); + return values; + }, + + getMetricsForDomain() { + return this.props.metrics + .filter(metric => ['Duplication'].indexOf(metric.domain) !== -1) + .map(metric => metric.key); + }, + + getMetricsForTimeline() { + return filterMetricsForDomains(this.props.metrics, ['Duplication']); + }, + + getAllMetricsForTimeline() { + return filterMetrics(this.props.metrics); + }, - - - + requestMeasures () { + return getMeasuresAndVariations(this.props.component.key, this.getMetricsForDomain()); + }, - -
-
- + renderLoading () { + return
+ +
; + }, + + renderLegend () { + return ; + }, + + renderMeasures() { + let metrics = filterMetricsForDomains(this.props.metrics, ['Duplication']) + .map(metric => { + return ; + }); + return
{metrics}
; + }, + + render () { + if (!this.state.ready) { + return this.renderLoading(); + } + let treemapScale = d3.scale.linear() + .domain([0, 100]) + .range(CHART_COLORS_RANGE_PERCENT); + return
+
+
+
+
Duplications Overview
+ {this.renderLegend()} +
+
+ {this.renderMeasures()} +
+
- - +
+ + +
; + } -} +}); diff --git a/server/sonar-web/src/main/js/apps/overview/duplications/timeline.js b/server/sonar-web/src/main/js/apps/overview/duplications/timeline.js deleted file mode 100644 index abd6987af7d..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/duplications/timeline.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; - -import { DomainTimeline } from '../domain/timeline'; -import { filterMetricsForDomains } from '../helpers/metrics'; - - -const DOMAINS = ['Duplication']; - - -export class DuplicationsTimeline extends React.Component { - render () { - return ; - } -} diff --git a/server/sonar-web/src/main/js/apps/overview/duplications/treemap.js b/server/sonar-web/src/main/js/apps/overview/duplications/treemap.js deleted file mode 100644 index 019e78e4cab..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/duplications/treemap.js +++ /dev/null @@ -1,20 +0,0 @@ -import d3 from 'd3'; -import React from 'react'; - -import { DomainTreemap } from '../domain/treemap'; - - -const COLORS_5 = ['#00aa00', '#80cc00', '#ffee00', '#f77700', '#ee0000']; - - -export class DuplicationsTreemap extends React.Component { - render () { - let scale = d3.scale.linear() - .domain([0, 25, 50, 75, 100]) - .range(COLORS_5); - return ; - } -} diff --git a/server/sonar-web/src/main/js/apps/overview/main/duplications.js b/server/sonar-web/src/main/js/apps/overview/main/duplications.js index 4ae63e785bd..db4c6e6c320 100644 --- a/server/sonar-web/src/main/js/apps/overview/main/duplications.js +++ b/server/sonar-web/src/main/js/apps/overview/main/duplications.js @@ -31,7 +31,7 @@ export const GeneralDuplications = React.createClass({ render () { return - + diff --git a/server/sonar-web/src/main/js/apps/overview/overview.js b/server/sonar-web/src/main/js/apps/overview/overview.js index dae5318a477..21c5eb3ccc0 100644 --- a/server/sonar-web/src/main/js/apps/overview/overview.js +++ b/server/sonar-web/src/main/js/apps/overview/overview.js @@ -4,6 +4,7 @@ import Gate from './main/gate/gate'; import GeneralMain from './main/main'; import Meta from './meta'; import { SizeMain } from './size/main'; +import { DuplicationsMain } from './duplications/main'; import { getMetrics } from '../../api/metrics'; import { RouterMixin } from '../../components/router/router'; @@ -46,6 +47,12 @@ export const Overview = React.createClass({
; }, + renderDuplications () { + return
+ +
; + }, + render () { if (!this.state.ready) { return this.renderLoading(); @@ -55,6 +62,8 @@ export const Overview = React.createClass({ return this.renderMain(); case '/size': return this.renderSize(); + case '/duplications': + return this.renderDuplications(); default: throw new Error('Unknown route: ' + this.state.route); } diff --git a/server/sonar-web/src/main/js/components/charts/bubble-chart.js b/server/sonar-web/src/main/js/components/charts/bubble-chart.js index 2dd929c7c4c..391bf25c786 100644 --- a/server/sonar-web/src/main/js/components/charts/bubble-chart.js +++ b/server/sonar-web/src/main/js/components/charts/bubble-chart.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import d3 from 'd3'; import React from 'react'; @@ -93,8 +94,7 @@ export const BubbleChart = React.createClass({ let x = xScale(tick); let y1 = yScale.range()[0]; let y2 = yScale.range()[1]; - return ; + return ; }); return {lines}; @@ -109,8 +109,7 @@ export const BubbleChart = React.createClass({ let y = yScale(tick); let x1 = xScale.range()[0]; let x2 = xScale.range()[1]; - return ; + return ; }); return {lines}; @@ -156,27 +155,27 @@ export const BubbleChart = React.createClass({ let availableHeight = this.state.height - this.props.padding[0] - this.props.padding[2]; let xScale = d3.scale.linear() - .domain([0, d3.max(this.props.items, d => d.x)]) - .range([0, availableWidth]) - .nice(); + .domain([0, d3.max(this.props.items, d => d.x)]) + .range([0, availableWidth]) + .nice(); let yScale = d3.scale.linear() - .domain([0, d3.max(this.props.items, d => d.y)]) - .range([availableHeight, 0]) - .nice(); + .domain([0, d3.max(this.props.items, d => d.y)]) + .range([availableHeight, 0]) + .nice(); let sizeScale = d3.scale.linear() - .domain([0, d3.max(this.props.items, d => d.size)]) - .range(this.props.sizeRange); + .domain([0, d3.max(this.props.items, d => d.size)]) + .range(this.props.sizeRange); xScale.range(this.getXRange(xScale, sizeScale, availableWidth)); yScale.range(this.getYRange(yScale, sizeScale, availableHeight)); - let bubbles = this.props.items - .map((item, index) => { - return ; - }); + let bubbles = _.sortBy(this.props.items, (a, b) => b.size - a.size) + .map((item, index) => { + return ; + }); return diff --git a/server/sonar-web/src/main/js/helpers/constants.js b/server/sonar-web/src/main/js/helpers/constants.js index 392adf2202a..d1ba85c5395 100644 --- a/server/sonar-web/src/main/js/helpers/constants.js +++ b/server/sonar-web/src/main/js/helpers/constants.js @@ -1,2 +1,4 @@ export const SEVERITIES = ['BLOCKER', 'CRITICAL', 'MAJOR', 'MINOR', 'INFO']; export const STATUSES = ['OPEN', 'REOPENED', 'CONFIRMED', 'RESOLVED', 'CLOSED']; + +export const CHART_COLORS_RANGE_PERCENT = ['#00aa00', '#80cc00', '#ffee00', '#f77700', '#ee0000']; diff --git a/server/sonar-web/src/main/less/pages/overview.less b/server/sonar-web/src/main/less/pages/overview.less index 1379e215a94..e85da65f77f 100644 --- a/server/sonar-web/src/main/less/pages/overview.less +++ b/server/sonar-web/src/main/less/pages/overview.less @@ -236,6 +236,7 @@ .overview-detailed-page { flex: 1; + animation: fadeIn 0.5s forwards; } .overview-detailed-measures-list { @@ -316,9 +317,14 @@ .overview-domain-charts { display: flex; - .overview-domain-chart { + & > .overview-domain, + & > .overview-domain-chart { flex: 1; } + + & > .overview-domain { + max-width: 560px; + } } .overview-domain-chart { @@ -445,6 +451,38 @@ align-items: center; } +.overview-bubble-chart { + padding: 10px; + border: 1px solid @barBorderColor; + box-sizing: border-box; + background-color: #fff; + + .bubble-chart-bubble { + fill: @blue; + fill-opacity: 0.2; + stroke: @blue; + cursor: pointer; + transition: all 0.2s ease; + + &:hover { fill-opacity: 0.8; } + } + + .bubble-chart-grid { + shape-rendering: crispedges; + stroke: #eee; + } + + .bubble-chart-tick { + fill: @secondFontColor; + font-size: 11px; + text-anchor: middle; + } + + .bubble-chart-tick-y { + text-anchor: end; + } +} + /* * Responsive Stuff */ diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/overview/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/overview/index.html.erb index b9447a88eb5..d1004193763 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/overview/index.html.erb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/overview/index.html.erb @@ -61,7 +61,7 @@ index: '<%= index -%>', mode: '<%= @snapshot.period_mode(index) -%>', modeParam: '<%= @snapshot.period_param(index) -%>', - date: '<%= @snapshot.period_datetime(index).to_date.strftime('%FT%T%z') -%>' + date: '<%= @snapshot.period_datetime(index).strftime('%FT%T%z') -%>' }, <% end %> <% end %> -- cgit v1.2.3