]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6331 improve rendering of domain tabs
authorStas Vilchik <vilchiks@gmail.com>
Tue, 27 Oct 2015 12:57:01 +0000 (13:57 +0100)
committerStas Vilchik <vilchiks@gmail.com>
Tue, 27 Oct 2015 12:57:09 +0000 (13:57 +0100)
12 files changed:
server/sonar-web/src/main/js/apps/overview/general/card.js
server/sonar-web/src/main/js/apps/overview/general/details-link.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/overview/general/details.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/overview/general/gate-empty.js
server/sonar-web/src/main/js/apps/overview/general/main.js
server/sonar-web/src/main/js/apps/overview/general/nutshell-coverage.js
server/sonar-web/src/main/js/apps/overview/general/nutshell-dups.js
server/sonar-web/src/main/js/apps/overview/general/nutshell-issues.js
server/sonar-web/src/main/js/apps/overview/general/nutshell-size.js
server/sonar-web/src/main/js/apps/overview/general/nutshell.js
server/sonar-web/src/main/js/apps/overview/main.js
server/sonar-web/src/main/less/pages/overview.less

index 1d1d563db8d36977e3accaabf5527d3a2a9f7061..3d0c474a7e7949ad2be024e3cdcc01f3fc4c6108 100644 (file)
@@ -2,18 +2,7 @@ import React from 'react';
 import classNames from 'classnames';
 
 export default React.createClass({
-  handleClick() {
-    if (this.props.linkTo) {
-      let tab = React.findDOMNode(this);
-      this.props.onRoute(this.props.linkTo, tab);
-    }
-  },
-
   render() {
-    let classes = classNames('overview-card', {
-      'overview-card-section': this.props.linkTo,
-      'active': this.props.active
-    });
-    return <li onClick={this.handleClick} className={classes}>{this.props.children}</li>;
+    return <li className="overview-card">{this.props.children}</li>;
   }
 });
diff --git a/server/sonar-web/src/main/js/apps/overview/general/details-link.js b/server/sonar-web/src/main/js/apps/overview/general/details-link.js
new file mode 100644 (file)
index 0000000..dca106d
--- /dev/null
@@ -0,0 +1,18 @@
+import React from 'react';
+import classNames from 'classnames';
+
+export default React.createClass({
+  handleClick(e) {
+    e.preventDefault();
+    this.props.onRoute(this.props.linkTo);
+  },
+
+  render() {
+    let classes = classNames('overview-card', 'overview-card-section', {
+      'active': this.props.active
+    });
+    return <li className={classes}>
+      <a onClick={this.handleClick}>{window.t('overview.domain', this.props.linkTo)}</a>
+    </li>;
+  }
+});
diff --git a/server/sonar-web/src/main/js/apps/overview/general/details.js b/server/sonar-web/src/main/js/apps/overview/general/details.js
new file mode 100644 (file)
index 0000000..f5ee24b
--- /dev/null
@@ -0,0 +1,34 @@
+import React from 'react';
+import Cards from './cards';
+import DetailsLink from './details-link';
+
+
+function checkMeasureForDomain (domain, measures) {
+  if (domain === 'coverage' && measures.coverage == null) {
+    return false;
+  }
+  if (domain === 'duplications' && measures.duplications == null) {
+    return false;
+  }
+  return true;
+}
+
+
+export default React.createClass({
+  render() {
+    let domains = ['issues', 'coverage', 'duplications', 'size'].map(domain => {
+      if (!checkMeasureForDomain(domain, this.props.measures)) {
+        return null;
+      }
+      let active = domain === this.props.section;
+      return <DetailsLink key={domain} linkTo={domain} onRoute={this.props.onRoute} active={active}/>;
+    });
+
+    return (
+        <div className="overview-more">
+          <h2 className="overview-title">More Details</h2>
+          <Cards>{domains}</Cards>
+        </div>
+    );
+  }
+});
index 56cdb552a05ce5d2d007bd5f66f722cf393fc5d2..fb74bfa8a12c8ce2640c4912d2568ef9b3956862 100644 (file)
@@ -7,7 +7,8 @@ export default React.createClass({
     return (
         <div className="overview-gate">
           <h2 className="overview-title">{window.t('overview.quality_gate')}</h2>
-          <p className="big-spacer-top">You should <a href={qualityGatesUrl}>define</a> a quality gate on this project.</p>
+          <p className="overview-paragraph big-spacer-top">
+            You should <a href={qualityGatesUrl}>define</a> a quality gate on this project.</p>
         </div>
     );
   }
index fc0cc1a1199ca46070491d12d2b56e57a83d7baa..233e111063e6cecfcac0efff6f25cef305394f71 100644 (file)
@@ -5,7 +5,8 @@ import React from 'react';
 import Gate from './gate';
 import Leak from './leak';
 import Nutshell from './nutshell';
-import {getPeriodDate} from './../helpers/period-label';
+import MoreDetails from './details';
+import { getPeriodDate } from './../helpers/period-label';
 
 export default React.createClass({
   getInitialState() {
@@ -90,8 +91,9 @@ export default React.createClass({
     return <div>
       <Gate component={this.props.component} gate={this.props.gate}/>
       <Leak component={this.props.component} leak={this.state.leak} measures={this.state.measures}/>
-      <Nutshell component={this.props.component} measures={this.state.measures} section={this.props.section}
-                onRoute={this.props.onRoute}/>
+      <Nutshell component={this.props.component} measures={this.state.measures} section={this.props.section}/>
+      <MoreDetails component={this.props.component} measures={this.state.measures}
+                   section={this.props.section} onRoute={this.props.onRoute}/>
     </div>;
   }
 });
index 20d8444d7981e13a25e950972df4f609760266b9..dabcb76cc6bd67b0c923942ec904995522652636 100644 (file)
@@ -18,10 +18,8 @@ export default React.createClass({
       return null;
     }
 
-    let active = this.props.section === 'coverage';
-
     return (
-        <Card linkTo="coverage" active={active} onRoute={this.props.onRoute}>
+        <Card>
           <div className="measures">
             <div className="measures-chart">
               <Donut data={donutData} size="47"/>
index 277d1662dacdc86b527c0b7858e29f407ebead64..6c4bb0b1c3a9ba4eedc6601b34f975916d38a449 100644 (file)
@@ -18,10 +18,8 @@ export default React.createClass({
       return null;
     }
 
-    let active = this.props.section === 'duplications';
-
     return (
-        <Card linkTo="duplications" active={active} onRoute={this.props.onRoute}>
+        <Card>
           <div className="measures">
             <div className="measures-chart">
               <Donut data={donutData} size="47"/>
index 2d72a7d024397c1b453be06df69592abbdccfac4..3cad21fdc41d4df3fffa41e2c99d5da075e2cf27 100644 (file)
@@ -17,10 +17,8 @@ export default React.createClass({
         criticalIssues = this.props.measures.criticalIssues,
         issuesToReview = this.props.measures.openIssues + this.props.measures.reopenedIssues;
 
-    let active = this.props.section === 'issues';
-
     return (
-        <Card linkTo="issues" active={active} onRoute={this.props.onRoute}>
+        <Card>
           <div className="measures">
             <div className="measure measure-big" data-metric="sqale_rating">
               <DrilldownLink component={this.props.component.key} metric="sqale_rating">
index e1136d228bb15a34a27ac2abbcd225ddd373a572..845088f9491dfed9fe321c4c2cfd0c7f747801ee 100644 (file)
@@ -9,10 +9,8 @@ export default React.createClass({
         lines = this.props.measures['lines'],
         files = this.props.measures['files'];
 
-    let active = this.props.section === 'size';
-
     return (
-        <Card linkTo="size" active={active} onRoute={this.props.onRoute}>
+        <Card>
           <div className="measures">
             <div className="measure measure-big" data-metric="lines">
               <span className="measure-name">{window.t('overview.metric.lines')}</span>
index 1c9c586b62723a66f050cb32f8b05e6c23bee261..e538149958e23e3d123e2bbe4fa7cdea4bdf80e7 100644 (file)
@@ -9,9 +9,7 @@ export default React.createClass({
   render() {
     let props = {
       measures: this.props.measures,
-      component: this.props.component,
-      section: this.props.section,
-      onRoute: this.props.onRoute
+      component: this.props.component
     };
     return (
         <div className="overview-nutshell">
index aa7ba34cb607ef41db27948ca312425b7c565bbb..f8232b489c3e3256a019e3f3ca75a897c1e932b4 100644 (file)
@@ -33,8 +33,9 @@ export const Overview = React.createClass({
     return getMetrics().then(metrics => this.setState({ metrics }));
   },
 
-  handleRoute (section, el) {
+  handleRoute (section) {
     if (section !== this.state.section) {
+      let el = document.querySelector('.overview-more');
       this.setState({ section }, () => this.scrollToEl(el));
       window.location.href = '#' + section;
     } else {
@@ -49,7 +50,7 @@ export const Overview = React.createClass({
   },
 
   scrollToEl (el) {
-    let top = offset(el).top - el.getBoundingClientRect().height;
+    let top = offset(el).top;
     window.scrollTo(0, top);
   },
 
index 4798e0df32bf56f9da973e2373d8b1bfb0650614..abb1e8990e3c5b47cbeb40e9d2d3723616db9595 100644 (file)
@@ -3,6 +3,8 @@
 @import (reference) "../init/type";
 @import (reference) "../init/links";
 
+@side-padding: 30px;
+
 .overview {
   display: flex;
   width: 100%;
@@ -90,7 +92,7 @@
 }
 
 .overview-title {
-  padding: 0 30px;
+  padding: 0 @side-padding;
   font-size: 18px;
   font-weight: 400;
 
 }
 
 .overview-nutshell {
-  padding: 50px 0 0;
+  padding: 50px 0 25px;
 }
 
 .overview-cards {
 
 .overview-card {
   flex: 1 0 25%;
-  padding: 25px 30px;
+  padding: 25px @side-padding;
   box-sizing: border-box;
 
   .overview-gate & {
   }
 
   .measure-big + .measure-big {
-    margin-left: 30px;
+    margin-left: @side-padding;
   }
 
   .measure-big .measure-name {
 }
 
 .overview-card-section {
-  position: relative;
-  z-index: 100;
-  cursor: pointer;
-
-  &:hover, &.active {
-    &:before {
-      position: absolute;
-      top: 100%;
-      left: 50%;
-      margin-left: -10px;
-      width: 0;
-      height: 0;
-      border: solid transparent;
-      border-width: 10px;
-      border-top-color: #fff;
-      content: '';
-      pointer-events: none;
-    }
+  padding: 0;
+  text-align: center;
+
+  a {
+    display: block;
+    padding: 25px @side-padding;
+    .link-no-underline;
+    cursor: pointer;
+    transition: none;
   }
 }
 
+.overview-card-section.active a,
+.overview-card-section a:hover {
+  background-color: #2c3946;
+  color: mix(#fff, #2c3946, 75%);
+}
+
 .overview-measure {
   font-size: 28px;
 }
   }
 }
 
+.overview-domain {
+  margin-top: -25px;
+}
+
 .overview-domain-dark {
   background-color: #2c3946;
   color: mix(#fff, #2c3946, 75%);
 }
 
 .overview-domain-section {
-  padding: 50px 30px;
+  padding: 50px @side-padding;
 
   .overview-title {
     margin-bottom: 25px;
   display: flex;
   align-items: baseline;
   margin-bottom: 20px;;
-  padding: 50px 30px 0;
+  padding: 50px @side-padding 0;
 
   .overview-title {
     flex: 1;
   align-items: center;
   align-content: center;
 }
+
+.overview-paragraph {
+  padding: 0 @side-padding;
+}
+
+.overview-more {
+  padding-top: 50px;
+  padding-bottom: 25px;
+  border-top: 1px solid @barBorderColor;
+
+  .overview-title {
+    padding-bottom: 25px;
+  }
+}