]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7402 improve measures home page (#900)
authorStas Vilchik <vilchiks@gmail.com>
Fri, 15 Apr 2016 11:16:27 +0000 (13:16 +0200)
committerStas Vilchik <vilchiks@gmail.com>
Fri, 15 Apr 2016 11:16:27 +0000 (13:16 +0200)
server/sonar-web/src/main/js/apps/component-measures/config/domains.js
server/sonar-web/src/main/js/apps/component-measures/home/AllMeasuresDomain.js
server/sonar-web/src/main/js/apps/component-measures/home/DomainMeasures.js
server/sonar-web/src/main/js/apps/component-measures/home/Home.js
server/sonar-web/src/main/js/apps/component-measures/home/HomeMeasuresList.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/component-measures/home/MeasuresList.js
server/sonar-web/src/main/js/apps/component-measures/styles.css
server/sonar-web/src/main/less/components/ui.less

index d7e5210020266eeb9f0ea3a1ac681ff8eb149e22..d4f2611c02b172feec456aff5a20e220b61946ea 100644 (file)
  */
 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',
index 236775e00f7a1ff82e515d9cb6ab751da390650e..8403346f9ec1aebaf8ed0e907e95f20a60478d4d 100644 (file)
  * 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 (
         <li>
@@ -50,11 +33,9 @@ export default class AllMeasuresDomain extends React.Component {
               </header>
           )}
 
-          <MeasuresList
-              measures={sortedMeasures}
-              hasLeak={hasLeak}
-              component={component}
-              spaces={domainConfig.spaces || []}/>
+          <HomeMeasuresList
+              domain={domain}
+              component={component}/>
         </li>
     );
   }
index d7610e48ad4ba262a687cb192c178311d43f4eff..00d84915590df4bab4cfa12dd9e489012042ce6a 100644 (file)
  * 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 (
         <section id="component-measures-domain">
-          <MeasuresList
-              measures={sortedMeasures}
-              component={component}
-              spaces={spaces}
-              hasLeak={leakPeriodLabel != null}/>
+          <HomeMeasuresList
+              domain={domain}
+              component={component}/>
 
           {hasBubbleChart(domainName) && (
               <MeasureBubbleChartContainer domainName={domainName}/>
index a9e4dd58e9fba3ad7ecd6df9e069e01bb7eb4612..084d75fc0c9851067860b3ea09ba9243d52c22f8 100644 (file)
@@ -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 (file)
index 0000000..d8eaa2b
--- /dev/null
@@ -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 (
+      <div className="home-measures-list clearfix">
+        {sortedMainMeasures.length > 0 && (
+            <MeasuresList
+                className="main-domain-measures"
+                measures={sortedMainMeasures}
+                component={component}
+                spaces={[]}/>
+        )}
+
+        {sortedOtherMeasures.length > 0 && (
+            <MeasuresList
+                measures={sortedOtherMeasures}
+                component={component}
+                spaces={[]}/>
+        )}
+      </div>
+  );
+};
+
+export default HomeMeasuresList;
index 399923a915b785ba2f8aaf72eeb50530d668d909..9ed099b4924472b6dd8a3e67f2a008a75f4144b6 100644 (file)
  */
 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 (
-      <ul className="domain-measures">
-        {measures.map((measure, index) => (
+      <ul className={className}>
+        {measures.map(measure => (
             <li
                 key={measure.metric.key}
-                id={`measure-${measure.metric.key}`}
-                className={classNames({ 'big-spacer-top': shouldPutSpace(spaces, measure, index) })}>
+                id={`measure-${measure.metric.key}`}>
               <Link to={{ pathname: `metric/${measure.metric.key}`, query: { id: component.key } }}>
                 <div className="domain-measures-name">
                     <span id={`measure-${measure.metric.key}-name`}>
index 6864b46525877650b23481ea8fcf80f36aa23169..b0adf4f3527bc260260cbabe33b10bc2708a0469 100644 (file)
@@ -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 {
   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;
 }
 
   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;
 }
 
 .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 {
 .component-measures-breadcrumbs > li:last-child::after {
   display: none;
 }
+
+.home-measures-list {
+  border: 1px solid #e6e6e6;
+  background-color: #fff;
+}
index a8916cb5e12050f0a2baa8daf7a26edec16f6a8b..9156682be88de88f9f0becddbbfd1f71331e33ba 100644 (file)
       padding: 3px 10px;
       border: 1px solid transparent;
       border-radius: 24px;
-      background-color: #fff;
       color: @darkBlue;
       transition: none;