diff options
author | Stas Vilchik <vilchiks@gmail.com> | 2013-12-11 17:17:16 +0600 |
---|---|---|
committer | Stas Vilchik <vilchiks@gmail.com> | 2013-12-11 17:17:26 +0600 |
commit | 7ef81a9c53df3db31b60981649946705ad1d1daa (patch) | |
tree | 6166c4804444930b99e05be92abbe7774b9f79d7 | |
parent | a1f2499b07cab3fa7bbe09f1f9139b2bb16a8fa2 (diff) | |
download | sonarqube-7ef81a9c53df3db31b60981649946705ad1d1daa.tar.gz sonarqube-7ef81a9c53df3db31b60981649946705ad1d1daa.zip |
SONAR-4952 Provide a new PieChart widget to display a measure filter
5 files changed, 172 insertions, 42 deletions
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/pie_chart.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/pie_chart.html.erb index b83b65e351f..c0f2ae42206 100644 --- a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/pie_chart.html.erb +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/pie_chart.html.erb @@ -1,11 +1,53 @@ +<% + containerId = 'pie-chart-widget' + widget.id.to_s + chartHeight = widget_properties["chartHeight"] + chartTitle = widget_properties["chartTitle"] + filterId = widget_properties["filter"].to_i +%> + +<div class="pie-chart-widget" id="<%= containerId %>"> + <!--[if lte IE 8 ]> <h3><%= message('widget.unsupported_browser_warning') -%></h3> <![endif]--> + + <!--[if (gte IE 9)|!(IE)]><!--> + <% if chartTitle %> + <h3 style="text-align: center;"><%= h(chartTitle) -%></h3> + <% end %> + <!--<![endif]--> +</div> + +<!--[if (gte IE 9)|!(IE)]><!--> <script> - var filterId = <%= widget_properties['filter'].to_i %>; - var chartTitle = <%= widget_properties['chartTitle'].to_s %>; - var chartHeight = <%= widget_properties['chartHeight'].to_s %>; - var mainMetric = <%= widget_properties['mainMetric'].to_s %>; - var extraMetric1 = <%= widget_properties['extraMetric1'].to_s %>; - var extraMetric2 = <%= widget_properties['extraMetric2'].to_s %>; - var extraMetric3 = <%= widget_properties['extraMetric3'].to_s %>; + (function () { + var metrics = [ + '<%= widget_properties["mainMetric"].name %>', + '<%= widget_properties["extraMetric1"].name %>', + '<%= widget_properties["extraMetric2"].name %>', + '<%= widget_properties["extraMetric3"].name %>' + ], + query = [ + 'filter=<%= filterId %>', + 'metrics=' + metrics.join(','), + 'fields=name', + 'pageSize=9', + 'page=1', + 'sort=metric:' + metrics[0], + 'asc=false' + ].join('&'); + widget = new SonarWidgets.Widget(); + + widget + .type('PieChart') + .source(baseUrl + '/measures/search_filter?' + query) + .metricsPriority(metrics) + .height(<%= chartHeight %>) + .render('#<%= containerId %>'); + + autoResize(500, function() { + widget.update('#<%= containerId %>'); + }); + })(); </script> +<!--<![endif]--> + diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_head.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_head.html.erb index a1fd777877a..cd405b7318f 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_head.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_head.html.erb @@ -47,6 +47,7 @@ <%= javascript_include_tag 'third-party/jquery.ba-throttle-debounce.min.js' %> <%= javascript_include_tag 'third-party/select2.min' %> + <%= javascript_include_tag 'widgets/widget' %> <%= javascript_include_tag 'widgets/bubble-chart' %> <%= javascript_include_tag 'widgets/timeline' %> <%= javascript_include_tag 'widgets/stack-area' %> diff --git a/sonar-server/src/main/webapp/javascripts/widgets/pie-chart.js b/sonar-server/src/main/webapp/javascripts/widgets/pie-chart.js index 4bb174b4382..254b7db6ea9 100644 --- a/sonar-server/src/main/webapp/javascripts/widgets/pie-chart.js +++ b/sonar-server/src/main/webapp/javascripts/widgets/pie-chart.js @@ -7,8 +7,9 @@ window.SonarWidgets = window.SonarWidgets == null ? {} : window.SonarWidgets; window.SonarWidgets.PieChart = function () { // Set default values - this._data = []; + this._components = []; this._metrics = []; + this._metricsPriority = []; this._width = window.SonarWidgets.PieChart.defaults.width; this._height = window.SonarWidgets.PieChart.defaults.height; this._margin = window.SonarWidgets.PieChart.defaults.margin; @@ -16,15 +17,20 @@ window.SonarWidgets = window.SonarWidgets == null ? {} : window.SonarWidgets; this._lineHeight = 20; - // Export global variables - this.data = function (_) { - return param.call(this, '_data', _); - }; + // Export global variables this.metrics = function (_) { return param.call(this, '_metrics', _); }; + this.metricsPriority = function (_) { + return param.call(this, '_metricsPriority', _); + }; + + this.components = function (_) { + return param.call(this, '_components', _); + }; + this.width = function (_) { return param.call(this, '_width', _); }; @@ -60,6 +66,13 @@ window.SonarWidgets = window.SonarWidgets == null ? {} : window.SonarWidgets; .attr('transform', trans(this.margin().left, this.margin().top)); + // Configure metrics + this.mainMetric = this.metricsPriority()[0]; + this.getMainMetric = function(d) { + return d.measures[widget.mainMetric].val; + }; + + // Configure scales this.color = d3.scale.category20(); @@ -72,12 +85,12 @@ window.SonarWidgets = window.SonarWidgets == null ? {} : window.SonarWidgets; // Configure pie this.pie = d3.layout.pie() .sort(null) - .value(function(d) { return d.metric; }); + .value(function(d) { return widget.getMainMetric(d); }); // Configure sectors this.sectors = this.plotWrap.selectAll('.arc') - .data(this.pie(this.data())) + .data(this.pie(this.components())) .enter().append('g').attr('class', 'arc'); this.sectors.append('path') @@ -89,8 +102,9 @@ window.SonarWidgets = window.SonarWidgets == null ? {} : window.SonarWidgets; .attr('width', this.legendWidth()); this.legends = this.legendWrap.selectAll('.legend') - .data(this.pie(this.data())) - .enter().append('g') + .data(this.components()); + + this.legends.enter().append('g') .attr('class', 'legend') .attr('transform', function(d, i) { return trans(0, 10 + i * widget._lineHeight); }); @@ -102,7 +116,7 @@ window.SonarWidgets = window.SonarWidgets == null ? {} : window.SonarWidgets; this.legends.append('text') .attr('class', 'legend-text') .attr('transform', trans(10, 3)) - .text(function(d) { return d.data.name; }); + .text(function(d) { return d.name; }); // Configure details @@ -113,22 +127,16 @@ window.SonarWidgets = window.SonarWidgets == null ? {} : window.SonarWidgets; this.detailsColorIndicator = this.detailsWrap.append('rect') .classed('details-color-indicator', true) + .attr('transform', trans(-1, 0)) .attr('x', 0) .attr('y', 0) - .attr('width', 4) + .attr('width', 3) .attr('height', this._detailsHeight) .style('opacity', 0); - this._detailsMetric = [ - { name: 'Technical Dept', value: 8.5 }, - { name: 'Lines of Code', value: 362 }, - { name: 'Issues', value: 3 }, - { name: 'Coverage', value: 86 } - ]; - // Configure events - var enterHandler = function(sector, legend, i) { + var enterHandler = function(sector, legend, d, i) { var scaleFactor = (widget.radius + 5) / widget.radius; d3.select(legend) .classed('legend-active', true); @@ -136,12 +144,19 @@ window.SonarWidgets = window.SonarWidgets == null ? {} : window.SonarWidgets; .classed('arc-active', true) .style('-webkit-transform', 'scale(' + scaleFactor + ')'); - updateMetrics(widget._detailsMetric); + var metrics = widget.metricsPriority().map(function(m) { + return { + name: widget.metrics()[m].name, + value: d.measures[m].val + }; + }); + updateMetrics(metrics); widget.detailsColorIndicator .style('opacity', 1) .style('fill', widget.color(i)); }, + leaveHandler = function(sector, legend) { d3.select(legend).classed('legend-active', false); d3.select(sector) @@ -152,25 +167,28 @@ window.SonarWidgets = window.SonarWidgets == null ? {} : window.SonarWidgets; widget.detailsMetrics .style('opacity', 0); }, + updateMetrics = function(metrics) { + widget.detailsMetrics = widget.detailsWrap.selectAll('.details-metric') .data(metrics); - widget.detailsMetrics - .enter().append('text') - .text(function(d) { return d.name + ': ' + d.value; }); - - widget.detailsMetrics + widget.detailsMetrics.enter().append('text') .classed('details-metric', true) .classed('details-metric-main', function(d, i) { return i === 0; }) .attr('transform', function(d, i) { return trans(10, i * widget._lineHeight); }) - .attr('dy', '1.2em') + .attr('dy', '1.2em'); + + widget.detailsMetrics + .text(function(d) { return d.name + ': ' + d.value; }) .style('opacity', 1); + + widget.detailsMetrics.exit().remove(); }; this.legends .on('mouseenter', function(d, i) { - return enterHandler(widget.sectors[0][i], this, i); + return enterHandler(widget.sectors[0][i], this, d, i); }) .on('mouseleave', function(d, i) { return leaveHandler(widget.sectors[0][i], this); @@ -178,7 +196,7 @@ window.SonarWidgets = window.SonarWidgets == null ? {} : window.SonarWidgets; this.sectors .on('mouseenter', function(d, i) { - return enterHandler(this, widget.legends[0][i], i); + return enterHandler(this, widget.legends[0][i], d.data, i); }) .on('mouseleave', function(d, i) { return leaveHandler(this, widget.legends[0][i]); @@ -213,21 +231,22 @@ window.SonarWidgets = window.SonarWidgets == null ? {} : window.SonarWidgets; // Update radius - this.radius = Math.min(this.availableWidth - this.legendWidth(), this.availableHeight) / 2; + this.radius = Math.min(this.availableWidth, this.availableHeight) / 2; // Update plot this.plotWrap - .attr('transform', trans(this.radius, this.radius)); + .attr('transform', trans(this.radius, this.radius)); // Update arc - this.arc.outerRadius(this.radius); + this.arc + .outerRadius(this.radius); // Update legend this.legendWrap - .attr('transform', trans(20 + this.radius * 2, 0 )); + .attr('transform', trans(20 + this.radius * 2, 0 )); // Update details @@ -239,9 +258,6 @@ window.SonarWidgets = window.SonarWidgets == null ? {} : window.SonarWidgets; this.sectors.selectAll('path') .transition() .attr('d', this.arc); - - this.sectors.selectAll('text') - .attr('transform', function(d) { return 'translate(' + widget.arc.centroid(d) + ')'; }); }; diff --git a/sonar-server/src/main/webapp/javascripts/widgets/widget.js b/sonar-server/src/main/webapp/javascripts/widgets/widget.js new file mode 100644 index 00000000000..f4db2b92c8b --- /dev/null +++ b/sonar-server/src/main/webapp/javascripts/widgets/widget.js @@ -0,0 +1,70 @@ +/*global d3:false, SonarWidgets:false */ +/*jshint eqnull:true */ + +window.SonarWidgets = window.SonarWidgets == null ? {} : window.SonarWidgets; + +(function () { + + window.SonarWidgets.Widget = function () { + // Set default values + this._type = null; + this._source = null; + this._metricsPriority = null; + this._height = null; + + + // Export global variables + this.type = function (_) { + return param.call(this, '_type', _); + }; + + this.source = function (_) { + return param.call(this, '_source', _); + }; + + this.metricsPriority = function (_) { + return param.call(this, '_metricsPriority', _); + }; + + this.height = function (_) { + return param.call(this, '_height', _); + }; + }; + + + window.SonarWidgets.Widget.prototype.render = function(container) { + var that = this; + + d3.json(this.source(), function(error, response) { + if (response && !error) { + that.widget = new SonarWidgets[that.type()](); + that.widget + .metrics(response.metrics) + .metricsPriority(that.metricsPriority()) + .components(response.components) + .height(that.height()); + that.widget.render(container); + } + }); + }; + + + window.SonarWidgets.Widget.prototype.update = function(container) { + return this.widget && this.widget.update(container); + }; + + + + // Some helper functions + + // Gets or sets parameter + function param(name, value) { + if (value == null) { + return this[name]; + } else { + this[name] = value; + return this; + } + } + +})(); diff --git a/sonar-server/wro.xml b/sonar-server/wro.xml index 11c1ba2ebe4..46f78721d25 100644 --- a/sonar-server/wro.xml +++ b/sonar-server/wro.xml @@ -27,6 +27,7 @@ <js>/javascripts/third-party/jquery.ba-throttle-debounce.min.js</js> <js>/javascripts/third-party/select2.min.js</js> + <js>/javascripts/widgets/widget.js</js> <js>/javascripts/widgets/bubble-chart.js</js> <js>/javascripts/widgets/timeline.js</js> <js>/javascripts/widgets/stack-area.js</js> |