You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ea...@apache.org on 2019/11/23 17:48:22 UTC

[qpid-dispatch] branch eallen-DISPATCH-1385 updated: More tests

This is an automated email from the ASF dual-hosted git repository.

eallen pushed a commit to branch eallen-DISPATCH-1385
in repository https://gitbox.apache.org/repos/asf/qpid-dispatch.git


The following commit(s) were added to refs/heads/eallen-DISPATCH-1385 by this push:
     new aea3637  More tests
aea3637 is described below

commit aea3637c2b6f8fb342dcb6f7687ed92e37e3bbae
Author: Ernest Allen <ea...@redhat.com>
AuthorDate: Sat Nov 23 12:47:50 2019 -0500

    More tests
---
 console/react/src/App.css                          |   1 +
 console/react/src/common/updated.js                |   4 +-
 console/react/src/details/createEntity.js          |   6 +-
 .../{updateEntity.js => createEntity.test.js}      |  28 +++---
 .../react/src/details/dataSources/routerData.js    | 101 ---------------------
 console/react/src/details/deleteEntity.js          |  35 +++----
 .../{createEntity.js => deleteEntity.test.js}      |  27 +++---
 console/react/src/details/detailsTablePage.test.js |  30 ++++--
 console/react/src/details/entityList.test.js       |   4 +
 console/react/src/details/routerSelect.js          |  26 +++---
 console/react/src/details/updateEntity.js          |   6 +-
 .../{updateEntity.js => updateEntity.test.js}      |  28 +++---
 console/react/src/overview/dashboard/layout.js     |   4 +-
 console/react/src/topology/clientInfoComponent.js  |   7 ++
 console/react/src/topology/nodes.js                |   2 +-
 console/react/src/topology/routerInfoComponent.js  |   3 +
 console/react/src/topology/svgUtils.js             |   2 +-
 console/react/src/topology/topologyViewer.js       |  78 ++++++----------
 18 files changed, 145 insertions(+), 247 deletions(-)

diff --git a/console/react/src/App.css b/console/react/src/App.css
index 2f0581f..e4a51a6 100644
--- a/console/react/src/App.css
+++ b/console/react/src/App.css
@@ -753,6 +753,7 @@ path.empty {
   opacity: 1;
   padding: 12px;
   font-size: 14px;
+  display: none;
 }
 
 #popover-div {
diff --git a/console/react/src/common/updated.js b/console/react/src/common/updated.js
index e762d8e..1813edd 100644
--- a/console/react/src/common/updated.js
+++ b/console/react/src/common/updated.js
@@ -28,9 +28,7 @@ class Updated extends Component {
   render() {
     return (
       <pre aria-label="last-updated" data-pf-content="true" className="overview-loading">
-        {`Updated ${this.props.service.utilities.strDate(
-          this.props.lastUpdated
-        )}`}
+        {`Updated ${this.props.service.utilities.strDate(this.props.lastUpdated)}`}
       </pre>
     );
   }
diff --git a/console/react/src/details/createEntity.js b/console/react/src/details/createEntity.js
index 3f5dfe2..205b37b 100644
--- a/console/react/src/details/createEntity.js
+++ b/console/react/src/details/createEntity.js
@@ -31,7 +31,11 @@ class CreateEntity extends React.Component {
   };
 
   render() {
-    return <Button onClick={this.handleClick}>Create</Button>;
+    return (
+      <Button aria-label="create-entity-button" onClick={this.handleClick}>
+        Create
+      </Button>
+    );
   }
 }
 
diff --git a/console/react/src/details/updateEntity.js b/console/react/src/details/createEntity.test.js
similarity index 62%
copy from console/react/src/details/updateEntity.js
copy to console/react/src/details/createEntity.test.js
index 4d1fac9..d31f50d 100644
--- a/console/react/src/details/updateEntity.js
+++ b/console/react/src/details/createEntity.test.js
@@ -18,21 +18,21 @@ under the License.
 */
 
 import React from "react";
-import { Button } from "@patternfly/react-core";
+import { render, fireEvent } from "@testing-library/react";
+import CreateEntity from "./createEntity";
 
-class UpdateEntity extends React.Component {
-  constructor(props) {
-    super(props);
-    this.state = {};
-  }
-
-  handleClick = () => {
-    this.props.handleEntityAction("update", this.props.record);
+it("renders UpdateEntity", () => {
+  let buttonClicked = false;
+  const props = {
+    handleEntityAction: () => {
+      buttonClicked = true;
+    }
   };
 
-  render() {
-    return <Button onClick={this.handleClick}>Update</Button>;
-  }
-}
+  const { getByLabelText } = render(<CreateEntity {...props} />);
+  const button = getByLabelText("create-entity-button");
+  expect(button).toBeInTheDocument();
 
-export default UpdateEntity;
+  fireEvent.click(button);
+  expect(buttonClicked).toBe(true);
+});
diff --git a/console/react/src/details/dataSources/routerData.js b/console/react/src/details/dataSources/routerData.js
deleted file mode 100644
index 67c015e..0000000
--- a/console/react/src/details/dataSources/routerData.js
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License.  You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing,
-software distributed under the License is distributed on an
-"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, either express or implied.  See the License for the
-specific language governing permissions and limitations
-under the License.
-*/
-
-class RouterData {
-  constructor(service) {
-    this.service = service;
-    this.fields = [
-      { title: "Router", field: "name" },
-      { title: "Area", field: "area" },
-      { title: "Mode", field: "mode" },
-      {
-        title: "Addresses",
-        field: "addrCount",
-        numeric: true
-      },
-      {
-        title: "Links",
-        field: "linkCount",
-        numeric: true
-      },
-      {
-        title: "External connections",
-        field: "connections",
-        numeric: true
-      }
-    ];
-    this.detailEntity = "router";
-    this.detailName = "Router";
-  }
-
-  fetchRecord = (currentRecord, schema) => {
-    return new Promise(resolve => {
-      this.service.management.topology.fetchEntities(
-        currentRecord.nodeId,
-        [{ entity: "router" }],
-        results => {
-          const record = results[currentRecord.nodeId].router;
-          let router = this.service.utilities.flatten(
-            record.attributeNames,
-            record.results[0]
-          );
-          router = this.service.utilities.formatAttributes(
-            router,
-            schema.entityTypes.router
-          );
-          resolve(router);
-        }
-      );
-    });
-  };
-
-  doFetch = (page, perPage) => {
-    return new Promise(resolve => {
-      this.service.management.topology.fetchAllEntities(
-        [{ entity: "connection", attrs: ["role"] }, { entity: "router" }],
-        nodes => {
-          // we have all the data now in the nodes object
-          let allRouterFields = [];
-          for (let node in nodes) {
-            let connections = 0;
-            for (let i = 0; i < nodes[node]["connection"].results.length; ++i) {
-              // we only requested "role" so it will be at results[0]
-              if (nodes[node]["connection"].results[i][0] !== "inter-router")
-                ++connections;
-            }
-            let routerRow = {
-              connections,
-              nodeId: node,
-              id: this.service.utilities.nameFromId(node)
-            };
-            nodes[node]["router"].attributeNames.forEach((routerAttr, i) => {
-              if (routerAttr !== "id") {
-                routerRow[routerAttr] = nodes[node]["router"].results[0][i];
-              }
-            });
-            allRouterFields.push(routerRow);
-          }
-          resolve({ data: allRouterFields, page, perPage });
-        }
-      );
-    });
-  };
-}
-
-export default RouterData;
diff --git a/console/react/src/details/deleteEntity.js b/console/react/src/details/deleteEntity.js
index 8313fbf..8a40979 100644
--- a/console/react/src/details/deleteEntity.js
+++ b/console/react/src/details/deleteEntity.js
@@ -43,9 +43,7 @@ class DeleteEntity extends React.Component {
   };
 
   getName = record => {
-    return record.name !== null
-      ? record.name
-      : `${this.props.entity}/${record.identity}`;
+    return record.name !== null ? record.name : `${this.props.entity}/${record.identity}`;
   };
 
   delete = () => {
@@ -60,35 +58,21 @@ class DeleteEntity extends React.Component {
           "DELETE"
         )
         .then(results => {
-          let statusCode =
-            results.context.message.application_properties.statusCode;
+          let statusCode = results.context.message.application_properties.statusCode;
           if (statusCode < 200 || statusCode >= 300) {
             const msg = `Deleted ${name} failed with message: ${results.context.message.application_properties.statusDescription}`;
             console.log(`error ${msg}`);
-            this.props.handleAddNotification(
-              "action",
-              msg,
-              new Date(),
-              "danger"
-            );
+            this.props.handleAddNotification("action", msg, new Date(), "danger");
           } else {
             const msg = `Deleted ${this.props.entity} ${name}`;
             console.log(`success ${msg}`);
-            this.props.handleAddNotification(
-              "action",
-              msg,
-              new Date(),
-              "success"
-            );
+            this.props.handleAddNotification("action", msg, new Date(), "success");
           }
-          this.setState(
-            { isModalOpen: false, closing: false, closed: true },
-            () => {
-              if (this.props.notifyClick) {
-                this.props.notifyClick("Done");
-              }
+          this.setState({ isModalOpen: false, closing: false, closed: true }, () => {
+            if (this.props.notifyClick) {
+              this.props.notifyClick("Done");
             }
-          );
+          });
         });
     });
   };
@@ -101,6 +85,7 @@ class DeleteEntity extends React.Component {
       <React.Fragment>
         {!this.props.showNow && (
           <Button
+            aria-label="delete-entity-button"
             className={`${this.props.asButton ? "" : "link-button"}`}
             onClick={this.handleModalShow}
           >
@@ -114,6 +99,7 @@ class DeleteEntity extends React.Component {
           onClose={this.handleModalHide}
           actions={[
             <Button
+              aria-label="confirm-delete"
               key="confirm"
               variant="primary"
               onClick={this.delete}
@@ -122,6 +108,7 @@ class DeleteEntity extends React.Component {
               Delete
             </Button>,
             <Button
+              aria-label="cancel-delete"
               key="cancel"
               variant="link"
               onClick={this.handleModalHide}
diff --git a/console/react/src/details/createEntity.js b/console/react/src/details/deleteEntity.test.js
similarity index 61%
copy from console/react/src/details/createEntity.js
copy to console/react/src/details/deleteEntity.test.js
index 3f5dfe2..ac30005 100644
--- a/console/react/src/details/createEntity.js
+++ b/console/react/src/details/deleteEntity.test.js
@@ -18,21 +18,20 @@ under the License.
 */
 
 import React from "react";
-import { Button } from "@patternfly/react-core";
+import { render, fireEvent } from "@testing-library/react";
+import DeleteEntity from "./deleteEntity";
 
-class CreateEntity extends React.Component {
-  constructor(props) {
-    super(props);
-    this.state = {};
-  }
-
-  handleClick = () => {
-    this.props.handleEntityAction("create");
+it("renders DeleteEntity", () => {
+  const entity = "listener";
+  const props = {
+    entity,
+    record: { name: "testListener" }
   };
 
-  render() {
-    return <Button onClick={this.handleClick}>Create</Button>;
-  }
-}
+  const { getByLabelText } = render(<DeleteEntity {...props} />);
+  const button = getByLabelText("delete-entity-button");
+  expect(button).toBeInTheDocument();
 
-export default CreateEntity;
+  fireEvent.click(button);
+  expect(getByLabelText("confirm-delete")).toBeInTheDocument();
+});
diff --git a/console/react/src/details/detailsTablePage.test.js b/console/react/src/details/detailsTablePage.test.js
index 9f49d38..53b927b 100644
--- a/console/react/src/details/detailsTablePage.test.js
+++ b/console/react/src/details/detailsTablePage.test.js
@@ -19,18 +19,30 @@ under the License.
 
 import React from "react";
 import { render } from "@testing-library/react";
-import DetailsTablePage from "./detailsTablePage";
+import { service, login } from "../serviceTest";
+import DetailTablesPage from "./detailsTablePage";
+
+it("renders the DetailTablesPage", async () => {
+  const entity = "router";
+  const routerName = "A";
+
+  await login();
+  expect(service.management.connection.is_connected()).toBe(true);
 
-it("renders the detailsTablePage", () => {
-  const entity = "testEntity";
   const props = {
     entity,
-    locationState: { currentRecord: { name: "test" } },
+    locationState: {
+      currentRecord: {
+        name: routerName,
+        routerId: service.utilities.idFromName(routerName, "_topo")
+      }
+    },
     details: true,
-    schema: { entityTypes: { testEntity: { attributes: [], operations: [] } } },
-    service: { management: { topology: { fetchEntities: () => Promise.resolve([]) } } }
+    service,
+    schema: service.schema
   };
-  const { getByLabelText } = render(<DetailsTablePage {...props} />);
-  const table = getByLabelText(entity);
-  expect(table).toBeInTheDocument();
+
+  const { getByTestId } = render(<DetailTablesPage {...props} />);
+  const header = getByTestId(`detail-for-${routerName}`);
+  expect(header).toBeInTheDocument();
 });
diff --git a/console/react/src/details/entityList.test.js b/console/react/src/details/entityList.test.js
index 40b43f7..d86d4c7 100644
--- a/console/react/src/details/entityList.test.js
+++ b/console/react/src/details/entityList.test.js
@@ -42,4 +42,8 @@ it("renders a EntityList", async () => {
 
   // clicking on the log entity should not crash
   fireEvent.click(logEntity);
+
+  fireEvent.click(getByTestId("router"));
+  fireEvent.click(getByTestId("autoLink"));
+  fireEvent.click(getByTestId("connection"));
 });
diff --git a/console/react/src/details/routerSelect.js b/console/react/src/details/routerSelect.js
index 0d3bec0..f6af128 100644
--- a/console/react/src/details/routerSelect.js
+++ b/console/react/src/details/routerSelect.js
@@ -33,19 +33,6 @@ class RouterSelect extends React.Component {
       selectedOption: "",
       routers: []
     };
-
-    this.onToggle = () => {
-      this.setState({
-        isOpen: !this.state.isOpen
-      });
-    };
-
-    this.onSelect = event => {
-      const routerName = event.target.id;
-      this.setState({ selectedOption: routerName, isOpen: false }, () => {
-        this.props.handleRouterSelected(this.nameToId[routerName]);
-      });
-    };
   }
 
   componentDidMount = () => {
@@ -62,6 +49,19 @@ class RouterSelect extends React.Component {
     });
   };
 
+  onToggle = () => {
+    this.setState({
+      isOpen: !this.state.isOpen
+    });
+  };
+
+  onSelect = event => {
+    const routerName = event.target.textContent;
+    this.setState({ selectedOption: routerName, isOpen: false }, () => {
+      this.props.handleRouterSelected(this.nameToId[routerName]);
+    });
+  };
+
   render() {
     const { routers, selectedOption, isOpen } = this.state;
     const menuItems = routers.map(r => (
diff --git a/console/react/src/details/updateEntity.js b/console/react/src/details/updateEntity.js
index 4d1fac9..3abeba8 100644
--- a/console/react/src/details/updateEntity.js
+++ b/console/react/src/details/updateEntity.js
@@ -31,7 +31,11 @@ class UpdateEntity extends React.Component {
   };
 
   render() {
-    return <Button onClick={this.handleClick}>Update</Button>;
+    return (
+      <Button aria-label="update-entity-button" onClick={this.handleClick}>
+        Update
+      </Button>
+    );
   }
 }
 
diff --git a/console/react/src/details/updateEntity.js b/console/react/src/details/updateEntity.test.js
similarity index 62%
copy from console/react/src/details/updateEntity.js
copy to console/react/src/details/updateEntity.test.js
index 4d1fac9..43206d2 100644
--- a/console/react/src/details/updateEntity.js
+++ b/console/react/src/details/updateEntity.test.js
@@ -18,21 +18,21 @@ under the License.
 */
 
 import React from "react";
-import { Button } from "@patternfly/react-core";
+import { render, fireEvent } from "@testing-library/react";
+import UpdateEntity from "./updateEntity";
 
-class UpdateEntity extends React.Component {
-  constructor(props) {
-    super(props);
-    this.state = {};
-  }
-
-  handleClick = () => {
-    this.props.handleEntityAction("update", this.props.record);
+it("renders UpdateEntity", () => {
+  let buttonClicked = false;
+  const props = {
+    handleEntityAction: () => {
+      buttonClicked = true;
+    }
   };
 
-  render() {
-    return <Button onClick={this.handleClick}>Update</Button>;
-  }
-}
+  const { getByLabelText } = render(<UpdateEntity {...props} />);
+  const button = getByLabelText("update-entity-button");
+  expect(button).toBeInTheDocument();
 
-export default UpdateEntity;
+  fireEvent.click(button);
+  expect(buttonClicked).toBe(true);
+});
diff --git a/console/react/src/overview/dashboard/layout.js b/console/react/src/overview/dashboard/layout.js
index 46fe58c..fc6e6b6 100644
--- a/console/react/src/overview/dashboard/layout.js
+++ b/console/react/src/overview/dashboard/layout.js
@@ -88,8 +88,8 @@ class PageLayout extends React.Component {
       visualizations: [{ name: "topology" }, { name: "flow", title: "Message flow" }],
       details: [{ name: "entities" }, { name: "schema" }]
     };
-    this.state.connecting = true;
-    this.tryInitialConnect();
+    //this.state.connecting = true;
+    //this.tryInitialConnect();
   }
 
   componentDidMount = () => {
diff --git a/console/react/src/topology/clientInfoComponent.js b/console/react/src/topology/clientInfoComponent.js
index feb6e5f..e782c38 100644
--- a/console/react/src/topology/clientInfoComponent.js
+++ b/console/react/src/topology/clientInfoComponent.js
@@ -162,6 +162,8 @@ class ClientInfoComponent extends Component {
   };
 
   componentWillUnmount = () => {
+    this.unmounted = true;
+
     if (this.timer) {
       clearInterval(this.timer);
       this.timer = null;
@@ -173,6 +175,7 @@ class ClientInfoComponent extends Component {
   };
   getTooltip = () => {
     this.props.d.toolTip(this.props.topology, true).then(toolTip => {
+      if (this.unmounted) return;
       this.setState({ toolTip });
     });
   };
@@ -248,6 +251,7 @@ class ClientInfoComponent extends Component {
         }
         // await until all sent requests have completed
         q.await(() => {
+          if (this.unmounted) return;
           this.setState({
             detail: { template: "edgeRouters", title: "edge router" }
           });
@@ -265,6 +269,7 @@ class ClientInfoComponent extends Component {
           this.d.key,
           [{ entity: "router.link", attrs: attrs }],
           results => {
+            if (this.unmounted) return;
             let links = results[this.d.key]["router.link"];
             for (let i = 0; i < this.d.normals.length; i++) {
               let n = this.d.normals[i];
@@ -319,6 +324,7 @@ class ClientInfoComponent extends Component {
   };
   updateDetail = () => {
     this.groupDetail().then(det => {
+      if (this.unmounted) return;
       Object.keys(det.infoPerId).forEach(id => {
         this.cachedInfo.push(det.infoPerId[id]);
       });
@@ -406,6 +412,7 @@ class ClientInfoComponent extends Component {
 
   onExpand = (event, rowIndex, colIndex, isOpen, rowData, extraData) => {
     const { rows } = this.state;
+    if (this.unmounted) return;
     if (!isOpen) {
       //set all other expanded cells false in this row if we are expanding
       rows[rowIndex].cells.forEach(cell => {
diff --git a/console/react/src/topology/nodes.js b/console/react/src/topology/nodes.js
index 1ae1337..d25b93f 100644
--- a/console/react/src/topology/nodes.js
+++ b/console/react/src/topology/nodes.js
@@ -185,7 +185,7 @@ const nodeProperties = {
     charge: [-1800, -900]
   },
   edge: {
-    radius: 20,
+    radius: 24,
     refX: {
       end: 24,
       start: -12
diff --git a/console/react/src/topology/routerInfoComponent.js b/console/react/src/topology/routerInfoComponent.js
index 6ac557c..c5b14d6 100644
--- a/console/react/src/topology/routerInfoComponent.js
+++ b/console/react/src/topology/routerInfoComponent.js
@@ -35,13 +35,16 @@ class RouterInfoComponent extends Component {
   };
 
   componentWillUnmount = () => {
+    this.unmounted = true;
     if (this.timer) {
       clearInterval(this.timer);
       this.timer = null;
     }
   };
+
   getTooltip = () => {
     this.props.d.toolTip(this.props.topology, true).then(toolTip => {
+      if (this.unmounted) return;
       this.setState({ toolTip });
     });
   };
diff --git a/console/react/src/topology/svgUtils.js b/console/react/src/topology/svgUtils.js
index c9d9477..9243f7f 100644
--- a/console/react/src/topology/svgUtils.js
+++ b/console/react/src/topology/svgUtils.js
@@ -111,7 +111,7 @@ export function appendContent(g) {
       else if (d.nodeType === "edge" || d.nodeType === "_edge") y = 4;
       return y;
     })
-    .attr("class", "id")
+    .attr("class", d => (d.nodeType === "_topo" ? "label" : "id"))
     .classed("console", function(d) {
       return utils.isConsole(d);
     })
diff --git a/console/react/src/topology/topologyViewer.js b/console/react/src/topology/topologyViewer.js
index 5871970..610efb0 100644
--- a/console/react/src/topology/topologyViewer.js
+++ b/console/react/src/topology/topologyViewer.js
@@ -26,7 +26,6 @@ import {
   TopologySideBar
 } from "@patternfly/react-topology";
 
-import QDRPopup from "../common/qdrPopup";
 import { Traffic } from "./traffic.js";
 import { separateAddresses } from "../chord/filters.js";
 import { Nodes } from "./nodes.js";
@@ -83,8 +82,6 @@ class TopologyPage extends Component {
       savedOptions.map.show = false;
     }
     this.state = {
-      popupContent: "",
-      showPopup: false,
       legendOptions: savedOptions,
       showRouterInfo: false,
       showClientInfo: false,
@@ -149,6 +146,7 @@ class TopologyPage extends Component {
     this.props.service.management.topology.setUpdateEntities([]);
     this.props.service.management.topology.stopUpdating();
     this.props.service.management.topology.delChangedAction("topology");
+    this.props.service.management.topology.delUpdatedAction("connectionPopupHTML");
     this.traffic.remove();
     this.forceData.nodes.savePositions();
     window.removeEventListener("resize", this.resize);
@@ -391,7 +389,7 @@ class TopologyPage extends Component {
     // mouse out of a path
     this.popupCancelled = true;
     this.props.service.management.topology.delUpdatedAction("connectionPopupHTML");
-    this.setState({ showPopup: false });
+    this.hideTooltip();
     d.selected = false;
     connectionPopupHTML();
   };
@@ -429,14 +427,13 @@ class TopologyPage extends Component {
         d.selected = true;
         this.popupCancelled = false;
         let updateTooltip = () => {
+          if (this.popupCancelled) return;
           if (d.selected) {
-            this.setState({
-              popupContent: connectionPopupHTML(
-                d,
-                this.props.service.management.topology._nodeInfo
-              )
-            });
-            this.displayTooltip(event);
+            const popupContent = connectionPopupHTML(
+              d,
+              this.props.service.management.topology._nodeInfo
+            );
+            this.displayTooltip(popupContent, { x: event.pageX, y: event.pageY });
           } else {
             this.handleMouseOutPath(d);
           }
@@ -534,7 +531,8 @@ class TopologyPage extends Component {
         let e = d3.event;
         self.popupCancelled = false;
         d.toolTip(self.props.service.management.topology).then(function(toolTip) {
-          self.showToolTip(toolTip, e);
+          if (self.popupCancelled) return;
+          self.displayTooltip(toolTip, { x: e.pageX, y: e.pageY });
         });
         if (d === self.mousedown_node) return;
         // enlarge target node
@@ -564,8 +562,8 @@ class TopologyPage extends Component {
         self.current_node = null;
         // unenlarge target node
         d3.select(this).attr("transform", "");
-        self.setState({ showPopup: false });
         self.popupCancelled = true;
+        self.hideTooltip();
         self.clearAllHighlights();
         self.mouseover_node = null;
         self.restart();
@@ -610,9 +608,9 @@ class TopologyPage extends Component {
         self.clearAllHighlights();
         self.mousedown_node = null;
         // handle clicking on nodes that represent multiple sub-nodes
-        if (d.normals && !d.isArtemis && !d.isQpid) {
+        if (d.normals && !d.isArtemis && !d.isQpid && d.nodeType !== "edge") {
           self.doDialog(d, "client");
-        } else if (d.nodeType === "_topo") {
+        } else if (d.nodeType === "_topo" || d.nodeType === "edge") {
           self.doDialog(d, "router");
         }
         // apply any data changes to the interface
@@ -670,7 +668,7 @@ class TopologyPage extends Component {
     this.svg.selectAll(".subtext").remove();
     let multiples = this.svg.selectAll(".multiple");
     multiples.each(function(d) {
-      let g = d3.select(this);
+      let g = d3.select(this.parentNode);
       let r = Nodes.radius(d.nodeType);
       g.append("svg:text")
         .attr("x", r + 4)
@@ -764,37 +762,29 @@ class TopologyPage extends Component {
       this.setState({ showClientInfo: true });
     }
   };
+
   handleCloseRouterInfo = type => {
     this.setState({ showRouterInfo: false });
   };
+
   handleCloseClientInfo = () => {
     this.setState({ showClientInfo: false });
   };
 
-  showToolTip = (title, event) => {
-    // show the tooltip
-    this.setState({ popupContent: title });
-    this.displayTooltip(event);
-  };
-
-  displayTooltip = event => {
+  displayTooltip = (content, xy) => {
     if (this.popupCancelled) {
-      this.setState({ showPopup: false });
-      return;
+      return this.hideTooltip();
     }
     // position the popup
     d3.select("#popover-div")
-      .style("left", event.pageX + 5 + "px")
-      .style("top", `${event.pageY}px`);
-    // show popup
-    this.setState({ showPopup: true }, () =>
-      d3
-        .select("#popover-div")
-        //.style("left", Math.min(width - pwidth, event.pageX + 5) + "px")
-        .on("mouseout", () => {
-          this.setState({ showPopup: false });
-        })
-    );
+      .style("left", `${xy.x + 5}px`)
+      .style("top", `${xy.y}px`)
+      .style("display", "block")
+      .html(content);
+  };
+
+  hideTooltip = () => {
+    d3.select("#popover-div").style("display", "none");
   };
 
   clearAllHighlights = () => {
@@ -1020,31 +1010,21 @@ class TopologyPage extends Component {
           />
         )}
 
-        <div
-          id="popover-div"
-          className={this.state.showPopup ? "qdrPopup" : "qdrPopup hidden"}
-          ref={el => (this.popupRef = el)}
-        >
-          <QDRPopup content={this.state.popupContent}></QDRPopup>
-        </div>
+        <div id="popover-div" className="qdrPopup"></div>
 
-        {this.state.showRouterInfo ? (
+        {this.state.showRouterInfo && (
           <RouterInfoComponent
             d={this.d}
             topology={this.props.service.management.topology}
             handleCloseRouterInfo={this.handleCloseRouterInfo}
           />
-        ) : (
-          <div />
         )}
-        {this.state.showClientInfo ? (
+        {this.state.showClientInfo && (
           <ClientInfoComponent
             d={this.d}
             topology={this.props.service.management.topology}
             handleCloseClientInfo={this.handleCloseClientInfo}
           />
-        ) : (
-          <div />
         )}
       </TopologyView>
     );


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org