From 9538c3f0e6c29342c4eb4e426ee7f8b4f06abbc2 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Wed, 28 Aug 2019 10:51:52 +0300
Subject: [PATCH] Updated the VennDiagram

---
 .../zerotrust/venn-components/ArcNode.js      | 36 +++++----
 .../zerotrust/venn-components/CircularNode.js | 48 ++++++------
 .../zerotrust/venn-components/Tooltip.js      | 43 -----------
 .../zerotrust/venn-components/Utility.js      |  6 --
 .../zerotrust/venn-components/VennDiagram.js  | 76 ++++++++-----------
 5 files changed, 78 insertions(+), 131 deletions(-)
 delete mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Tooltip.js

diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js
index 95d86db2f..1b4343045 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js
@@ -1,5 +1,6 @@
 import React from 'react'
 import PropTypes from 'prop-types';
+import {Popover, OverlayTrigger} from 'react-bootstrap';
 import * as d3 from 'd3'
 
 class ArcNode extends React.Component {
@@ -10,23 +11,28 @@ class ArcNode extends React.Component {
     let id = prefix + 'Node_' + index;
 
     return (
-      <g transform={'rotate(180)'} id={data.node.pillar}>
-        <path
+      <OverlayTrigger key={prefix + 'ArcOverlayTrigger' + index} trigger={['hover', 'focus']} placement={data.popover}
+                      overlay={<Popover key={prefix + 'ArcTooltip' + index} id={prefix + 'Popover'}
+                                        style={{backgroundColor: data.hex}}
+                                        title={data.node.pillar}>{data.tooltip}</Popover>}>
+        <g transform={'rotate(180)'} id={data.node.pillar}>
+          <path
 
-          id={prefix + 'Node_' + index}
-          className={'arcNode'}
-          data-tooltip={data.tooltip}
-          d={arc()}
-          fill={data.hex}
+            id={prefix + 'Node_' + index}
+            className={'arcNode'}
+            data-tooltip={data.tooltip}
+            d={arc()}
+            fill={data.hex}
 
-        />
-        <text x={0} dy={data.fontStyle.size * 1.2} fontSize={data.fontStyle.size} textAnchor='middle'
-              pointerEvents={'none'}>
-          <textPath href={'#' + id} startOffset={'26.4%'}>
-            {data.label}
-          </textPath>
-        </text>
-      </g>
+          />
+          <text x={0} dy={data.fontStyle.size * 1.2} fontSize={data.fontStyle.size} textAnchor='middle'
+                pointerEvents={'none'}>
+            <textPath href={'#' + id} startOffset={'26.4%'}>
+              {data.label}
+            </textPath>
+          </text>
+        </g>
+      </OverlayTrigger>
     );
   }
 }
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js
index 43ef529f3..a5cbc6698 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js
@@ -1,36 +1,36 @@
 import React from 'react'
+import PillarLabel from "../PillarLabel";
+import {Popover, OverlayTrigger} from 'react-bootstrap';
 import PropTypes from 'prop-types';
 
 class CircularNode extends React.Component {
   render() {
     let {prefix, index, data} = this.props;
 
-    let tspans = data.label.split("|").map((d_, i_) => {
-      let halfTextHeight = data.fontStyle.size * data.label.split("|").length / 2;
-      let key = 'vennDiagramCircularNode' + index + '_Tspan' + i_;
-
-      return (
-        <tspan key={key} x={data.offset.x} y={data.offset.y - halfTextHeight + i_ * data.fontStyle.size}>{d_}</tspan>
-      );
-    });
-
     let translate = 'translate(' + data.cx + ',' + data.cy + ')';
-
     return (
-      <g transform={translate} id={data.node.pillar}>
-        <circle
-          id={prefix + 'Node_' + index}
-          className={'circularNode'}
-          data-tooltip={data.tooltip}
-          r={data.r}
-          opacity={0.8}
-          fill={data.hex}
-        />
-        <text textAnchor='middle' fill={data.fontStyle.color} dominantBaseline={'middle'}
-              fontSize={data.fontStyle.size + 'px'} pointerEvents={'none'}>
-          {tspans}
-        </text>
-      </g>
+      <OverlayTrigger key={prefix + 'CircularOverlayTrigger' + index} trigger={['hover', 'focus']}
+                      placement={data.popover}
+                      overlay={<Popover key={prefix + 'CircularTooltip' + index} id={prefix + 'Popover'}
+                                        style={{backgroundColor: data.hex}}
+                                        title={data.node.pillar}>{data.tooltip}</Popover>}>
+        <g transform={translate} id={data.node.pillar}>
+          <circle
+            id={prefix + 'Node_' + index}
+            className={'circularNode'}
+            data-tooltip={data.tooltip}
+            r={data.r}
+            opacity={0.8}
+            fill={data.hex}
+          />
+          <foreignObject style={{fontSize: data.fontStyle.size, pointerEvents: 'none'}}
+                         key={prefix + 'PillarLabel' + index} x={data.offset.x - data.fontStyle.size * 6}
+                         y={data.offset.y - data.fontStyle.size} width={data.fontStyle.size * 12}
+                         height={data.fontStyle.size * 6}>
+            <PillarLabel pillar={data.node.pillar} status={data.status}/>
+          </foreignObject>
+        </g>
+      </OverlayTrigger>
     );
   }
 }
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Tooltip.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Tooltip.js
deleted file mode 100644
index e103af3c3..000000000
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Tooltip.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types';
-
-class Tooltip extends React.Component {
-
-  render() {
-    const {prefix, bcolor, top, left, display, html} = this.props;
-
-    const style = {
-      backgroundColor: bcolor,
-      border: '1px solid #FFFFFF',
-      borderRadius: '2px',
-      fontSize: 10,
-      padding: 8,
-      display,
-      opacity: 0.9,
-      position: 'fixed',
-      top,
-      left,
-      pointerEvents: 'none'
-    };
-
-    return (
-
-      <div className='tooltip' style={style}>
-        {html.split('\n').map((i_, key_) => {
-          return <div key={prefix + 'Element' + key_}>{i_}</div>;
-        })}
-      </div>
-    );
-  }
-}
-
-Tooltip.propTypes = {
-  prefix: PropTypes.string,
-  bcolor: PropTypes.string,
-  top: PropTypes.number,
-  left: PropTypes.number,
-  display: PropTypes.string,
-  html: PropTypes.string
-};
-
-export default Tooltip;
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Utility.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Utility.js
index 230e277f6..fa9309506 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Utility.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Utility.js
@@ -1,5 +1,4 @@
 export class TypographicUtilities {
-
   static removeAmpersand(string_) {
     return string_.replace(' & ', 'And');
   }
@@ -7,9 +6,4 @@ export class TypographicUtilities {
   static removeBrokenBar(string_) {
     return string_.replace(/\|/g, ' ');
   }
-
-  static setTitle(string_) {
-    return string_.charAt(0).toUpperCase() + string_.substr(1).toLowerCase();
-  }
-
 }
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js
index 25e57c2dd..6985607a0 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js
@@ -1,6 +1,5 @@
 import React from 'react'
 import PropTypes from 'prop-types'
-import Tooltip from './Tooltip'
 import CircularNode from './CircularNode'
 import ArcNode from './ArcNode'
 import {TypographicUtilities} from './Utility.js'
@@ -14,7 +13,6 @@ class VennDiagram extends React.Component {
 
     this.width = this.height = 512;
 
-    this.colors = ['#777777', '#D9534F', '#F0AD4E', '#5CB85C'];
     this.prefix = 'vennDiagram';
     this.suffices = ['', '|tests are|conclusive', '|tests were|inconclusive', '|tests|performed'];
     this.fontStyles = [{size: Math.max(9, this.width / 32), color: 'white'}, {
@@ -28,19 +26,27 @@ class VennDiagram extends React.Component {
     this.width2By7 = 2 * this.width / 7;
     this.width1By11 = this.width / 11;
     this.width1By28 = this.width / 28;
+    this.arcNodesGap = 4;
 
     this.toggle = false;
 
     this.layout = {
-      Data: {cx: 0, cy: 0, r: this.thirdWidth - this.offset * 2, offset: {x: 0, y: 0}},
-      People: {cx: -this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: this.width1By11, y: 0}},
-      Networks: {cx: this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: -this.width1By11, y: 0}},
-      Devices: {cx: 0, cy: this.width2By7, r: this.sixthWidth, offset: {x: 0, y: -this.width1By11}},
-      Workloads: {cx: 0, cy: -this.width2By7, r: this.sixthWidth, offset: {x: 0, y: this.width1By11}},
-      VisibilityAndAnalytics: {inner: this.thirdWidth - this.width1By28, outer: this.thirdWidth},
+      Data: {cx: 0, cy: 0, r: this.sixthWidth, offset: {x: 0, y: 0}, popover: 'top'},
+      People: {cx: -this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: this.width1By11, y: 0}, popover: 'right'},
+      Networks: {cx: this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: -this.width1By11, y: 0}, popover: 'left'},
+      Devices: {cx: 0, cy: this.width2By7, r: this.sixthWidth, offset: {x: 0, y: -this.width1By11}, popover: 'top'},
+      Workloads: {
+        cx: 0,
+        cy: -this.width2By7,
+        r: this.sixthWidth,
+        offset: {x: 0, y: this.width1By11},
+        popover: 'bottom'
+      },
+      VisibilityAndAnalytics: {inner: this.thirdWidth - this.width1By28, outer: this.thirdWidth, popover: 'left'},
       AutomationAndOrchestration: {
-        inner: this.thirdWidth - this.width1By28 * 2,
-        outer: this.thirdWidth - this.width1By28
+        inner: this.thirdWidth - this.width1By28 * 2 - this.arcNodesGap,
+        outer: this.thirdWidth - this.width1By28 - this.arcNodesGap,
+        popover: 'right'
       }
     };
 
@@ -65,37 +71,36 @@ class VennDiagram extends React.Component {
     this.rules = [
 
       {
-        id: 'Rule #1', f: function (d_) {
+        id: 'Rule #1', status: 'Unexecuted', hex: '#777777', f: function (d_) {
           return d_['Conclusive'] + d_['Inconclusive'] + d_['Positive'] === 0;
         }
       },
       {
-        id: 'Rule #2', f: function (d_) {
+        id: 'Rule #2', status: 'Conclusive', hex: '#D9534F', f: function (d_) {
           return d_['Conclusive'] > 0;
         }
       },
       {
-        id: 'Rule #3', f: function (d_) {
+        id: 'Rule #3', status: 'Inconclusive', hex: '#F0AD4E', f: function (d_) {
           return d_['Conclusive'] === 0 && d_['Inconclusive'] > 0;
         }
       },
       {
-        id: 'Rule #4', f: function (d_) {
+        id: 'Rule #4', status: 'Positive', hex: '#5CB85C', f: function (d_) {
           return d_['Positive'] + d_['Unexecuted'] >= 2 && d_['Positive'] * d_['Unexecuted'] > 0;
         }
       }
 
     ];
 
-    this._onScroll = this._onScroll.bind(this);
   }
 
   componentDidMount() {
     this.parseData();
-    window.addEventListener('scroll', this._onScroll);
   }
 
   _onMouseMove(e) {
+
     let self = this;
 
     if (!this.toggle) {
@@ -108,16 +113,12 @@ class VennDiagram extends React.Component {
       });
 
       if (e.target.id.includes('Node')) {
-        html = e.target.dataset.tooltip;
-        this.divElement.style.cursor = 'pointer';
-        hidden = 'block';
+
         e.target.setAttribute('opacity', 0.95);
-        bcolor = e.target.getAttribute('fill');
 
         // Set highest z-index
         e.target.parentNode.parentNode.appendChild(e.target.parentNode);
       } else {
-        this.divElement.style.cursor = 'default';
 
         // Return z indices to default
         Object.keys(this.layout).forEach(function (d_, i_) {
@@ -125,25 +126,9 @@ class VennDiagram extends React.Component {
         })
       }
 
-      this.setState({
-        target: e,
-        tooltip: {
-          target: e.target,
-          bcolor: bcolor,
-          top: e.clientY + 8,
-          left: e.clientX + 8,
-          display: hidden,
-          html: html
-        }
-      });
     }
   }
 
-  _onScroll(e) {
-    this.divElement.style.cursor = 'default';
-    this.setState({target: null, tooltip: {target: null, bcolor: 'none', top: 0, left: 0, display: 'none', html: ''}});
-  }
-
   _onClick(e) {
     this.toggle = this.state.tooltip.target === e.target;
 
@@ -152,6 +137,7 @@ class VennDiagram extends React.Component {
   }
 
   parseData() {
+
     let self = this;
     let data = [];
     const omit = (prop, {[prop]: _, ...rest}) => rest;
@@ -159,9 +145,8 @@ class VennDiagram extends React.Component {
     this.props.pillarsGrades.forEach((d_, i_) => {
 
       let params = omit('pillar', d_);
-      let sum = Object.keys(params).reduce((sum_, key_) => sum_ + parseFloat(params[key_] || 0), 0);
       let key = TypographicUtilities.removeAmpersand(d_.pillar);
-      let html = self.buildTooltipHtmlContent(d_);
+      let html = self.buildTooltipHtmlContent(params);
       let rule = null;
 
       for (let j = 0; j < self.rules.length; j++) {
@@ -181,7 +166,13 @@ class VennDiagram extends React.Component {
   }
 
   buildTooltipHtmlContent(object_) {
-    return Object.keys(object_).reduce((out_, key_) => out_ + TypographicUtilities.setTitle(key_) + ': ' + object_[key_] + '\n', '');
+
+    var out = [];
+    Object.keys(object_).forEach(function (d_) {
+      out.push([d_ + ': ' + object_[d_], <br/>]);
+    });
+    return out;
+
   }
 
   setLayoutElement(rule_, key_, html_, d_) {
@@ -195,7 +186,8 @@ class VennDiagram extends React.Component {
       this.layout[key_].fontStyle = this.fontStyles[1];
     }
 
-    this.layout[key_].hex = this.colors[rule_];
+    this.layout[key_].hex = this.rules[rule_].hex;
+    this.layout[key_].status = this.rules[rule_].status;
     this.layout[key_].label = d_.pillar + this.suffices[rule_];
     this.layout[key_].node = d_;
     this.layout[key_].tooltip = html_;
@@ -220,7 +212,6 @@ class VennDiagram extends React.Component {
           );
         } else {
           d_.label = TypographicUtilities.removeBrokenBar(d_.label);
-
           return (
             <ArcNode
               prefix={this.prefix}
@@ -239,7 +230,6 @@ class VennDiagram extends React.Component {
                xmlns='http://www.w3.org/2000/svg' xmlnsXlink='http://www.w3.org/1999/xlink'>
             {nodes}
           </svg>
-          <Tooltip id={this.prefix + 'Tooltip'} prefix={this.prefix} {...this.state.tooltip} />
         </div>
       )
     }