diff options
author | Stas Vilchik <vilchiks@gmail.com> | 2015-04-08 14:13:10 +0200 |
---|---|---|
committer | Stas Vilchik <vilchiks@gmail.com> | 2015-04-08 14:27:22 +0200 |
commit | 94532b34aa649733d0a38d3aabe6e23ee7cfceaf (patch) | |
tree | c09a3d292faa8655a99879d855e420c88854c264 | |
parent | 315d615fe166dcae24500983e77ead9ebdb8ea99 (diff) | |
download | sonarqube-94532b34aa649733d0a38d3aabe6e23ee7cfceaf.tar.gz sonarqube-94532b34aa649733d0a38d3aabe6e23ee7cfceaf.zip |
SONAR-6331 apply feedback
28 files changed, 523 insertions, 162 deletions
diff --git a/server/sonar-web/src/main/hbs/overview/_overview-gate-condition.hbs b/server/sonar-web/src/main/hbs/overview/_overview-gate-condition.hbs new file mode 100644 index 00000000000..c3c278b9fd9 --- /dev/null +++ b/server/sonar-web/src/main/hbs/overview/_overview-gate-condition.hbs @@ -0,0 +1,15 @@ +<li class="overview-gate-condition spacer-bottom text-ellipsis"> + <i class="{{alertIconClass level}}"></i> + {{#canHaveDrilldownUrl metric period}} + <a href="{{urlForDrilldown ../../componentKey metric period periodDate}}" + class="overview-status overview-status-{{level}}" + title="{{#notEq level 'OK'}}{{t 'quality_gates.operator' op 'short'}} {{/notEq}}{{#eq level 'ERROR'}}{{formatMeasure error type}}{{/eq}}{{#eq level 'WARN'}}{{formatMeasure warning type}}{{/eq}}" + data-toggle="tooltip" data-placement="bottom">{{formatMeasure actual type}}</a> + {{else}} + <span class="overview-status overview-status-{{level}}" + title="{{#notEq level 'OK'}}{{t 'quality_gates.operator' op 'short'}} {{/notEq}}{{#eq level 'ERROR'}}{{formatMeasure error type}}{{/eq}}{{#eq level 'WARN'}}{{formatMeasure warning type}}{{/eq}}" + data-toggle="tooltip" data-placement="bottom">{{formatMeasure actual type}}</span> + {{/canHaveDrilldownUrl}} + <span class="note text-lowercase" style="padding-top: 4px;">{{t 'metric' metric 'name'}}</span> + <span class="note text-lowercase">{{default periodName period}}</span> +</li> diff --git a/server/sonar-web/src/main/hbs/overview/overview-coverage.hbs b/server/sonar-web/src/main/hbs/overview/overview-coverage.hbs index bdca7c4d95b..ce3d7a41109 100644 --- a/server/sonar-web/src/main/hbs/overview/overview-coverage.hbs +++ b/server/sonar-web/src/main/hbs/overview/overview-coverage.hbs @@ -1,26 +1,25 @@ +<header class="overview-card-header page-header"> + <h2 class="page-title">{{t 'overview.testing'}}</h2> +</header> + <div class="overview-highlight"> - <h6 class="note">{{t 'overview.coverage'}}</h6> + <h6 class="note">{{t 'metric.coverage.name'}}</h6> <div class="overview-main-measure"> {{#notNull coverage}} - <a href="{{urlForDrilldown componentKey 'overall_coverage'}}">{{formatMeasure coverage 'PERCENT'}}</a> + <a href="{{urlForDrilldown componentKey 'coverage'}}">{{formatMeasure coverage 'PERCENT'}}</a> {{else}} – {{/notNull}} </div> - {{#notNull coverage3}} - <div class="spacer-top"> - <span class="overview-measure {{#gt coverage3 0}}text-success{{/gt}}{{#lt coverage3 0}}text-danger{{/lt}}">{{formatMeasureVariation coverage3 'PERCENT'}}</span> - <span class="note">{{period3Name}}</span> - </div> - {{/notNull}} {{#notNull newCoverage3}} - <div class="spacer-top"> - <a class="overview-measure spacer-top" href="{{urlForDrilldown componentKey 'new_overall_coverage' 3}}">{{formatMeasure newCoverage3 'PERCENT'}}</a> - <span class="note">on new code</span> - </div> + <h6 class="note">{{period3Name}}</h6> + <a class="overview-measure spacer-top" + href="{{urlForDrilldown componentKey 'new_coverage' 3}}">{{formatMeasure newCoverage3 'PERCENT'}}</a> + <span class="note">{{t 'overview.on_new_code'}}</span> {{/notNull}} </div> <div class="overview-trend"> - <div class="overview-sparkline" id="overview-coverage-trend" data-width="300" data-height="50" data-color="#f3ca8e" data-type="PERCENT"></div> + <div class="overview-sparkline" id="overview-coverage-trend" data-height="120" data-color="#f3ca8e" + data-type="PERCENT"></div> </div> diff --git a/server/sonar-web/src/main/hbs/overview/overview-debt.hbs b/server/sonar-web/src/main/hbs/overview/overview-debt.hbs new file mode 100644 index 00000000000..6c4e6445de6 --- /dev/null +++ b/server/sonar-web/src/main/hbs/overview/overview-debt.hbs @@ -0,0 +1,21 @@ +<header class="overview-card-header page-header"> + <h2 class="page-title">{{t 'overview.debt'}}</h2> +</header> + +<div class="overview-highlight"> + <h6 class="note">{{t 'metric.sqale_index.name'}}</h6> + <div class="overview-main-measure"> + <a href="{{urlForDrilldown componentKey 'sqale_index'}}">{{formatMeasure debt 'WORK_DUR'}}</a> + </div> + {{#notNull issues3}} + <h6 class="note">{{period3Name}}</h6> + <a href="{{urlForDrilldown componentKey 'new_technical_debt'}}" + class="overview-measure {{#gt newDebt 0}}text-danger{{else}}text-success{{/gt}}">{{formatMeasure newDebt 'WORK_DUR'}}</a> + <span class="note">{{t 'overview.new'}}</span> + {{/notNull}} +</div> + +<div class="overview-trend"> + <div class="overview-sparkline" id="overview-debt-trend" data-height="120" data-color="#f3ca8e" + data-type="WORK_DUR"></div> +</div> diff --git a/server/sonar-web/src/main/hbs/overview/overview-duplications.hbs b/server/sonar-web/src/main/hbs/overview/overview-duplications.hbs index 68b7ecf1748..6c675595748 100644 --- a/server/sonar-web/src/main/hbs/overview/overview-duplications.hbs +++ b/server/sonar-web/src/main/hbs/overview/overview-duplications.hbs @@ -1,19 +1,22 @@ +<header class="overview-card-header page-header"> + <h2 class="page-title">{{t 'overview.duplications'}}</h2> +</header> + <div class="overview-highlight"> - <h6 class="note">{{t 'overview.duplications'}}</h6> + <h6 class="note">{{t 'metric.duplicated_lines_density.short_name'}}</h6> <div class="overview-main-measure"> <a href="{{urlForDrilldown componentKey 'duplicated_lines_density'}}"> {{formatMeasure duplications 'PERCENT'}} </a> </div> {{#notNull duplications3}} - <div class="spacer-top"> - <a class="overview-measure {{#gt duplications3 0}}text-danger{{/gt}}{{#lt duplications3 0}}text-success{{/lt}}" - href="{{urlForDrilldown componentKey 'duplicated_lines_density' 3}}">{{formatMeasureVariation duplications3 'PERCENT'}}</a> - <span class="note">{{period3Name}}</span> - </div> + <h6 class="note">{{period3Name}}</h6> + <a class="overview-measure {{#gt duplications3 0}}text-danger{{/gt}}{{#lt duplications3 0}}text-success{{/lt}}" + href="{{urlForDrilldown componentKey 'duplicated_lines_density' 3}}">{{formatMeasureVariation duplications3 'PERCENT'}}</a> {{/notNull}} </div> <div class="overview-trend"> - <div class="overview-sparkline" id="overview-duplications-trend" data-width="300" data-height="50" data-color="#f3ca8e" data-type="PERCENT"></div> + <div class="overview-sparkline" id="overview-duplications-trend" data-height="120" data-color="#f3ca8e" + data-type="PERCENT"></div> </div> diff --git a/server/sonar-web/src/main/hbs/overview/overview-gate.hbs b/server/sonar-web/src/main/hbs/overview/overview-gate.hbs index a43f972baac..a354e431b1e 100644 --- a/server/sonar-web/src/main/hbs/overview/overview-gate.hbs +++ b/server/sonar-web/src/main/hbs/overview/overview-gate.hbs @@ -1,22 +1,22 @@ -<div class="text-center"> - {{#notEmpty gateConditions}} - <ul class="list-inline spacer-top" style="display: inline-block;"> - {{#each gateConditions}} - <li> - {{#canHaveDrilldownUrl metric period}} - <a href="{{urlForDrilldown ../../componentKey metric period periodDate}}" - class="overview-status overview-status-{{level}}" - title="{{#notEq level 'OK'}}{{t 'quality_gates.operator' op 'short'}} {{/notEq}}{{#eq level 'ERROR'}}{{formatMeasure error type}}{{/eq}}{{#eq level 'WARN'}}{{formatMeasure warning type}}{{/eq}}" - data-toggle="tooltip" data-placement="bottom">{{formatMeasure actual type}}</a> - {{else}} - <span class="overview-status overview-status-{{level}}" - title="{{#notEq level 'OK'}}{{t 'quality_gates.operator' op 'short'}} {{/notEq}}{{#eq level 'ERROR'}}{{formatMeasure error type}}{{/eq}}{{#eq level 'WARN'}}{{formatMeasure warning type}}{{/eq}}" - data-toggle="tooltip" data-placement="bottom">{{formatMeasure actual type}}</span> - {{/canHaveDrilldownUrl}} - <p class="note text-lowercase" style="padding-top: 4px;">{{t 'metric' metric 'name'}}</p> - <p class="note">{{default periodName period}}</p> - </li> - {{/each}} - </ul> - {{/notEmpty}} -</div> +<header class="overview-card-header page-header"> + <h2 class="page-title">{{t 'overview.gate'}}</h2> +</header> + +{{#notEmpty gateConditions}} + <div class="columns"> + <div class="column-half"> + <ul> + {{#eachEven gateConditions}} + {{> '_overview-gate-condition'}} + {{/eachEven}} + </ul> + </div> + <div class="column-half"> + <ul> + {{#eachOdd gateConditions}} + {{> '_overview-gate-condition'}} + {{/eachOdd}} + </ul> + </div> + </div> +{{/notEmpty}} diff --git a/server/sonar-web/src/main/hbs/overview/overview-issues.hbs b/server/sonar-web/src/main/hbs/overview/overview-issues.hbs index ddc1ebdc350..09940d8cc95 100644 --- a/server/sonar-web/src/main/hbs/overview/overview-issues.hbs +++ b/server/sonar-web/src/main/hbs/overview/overview-issues.hbs @@ -1,24 +1,21 @@ +<header class="overview-card-header page-header"> + <h2 class="page-title">{{t 'overview.issues'}}</h2> +</header> + <div class="overview-highlight"> <h6 class="note">{{t 'overview.issues'}}</h6> <div class="overview-main-measure"> - <a href="{{urlForIssuesOverview componentKey}}">{{formatMeasure issues 'INT'}}</a> - </div> - <div class="spacer-top"> - {{#notNull sqaleRating}} - <a class="overview-measure" href="{{urlForDrilldown componentKey 'sqale_rating'}}"><span class="rating rating-{{formatMeasure sqaleRating 'RATING'}}">{{formatMeasure sqaleRating 'RATING'}}</span></a> - {{/notNull}} - - <a class="overview-measure" href="{{urlForDrilldown componentKey 'sqale_index'}}">{{formatMeasure debt 'WORK_DUR'}}</a> + <a href="{{urlForIssuesOverview componentKey}}">{{formatMeasure issues 'SHORT_INT'}}</a> </div> {{#notNull issues3}} - <div class="spacer-top"> - <a href="{{urlForIssuesOverview componentKey period3Date}}" - class="overview-measure {{#gt issues3 0}}text-danger{{else}}text-success{{/gt}}">{{formatMeasureVariation issues3 'INT'}}</a> - <span class="note">new {{period3Name}}</span> - </div> + <h6 class="note">{{period3Name}}</h6> + <a href="{{urlForIssuesOverview componentKey period3Date}}" + class="overview-measure {{#gt issues3 0}}text-danger{{else}}text-success{{/gt}}">{{formatMeasure issues3 'INT'}}</a> + <span class="note">{{t 'overview.new'}}</span> {{/notNull}} </div> <div class="overview-trend"> - <div class="overview-sparkline" id="overview-issues-trend" data-width="300" data-height="50" data-color="#f3ca8e" data-type="INT"></div> + <div class="overview-sparkline" id="overview-issues-trend" data-height="120" data-color="#f3ca8e" + data-type="SHORT_INT"></div> </div> diff --git a/server/sonar-web/src/main/hbs/overview/overview-main-layout.hbs b/server/sonar-web/src/main/hbs/overview/overview-main-layout.hbs index 8ce2a7d4e4f..827246359f9 100644 --- a/server/sonar-web/src/main/hbs/overview/overview-main-layout.hbs +++ b/server/sonar-web/src/main/hbs/overview/overview-main-layout.hbs @@ -1,18 +1,12 @@ -<div class="overview-card overview-gate" id="overview-gate"></div> - -<div class="overview-container columns"> - <div class="column-half"> - <div class="overview-card" id="overview-size"></div> - </div> +<div class="columns"> <div class="column-half"> + <div class="overview-card overview-gate" id="overview-gate"></div> <div class="overview-card" id="overview-issues"></div> + <div class="overview-card" id="overview-debt"></div> </div> -</div> -<div class="overview-container columns"> <div class="column-half"> + <div class="overview-card" id="overview-size"></div> <div class="overview-card" id="overview-coverage"></div> - </div> - <div class="column-half"> <div class="overview-card" id="overview-duplications"></div> </div> </div> diff --git a/server/sonar-web/src/main/hbs/overview/overview-size.hbs b/server/sonar-web/src/main/hbs/overview/overview-size.hbs index b06b14ed706..907e4727c9f 100644 --- a/server/sonar-web/src/main/hbs/overview/overview-size.hbs +++ b/server/sonar-web/src/main/hbs/overview/overview-size.hbs @@ -1,16 +1,24 @@ +<header class="overview-card-header page-header"> + <h2 class="page-title">{{t 'overview.size'}}</h2> +</header> + <div class="overview-highlight"> - <h6 class="note">{{t 'overview.lines_of_code'}}</h6> + <h6 class="note">{{t 'metric.ncloc.name'}}</h6> <div class="overview-main-measure"> - <a href="{{urlForDrilldown componentKey 'ncloc'}}">{{formatMeasure ncloc 'INT'}}</a> + <a href="{{urlForDrilldown componentKey 'ncloc'}}">{{formatMeasure ncloc 'SHORT_INT'}}</a> </div> + {{#notNull ncloc3}} <div class="spacer-top"> - <span class="overview-measure">{{formatMeasureVariation ncloc3 'INT'}}</span> - <span class="note">{{period3Name}}</span> + <h6 class="note">{{period3Name}}</h6> + <span class="overview-measure">{{formatMeasureVariation ncloc3 'SHORT_INT'}}</span> </div> {{/notNull}} </div> <div class="overview-trend"> - <div class="overview-sparkline" id="overview-size-trend" data-width="300" data-height="50" data-color="#f3ca8e" data-type="INT"></div> + <div class="overview-sparkline" id="overview-size-trend" data-height="120" data-color="#f3ca8e" + data-type="SHORT_INT"></div> </div> + + diff --git a/server/sonar-web/src/main/js/application.js b/server/sonar-web/src/main/js/application.js index 530f2eba504..592c1265493 100644 --- a/server/sonar-web/src/main/js/application.js +++ b/server/sonar-web/src/main/js/application.js @@ -288,6 +288,28 @@ function closeModalWindow () { (function () { + function shortIntFormatter (value) { + var format = '0,0'; + if (value >= 1000) { + format = '0.[0]a'; + } + if (value >= 10000) { + format = '0a'; + } + return numeral(value).format(format); + } + + function shortIntVariationFormatter (value) { + var format = '+0,0'; + if (value >= 1000) { + format = '+0.[0]a'; + } + if (value >= 10000) { + format = '+0a'; + } + return numeral(value).format(format); + } + /** * Check if days should be displayed for a work duration * @param {number} days @@ -324,7 +346,7 @@ function closeModalWindow () { * @returns {string} */ function addSpaceIfNeeded (value) { - return value.length > 0 ? value : value + ' '; + return value.length > 0 ? value + ' ' : value; } /** @@ -402,6 +424,7 @@ function closeModalWindow () { 'INT': function (value) { return numeral(value).format('0,0'); }, + 'SHORT_INT': shortIntFormatter, 'FLOAT': function (value) { return numeral(value).format('0,0.0'); }, @@ -429,6 +452,7 @@ function closeModalWindow () { 'INT': function (value) { return value === 0 ? '0' : numeral(value).format('+0,0'); }, + 'SHORT_INT': shortIntVariationFormatter, 'FLOAT': function (value) { return value === 0 ? '0' : numeral(value).format('+0,0.0'); }, diff --git a/server/sonar-web/src/main/js/common/handlebars-extensions.js b/server/sonar-web/src/main/js/common/handlebars-extensions.js index 86b4286efb4..1a8fb87bef3 100644 --- a/server/sonar-web/src/main/js/common/handlebars-extensions.js +++ b/server/sonar-web/src/main/js/common/handlebars-extensions.js @@ -370,11 +370,14 @@ }); Handlebars.registerHelper('numberShort', function (number) { - if (number > 9999) { - return numeral(number).format('0.[0]a'); - } else { - return numeral(number).format('0,0'); + var format = '0,0'; + if (number >= 10000) { + format = '0.[0]a'; + } + if (number >= 100000) { + format = '0a'; } + return numeral(number).format(format); }); Handlebars.registerHelper('pluginActions', function (actions, options) { diff --git a/server/sonar-web/src/main/js/graphics/sparkline.js b/server/sonar-web/src/main/js/graphics/sparkline.js index 986011c0e58..6d2397f7210 100644 --- a/server/sonar-web/src/main/js/graphics/sparkline.js +++ b/server/sonar-web/src/main/js/graphics/sparkline.js @@ -26,7 +26,7 @@ var defaults = { height: 30, color: '#1f77b4', - interpolate: 'bundle', + interpolate: 'linear', tension: 1, type: 'INT' }; @@ -57,15 +57,17 @@ xScale = d3.time.scale() .domain(d3.extent(data, function (d) { return moment(d.val).toDate(); - })), + })) + .nice(), yScale = d3.scale.linear() .domain(d3.extent(data, function (d) { return d.count; - })), + })) + .nice(), - minValue = yScale.domain()[0], - maxValue = yScale.domain()[1], + xTicks = xScale.ticks(5), + yTicks = yScale.ticks(3), line = d3.svg.line() .x(function (d) { @@ -75,31 +77,13 @@ return yScale(d.count); }) .interpolate(options.interpolate) - .tension(options.tension), - - minLabel = plot.append('text') - .text(window.formatMeasure(minValue, options.type)) - .attr('dy', '3px') - .style('text-anchor', 'end') - .style('font-size', '10px') - .style('font-weight', '300') - .style('fill', '#aaa'), - - maxLabel = plot.append('text') - .text(window.formatMeasure(maxValue, options.type)) - .attr('dy', '5px') - .style('text-anchor', 'end') - .style('font-size', '10px') - .style('font-weight', '300') - .style('fill', '#aaa'), - - maxLabelWidth = Math.max(minLabel.node().getBBox().width, maxLabel.node().getBBox().width) + 3; + .tension(options.tension); _.extend(options, { - marginLeft: 1, - marginRight: 1 + maxLabelWidth, - marginTop: 6, - marginBottom: 6 + marginLeft: 20, + marginRight: 50, + marginTop: 0, + marginBottom: 25 }); _.extend(options, { @@ -111,18 +95,49 @@ xScale.range([0, options.availableWidth]); yScale.range([options.availableHeight, 0]); + xTicks.forEach(function (tick) { + plot.append('text') + .text(xScale.tickFormat()(tick)) + .attr('x', xScale(tick)) + .attr('y', options.availableHeight + 20) + .attr('dy', '0') + .style('text-anchor', 'middle') + .style('font-size', '10px') + .style('font-weight', '300') + .style('fill', '#aaa'); + plot.append('line') + .attr('x1', xScale(tick)) + .attr('x2', xScale(tick)) + .attr('y1', 0) + .attr('y2', options.availableHeight) + .style('stroke', '#eee') + .style('shape-rendering', 'crispedges'); + }); + + yTicks.forEach(function (tick) { + plot.append('text') + .text(window.formatMeasure(tick, options.type)) + .attr('x', options.availableWidth + 50) + .attr('y', yScale(tick)) + .attr('dy', '5px') + .style('text-anchor', 'end') + .style('font-size', '10px') + .style('font-weight', '300') + .style('fill', '#aaa'); + plot.append('line') + .attr('x1', 0) + .attr('x2', options.availableWidth) + .attr('y1', yScale(tick)) + .attr('y2', yScale(tick)) + .style('stroke', '#eee') + .style('shape-rendering', 'crispedges'); + }); + plot.append('path') .datum(data) .attr('d', line) .classed('line', true) .style('stroke', options.color); - - minLabel - .attr('x', options.availableWidth + maxLabelWidth) - .attr('y', yScale(minValue)); - maxLabel - .attr('x', options.availableWidth + maxLabelWidth) - .attr('y', yScale(maxValue)); } ); }; diff --git a/server/sonar-web/src/main/js/overview/app.js b/server/sonar-web/src/main/js/overview/app.js index 4bf5db63969..04f885f2683 100644 --- a/server/sonar-web/src/main/js/overview/app.js +++ b/server/sonar-web/src/main/js/overview/app.js @@ -35,6 +35,7 @@ requirejs([ // add state model this.state = new State(window.overviewConf); + this.state.set('period3Name', 'During Leak Period'); // create and render layout this.layout = new Layout({ diff --git a/server/sonar-web/src/main/js/overview/controller.js b/server/sonar-web/src/main/js/overview/controller.js index f72b99cefdf..69f50d885bd 100644 --- a/server/sonar-web/src/main/js/overview/controller.js +++ b/server/sonar-web/src/main/js/overview/controller.js @@ -22,12 +22,14 @@ define([ 'overview/main/gate-view', 'overview/main/size-view', 'overview/main/issues-view', + 'overview/main/debt-view', 'overview/main/coverage-view', 'overview/main/duplications-view' ], function (MainLayout, GateView, SizeView, IssuesView, + DebtView, CoverageView, DuplicationsView) { @@ -45,6 +47,7 @@ define([ mainLayout.gateRegion.show(new GateView(options)); mainLayout.sizeRegion.show(new SizeView(options)); mainLayout.issuesRegion.show(new IssuesView(options)); + mainLayout.debtRegion.show(new DebtView(options)); mainLayout.coverageRegion.show(new CoverageView(options)); mainLayout.duplicationsRegion.show(new DuplicationsView(options)); this.state.fetch(); diff --git a/server/sonar-web/src/main/js/overview/main/coverage-view.js b/server/sonar-web/src/main/js/overview/main/coverage-view.js index 8b807e4d6ed..28bb1e5c7a0 100644 --- a/server/sonar-web/src/main/js/overview/main/coverage-view.js +++ b/server/sonar-web/src/main/js/overview/main/coverage-view.js @@ -18,8 +18,9 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ define([ + 'overview/trend-view', 'templates/overview' -], function () { +], function (TrendView) { return Marionette.Layout.extend({ template: Templates['overview-coverage'], @@ -32,7 +33,16 @@ define([ var trend = this.model.get('coverageTrend'), hasCoverage = this.model.get('coverage') != null; if (_.size(trend) > 1 && hasCoverage) { - this.$('#overview-coverage-trend').sparkline(this.model.get('coverageTrend')); + this.trendView = new TrendView({ data: trend, type: 'PERCENT' }); + this.trendView.render() + .$el.appendTo(this.$('#overview-coverage-trend')); + this.trendView.update(); + } + }, + + onClose: function () { + if (this.trendView != null) { + this.trendView.detachEvents().remove(); } } }); diff --git a/server/sonar-web/src/main/js/overview/main/debt-view.js b/server/sonar-web/src/main/js/overview/main/debt-view.js new file mode 100644 index 00000000000..80861a82152 --- /dev/null +++ b/server/sonar-web/src/main/js/overview/main/debt-view.js @@ -0,0 +1,50 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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. + */ +define([ + 'overview/trend-view', + 'templates/overview' +], function (TrendView) { + + return Marionette.Layout.extend({ + template: Templates['overview-debt'], + + modelEvents: { + 'change': 'render' + }, + + onRender: function () { + var trend = this.model.get('debtTrend'), + hasDebt = this.model.get('debt') != null; + if (_.size(trend) > 1 && hasDebt) { + this.trendView = new TrendView({ data: trend, type: 'WORK_DUR' }); + this.trendView.render() + .$el.appendTo(this.$('#overview-debt-trend')); + this.trendView.update(); + } + }, + + onClose: function () { + if (this.trendView != null) { + this.trendView.detachEvents().remove(); + } + } + }); + +}); diff --git a/server/sonar-web/src/main/js/overview/main/duplications-view.js b/server/sonar-web/src/main/js/overview/main/duplications-view.js index 18e69af549b..a1310587288 100644 --- a/server/sonar-web/src/main/js/overview/main/duplications-view.js +++ b/server/sonar-web/src/main/js/overview/main/duplications-view.js @@ -18,8 +18,9 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ define([ + 'overview/trend-view', 'templates/overview' -], function () { +], function (TrendView) { return Marionette.Layout.extend({ template: Templates['overview-duplications'], @@ -32,7 +33,16 @@ define([ var trend = this.model.get('duplicationsTrend'), hasDuplications = this.model.get('duplications') != null; if (_.size(trend) > 1 && hasDuplications) { - this.$('#overview-duplications-trend').sparkline(this.model.get('duplicationsTrend')); + this.trendView = new TrendView({ data: trend, type: 'PERCENT' }); + this.trendView.render() + .$el.appendTo(this.$('#overview-duplications-trend')); + this.trendView.update(); + } + }, + + onClose: function () { + if (this.trendView != null) { + this.trendView.detachEvents().remove(); } } }); diff --git a/server/sonar-web/src/main/js/overview/main/issues-view.js b/server/sonar-web/src/main/js/overview/main/issues-view.js index a17a1d9547a..bfda55b2578 100644 --- a/server/sonar-web/src/main/js/overview/main/issues-view.js +++ b/server/sonar-web/src/main/js/overview/main/issues-view.js @@ -18,8 +18,9 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ define([ + 'overview/trend-view', 'templates/overview' -], function () { +], function (TrendView) { return Marionette.Layout.extend({ template: Templates['overview-issues'], @@ -32,7 +33,16 @@ define([ var trend = this.model.get('issuesTrend'), hasIssues = this.model.get('issues') != null; if (_.size(trend) > 1 && hasIssues) { - this.$('#overview-issues-trend').sparkline(this.model.get('issuesTrend')); + this.trendView = new TrendView({ data: trend, type: 'SHORT_INT' }); + this.trendView.render() + .$el.appendTo(this.$('#overview-issues-trend')); + this.trendView.update(); + } + }, + + onClose: function () { + if (this.trendView != null) { + this.trendView.detachEvents().remove(); } } }); diff --git a/server/sonar-web/src/main/js/overview/main/layout.js b/server/sonar-web/src/main/js/overview/main/layout.js index 8db7c1e088d..c0e46deffd5 100644 --- a/server/sonar-web/src/main/js/overview/main/layout.js +++ b/server/sonar-web/src/main/js/overview/main/layout.js @@ -28,6 +28,7 @@ define([ gateRegion: '#overview-gate', sizeRegion: '#overview-size', issuesRegion: '#overview-issues', + debtRegion: '#overview-debt', coverageRegion: '#overview-coverage', duplicationsRegion: '#overview-duplications' }, @@ -39,7 +40,7 @@ define([ toggleRegions: function () { var conditions = this.model.get('gateConditions'), hasGate = _.isArray(conditions) && conditions.length > 0; - this.$(this.gateRegion.el).toggle(hasGate); + this.$(this.gateRegion.el).toggleClass('hidden', !hasGate); } }); diff --git a/server/sonar-web/src/main/js/overview/main/size-view.js b/server/sonar-web/src/main/js/overview/main/size-view.js index db1ab7e41a8..07917e89c52 100644 --- a/server/sonar-web/src/main/js/overview/main/size-view.js +++ b/server/sonar-web/src/main/js/overview/main/size-view.js @@ -18,8 +18,9 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ define([ + 'overview/trend-view', 'templates/overview' -], function () { +], function (TrendView) { return Marionette.Layout.extend({ template: Templates['overview-size'], @@ -32,7 +33,16 @@ define([ var trend = this.model.get('sizeTrend'), hasSize = this.model.get('ncloc') != null; if (_.size(trend) > 1 && hasSize) { - this.$('#overview-size-trend').sparkline(this.model.get('sizeTrend')); + this.trendView = new TrendView({ data: trend, type: 'SHORT_INT' }); + this.trendView.render() + .$el.appendTo(this.$('#overview-size-trend')); + this.trendView.update(); + } + }, + + onClose: function () { + if (this.trendView != null) { + this.trendView.detachEvents().remove(); } } }); diff --git a/server/sonar-web/src/main/js/overview/models/state.js b/server/sonar-web/src/main/js/overview/models/state.js index 6b19a415287..d3be4446ee1 100644 --- a/server/sonar-web/src/main/js/overview/models/state.js +++ b/server/sonar-web/src/main/js/overview/models/state.js @@ -24,9 +24,10 @@ define(function () { SIZE_METRIC = 'ncloc', ISSUES_METRIC = 'violations', DEBT_METRIC = 'sqale_index', + NEW_DEBT_METRIC = 'new_technical_debt', SQALE_RATING_METRIC = 'sqale_rating', - COVERAGE_METRIC = 'overall_coverage', - NEW_COVERAGE_METRIC = 'new_overall_coverage', + COVERAGE_METRIC = 'coverage', + NEW_COVERAGE_METRIC = 'new_coverage', DUPLICATIONS_METRIC = 'duplicated_lines_density'; return Backbone.Model.extend({ @@ -68,6 +69,7 @@ define(function () { GATE_METRIC, SIZE_METRIC, DEBT_METRIC, + NEW_DEBT_METRIC, SQALE_RATING_METRIC, COVERAGE_METRIC, NEW_COVERAGE_METRIC, @@ -148,10 +150,14 @@ define(function () { parseDebt: function (msr) { var debtMeasure = _.findWhere(msr, { key: DEBT_METRIC }), + newDebtMeasure = _.findWhere(msr, { key: NEW_DEBT_METRIC }), sqaleRatingMeasure = _.findWhere(msr, { key: SQALE_RATING_METRIC }); if (debtMeasure != null) { this.set({ debt: debtMeasure.val }); } + if (newDebtMeasure != null) { + this.set({ newDebt: newDebtMeasure.var3 }); + } if (sqaleRatingMeasure != null) { this.set({ sqaleRating: sqaleRatingMeasure.val }); } @@ -191,6 +197,7 @@ define(function () { metrics: [ SIZE_METRIC, ISSUES_METRIC, + DEBT_METRIC, COVERAGE_METRIC, DUPLICATIONS_METRIC ].join(',') @@ -199,6 +206,7 @@ define(function () { if (_.isArray(r)) { that.parseSizeTrend(r[0]); that.parseIssuesTrend(r[0]); + that.parseDebtTrend(r[0]); that.parseCoverageTrend(r[0]); that.parseDuplicationsTrend(r[0]); } @@ -224,6 +232,10 @@ define(function () { this.parseTrend(r, 'issuesTrend', ISSUES_METRIC); }, + parseDebtTrend: function (r) { + this.parseTrend(r, 'debtTrend', DEBT_METRIC); + }, + parseCoverageTrend: function (r) { this.parseTrend(r, 'coverageTrend', COVERAGE_METRIC); }, diff --git a/server/sonar-web/src/main/js/overview/trend-view.js b/server/sonar-web/src/main/js/overview/trend-view.js new file mode 100644 index 00000000000..aeaac5ad697 --- /dev/null +++ b/server/sonar-web/src/main/js/overview/trend-view.js @@ -0,0 +1,163 @@ +define(function () { + + function trans (left, top) { + return 'translate(' + left + ', ' + top + ')'; + } + + var $ = jQuery; + + + return Backbone.View.extend({ + + initialize: function (options) { + this.data = options.data; + this.type = options.type || 'INT'; + this.width = 0; + this.height = 0; + }, + + attachEvents: function () { + this.detachEvents(); + var event = 'resize.trend-' + this.cid, + update = _.throttle(_.bind(this.update, this), 50); + $(window).on(event, update); + }, + + detachEvents: function () { + var event = 'resize.trend-' + this.cid; + $(window).off(event); + return this; + }, + + render: function () { + var that = this, + data = this.data; + this.container = d3.select(this.el); + this.svg = this.container.append('svg') + .classed('sonar-d3', true); + this.plot = this.svg.append('g') + .classed('plot', true); + + this.xScale = d3.time.scale() + .domain(d3.extent(data, function (d) { + return moment(d.val).toDate(); + })) + .nice(); + this.yScale = d3.scale.linear() + .domain(d3.extent(data, function (d) { + return d.count; + })) + .nice(); + + this.line = d3.svg.line() + .x(function (d) { + return that.xScale(moment(d.val).toDate()); + }) + .y(function (d) { + return that.yScale(d.count); + }) + .interpolate('linear'); + + this.xScaleTicks = this.xScale.ticks(5); + this.yScaleTicks = this.yScale.ticks(3); + + this.xTicks = this.xScaleTicks.map(function (tick) { + return that.plot.append('text') + .datum(tick) + .text(that.xScale.tickFormat()(tick)) + .attr('dy', '0') + .style('text-anchor', 'middle') + .style('font-size', '10px') + .style('font-weight', '300') + .style('fill', '#aaa'); + }); + this.yTicks = this.yScaleTicks.map(function (tick) { + return that.plot.append('text') + .datum(tick) + .text(window.formatMeasure(tick, that.type)) + .attr('dy', '5px') + .style('text-anchor', 'end') + .style('font-size', '10px') + .style('font-weight', '300') + .style('fill', '#aaa'); + }); + + this.xTickLines = this.xScaleTicks.map(function (tick) { + return that.plot.append('line') + .datum(tick) + .style('stroke', '#eee') + .style('shape-rendering', 'crispedges'); + }); + this.yTickLines = this.yScaleTicks.map(function (tick) { + return that.plot.append('line') + .datum(tick) + .style('stroke', '#eee') + .style('shape-rendering', 'crispedges'); + }); + + this.path = this.plot.append('path') + .datum(data) + .classed('line', true) + .style('stroke', 'rgb(31, 119, 180)'); + + this.attachEvents(); + + return this; + }, + + update: function () { + var that = this, + width = this.$el.closest('.overview-trend').width(), + height = 150, + marginLeft = 20, + marginRight = 50, + marginTop = 5, + marginBottom = 25, + availableWidth = width - marginLeft - marginRight, + availableHeight = height - marginTop - marginBottom; + + this.svg + .attr('width', width) + .attr('height', height); + + this.plot.attr('transform', trans(marginLeft, marginTop)); + this.xScale.range([0, availableWidth]); + this.yScale.range([availableHeight, 0]); + + this.path + .attr('d', this.line); + + this.xTicks.forEach(function (tick) { + tick + .attr('x', that.xScale(tick.datum())) + .attr('y', availableHeight + 20); + }); + + this.yTicks.forEach(function (tick) { + tick + .attr('x', availableWidth + 50) + .attr('y', that.yScale(tick.datum())); + }); + + this.xTickLines.forEach(function (tick) { + tick + .attr('x1', that.xScale(tick.datum())) + .attr('x2', that.xScale(tick.datum())) + .attr('y1', 0) + .attr('y2', availableHeight); + }); + + this.yTickLines.forEach(function (tick) { + tick + .attr('x1', 0) + .attr('x2', availableWidth) + .attr('y1', that.yScale(tick.datum())) + .attr('y2', that.yScale(tick.datum())); + }); + + return this; + } + + }); + +}); diff --git a/server/sonar-web/src/main/less/components/columns.less b/server/sonar-web/src/main/less/components/columns.less index 0e4b489e74b..699573084ee 100644 --- a/server/sonar-web/src/main/less/components/columns.less +++ b/server/sonar-web/src/main/less/components/columns.less @@ -37,7 +37,14 @@ .column-third { float: left; - width: 33%; + width: 33.3333333333%; + padding: 0 10px; + .box-sizing(border-box); +} + +.column-two-thirds { + float: left; + width: 66.6666666667%; padding: 0 10px; .box-sizing(border-box); } diff --git a/server/sonar-web/src/main/less/pages/overview.less b/server/sonar-web/src/main/less/pages/overview.less index 311e5bb6bc4..4582868d23e 100644 --- a/server/sonar-web/src/main/less/pages/overview.less +++ b/server/sonar-web/src/main/less/pages/overview.less @@ -28,13 +28,14 @@ } .overview-card { - padding-top: 5px; - background: @white; + .clearfix; + padding: 20px; border: 1px solid @barBorderColor; + background: @white; } -.overview-gate { - padding: 10px; +.overview-card:not(.hidden) + .overview-card { + margin-top: 20px; } .overview-gate-ok { border: 2px solid @green; } @@ -43,13 +44,17 @@ .overview-gate-error { border: 2px solid @red; } +.overview-gate-condition { + padding-left: 1px; +} + .overview-container { margin-top: 20px; - text-align: center; } .overview-card-header { - padding-bottom: 5px; + margin-bottom: 20px; + padding-bottom: 20px; border-bottom: 1px solid @barBorderColor; } @@ -57,7 +62,6 @@ margin: 0; padding: 0 6px; color: #fff !important; - font-size: 24px; font-weight: 300; a& { @@ -74,31 +78,29 @@ .overview-status-ERROR { background-color: @red; } .overview-highlight { - height: 145px; -} - -.overview-main-measure { - line-height: 1.3; - font-size: 36px; - font-weight: 300; + float: left; + width: 160px; + padding-right: 20px; + .box-sizing(border-box); } .overview-trend { - height: 50px; - margin-top: 15px; - padding: 5px 10px; - border-top: 1px solid @barBorderColor; - background-color: #fdfdfd; + padding-left: 160px; .note { font-size: 10px; } } -.overview-sparkline { - display: inline-block; - vertical-align: middle; +.overview-main-measure { + line-height: 1.3; + margin-bottom: 20px; + font-size: 36px; + font-weight: 300; + + .rating { font-size: 30px; } } .overview-measure { - font-size: 16px; + line-height: 1.5; + font-size: 20px; font-weight: 300; } diff --git a/server/sonar-web/src/test/js/overview.js b/server/sonar-web/src/test/js/overview.js index 0a1cb43e0f1..78c2dcbb23a 100644 --- a/server/sonar-web/src/test/js/overview.js +++ b/server/sonar-web/src/test/js/overview.js @@ -26,7 +26,7 @@ lib.changeWorkingDirectory('overview'); lib.configureCasper(); -casper.test.begin(testName(), 23, function (test) { +casper.test.begin(testName(), 22, function (test) { casper .start(lib.buildUrl('overview'), function () { lib.setDefaultViewport(); @@ -44,7 +44,7 @@ casper.test.begin(testName(), 23, function (test) { }) .then(function () { - casper.waitForText('165,077'); + casper.waitForText('165k'); }) .then(function () { @@ -58,17 +58,17 @@ casper.test.begin(testName(), 23, function (test) { test.assertElementCount('#overview-gate .overview-status-WARN', 1); test.assertElementCount('#overview-gate .overview-status-OK', 5); - test.assertSelectorContains('#overview-size', '165,077'); - test.assertSelectorContains('#overview-size', '+3,916'); + test.assertSelectorContains('#overview-size', '165k'); + test.assertSelectorContains('#overview-size', '+3.9k'); test.assertExists('#overview-size-trend path'); - test.assertSelectorContains('#overview-issues', '1,605'); - test.assertSelectorContains('#overview-issues', 'A'); - test.assertSelectorContains('#overview-issues', '66'); + test.assertSelectorContains('#overview-issues', '1.6k'); test.assertExists('#overview-issues-trend path'); + test.assertSelectorContains('#overview-debt', '66'); + test.assertExists('#overview-debt-trend path'); + test.assertSelectorContains('#overview-coverage', '83.9%'); - test.assertSelectorContains('#overview-coverage', '+0.6%'); test.assertSelectorContains('#overview-coverage', '90.0%'); test.assertExists('#overview-coverage-trend path'); diff --git a/server/sonar-web/src/test/json/overview/measures.json b/server/sonar-web/src/test/json/overview/measures.json index 33c235ca575..826a1ab308d 100644 --- a/server/sonar-web/src/test/json/overview/measures.json +++ b/server/sonar-web/src/test/json/overview/measures.json @@ -74,7 +74,7 @@ "fvar3": "0" }, { - "key": "overall_coverage", + "key": "coverage", "val": 83.9, "frmt_val": "83.9%", "trend": 0, @@ -85,7 +85,7 @@ "fvar3": "0.6%" }, { - "key": "new_overall_coverage", + "key": "new_coverage", "var1": 88.2352941176471, "fvar1": "88.2%", "var2": 87.9254132585941, diff --git a/server/sonar-web/src/test/json/overview/timemachine.json b/server/sonar-web/src/test/json/overview/timemachine.json index a2d4391d1e7..1ac438fc2bb 100644 --- a/server/sonar-web/src/test/json/overview/timemachine.json +++ b/server/sonar-web/src/test/json/overview/timemachine.json @@ -11,7 +11,7 @@ "metric": "sqale_index" }, { - "metric": "overall_coverage" + "metric": "coverage" }, { "metric": "duplicated_lines_density" diff --git a/server/sonar-web/src/test/views/overview.jade b/server/sonar-web/src/test/views/overview.jade index 7f50024c955..4e381f1ce6d 100644 --- a/server/sonar-web/src/test/views/overview.jade +++ b/server/sonar-web/src/test/views/overview.jade @@ -14,4 +14,3 @@ block body componentKey: 'org.codehaus.sonar:sonar', componentUuid: '69e57151-be0d-4157-adff-c06741d88879' }; - require(['overview/app']); diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 16d538afaff..0b1657038ba 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -2259,7 +2259,7 @@ metric.generated_ncloc.abbreviation=Gen. LOC metric.lines.name=Lines metric.lines.description=Lines -metric.ncloc.name=Lines of code +metric.ncloc.name=Lines of Code metric.ncloc.description=Non Commenting Lines of Code metric.ncloc.abbreviation=LOC @@ -2533,6 +2533,7 @@ metric.duplicated_files.description=Duplicated files metric.duplicated_files.abbreviation=Dup. files metric.duplicated_lines_density.name=Duplicated lines (%) +metric.duplicated_lines_density.short_name=Duplications metric.duplicated_lines_density.description=Duplicated lines balanced by statements metric.duplicated_lines_density.abbreviation=Dup. lines(%) @@ -2993,8 +2994,11 @@ workspace.close=Remove from the list of pinned files # OVERVIEW # #------------------------------------------------------------------------------ -overview.lines_of_code=Lines of Code +overview.size=Size +overview.gate=Quality Gate overview.issues=Issues -overview.debt=Debt -overview.coverage=Coverage +overview.debt=Technical Debt +overview.testing=Testing overview.duplications=Duplications +overview.new=new +overview.on_new_code=on new code |