From 95a49ae41384bef3fef6fffebc681917124da412 Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Fri, 15 Apr 2016 13:16:27 +0200 Subject: [PATCH] SONAR-7402 improve measures home page (#900) --- .../apps/component-measures/config/domains.js | 74 ++++++++++++- .../home/AllMeasuresDomain.js | 29 +---- .../component-measures/home/DomainMeasures.js | 52 +-------- .../js/apps/component-measures/home/Home.js | 5 + .../home/HomeMeasuresList.js | 104 ++++++++++++++++++ .../component-measures/home/MeasuresList.js | 14 +-- .../js/apps/component-measures/styles.css | 102 +++++++++++------ .../src/main/less/components/ui.less | 1 - 8 files changed, 261 insertions(+), 120 deletions(-) create mode 100644 server/sonar-web/src/main/js/apps/component-measures/home/HomeMeasuresList.js diff --git a/server/sonar-web/src/main/js/apps/component-measures/config/domains.js b/server/sonar-web/src/main/js/apps/component-measures/config/domains.js index d7e52100202..d4f2611c02b 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/config/domains.js +++ b/server/sonar-web/src/main/js/apps/component-measures/config/domains.js @@ -19,6 +19,11 @@ */ export const domains = { 'Reliability': { + main: [ + 'bugs', + 'new_bugs', + 'reliability_rating' + ], order: [ 'bugs', 'new_bugs', @@ -29,6 +34,11 @@ export const domains = { }, 'Security': { + main: [ + 'vulnerabilities', + 'new_vulnerabilities', + 'security_rating' + ], order: [ 'vulnerabilities', 'new_vulnerabilities', @@ -39,12 +49,17 @@ export const domains = { }, 'Maintainability': { + main: [ + 'code_smells', + 'new_code_smells', + 'sqale_rating' + ], order: [ 'code_smells', 'new_code_smells', + 'sqale_rating', 'sqale_index', 'new_technical_debt', - 'sqale_rating', 'sqale_debt_ratio', 'new_sqale_debt_ratio', 'effort_to_reach_maintainability_rating_a' @@ -52,6 +67,15 @@ export const domains = { }, 'Tests': { + main: [ + 'overall_coverage', + 'coverage', + 'it_coverage', + 'new_overall_coverage', + 'new_coverage', + 'new_it_coverage', + 'tests' + ], order: [ 'overall_coverage', 'new_overall_coverage', @@ -107,6 +131,9 @@ export const domains = { }, 'Duplication': { + main: [ + 'duplicated_lines_density' + ], order: [ 'duplicated_lines_density', 'duplicated_blocks', @@ -116,12 +143,55 @@ export const domains = { }, 'Size': { - order: [ + main: [ 'ncloc' + ], + order: [ + 'ncloc', + 'lines', + 'statements', + 'functions', + 'classes', + 'files', + 'directories' + ] + }, + + 'Complexity': { + main: [ + 'complexity' + ], + order: [ + 'complexity', + 'function_complexity', + 'file_complexity', + 'class_complexity' + ] + }, + + 'Documentation': { + main: [ + 'comment_lines_density' + ], + order: [ + 'comment_lines_density' + ] + }, + + 'General': { + main: [ + 'alert_status' + ], + order: [ + 'alert_status' ] }, 'Issues': { + main: [ + 'violations', + 'new_violations' + ], order: [ 'violations', 'new_violations', diff --git a/server/sonar-web/src/main/js/apps/component-measures/home/AllMeasuresDomain.js b/server/sonar-web/src/main/js/apps/component-measures/home/AllMeasuresDomain.js index 236775e00f7..8403346f9ec 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/home/AllMeasuresDomain.js +++ b/server/sonar-web/src/main/js/apps/component-measures/home/AllMeasuresDomain.js @@ -17,30 +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 sortBy from '../../../../../../node_modules/lodash/sortBy'; -import partition from '../../../../../../node_modules/lodash/partition'; import React from 'react'; -import MeasuresList from './MeasuresList'; -import { domains } from '../config/domains'; - -const sortMeasures = (measures, order) => { - const [known, unknown] = partition(measures, measure => order.includes(measure.metric.key)); - return [ - ...sortBy(known, measure => order.indexOf(measure.metric.key)), - ...sortBy(unknown, measure => measure.metric.name) - ]; -}; +import HomeMeasuresList from './HomeMeasuresList'; export default class AllMeasuresDomain extends React.Component { render () { - const { domain, component, leakPeriodLabel, displayHeader } = this.props; - - const hasLeak = !!leakPeriodLabel; - const { measures } = domain; - const domainConfig = domains[domain.name] || { order: [] }; - const orderedMeasures = domainConfig.order; - const sortedMeasures = sortMeasures(measures, orderedMeasures); + const { domain, component, displayHeader } = this.props; return (
  • @@ -50,11 +33,9 @@ export default class AllMeasuresDomain extends React.Component { )} - +
  • ); } diff --git a/server/sonar-web/src/main/js/apps/component-measures/home/DomainMeasures.js b/server/sonar-web/src/main/js/apps/component-measures/home/DomainMeasures.js index d7610e48ad4..00d84915590 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/home/DomainMeasures.js +++ b/server/sonar-web/src/main/js/apps/component-measures/home/DomainMeasures.js @@ -17,65 +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. */ -import sortBy from '../../../../../../node_modules/lodash/sortBy'; -import partition from '../../../../../../node_modules/lodash/partition'; import React from 'react'; -import MeasuresList from './MeasuresList'; +import HomeMeasuresList from './HomeMeasuresList'; import MeasureBubbleChartContainer from '../components/bubbleChart/MeasureBubbleChartContainer'; -import { getLeakPeriodLabel } from '../../../helpers/periods'; import { hasBubbleChart } from '../utils'; -import { domains as domainsConf } from '../config/domains'; - -const sortMeasures = (measures, order) => { - const [known, unknown] = partition(measures, measure => order.includes(measure.metric.key)); - return [ - ...sortBy(known, measure => order.indexOf(measure.metric.key)), - ...sortBy(unknown, measure => measure.metric.name) - ]; -}; - -const filterCoverageMeasures = measures => { - const hasOverallCoverage = !!measures.find(measure => measure.metric.key === 'overall_coverage'); - const hasUTCoverage = !!measures.find(measure => measure.metric.key === 'coverage'); - const hasITCoverage = !!measures.find(measure => measure.metric.key === 'it_coverage'); - - // display overall coverage only if all types of coverage exist - const shouldShowOverallCoverage = hasOverallCoverage && hasUTCoverage && hasITCoverage; - - // skip if we should display overall coverage - if (shouldShowOverallCoverage) { - return measures; - } - - // otherwise, hide all overall coverage measures - return measures.filter(measure => { - return measure.metric.key.indexOf('overall_') !== 0 && - measure.metric.key.indexOf('new_overall_') !== 0; - }); -}; export default class DomainMeasures extends React.Component { render () { - const { component, domains, periods } = this.props; + const { component, domains } = this.props; const { domainName } = this.props.params; const domain = domains.find(d => d.name === domainName); - const { measures } = domain; - const leakPeriodLabel = getLeakPeriodLabel(periods); - - const filteredMeasures = filterCoverageMeasures(measures); - const conf = domainsConf[domainName]; - const order = conf ? conf.order : []; - const spaces = conf && conf.spaces ? conf.spaces : []; - const sortedMeasures = sortMeasures(filteredMeasures, order); return (
    - + {hasBubbleChart(domainName) && ( diff --git a/server/sonar-web/src/main/js/apps/component-measures/home/Home.js b/server/sonar-web/src/main/js/apps/component-measures/home/Home.js index a9e4dd58e9f..084d75fc0c9 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/home/Home.js +++ b/server/sonar-web/src/main/js/apps/component-measures/home/Home.js @@ -25,10 +25,15 @@ import { translate, translateWithParameters } from '../../../helpers/l10n'; export default class Home extends React.Component { componentDidMount () { + document.querySelector('html').classList.add('dashboard-page'); this.props.onDisplay(); this.props.fetchMeasures(); } + componentWillUnmount () { + document.querySelector('html').classList.remove('dashboard-page'); + } + render () { const { component, domains, periods } = this.props; diff --git a/server/sonar-web/src/main/js/apps/component-measures/home/HomeMeasuresList.js b/server/sonar-web/src/main/js/apps/component-measures/home/HomeMeasuresList.js new file mode 100644 index 00000000000..d8eaa2bf8fe --- /dev/null +++ b/server/sonar-web/src/main/js/apps/component-measures/home/HomeMeasuresList.js @@ -0,0 +1,104 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 React from 'react'; +import partition from 'lodash/partition'; +import sortBy from 'lodash/sortBy'; + +import MeasuresList from './MeasuresList'; +import { domains } from '../config/domains'; + +function sortMeasures (measures, order) { + const [known, unknown] = partition(measures, measure => order.includes(measure.metric.key)); + return [ + ...sortBy(known, measure => order.indexOf(measure.metric.key)), + ...sortBy(unknown, measure => measure.metric.name) + ]; +} + +function filterCoverageMeasures (measures) { + const hasOverallCoverage = !!measures.find(measure => measure.metric.key === 'overall_coverage'); + const hasUTCoverage = !!measures.find(measure => measure.metric.key === 'coverage'); + const hasITCoverage = !!measures.find(measure => measure.metric.key === 'it_coverage'); + + // display overall coverage only if all types of coverage exist + const shouldShowOverallCoverage = hasOverallCoverage && hasUTCoverage && hasITCoverage; + + // skip if we should display overall coverage + if (shouldShowOverallCoverage) { + return measures; + } + + // otherwise, hide all overall coverage measures + return measures.filter(measure => { + return measure.metric.key.indexOf('overall_') !== 0 && + measure.metric.key.indexOf('new_overall_') !== 0; + }); +} + +function filterIssuesMeasures (measures) { + const BANNED_MEASURES = [ + 'blocker_violations', + 'new_blocker_violations', + 'critical_violations', + 'new_critical_violations', + 'major_violations', + 'new_major_violations', + 'minor_violations', + 'new_minor_violations', + 'info_violations', + 'new_info_violations' + ]; + return measures.filter(measure => !BANNED_MEASURES.includes(measure.metric.key)); +} + +const HomeMeasuresList = ({ domain, component }) => { + const { measures, name } = domain; + const config = domains[name] || {}; + + const filteredMeasures = filterCoverageMeasures(filterIssuesMeasures(measures)); + + const configMain = config.main || []; + const [mainMeasures, otherMeasures] = partition(filteredMeasures, measure => configMain.includes(measure.metric.key)); + + const configOrder = config.order || []; + const sortedMainMeasures = sortMeasures(mainMeasures, configOrder); + const sortedOtherMeasures = sortMeasures(otherMeasures, configOrder); + + return ( +
    + {sortedMainMeasures.length > 0 && ( + + )} + + {sortedOtherMeasures.length > 0 && ( + + )} +
    + ); +}; + +export default HomeMeasuresList; diff --git a/server/sonar-web/src/main/js/apps/component-measures/home/MeasuresList.js b/server/sonar-web/src/main/js/apps/component-measures/home/MeasuresList.js index 399923a915b..9ed099b4924 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/home/MeasuresList.js +++ b/server/sonar-web/src/main/js/apps/component-measures/home/MeasuresList.js @@ -19,22 +19,16 @@ */ import React from 'react'; import { Link } from 'react-router'; -import classNames from 'classnames'; import MeasureListValue from './MeasureListValue'; -const shouldPutSpace = (spaces, measure, index) => { - return index !== 0 && spaces.includes(measure.metric.key); -}; - -const MeasuresList = ({ measures, component, spaces }) => { +const MeasuresList = ({ measures, component, className = 'domain-measures' }) => { return ( -
      - {measures.map((measure, index) => ( +
        + {measures.map(measure => (
      • + id={`measure-${measure.metric.key}`}>
        diff --git a/server/sonar-web/src/main/js/apps/component-measures/styles.css b/server/sonar-web/src/main/js/apps/component-measures/styles.css index 6864b465258..b0adf4f3527 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/styles.css +++ b/server/sonar-web/src/main/js/apps/component-measures/styles.css @@ -2,14 +2,19 @@ display: flex; justify-content: space-between; align-items: flex-start; - margin-bottom: 30px; + margin-bottom: 20px; } .measures-domains { } .measures-domains > li { - padding-bottom: 20px; + margin-bottom: 20px; +} + +.measures-domains > li > div { + border: 1px solid #e6e6e6; + background-color: #fff; } .measures-domains-leak-header { @@ -20,7 +25,51 @@ white-space: nowrap; } +.main-domain-measures { + display: flex; + flex-wrap: wrap; + justify-content: space-around; + float: left; + width: 480px; + margin-right: 60px; +} + +.main-domain-measures > li { + padding: 12px 0; +} + +.main-domain-measures > li > a { + display: flex; + flex-direction: column-reverse; + width: 160px; + border: none; + text-align: center; +} + +.main-domain-measures .domain-measures-value { + height: 40px; + box-sizing: border-box; + color: #444; + font-size: 30px; + font-weight: 300; +} + +.main-domain-measures .domain-measures-name { + margin-top: 8px; +} + +.main-domain-measures .domain-measures-name > span { + border-bottom: 1px solid #cae3f2; +} + +.main-domain-measures .domain-measures-leak { + margin: 0 20px; + border: 1px solid #eae3c7; + background-color: #fbf3d5; +} + .domain-measures { + overflow: hidden; line-height: 1.4; } @@ -38,61 +87,35 @@ background-color: #ecf6fe !important; } -.domain-measures-name, -.domain-measures-value { +.domain-measures .domain-measures-name, +.domain-measures .domain-measures-value { padding: 7px 10px; box-sizing: border-box; } -.domain-measures-name { +.domain-measures .domain-measures-name { width: calc(100% - 160px); } -.domain-measures-name > span { +.domain-measures .domain-measures-name > span { border-bottom: 1px solid #cae3f2; } -.domain-measures-value { +.domain-measures .domain-measures-value { width: 80px; color: #444; text-align: right; } -.domain-measures-leak { +.domain-measures .domain-measures-leak { background-color: #fbf3d5; transition: background-color 0.3s ease; } -.domain-measures > li:nth-child(odd) .domain-measures-leak { +.domain-measures .domain-measures > li:nth-child(odd) .domain-measures-leak { background-color: #f5eed0; } -.domain-main-measures { - display: flex; - flex-wrap: wrap; - padding: 0 10px; -} - -.domain-main-measures > li { - margin-right: 40px; - margin-bottom: 30px; -} - -.domain-main-measures-value { - line-height: 1; - font-size: 24px; - font-weight: 300; -} - -.domain-main-measures-leak { - -} - -.domain-main-measures-label { - margin-top: 4px; - white-space: nowrap; -} - .measure-details { margin-top: 10px; } @@ -267,8 +290,10 @@ .measure-details-bubble-chart { position: relative; - margin: 10px 0; + margin: 40px 0 10px; padding: 30px 0 30px 60px; + border: 1px solid #e6e6e6; + background-color: #fff; } .measure-details-bubble-chart-axis { @@ -326,3 +351,8 @@ .component-measures-breadcrumbs > li:last-child::after { display: none; } + +.home-measures-list { + border: 1px solid #e6e6e6; + background-color: #fff; +} diff --git a/server/sonar-web/src/main/less/components/ui.less b/server/sonar-web/src/main/less/components/ui.less index a8916cb5e12..9156682be88 100644 --- a/server/sonar-web/src/main/less/components/ui.less +++ b/server/sonar-web/src/main/less/components/ui.less @@ -272,7 +272,6 @@ padding: 3px 10px; border: 1px solid transparent; border-radius: 24px; - background-color: #fff; color: @darkBlue; transition: none; -- 2.39.5